Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nft 721 spec #50

Merged
merged 10 commits into from
Aug 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ workflows:
- package_cw1
- package_cw2
- package_cw20
- package_cw721
- lint
deploy:
jobs:
Expand Down Expand Up @@ -303,6 +304,49 @@ jobs:
- target
key: cargocache-v2-cw20:1.44.1-{{ checksum "~/project/Cargo.lock" }}

package_cw721:
docker:
- image: rust:1.44.1
working_directory: ~/project/packages/cw721
steps:
- checkout:
path: ~/project
- run:
name: Version information
command: rustc --version; cargo --version; rustup --version; rustup target list --installed
- restore_cache:
keys:
- cargocache-v2-cw721:1.44.1-{{ checksum "~/project/Cargo.lock" }}
- run:
name: Add wasm32 target
command: rustup target add wasm32-unknown-unknown && rustup target list --installed
- run:
name: Build library for native target
command: cargo build --locked
- run:
name: Build library for wasm target
command: cargo wasm --locked
- run:
name: Run unit tests
command: cargo test --locked
- run:
name: Build and run schema generator
command: cargo schema --locked
- run:
name: Ensure schemas are up-to-date
command: |
CHANGES_IN_REPO=$(git status --porcelain)
if [[ -n "$CHANGES_IN_REPO" ]]; then
echo "Repository is dirty. Showing 'git status' and 'git --no-pager diff' for debugging now:"
git status && git --no-pager diff
exit 1
fi
- save_cache:
paths:
- /usr/local/cargo/registry
- target
key: cargocache-v2-cw721:1.44.1-{{ checksum "~/project/Cargo.lock" }}

lint:
docker:
- image: rust:1.44.1
Expand Down
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/cw721/.cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[alias]
wasm = "build --release --target wasm32-unknown-unknown"
wasm-debug = "build --target wasm32-unknown-unknown"
schema = "run --example schema"
15 changes: 15 additions & 0 deletions packages/cw721/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "cw721"
version = "0.1.1"
authors = ["Ethan Frey <ethanfrey@users.noreply.github.com>"]
edition = "2018"
description = "Definition and types for the CosmWasm-721 NFT interface"
license = "Apache-2.0"

[dependencies]
cosmwasm-std = { version = "0.10.0" }
schemars = "0.7"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }

[dev-dependencies]
cosmwasm-schema = { version = "0.10.0" }
14 changes: 14 additions & 0 deletions packages/cw721/NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CW721: A CosmWasm spec for non-fungible token contracts
Copyright (C) 2020 Confio OÜ

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
120 changes: 120 additions & 0 deletions packages/cw721/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# CW721 Spec: Non Fungible Tokens

CW721 is a specification for non-fungible tokens based on CosmWasm.
The name and design is based on Ethereum's ERC721 standard,
with some enhancements. The types in here can be imported by
contracts that wish to implement this spec, or by contracts that call
to any standard cw721 contract.

The specification is split into multiple sections, a contract may only
implement some of this functionality, but must implement the base.

## Base

This handles ownership, transfers, and allowances. These must be supported
as is by all CW721 contracts. Note that all tokens must have an owner,
as well as an ID. The ID is an arbitrary string, unique within the contract.

### Messages

`TransferNft{recipient, token_id}` -
This transfers ownership of the token to `recipient` account. This is
designed to send to an address controlled by a private key and *does not*
trigger any actions on the recipient if it is a contract.

Requires `token_id` to point to a valid token, and `env.sender` to be
the owner of it, or have an allowance to transfer it.

`SendNft{contract, token_id, msg}` -
This transfers ownership of the token to `contract` account. `contract`
must be an address controlled by a smart contract, which implements
the CW721Receiver interface. The `msg` will be passed to the recipient
contract, along with the token_id.

Requires `token_id` to point to a valid token, and `env.sender` to be
the owner of it, or have an allowance to transfer it.

`Approve{spender, token_id, expires}` - Grants permission to `spender` to
transfer or send the given token. This can only be performed when
`env.sender` is the owner of the given `token_id` or an `operator`.
There can multiple spender accounts per token, and they are cleared once
the token is transfered or sent.

`Revoke{spender, token_id}` - This revokes a previously granted permission
to transfer the given `token_id`. This can only be granted when
`env.sender` is the owner of the given `token_id` or an `operator`.

