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

Add CW-1155 to implement EIP 1155 #27

Closed
wants to merge 9 commits into from
Closed
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
26 changes: 26 additions & 0 deletions Cargo.lock

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

5 changes: 5 additions & 0 deletions contracts/cw1155-base/.cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[alias]
wasm = "build --release --target wasm32-unknown-unknown"
wasm-debug = "build --target wasm32-unknown-unknown"
unit-test = "test --lib"
schema = "run --example schema"
38 changes: 38 additions & 0 deletions contracts/cw1155-base/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[package]
name = "cw1155-base"
version = "0.10.0"
authors = ["Ethan Frey <ethanfrey@users.noreply.github.com>"]
edition = "2018"
description = "Basic implementation cw1155 tokens"
license = "Apache-2.0"
repository = "https://github.com/CosmWasm/cw-plus"
homepage = "https://cosmwasm.com"
documentation = "https://docs.cosmwasm.com"

exclude = [
# Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication.
"artifacts/*",
]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib", "rlib"]

[features]
# for more explicit tests, cargo test --features=backtraces
backtraces = ["cosmwasm-std/backtraces"]
# use library feature to disable all instantiate/execute/query exports
library = []

[dependencies]
cw0 = { version = "0.10.0" }
cw2 = { version = "0.10.0" }
cw1155 = { path = "../../packages/cw1155", version = "0.10.0" }
cw-storage-plus = { version = "0.10.0" }
cosmwasm-std = { version = "1.0.0-beta" }
schemars = "0.8.1"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.23" }

[dev-dependencies]
cosmwasm-schema = { version = "1.0.0-beta" }
14 changes: 14 additions & 0 deletions contracts/cw1155-base/NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Cw1155_base
Copyright (C) 2020-2021 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.
69 changes: 69 additions & 0 deletions contracts/cw1155-base/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Cw1155 Basic

This is a basic implementation of a cw1155 contract. It implements
the [CW1155 spec](../../packages/cw1155/README.md) and is designed to
be deployed as is, or imported into other contracts to easily build
cw1155-compatible tokens with custom logic.

Implements:

