Skip to content

Commit

Permalink
Merge pull request #16 from casper-ecosystem/contract-ownership
Browse files Browse the repository at this point in the history
Implement contract ownership and metadata validation
  • Loading branch information
EdHastingsCasperAssociation authored Jun 15, 2022
2 parents bf99326 + 9279b6c commit d10399e
Show file tree
Hide file tree
Showing 29 changed files with 3,041 additions and 648 deletions.
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ build-contract:
cd client/balance_of_session && cargo build --release --target wasm32-unknown-unknown
cd client/owner_of_session && cargo build --release --target wasm32-unknown-unknown
cd client/get_approved_session && cargo build --release --target wasm32-unknown-unknown
cd client/minting_contract && cargo build --release --target wasm32-unknown-unknown
cd client/transfer_session && cargo build --release --target wasm32-unknown-unknown
wasm-strip contract/target/wasm32-unknown-unknown/release/contract.wasm 2>/dev/null | true
wasm-strip entrypoint_session/target/wasm32-unknown-unknown/release/entrypoint_call.wasm 2>/dev/null | true
wasm-strip client/mint_session/target/wasm32-unknown-unknown/release/mint_call.wasm 2>/dev/null | true
wasm-strip client/balance_of_session/target/wasm32-unknown-unknown/release/balance_of_call.wasm 2>/dev/null | true
wasm-strip client/owner_of_session/target/wasm32-unknown-unknown/release/owner_of_call.wasm 2>/dev/null | true
wasm-strip client/get_approved_session/target/wasm32-unknown-unknown/release/get_approved_call.wasm 2>/dev/null | true
wasm-strip client/minting_contract/target/wasm32-unknown-unknown/release/minting_contract.wasm 2>/dev/null | true
wasm-strip client/transfer_session/target/wasm32-unknown-unknown/release/transfer_call.wasm 2>/dev/null | true

test: build-contract
mkdir -p tests/wasm
Expand All @@ -21,6 +25,8 @@ test: build-contract
cp client/balance_of_session/target/wasm32-unknown-unknown/release/balance_of_call.wasm tests/wasm
cp client/owner_of_session/target/wasm32-unknown-unknown/release/owner_of_call.wasm tests/wasm
cp client/get_approved_session/target/wasm32-unknown-unknown/release/get_approved_call.wasm tests/wasm
cp client/minting_contract/target/wasm32-unknown-unknown/release/minting_contract.wasm tests/wasm
cp client/transfer_session/target/wasm32-unknown-unknown/release/transfer_call.wasm tests/wasm
cd tests && cargo test

clippy:
Expand Down
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# Enhanced NFT standard

## Design goals
- User attempting to use an NFT contract should be able to install the contract
with any differentiating arguments easily. Must work out of the box.
- Reference implementation must be straightforward/clear and obvious.
- Externally observable association between `Accounts` and `NFT`s they "own".
- Should be well documented with sufficient/exhaustive tests.
- Must be entirely self-contained within a singular repo, this includes the contract, tests
and any utilities such as clients and/or links to documentation.
- Must support mainstream expectations about common NFT, additional features beyond the norms
as long as they don't interfere with the core functionality.
- Metadata and Payload should be conformant with the community standards and need not be
constrained to CLType.
- DApp developer attempting to create an NFT contract should be able to install the contract as is,
configured for the specific builtin behavior they want their NFT contract instance to have. Must work out of the box.
- Reference implementation must be straightforward, clear, and obvious.
- Externally observable association between `Accounts` and/or `Contracts` and `NFT`s they "own".
- Should be well documented with exhaustive tests that prove all possible combinations of defined behavior work as intended.
- Must be entirely self-contained within a singular repo, this includes the all code, all tests
all relevant Casperlabs provided SDKs, and all relevant documentation.
- Must support mainstream expectations about common NFT conventions.
- A given NFT contract instance must be able to choose when created if it is using a Metadata schema conformant with existing community standards or a specific custom schema which they provide.
- A NFT contract instance must validate provided metadata against the specified metadata schema for that contract.
- Standardized session code to interact with an NFT contract instance must be usable as is, so that a given DApp developer doesn't have to write any Wasm producing logic for normal usage of NFT contract instances produced by this contract.

## Features and usage

Expand Down
20 changes: 20 additions & 0 deletions client/balance_of_session/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Session code for the Balance entry point

