-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ecd2958
commit e360893
Showing
31 changed files
with
1,891 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
4 changes: 4 additions & 0 deletions
4
contracts/whitelists/tiered-whitelist-merkletree/.cargo/config
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
[alias] | ||
wasm = "build --release --lib --target wasm32-unknown-unknown" | ||
unit-test = "test --lib" | ||
schema = "run --bin schema" |
11 changes: 11 additions & 0 deletions
11
contracts/whitelists/tiered-whitelist-merkletree/.editorconfig
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
root = true | ||
|
||
[*] | ||
indent_style = space | ||
indent_size = 2 | ||
charset = utf-8 | ||
trim_trailing_whitespace = true | ||
insert_final_newline = true | ||
|
||
[*.rs] | ||
indent_size = 4 |
15 changes: 15 additions & 0 deletions
15
contracts/whitelists/tiered-whitelist-merkletree/.gitignore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Build results | ||
/target | ||
|
||
# Cargo+Git helper file (https://github.com/rust-lang/cargo/blob/0.44.1/src/cargo/sources/git/utils.rs#L320-L327) | ||
.cargo-ok | ||
|
||
# Text file backups | ||
**/*.rs.bk | ||
|
||
# macOS | ||
.DS_Store | ||
|
||
# IDEs | ||
*.iml | ||
.idea |
45 changes: 45 additions & 0 deletions
45
contracts/whitelists/tiered-whitelist-merkletree/Cargo.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
[package] | ||
name = "tiered-whitelist-merkletree" | ||
authors = ["Martin Mo Kromsten <kromsten@pm.me>"] | ||
description = "Stargaze Merkle Tree Whitelist Contract" | ||
version = { workspace = true } | ||
edition = { workspace = true } | ||
homepage = { workspace = true } | ||
repository = { workspace = true } | ||
license = { workspace = true } | ||
|
||
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. | ||
"contract.wasm", | ||
"hash.txt", | ||
] | ||
|
||
# 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] | ||
cosmwasm-schema = { workspace = true } | ||
cosmwasm-std = { workspace = true } | ||
cw2 = { workspace = true } | ||
cw-storage-plus = { workspace = true } | ||
cw-utils = { workspace = true } | ||
rust_decimal = { version = "1.14.3" } | ||
schemars = { workspace = true } | ||
serde = { workspace = true } | ||
sg1 = { workspace = true } | ||
sg-std = { workspace = true } | ||
thiserror = { workspace = true } | ||
url = { workspace = true } | ||
hex = "0.4.3" | ||
serde_json = "1.0.105" | ||
rs_merkle = { version = "1.4.1", default-features = false } | ||
semver = { workspace = true } | ||
|
26 changes: 26 additions & 0 deletions
26
contracts/whitelists/tiered-whitelist-merkletree/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Whitelist MerkleTree contract | ||
|
||
A whitelist contract that relies on MerkleTree data structure for verifying inclusion of an address in a whitelist. | ||
|
||
Only merkle root (and optionaly URI of a tree) are stored within the state. Inclusion can be verified by submitting a user address and hex-encoded list of merklee proofs. This approach allows significant reduction of gas usage during storage phase with a downside of having actual data off-chain and reliance on 3rd parties for providing inclusiong proofs. | ||
|
||
Inclusion operation is a slighly more complex and costly when compared to the standard map-based whitelist. The contract uses **Sha256** for hashing concatenated proofs. Hashes are sorted on byte level prior to concatenation, which significantly simplifies the verification process by not requiring submission of leaf positions. | ||
|
||
**Important:** Make sure that your algorithm for merkle tree construction also sort the hashes. See example of extending `rs-merkle` library in `tests/hasher.rs` | ||
|
||
## Gas Usage | ||
|
||
The contracts for the merkletree based whitelist and the updated minter that supports it were both deployed to the testnet to measure actual gas usage in production. The contracts were instantiated and tested with two different whitelist sizes: **703** and **91,750,400** entries | ||
|
||
#### Instantiating | ||
Naturally due to only needing to store a merkle tree root in the state of the contract there is no difference between instantiating a whitelist with the [smaller](https://testnet-explorer.publicawesome.dev/stargaze/tx/07BB768915A24C17C12982D3FE34ADF0453AA9231961197A8B4E5E228D5C6B54) and the [bigger](https://testnet-explorer.publicawesome.dev/stargaze/tx/14E2DFB03AFB2A711A6AF601FA43FAEADFC8D0BA8581DD9E02EEFFB582E8AFB7) list sizes and they both consume 190,350 units of gas. | ||
|
||
#### Minting | ||
|
||
Number of hashing operations required to check for inclusion of an address in a merkle tree is at most `Math.ceil[ log₂N ]` and in some cases even smaller depending on the depth of a leaf within a tree. | ||
|
||
In case of the smaller tree with 704 records we had to submit 8 hash proofs and an example mint [transaction](https://testnet-explorer.publicawesome.dev/stargaze/tx/8692581537939E09BF5D81594B078436D4224F0944B515A421F096CEE480ECA9) took 635,345 units of gas | ||
|
||
The bigger tree with ~90 million records [used](https://testnet-explorer.publicawesome.dev/stargaze/tx/670A76A64F0A64FB1A5077DADDB6C326A9A64B66999215345C47BA3F03265811) 647,448 units of gas and required 24 proofs only (up to 27 with deeper leaves). | ||
|
||
The jump from computing 8 to computing 24 proofs (+16) only took additional 8 thousands units of gas. Keep in mind that another increase in 16 proofs allow us to check for inclusion in a tree with 1 trillion addresses. |
15 changes: 15 additions & 0 deletions
15
contracts/whitelists/tiered-whitelist-merkletree/rustfmt.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# stable | ||
newline_style = "unix" | ||
hard_tabs = false | ||
tab_spaces = 4 | ||
|
||
# unstable... should we require `rustup run nightly cargo fmt` ? | ||
# or just update the style guide when they are stable? | ||
#fn_single_line = true | ||
#format_code_in_doc_comments = true | ||
#overflow_delimited_expr = true | ||
#reorder_impl_items = true | ||
#struct_field_align_threshold = 20 | ||
#struct_lit_single_line = true | ||
#report_todo = "Always" | ||
|
146 changes: 146 additions & 0 deletions
146
contracts/whitelists/tiered-whitelist-merkletree/schema/raw/execute.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
{ | ||
"$schema": "http://json-schema.org/draft-07/schema#", | ||
"title": "ExecuteMsg", | ||
"oneOf": [ | ||
{ | ||
"type": "object", | ||
"required": [ | ||
"update_stage_config" | ||
], | ||
"properties": { | ||
"update_stage_config": { | ||
"$ref": "#/definitions/UpdateStageConfigMsg" | ||
} | ||
}, | ||
"additionalProperties": false | ||
}, | ||
{ | ||
"type": "object", | ||
"required": [ | ||
"update_admins" | ||
], | ||
"properties": { | ||
"update_admins": { | ||
"type": "object", | ||
"required": [ | ||
"admins" | ||
], | ||
"properties": { | ||
"admins": { | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
} | ||
} | ||
}, | ||
"additionalProperties": false | ||
} | ||
}, | ||
"additionalProperties": false | ||
}, | ||
{ | ||
"type": "object", | ||
"required": [ | ||
"freeze" | ||
], | ||
"properties": { | ||
"freeze": { | ||
"type": "object", | ||
"additionalProperties": false | ||
} | ||
}, | ||
"additionalProperties": false | ||
} | ||
], | ||
"definitions": { | ||
"Coin": { | ||
"type": "object", | ||
"required": [ | ||
"amount", | ||
"denom" | ||
], | ||
"properties": { | ||
"amount": { | ||
"$ref": "#/definitions/Uint128" | ||
}, | ||
"denom": { | ||
"type": "string" | ||
} | ||
} | ||
}, | ||
"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" | ||
} | ||
] | ||
}, | ||
"Uint128": { | ||
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 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 `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", | ||
"type": "string" | ||
}, | ||
"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" | ||
}, | ||
"UpdateStageConfigMsg": { | ||
"type": "object", | ||
"required": [ | ||
"stage_id" | ||
], | ||
"properties": { | ||
"end_time": { | ||
"anyOf": [ | ||
{ | ||
"$ref": "#/definitions/Timestamp" | ||
}, | ||
{ | ||
"type": "null" | ||
} | ||
] | ||
}, | ||
"mint_price": { | ||
"anyOf": [ | ||
{ | ||
"$ref": "#/definitions/Coin" | ||
}, | ||
{ | ||
"type": "null" | ||
} | ||
] | ||
}, | ||
"name": { | ||
"type": [ | ||
"string", | ||
"null" | ||
] | ||
}, | ||
"per_address_limit": { | ||
"type": [ | ||
"integer", | ||
"null" | ||
], | ||
"format": "uint32", | ||
"minimum": 0.0 | ||
}, | ||
"stage_id": { | ||
"type": "integer", | ||
"format": "uint32", | ||
"minimum": 0.0 | ||
}, | ||
"start_time": { | ||
"anyOf": [ | ||
{ | ||
"$ref": "#/definitions/Timestamp" | ||
}, | ||
{ | ||
"type": "null" | ||
} | ||
] | ||
} | ||
}, | ||
"additionalProperties": false | ||
} | ||
} | ||
} |
Oops, something went wrong.