- [x] CW1155 Base
- [x] Metadata extension
- [ ] Enumerable extension (AllTokens done, but not Tokens - requires [#81](https://github.com/CosmWasm/cw-plus/issues/81))

## Implementation

The `ExecuteMsg` and `QueryMsg` implementations follow the [CW1155 spec](../../packages/cw1155/README.md) and are described there.
Beyond that, we make a few additions:

* `InstantiateMsg` takes name and symbol (for metadata), as well as a **Minter** address. This is a special address that has full
power to mint new tokens (but not modify existing ones)
* `ExecuteMsg::Mint{token_id, owner, token_uri, amount, token_extension}` - creates a new token with given owner and (optional) metadata. It can only be called by
the Minter set in `instantiate`.
* `QueryMsg::Minter{}` - returns the minter address for this contract.

It requires all tokens to have defined metadata in the standard format (with no extensions). For generic tokens this may
often be enough.

The *Minter* can either be an external actor (eg. web server, using PubKey) or another contract. If you just want to customize
the minting behavior but not other functionality, you could extend this contract (importing code and wiring it together)
or just create a custom contract as the owner and use that contract to Mint.

If provided, it is expected that the _token_uri_ points to a JSON file following the [ERC721 Metadata JSON Schema](https://eips.ethereum.org/EIPS/eip-721).

## Running this contract

You will need Rust 1.44.1+ with `wasm32-unknown-unknown` target installed.

You can run unit tests on this via:

`cargo test`

Once you are happy with the content, you can compile it to wasm via:

```
RUSTFLAGS='-C link-arg=-s' cargo wasm
cp ../../target/wasm32-unknown-unknown/release/cw1155_base.wasm .
ls -l cw1155_base.wasm
sha256sum cw1155_base.wasm
```

Or for a production-ready (optimized) build, run a build command in the
the repository root: https://github.com/CosmWasm/cw-plus#compiling.

## Importing this contract

You can also import much of the logic of this contract to build another
CW1155-compliant contract, such as tradable game items or tokenized
songs & books

Basically, you just need to write your handle function and import
`cw1155_base::contract::handle_transfer`, etc and dispatch to them.
This allows you to use custom `ExecuteMsg` and `QueryMsg` with your additional
calls, but then use the underlying implementation for the standard cw1155
messages you want to support. The same with `QueryMsg`. You will most
likely want to write a custom, domain-specific `instantiate`.

For now, you can look at [`cw721-metadata-onchain`](../cw721-metadata-onchain/README.md)
for an example of how to "inherit" cw721 functionality and combine it with custom logic.
The process is similar for cw1155.
33 changes: 33 additions & 0 deletions contracts/cw1155-base/examples/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use std::env::current_dir;
use std::fs::create_dir_all;

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

use cw1155::{
ApprovedForAllResponse, BalanceOfResponse, ContractInfoResponse, NumTokensResponse,
TokenInfoResponse, TokensResponse,
};
use cw1155_base::{ExecuteMsg, Extension, InstantiateMsg, MinterResponse, QueryMsg};

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!(InstantiateMsg), &out_dir);
export_schema_with_title(&schema_for!(ExecuteMsg<Extension>), &out_dir, "ExecuteMsg");
export_schema(&schema_for!(QueryMsg), &out_dir);

export_schema(&schema_for!(ApprovedForAllResponse), &out_dir);
export_schema(&schema_for!(ContractInfoResponse), &out_dir);
export_schema(&schema_for!(MinterResponse), &out_dir);
export_schema_with_title(
&schema_for!(TokenInfoResponse<Extension>),
&out_dir,
"TokenInfoResponse",
);
export_schema(&schema_for!(NumTokensResponse), &out_dir);
export_schema(&schema_for!(BalanceOfResponse), &out_dir);
export_schema(&schema_for!(TokensResponse), &out_dir);
}
97 changes: 97 additions & 0 deletions contracts/cw1155-base/schema/approved_for_all_response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ApprovedForAllResponse",
"type": "object",
"required": [
"operators"
],
"properties": {
"operators": {
"type": "array",
"items": {
"$ref": "#/definitions/Approval"
}
}
},
"definitions": {
"Approval": {
"type": "object",
"required": [
"expires",
"spender"
],
"properties": {
"expires": {
"description": "When the Approval expires (maybe Expiration::never)",
"allOf": [
{
"$ref": "#/definitions/Expiration"
}
]
},
"spender": {
"description": "Account that can transfer/send the token",
"type": "string"
}
}
},
"Expiration": {
"description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)",
"oneOf": [
{
"description": "AtHeight will expire when `env.block.height` >= height",
"type": "object",
"required": [
"at_height"
],
"properties": {
"at_height": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
}
},
"additionalProperties": false
},
{
"description": "AtTime will expire when `env.block.time` >= time",
"type": "object",
"required": [
"at_time"
],
"properties": {
"at_time": {
"$ref": "#/definitions/Timestamp"
}
},
"additionalProperties": false
},
{
"description": "Never will never expire. Used to express the empty variant",
"type": "object",
"required": [
"never"
],
"properties": {
"never": {
"type": "object"
}
},
"additionalProperties": false
}
]
},
"Timestamp": {
"description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```",
"allOf": [
{
"$ref": "#/definitions/Uint64"
}
]
},
"Uint64": {
"description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```",
"type": "string"
}
}
}
24 changes: 24 additions & 0 deletions contracts/cw1155-base/schema/balance_of_response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "BalanceOfResponse",
"type": "object",
"required": [
"balance"
],
"properties": {
"balance": {
"description": "Balance available to the owner for the given token_id",
"allOf": [
{
"$ref": "#/definitions/Uint64"
}
]
}
},
"definitions": {
"Uint64": {
"description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```",
"type": "string"
}
}
}
17 changes: 17 additions & 0 deletions contracts/cw1155-base/schema/contract_info_response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ContractInfoResponse",
"type": "object",
"required": [
"name",
"symbol"
],
"properties": {
"name": {
"type": "string"
},
"symbol": {
"type": "string"
}
}
}
Loading