Utility session code meant for interacting with the `balance_of` entry point on the main enhanced NFT contract.
The `balance_of` session code calls the relevant entry point and saves the amount of tokens owned by either an `Account`
or `Contract` and saves the value in the `NamedKeys` of `Account` executing the session code.


## Compiling session code

The session code can be compiled to Wasm by running the `make build-contract` command provided in the Makefile at the top level.
The Wasm will be found in the `client/balance_of_session/target/wasm32-unknown-unknown/release` as `balance_of.wasm`.

## Usage

The `balance_of` session code takes in the following required runtime arguments.

* `nft_contract_hash`: The hash of a given Enhanced NFT contract passed in as a `Key`.
* `token_owner`: The `Key` of either the `Account` or `Contract` whose balance is being queried.
* `key_name`: The name for the entry within the `NamedKeys` under which the token amount will be stored, passed in as a `String`.

10 changes: 7 additions & 3 deletions client/balance_of_session/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ compile_error!("target arch should be wasm32: compile with '--target wasm32-unkn

extern crate alloc;
use alloc::string::String;

use casper_contract::contract_api::{runtime, storage};
use casper_types::{runtime_args, ContractHash, Key, RuntimeArgs, U256};
use casper_types::{runtime_args, ContractHash, Key, RuntimeArgs};

const ENTRY_POINT_BALANCE_OF: &str = "balance_of";
const ARG_NFT_CONTRACT_HASH: &str = "nft_contract_hash";
Expand All @@ -16,11 +17,14 @@ const ARG_KEY_NAME: &str = "key_name";

#[no_mangle]
pub extern "C" fn call() {
let nft_contract_hash: ContractHash = runtime::get_named_arg(ARG_NFT_CONTRACT_HASH);
let nft_contract_hash: ContractHash = runtime::get_named_arg::<Key>(ARG_NFT_CONTRACT_HASH)
.into_hash()
.map(|hash| ContractHash::new(hash))
.unwrap();
let key_name: String = runtime::get_named_arg(ARG_KEY_NAME);
let token_owner: Key = runtime::get_named_arg(ARG_TOKEN_OWNER);

let balance = runtime::call_contract::<U256>(
let balance = runtime::call_contract::<u64>(
nft_contract_hash,
ENTRY_POINT_BALANCE_OF,
runtime_args! {
Expand Down
18 changes: 18 additions & 0 deletions client/get_approved_session/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Session code for get_approved

Utility session code for interacting with the `get_approved` entry point present on the enhanced NFT contract. It returns
a `Key` if a given NFT is approved to be spent by another `Account` or `Contract` apart from the owner of the
NFT itself. It returns `Some(Key)` if there is an approved spender, `None` if there is no spender.

## Compiling session code

The session code can be compiled to Wasm by running the `make build-contract` command provided in the Makefile at the top level.
The Wasm will be found in the `client/get_approved_session/target/wasm32-unknown-unknown/release` as `get_approved.wasm`.

## Usage

The `get_approved` session code takes in the following required runtime arguments.

* `nft_contract_hash`: The hash of a given Enhanced NFT contract passed in as a `Key`.
* `token_id`: The `id` of the NFT, passed in as a `u64`.
* `key_name`: The name for the entry within the `NamedKeys` under which `Option<Key>` value is stored, passed in as a `String`.
34 changes: 26 additions & 8 deletions client/get_approved_session/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,44 @@ compile_error!("target arch should be wasm32: compile with '--target wasm32-unkn

extern crate alloc;
use alloc::string::String;

use casper_contract::contract_api::{runtime, storage};
use casper_types::{runtime_args, ContractHash, Key, RuntimeArgs, U256};
use casper_types::{runtime_args, ContractHash, Key, RuntimeArgs};

const ENTRY_POINT_GET_APPROVED: &str = "get_approved";
const ARG_NFT_CONTRACT_HASH: &str = "nft_contract_hash";
const ARG_KEY_NAME: &str = "key_name";
const ARG_TOKEN_ID: &str = "token_id";
const ARG_TOKEN_HASH: &str = "token_hash";
const ARG_IS_HASH_IDENTIFIER_MODE: &str = "is_hash_identifier_mode";

#[no_mangle]
pub extern "C" fn call() {
let nft_contract_hash: ContractHash = runtime::get_named_arg(ARG_NFT_CONTRACT_HASH);
let nft_contract_hash: ContractHash = runtime::get_named_arg::<Key>(ARG_NFT_CONTRACT_HASH)
.into_hash()
.map(|hash| ContractHash::new(hash))
.unwrap();
let key_name: String = runtime::get_named_arg(ARG_KEY_NAME);
let token_id = runtime::get_named_arg::<U256>(ARG_TOKEN_ID);

let maybe_operator = runtime::call_contract::<Option<Key>>(
nft_contract_hash,
ENTRY_POINT_GET_APPROVED,
runtime_args! {

let maybe_operator = if runtime::get_named_arg::<bool>(ARG_IS_HASH_IDENTIFIER_MODE) {
let token_hash = runtime::get_named_arg::<String>(ARG_TOKEN_HASH);
runtime::call_contract::<Option<Key>>(
nft_contract_hash,
ENTRY_POINT_GET_APPROVED,
runtime_args! {
ARG_TOKEN_HASH => token_hash,
},
)
} else {
let token_id = runtime::get_named_arg::<u64>(ARG_TOKEN_ID);
runtime::call_contract::<Option<Key>>(
nft_contract_hash,
ENTRY_POINT_GET_APPROVED,
runtime_args! {
ARG_TOKEN_ID => token_id,
},
);
)
};
runtime::put_key(&key_name, storage::new_uref(maybe_operator).into());
}
18 changes: 18 additions & 0 deletions client/mint_session/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Session code for minting

Utility session code for interacting with the `mint` entry point present on the enhanced NFT contract. The session code retrieves
the read only reference and inserts the reference under the executing `Account`s `NamedKeys`.

## Compiling session code

The session code can be compiled to Wasm by running the `make build-contract` command provided in the Makefile at the top level.
The Wasm will be found in the `client/mint_session/target/wasm32-unknown-unknown/release` as `mint_call.wasm`.

## Usage

The `mint_call` session code takes in the following required runtime arguments.

* `nft_contract_hash`: The hash of a given Enhanced NFT contract passed in as a `Key`.
* `token_owner`: The `Key` of the owner for the NFT to be minted. Note, this argument is ignored in the `Ownership::Minter` mode.
* `token_metadata`: The metadata describing the NFT to be minted, passed in as a `String`.
* `token_uri`: The URI for the off-chain resource represented by the NFT to be minted, passed in as a `String`
11 changes: 4 additions & 7 deletions client/mint_session/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ compile_error!("target arch should be wasm32: compile with '--target wasm32-unkn

extern crate alloc;

use alloc::format;

use alloc::string::String;
use casper_contract::contract_api::{runtime};
use casper_types::{runtime_args, ContractHash, Key, RuntimeArgs};
Expand All @@ -16,7 +16,7 @@ const ENTRY_POINT_MINT: &str = "mint";
const ARG_NFT_CONTRACT_HASH: &str = "nft_contract_hash";
const ARG_TOKEN_OWNER: &str = "token_owner";
const ARG_TOKEN_META_DATA: &str = "token_meta_data";
const ARG_TOKEN_URI: &str = "token_uri";


#[no_mangle]
pub extern "C" fn call() {
Expand All @@ -27,18 +27,15 @@ pub extern "C" fn call() {

let token_owner = runtime::get_named_arg::<Key>(ARG_TOKEN_OWNER);
let token_metadata: String = runtime::get_named_arg(ARG_TOKEN_META_DATA);
let token_uri: String = runtime::get_named_arg(ARG_TOKEN_URI);

let (owned_tokens_dictionary_key, collection_name) = runtime::call_contract::<(Key, String)>(
let (receipt_name, owned_tokens_dictionary_key, ) = runtime::call_contract::<(String, Key)>(
nft_contract_hash,
ENTRY_POINT_MINT,
runtime_args! {
ARG_TOKEN_OWNER => token_owner,
ARG_TOKEN_META_DATA => token_metadata,
ARG_TOKEN_URI =>token_uri,
},
);

let nft_contract_named_key = format!("{}_{}", nft_contract_hash.to_formatted_string(), collection_name);
runtime::put_key(&nft_contract_named_key, owned_tokens_dictionary_key)
runtime::put_key(&receipt_name, owned_tokens_dictionary_key)
}
21 changes: 21 additions & 0 deletions client/minting_contract/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "minting_contract"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
casper-contract = "1.4.3"
casper-types = "1.4.5"

[[bin]]
name = "minting_contract"
path = "src/main.rs"
bench = false
doctest = false
test = false

[profile.release]
codegen-units = 1
lto = true
Loading

0 comments on commit d10399e

Please sign in to comment.