`ApproveAll{operator, expires}` - Grant `operator` permission to transfer or send
all tokens owned by `env.sender`. This approval is tied to the owner, not the
tokens and applies to any future token that the owner receives as well.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can ApproveAll be given to more than one operator at once?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, any number. Again taken from her 721 specs. Not sure why this is unlimited and approval is limited


`RevokeAll{operator}` - Revoke a previous `ApproveAll` permission granted
to the given `operator`.

### Queries

`OwnerOf{token_id}` - Returns the owner of the given token,
as well as anyone with approval on this particular token.
If the token is unknown, returns an error. Return type is
`OwnerResponse{owner}`.

ethanfrey marked this conversation as resolved.
Show resolved Hide resolved
`ApprovedForAll{owner}` - List all operators that can access all of
the owner's tokens. Return type is `ApprovedForAllResponse`

`NumTokens{}` - Total number of tokens issued

### Receiver

The counter-part to `SendNft` is `ReceiveNft`, which must be implemented by
any contract that wishes to manage CW721 tokens. This is generally *not*
implemented by any CW721 contract.

`ReceiveNft{sender, token_id, msg}` - This is designed to handle `SendNft`
messages. The address of the contract is stored in `env.sender`
so it cannot be faked. The contract should ensure the sender matches
the token contract it expects to handle, and not allow arbitrary addresses.

The `sender` is the original account requesting to move the token
and `msg` is a `Binary` data that can be decoded into a contract-specific
message. This can be empty if we have only one default action,
or it may be a `ReceiveMsg` variant to clarify the intention. For example,
if I send to an exchange, I can specify the price I want to list the token
for.

## Metadata

### Queries

`ContractInfo{}` - This returns top-level metadata about the contract.
Namely, `name` and `symbol`.

`NftInfo{token_id}` - This returns metadata about one particular token.
The return value is based on *ERC721 Metadata JSON Schema*, but directly
from the contract, not as a Uri. Only the image link is a Uri.

`AllNftInfo{token_id}` - This returns the result of both `NftInfo`
and `OwnerOf` as one query as an optimization for clients, which may
want both info to display one NFT.

## Enumerable

### Queries

Pagination is acheived via `start_after` and `limit`. Limit is a request
set by the client, if unset, the contract will automatically set it to
`DefaultLimit` (suggested 10). If set, it will be used up to a `MaxLimit`
value (suggested 30). Contracts can define other `DefaultLimit` and `MaxLimit`
values without violating the CW721 spec, and clients should not rely on
any particular values.

If `start_after` is unset, the query returns the first results, ordered by
lexogaphically by `token_id`. If `start_after` is set, then it returns the
first `limit` tokens *after* the given one. This allows straight-forward
pagination by taking the last result returned (a `token_id`) and using it
as the `start_after` value in a future query.

`Tokens{owner, start_after, limit}` - List all token_ids that belong to a given owner.
Return type is `TokensResponse{tokens: Vec<token_id>}`.

`AllTokens{start_after, limit}` - Requires pagination. Lists all token_ids controlled by
the contract.
28 changes: 28 additions & 0 deletions packages/cw721/examples/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use std::env::current_dir;
use std::fs::create_dir_all;

use cosmwasm_schema::{export_schema, remove_schemas, schema_for};

use cw721::{
AllNftInfoResponse, ApprovedForAllResponse, ContractInfoResponse, Cw721HandleMsg,
Cw721QueryMsg, Cw721ReceiveMsg, NftInfoResponse, NumTokensResponse, OwnerOfResponse,
TokensResponse,
};

fn main() {
let mut out_dir = current_dir().unwrap();
out_dir.push("schema");
create_dir_all(&out_dir).unwrap();
remove_schemas(&out_dir).unwrap();

export_schema(&schema_for!(Cw721HandleMsg), &out_dir);
export_schema(&schema_for!(Cw721QueryMsg), &out_dir);
export_schema(&schema_for!(Cw721ReceiveMsg), &out_dir);
export_schema(&schema_for!(AllNftInfoResponse), &out_dir);
export_schema(&schema_for!(ApprovedForAllResponse), &out_dir);
export_schema(&schema_for!(ContractInfoResponse), &out_dir);
export_schema(&schema_for!(OwnerOfResponse), &out_dir);
export_schema(&schema_for!(NftInfoResponse), &out_dir);
export_schema(&schema_for!(NumTokensResponse), &out_dir);
export_schema(&schema_for!(TokensResponse), &out_dir);
}
Loading