diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile deleted file mode 100644 index bff8bc5..0000000 --- a/.gitpod.Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -### wasmd ### -FROM cosmwasm/wasmd:v0.18.0 as wasmd - -### rust-optimizer ### -FROM cosmwasm/rust-optimizer:0.11.5 as rust-optimizer - -FROM gitpod/workspace-full:latest - -COPY --from=wasmd /usr/bin/wasmd /usr/local/bin/wasmd -COPY --from=wasmd /opt/* /opt/ - -RUN sudo apt-get update \ - && sudo apt-get install -y jq \ - && sudo rm -rf /var/lib/apt/lists/* - -RUN rustup update stable \ - && rustup target add wasm32-unknown-unknown diff --git a/.gitpod.yml b/.gitpod.yml deleted file mode 100644 index d03610c..0000000 --- a/.gitpod.yml +++ /dev/null @@ -1,10 +0,0 @@ -image: cosmwasm/cw-gitpod-base:v0.16 - -vscode: - extensions: - - rust-lang.rust - -tasks: - - name: Dependencies & Build - init: | - cargo build diff --git a/Cargo.lock b/Cargo.lock index 3e5d590..54dbbad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,33 @@ version = 3 [[package]] name = "anyhow" -version = "1.0.52" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3" +checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "base-x" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc19a4937b4fbd3fe3379793130e42060d10627a360f2127802b10b87e7baf74" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" [[package]] name = "base64" @@ -14,6 +38,53 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "base64ct" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179" + +[[package]] +name = "bech32" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b" + +[[package]] +name = "blake2b_simd" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "blake2s_simd" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db539cc2b5f6003621f1cd9ef92d7ded8ea5232c7de0f9faa2de251cd98730d4" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "blake3" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -24,6 +95,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + [[package]] name = "block-padding" version = "0.2.1" @@ -36,45 +116,85 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cid" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a52cffa791ce5cf490ac3b2d6df970dc04f931b04e727be3c3e220e17164dfc4" +dependencies = [ + "core2", + "multibase", + "multihash", + "serde", + "unsigned-varint", +] + [[package]] name = "const-oid" -version = "0.6.2" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] [[package]] name = "cosmwasm-crypto" -version = "1.0.0-beta3" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a380b87642204557629c9b72988c47b55fbfe6d474960adba56b22331504956a" +checksum = "5eb0afef2325df81aadbf9be1233f522ed8f6e91df870c764bc44cca2b1415bd" dependencies = [ - "digest", + "digest 0.9.0", "ed25519-zebra", "k256", - "rand_core 0.5.1", + "rand_core 0.6.3", "thiserror", ] [[package]] name = "cosmwasm-derive" -version = "1.0.0-beta3" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866713b2fe13f23038c7d8824c3059d1f28dd94685fb406d1533c4eeeefeefae" +checksum = "4b36e527620a2a3e00e46b6e731ab6c9b68d11069c986f7d7be8eba79ef081a4" dependencies = [ "syn", ] [[package]] name = "cosmwasm-schema" -version = "1.0.0-beta3" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818b928263c09a3269c2bed22494a62107a43ef87900e273af8ad2cb9f7e4440" +checksum = "772e80bbad231a47a2068812b723a1ff81dd4a0d56c9391ac748177bea3a61da" dependencies = [ "schemars", "serde_json", @@ -82,13 +202,14 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.0.0-beta3" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dbb9939b31441dfa9af3ec9740c8a24d585688401eff1b6b386abb7ad0d10a8" +checksum = "875994993c2082a6fcd406937bf0fca21c349e4a624f3810253a14fa83a3a195" dependencies = [ "base64", "cosmwasm-crypto", "cosmwasm-derive", + "forward_ref", "schemars", "serde", "serde-json-wasm", @@ -96,11 +217,21 @@ dependencies = [ "uint", ] +[[package]] +name = "cosmwasm-storage" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d18403b07304d15d304dad11040d45bbcaf78d603b4be3fb5e2685c16f9229b5" +dependencies = [ + "cosmwasm-std", + "serde", +] + [[package]] name = "cpufeatures" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" dependencies = [ "libc", ] @@ -113,9 +244,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.2.11" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83bd3bb4314701c568e340cd8cf78c975aa0ca79e03d3f6d1677d5b0c9c0c03" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" dependencies = [ "generic-array", "rand_core 0.6.3", @@ -123,6 +254,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "crypto-mac" version = "0.11.1" @@ -140,40 +281,106 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" dependencies = [ "byteorder", - "digest", + "digest 0.9.0", "rand_core 0.5.1", "subtle", "zeroize", ] [[package]] -name = "cw-cyber-airdrop" -version = "0.1.0" +name = "cw-cyber-gift" +version = "1.0.0" dependencies = [ "anyhow", - "base64", - "cosmwasm-crypto", "cosmwasm-schema", "cosmwasm-std", + "cw-cyber-passport", + "cw-cyber-subgraph", + "cw-multi-test", "cw-storage-plus", "cw-utils", "cw0", + "cw1-subkeys", + "cw1-whitelist", "cw2", "cw20", + "cyber-std", + "cyber-std-test", "hex", "schemars", + "semver", + "serde", + "sha2 0.9.9", + "thiserror", +] + +[[package]] +name = "cw-cyber-passport" +version = "1.0.0" +dependencies = [ + "base64", + "bech32", + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-cyber-subgraph", + "cw-storage-plus", + "cw-utils", + "cw2", + "cw721", + "cw721-base", + "cyber-std", + "hex", + "primitive-types", + "ripemd160", + "schemars", + "semver", + "serde", + "sha2 0.9.9", + "sha3 0.9.1", + "thiserror", +] + +[[package]] +name = "cw-cyber-subgraph" +version = "1.0.0" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "cw2", + "cyber-std", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "cw-multi-test" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f9a8ab7c3c29ec93cb7a39ce4b14a05e053153b4a17ef7cf2246af1b7c087e" +dependencies = [ + "anyhow", + "cosmwasm-std", + "cosmwasm-storage", + "cw-storage-plus", + "cw-utils", + "derivative", + "itertools", + "prost", + "schemars", "serde", - "serde_json", - "sha2", - "sha3", "thiserror", ] [[package]] name = "cw-storage-plus" -version = "0.10.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b8b840947313c1a1cccf056836cd79a60b4526bdcd6582995be37dc97be4ae" +checksum = "648b1507290bbc03a8d88463d7cd9b04b1fa0155e5eef366c4fa052b9caaac7a" dependencies = [ "cosmwasm-std", "schemars", @@ -182,9 +389,9 @@ dependencies = [ [[package]] name = "cw-utils" -version = "0.11.1" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef842a1792e4285beff7b3b518705f760fa4111dc1e296e53f3e92d1ef7f6220" +checksum = "9dbaecb78c8e8abfd6b4258c7f4fbeb5c49a5e45ee4d910d3240ee8e1d714e1b" dependencies = [ "cosmwasm-std", "schemars", @@ -204,11 +411,56 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cw1" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "362649aa18f898ff8a7a18f2bfc5568e9ba56417f3c9ce0e01bc32ccb2e125e0" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + +[[package]] +name = "cw1-subkeys" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2a36327a5a87663c74438a88cca275fcdf858baac4d7ac7dbb6a91f528b3a5" +dependencies = [ + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", + "cw1", + "cw1-whitelist", + "cw2", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "cw1-whitelist" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4d6d212426746c4c052892166cf239d6604deec850d94c6eb57f8019b1afaa" +dependencies = [ + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", + "cw1", + "cw2", + "schemars", + "serde", + "thiserror", +] + [[package]] name = "cw2" -version = "0.10.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7002e6f9c1a1ef3915dca704a6306ba00c39495efd928551d6077c734f9a3d8" +checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1" dependencies = [ "cosmwasm-std", "cw-storage-plus", @@ -218,25 +470,120 @@ dependencies = [ [[package]] name = "cw20" -version = "0.10.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56fc9d54b9b365801beb101a5c0375dceaa5a285bfc14438a7b6007f9d362142" +checksum = "4cb782b8f110819a4eb5dbbcfed25ffba49ec16bbe32b4ad8da50a5ce68fec05" dependencies = [ "cosmwasm-std", - "cw0", + "cw-utils", + "schemars", + "serde", +] + +[[package]] +name = "cw721" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b9fd71276795554c35899bb3a378561ed0c288d231113e9915f6ee1f42b7b5" +dependencies = [ + "cosmwasm-std", + "cw-utils", + "schemars", + "serde", +] + +[[package]] +name = "cw721-base" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b61200af4e027af2d7485dbdc37c2a9c4093b6b2f2b811732329ef456076f97e" +dependencies = [ + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", + "cw2", + "cw721", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cyber-std" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ba03b7658a0f54674726aafddbe00a73460adbc4dde854bd57b0e60cdeb0e68" +dependencies = [ + "cid", + "cosmwasm-std", + "cw721", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cyber-std-test" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aef8f5f1cc2b8414235518e11bda34d3e87a471cabb70420e9c081a7c7ac30c" +dependencies = [ + "anyhow", + "cosmwasm-std", + "cw-multi-test", + "cw-storage-plus", + "cyber-std", "schemars", "serde", + "thiserror", +] + +[[package]] +name = "data-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" + +[[package]] +name = "data-encoding-macro" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86927b7cd2fe88fa698b87404b287ab98d1a0063a34071d92e575b72d3029aca" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5bbed42daaa95e780b60a50546aa345b8413a1e46f9a40a12907d3598f038db" +dependencies = [ + "data-encoding", + "syn", ] [[package]] name = "der" -version = "0.4.5" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b71cca7d95d7681a4b3b9cdf63c8dbc3730d0584c2c74e31416d64a90493f4" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" dependencies = [ "const-oid", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "digest" version = "0.9.0" @@ -246,69 +593,104 @@ dependencies = [ "generic-array", ] +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer 0.10.2", + "crypto-common", +] + [[package]] name = "dyn-clone" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" +checksum = "21e50f3adc76d6a43f5ed73b698a87d0760ca74617f60f7c3b879003536fdd28" [[package]] name = "ecdsa" -version = "0.12.4" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43ee23aa5b4f68c7a092b5c3beb25f50c406adc75e2363634f242f28ab255372" +checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" dependencies = [ "der", "elliptic-curve", - "hmac", + "rfc6979", "signature", ] [[package]] name = "ed25519-zebra" -version = "2.2.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a128b76af6dd4b427e34a6fd43dc78dbfe73672ec41ff615a2414c1a0ad0409" +checksum = "403ef3e961ab98f0ba902771d29f842058578bb1ce7e3c59dad5a6a93e784c69" dependencies = [ "curve25519-dalek", "hex", - "rand_core 0.5.1", + "rand_core 0.6.3", "serde", - "sha2", + "sha2 0.9.9", "thiserror", + "zeroize", ] +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "elliptic-curve" -version = "0.10.6" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beca177dcb8eb540133e7680baff45e7cc4d93bf22002676cec549f82343721b" +checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" dependencies = [ + "base16ct", "crypto-bigint", + "der", "ff", "generic-array", "group", - "pkcs8", "rand_core 0.6.3", + "sec1", "subtle", "zeroize", ] [[package]] name = "ff" -version = "0.10.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f40b2dcd8bc322217a5f6559ae5f9e9d1de202a2ecee2e9eafcbece7562a4f" +checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924" dependencies = [ "rand_core 0.6.3", "subtle", ] +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "static_assertions", +] + +[[package]] +name = "forward_ref" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" + [[package]] name = "generic-array" -version = "0.14.4" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" dependencies = [ "typenum", "version_check", @@ -327,9 +709,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" dependencies = [ "cfg-if", "libc", @@ -338,9 +720,9 @@ dependencies = [ [[package]] name = "group" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912" +checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" dependencies = [ "ff", "rand_core 0.6.3", @@ -360,7 +742,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" dependencies = [ "crypto-mac", - "digest", + "digest 0.9.0", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", ] [[package]] @@ -371,14 +762,15 @@ checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "k256" -version = "0.9.6" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "903ae2481bcdfdb7b68e0a9baa4b7c9aff600b9ae2e8e5bb5833b8c91ab851ea" +checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", - "sha2", + "sec1", + "sha2 0.9.9", ] [[package]] @@ -389,9 +781,57 @@ checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" [[package]] name = "libc" -version = "0.2.112" +version = "0.2.124" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "multibase" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" +checksum = "e3db354f401db558759dfc1e568d010a5d4146f4d3f637be1275ec4a3cf09689" +dependencies = [ + "blake2b_simd", + "blake2s_simd", + "blake3", + "core2", + "digest 0.10.3", + "multihash-derive", + "sha2 0.10.2", + "sha3 0.10.1", + "unsigned-varint", +] + +[[package]] +name = "multihash-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" +dependencies = [ + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", + "synstructure", +] [[package]] name = "opaque-debug" @@ -401,28 +841,96 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "pkcs8" -version = "0.7.6" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee3ef9b64d26bad0536099c816c6734379e45bbd5f14798def6809e5cc350447" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" dependencies = [ "der", "spki", + "zeroize", +] + +[[package]] +name = "primitive-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" +dependencies = [ + "fixed-hash", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", ] [[package]] name = "proc-macro2" -version = "1.0.34" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" dependencies = [ "unicode-xid", ] +[[package]] +name = "prost" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "quote" -version = "1.0.10" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ "proc-macro2", ] @@ -442,7 +950,29 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom 0.2.3", + "getrandom 0.2.6", +] + +[[package]] +name = "rfc6979" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "ripemd160" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "opaque-debug", ] [[package]] @@ -475,29 +1005,48 @@ dependencies = [ "syn", ] +[[package]] +name = "sec1" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" +dependencies = [ + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4" + [[package]] name = "serde" -version = "1.0.131" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1" +checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47" dependencies = [ "serde_derive", ] [[package]] name = "serde-json-wasm" -version = "0.3.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "042ac496d97e5885149d34139bad1d617192770d7eb8f1866da2317ff4501853" +checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.131" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2" +checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c" dependencies = [ "proc-macro2", "quote", @@ -517,9 +1066,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.73" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" dependencies = [ "itoa", "ryu", @@ -528,45 +1077,67 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest", + "digest 0.9.0", "opaque-debug", ] +[[package]] +name = "sha2" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.3", +] + [[package]] name = "sha3" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" dependencies = [ - "block-buffer", - "digest", + "block-buffer 0.9.0", + "digest 0.9.0", "keccak", "opaque-debug", ] +[[package]] +name = "sha3" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881bf8156c87b6301fc5ca6b27f11eeb2761224c7081e69b409d5a1951a70c86" +dependencies = [ + "digest 0.10.3", + "keccak", +] + [[package]] name = "signature" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2807892cfa58e081aa1f1111391c7a0649d4fa127a4ffbe34bcbfb35a1171a4" dependencies = [ - "digest", + "digest 0.9.0", "rand_core 0.6.3", ] [[package]] name = "spki" -version = "0.4.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c01a0c15da1b0b0e1494112e7af814a678fec9bd157881b49beac661e9b6f32" +checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" dependencies = [ + "base64ct", "der", ] @@ -584,46 +1155,67 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.82" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", + "syn", "unicode-xid", ] [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + [[package]] name = "typenum" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "uint" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f" +checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" dependencies = [ "byteorder", "crunchy", @@ -637,11 +1229,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "unsigned-varint" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" + [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" diff --git a/Cargo.toml b/Cargo.toml index 7ecca8e..66ffce9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,15 @@ [workspace] members = ["contracts/*"] -[profile.release.package.cw721-marketplace ] +[profile.release.package.cw-cyber-gift] +codegen-units = 1 +incremental = false + +[profile.release.package.cw-cyber-passport] +codegen-units = 1 +incremental = false + +[profile.release.package.cw-cyber-subgraph] codegen-units = 1 incremental = false diff --git a/LICENSE b/LICENSE index d645695..460a383 100644 --- a/LICENSE +++ b/LICENSE @@ -1,202 +1,3 @@ +Cyber License - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. +Don’t believe, don’t fear, don’t ask. diff --git a/NOTICE b/NOTICE deleted file mode 100644 index b82cbb9..0000000 --- a/NOTICE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2021 Orkun Külçe - -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. diff --git a/README.md b/README.md index 99591a6..fbac14f 100644 --- a/README.md +++ b/README.md @@ -1 +1,36 @@ -# cw-cybergift +# cyberGift Contracts + +``` +docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/workspace-optimizer:0.12.6 +``` + +## Contracts +### Gift Contract +[cw-cyber-airdrop](contracts/cw-cyber-airdrop) +### Passport Contract +[cw-cyber-passport](contracts/cw-cyber-passport) +### Subgraph Contract +[cw-cyber-subgraph](contracts/cw-cyber-subgraph) +### Treasury Contract +[cw1-subkeys](https://github.com/CosmWasm/cw-plus/tree/main/contracts/cw1-subkeys) +## Gift Execution +![Gift Execution](img/gift_execution.png) +## Contracts Initial Data and Functions +![Contracts Initial Data and Functions](img/contract_initiation_and_functions.png) +## User Story +### Create Passport +![Create Passport](img/create_passport.png) +### Prove Address +![Prove Address](img/prove_address.png) +### Claim Gift +![Claim Gift](img/claim_gift.png) +### Release Gift +![Release Gift](img/release_gift.png) +## Load testing +[Jupyter notebook](testdata/generate_test_data/gift_and_passport_contracts_load_testing.ipynb) + +## Bounty +cyberGift contracts have treasury which works as a security proxy for claiming process. cyberCongress will gradually replenish the treasury to minimize the risks of a hack. All BOOT on a contract balance is subject to bounty. If you find a way to get BOOT from contracts - do, but please report on the vulnerability. diff --git a/contracts/cw-cyber-airdrop/Cargo.toml b/contracts/cw-cyber-airdrop/Cargo.toml deleted file mode 100644 index 4e9c266..0000000 --- a/contracts/cw-cyber-airdrop/Cargo.toml +++ /dev/null @@ -1,59 +0,0 @@ -[package] -name = "cw-cyber-airdrop" -version = "0.1.0" -authors = ["Orkun Külçe "] -edition = "2018" - -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"] - -[profile.release] -opt-level = 3 -debug = false -rpath = false -lto = true -debug-assertions = false -codegen-units = 1 -panic = 'abort' -incremental = false -overflow-checks = true - -[features] -# for more explicit tests, cargo test --features=backtraces -backtraces = ["cosmwasm-std/backtraces"] -# use library feature to disable all instantiate/execute/query exports -library = [] - -[package.metadata.scripts] -optimize = """docker run --rm -v "$(pwd)":/code \ - --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ - --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/rust-optimizer:0.12.4 -""" - -[dependencies] -anyhow = "1" -cw0 = "0.10" -cw2 = "0.10" -cw20 = "0.10" -cosmwasm-std = { version = "1.0.0-beta" } -cw-utils = { version = "0.11.0" } -cosmwasm-crypto = { version = "1.0.0-beta" } -cw-storage-plus = "0.10" -schemars = "0.8.3" -serde = { version = "1.0.103", default-features = false, features = ["derive"] } -thiserror = { version = "1.0.27" } -hex = "0.4" -sha2 = { version = "0.9.5", default-features = false } -sha3 = "0.9" -serde_json = "1.0" -cosmwasm-schema = "1.0.0-beta" -base64 = "0.13" diff --git a/contracts/cw-cyber-airdrop/README.md b/contracts/cw-cyber-airdrop/README.md deleted file mode 100644 index 619bede..0000000 --- a/contracts/cw-cyber-airdrop/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# cw-cyber-airdrop - -Claim msg input: - -- nickname -- avatar_cid -- claimer_addr -- target_addr -- relay_reward diff --git a/contracts/cw-cyber-airdrop/schema/execute_msg.json b/contracts/cw-cyber-airdrop/schema/execute_msg.json deleted file mode 100644 index 7397eba..0000000 --- a/contracts/cw-cyber-airdrop/schema/execute_msg.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ExecuteMsg", - "oneOf": [ - { - "type": "object", - "required": [ - "update_config" - ], - "properties": { - "update_config": { - "type": "object", - "properties": { - "new_owner": { - "description": "NewOwner if non sent, contract gets locked. Recipients can receive airdrops but owner cannot register new stages.", - "type": [ - "string", - "null" - ] - } - } - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "register_merkle_root" - ], - "properties": { - "register_merkle_root": { - "type": "object", - "required": [ - "merkle_root" - ], - "properties": { - "merkle_root": { - "description": "MerkleRoot is hex-encoded merkle root.", - "type": "string" - } - } - } - }, - "additionalProperties": false - }, - { - "description": "Claim does not check if contract has enough funds, owner must ensure it.", - "type": "object", - "required": [ - "claim" - ], - "properties": { - "claim": { - "type": "object", - "required": [ - "claim_amount", - "claim_msg", - "proof", - "signature" - ], - "properties": { - "claim_amount": { - "$ref": "#/definitions/Uint128" - }, - "claim_msg": { - "$ref": "#/definitions/ClaimMsg" - }, - "proof": { - "description": "Proof is hex-encoded merkle proof.", - "type": "array", - "items": { - "type": "string" - } - }, - "signature": { - "$ref": "#/definitions/Binary" - } - } - } - }, - "additionalProperties": false - } - ], - "definitions": { - "Binary": { - "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec", - "type": "string" - }, - "ClaimMsg": { - "type": "object", - "required": [ - "avatar_cid", - "gift_claiming_address", - "gift_claiming_address_type", - "nickname", - "relay_reward", - "target_address" - ], - "properties": { - "avatar_cid": { - "type": "string" - }, - "gift_claiming_address": { - "type": "string" - }, - "gift_claiming_address_type": { - "$ref": "#/definitions/ClaimerType" - }, - "nickname": { - "type": "string" - }, - "relay_reward": { - "$ref": "#/definitions/Decimal" - }, - "target_address": { - "type": "string" - } - } - }, - "ClaimerType": { - "type": "string", - "enum": [ - "ethereum", - "cosmos" - ] - }, - "Decimal": { - "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", - "type": "string" - }, - "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" - } - } -} diff --git a/contracts/cw-cyber-airdrop/schema/query_msg.json b/contracts/cw-cyber-airdrop/schema/query_msg.json deleted file mode 100644 index d25bd55..0000000 --- a/contracts/cw-cyber-airdrop/schema/query_msg.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "QueryMsg", - "oneOf": [ - { - "type": "object", - "required": [ - "config" - ], - "properties": { - "config": { - "type": "object" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "merkle_root" - ], - "properties": { - "merkle_root": { - "type": "object" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "is_claimed" - ], - "properties": { - "is_claimed": { - "type": "object", - "required": [ - "address" - ], - "properties": { - "address": { - "type": "string" - } - } - } - }, - "additionalProperties": false - } - ] -} diff --git a/contracts/cw-cyber-airdrop/src/execute.rs b/contracts/cw-cyber-airdrop/src/execute.rs deleted file mode 100644 index 1630a4e..0000000 --- a/contracts/cw-cyber-airdrop/src/execute.rs +++ /dev/null @@ -1,424 +0,0 @@ -#[cfg(not(feature = "library"))] -use cosmwasm_std::entry_point; -use cosmwasm_std::{ - attr, has_coins, to_binary, BankMsg, Binary, Coin, Decimal, Deps, DepsMut, Env, MessageInfo, - Response, StdResult, Uint128, Uint64, -}; -use cw2::{get_contract_version, set_contract_version}; - -use crate::error::ContractError; -use crate::helpers; -use crate::helpers::{update_coefficient, verify_cosmos, verify_merkle_proof}; -use crate::msg::{ - ClaimMsg, ClaimerType, ConfigResponse, ExecuteMsg, InstantiateMsg, IsClaimedResponse, - MerkleRootResponse, MigrateMsg, QueryMsg, ReleaseStateResponse, -}; -use crate::state::{Config, ReleaseState, CLAIM, CONFIG, MERKLE_ROOT, RELEASE}; -use cw_utils::{Duration, Expiration, Expiration::Never, DAY}; -use std::ops::{Add, Mul, Sub}; - -// Version info, for migration info -const CONTRACT_NAME: &str = "crates.io:cw-cyber-gift"; -const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -const RELEASE_STAGES: u64 = 9; - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn instantiate( - deps: DepsMut, - _env: Env, - info: MessageInfo, - msg: InstantiateMsg, -) -> Result { - set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - - let owner = msg - .owner - .map_or(Ok(info.sender), |o| deps.api.addr_validate(&o))?; - - if !has_coins( - &info.funds, - &Coin { - denom: msg.allowed_native.clone(), - amount: msg.initial_balance, - }, - ) { - return Err(ContractError::InvalidInput {}); - } - - let config = Config { - owner: Some(owner), - passport_addr: deps.api.addr_validate(&msg.passport)?, - target_claim: msg.target_claim, - allowed_native: msg.allowed_native, - current_balance: msg.initial_balance, - initial_balance: msg.initial_balance, - coefficient_up: msg.coefficient_up, - coefficient_down: msg.coefficient_down, - coefficient: Decimal::from_ratio(msg.coefficient, 1u128), - claims: Uint64::zero(), - releases: Uint64::zero(), - }; - CONFIG.save(deps.storage, &config)?; - - Ok(Response::default()) -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn execute( - deps: DepsMut, - env: Env, - info: MessageInfo, - msg: ExecuteMsg, -) -> Result { - match msg { - ExecuteMsg::UpdateOwner { new_owner } => execute_update_owner(deps, env, info, new_owner), - ExecuteMsg::UpdatePassportAddr { new_passport_addr: new_passport } => { - execute_update_passport(deps, env, info, new_passport) - } - ExecuteMsg::UpdateTarget { new_target } => { - execute_update_target(deps, env, info, new_target) - } - ExecuteMsg::RegisterMerkleRoot { merkle_root } => { - execute_register_merkle_root(deps, env, info, merkle_root) - } - ExecuteMsg::Claim { - claim_msg, - signature, - claim_amount, - proof, - } => execute_claim(deps, env, info, claim_msg, signature, claim_amount, proof), - ExecuteMsg::Release {} => execute_release(deps, env, info), - } -} - -pub fn execute_update_owner( - deps: DepsMut, - _env: Env, - info: MessageInfo, - new_owner: Option, -) -> Result { - let cfg = CONFIG.load(deps.storage)?; - let owner = cfg.owner.ok_or(ContractError::Unauthorized {})?; - if info.sender != owner { - return Err(ContractError::Unauthorized {}); - } - - let mut tmp_owner = None; - if let Some(addr) = new_owner { - tmp_owner = Some(deps.api.addr_validate(&addr)?) - } - - CONFIG.update(deps.storage, |mut exists| -> StdResult<_> { - exists.owner = tmp_owner; - Ok(exists) - })?; - - Ok(Response::new().add_attributes(vec![attr("action", "update_owner")])) -} - -pub fn execute_update_passport( - deps: DepsMut, - _env: Env, - info: MessageInfo, - new_passport: String, -) -> Result { - let cfg = CONFIG.load(deps.storage)?; - let owner = cfg.owner.ok_or(ContractError::Unauthorized {})?; - if info.sender != owner { - return Err(ContractError::Unauthorized {}); - } - - let passport = deps.api.addr_validate(&new_passport)?; - - CONFIG.update(deps.storage, |mut exists| -> StdResult<_> { - exists.passport_addr = passport; - Ok(exists) - })?; - - Ok(Response::new().add_attributes(vec![ - attr("action", "update_passport"), - attr("passport", new_passport), - ])) -} - -pub fn execute_update_target( - deps: DepsMut, - _env: Env, - info: MessageInfo, - new_target: Uint64, -) -> Result { - let cfg = CONFIG.load(deps.storage)?; - let owner = cfg.owner.ok_or(ContractError::Unauthorized {})?; - if info.sender != owner { - return Err(ContractError::Unauthorized {}); - } - - CONFIG.update(deps.storage, |mut exists| -> StdResult<_> { - exists.target_claim = new_target; - Ok(exists) - })?; - - Ok(Response::new().add_attributes(vec![ - attr("action", "update_target"), - attr("target", new_target.to_string()), - ])) -} - -pub fn execute_register_merkle_root( - deps: DepsMut, - _env: Env, - info: MessageInfo, - merkle_root: String, -) -> Result { - let cfg = CONFIG.load(deps.storage)?; - let owner = cfg.owner.ok_or(ContractError::Unauthorized {})?; - if info.sender != owner { - return Err(ContractError::Unauthorized {}); - } - - let mut root_buf: [u8; 32] = [0; 32]; - hex::decode_to_slice(merkle_root.to_string(), &mut root_buf)?; - - MERKLE_ROOT.save(deps.storage, &merkle_root)?; - - Ok(Response::new().add_attributes(vec![ - attr("action", "register_merkle_root"), - attr("merkle_root", merkle_root), - ])) -} - -const CLAIM_BOUNTY: u128 = 100000; - -pub fn execute_claim( - deps: DepsMut, - _env: Env, - info: MessageInfo, - claim_msg: ClaimMsg, - signature: Binary, - amount: Uint128, - proof: Vec, -) -> Result { - let claimed = CLAIM.may_load(deps.storage, claim_msg.target_address.clone())?; - if claimed.is_some() { - return Err(ContractError::Claimed {}); - } - - let mut config = CONFIG.load(deps.storage)?; - let claim_amount = amount * config.coefficient; - - // TODO: delete after debug - println!("{:?}", "execute_claim"); - println!("{:?}", config.coefficient); - println!("{:?}", claim_amount.to_string()); - - if config.current_balance < claim_amount { - return Err(ContractError::GiftIsOver {}); - } - - is_eligible(deps.as_ref(), &claim_msg, signature)?; - - verify_merkle_proof( - &deps, - &info, - claim_msg.clone().gift_claiming_address, - amount, - proof, - )?; - - CLAIM.save(deps.storage, claim_msg.target_address.clone(), &true)?; - - update_coefficient(deps.storage, claim_amount, &mut config)?; - - let release_state = ReleaseState { - balance_claim: claim_amount.checked_sub(Uint128::new(CLAIM_BOUNTY))?, - stage: Uint64::zero(), - stage_expiration: Expiration::Never {}, - }; - - RELEASE.save( - deps.storage, - claim_msg.target_address.clone(), - &release_state, - )?; - - CONFIG.update(deps.storage, |mut cfg| -> StdResult<_> { - cfg.claims = cfg.claims.add(Uint64::new(1)); - Ok(cfg) - })?; - - let res = Response::new() - .add_message(BankMsg::Send { - to_address: claim_msg.clone().target_address, - amount: vec![Coin { - denom: config.allowed_native, - amount: Uint128::new(CLAIM_BOUNTY), - }], - }) - .add_attributes(vec![ - attr("action", "claim"), - attr("original", claim_msg.clone().gift_claiming_address), - attr( - "type", - claim_msg.clone().gift_claiming_address_type.to_string(), - ), - attr("target", claim_msg.clone().target_address), - attr("amount", claim_amount), - ]); - Ok(res) -} - -pub fn execute_release( - deps: DepsMut, - env: Env, - info: MessageInfo, -) -> Result { - let claimed = CLAIM.may_load(deps.storage, info.clone().sender.into_string())?; - if claimed.is_none() { - return Err(ContractError::NotClaimed {}); - } - - let mut config = CONFIG.load(deps.storage)?; - if config.claims < config.target_claim { - return Err(ContractError::NotActivated {}); - } - - let mut release_state = RELEASE.load(deps.storage, info.clone().sender.into_string())?; - - let amount: Uint128; - // let expiration:Expiration; - // let stage:u64; - - if release_state.balance_claim.is_zero() { - return Err(ContractError::GiftReleased {}); - } - - if release_state.stage.is_zero() { - amount = release_state.balance_claim.mul(Decimal::percent(10)); - release_state.stage_expiration = DAY.after(&env.block); - release_state.stage = Uint64::new(RELEASE_STAGES); - } else { - if release_state.stage_expiration.is_expired(&env.block) { - if release_state.stage.u64() == 1 { - amount = release_state.balance_claim; - release_state.stage_expiration = Expiration::Never {}; - release_state.stage = Uint64::zero(); - } else { - amount = release_state - .balance_claim - .mul(Decimal::from_ratio(1u128, release_state.stage)); - release_state.stage_expiration = DAY.after(&env.block); - release_state.stage = release_state.stage.checked_sub(Uint64::new(1))?; - } - } else { - return Err(ContractError::StageReleased {}); - } - } - - release_state.balance_claim = release_state.balance_claim - amount; - - RELEASE.save( - deps.storage, - info.clone().sender.to_string(), - &release_state, - )?; - - CONFIG.update(deps.storage, |mut cfg| -> StdResult<_> { - cfg.releases = cfg.releases.add(Uint64::new(1)); - Ok(cfg) - })?; - - let res = Response::new() - .add_message(BankMsg::Send { - to_address: info.sender.to_string(), - amount: vec![Coin { - denom: config.allowed_native, - amount, - }], - }) - .add_attributes(vec![ - attr("action", "release"), - attr("address", info.sender), - attr("stage", release_state.stage.to_string()), - attr("amount", amount), - ]); - Ok(res) -} - -fn is_eligible(deps: Deps, claim_msg: &ClaimMsg, signature: Binary) -> Result { - match claim_msg.gift_claiming_address_type { - ClaimerType::Ethereum => helpers::verify_ethereum(deps, &claim_msg, signature), - ClaimerType::Cosmos => verify_cosmos(deps, &claim_msg, signature), - _ => Err(ContractError::IsNotEligible { - msg: "address prefix not allowed".to_string(), - }), - } -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { - match msg { - QueryMsg::Config {} => to_binary(&query_config(deps)?), - QueryMsg::MerkleRoot {} => to_binary(&query_merkle_root(deps)?), - QueryMsg::IsClaimed { address } => to_binary(&query_is_claimed(deps, address)?), - QueryMsg::ReleaseState { address } => to_binary(&query_release_state(deps, address)?), - } -} - -pub fn query_config(deps: Deps) -> StdResult { - let cfg = CONFIG.load(deps.storage)?; - Ok(ConfigResponse { - owner: cfg.owner.map(|o| o.to_string()), - passport: cfg.passport_addr.to_string(), - target_claim: cfg.target_claim, - allowed_native: cfg.allowed_native, - current_balance: cfg.current_balance, - initial_balance: cfg.initial_balance, - coefficient_up: cfg.coefficient_up, - coefficient_down: cfg.coefficient_down, - coefficient: cfg.coefficient, - claims: cfg.claims, - releases: cfg.releases, - }) -} - -pub fn query_merkle_root(deps: Deps) -> StdResult { - let merkle_root = MERKLE_ROOT.load(deps.storage)?; - let resp = MerkleRootResponse { merkle_root }; - - Ok(resp) -} - -pub fn query_is_claimed(deps: Deps, address: String) -> StdResult { - let is_claimed = CLAIM.may_load(deps.storage, address)?.unwrap_or(false); - let resp = IsClaimedResponse { is_claimed }; - - Ok(resp) -} - -pub fn query_release_state(deps: Deps, address: String) -> StdResult { - let release_state = RELEASE - .may_load(deps.storage, address)? - .unwrap_or(ReleaseState { - balance_claim: Default::default(), - stage: Uint64::zero(), - stage_expiration: Default::default(), - }); - let resp = ReleaseStateResponse { - balance_claim: release_state.balance_claim, - stage: release_state.stage, - stage_expiration: release_state.stage_expiration, - }; - - Ok(resp) -} - -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result { - let version = get_contract_version(deps.storage)?; - if version.contract != CONTRACT_NAME { - return Err(ContractError::CannotMigrate { - previous_contract: version.contract, - }); - } - Ok(Response::default()) -} diff --git a/contracts/cw-cyber-airdrop/src/helpers.rs b/contracts/cw-cyber-airdrop/src/helpers.rs deleted file mode 100644 index 02532b8..0000000 --- a/contracts/cw-cyber-airdrop/src/helpers.rs +++ /dev/null @@ -1,211 +0,0 @@ -use crate::msg::ClaimMsg; -use crate::state::{Config, CONFIG, MERKLE_ROOT}; -use crate::ContractError; -use anyhow::Result; -use cosmwasm_std::{ - from_binary, to_vec, Binary, Decimal, Deps, DepsMut, MessageInfo, StdError, StdResult, Storage, - Uint128, VerificationError, -}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use serde_json::to_string; -use sha2::{Digest, Sha256}; -use sha3::Keccak256; -use std::convert::TryInto; -use std::ops::{Add, Mul, Sub}; - -pub fn update_coefficient( - store: &mut dyn Storage, - amount: Uint128, - config: &mut Config, -) -> StdResult<()> { - let coefficient_up = config.coefficient_up; - let coefficient_down = config.coefficient_down; - let initial_balance = config.initial_balance; - let current_balance = config.current_balance; - - // TODO delete after debug - println!("{:?}", "update_coefficient"); - println!("{:?}", coefficient_up.to_string()); - println!("{:?}", coefficient_down.to_string()); - println!("{:?}", initial_balance.to_string()); - println!("{:?}", current_balance.to_string()); - println!("{:?}", amount.to_string()); - - let new_balance_ratio = Decimal::from_ratio(current_balance, initial_balance); - - let new_coefficient = Decimal::one() - .sub(new_balance_ratio) - .mul(Decimal::from_ratio(coefficient_down, 1u128)) - .add(Decimal::from_ratio(coefficient_up, 1u128).mul(new_balance_ratio)); - - // TODO delete after debug - println!("{:?}", new_balance_ratio.to_string()); - println!("{:?}", new_coefficient.to_string()); - - config.coefficient = new_coefficient; - config.current_balance = current_balance - amount; - CONFIG.save(store, &config) -} - -pub fn verify_merkle_proof( - deps: &DepsMut, - _info: &MessageInfo, - claimer: String, - amount: Uint128, - proof: Vec, -) -> Result { - let merkle_root = MERKLE_ROOT.load(deps.storage)?; - - let user_input = format!("{}{}", claimer, amount); - let hash = sha2::Sha256::digest(user_input.as_bytes()) - .as_slice() - .try_into() - .map_err(|_| ContractError::WrongLength {})?; - - let hash = proof.into_iter().try_fold(hash, |hash, p| { - let mut proof_buf = [0; 32]; - hex::decode_to_slice(p, &mut proof_buf)?; - let mut hashes = [hash, proof_buf]; - hashes.sort_unstable(); - sha2::Sha256::digest(&hashes.concat()) - .as_slice() - .try_into() - .map_err(|_| ContractError::WrongLength {}) - })?; - - let mut root_buf: [u8; 32] = [0; 32]; - hex::decode_to_slice(merkle_root, &mut root_buf)?; - if root_buf != hash { - return Err(StdError::verification_err(VerificationError::GenericErr {}).into()); - } - Ok(true) -} - -pub fn verify_ethereum( - deps: Deps, - claim_msg: &ClaimMsg, - signature: Binary, -) -> Result { - let mut hasher = Keccak256::new(); - - let msg = to_string(claim_msg).map_err(|_| ContractError::InvalidInput {})?; - - hasher.update(format!("\x19Ethereum Signed Message:\n{}", msg.len())); - hasher.update(msg); - let hash = hasher.finalize(); - let sig = decode_signature(&signature.clone().to_string())?; - - // Decompose signature - let (v, rs) = match sig.split_last() { - Some(pair) => pair, - None => { - return Err(ContractError::IsNotEligible { - msg: "Signature must not be empty".to_string(), - }) - } - }; - let recovery = get_recovery_param(*v)?; - - // Verification - let calculated_pubkey = deps.api.secp256k1_recover_pubkey(&hash, rs, recovery)?; - let calculated_address = ethereum_address_raw(&calculated_pubkey)?; - let signer_address = decode_address(claim_msg.gift_claiming_address.clone().as_str())?; - if signer_address != calculated_address { - return Err(ContractError::IsNotEligible { - msg: "signer address is not calculated addr".to_string(), - }); - } - deps.api - .secp256k1_verify(&hash, rs, &calculated_pubkey) - .map_err(|err| ContractError::IsNotEligible { - msg: err.to_string(), - }) -} - -fn get_recovery_param(v: u8) -> StdResult { - match v { - 27 => Ok(0), - 28 => Ok(1), - _ => Err(StdError::generic_err("Values of v other than 27 and 28 not supported. Replay protection (EIP-155) cannot be used here.")) - } -} - -/// Returns a raw 20 byte Ethereum address -fn ethereum_address_raw(pubkey: &[u8]) -> StdResult<[u8; 20]> { - let (tag, data) = match pubkey.split_first() { - Some(pair) => pair, - None => return Err(StdError::generic_err("Public key must not be empty")), - }; - if *tag != 0x04 { - return Err(StdError::generic_err("Public key must start with 0x04")); - } - if data.len() != 64 { - return Err(StdError::generic_err("Public key must be 65 bytes long")); - } - - let hash = Keccak256::digest(data); - Ok(hash[hash.len() - 20..].try_into().unwrap()) -} - -/// Returns a raw 20 byte Ethereum address from hex -pub fn decode_address(input: &str) -> StdResult<[u8; 20]> { - if input.len() != 42 { - return Err(StdError::generic_err( - "Ethereum address must be 42 characters long", - )); - } - if !input.starts_with("0x") { - return Err(StdError::generic_err("Ethereum address must start wit 0x")); - } - let data = hex::decode(&input[2..]).map_err(|_| StdError::generic_err("hex decoding error"))?; - Ok(data.try_into().unwrap()) -} - -/// Returns a raw 65 byte Ethereum signature from hex -pub fn decode_signature(input: &str) -> StdResult<[u8; 65]> { - if input.len() != 132 { - return Err(StdError::generic_err( - "Ethereum signature must be 132 characters long", - )); - } - if !input.starts_with("0x") { - return Err(StdError::generic_err( - "Ethereum signature must start wit 0x", - )); - } - let data = hex::decode(&input[2..]).map_err(|_| StdError::generic_err("hex decoding error"))?; - Ok(data.try_into().unwrap()) -} - -pub fn verify_cosmos( - deps: Deps, - claim_msg: &ClaimMsg, - signature: Binary, -) -> Result { - let msg_raw = to_vec(claim_msg)?; - let hash = Sha256::digest(&msg_raw); - let sig: CosmosSignature = from_binary(&signature).unwrap(); - - let result = deps - .api - .secp256k1_verify( - hash.as_ref(), - sig.signature.as_slice(), - sig.pub_key.as_slice(), - ) - .map_err(|err| ContractError::IsNotEligible { - msg: err.to_string(), - }); - return result; -} - -/* -{"pub_key": "Aoz4+N8ckpDiz4oOKKKVERJGeS49nOgGrXw0s0dkymDr","signature":"CvMkqkQHVPV3DTyVErth16OdTjAwqQD6t+r9ImSpdwUZFin+UPeGSfH9hhuAmqYAp4CffhNNSEisdfzwwvlN/w=="} - */ - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct CosmosSignature { - pub_key: Binary, - signature: Binary, -} diff --git a/contracts/cw-cyber-airdrop/src/tests.rs b/contracts/cw-cyber-airdrop/src/tests.rs deleted file mode 100644 index 04dcb62..0000000 --- a/contracts/cw-cyber-airdrop/src/tests.rs +++ /dev/null @@ -1,442 +0,0 @@ -#[cfg(test)] -mod tests { - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - use cosmwasm_std::{ - attr, from_binary, from_slice, BankMsg, Binary, Coin, CosmosMsg, Env, SubMsg, Uint128, - Uint64, - }; - use serde::Deserialize; - - use crate::execute::*; - use crate::msg::{ - ClaimMsg, ClaimerType, ConfigResponse, ExecuteMsg, InstantiateMsg, IsClaimedResponse, - MerkleRootResponse, QueryMsg, ReleaseStateResponse, - }; - use crate::ContractError; - use std::ops::Mul; - - const NATIVE_TOKEN: &str = "boot"; - - fn mock_env_time(time_delta: u64) -> Env { - let mut env = mock_env(); - env.block.time = env.block.time.plus_seconds(time_delta); - env - } - - #[test] - fn proper_instantiation() { - let mut deps = mock_dependencies(); - - let msg = InstantiateMsg { - owner: Some("owner0000".to_string()), - passport: "passport".to_string(), - target_claim: Uint64::new(4), - allowed_native: NATIVE_TOKEN.to_string(), - initial_balance: Uint128::new(10000000000000), - coefficient_up: Uint128::new(20), - coefficient_down: Uint128::new(5), - coefficient: Uint128::new(20), - }; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: NATIVE_TOKEN.to_string(), - amount: Uint128::new(10000000000000), - }], - ); - - // we can just call .unwrap() to assert this was a success - let _res = instantiate(deps.as_mut(), env.clone(), info, msg).unwrap(); - - // it worked, let's query the state - let res = query(deps.as_ref(), env, QueryMsg::Config {}).unwrap(); - let config: ConfigResponse = from_binary(&res).unwrap(); - assert_eq!("owner0000", config.owner.unwrap().as_str()); - assert_eq!("boot", config.allowed_native.as_str()); - } - - #[test] - fn update_owner() { - let mut deps = mock_dependencies(); - - let msg = InstantiateMsg { - owner: None, - passport: "passport".to_string(), - target_claim: Uint64::new(4), - allowed_native: NATIVE_TOKEN.to_string(), - initial_balance: Uint128::new(100), - coefficient_up: Default::default(), - coefficient_down: Default::default(), - coefficient: Default::default(), - }; - - let env = mock_env(); - let info = mock_info( - "owner0000", - &[Coin { - denom: NATIVE_TOKEN.to_string(), - amount: Uint128::new(100), - }], - ); - let _res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - - // update owner - let env = mock_env(); - let info = mock_info("owner0000", &[]); - let msg = ExecuteMsg::UpdateOwner { - new_owner: Some("owner0001".to_string()), - }; - - let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - - // it worked, let's query the state - let res = query(deps.as_ref(), env, QueryMsg::Config {}).unwrap(); - let config: ConfigResponse = from_binary(&res).unwrap(); - assert_eq!("owner0001", config.owner.unwrap().as_str()); - - // Unauthorized err - let env = mock_env(); - let info = mock_info("owner0000", &[]); - let msg = ExecuteMsg::UpdateOwner { new_owner: None }; - - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, ContractError::Unauthorized {}); - } - - // TODO write update_passport - // TODO write update_target - - #[test] - fn register_merkle_root() { - let mut deps = mock_dependencies(); - - let msg = InstantiateMsg { - owner: Some("owner0000".to_string()), - passport: "passport".to_string(), - target_claim: Uint64::new(4), - allowed_native: NATIVE_TOKEN.to_string(), - initial_balance: Default::default(), - coefficient_up: Default::default(), - coefficient_down: Default::default(), - coefficient: Default::default(), - }; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: NATIVE_TOKEN.to_string(), - amount: Uint128::new(100), - }], - ); - let _res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - - // register new merkle root - let env = mock_env(); - let info = mock_info("owner0000", &[]); - let msg = ExecuteMsg::RegisterMerkleRoot { - merkle_root: "634de21cde1044f41d90373733b0f0fb1c1c71f9652b905cdf159e73c4cf0d37" - .to_string(), - }; - - let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); - assert_eq!( - res.attributes, - vec![ - attr("action", "register_merkle_root"), - attr( - "merkle_root", - "634de21cde1044f41d90373733b0f0fb1c1c71f9652b905cdf159e73c4cf0d37" - ) - ] - ); - - let res = query(deps.as_ref(), env, QueryMsg::MerkleRoot {}).unwrap(); - let merkle_root: MerkleRootResponse = from_binary(&res).unwrap(); - assert_eq!( - "634de21cde1044f41d90373733b0f0fb1c1c71f9652b905cdf159e73c4cf0d37".to_string(), - merkle_root.merkle_root - ); - } - - const ETH_TEST: &[u8] = - include_bytes!("../testdata/airdrop_stage_1_test_data_ethereum_address.json"); - const COSMOS_TEST: &[u8] = - include_bytes!("../testdata/airdrop_stage_1_test_data_cosmos_address.json"); - - #[derive(Deserialize, Debug)] - struct Encoded { - claim_msg: Binary, - signature: Binary, - amount: Uint128, - root: String, - proofs: Vec, - } - - #[test] - fn claim() { - // Case #1 - Claim with Ethereum - - let mut deps = mock_dependencies(); - let eth_test_data: Encoded = from_slice(ETH_TEST).unwrap(); - - let msg = InstantiateMsg { - owner: Some("owner0000".to_string()), - passport: "passport".to_string(), - target_claim: Uint64::new(2), - allowed_native: NATIVE_TOKEN.to_string(), - initial_balance: Uint128::new(10000000000000), - coefficient_up: Uint128::new(20), - coefficient_down: Uint128::new(5), - coefficient: Uint128::new(20), - }; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: NATIVE_TOKEN.to_string(), - amount: Uint128::new(10000000000000), - }], - ); - let _res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - - let env = mock_env(); - let info = mock_info("owner0000", &[]); - let msg = ExecuteMsg::RegisterMerkleRoot { - merkle_root: eth_test_data.root, - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - let claim_msg_ethereum = from_binary(ð_test_data.claim_msg).unwrap(); - let msg = ExecuteMsg::Claim { - claim_msg: claim_msg_ethereum, - signature: eth_test_data.signature, - proof: eth_test_data.proofs, - claim_amount: eth_test_data.amount, - }; - - let env = mock_env(); - let info = mock_info("addr0001", &[]); - let res = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone()).unwrap(); - - let claim_msg_ethereum: ClaimMsg = from_binary(ð_test_data.claim_msg).unwrap(); - let expected = SubMsg::new(CosmosMsg::Bank(BankMsg::Send { - to_address: claim_msg_ethereum.clone().target_address, - amount: vec![Coin { - denom: NATIVE_TOKEN.to_string(), - amount: Uint128::new(100000), - }], - })); - assert_eq!(res.messages, vec![expected]); - - assert_eq!( - res.attributes, - vec![ - attr("action", "claim"), - attr("original", claim_msg_ethereum.clone().gift_claiming_address), - attr("type", ClaimerType::Ethereum.to_string()), - attr("target", claim_msg_ethereum.clone().target_address), - attr("amount", eth_test_data.amount.u128().mul(20).to_string()) - ] - ); - - assert!( - from_binary::( - &query( - deps.as_ref(), - env.clone(), - QueryMsg::IsClaimed { - address: claim_msg_ethereum.target_address - } - ) - .unwrap() - ) - .unwrap() - .is_claimed - ); - - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, ContractError::Claimed {}); - - // Case #2 - Claim with Cosmos - - let cosmos_test_data: Encoded = from_slice(COSMOS_TEST).unwrap(); - - let claim_msg_cosmos: ClaimMsg = from_binary(&cosmos_test_data.claim_msg).unwrap(); - let msg = ExecuteMsg::Claim { - claim_msg: claim_msg_cosmos, - signature: cosmos_test_data.signature, - proof: cosmos_test_data.proofs, - claim_amount: cosmos_test_data.amount, - }; - - let env = mock_env(); - let info = mock_info("addr0002", &[]); - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - - let claim_msg_cosmos: ClaimMsg = from_binary(&cosmos_test_data.claim_msg).unwrap(); - let expected = SubMsg::new(CosmosMsg::Bank(BankMsg::Send { - to_address: claim_msg_cosmos.clone().target_address, - amount: vec![Coin { - denom: NATIVE_TOKEN.to_string(), - amount: Uint128::new(100000), - }], - })); - assert_eq!(res.messages, vec![expected]); - - assert_eq!( - res.attributes, - vec![ - attr("action", "claim"), - attr("original", claim_msg_cosmos.clone().gift_claiming_address), - attr("type", ClaimerType::Cosmos.to_string()), - attr("target", claim_msg_cosmos.clone().target_address), - attr("amount", cosmos_test_data.amount.u128().mul(20).to_string()) - ] - ); - - let env = mock_env(); - let res = query(deps.as_ref(), env, QueryMsg::Config {}).unwrap(); - let config: ConfigResponse = from_binary(&res).unwrap(); - assert_eq!(2, config.claims.u64()); - - let claim_msg: ClaimMsg = from_binary(ð_test_data.claim_msg).unwrap(); - let env = mock_env(); - let info = mock_info(claim_msg.target_address.as_str(), &[]); - let res = execute(deps.as_mut(), env, info, ExecuteMsg::Release {}).unwrap(); - println!("{:?}", res); - - let claim_msg: ClaimMsg = from_binary(&cosmos_test_data.claim_msg).unwrap(); - let env = mock_env(); - let info = mock_info(claim_msg.target_address.as_str(), &[]); - let res = execute(deps.as_mut(), env, info, ExecuteMsg::Release {}).unwrap(); - println!("{:?}", res); - - let claim_msg: ClaimMsg = from_binary(ð_test_data.claim_msg).unwrap(); - let env = mock_env(); - let res = query( - deps.as_ref(), - env, - QueryMsg::ReleaseState { - address: claim_msg.target_address, - }, - ) - .unwrap(); - let release_state: ReleaseStateResponse = from_binary(&res).unwrap(); - println!("{:?}", release_state); - - let claim_msg: ClaimMsg = from_binary(&cosmos_test_data.claim_msg).unwrap(); - let env = mock_env(); - let res = query( - deps.as_ref(), - env, - QueryMsg::ReleaseState { - address: claim_msg.target_address, - }, - ) - .unwrap(); - let release_state: ReleaseStateResponse = from_binary(&res).unwrap(); - println!("{:?}", release_state); - - let claim_msg: ClaimMsg = from_binary(ð_test_data.claim_msg).unwrap(); - let env = mock_env(); - let info = mock_info(claim_msg.target_address.as_str(), &[]); - let res = execute(deps.as_mut(), env, info, ExecuteMsg::Release {}).unwrap_err(); - println!("{:?}", res); - - let claim_msg: ClaimMsg = from_binary(&cosmos_test_data.claim_msg).unwrap(); - let env = mock_env(); - let info = mock_info(claim_msg.target_address.as_str(), &[]); - let res = execute(deps.as_mut(), env, info, ExecuteMsg::Release {}).unwrap_err(); - println!("{:?}", res); - - let claim_msg: ClaimMsg = from_binary(ð_test_data.claim_msg).unwrap(); - let env = mock_env_time(86400); - let info = mock_info(claim_msg.target_address.as_str(), &[]); - let res = execute(deps.as_mut(), env, info, ExecuteMsg::Release {}).unwrap(); - println!("{:?}", res); - - let claim_msg: ClaimMsg = from_binary(&cosmos_test_data.claim_msg).unwrap(); - let env = mock_env_time(86400); - let info = mock_info(claim_msg.target_address.as_str(), &[]); - let res = execute(deps.as_mut(), env, info, ExecuteMsg::Release {}).unwrap(); - println!("{:?}", res); - } - - #[test] - fn owner_freeze() { - let mut deps = mock_dependencies(); - - let msg = InstantiateMsg { - owner: Some("owner0000".to_string()), - passport: "passport".to_string(), - target_claim: Uint64::new(4), - allowed_native: NATIVE_TOKEN.to_string(), - initial_balance: Uint128::new(10000000000000), - coefficient_up: Uint128::new(20), - coefficient_down: Uint128::new(5), - coefficient: Uint128::new(20), - }; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: NATIVE_TOKEN.to_string(), - amount: Uint128::new(10000000000000), - }], - ); - let _res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - - // can register merkle root - let env = mock_env(); - let info = mock_info("owner0000", &[]); - let msg = ExecuteMsg::RegisterMerkleRoot { - merkle_root: "5d4f48f147cb6cb742b376dce5626b2a036f69faec10cd73631c791780e150fc" - .to_string(), - }; - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - // can update owner - let env = mock_env(); - let info = mock_info("owner0000", &[]); - let msg = ExecuteMsg::UpdateOwner { - new_owner: Some("owner0001".to_string()), - }; - - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - - // freeze contract - let env = mock_env(); - let info = mock_info("owner0001", &[]); - let msg = ExecuteMsg::UpdateOwner { new_owner: None }; - - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - - // cannot register new drop - let env = mock_env(); - let info = mock_info("owner0001", &[]); - let msg = ExecuteMsg::RegisterMerkleRoot { - merkle_root: "ebaa83c7eaf7467c378d2f37b5e46752d904d2d17acd380b24b02e3b398b3e5a" - .to_string(), - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, ContractError::Unauthorized {}); - - // cannot update config - let env = mock_env(); - let info = mock_info("owner0001", &[]); - let msg = ExecuteMsg::RegisterMerkleRoot { - merkle_root: "ebaa83c7eaf7467c378d2f37b5e46752d904d2d17acd380b24b02e3b398b3e5a" - .to_string(), - }; - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - assert_eq!(res, ContractError::Unauthorized {}); - } -} diff --git a/contracts/cw-cyber-airdrop/testdata/generate_test_data/index.ts b/contracts/cw-cyber-airdrop/testdata/generate_test_data/index.ts deleted file mode 100644 index bbd107d..0000000 --- a/contracts/cw-cyber-airdrop/testdata/generate_test_data/index.ts +++ /dev/null @@ -1,49 +0,0 @@ -import CryptoJS from "crypto-js"; -import sha256 from "crypto-js/sha256"; -import { MerkleTree } from "merkletreejs"; -import receivers from "./airdrop_stage_1_list.json"; - -interface Encoding { - target_address: string; - amount: string; -} -class Airdrop { - private tree: MerkleTree; - - constructor(accounts: Array) { - const leaves = accounts.map((a) => this.encode_data(a)); - this.tree = new MerkleTree(leaves, sha256, { sort: true }); - } - - encode_data(data: Encoding): CryptoJS.lib.WordArray { - return sha256( - data.target_address + data.amount - ); - } - - public getMerkleRoot(): string { - return this.tree.getRoot().toString("hex"); - } - - public getMerkleProof(data: Encoding): string[] { - return this.tree - .getProof(this.encode_data(data).toString()) - .map((v) => v.data.toString("hex")); - } - - public verify(proof: [string], data: Encoding): boolean { - return this.tree.verify( - proof, - this.encode_data(data).toString(), - this.tree.getRoot() - ); - } -} - -let airdrop = new Airdrop(receivers); - -console.log(airdrop.getMerkleRoot()); -console.log(receivers[0]) -console.log(airdrop.getMerkleProof(receivers[0])); -console.log(receivers[1]) -console.log(airdrop.getMerkleProof(receivers[1])); diff --git a/contracts/cw-cyber-gift/Cargo.toml b/contracts/cw-cyber-gift/Cargo.toml new file mode 100644 index 0000000..fc4a70e --- /dev/null +++ b/contracts/cw-cyber-gift/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "cw-cyber-gift" +version = "1.0.0" +authors = ["CyberHead"] +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[profile.release] +rpath = false +lto = true +overflow-checks = true +opt-level = 3 +debug = false +debug-assertions = false +codegen-units = 1 +incremental = false +panic = 'abort' + +[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] +anyhow = "1" +cw0 = "0.10.3" +cw2 = "0.13.4" +cw20 = "0.13.4" +cw1-subkeys = "0.13.4" +cosmwasm-std = { version = "1.0.0", features = ["staking"] } +cyber-std = { version = "0.2.1" } +cw-utils = { version = "0.13.4" } +cw-cyber-passport = { path = "../cw-cyber-passport", features = ["library"] } +cw-cyber-subgraph = { path = "../cw-cyber-subgraph", features = ["library"] } +cw-storage-plus = { version = "0.13.4" } +schemars = "0.8.8" +serde = { version = "1.0.137", default-features = false, features = ["derive"] } +thiserror = { version = "1.0.31" } +hex = "0.4" +sha2 = { version = "0.9.5", default-features = false } +semver = "1" + +[dev-dependencies] +cosmwasm-schema = { version = "1.0.0" } +cw-multi-test = { version = "0.13.4" } +cw1-whitelist = { version = "0.13.4" } +cyber-std-test = { version = "0.2.1" } diff --git a/contracts/cw-cyber-gift/README.md b/contracts/cw-cyber-gift/README.md new file mode 100644 index 0000000..c7fe754 --- /dev/null +++ b/contracts/cw-cyber-gift/README.md @@ -0,0 +1,10 @@ +# cw-cyber-gift + +## Generate root, proofs and verify proofs + +### cw20-merkle-airdrop helper +This is a helper client shipped along contract +[merkle-airdrop-cli](https://github.com/CosmWasm/cw-tokens/tree/main/contracts/cw20-merkle-airdrop/helpers) + +### Calculation of Merkle Root and Proofs for Final Distribution +[Jupyter notebook](testdata/generate_test_data/gift_final_merkle_tree.ipynb) diff --git a/contracts/cw-cyber-gift/examples/schema.rs b/contracts/cw-cyber-gift/examples/schema.rs new file mode 100644 index 0000000..6455b31 --- /dev/null +++ b/contracts/cw-cyber-gift/examples/schema.rs @@ -0,0 +1,23 @@ +use std::env::current_dir; +use std::fs::create_dir_all; + +use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; +use cw_cyber_gift::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, ConfigResponse, StateResponse, IsClaimedResponse, ClaimResponse, ReleaseStageStateResponse, AllReleaseStageStateResponse, MerkleRootResponse}; + +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(&schema_for!(ExecuteMsg), &out_dir); + export_schema(&schema_for!(QueryMsg), &out_dir); + export_schema(&schema_for!(MerkleRootResponse), &out_dir); + export_schema(&schema_for!(IsClaimedResponse), &out_dir); + export_schema(&schema_for!(ClaimResponse), &out_dir); + export_schema(&schema_for!(ConfigResponse), &out_dir); + export_schema(&schema_for!(StateResponse), &out_dir); + export_schema(&schema_for!(ReleaseStageStateResponse), &out_dir); + export_schema(&schema_for!(AllReleaseStageStateResponse), &out_dir); +} diff --git a/contracts/cw-cyber-gift/schema/all_release_stage_state_response.json b/contracts/cw-cyber-gift/schema/all_release_stage_state_response.json new file mode 100644 index 0000000..1d5cd47 --- /dev/null +++ b/contracts/cw-cyber-gift/schema/all_release_stage_state_response.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AllReleaseStageStateResponse", + "type": "object", + "required": [ + "releases" + ], + "properties": { + "releases": { + "type": "array", + "items": { + "$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" + } + } +} diff --git a/contracts/cw-cyber-airdrop/schema/config_response.json b/contracts/cw-cyber-gift/schema/claim_response.json similarity index 66% rename from contracts/cw-cyber-airdrop/schema/config_response.json rename to contracts/cw-cyber-gift/schema/claim_response.json index 1d6b80c..546a97b 100644 --- a/contracts/cw-cyber-airdrop/schema/config_response.json +++ b/contracts/cw-cyber-gift/schema/claim_response.json @@ -1,39 +1,17 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ConfigResponse", + "title": "ClaimResponse", "type": "object", "required": [ - "allowed_native", - "coefficient", - "coefficient_down", - "coefficient_up", - "current_balance", - "initial_balance" + "claim", + "multiplier" ], "properties": { - "allowed_native": { - "type": "string" - }, - "coefficient": { - "$ref": "#/definitions/Decimal" - }, - "coefficient_down": { - "$ref": "#/definitions/Uint128" - }, - "coefficient_up": { + "claim": { "$ref": "#/definitions/Uint128" }, - "current_balance": { - "$ref": "#/definitions/Uint128" - }, - "initial_balance": { - "$ref": "#/definitions/Uint128" - }, - "owner": { - "type": [ - "string", - "null" - ] + "multiplier": { + "$ref": "#/definitions/Decimal" } }, "definitions": { diff --git a/contracts/cw-cyber-gift/schema/config_response.json b/contracts/cw-cyber-gift/schema/config_response.json new file mode 100644 index 0000000..a3dcd6a --- /dev/null +++ b/contracts/cw-cyber-gift/schema/config_response.json @@ -0,0 +1,49 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigResponse", + "type": "object", + "required": [ + "allowed_native", + "coefficient_down", + "coefficient_up", + "initial_balance", + "passport", + "target_claim" + ], + "properties": { + "allowed_native": { + "type": "string" + }, + "coefficient_down": { + "$ref": "#/definitions/Uint128" + }, + "coefficient_up": { + "$ref": "#/definitions/Uint128" + }, + "initial_balance": { + "$ref": "#/definitions/Uint128" + }, + "owner": { + "type": [ + "string", + "null" + ] + }, + "passport": { + "type": "string" + }, + "target_claim": { + "$ref": "#/definitions/Uint64" + } + }, + "definitions": { + "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" + } + } +} diff --git a/contracts/cw-cyber-gift/schema/execute_msg.json b/contracts/cw-cyber-gift/schema/execute_msg.json new file mode 100644 index 0000000..feb7f3c --- /dev/null +++ b/contracts/cw-cyber-gift/schema/execute_msg.json @@ -0,0 +1,1420 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "execute" + ], + "properties": { + "execute": { + "type": "object", + "required": [ + "msgs" + ], + "properties": { + "msgs": { + "type": "array", + "items": { + "$ref": "#/definitions/CosmosMsg_for_CyberMsgWrapper" + } + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_owner" + ], + "properties": { + "update_owner": { + "type": "object", + "properties": { + "new_owner": { + "type": [ + "string", + "null" + ] + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_treasury_addr" + ], + "properties": { + "update_treasury_addr": { + "type": "object", + "required": [ + "new_treasury_addr" + ], + "properties": { + "new_treasury_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_target" + ], + "properties": { + "update_target": { + "type": "object", + "required": [ + "new_target" + ], + "properties": { + "new_target": { + "$ref": "#/definitions/Uint64" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "register_merkle_root" + ], + "properties": { + "register_merkle_root": { + "type": "object", + "required": [ + "merkle_root" + ], + "properties": { + "merkle_root": { + "description": "MerkleRoot is hex-encoded merkle root.", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "claim" + ], + "properties": { + "claim": { + "type": "object", + "required": [ + "gift_amount", + "gift_claiming_address", + "nickname", + "proof" + ], + "properties": { + "gift_amount": { + "$ref": "#/definitions/Uint128" + }, + "gift_claiming_address": { + "type": "string" + }, + "nickname": { + "type": "string" + }, + "proof": { + "description": "Proof is hex-encoded merkle proof.", + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "release" + ], + "properties": { + "release": { + "type": "object", + "required": [ + "gift_address" + ], + "properties": { + "gift_address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ], + "definitions": { + "BankMsg": { + "description": "The message types of the bank module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto", + "oneOf": [ + { + "description": "Sends native tokens from the contract to the given address.\n\nThis is translated to a [MsgSend](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L19-L28). `from_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "send" + ], + "properties": { + "send": { + "type": "object", + "required": [ + "amount", + "to_address" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "to_address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will burn the given coins from the contract's account. There is no Cosmos SDK message that performs this, but it can be done by calling the bank keeper. Important if a contract controls significant token supply that must be retired.", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "CosmosMsg_for_CyberMsgWrapper": { + "oneOf": [ + { + "type": "object", + "required": [ + "bank" + ], + "properties": { + "bank": { + "$ref": "#/definitions/BankMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "custom" + ], + "properties": { + "custom": { + "$ref": "#/definitions/CyberMsgWrapper" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "staking" + ], + "properties": { + "staking": { + "$ref": "#/definitions/StakingMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "distribution" + ], + "properties": { + "distribution": { + "$ref": "#/definitions/DistributionMsg" + } + }, + "additionalProperties": false + }, + { + "description": "A Stargate message encoded the same way as a protobuf [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). This is the same structure as messages in `TxBody` from [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md)", + "type": "object", + "required": [ + "stargate" + ], + "properties": { + "stargate": { + "type": "object", + "required": [ + "type_url", + "value" + ], + "properties": { + "type_url": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/Binary" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "ibc" + ], + "properties": { + "ibc": { + "$ref": "#/definitions/IbcMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "wasm" + ], + "properties": { + "wasm": { + "$ref": "#/definitions/WasmMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "gov" + ], + "properties": { + "gov": { + "$ref": "#/definitions/GovMsg" + } + }, + "additionalProperties": false + } + ] + }, + "CyberMsg": { + "oneOf": [ + { + "type": "object", + "required": [ + "cyberlink" + ], + "properties": { + "cyberlink": { + "type": "object", + "required": [ + "links", + "neuron" + ], + "properties": { + "links": { + "type": "array", + "items": { + "$ref": "#/definitions/Link" + } + }, + "neuron": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "investmint" + ], + "properties": { + "investmint": { + "type": "object", + "required": [ + "amount", + "length", + "neuron", + "resource" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "length": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "neuron": { + "type": "string" + }, + "resource": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "create_energy_route" + ], + "properties": { + "create_energy_route": { + "type": "object", + "required": [ + "destination", + "name", + "source" + ], + "properties": { + "destination": { + "type": "string" + }, + "name": { + "type": "string" + }, + "source": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "edit_energy_route" + ], + "properties": { + "edit_energy_route": { + "type": "object", + "required": [ + "destination", + "source", + "value" + ], + "properties": { + "destination": { + "type": "string" + }, + "source": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/Coin" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "edit_energy_route_name" + ], + "properties": { + "edit_energy_route_name": { + "type": "object", + "required": [ + "destination", + "name", + "source" + ], + "properties": { + "destination": { + "type": "string" + }, + "name": { + "type": "string" + }, + "source": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "delete_energy_route" + ], + "properties": { + "delete_energy_route": { + "type": "object", + "required": [ + "destination", + "source" + ], + "properties": { + "destination": { + "type": "string" + }, + "source": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "create_thought" + ], + "properties": { + "create_thought": { + "type": "object", + "required": [ + "load", + "name", + "particle", + "program", + "trigger" + ], + "properties": { + "load": { + "$ref": "#/definitions/Load" + }, + "name": { + "type": "string" + }, + "particle": { + "type": "string" + }, + "program": { + "type": "string" + }, + "trigger": { + "$ref": "#/definitions/Trigger" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "forget_thought" + ], + "properties": { + "forget_thought": { + "type": "object", + "required": [ + "name", + "program" + ], + "properties": { + "name": { + "type": "string" + }, + "program": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "change_thought_input" + ], + "properties": { + "change_thought_input": { + "type": "object", + "required": [ + "input", + "name", + "program" + ], + "properties": { + "input": { + "type": "string" + }, + "name": { + "type": "string" + }, + "program": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "change_thought_period" + ], + "properties": { + "change_thought_period": { + "type": "object", + "required": [ + "name", + "period", + "program" + ], + "properties": { + "name": { + "type": "string" + }, + "period": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "program": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "change_thought_block" + ], + "properties": { + "change_thought_block": { + "type": "object", + "required": [ + "block", + "name", + "program" + ], + "properties": { + "block": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "name": { + "type": "string" + }, + "program": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "create_pool" + ], + "properties": { + "create_pool": { + "type": "object", + "required": [ + "deposit_coins", + "pool_creator_address", + "pool_type_id" + ], + "properties": { + "deposit_coins": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "pool_creator_address": { + "type": "string" + }, + "pool_type_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "deposit_within_batch" + ], + "properties": { + "deposit_within_batch": { + "type": "object", + "required": [ + "deposit_coins", + "depositor_address", + "pool_id" + ], + "properties": { + "deposit_coins": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "depositor_address": { + "type": "string" + }, + "pool_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "withdraw_within_batch" + ], + "properties": { + "withdraw_within_batch": { + "type": "object", + "required": [ + "pool_coin", + "pool_id", + "withdrawer_address" + ], + "properties": { + "pool_coin": { + "$ref": "#/definitions/Coin" + }, + "pool_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "withdrawer_address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "swap_within_batch" + ], + "properties": { + "swap_within_batch": { + "type": "object", + "required": [ + "demand_coin_denom", + "offer_coin", + "offer_coin_fee", + "order_price", + "pool_id", + "swap_requester_address", + "swap_type_id" + ], + "properties": { + "demand_coin_denom": { + "type": "string" + }, + "offer_coin": { + "$ref": "#/definitions/Coin" + }, + "offer_coin_fee": { + "$ref": "#/definitions/Coin" + }, + "order_price": { + "$ref": "#/definitions/Decimal" + }, + "pool_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "swap_requester_address": { + "type": "string" + }, + "swap_type_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + } + ] + }, + "CyberMsgWrapper": { + "type": "object", + "required": [ + "msg_data", + "route" + ], + "properties": { + "msg_data": { + "$ref": "#/definitions/CyberMsg" + }, + "route": { + "$ref": "#/definitions/CyberRoute" + } + } + }, + "CyberRoute": { + "description": "CyberRoute is enum type to represent cyber query route path", + "type": "string", + "enum": [ + "rank", + "graph", + "resources", + "grid", + "dmn", + "bandwidth", + "liquidity" + ] + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "DistributionMsg": { + "description": "The message types of the distribution module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "set_withdraw_address" + ], + "properties": { + "set_withdraw_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The `withdraw_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "withdraw_delegator_reward" + ], + "properties": { + "withdraw_delegator_reward": { + "type": "object", + "required": [ + "validator" + ], + "properties": { + "validator": { + "description": "The `validator_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "GovMsg": { + "oneOf": [ + { + "description": "This maps directly to [MsgVote](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/gov/v1beta1/tx.proto#L46-L56) in the Cosmos SDK with voter set to the contract address.", + "type": "object", + "required": [ + "vote" + ], + "properties": { + "vote": { + "type": "object", + "required": [ + "proposal_id", + "vote" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "vote": { + "$ref": "#/definitions/VoteOption" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcMsg": { + "description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)", + "oneOf": [ + { + "description": "Sends bank tokens owned by the contract to the given address on another chain. The channel must already be established between the ibctransfer module on this chain and a matching module on the remote chain. We cannot select the port_id, this is whatever the local chain has bound the ibctransfer module to.", + "type": "object", + "required": [ + "transfer" + ], + "properties": { + "transfer": { + "type": "object", + "required": [ + "amount", + "channel_id", + "timeout", + "to_address" + ], + "properties": { + "amount": { + "description": "packet data only supports one coin https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "channel_id": { + "description": "exisiting channel to send the tokens over", + "type": "string" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + }, + "to_address": { + "description": "address on the remote chain to receive these tokens", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sends an IBC packet with given data over the existing channel. Data should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.", + "type": "object", + "required": [ + "send_packet" + ], + "properties": { + "send_packet": { + "type": "object", + "required": [ + "channel_id", + "data", + "timeout" + ], + "properties": { + "channel_id": { + "type": "string" + }, + "data": { + "$ref": "#/definitions/Binary" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will close an existing channel that is owned by this contract. Port is auto-assigned to the contract's IBC port", + "type": "object", + "required": [ + "close_channel" + ], + "properties": { + "close_channel": { + "type": "object", + "required": [ + "channel_id" + ], + "properties": { + "channel_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcTimeout": { + "description": "In IBC each package must set at least one type of timeout: the timestamp or the block height. Using this rather complex enum instead of two timeout fields we ensure that at least one timeout is set.", + "type": "object", + "properties": { + "block": { + "anyOf": [ + { + "$ref": "#/definitions/IbcTimeoutBlock" + }, + { + "type": "null" + } + ] + }, + "timestamp": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + } + }, + "IbcTimeoutBlock": { + "description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)", + "type": "object", + "required": [ + "height", + "revision" + ], + "properties": { + "height": { + "description": "block height after which the packet times out. the height within the given revision", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "revision": { + "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, + "Link": { + "type": "object", + "required": [ + "from", + "to" + ], + "properties": { + "from": { + "type": "string" + }, + "to": { + "type": "string" + } + } + }, + "Load": { + "type": "object", + "required": [ + "gas_price", + "input" + ], + "properties": { + "gas_price": { + "$ref": "#/definitions/Coin" + }, + "input": { + "type": "string" + } + } + }, + "StakingMsg": { + "description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "delegate" + ], + "properties": { + "delegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "undelegate" + ], + "properties": { + "undelegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "redelegate" + ], + "properties": { + "redelegate": { + "type": "object", + "required": [ + "amount", + "dst_validator", + "src_validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "dst_validator": { + "type": "string" + }, + "src_validator": { + "type": "string" + } + } + } + }, + "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" + } + ] + }, + "Trigger": { + "type": "object", + "required": [ + "block", + "period" + ], + "properties": { + "block": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "period": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, + "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" + }, + "VoteOption": { + "type": "string", + "enum": [ + "yes", + "no", + "abstain", + "no_with_veto" + ] + }, + "WasmMsg": { + "description": "The message types of the wasm module.\n\nSee https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto", + "oneOf": [ + { + "description": "Dispatches a call to another contract at a known address (with known ABI).\n\nThis is translated to a [MsgExecuteContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L68-L78). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "execute" + ], + "properties": { + "execute": { + "type": "object", + "required": [ + "contract_addr", + "funds", + "msg" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "msg": { + "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Instantiates a new contracts from previously uploaded Wasm code.\n\nThis is translated to a [MsgInstantiateContract](https://github.com/CosmWasm/wasmd/blob/v0.16.0-alpha1/x/wasm/internal/types/tx.proto#L47-L61). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "instantiate" + ], + "properties": { + "instantiate": { + "type": "object", + "required": [ + "code_id", + "funds", + "label", + "msg" + ], + "properties": { + "admin": { + "type": [ + "string", + "null" + ] + }, + "code_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "label": { + "description": "A human-readbale label for the contract", + "type": "string" + }, + "msg": { + "description": "msg is the JSON-encoded InstantiateMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Migrates a given contracts to use new wasm code. Passes a MigrateMsg to allow us to customize behavior.\n\nOnly the contract admin (as defined in wasmd), if any, is able to make this call.\n\nThis is translated to a [MsgMigrateContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L86-L96). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "migrate" + ], + "properties": { + "migrate": { + "type": "object", + "required": [ + "contract_addr", + "msg", + "new_code_id" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "msg": { + "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + }, + "new_code_id": { + "description": "the code_id of the new logic to place in the given contract", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sets a new admin (for migrate) on the given contract. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "update_admin" + ], + "properties": { + "update_admin": { + "type": "object", + "required": [ + "admin", + "contract_addr" + ], + "properties": { + "admin": { + "type": "string" + }, + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Clears the admin on the given contract, so no more migration possible. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "clear_admin" + ], + "properties": { + "clear_admin": { + "type": "object", + "required": [ + "contract_addr" + ], + "properties": { + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + } + } +} diff --git a/contracts/cw-cyber-airdrop/schema/instantiate_msg.json b/contracts/cw-cyber-gift/schema/instantiate_msg.json similarity index 62% rename from contracts/cw-cyber-airdrop/schema/instantiate_msg.json rename to contracts/cw-cyber-gift/schema/instantiate_msg.json index 4a00d87..f80f97b 100644 --- a/contracts/cw-cyber-airdrop/schema/instantiate_msg.json +++ b/contracts/cw-cyber-gift/schema/instantiate_msg.json @@ -7,7 +7,10 @@ "coefficient", "coefficient_down", "coefficient_up", - "initial_balance" + "initial_balance", + "passport", + "target_claim", + "treasury" ], "properties": { "allowed_native": { @@ -26,17 +29,29 @@ "$ref": "#/definitions/Uint128" }, "owner": { - "description": "Owner if none set to info.sender.", "type": [ "string", "null" ] + }, + "passport": { + "type": "string" + }, + "target_claim": { + "$ref": "#/definitions/Uint64" + }, + "treasury": { + "type": "string" } }, "definitions": { "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" } } } diff --git a/contracts/cw-cyber-airdrop/schema/is_claimed_response.json b/contracts/cw-cyber-gift/schema/is_claimed_response.json similarity index 100% rename from contracts/cw-cyber-airdrop/schema/is_claimed_response.json rename to contracts/cw-cyber-gift/schema/is_claimed_response.json diff --git a/contracts/cw-cyber-airdrop/schema/merkle_root_response.json b/contracts/cw-cyber-gift/schema/merkle_root_response.json similarity index 100% rename from contracts/cw-cyber-airdrop/schema/merkle_root_response.json rename to contracts/cw-cyber-gift/schema/merkle_root_response.json diff --git a/contracts/cw-cyber-gift/schema/query_msg.json b/contracts/cw-cyber-gift/schema/query_msg.json new file mode 100644 index 0000000..838ce39 --- /dev/null +++ b/contracts/cw-cyber-gift/schema/query_msg.json @@ -0,0 +1,158 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "type": "object" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "state" + ], + "properties": { + "state": { + "type": "object" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "merkle_root" + ], + "properties": { + "merkle_root": { + "type": "object" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "is_claimed" + ], + "properties": { + "is_claimed": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "claim" + ], + "properties": { + "claim": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "release_state" + ], + "properties": { + "release_state": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "release_stage_state" + ], + "properties": { + "release_stage_state": { + "type": "object", + "required": [ + "stage" + ], + "properties": { + "stage": { + "$ref": "#/definitions/Uint64" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "all_release_stage_state" + ], + "properties": { + "all_release_stage_state": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint8", + "minimum": 0.0 + }, + "start": { + "type": [ + "integer", + "null" + ], + "format": "uint8", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + } + ], + "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" + } + } +} diff --git a/contracts/cw-cyber-gift/schema/release_stage_state_response.json b/contracts/cw-cyber-gift/schema/release_stage_state_response.json new file mode 100644 index 0000000..182facb --- /dev/null +++ b/contracts/cw-cyber-gift/schema/release_stage_state_response.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ReleaseStageStateResponse", + "type": "object", + "required": [ + "releases" + ], + "properties": { + "releases": { + "$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" + } + } +} diff --git a/contracts/cw-cyber-gift/schema/state_response.json b/contracts/cw-cyber-gift/schema/state_response.json new file mode 100644 index 0000000..8916121 --- /dev/null +++ b/contracts/cw-cyber-gift/schema/state_response.json @@ -0,0 +1,39 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "StateResponse", + "type": "object", + "required": [ + "claims", + "coefficient", + "current_balance", + "releases" + ], + "properties": { + "claims": { + "$ref": "#/definitions/Uint64" + }, + "coefficient": { + "$ref": "#/definitions/Decimal" + }, + "current_balance": { + "$ref": "#/definitions/Uint128" + }, + "releases": { + "$ref": "#/definitions/Uint64" + } + }, + "definitions": { + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "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" + } + } +} diff --git a/contracts/cw-cyber-gift/src/contract.rs b/contracts/cw-cyber-gift/src/contract.rs new file mode 100644 index 0000000..5eda0c2 --- /dev/null +++ b/contracts/cw-cyber-gift/src/contract.rs @@ -0,0 +1,127 @@ +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{to_binary, Binary, Decimal, Deps, DepsMut, Env, StdResult, Uint64, MessageInfo, Empty}; +use cw2::{get_contract_version, set_contract_version}; + +use crate::error::ContractError; +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::state::{Config, CONFIG, State, STATE}; +use crate::execute::{execute_claim, execute_execute, execute_register_merkle_root, execute_release, execute_update_owner, execute_update_target, execute_update_treasury}; +use crate::query::{query_all_release_stage_state, query_claim, query_config, query_is_claimed, query_merkle_root, query_release_stage_state, query_release_state, query_state}; +use cyber_std::CyberMsgWrapper; +use semver::Version; + +type Response = cosmwasm_std::Response; + +// Version info, for migration info +const CONTRACT_NAME: &str = "cyber-gift"; +const CONTRACT_VERSION: &str = "1.0.0"; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + info: MessageInfo, + msg: InstantiateMsg, +) -> StdResult { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + let owner = msg + .owner + .map_or(Ok(info.sender), |o| deps.api.addr_validate(&o))?; + + let config = Config { + owner: Some(owner), + passport_addr: deps.api.addr_validate(&msg.passport)?, + treasury_addr: deps.api.addr_validate(&msg.treasury)?, + target_claim: msg.target_claim, + allowed_native: msg.allowed_native, + initial_balance: msg.initial_balance, + coefficient_up: msg.coefficient_up, + coefficient_down: msg.coefficient_down, + }; + + let state = State { + current_balance: msg.initial_balance, + coefficient: Decimal::from_ratio(msg.coefficient, 1u128), + claims: Uint64::zero(), + releases: Uint64::zero() + }; + + CONFIG.save(deps.storage, &config)?; + STATE.save(deps.storage, &state)?; + + Ok(Response::default()) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::Execute { msgs } => execute_execute(deps, env, info, msgs), + ExecuteMsg::UpdateOwner { new_owner } => execute_update_owner(deps, env, info, new_owner), + ExecuteMsg::UpdateTreasuryAddr { new_treasury_addr: new_treasury } => { + execute_update_treasury(deps, env, info, new_treasury) + } + ExecuteMsg::UpdateTarget { new_target } => { + execute_update_target(deps, env, info, new_target) + } + ExecuteMsg::RegisterMerkleRoot { merkle_root } => { + execute_register_merkle_root(deps, env, info, merkle_root) + } + ExecuteMsg::Claim { + nickname, + gift_claiming_address, + gift_amount, + proof, + } => execute_claim(deps, env, info, nickname, gift_claiming_address, gift_amount, proof), + ExecuteMsg::Release { gift_address } => execute_release(deps, env, info, gift_address), + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::Config {} => to_binary(&query_config(deps)?), + QueryMsg::State {} => to_binary(&query_state(deps)?), + QueryMsg::MerkleRoot {} => to_binary(&query_merkle_root(deps)?), + QueryMsg::IsClaimed { address } => to_binary(&query_is_claimed(deps, address)?), + QueryMsg::Claim { address } => to_binary(&query_claim(deps, address)?), + QueryMsg::ReleaseState { address } => to_binary(&query_release_state(deps, address)?), + QueryMsg::ReleaseStageState { stage } => to_binary(&query_release_stage_state(deps, stage)?), + QueryMsg::AllReleaseStageState {start, limit} => to_binary(&query_all_release_stage_state(deps, start, limit)?), + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate( + deps: DepsMut, + _env: Env, + _msg: Empty, +) -> Result { + let stored = get_contract_version(deps.storage)?; + if stored.contract != CONTRACT_NAME { + return Err(ContractError::CannotMigrate { + previous_contract: stored.contract, + }); + } + + let version: Version = CONTRACT_VERSION.parse()?; + let storage_version: Version = get_contract_version(deps.storage)?.version.parse()?; + + if storage_version > version { + return Err(ContractError::CannotMigrateVersion { + previous_version: stored.version, + }); + } + + if storage_version < version { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + } + + Ok(Response::new()) +} diff --git a/contracts/cw-cyber-airdrop/src/error.rs b/contracts/cw-cyber-gift/src/error.rs similarity index 55% rename from contracts/cw-cyber-airdrop/src/error.rs rename to contracts/cw-cyber-gift/src/error.rs index 5d41963..603558a 100644 --- a/contracts/cw-cyber-airdrop/src/error.rs +++ b/contracts/cw-cyber-gift/src/error.rs @@ -22,36 +22,45 @@ pub enum ContractError { #[error("Unauthorized")] Unauthorized {}, - #[error("Invalid input")] - InvalidInput {}, - - #[error("Already claimed")] + #[error("Your gift is already claimed")] Claimed {}, - #[error("Not claimed")] + #[error("Your gift is not claimed yet")] NotClaimed {}, - #[error("Not activated")] + #[error("Gift is not activated yet")] NotActivated {}, - #[error("Stage released")] + #[error("Stage released, wait for the next stage")] StageReleased {}, - #[error("Gift released")] + #[error("Your gift is fully released")] GiftReleased {}, - #[error("Empty release state")] - EmptyReleaseState {}, - #[error("Wrong length")] WrongLength {}, #[error("Cannot migrate from different contract type: {previous_contract}")] CannotMigrate { previous_contract: String }, - #[error("Address is not eligible to claim airdrop, {msg}")] - IsNotEligible { msg: String }, + #[error("Cannot migrate from unsupported version: {previous_version}")] + CannotMigrateVersion { previous_version: String }, + + #[error("Address is not proved with your passport")] + IsNotProved {}, #[error("Gift is over")] GiftIsOver {}, + + #[error("Verification failed")] + VerificationFailed {}, + + #[error("Semver parsing error: {0}")] + SemVer(String), +} + +impl From for ContractError { + fn from(err: semver::Error) -> Self { + Self::SemVer(err.to_string()) + } } diff --git a/contracts/cw-cyber-gift/src/execute.rs b/contracts/cw-cyber-gift/src/execute.rs new file mode 100644 index 0000000..a7daf66 --- /dev/null +++ b/contracts/cw-cyber-gift/src/execute.rs @@ -0,0 +1,333 @@ +use cosmwasm_std::{Addr, attr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, Empty, Env, MessageInfo, StdResult, to_binary, Uint128, Uint64, WasmMsg}; + +use crate::error::ContractError; +use crate::helpers::{update_coefficient, verify_merkle_proof}; +use crate::state::{ReleaseState, CLAIM, CONFIG, MERKLE_ROOT, RELEASE, ClaimState, STATE, RELEASE_INFO}; +use cw_utils::{Expiration, DAY}; +use std::ops::{Add, Mul}; +use cw_cyber_passport::msg::{QueryMsg as PassportQueryMsg}; +use crate::msg::{AddressResponse, SignatureResponse}; +use cw1_subkeys::msg::{ExecuteMsg as Cw1ExecuteMsg}; +use cyber_std::CyberMsgWrapper; + +type Response = cosmwasm_std::Response; + +const RELEASE_STAGES: u64 = 90; + +pub fn execute_execute( + deps: DepsMut, + _env: Env, + info: MessageInfo, + msgs: Vec>, +) -> Result { + let mut res = Response::new().add_attribute("action", "execute"); + + let cfg = CONFIG.load(deps.storage)?; + let owner = cfg.owner.ok_or(ContractError::Unauthorized {})?; + if info.sender != owner { + return Err(ContractError::Unauthorized {}); + } + + res = res.add_messages(msgs); + + Ok(res) +} + +pub fn execute_update_owner( + deps: DepsMut, + _env: Env, + info: MessageInfo, + new_owner: Option, +) -> Result { + let cfg = CONFIG.load(deps.storage)?; + let owner = cfg.owner.ok_or(ContractError::Unauthorized {})?; + if info.sender != owner { + return Err(ContractError::Unauthorized {}); + } + + let mut tmp_owner = None; + if let Some(addr) = new_owner { + tmp_owner = Some(deps.api.addr_validate(&addr)?) + } + + CONFIG.update(deps.storage, |mut exists| -> StdResult<_> { + exists.owner = tmp_owner; + Ok(exists) + })?; + + Ok(Response::new().add_attributes(vec![attr("action", "update_owner")])) +} + +pub fn execute_update_treasury( + deps: DepsMut, + _env: Env, + info: MessageInfo, + new_treasury: String, +) -> Result { + let cfg = CONFIG.load(deps.storage)?; + let owner = cfg.owner.ok_or(ContractError::Unauthorized {})?; + if info.sender != owner { + return Err(ContractError::Unauthorized {}); + } + + let treasury = deps.api.addr_validate(&new_treasury)?; + + CONFIG.update(deps.storage, |mut cfg| -> StdResult<_> { + cfg.treasury_addr = treasury; + Ok(cfg) + })?; + + Ok(Response::new().add_attributes(vec![ + attr("action", "update_treasury"), + attr("treasury", new_treasury), + ])) +} + +pub fn execute_update_target( + deps: DepsMut, + _env: Env, + info: MessageInfo, + new_target: Uint64, +) -> Result { + let cfg = CONFIG.load(deps.storage)?; + let owner = cfg.owner.ok_or(ContractError::Unauthorized {})?; + if info.sender != owner { + return Err(ContractError::Unauthorized {}); + } + + CONFIG.update(deps.storage, |mut cfg| -> StdResult<_> { + cfg.target_claim = new_target; + Ok(cfg) + })?; + + Ok(Response::new().add_attributes(vec![ + attr("action", "update_target"), + attr("target", new_target.to_string()), + ])) +} + +pub fn execute_register_merkle_root( + deps: DepsMut, + _env: Env, + info: MessageInfo, + merkle_root: String, +) -> Result { + let cfg = CONFIG.load(deps.storage)?; + let owner = cfg.owner.ok_or(ContractError::Unauthorized {})?; + if info.sender != owner { + return Err(ContractError::Unauthorized {}); + } + + let mut root_buf: [u8; 32] = [0; 32]; + hex::decode_to_slice(merkle_root.to_string(), &mut root_buf)?; + + MERKLE_ROOT.save(deps.storage, &merkle_root)?; + + Ok(Response::new().add_attributes(vec![ + attr("action", "register_merkle_root"), + attr("merkle_root", merkle_root), + ])) +} + +const CLAIM_BOUNTY: u128 = 100000; + +pub fn execute_claim( + deps: DepsMut, + _env: Env, + info: MessageInfo, + nickname: String, + mut gift_claiming_address: String, + gift_amount: Uint128, + proof: Vec, +) -> Result { + gift_claiming_address = gift_claiming_address.to_lowercase(); + + let claimed = CLAIM.may_load(deps.storage, gift_claiming_address.clone())?; + if claimed.is_some() { + return Err(ContractError::Claimed {}); + } + + let config = CONFIG.load(deps.storage)?; + let mut state = STATE.load(deps.storage)?; + let claim_amount = gift_amount * state.coefficient; + + if state.current_balance < claim_amount { + return Err(ContractError::GiftIsOver {}); + } + + let res: SignatureResponse = deps.querier.query_wasm_smart( + config.clone().passport_addr, + &PassportQueryMsg::PassportSigned{ + nickname: nickname.clone(), + address: gift_claiming_address.clone() + } + )?; + + if res.signed == false { + return Err(ContractError::IsNotProved {}); + } + + // returns error of proof is invalid + verify_merkle_proof( + &deps, + &info, + gift_claiming_address.clone(), + gift_amount.clone(), + proof, + )?; + + // only claim once by given verified address + CLAIM.save(deps.storage, gift_claiming_address.clone(), &ClaimState{ claim: claim_amount, multiplier: state.coefficient })?; + + update_coefficient(deps.storage, claim_amount, &config, &mut state)?; + + // get address of the passport by nickname + let res: AddressResponse = deps.querier.query_wasm_smart( + config.passport_addr, + &PassportQueryMsg::AddressByNickname { + nickname: nickname.clone(), + } + )?; + + let release_state = ReleaseState { + address: Addr::unchecked(res.clone().address), + balance_claim: claim_amount.checked_sub(Uint128::new(CLAIM_BOUNTY))?, + stage: Uint64::zero(), + stage_expiration: Expiration::Never {}, + }; + + RELEASE.save( + deps.storage, + gift_claiming_address.clone(), + &release_state, + )?; + + STATE.update(deps.storage, |mut stt| -> StdResult<_> { + stt.claims = stt.claims.add(Uint64::new(1)); + Ok(stt) + })?; + + // send funds from treasury controlled by Congress + Ok(Response::new() + .add_message(WasmMsg::Execute { + contract_addr: config.treasury_addr.to_string(), + msg: to_binary(&Cw1ExecuteMsg::Execute:: { + msgs: vec![ + CosmosMsg::Bank(BankMsg::Send { + to_address: res.address.clone(), + amount: vec![Coin { + denom: config.allowed_native, + amount: Uint128::new(CLAIM_BOUNTY), + }], + }).into() + ]})?, + funds: vec![] + }) + .add_attributes(vec![ + attr("action", "claim"), + attr("original", gift_claiming_address), + attr("target", res.address), + attr("amount", claim_amount), + ]) + ) +} + +pub fn execute_release( + deps: DepsMut, + env: Env, + info: MessageInfo, + mut gift_address: String +) -> Result { + gift_address = gift_address.to_lowercase(); + + let claimed = CLAIM.may_load(deps.storage, gift_address.clone())?; + if claimed.is_none() { + return Err(ContractError::NotClaimed {}); + } + + let config = CONFIG.load(deps.storage)?; + let state = STATE.load(deps.storage)?; + if state.claims < config.target_claim { + return Err(ContractError::NotActivated {}); + } + + let mut release_state = RELEASE.load(deps.storage, gift_address.clone())?; + + if release_state.address != info.sender { + return Err(ContractError::Unauthorized {}); + } + + if release_state.balance_claim.is_zero() { + return Err(ContractError::GiftReleased {}); + } + + let amount: Uint128; + if release_state.stage.is_zero() { + // first claim, amount 10% of claim + amount = release_state + .balance_claim + .mul(Decimal::percent(10)); + release_state.stage_expiration = DAY.after(&env.block); + release_state.stage = Uint64::new(RELEASE_STAGES); + } else { + if release_state.stage_expiration.is_expired(&env.block) { + // last claim, amount is rest + if release_state.stage.u64() == 1 { + amount = release_state.balance_claim; + release_state.stage_expiration = Expiration::Never {}; + release_state.stage = Uint64::zero(); + } else { + // amount is equal during all intermediate stages + amount = release_state + .balance_claim + .mul(Decimal::from_ratio(1u128, release_state.stage)); + release_state.stage_expiration = DAY.after(&env.block); + release_state.stage = release_state.stage.checked_sub(Uint64::new(1))?; + } + } else { + return Err(ContractError::StageReleased {}); + } + } + + release_state.balance_claim = release_state.balance_claim - amount; + + RELEASE_INFO.update(deps.storage, release_state.stage.u64(), |rls: Option| -> StdResult { + Ok(rls.unwrap_or_default().add(Uint64::new(1))) + })?; + + RELEASE.save( + deps.storage, + gift_address.clone(), + &release_state, + )?; + + STATE.update(deps.storage, |mut stt| -> StdResult<_> { + stt.releases = stt.releases.add(Uint64::new(1)); + Ok(stt) + })?; + + // send funds from treasury controlled by Congress + Ok(Response::new() + .add_message(WasmMsg::Execute { + contract_addr: config.treasury_addr.to_string(), + msg: to_binary(&Cw1ExecuteMsg::Execute:: { + msgs: vec![ + CosmosMsg::Bank(BankMsg::Send { + to_address: release_state.clone().address.into(), + amount: vec![Coin { + denom: config.allowed_native, + amount: amount, + }], + }).into() + ]})?, + funds: vec![] + }) + .add_attributes(vec![ + attr("action", "release"), + attr("address", release_state.clone().address.to_string()), + attr("gift_address", gift_address), + attr("stage", release_state.stage.to_string()), + attr("amount", amount), + ]) + ) +} diff --git a/contracts/cw-cyber-gift/src/helpers.rs b/contracts/cw-cyber-gift/src/helpers.rs new file mode 100644 index 0000000..8a459d2 --- /dev/null +++ b/contracts/cw-cyber-gift/src/helpers.rs @@ -0,0 +1,66 @@ +use crate::state::{Config, MERKLE_ROOT, State, STATE}; +use crate::ContractError; +use cosmwasm_std::{ + Decimal, DepsMut, MessageInfo, StdResult, Storage, + Uint128, +}; +use sha2::Digest; +use std::convert::TryInto; +use std::ops::{Add, Mul, Sub}; + +pub fn update_coefficient( + store: &mut dyn Storage, + amount: Uint128, + config: &Config, + state: &mut State, +) -> StdResult<()> { + let coefficient_up = config.coefficient_up; + let coefficient_down = config.coefficient_down; + let initial_balance = config.initial_balance; + let current_balance = state.current_balance; + + let new_balance_ratio = Decimal::from_ratio(current_balance, initial_balance); + + let new_coefficient = Decimal::one() + .sub(new_balance_ratio) + .mul(Decimal::from_ratio(coefficient_down, 1u128)) + .add(Decimal::from_ratio(coefficient_up, 1u128).mul(new_balance_ratio)); + + state.coefficient = new_coefficient; + state.current_balance = current_balance - amount; + STATE.save(store, &state) +} + +pub fn verify_merkle_proof( + deps: &DepsMut, + _info: &MessageInfo, + claimer: String, + amount: Uint128, + proof: Vec, +) -> Result { + let merkle_root = MERKLE_ROOT.load(deps.storage)?; + + let user_input = format!("{}{}", claimer, amount); + let hash = sha2::Sha256::digest(user_input.as_bytes()) + .as_slice() + .try_into() + .map_err(|_| ContractError::WrongLength {})?; + + let hash = proof.into_iter().try_fold(hash, |hash, p| { + let mut proof_buf = [0; 32]; + hex::decode_to_slice(p, &mut proof_buf)?; + let mut hashes = [hash, proof_buf]; + hashes.sort_unstable(); + sha2::Sha256::digest(&hashes.concat()) + .as_slice() + .try_into() + .map_err(|_| ContractError::WrongLength {}) + })?; + + let mut root_buf: [u8; 32] = [0; 32]; + hex::decode_to_slice(merkle_root, &mut root_buf)?; + if root_buf != hash { + return Err(ContractError::VerificationFailed {}); + } + Ok(true) +} diff --git a/contracts/cw-cyber-gift/src/lib.rs b/contracts/cw-cyber-gift/src/lib.rs new file mode 100644 index 0000000..c333bc9 --- /dev/null +++ b/contracts/cw-cyber-gift/src/lib.rs @@ -0,0 +1,10 @@ +pub mod contract; +pub mod error; +pub mod execute; +pub mod helpers; +pub mod msg; +pub mod state; +pub mod query; +mod tests; + +pub use crate::error::ContractError; diff --git a/contracts/cw-cyber-airdrop/src/msg.rs b/contracts/cw-cyber-gift/src/msg.rs similarity index 64% rename from contracts/cw-cyber-airdrop/src/msg.rs rename to contracts/cw-cyber-gift/src/msg.rs index 7e88259..7d4a1bf 100644 --- a/contracts/cw-cyber-airdrop/src/msg.rs +++ b/contracts/cw-cyber-gift/src/msg.rs @@ -1,14 +1,15 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cosmwasm_std::{Binary, Decimal, Uint128, Uint64}; +use cosmwasm_std::{CosmosMsg, Decimal, Uint128, Uint64}; use cw_utils::Expiration; +use cyber_std::CyberMsgWrapper; -#[derive(Serialize, Deserialize, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct InstantiateMsg { - /// Owner if none set to info.sender. pub owner: Option, pub passport: String, + pub treasury: String, pub allowed_native: String, pub initial_balance: Uint128, pub coefficient_up: Uint128, @@ -20,14 +21,14 @@ pub struct InstantiateMsg { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ExecuteMsg { + Execute { + msgs: Vec>, + }, UpdateOwner { - /// NewOwner if non sent, contract gets locked. Recipients can receive airdrops - /// but owner cannot register new stages. new_owner: Option, }, - /// Allows to easily debug - UpdatePassportAddr { - new_passport_addr: String, + UpdateTreasuryAddr { + new_treasury_addr: String, }, UpdateTarget { new_target: Uint64, @@ -36,59 +37,30 @@ pub enum ExecuteMsg { /// MerkleRoot is hex-encoded merkle root. merkle_root: String, }, - /// Claim does not check if contract has enough funds, owner must ensure it. Claim { - claim_msg: ClaimMsg, - signature: Binary, - claim_amount: Uint128, + nickname: String, + gift_claiming_address: String, + gift_amount: Uint128, /// Proof is hex-encoded merkle proof. proof: Vec, }, - Release {}, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct ClaimMsg { - pub nickname: String, - pub avatar_cid: String, - pub gift_claiming_address_type: ClaimerType, - pub gift_claiming_address: String, - pub target_address: String, - pub relay_reward: Decimal, -} - -/* -impl ToString for ClaimMsg { - fn to_string(&self) -> String { - format!("\{{}\}", self.target_address, ) - } -} - - */ - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum ClaimerType { - Ethereum, - Cosmos, -} - -impl ToString for ClaimerType { - fn to_string(&self) -> String { - match self { - ClaimerType::Ethereum => String::from("ethereum"), - ClaimerType::Cosmos => String::from("cosmos"), - } - } + Release { gift_address: String }, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum QueryMsg { Config {}, + State {}, MerkleRoot {}, IsClaimed { address: String }, + Claim { address: String }, ReleaseState { address: String }, + ReleaseStageState { stage: Uint64 }, + AllReleaseStageState { + start: Option, + limit: Option, + }, } #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] @@ -98,10 +70,15 @@ pub struct ConfigResponse { pub passport: String, pub target_claim: Uint64, pub allowed_native: String, - pub current_balance: Uint128, pub initial_balance: Uint128, pub coefficient_up: Uint128, pub coefficient_down: Uint128, +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +#[serde(rename_all = "snake_case")] +pub struct StateResponse { + pub current_balance: Uint128, pub coefficient: Decimal, pub claims: Uint64, pub releases: Uint64, @@ -118,12 +95,37 @@ pub struct IsClaimedResponse { pub is_claimed: bool, } +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct ClaimResponse { + pub claim: Uint128, + pub multiplier: Decimal +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct ReleaseStateResponse { + pub address: String, pub balance_claim: Uint128, pub stage: Uint64, pub stage_expiration: Expiration, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct MigrateMsg {} +pub struct ReleaseStageStateResponse { + pub releases: Uint64, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct AllReleaseStageStateResponse { + pub releases: Vec, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct SignatureResponse { + pub signed: bool, +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +#[serde(rename_all = "snake_case")] +pub struct AddressResponse { + pub address: String, +} diff --git a/contracts/cw-cyber-gift/src/query.rs b/contracts/cw-cyber-gift/src/query.rs new file mode 100644 index 0000000..4035133 --- /dev/null +++ b/contracts/cw-cyber-gift/src/query.rs @@ -0,0 +1,91 @@ +use cosmwasm_std::{Deps, Order, StdResult, Uint64}; +use cw_storage_plus::Bound; +use crate::msg::{AllReleaseStageStateResponse, ClaimResponse, ConfigResponse, IsClaimedResponse, MerkleRootResponse, ReleaseStageStateResponse, ReleaseStateResponse, StateResponse}; +use crate::state::{CLAIM, CONFIG, MERKLE_ROOT, RELEASE, RELEASE_INFO, STATE}; + + +pub fn query_config(deps: Deps) -> StdResult { + let cfg = CONFIG.load(deps.storage)?; + Ok(ConfigResponse { + owner: cfg.owner.map(|o| o.to_string()), + passport: cfg.passport_addr.to_string(), + target_claim: cfg.target_claim, + allowed_native: cfg.allowed_native, + initial_balance: cfg.initial_balance, + coefficient_up: cfg.coefficient_up, + coefficient_down: cfg.coefficient_down, + }) +} + +pub fn query_state(deps: Deps) -> StdResult { + let stt = STATE.load(deps.storage)?; + Ok(StateResponse { + current_balance: stt.current_balance, + coefficient: stt.coefficient, + claims: stt.claims, + releases: stt.releases + }) +} + +pub fn query_merkle_root(deps: Deps) -> StdResult { + let merkle_root = MERKLE_ROOT.load(deps.storage)?; + let resp = MerkleRootResponse { merkle_root }; + + Ok(resp) +} + +pub fn query_is_claimed(deps: Deps, address: String) -> StdResult { + let claim = CLAIM.may_load(deps.storage, address)?; + let mut is_claimed = false; + if claim.is_some() { + is_claimed = true; + } + let resp = IsClaimedResponse { is_claimed }; + + Ok(resp) +} + +pub fn query_claim(deps: Deps, address: String) -> StdResult { + let claim = CLAIM.load(deps.storage, address)?; + + let resp = ClaimResponse { + claim: claim.claim, + multiplier: claim.multiplier + }; + + Ok(resp) +} + +pub fn query_release_state(deps: Deps, address: String) -> StdResult { + let release_state = RELEASE.load(deps.storage, address)?; + let resp = ReleaseStateResponse { + address: release_state.address.into(), + balance_claim: release_state.balance_claim, + stage: release_state.stage, + stage_expiration: release_state.stage_expiration, + }; + Ok(resp) +} + +pub fn query_release_stage_state(deps: Deps, stage: Uint64) -> StdResult { + let release_stage_state = RELEASE_INFO.load(deps.storage, stage.u64())?; + let resp = ReleaseStageStateResponse { + releases: release_stage_state + }; + Ok(resp) +} + +pub fn query_all_release_stage_state(deps: Deps, start: Option, limit: Option) -> StdResult { + let start = start.map(Bound::inclusive); + let limit = limit.unwrap_or(100) as usize; + + let all_release_stage_state = RELEASE_INFO + .range(deps.storage, start, None, Order::Ascending) + .take(limit) + .map(|item| item.unwrap().1) + .collect::>(); + let resp = AllReleaseStageStateResponse { + releases: all_release_stage_state + }; + Ok(resp) +} diff --git a/contracts/cw-cyber-airdrop/src/state.rs b/contracts/cw-cyber-gift/src/state.rs similarity index 68% rename from contracts/cw-cyber-airdrop/src/state.rs rename to contracts/cw-cyber-gift/src/state.rs index 4e8b462..df22343 100644 --- a/contracts/cw-cyber-airdrop/src/state.rs +++ b/contracts/cw-cyber-gift/src/state.rs @@ -8,38 +8,52 @@ use cw_utils::Expiration; // TODO move coefficient, current_balance, claims and releases out of config #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct Config { - /// Owner If None set, contract is frozen. pub owner: Option, pub passport_addr: Addr, - /// target_claim amount of claimed accounts to start release (activate gift) + pub treasury_addr: Addr, pub target_claim: Uint64, pub allowed_native: String, - pub current_balance: Uint128, pub initial_balance: Uint128, pub coefficient_up: Uint128, pub coefficient_down: Uint128, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct State { + pub current_balance: Uint128, pub coefficient: Decimal, - /// amount of claimed accounts pub claims: Uint64, - /// amount of total releases by all accounts pub releases: Uint64, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct ReleaseState { + pub address: Addr, pub balance_claim: Uint128, pub stage: Uint64, pub stage_expiration: Expiration, } +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct ClaimState { + pub claim: Uint128, + pub multiplier: Decimal, +} + pub const CONFIG_KEY: &str = "config"; pub const CONFIG: Item = Item::new(CONFIG_KEY); +pub const STATE_KEY: &str = "state"; +pub const STATE: Item = Item::new(STATE_KEY); + pub const MERKLE_ROOT_PREFIX: &str = "merkle_root"; pub const MERKLE_ROOT: Item = Item::new(MERKLE_ROOT_PREFIX); pub const CLAIM_PREFIX: &str = "claim"; -pub const CLAIM: Map = Map::new(CLAIM_PREFIX); +pub const CLAIM: Map = Map::new(CLAIM_PREFIX); pub const RELEASE_PREFIX: &str = "release"; pub const RELEASE: Map = Map::new(RELEASE_PREFIX); + +pub const RELEASE_INFO_PREFIX: &str = "release_info"; +pub const RELEASE_INFO: Map = Map::new(RELEASE_INFO_PREFIX); diff --git a/contracts/cw-cyber-gift/src/tests.rs b/contracts/cw-cyber-gift/src/tests.rs new file mode 100644 index 0000000..cd02936 --- /dev/null +++ b/contracts/cw-cyber-gift/src/tests.rs @@ -0,0 +1,488 @@ +#[cfg(test)] +mod tests { + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::{attr, from_binary, Binary, Coin, Uint128, Uint64, Empty, Addr, coins, BlockInfo}; + use crate::msg::{AllReleaseStageStateResponse, ConfigResponse, ExecuteMsg, InstantiateMsg, MerkleRootResponse, QueryMsg, ReleaseStageStateResponse, StateResponse}; + use crate::ContractError; + use crate::contract::{execute, instantiate, query}; + use cw_multi_test::{next_block, Contract, ContractWrapper, Executor}; + use cyber_std::{CyberMsgWrapper}; + use cw_cyber_passport::msg::ExecuteMsg as PassportExecuteMsg; + use cyber_std_test::CyberApp; + + + const NATIVE_TOKEN: &str = "boot"; + const OWNER: &str = "owner"; + const CYB1: &str = "bostrom19nk207agguzdvpj9nqsf4zrjw8mcuu9afun3fv"; + const SPACE1: &str = "space1"; + const SPACE2: &str = "space2"; + const SPACE3: &str = "space3"; + const INIT_BALANCE_OWNER: Uint128 = Uint128::new(10000000000000); + const INIT_BALANCE_TREASURY: Uint128 = Uint128::new(300000000); + const CF_UP: Uint128 = Uint128::new(20); + const CF_DOWN: Uint128 = Uint128::new(5); + const CF: Uint128 = Uint128::new(20); + const TARGET_CLAIM: Uint64 = Uint64::new(2); + + pub fn next_hour(block: &mut BlockInfo) { + block.time = block.time.plus_seconds(3600); + block.height += 1; + } + + pub fn contract_gift() -> Box> { + let contract = ContractWrapper::new( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ); + Box::new(contract) + } + + pub fn contract_passport() -> Box> { + let contract = ContractWrapper::new( + cw_cyber_passport::contract::execute, + cw_cyber_passport::contract::instantiate, + cw_cyber_passport::contract::query, + ) + .with_reply(cw_cyber_passport::contract::reply); + Box::new(contract) + } + + pub fn contract_treasury() -> Box> { + let contract = ContractWrapper::new_with_empty( + cw1_subkeys::contract::execute, + cw1_subkeys::contract::instantiate, + cw1_subkeys::contract::query, + ); + Box::new(contract) + } + + pub fn contract_subgraph() -> Box> { + let contract = ContractWrapper::new( + cw_cyber_subgraph::contract::execute, + cw_cyber_subgraph::contract::instantiate, + cw_cyber_subgraph::contract::query, + ) + .with_reply(cw_cyber_subgraph::contract::reply); + Box::new(contract) + } + + fn mock_app(init_funds: &[Coin]) -> CyberApp { + let mut app = CyberApp::new(); + app.init_modules(|router, _, storage| { + router + .bank + .init_balance(storage, &Addr::unchecked(OWNER), init_funds.to_vec()) + .unwrap(); + }); + app + } + + fn instantiate_gift(app: &mut CyberApp, passport: String, treasury: String) -> Addr { + let gift_id = app.store_code(contract_gift()); + let msg = crate::msg::InstantiateMsg { + owner: Some(OWNER.to_string()), + passport: passport.to_string(), + treasury: treasury.to_string(), + allowed_native: NATIVE_TOKEN.to_string(), + initial_balance: INIT_BALANCE_TREASURY, + coefficient_up: CF_UP, + coefficient_down: CF_DOWN, + coefficient: CF, + target_claim: TARGET_CLAIM + }; + app.instantiate_contract(gift_id, Addr::unchecked(OWNER), &msg, &[], "gift", None) + .unwrap() + } + + fn instantiate_passport(app: &mut CyberApp) -> Addr { + let passport_id = app.store_code(contract_passport()); + let msg = cw_cyber_passport::msg::InstantiateMsg { + name: "MoonPassport".to_string(), + symbol: "MP".to_string(), + minter: "cosmos2contract".to_string(), + owner: OWNER.to_string(), + name_subgraph: SPACE1.to_string(), + avatar_subgraph: SPACE2.to_string(), + proof_subgraph: SPACE3.to_string(), + }; + app.instantiate_contract(passport_id, Addr::unchecked(OWNER), &msg, &[], "passport", None) + .unwrap() + } + + fn instantiate_treasury(app: &mut CyberApp) -> Addr { + let treasury_id = app.store_code(contract_treasury()); + let msg = cw1_whitelist::msg::InstantiateMsg { + admins: vec![OWNER.to_string()], + mutable: false + }; + app.instantiate_contract(treasury_id, Addr::unchecked(OWNER), &msg, &coins(INIT_BALANCE_TREASURY.u128(), NATIVE_TOKEN), "treasury", None) + .unwrap() + } + + fn instantiate_subgraph(app: &mut CyberApp, owner: String, executer: String) -> Addr { + let treasury_id = app.store_code(contract_subgraph()); + let msg = cw_cyber_subgraph::msg::InstantiateMsg { + admins: vec![owner.to_string()], + executers: vec![executer.to_string()] + }; + app.instantiate_contract(treasury_id, Addr::unchecked(OWNER), &msg, &[], "subgraph", None) + .unwrap() + } + + fn setup_contracts( + app: &mut CyberApp, + ) -> (Addr, Addr, Addr) { + let treasury_addr = instantiate_treasury(app); + app.update_block(next_block); + + let passport_addr = instantiate_passport(app); + app.update_block(next_block); + + let gift_addr = instantiate_gift(app, passport_addr.to_string(), treasury_addr.to_string()); + app.update_block(next_block); + + let _res = app.execute_contract( + Addr::unchecked(OWNER), + treasury_addr.clone(), + &cw1_subkeys::msg::ExecuteMsg::IncreaseAllowance:: { + spender: gift_addr.to_string(), + amount: Coin::new(INIT_BALANCE_TREASURY.u128(), NATIVE_TOKEN), + expires: None, + }, + &[], + ); + app.update_block(next_block); + + let name_subgraph = instantiate_subgraph(app, OWNER.to_string(), passport_addr.to_string()); + let avatar_subgraph = instantiate_subgraph(app, OWNER.to_string(), passport_addr.to_string()); + let proof_subgraph = instantiate_subgraph(app, OWNER.to_string(), passport_addr.to_string()); + app.update_block(next_block); + + let _res = app.execute_contract( + Addr::unchecked(OWNER), + passport_addr.clone(), + &PassportExecuteMsg::SetSubgraphs { + name_subgraph: name_subgraph.to_string(), + avatar_subgraph: avatar_subgraph.to_string(), + proof_subgraph: proof_subgraph.to_string() + }, + &[], + ); + app.update_block(next_block); + + (gift_addr, passport_addr, treasury_addr) + } + + #[test] + fn proper_flow() { + // NOTE to run tests change RELEASE_STAGES to 9 + // NOTE to run tests change CONSTITUTION to QmRX8qYgeZoYM3M5zzQaWEpVFdpin6FvVXvp6RPQK3oufV + // NOTE to run tests change duration from DAY to HOUR + + let init_funds = coins(INIT_BALANCE_OWNER.u128(), NATIVE_TOKEN); + let mut app = mock_app(&init_funds); + + let (gift_addr, passport_addr, treasury_addr) = setup_contracts(&mut app); + + let _res = app.execute_contract( + Addr::unchecked(OWNER), + gift_addr.clone(), + &ExecuteMsg::RegisterMerkleRoot { + merkle_root: "96c287db438923b77acee90e134e1f2d9bc506bc5544eab8e89e8886b83ca5c7".to_string() + }, + &[], + ); + + let _res = app.execute_contract( + Addr::unchecked(CYB1), + passport_addr.clone(), + &PassportExecuteMsg::CreatePassport { + nickname: "passport1".to_string(), + avatar: "QmVPRR3i2oFRjgMKS5dw4QbGNwdXNoYxfcpS3C9pVxHEbb".to_string(), + signature: Binary::from_base64("eyJwdWJfa2V5IjoiQStNWEZwN1llTE12b1ZsQVU2NlV1MHozV3RjOUN1d3EwZW9jVWh0Tk9tbnciLAoic2lnbmF0dXJlIjoicGRWNHhVY1RCT3loMFNFY2dWRnJxYUc4cXBOSHJocktLZGRxdzJ5d3Eyb2NVWGpybDNDdW8rZlRtUjR4bUpucGVIQi90blM4NEF2K0FuUnlRSlJ1S0E9PSJ9").unwrap(), + }, + &[], + ); + + let _res = app.execute_contract( + Addr::unchecked(CYB1), + passport_addr.clone(), + &PassportExecuteMsg::ProofAddress { + nickname: "passport1".to_string(), + address: "0x0408522089294b8b3f0c9514086e6ae1df00394c".to_string(), + signature: Binary::from_base64("0xa3b7b3adee5805488a62d96ca58ccee80a65a3f74343d1e6f19b0b597afe65da123c020cb968ca141d48b844b098ee33ad5aa827b0da89fb3b89ea272f9a42b01b").unwrap(), + }, + &[], + ); + + let _res = app.execute_contract( + Addr::unchecked(CYB1), + passport_addr.clone(), + &PassportExecuteMsg::CreatePassport { + nickname: "passport2".to_string(), + avatar: "QmVPRR3i2oFRjgMKS5dw4QbGNwdXNoYxfcpS3C9pVxHEbb".to_string(), + signature: Binary::from_base64("eyJwdWJfa2V5IjoiQStNWEZwN1llTE12b1ZsQVU2NlV1MHozV3RjOUN1d3EwZW9jVWh0Tk9tbnciLAoic2lnbmF0dXJlIjoicGRWNHhVY1RCT3loMFNFY2dWRnJxYUc4cXBOSHJocktLZGRxdzJ5d3Eyb2NVWGpybDNDdW8rZlRtUjR4bUpucGVIQi90blM4NEF2K0FuUnlRSlJ1S0E9PSJ9").unwrap(), + }, + &[], + ); + + let _res = app.execute_contract( + Addr::unchecked(CYB1), + passport_addr.clone(), + &PassportExecuteMsg::ProofAddress { + nickname: "passport2".to_string(), + address: "bostrom19nk207agguzdvpj9nqsf4zrjw8mcuu9afun3fv".to_string(), + signature: Binary::from_base64("eyJwdWJfa2V5IjoiQStNWEZwN1llTE12b1ZsQVU2NlV1MHozV3RjOUN1d3EwZW9jVWh0Tk9tbnciLAoic2lnbmF0dXJlIjoicGRWNHhVY1RCT3loMFNFY2dWRnJxYUc4cXBOSHJocktLZGRxdzJ5d3Eyb2NVWGpybDNDdW8rZlRtUjR4bUpucGVIQi90blM4NEF2K0FuUnlRSlJ1S0E9PSJ9").unwrap(), + }, + &[], + ); + + let _res = app.execute_contract( + Addr::unchecked(CYB1), + gift_addr.clone(), + &ExecuteMsg::Claim { + nickname: "passport1".to_string(), + gift_claiming_address: "0x0408522089294b8b3f0c9514086e6ae1df00394c".to_string(), + gift_amount: Uint128::new(10000000), + proof: vec!["020feac4e445b8710e223ef9d32d60d0fa060e5a33c421c217ac4976641afa9f".to_string()], + }, + &[], + ); + + let _res = app.execute_contract( + Addr::unchecked(CYB1), + gift_addr.clone(), + &ExecuteMsg::Claim { + nickname: "passport2".to_string(), + gift_claiming_address: "bostrom19nk207agguzdvpj9nqsf4zrjw8mcuu9afun3fv".to_string(), + gift_amount: Uint128::new(5000000), + proof: vec!["c0d07d81376100727f8de10cbbc3f46c04c13a906c4a8de884abebaa94d33737".to_string()] + }, + &[], + ); + + for i in 0..10 { + let res = app.execute_contract( + Addr::unchecked(CYB1), + gift_addr.clone(), + &ExecuteMsg::Release { + gift_address: "0x0408522089294b8b3f0c9514086e6ae1df00394c".to_string(), + }, + &[], + ); + println!("Release [ETH][{:?}]- {:?}", i, res); + + let res = app.execute_contract( + Addr::unchecked(CYB1), + gift_addr.clone(), + &ExecuteMsg::Release { + gift_address: "bostrom19nk207agguzdvpj9nqsf4zrjw8mcuu9afun3fv".to_string(), + }, + &[], + ); + println!("Release [CMS][{:?}]- {:?}", i, res); + + app.update_block(next_hour); + } + + + println!("GIFT BAL - {:?}", app.wrap().query_balance(&gift_addr, "boot").unwrap()); + println!("TREASURY BAL - {:?}", app.wrap().query_balance(&treasury_addr, "boot").unwrap()); + println!("PASSPORT #1 BAL- {:?}", app.wrap().query_balance(&Addr::unchecked(CYB1), "boot").unwrap()); + + for i in 0..10 { + let info: ReleaseStageStateResponse = app.wrap().query_wasm_smart( + &gift_addr, + &QueryMsg::ReleaseStageState { stage: Uint64::from(1u64) } + ).unwrap(); + println!("STAGE {:?} - RELEASES {:?}", i, info.releases.u64()); + } + let info: StateResponse = app.wrap().query_wasm_smart(&gift_addr, &QueryMsg::State {}).unwrap(); + println!("STATE - {:?}", info); + + let res: AllReleaseStageStateResponse = app.wrap().query_wasm_smart( + &gift_addr, + &QueryMsg::AllReleaseStageState {start:Some(7u8), limit:Some(2u8)} + ).unwrap(); + println!("RELEASES - {:?}", res); + } + + #[test] + fn update_owner() { + let mut deps = mock_dependencies(); + + let msg = InstantiateMsg { + owner: None, + passport: "passport".to_string(), + target_claim: Uint64::new(4), + allowed_native: NATIVE_TOKEN.to_string(), + initial_balance: Uint128::new(100), + coefficient_up: Default::default(), + coefficient_down: Default::default(), + coefficient: Default::default(), + treasury: "treasury".to_string() + }; + + let env = mock_env(); + let info = mock_info( + "owner", + &[Coin { + denom: NATIVE_TOKEN.to_string(), + amount: Uint128::new(100), + }], + ); + let _res = instantiate(deps.as_mut(), env, info, msg).unwrap(); + + // update owner + let env = mock_env(); + let info = mock_info("owner", &[]); + let msg = ExecuteMsg::UpdateOwner { + new_owner: Some("owner0001".to_string()), + }; + + let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + assert_eq!(0, res.messages.len()); + + // it worked, let's query the state + let res = query(deps.as_ref(), env, QueryMsg::Config {}).unwrap(); + let config: ConfigResponse = from_binary(&res).unwrap(); + assert_eq!("owner0001", config.owner.unwrap().as_str()); + + // Unauthorized err + let env = mock_env(); + let info = mock_info("owner", &[]); + let msg = ExecuteMsg::UpdateOwner { new_owner: None }; + + let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(res, ContractError::Unauthorized {}); + } + + #[test] + fn register_merkle_root() { + let mut deps = mock_dependencies(); + + let msg = InstantiateMsg { + owner: Some("owner".to_string()), + passport: "passport".to_string(), + target_claim: Uint64::new(4), + allowed_native: NATIVE_TOKEN.to_string(), + initial_balance: Default::default(), + coefficient_up: Default::default(), + coefficient_down: Default::default(), + coefficient: Default::default(), + treasury: "treasury".to_string() + }; + + let env = mock_env(); + let info = mock_info( + "addr0000", + &[Coin { + denom: NATIVE_TOKEN.to_string(), + amount: Uint128::new(100), + }], + ); + let _res = instantiate(deps.as_mut(), env, info, msg).unwrap(); + + // register new merkle root + let env = mock_env(); + let info = mock_info("owner", &[]); + let msg = ExecuteMsg::RegisterMerkleRoot { + merkle_root: "634de21cde1044f41d90373733b0f0fb1c1c71f9652b905cdf159e73c4cf0d37" + .to_string(), + }; + + let res = execute(deps.as_mut(), env.clone(), info, msg).unwrap(); + assert_eq!( + res.attributes, + vec![ + attr("action", "register_merkle_root"), + attr( + "merkle_root", + "634de21cde1044f41d90373733b0f0fb1c1c71f9652b905cdf159e73c4cf0d37" + ) + ] + ); + + let res = query(deps.as_ref(), env, QueryMsg::MerkleRoot {}).unwrap(); + let merkle_root: MerkleRootResponse = from_binary(&res).unwrap(); + assert_eq!( + "634de21cde1044f41d90373733b0f0fb1c1c71f9652b905cdf159e73c4cf0d37".to_string(), + merkle_root.merkle_root + ); + } + + #[test] + fn owner_freeze() { + let mut deps = mock_dependencies(); + + let msg = InstantiateMsg { + owner: Some("owner".to_string()), + passport: "passport".to_string(), + target_claim: Uint64::new(4), + allowed_native: NATIVE_TOKEN.to_string(), + initial_balance: Uint128::new(10000000000000), + coefficient_up: Uint128::new(20), + coefficient_down: Uint128::new(5), + coefficient: Uint128::new(20), + treasury: "treasury".to_string() + }; + + let env = mock_env(); + let info = mock_info( + "addr0000", + &[Coin { + denom: NATIVE_TOKEN.to_string(), + amount: Uint128::new(10000000000000), + }], + ); + let _res = instantiate(deps.as_mut(), env, info, msg).unwrap(); + + // can register merkle root + let env = mock_env(); + let info = mock_info("owner", &[]); + let msg = ExecuteMsg::RegisterMerkleRoot { + merkle_root: "5d4f48f147cb6cb742b376dce5626b2a036f69faec10cd73631c791780e150fc" + .to_string(), + }; + let _res = execute(deps.as_mut(), env, info, msg).unwrap(); + + // can update owner + let env = mock_env(); + let info = mock_info("owner", &[]); + let msg = ExecuteMsg::UpdateOwner { + new_owner: Some("owner0001".to_string()), + }; + + let res = execute(deps.as_mut(), env, info, msg).unwrap(); + assert_eq!(0, res.messages.len()); + + // freeze contract + let env = mock_env(); + let info = mock_info("owner0001", &[]); + let msg = ExecuteMsg::UpdateOwner { new_owner: None }; + + let res = execute(deps.as_mut(), env, info, msg).unwrap(); + assert_eq!(0, res.messages.len()); + + // cannot register new drop + let env = mock_env(); + let info = mock_info("owner0001", &[]); + let msg = ExecuteMsg::RegisterMerkleRoot { + merkle_root: "ebaa83c7eaf7467c378d2f37b5e46752d904d2d17acd380b24b02e3b398b3e5a" + .to_string(), + }; + let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(res, ContractError::Unauthorized {}); + + // cannot update config + let env = mock_env(); + let info = mock_info("owner0001", &[]); + let msg = ExecuteMsg::RegisterMerkleRoot { + merkle_root: "ebaa83c7eaf7467c378d2f37b5e46752d904d2d17acd380b24b02e3b398b3e5a" + .to_string(), + }; + let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); + assert_eq!(res, ContractError::Unauthorized {}); + } +} diff --git a/contracts/cw-cyber-airdrop/testdata/airdrop_stage_1_test_data_cosmos_address.json b/contracts/cw-cyber-gift/testdata/airdrop_stage_1_test_data_cosmos_address.json similarity index 100% rename from contracts/cw-cyber-airdrop/testdata/airdrop_stage_1_test_data_cosmos_address.json rename to contracts/cw-cyber-gift/testdata/airdrop_stage_1_test_data_cosmos_address.json diff --git a/contracts/cw-cyber-airdrop/testdata/airdrop_stage_1_test_data_ethereum_address.json b/contracts/cw-cyber-gift/testdata/airdrop_stage_1_test_data_ethereum_address.json similarity index 100% rename from contracts/cw-cyber-airdrop/testdata/airdrop_stage_1_test_data_ethereum_address.json rename to contracts/cw-cyber-gift/testdata/airdrop_stage_1_test_data_ethereum_address.json diff --git a/contracts/cw-cyber-airdrop/testdata/cw-cybergift-data.md b/contracts/cw-cyber-gift/testdata/cw-cybergift-data.md similarity index 100% rename from contracts/cw-cyber-airdrop/testdata/cw-cybergift-data.md rename to contracts/cw-cyber-gift/testdata/cw-cybergift-data.md diff --git a/contracts/cw-cyber-gift/testdata/generate_test_data/.env.example b/contracts/cw-cyber-gift/testdata/generate_test_data/.env.example new file mode 100644 index 0000000..a6dc265 --- /dev/null +++ b/contracts/cw-cyber-gift/testdata/generate_test_data/.env.example @@ -0,0 +1,3 @@ +WALLET_SEED='correct lens reopen movie practice harsh blush satisfy ecology action unlock feed begin deny trouble timber bulb infant gun test steel bid rate input' +WALLET_ADDRESS='bostrom1ywemhcrla7f9lkktfsu3cnqv2mapw82a5yf5fc' +PYTHON_PATH='python3' \ No newline at end of file diff --git a/contracts/cw-cyber-airdrop/testdata/generate_test_data/.gitignore b/contracts/cw-cyber-gift/testdata/generate_test_data/.gitignore similarity index 76% rename from contracts/cw-cyber-airdrop/testdata/generate_test_data/.gitignore rename to contracts/cw-cyber-gift/testdata/generate_test_data/.gitignore index 3c3629e..1dcef2d 100644 --- a/contracts/cw-cyber-airdrop/testdata/generate_test_data/.gitignore +++ b/contracts/cw-cyber-gift/testdata/generate_test_data/.gitignore @@ -1 +1,2 @@ node_modules +.env \ No newline at end of file diff --git a/contracts/cw-cyber-airdrop/testdata/generate_test_data/airdrop_stage_1_list.json b/contracts/cw-cyber-gift/testdata/generate_test_data/airdrop_stage_1_list.json similarity index 100% rename from contracts/cw-cyber-airdrop/testdata/generate_test_data/airdrop_stage_1_list.json rename to contracts/cw-cyber-gift/testdata/generate_test_data/airdrop_stage_1_list.json diff --git a/contracts/cw-cyber-gift/testdata/generate_test_data/contract_utils.py b/contracts/cw-cyber-gift/testdata/generate_test_data/contract_utils.py new file mode 100644 index 0000000..e0c1a40 --- /dev/null +++ b/contracts/cw-cyber-gift/testdata/generate_test_data/contract_utils.py @@ -0,0 +1,365 @@ +import json +import base58 +import hashlib +import pandas as pd +from subprocess import Popen, PIPE + +from aiohttp import ClientConnectorError +from cyber_sdk.client.lcd import LCDClient +from cyber_sdk.client.lcd.api.tx import CreateTxOptions +from cyber_sdk.core import AccAddress, Coins +from cyber_sdk.core.wasm import MsgExecuteContract +from cyber_sdk.key.mnemonic import MnemonicKey +from cyber_sdk.core.bank import MsgMultiSend +from cyber_sdk.core.bank.msgs import MultiSendInput, MultiSendOutput + +NODE_URL = 'https://rpc.space-pussy-1.cybernode.ai:443' +LCD_URL = 'https://lcd.space-pussy-1.cybernode.ai/' +NETWORK = 'space-pussy-1' + + +def Error_Handler(func): + def Inner_Function(*args, **kwargs): + try: + func(*args, **kwargs) + except (EOFError, IndexError, KeyError, TimeoutError, ClientConnectorError) as e: + print(f'Error: {e}') + except Exception as e: + print(f'Error: {e}') + return Inner_Function + + +def execute_bash(bash_command: str, display_data: bool = False) -> [str, str]: + _output, _error = Popen(bash_command, shell=True, stdout=PIPE, stderr=PIPE).communicate() + if _error: + display_data = True + if display_data: + print(bash_command) + if _output: + try: + print(json.dumps(json.loads(_output.decode('utf-8')), indent=4, sort_keys=True)) + except json.JSONDecodeError: + print(_output) + if _error: + print(_error) + return _output.decode('utf-8'), _error.decode('utf-8') + + +def instantiate_contract(init_query: str, contract_code_id: str, contract_label: str, amount: str = '', + from_address: str = '$WALLET', display_data: bool = False) -> str: + _init_output, _init_error = execute_bash( + f'''INIT='{init_query}' \ + && cyber tx wasm instantiate {contract_code_id} "$INIT" --from {from_address} \ + {'--amount ' + amount + 'boot' if amount else ''} --label "{contract_label}" \ + -y --gas 3500000 --broadcast-mode block -o json --chain-id={NETWORK} --node={NODE_URL}''') + if display_data: + try: + print(json.dumps(json.loads(_init_output), indent=4, sort_keys=True)) + except json.JSONDecodeError: + print(_init_output) + if _init_error: + print(_init_error) + _init_json = json.loads(_init_output) + return [event['attributes'][0]['value'] + for event in _init_json['logs'][0]['events'] + if event['type'] == 'instantiate'][0] + + +def execute_contract_bash(execute_query: str, contract_address: str, from_address: str = '$WALLET', gas: int = 300000, + display_data: bool = False) -> str: + _execute_output, _execute_error = execute_bash( + f'''EXECUTE='{execute_query}' \ + && CONTRACT="{contract_address}" \ + && cyber tx wasm execute $CONTRACT "$EXECUTE" --from {from_address} --broadcast-mode block -o json -y \ + --gas={gas} --chain-id={NETWORK} --node={NODE_URL}''') + if display_data: + try: + print(json.dumps(json.loads(_execute_output), indent=4, sort_keys=True)) + except json.JSONDecodeError: + print(_execute_output) + if _execute_error: + print(_execute_error) + return _execute_output + + +def query_contract(query: str, contract_address: str, display_data: bool = False) -> json: + _execute_output, _execute_error = execute_bash( + f'''QUERY='{query}' \ + && cyber query wasm contract-state smart {contract_address} "$QUERY" -o json \ + --chain-id={NETWORK} --node={NODE_URL}''') + try: + if display_data: + print(json.dumps(json.loads(_execute_output), indent=4, sort_keys=True)) + return json.loads(_execute_output) + except json.JSONDecodeError: + print(_execute_output) + if _execute_error: + print(_execute_error) + return json.loads(_execute_output) + + +def get_ipfs_cid_from_str(source_str: str) -> str: + """ + Use only for getting valid CIDs. + Return is incorrect CID. + :param source_str: string for uploading as file into IPFS + :return IPFS CID (valid but !incorrect!)""" + assert type(source_str) == str + _source_hash = hashlib.sha256(str.encode(source_str)).hexdigest() + _source_hash_bytes = bytes.fromhex(_source_hash) + _length = bytes([len(_source_hash_bytes)]) + _hash = b'\x12' + _length + _source_hash_bytes + return base58.b58encode(_hash).decode('utf-8') + + +def get_proofs(input_file: str, + output_file: str, + start_index: int = 1, + end_index: int = -1) -> bool: + _root_and_proofs_output, _root_and_proofs_error = execute_bash( + f'export NODE_OPTIONS=--max_old_space_size=4096 && ' + f'yarn start --input {input_file} --output {output_file} --start_index {start_index} --end_index {end_index}') + if _root_and_proofs_output: + print(_root_and_proofs_output) + return True + else: + print(_root_and_proofs_error) + return False + + +class ContractUtils: + + def __init__(self, ipfs_client, address_dict: dict, url: str = LCD_URL, chain_id: str = NETWORK): + self.ipfs_client = ipfs_client + self.address_dict = address_dict + self.name_dict = {v: k for k, v in address_dict.items()} + self.bostrom_lcd_client = LCDClient( + url=url, + chain_id=chain_id + ) + + def set_address_dict(self, address_dict): + self.address_dict = address_dict + self.name_dict = {v: k for k, v in address_dict.items()} + + def send_coins(self, from_seed: str, to_addresses: list, amounts: list, gas: int = 70999, denom: str = 'boot', + display_data: bool = False) -> str: + _mk = MnemonicKey(mnemonic=from_seed) + _wallet = self.bostrom_lcd_client.wallet(key=_mk) + + _msg = MsgMultiSend( + inputs=[ + MultiSendInput(address=_wallet.key.acc_address, coins=Coins(boot=_amount)) + for _amount in amounts + ], + outputs=[ + MultiSendOutput(address=_to_address, coins=Coins(boot=_amount)) + for _to_address, _amount in zip(to_addresses, amounts) + ], + ) + + _tx = _wallet.create_and_sign_tx( + CreateTxOptions( + msgs=[_msg], + gas_prices="0.1boot", + gas=str(gas), + fee_denoms=["boot"], + ) + ) + if display_data: + print(_msg) + print('\n', _tx) + return self.bostrom_lcd_client.tx.broadcast(_tx).to_json() + + # def query_contract(self, query_msg: dict, contract_address: str) -> json: + # return self.bostrom_lcd_client.wasm.contract_query(contract_address=contract_address, query_msg=query_msg) + + def execute_contract(self, execute_msg: json, contract_address: str, mnemonic: str, + gas: int = 500000, gas_price: int = 0, display_data: bool = False) -> str: + _key = MnemonicKey(mnemonic=mnemonic) + _wallet = self.bostrom_lcd_client.wallet(key=_key) + + _msg = MsgExecuteContract( + sender=_wallet.key.acc_address, + contract=AccAddress(contract_address), + execute_msg=execute_msg) + + _tx = _wallet.create_and_sign_tx( + CreateTxOptions( + msgs=[_msg], + gas_prices=str(gas_price) + 'boot', + gas=str(gas) + ) + ) + if display_data: + print(_msg) + print(_tx) + return self.bostrom_lcd_client.tx.broadcast(_tx).to_json() + + def create_passport(self, claim_row: pd.Series, display_data: bool = False): + return self.execute_contract( + execute_msg={"create_passport": {"avatar": claim_row["avatar"], "nickname": claim_row["nickname"]}}, + contract_address=self.name_dict['Passport Contract'], + mnemonic=claim_row['cosmos_seed'], + gas=500000, + display_data=display_data) + + def proof_address(self, claim_row: pd.Series, network: str = 'ethereum', display_data: bool = False): + return self.execute_contract( + execute_msg={ + "proof_address": {"address": claim_row[network + "_address"], "nickname": claim_row["nickname"], + "signature": claim_row[network + "_message_signature"]}}, + contract_address=self.name_dict['Passport Contract'], + mnemonic=claim_row['cosmos_seed'], + gas=400000, + display_data=display_data) + + def claim(self, claim_row: pd.Series, network: str = 'ethereum', display_data: bool = False): + return self.execute_contract( + execute_msg={ + "claim": {"nickname": claim_row['nickname'], "gift_claiming_address": claim_row[network + "_address"], + "gift_amount": str(claim_row['amount']), "proof": claim_row[network + "_proof"]}}, + contract_address=self.name_dict['Gift Contract'], + mnemonic=claim_row['cosmos_seed'], + gas=500000, + display_data=display_data) + + def release(self, claim_row: pd.Series, network: str = 'ethereum', display_data: bool = False): + return self.execute_contract( + execute_msg={"release": {"gift_address": claim_row[network + "_address"]}}, + contract_address=self.name_dict['Gift Contract'], + mnemonic=claim_row['cosmos_seed'], + gas=400000, + display_data=display_data) + + def transfer_passport(self, claim_row: pd.Series, token_id: str, to_address: str = '', display_data: bool = False): + if to_address == '': + to_address = claim_row['bostrom_address'] + return self.execute_contract( + execute_msg={"transfer_nft": {"recipient": to_address, "token_id": str(token_id)}}, + contract_address=self.name_dict['Passport Contract'], + mnemonic=claim_row['cosmos_seed'], + gas=500000, + display_data=display_data) + + def burn_passport(self, claim_row: pd.Series, token_id: str, display_data: bool = False): + return self.execute_contract( + execute_msg={"burn": {"token_id": token_id}}, + contract_address=self.name_dict['Passport Contract'], + mnemonic=claim_row['cosmos_seed'], + gas=400000, + display_data=display_data) + + def update_name(self, claim_row: pd.Series, new_nickname: str, display_data: bool = False): + return self.execute_contract( + execute_msg={'update_name': {'new_nickname': new_nickname, 'old_nickname': claim_row['nickname']}}, + contract_address=self.name_dict['Passport Contract'], + mnemonic=claim_row['cosmos_seed'], + gas=500000, + display_data=display_data) + + def update_avatar(self, claim_row: pd.Series, new_avatar: str, display_data: bool = False): + return self.execute_contract( + execute_msg={'update_avatar': {'new_avatar': new_avatar, 'nickname': claim_row['nickname']}}, + contract_address=self.name_dict['Passport Contract'], + mnemonic=claim_row['cosmos_seed'], + gas=500000, + display_data=display_data) + + def remove_address(self, claim_row: pd.Series, removed_address: str, display_data: bool = False): + return self.execute_contract( + execute_msg={'remove_address': {'address': removed_address, 'nickname': claim_row['nickname']}}, + contract_address=self.name_dict['Passport Contract'], + mnemonic=claim_row['cosmos_seed'], + gas=500000, + display_data=display_data) + + def get_contract_name(self, contract_address: str) -> str: + try: + return self.address_dict[contract_address] + except KeyError: + return contract_address + + def get_name_from_cid(self, ipfs_hash: str, row=None) -> str: + if row is None: + return ipfs_hash + cid_name_dict = { + row['avatar']: 'Avatar', + self.ipfs_client.add_str(row['nickname']): 'Nickname', + self.ipfs_client.add_str(row['ethereum_address']): 'Ethereum Address', + self.ipfs_client.add_str(row['cosmos_address']): 'Cosmos Address', + self.ipfs_client.add_str(row['bostrom_address']): 'Passport Owner Address', + self.ipfs_client.add_str('cyberhole'): 'cyberhole'} + try: + return cid_name_dict[ipfs_hash] + except KeyError: + return ipfs_hash + + @Error_Handler + def parse_contract_execution_json(self, contract_execution_json: str, row=None) -> None: + print('\nEvents') + _contract_execution_json = json.loads(contract_execution_json) + _logs = _contract_execution_json['logs'] + if _logs is None or len(_logs) == 0: + print(_contract_execution_json['raw_log']) + else: + for log_item in _logs: + for event_item in log_item['events']: + print('') + if event_item['type'] == 'message': + if len(event_item["attributes"]) == 3: + print( + f'message from {self.get_contract_name(event_item["attributes"][-1]["value"])} ' + f'{event_item["attributes"][1]["value"]} {event_item["attributes"][0]["value"]}') + else: + print(event_item) + elif event_item['type'] == 'execute': + print('execute') + for attr_item in event_item["attributes"]: + if attr_item["key"] == '_contract_address': + print(f'\texecute contract: {self.get_contract_name(attr_item["value"])}') + else: + print(f'\t{attr_item["key"]}: {self.get_contract_name(attr_item["value"])}') + elif event_item['type'] == 'reply': + print('reply') + for attr_item in event_item["attributes"]: + if attr_item["key"] == '_contract_address': + print(f'\treply contract: {self.get_contract_name(attr_item["value"])}') + else: + print(f'\t{attr_item["key"]}: {self.get_contract_name(attr_item["value"])}') + elif event_item['type'] == 'cyberlink': + print('cyberlinks') + for i, attr_item in enumerate(event_item['attributes']): + if attr_item['key'] == 'particleFrom': + print( + f'\t{self.get_name_from_cid(attr_item["value"], row=row)} -> ' + f'{self.get_name_from_cid(event_item["attributes"][i + 1]["value"], row=row)}') + elif attr_item['key'] == 'particleTo': + pass + elif attr_item['key'] == 'neuron': + print(f'\tneuron: {self.get_contract_name(attr_item["value"])}\n') + else: + print(f'\t{attr_item["key"]}: {self.get_contract_name(attr_item["value"])}') + elif event_item['type'] == 'coin_received': + print('coin received') + for attr_item in event_item["attributes"]: + print(f'\t{attr_item["key"]}: {self.get_contract_name(attr_item["value"])}') + elif event_item['type'] == 'coin_spent': + print('coin spent') + for attr_item in event_item["attributes"]: + print(f'\t{attr_item["key"]}: {self.get_contract_name(attr_item["value"])}') + elif event_item['type'] == 'wasm': + print('wasm') + for attr_item in event_item["attributes"]: + if attr_item["key"] == 'amount': + print(f'\t{attr_item["key"]}: {int(attr_item["value"]):>,}') + else: + print(f'\t{attr_item["key"]}: {self.get_contract_name(attr_item["value"])}') + elif event_item['type'] == 'transfer': + print('transfer') + for attr_item in event_item["attributes"]: + print(f'\t{attr_item["key"]}: {self.get_contract_name(attr_item["value"])}') + else: + print(event_item) + print(f"Gas used: {int(_contract_execution_json['gas_used']):>,}") + print(f"Tx hash: {_contract_execution_json['txhash']}") diff --git a/contracts/cw-cyber-gift/testdata/generate_test_data/create_passport_and_claim_job.py b/contracts/cw-cyber-gift/testdata/generate_test_data/create_passport_and_claim_job.py new file mode 100644 index 0000000..75358b3 --- /dev/null +++ b/contracts/cw-cyber-gift/testdata/generate_test_data/create_passport_and_claim_job.py @@ -0,0 +1,80 @@ +import pandas as pd +import sys +import json +from contract_utils import ContractUtils +import warnings +from time import sleep +from aiohttp.client_exceptions import ClientConnectorError +from cyber_sdk.exceptions import LCDResponseError + +warnings.filterwarnings("ignore") + +DISPLAY_TX_EXECUTION = False + + +def participation(row: pd.Series, address_dict: dict, release_bool: bool = False) -> dict: + + contract_utils = ContractUtils(ipfs_client=None, address_dict=address_dict) + if release_bool: + _release_ethereum_json = contract_utils.release(row, display_data=DISPLAY_TX_EXECUTION) + sleep(1) + _release_cosmos_json = contract_utils.release(row, network='cosmos', display_data=DISPLAY_TX_EXECUTION) + return { + 'release_ethereum': _release_ethereum_json, + 'release_cosmos': _release_cosmos_json + } + else: + _create_passport_json = contract_utils.create_passport(row, display_data=DISPLAY_TX_EXECUTION) + sleep(1) + _proof_ethereum_address_json = contract_utils.proof_address(row, display_data=DISPLAY_TX_EXECUTION) + sleep(1) + _proof_cosmos_address_json = contract_utils.proof_address(row, network='cosmos', + display_data=DISPLAY_TX_EXECUTION) + sleep(1) + _claim_ethereum_json = contract_utils.claim(row, display_data=DISPLAY_TX_EXECUTION) + sleep(1) + _claim_cosmos_json = contract_utils.claim(row, network='cosmos', display_data=DISPLAY_TX_EXECUTION) + return { + 'create': _create_passport_json, + 'proof_ethereum': _proof_ethereum_address_json, + 'proof_cosmos': _proof_cosmos_address_json, + 'claim_ethereum': _claim_ethereum_json, + 'claim_cosmos': _claim_cosmos_json + } + + +if __name__ == '__main__': + + source_file_name = sys.argv[1] + index_number = sys.argv[2] + gift_contract_address = sys.argv[3] + release_bool = True if len(sys.argv) > 4 and sys.argv[4] == 'True' else False + + log_file = f'temp/contract_release_execution_log_{index_number}.txt' \ + if release_bool else f'temp/contract_participation_execution_log_{index_number}.txt' + address_dict = {gift_contract_address: 'Gift Contract', + 'bostrom1fzm6gzyccl8jvdv3qq6hp9vs6ylaruervs4m06c7k0ntzn2f8faq7ha2z2': 'Passport Contract'} + + row = pd.read_csv(source_file_name).iloc[int(index_number) % 10_000] + row['ethereum_proof'] = row['ethereum_proof'].replace('\'', '').replace('[', '').replace(']', '').split(', ') + row['cosmos_proof'] = row['cosmos_proof'].replace('\'', '').replace('[', '').replace(']', '').split(', ') + + res = None + last_error = None + i = 0 + while res is None and i < 5: + try: + res = participation(row=row, address_dict=address_dict, release_bool=release_bool) + except (OSError, ClientConnectorError, LCDResponseError) as e: + sleep(10) + print(f'Error: {e}\n') + last_error = e + i += 1 + if res is not None: + with open(log_file, 'w') as convert_file: + convert_file.write(json.dumps(res)) + print(f"{row['bostrom_address']}: done") + elif last_error is not None: + with open(log_file, 'w') as convert_file: + convert_file.write(f'Error: {last_error}') + print(f"{row['bostrom_address']}: unsuccessful") diff --git a/contracts/cw-cyber-gift/testdata/generate_test_data/gift_and_passport_contracts_load_testing.ipynb b/contracts/cw-cyber-gift/testdata/generate_test_data/gift_and_passport_contracts_load_testing.ipynb new file mode 100644 index 0000000..d8f0b58 --- /dev/null +++ b/contracts/cw-cyber-gift/testdata/generate_test_data/gift_and_passport_contracts_load_testing.ipynb @@ -0,0 +1,1312 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "## Gift Contract Testing" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 1, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/grovybear/.local/lib/python3.9/site-packages/ipfshttpclient/client/__init__.py:75: VersionMismatch: Unsupported daemon version '0.12.2' (not in range: 0.5.0 ≤ … < 0.9.0)\n", + " warnings.warn(exceptions.VersionMismatch(version, minimum, maximum))\n" + ] + } + ], + "source": [ + "from web3.auto import w3\n", + "import pandas as pd\n", + "from cyberpy import generate_wallet, address_to_address\n", + "from cyberpy._message_signer import Message\n", + "from cyberpy._wallet import seed_to_privkey\n", + "from eth_account.messages import encode_defunct\n", + "import json\n", + "import random\n", + "from time import time, sleep\n", + "import ipfshttpclient\n", + "from tqdm.notebook import tqdm\n", + "from base64 import b64encode\n", + "from multiprocess import Pool\n", + "from math import ceil\n", + "from dotenv import dotenv_values\n", + "from itertools import chain\n", + "\n", + "from contract_utils import execute_bash, instantiate_contract, execute_contract_bash, query_contract, get_ipfs_cid_from_str, get_proofs, ContractUtils\n", + "\n", + "\n", + "ipfs_client = ipfshttpclient.connect()\n", + "\n", + "NUMBER_OF_PARTICIPANTS = 20_000\n", + "NUMBER_OF_ACTIVATED_PARTICIPANTS = 5_000\n", + "INITIAL_BALANCE = str(30_000_000_000)\n", + "COEF_UP = str(13)\n", + "COEF_DOWN = str(7)\n", + "TARGET_CLAIM = str(2_000)\n", + "CLAIM_AMOUNT_LIST = [int(round(20_000 * random.random(), -3)) + 20_000 for _ in range(NUMBER_OF_PARTICIPANTS)]\n", + "\n", + "NUMBER_OF_THREADS = 100\n", + "KEY_PHRASE = 'KEY PHRASE'\n", + "NICKNAME_LIST = [f'john{round(time())}{number}' for number in range(NUMBER_OF_PARTICIPANTS)]\n", + "AVATAR_CID_LIST = [get_ipfs_cid_from_str(_nickname + '_avatar') for _nickname in NICKNAME_LIST]\n", + "PREFIXES = ['cosmos', 'osmo', 'terra']\n", + "\n", + "WALLET_ADDRESS = dotenv_values('.env')['WALLET_ADDRESS']\n", + "WALLET_SEED = dotenv_values('.env')['WALLET_SEED']\n", + "PYTHON_PATH = dotenv_values('.env')['PYTHON_PATH']\n", + "\n", + "DISPLAY_TX_EXECUTION = False\n", + "CALCULATE_ADDRESS_SET = True\n", + "CALCULATE_PROOFS = True\n", + "SEND_COINS = True\n", + "\n", + "INIT_SUBGRAPH_CONTRACTS = False\n", + "SUBGRAPH_CODE_ID = str(4)\n", + "NAME_SUBGRAPH_CONTRACT_ADDRESS = 'bostrom1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftq67yzdk'\n", + "AVATAR_SUBGRAPH_CONTRACT_ADDRESS = 'bostrom1ghd753shjuwexxywmgs4xz7x2q732vcnkm6h2pyv9s6ah3hylvrqxkr9dg'\n", + "PROOF_SUBGRAPH_CONTRACT_ADDRESS = 'bostrom1xt4ahzz2x8hpkc0tk6ekte9x6crw4w6u0r67cyt3kz9syh24pd7s4t9edr'\n", + "\n", + "INIT_PASSPORT_CONTRACT = False\n", + "PASSPORT_CODE_ID = str(7)\n", + "PASSPORT_CONTRACT_ADDRESS = 'bostrom1fzm6gzyccl8jvdv3qq6hp9vs6ylaruervs4m06c7k0ntzn2f8faq7ha2z2'\n", + "\n", + "INIT_TREASURY_CONTRACT = False\n", + "TREASURY_CODE_ID = str(5)\n", + "TREASURY_CONTRACT_ADDRESS = 'bostrom19ttnpna4fpwuhw9dwjukah5esz9ruutquuvhxa7ulyyk047vvpvs7nh8wu'\n", + "\n", + "INIT_GIFT_CONTRACT = False\n", + "GIFT_CODE_ID = str(2)\n", + "GIFT_CONTRACT_ADDRESS = 'bostrom1gc5wcdn9ges00w0l2cfxd7r2puyflak5dmkg26rsh083afmnrjxq3q6aaa'\n", + "\n", + "PARTICIPANTS_FILE_NAME = 'temp/participants_test_data_' + str(NUMBER_OF_PARTICIPANTS) + '_addresses.csv'\n", + "ROOT_SOURCE_FILE_NAME = 'temp/root_testing_source_' + str(NUMBER_OF_PARTICIPANTS) + '_addresses.json'\n", + "PROOF_FILE_NAME = 'temp/proof_testing_' + str(NUMBER_OF_PARTICIPANTS) + '_addresses.json'\n", + "ROOT_FILE_NAME = 'temp/root_testing_' + str(NUMBER_OF_PARTICIPANTS) + '_addresses'" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Generate addresses and sign messages" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 2, + "outputs": [ + { + "data": { + "text/plain": " Unnamed: 0 amount nickname \\\n0 0 34000 john16548361750 \n1 1 34000 john16548361751 \n2 2 33000 john16548361752 \n3 3 32000 john16548361753 \n4 4 38000 john16548361754 \n\n avatar \\\n0 QmaaXjnSDLGwpSRHdzUU8XrLjVJPt6E5pE5JavMyTCD381 \n1 QmT7Qs7FRxLjjzJD5QxY7BDZ4R1VgdavE4NcNjQzr3EV1D \n2 QmQrf4uaZFS8YyouRuYW7RdSjZAY18jxB9t6NrWWqa9WKn \n3 QmYLg2fgKA6ptwXa4mFXyq2JqjEyTWMrFXwvu3J23zbepW \n4 QmNWX5GLuXzhhtZdxNAnWWC42gqJsA6A2fjsseB5HPVDyw \n\n ethereum_address \\\n0 0x3691db823a183aee18e998f2ddd77076a11fd74c \n1 0xb42fe61f2d97178c18d6f36c637e55ec207a5441 \n2 0x597ad4478d64d2702ad3187fd07ed2159a9b38d3 \n3 0xff0cf02f40e8021a91c48b79b7412c7b1c568b2f \n4 0xd8b950ffa0b84b4d73e377a5ca86ea0c233a83c7 \n\n ethereum_private_key \\\n0 0x2aa29fbec6aed68bf55a7025c4280b85d38683edf00a... \n1 0xe6df5403ea994063ff6e8ae25596433c157bf2cc1e97... \n2 0x046508e879cf32970d3150c439b20dbf4b5aedeccbe6... \n3 0x17ab2a2fbc4dc59d3d837892de0af854d922e79117bd... \n4 0x66907d36d32bd3e99be73912172ebb6198a768dfa049... \n\n bostrom_address \\\n0 bostrom14qx4d5nwyx5vj4e2gs6zfamjzydnvruydrj6gy \n1 bostrom17wuryy4kexsgyq6rq2k3x0735n4ntqeq3flav5 \n2 bostrom1ag0uyutudtdtr7aenpc0c0jaedrwrsjf0jfury \n3 bostrom1gqz7pm32fcvzzskk7dg3kp0vgzs4aqfad5ulu4 \n4 bostrom1x9966p9dwtcju7vy6lcc4mvvqsj5rnffxsz79q \n\n cosmos_address \\\n0 terra14qx4d5nwyx5vj4e2gs6zfamjzydnvruyg5uf5r \n1 osmo17wuryy4kexsgyq6rq2k3x0735n4ntqeq6pc7yp \n2 terra1ag0uyutudtdtr7aenpc0c0jaedrwrsjf2980lr \n3 cosmos1gqz7pm32fcvzzskk7dg3kp0vgzs4aqfaw8gvzj \n4 cosmos1x9966p9dwtcju7vy6lcc4mvvqsj5rnff9rkdm8 \n\n cosmos_seed \\\n0 hope captain swallow label fish clarify demise... \n1 science surprise lunar hip truth citizen excha... \n2 broccoli year exclude ship butter install goss... \n3 autumn nominee slab attend iron odor antique w... \n4 aware card grass item rural swallow alcohol co... \n\n message \\\n0 bostrom14qx4d5nwyx5vj4e2gs6zfamjzydnvruydrj6gy... \n1 bostrom17wuryy4kexsgyq6rq2k3x0735n4ntqeq3flav5... \n2 bostrom1ag0uyutudtdtr7aenpc0c0jaedrwrsjf0jfury... \n3 bostrom1gqz7pm32fcvzzskk7dg3kp0vgzs4aqfad5ulu4... \n4 bostrom1x9966p9dwtcju7vy6lcc4mvvqsj5rnffxsz79q... \n\n ethereum_message_signature \\\n0 0xb38a11a271602c2dc33c81602ad994810f9cf48dfb4d... \n1 0x84c8f25ca81c784d243e406ec8a4a1e389aa728dc235... \n2 0xfc895e22ddcfc885b17b91fbc21146aa422d64e2ac43... \n3 0x9a6179bbb941309bd5247e82a30f6b1fab4f63bcdc87... \n4 0x59f2175d252ba9a551c8f9e957e26592769e2181e594... \n\n cosmos_message_signed_row \\\n0 {'pub_key': 'AyjWt69QbKZ4V09ZCXKDN+bRgnw0H3R9B... \n1 {'pub_key': 'A9bjehAjjY32eiYHqWier3WVEOdZUwZ8Q... \n2 {'pub_key': 'AtTl2h3Nafoifz384IlN2M97SvoX/oZiQ... \n3 {'pub_key': 'Amw0GvH82F8z30EaAGnRqT75oSpWxMWzI... \n4 {'pub_key': 'AtDo/b+BravMbmqq8qoD40ROzNA84J2iY... \n\n cosmos_message_signature \n0 eyJwdWJfa2V5IjoiQXlqV3Q2OVFiS1o0VjA5WkNYS0ROK2... \n1 eyJwdWJfa2V5IjoiQTliamVoQWpqWTMyZWlZSHFXaWVyM1... \n2 eyJwdWJfa2V5IjoiQXRUbDJoM05hZm9pZnozODRJbE4yTT... \n3 eyJwdWJfa2V5IjoiQW13MEd2SDgyRjh6MzBFYUFHblJxVD... \n4 eyJwdWJfa2V5IjoiQXREby9iK0JyYXZNYm1xcThxb0Q0MF... ", + "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
Unnamed: 0amountnicknameavatarethereum_addressethereum_private_keybostrom_addresscosmos_addresscosmos_seedmessageethereum_message_signaturecosmos_message_signed_rowcosmos_message_signature
0034000john16548361750QmaaXjnSDLGwpSRHdzUU8XrLjVJPt6E5pE5JavMyTCD3810x3691db823a183aee18e998f2ddd77076a11fd74c0x2aa29fbec6aed68bf55a7025c4280b85d38683edf00a...bostrom14qx4d5nwyx5vj4e2gs6zfamjzydnvruydrj6gyterra14qx4d5nwyx5vj4e2gs6zfamjzydnvruyg5uf5rhope captain swallow label fish clarify demise...bostrom14qx4d5nwyx5vj4e2gs6zfamjzydnvruydrj6gy...0xb38a11a271602c2dc33c81602ad994810f9cf48dfb4d...{'pub_key': 'AyjWt69QbKZ4V09ZCXKDN+bRgnw0H3R9B...eyJwdWJfa2V5IjoiQXlqV3Q2OVFiS1o0VjA5WkNYS0ROK2...
1134000john16548361751QmT7Qs7FRxLjjzJD5QxY7BDZ4R1VgdavE4NcNjQzr3EV1D0xb42fe61f2d97178c18d6f36c637e55ec207a54410xe6df5403ea994063ff6e8ae25596433c157bf2cc1e97...bostrom17wuryy4kexsgyq6rq2k3x0735n4ntqeq3flav5osmo17wuryy4kexsgyq6rq2k3x0735n4ntqeq6pc7ypscience surprise lunar hip truth citizen excha...bostrom17wuryy4kexsgyq6rq2k3x0735n4ntqeq3flav5...0x84c8f25ca81c784d243e406ec8a4a1e389aa728dc235...{'pub_key': 'A9bjehAjjY32eiYHqWier3WVEOdZUwZ8Q...eyJwdWJfa2V5IjoiQTliamVoQWpqWTMyZWlZSHFXaWVyM1...
2233000john16548361752QmQrf4uaZFS8YyouRuYW7RdSjZAY18jxB9t6NrWWqa9WKn0x597ad4478d64d2702ad3187fd07ed2159a9b38d30x046508e879cf32970d3150c439b20dbf4b5aedeccbe6...bostrom1ag0uyutudtdtr7aenpc0c0jaedrwrsjf0jfuryterra1ag0uyutudtdtr7aenpc0c0jaedrwrsjf2980lrbroccoli year exclude ship butter install goss...bostrom1ag0uyutudtdtr7aenpc0c0jaedrwrsjf0jfury...0xfc895e22ddcfc885b17b91fbc21146aa422d64e2ac43...{'pub_key': 'AtTl2h3Nafoifz384IlN2M97SvoX/oZiQ...eyJwdWJfa2V5IjoiQXRUbDJoM05hZm9pZnozODRJbE4yTT...
3332000john16548361753QmYLg2fgKA6ptwXa4mFXyq2JqjEyTWMrFXwvu3J23zbepW0xff0cf02f40e8021a91c48b79b7412c7b1c568b2f0x17ab2a2fbc4dc59d3d837892de0af854d922e79117bd...bostrom1gqz7pm32fcvzzskk7dg3kp0vgzs4aqfad5ulu4cosmos1gqz7pm32fcvzzskk7dg3kp0vgzs4aqfaw8gvzjautumn nominee slab attend iron odor antique w...bostrom1gqz7pm32fcvzzskk7dg3kp0vgzs4aqfad5ulu4...0x9a6179bbb941309bd5247e82a30f6b1fab4f63bcdc87...{'pub_key': 'Amw0GvH82F8z30EaAGnRqT75oSpWxMWzI...eyJwdWJfa2V5IjoiQW13MEd2SDgyRjh6MzBFYUFHblJxVD...
4438000john16548361754QmNWX5GLuXzhhtZdxNAnWWC42gqJsA6A2fjsseB5HPVDyw0xd8b950ffa0b84b4d73e377a5ca86ea0c233a83c70x66907d36d32bd3e99be73912172ebb6198a768dfa049...bostrom1x9966p9dwtcju7vy6lcc4mvvqsj5rnffxsz79qcosmos1x9966p9dwtcju7vy6lcc4mvvqsj5rnff9rkdm8aware card grass item rural swallow alcohol co...bostrom1x9966p9dwtcju7vy6lcc4mvvqsj5rnffxsz79q...0x59f2175d252ba9a551c8f9e957e26592769e2181e594...{'pub_key': 'AtDo/b+BravMbmqq8qoD40ROzNA84J2iY...eyJwdWJfa2V5IjoiQXREby9iK0JyYXZNYm1xcThxb0Q0MF...
\n
" + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def generate_wallet_data(i: int, prefixes: list = PREFIXES) -> dict:\n", + "\n", + " prefix = random.choice(prefixes)\n", + "\n", + " claim_item = {'amount': CLAIM_AMOUNT_LIST[i],\n", + " 'nickname': NICKNAME_LIST[i],\n", + " 'avatar': AVATAR_CID_LIST[i]}\n", + "\n", + " # Generate Ethereum wallet\n", + " ethereum_wallet = w3.eth.account.create(KEY_PHRASE)\n", + " claim_item['ethereum_address'] = ethereum_wallet.address.lower()\n", + " claim_item['ethereum_private_key'] = ethereum_wallet.privateKey.hex()\n", + "\n", + " # Generate Bostrom and Cosmos wallet\n", + " bostrom_wallet = generate_wallet()\n", + " claim_item['bostrom_address'] = bostrom_wallet['address']\n", + " claim_item['cosmos_address'] = address_to_address(bostrom_wallet['address'], prefix)\n", + " claim_item['cosmos_seed'] = bostrom_wallet['seed']\n", + "\n", + " # Create message\n", + " claim_item['message'] = f\"{claim_item['bostrom_address']}:QmRX8qYgeZoYM3M5zzQaWEpVFdpin6FvVXvp6RPQK3oufV\"\n", + "\n", + " # Sign message form Ethereum address\n", + " ethereum_signed_message = \\\n", + " w3.eth.account.sign_message(\n", + " signable_message=encode_defunct(text=claim_item['message']),\n", + " private_key=ethereum_wallet.privateKey)\n", + " claim_item['ethereum_message_signature'] = ethereum_signed_message.signature.hex()\n", + "\n", + " # Sign message form Cosmos address\n", + " cosmos_msg = Message(privkey=seed_to_privkey(seed=claim_item['cosmos_seed']))\n", + " cosmos_msg.add_message(signing_message=claim_item['message'], signer_prefix=prefix)\n", + " claim_item['cosmos_message_signed_row'] = cosmos_msg.get_signed_message()\n", + " claim_item['cosmos_message_signature'] = b64encode(json.dumps(claim_item['cosmos_message_signed_row']).replace('\\n', '').replace(' ', '').encode('utf-8')).decode(\"utf-8\")\n", + "\n", + " # Verify message\n", + " assert claim_item['ethereum_address'] == w3.eth.account.recover_message(\n", + " signable_message=encode_defunct(text=claim_item['message']),\n", + " signature=claim_item['ethereum_message_signature']).lower()\n", + "\n", + " return claim_item\n", + "\n", + "\n", + "if CALCULATE_ADDRESS_SET:\n", + "\n", + " tasks = list(range(NUMBER_OF_PARTICIPANTS))\n", + " print(f'Number of tasks: {len(tasks)}')\n", + " print(f'Number of threads: {NUMBER_OF_THREADS}')\n", + " with Pool(processes=NUMBER_OF_THREADS) as pool:\n", + " claims_list = list(tqdm(pool.imap(generate_wallet_data, tasks), total=len(tasks)))\n", + "\n", + " claims_df = pd.DataFrame(claims_list)\n", + " claims_df.to_csv('temp/claims_ethereum_test_data_without_proof_' + str(NUMBER_OF_PARTICIPANTS) + '_addresses.csv')\n", + "else:\n", + " claims_df = pd.read_csv('temp/claims_ethereum_test_data_without_proof_' + str(NUMBER_OF_PARTICIPANTS) + '_addresses.csv')\n", + "claims_df.head()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Create Merkle Tree, Get Root and Proofs" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 3, + "outputs": [], + "source": [ + "root_source_list_ethereum = \\\n", + " [{'address': _item[0],\n", + " 'amount': str(_item[1])}\n", + " for _item in claims_df.loc[:, ['ethereum_address', 'amount']].values.tolist()]\n", + "root_source_list_cosmos = \\\n", + " [{'address': _item[0],\n", + " 'amount': str(_item[1])}\n", + " for _item in claims_df.loc[:, ['cosmos_address', 'amount']].values.tolist()]\n", + "root_source_list = list(chain.from_iterable(zip(root_source_list_ethereum, root_source_list_cosmos)))\n", + "\n", + "with open(ROOT_SOURCE_FILE_NAME, 'w') as outfile:\n", + " outfile.write(str(root_source_list).replace(\"'\", '\"'))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 4, + "outputs": [], + "source": [ + "NUMBER_OF_THREADS = 30\n", + "BASH_SIZE = 2000\n", + "\n", + "tasks = set(\n", + " (\n", + " (ROOT_SOURCE_FILE_NAME,\n", + " f'temp/proofs_{i}.json',\n", + " i * BASH_SIZE,\n", + " min(NUMBER_OF_ACTIVATED_PARTICIPANTS * 2 + 1, (i + 1) * BASH_SIZE + 1))\n", + " for i in range(ceil(2 * NUMBER_OF_ACTIVATED_PARTICIPANTS/BASH_SIZE))\n", + " )\n", + ")\n", + "if CALCULATE_PROOFS:\n", + " print(f'Number of addresses: {2 * NUMBER_OF_PARTICIPANTS:>,}\\nNumber of activated participants: {NUMBER_OF_ACTIVATED_PARTICIPANTS:>,}\\nNumber of threads: {NUMBER_OF_THREADS:>,}\\nBash size: {BASH_SIZE:>,}\\nNumber of tasks: {len(tasks):>,}')\n", + " with Pool(processes=NUMBER_OF_THREADS) as pool:\n", + " res = pool.starmap(get_proofs, tqdm(tasks, total=len(tasks)))\n", + " assert res == [True] * len(res)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 5, + "outputs": [ + { + "data": { + "text/plain": " Unnamed: 0 amount nickname \\\n0 0 34000 john16548361750 \n1 1 34000 john16548361751 \n2 2 33000 john16548361752 \n3 3 32000 john16548361753 \n4 4 38000 john16548361754 \n\n avatar \\\n0 QmaaXjnSDLGwpSRHdzUU8XrLjVJPt6E5pE5JavMyTCD381 \n1 QmT7Qs7FRxLjjzJD5QxY7BDZ4R1VgdavE4NcNjQzr3EV1D \n2 QmQrf4uaZFS8YyouRuYW7RdSjZAY18jxB9t6NrWWqa9WKn \n3 QmYLg2fgKA6ptwXa4mFXyq2JqjEyTWMrFXwvu3J23zbepW \n4 QmNWX5GLuXzhhtZdxNAnWWC42gqJsA6A2fjsseB5HPVDyw \n\n ethereum_address \\\n0 0x3691db823a183aee18e998f2ddd77076a11fd74c \n1 0xb42fe61f2d97178c18d6f36c637e55ec207a5441 \n2 0x597ad4478d64d2702ad3187fd07ed2159a9b38d3 \n3 0xff0cf02f40e8021a91c48b79b7412c7b1c568b2f \n4 0xd8b950ffa0b84b4d73e377a5ca86ea0c233a83c7 \n\n ethereum_private_key \\\n0 0x2aa29fbec6aed68bf55a7025c4280b85d38683edf00a... \n1 0xe6df5403ea994063ff6e8ae25596433c157bf2cc1e97... \n2 0x046508e879cf32970d3150c439b20dbf4b5aedeccbe6... \n3 0x17ab2a2fbc4dc59d3d837892de0af854d922e79117bd... \n4 0x66907d36d32bd3e99be73912172ebb6198a768dfa049... \n\n bostrom_address \\\n0 bostrom14qx4d5nwyx5vj4e2gs6zfamjzydnvruydrj6gy \n1 bostrom17wuryy4kexsgyq6rq2k3x0735n4ntqeq3flav5 \n2 bostrom1ag0uyutudtdtr7aenpc0c0jaedrwrsjf0jfury \n3 bostrom1gqz7pm32fcvzzskk7dg3kp0vgzs4aqfad5ulu4 \n4 bostrom1x9966p9dwtcju7vy6lcc4mvvqsj5rnffxsz79q \n\n cosmos_address \\\n0 terra14qx4d5nwyx5vj4e2gs6zfamjzydnvruyg5uf5r \n1 osmo17wuryy4kexsgyq6rq2k3x0735n4ntqeq6pc7yp \n2 terra1ag0uyutudtdtr7aenpc0c0jaedrwrsjf2980lr \n3 cosmos1gqz7pm32fcvzzskk7dg3kp0vgzs4aqfaw8gvzj \n4 cosmos1x9966p9dwtcju7vy6lcc4mvvqsj5rnff9rkdm8 \n\n cosmos_seed \\\n0 hope captain swallow label fish clarify demise... \n1 science surprise lunar hip truth citizen excha... \n2 broccoli year exclude ship butter install goss... \n3 autumn nominee slab attend iron odor antique w... \n4 aware card grass item rural swallow alcohol co... \n\n message \\\n0 bostrom14qx4d5nwyx5vj4e2gs6zfamjzydnvruydrj6gy... \n1 bostrom17wuryy4kexsgyq6rq2k3x0735n4ntqeq3flav5... \n2 bostrom1ag0uyutudtdtr7aenpc0c0jaedrwrsjf0jfury... \n3 bostrom1gqz7pm32fcvzzskk7dg3kp0vgzs4aqfad5ulu4... \n4 bostrom1x9966p9dwtcju7vy6lcc4mvvqsj5rnffxsz79q... \n\n ethereum_message_signature \\\n0 0xb38a11a271602c2dc33c81602ad994810f9cf48dfb4d... \n1 0x84c8f25ca81c784d243e406ec8a4a1e389aa728dc235... \n2 0xfc895e22ddcfc885b17b91fbc21146aa422d64e2ac43... \n3 0x9a6179bbb941309bd5247e82a30f6b1fab4f63bcdc87... \n4 0x59f2175d252ba9a551c8f9e957e26592769e2181e594... \n\n cosmos_message_signed_row \\\n0 {'pub_key': 'AyjWt69QbKZ4V09ZCXKDN+bRgnw0H3R9B... \n1 {'pub_key': 'A9bjehAjjY32eiYHqWier3WVEOdZUwZ8Q... \n2 {'pub_key': 'AtTl2h3Nafoifz384IlN2M97SvoX/oZiQ... \n3 {'pub_key': 'Amw0GvH82F8z30EaAGnRqT75oSpWxMWzI... \n4 {'pub_key': 'AtDo/b+BravMbmqq8qoD40ROzNA84J2iY... \n\n cosmos_message_signature \\\n0 eyJwdWJfa2V5IjoiQXlqV3Q2OVFiS1o0VjA5WkNYS0ROK2... \n1 eyJwdWJfa2V5IjoiQTliamVoQWpqWTMyZWlZSHFXaWVyM1... \n2 eyJwdWJfa2V5IjoiQXRUbDJoM05hZm9pZnozODRJbE4yTT... \n3 eyJwdWJfa2V5IjoiQW13MEd2SDgyRjh6MzBFYUFHblJxVD... \n4 eyJwdWJfa2V5IjoiQXREby9iK0JyYXZNYm1xcThxb0Q0MF... \n\n ethereum_proof \\\n0 [4710b3ef8cb686c8b316654c7a6587fe33c256755bd65... \n1 [cc3c069b0ce8d95d69e48a7b4f3728d892a36270e3e52... \n2 [50a1a2537255c5e1f077442076b2273671acc7b2d5a46... \n3 [e069f04ee7b66a3181a7fcc473cde8fb80b10ef8077f3... \n4 [5239d46e245ce4af1f69ec17f7786c6042fea7f62a0d8... \n\n cosmos_proof \n0 [172e89389492e7389a96c98cae5190ffcae04a8d2eb85... \n1 [5398e38c29e181c2ff18cd4df1215ca90677cca24eeb6... \n2 [ad1576871f19a385d00e90552c06a38d084ba80aa043f... \n3 [0da1f0fe7351add378fb84ade27270a297bce88360b95... \n4 [9bb1d32faec7ad21dd219cce62196c374806fd1d77d36... ", + "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
Unnamed: 0amountnicknameavatarethereum_addressethereum_private_keybostrom_addresscosmos_addresscosmos_seedmessageethereum_message_signaturecosmos_message_signed_rowcosmos_message_signatureethereum_proofcosmos_proof
0034000john16548361750QmaaXjnSDLGwpSRHdzUU8XrLjVJPt6E5pE5JavMyTCD3810x3691db823a183aee18e998f2ddd77076a11fd74c0x2aa29fbec6aed68bf55a7025c4280b85d38683edf00a...bostrom14qx4d5nwyx5vj4e2gs6zfamjzydnvruydrj6gyterra14qx4d5nwyx5vj4e2gs6zfamjzydnvruyg5uf5rhope captain swallow label fish clarify demise...bostrom14qx4d5nwyx5vj4e2gs6zfamjzydnvruydrj6gy...0xb38a11a271602c2dc33c81602ad994810f9cf48dfb4d...{'pub_key': 'AyjWt69QbKZ4V09ZCXKDN+bRgnw0H3R9B...eyJwdWJfa2V5IjoiQXlqV3Q2OVFiS1o0VjA5WkNYS0ROK2...[4710b3ef8cb686c8b316654c7a6587fe33c256755bd65...[172e89389492e7389a96c98cae5190ffcae04a8d2eb85...
1134000john16548361751QmT7Qs7FRxLjjzJD5QxY7BDZ4R1VgdavE4NcNjQzr3EV1D0xb42fe61f2d97178c18d6f36c637e55ec207a54410xe6df5403ea994063ff6e8ae25596433c157bf2cc1e97...bostrom17wuryy4kexsgyq6rq2k3x0735n4ntqeq3flav5osmo17wuryy4kexsgyq6rq2k3x0735n4ntqeq6pc7ypscience surprise lunar hip truth citizen excha...bostrom17wuryy4kexsgyq6rq2k3x0735n4ntqeq3flav5...0x84c8f25ca81c784d243e406ec8a4a1e389aa728dc235...{'pub_key': 'A9bjehAjjY32eiYHqWier3WVEOdZUwZ8Q...eyJwdWJfa2V5IjoiQTliamVoQWpqWTMyZWlZSHFXaWVyM1...[cc3c069b0ce8d95d69e48a7b4f3728d892a36270e3e52...[5398e38c29e181c2ff18cd4df1215ca90677cca24eeb6...
2233000john16548361752QmQrf4uaZFS8YyouRuYW7RdSjZAY18jxB9t6NrWWqa9WKn0x597ad4478d64d2702ad3187fd07ed2159a9b38d30x046508e879cf32970d3150c439b20dbf4b5aedeccbe6...bostrom1ag0uyutudtdtr7aenpc0c0jaedrwrsjf0jfuryterra1ag0uyutudtdtr7aenpc0c0jaedrwrsjf2980lrbroccoli year exclude ship butter install goss...bostrom1ag0uyutudtdtr7aenpc0c0jaedrwrsjf0jfury...0xfc895e22ddcfc885b17b91fbc21146aa422d64e2ac43...{'pub_key': 'AtTl2h3Nafoifz384IlN2M97SvoX/oZiQ...eyJwdWJfa2V5IjoiQXRUbDJoM05hZm9pZnozODRJbE4yTT...[50a1a2537255c5e1f077442076b2273671acc7b2d5a46...[ad1576871f19a385d00e90552c06a38d084ba80aa043f...
3332000john16548361753QmYLg2fgKA6ptwXa4mFXyq2JqjEyTWMrFXwvu3J23zbepW0xff0cf02f40e8021a91c48b79b7412c7b1c568b2f0x17ab2a2fbc4dc59d3d837892de0af854d922e79117bd...bostrom1gqz7pm32fcvzzskk7dg3kp0vgzs4aqfad5ulu4cosmos1gqz7pm32fcvzzskk7dg3kp0vgzs4aqfaw8gvzjautumn nominee slab attend iron odor antique w...bostrom1gqz7pm32fcvzzskk7dg3kp0vgzs4aqfad5ulu4...0x9a6179bbb941309bd5247e82a30f6b1fab4f63bcdc87...{'pub_key': 'Amw0GvH82F8z30EaAGnRqT75oSpWxMWzI...eyJwdWJfa2V5IjoiQW13MEd2SDgyRjh6MzBFYUFHblJxVD...[e069f04ee7b66a3181a7fcc473cde8fb80b10ef8077f3...[0da1f0fe7351add378fb84ade27270a297bce88360b95...
4438000john16548361754QmNWX5GLuXzhhtZdxNAnWWC42gqJsA6A2fjsseB5HPVDyw0xd8b950ffa0b84b4d73e377a5ca86ea0c233a83c70x66907d36d32bd3e99be73912172ebb6198a768dfa049...bostrom1x9966p9dwtcju7vy6lcc4mvvqsj5rnffxsz79qcosmos1x9966p9dwtcju7vy6lcc4mvvqsj5rnff9rkdm8aware card grass item rural swallow alcohol co...bostrom1x9966p9dwtcju7vy6lcc4mvvqsj5rnffxsz79q...0x59f2175d252ba9a551c8f9e957e26592769e2181e594...{'pub_key': 'AtDo/b+BravMbmqq8qoD40ROzNA84J2iY...eyJwdWJfa2V5IjoiQXREby9iK0JyYXZNYm1xcThxb0Q0MF...[5239d46e245ce4af1f69ec17f7786c6042fea7f62a0d8...[9bb1d32faec7ad21dd219cce62196c374806fd1d77d36...
\n
" + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "if CALCULATE_PROOFS:\n", + " roots = []\n", + " proofs_df = pd.DataFrame(columns=['address', 'amount', 'proof'])\n", + " for task in tasks:\n", + " with open(task[1], 'r') as proof_file:\n", + " root_and_proof_json = json.load(proof_file)\n", + " roots.append(root_and_proof_json['merkle_root'])\n", + " proofs_df = pd.concat([proofs_df, pd.DataFrame(root_and_proof_json['proofs'])], ignore_index=True)\n", + " assert roots == [roots[0]] * len(roots)\n", + " root = roots[0]\n", + "\n", + " cosmos_proofs_df = proofs_df[~proofs_df.address.str.startswith('0x')][['address', 'proof']]\n", + " ethereum_proofs_df = proofs_df[proofs_df.address.str.startswith('0x')][['address', 'proof']]\n", + "\n", + " claims_with_proofs_df = claims_df\\\n", + " .merge(\n", + " ethereum_proofs_df.rename(columns={'address': 'ethereum_address', 'proof': 'ethereum_proof'}),\n", + " how='inner',\n", + " on='ethereum_address')\\\n", + " .merge(\n", + " cosmos_proofs_df.rename(columns={'address': 'cosmos_address', 'proof': 'cosmos_proof'}),\n", + " how='inner',\n", + " on='cosmos_address')\n", + "\n", + " claims_with_proofs_df.to_csv(PARTICIPANTS_FILE_NAME)\n", + "else:\n", + " claims_with_proofs_df = pd.read_csv(PARTICIPANTS_FILE_NAME)\n", + " claims_with_proofs_df.loc[:, 'ethereum_proof'] = claims_with_proofs_df['ethereum_proof'].map(lambda x: x.replace('\\'', '').replace('[', '').replace(']', '').split(', '))\n", + " claims_with_proofs_df.loc[:, 'cosmos_proof'] = claims_with_proofs_df['cosmos_proof'].map(lambda x: x.replace('\\'', '').replace('[', '').replace(']', '').split(', '))\n", + " with open('temp/proofs_1.json', 'r') as proof_file:\n", + " root_and_proof_json = json.load(proof_file)\n", + " root = root_and_proof_json['merkle_root']\n", + "claims_with_proofs_df.head()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 6, + "outputs": [], + "source": [ + "for i in range(ceil(NUMBER_OF_ACTIVATED_PARTICIPANTS/10_000)):\n", + " claims_with_proofs_df.loc[i*10_000:(i+1)*10_000,:].to_csv(f'temp/{i}_{PARTICIPANTS_FILE_NAME.split(\"/\")[1]}')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Instantiate Contracts\n", + "### Instantiate SUBGRAPH Contracts" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 7, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Name subgraph contract address: bostrom1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftq67yzdk\n", + "Avatar subgraph contract address: bostrom1ghd753shjuwexxywmgs4xz7x2q732vcnkm6h2pyv9s6ah3hylvrqxkr9dg\n", + "Proof subgraph contract address: bostrom1xt4ahzz2x8hpkc0tk6ekte9x6crw4w6u0r67cyt3kz9syh24pd7s4t9edr\n" + ] + } + ], + "source": [ + "if INIT_SUBGRAPH_CONTRACTS:\n", + " name_subgraph_contract_address = \\\n", + " instantiate_contract(\n", + " init_query=f'''{{\"owner\":\"{WALLET_ADDRESS}\", \"executer\":\"{WALLET_ADDRESS}\"}}''',\n", + " contract_code_id=SUBGRAPH_CODE_ID,\n", + " contract_label='test name subgraph')\n", + " avatar_subgraph_contract_address = \\\n", + " instantiate_contract(\n", + " init_query=f'''{{\"owner\":\"{WALLET_ADDRESS}\", \"executer\":\"{WALLET_ADDRESS}\"}}''',\n", + " contract_code_id=SUBGRAPH_CODE_ID,\n", + " contract_label='test avatar subgraph',\n", + " from_address=WALLET_ADDRESS)\n", + " proof_subgraph_contract_address = \\\n", + " instantiate_contract(\n", + " init_query=f'''{{\"owner\":\"{WALLET_ADDRESS}\", \"executer\":\"{WALLET_ADDRESS}\"}}''',\n", + " contract_code_id=SUBGRAPH_CODE_ID,\n", + " contract_label='test proof subgraph',\n", + " from_address=WALLET_ADDRESS)\n", + "else:\n", + " name_subgraph_contract_address = NAME_SUBGRAPH_CONTRACT_ADDRESS\n", + " avatar_subgraph_contract_address = AVATAR_SUBGRAPH_CONTRACT_ADDRESS\n", + " proof_subgraph_contract_address = PROOF_SUBGRAPH_CONTRACT_ADDRESS\n", + "print(f'Name subgraph contract address: {name_subgraph_contract_address}\\n'\n", + " f'Avatar subgraph contract address: {avatar_subgraph_contract_address}\\n'\n", + " f'Proof subgraph contract address: {proof_subgraph_contract_address}')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Instantiate Passport Contract" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 8, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Passport contract address: bostrom1fzm6gzyccl8jvdv3qq6hp9vs6ylaruervs4m06c7k0ntzn2f8faq7ha2z2\n" + ] + } + ], + "source": [ + "if INIT_PASSPORT_CONTRACT:\n", + " passport_contract_address = \\\n", + " instantiate_contract(\n", + " init_query=f'''{{\"name\":\"CPT\", \"minter\":\"{WALLET_ADDRESS}\", \"owner\":\"{WALLET_ADDRESS}\", \"symbol\":\"CPT\", \"avatar_subgraph\": \"{avatar_subgraph_contract_address}\", \"name_subgraph\": \"{name_subgraph_contract_address}\", \"proof_subgraph\": \"{proof_subgraph_contract_address}\"}}''',\n", + " contract_code_id=PASSPORT_CODE_ID,\n", + " contract_label='test passport',\n", + " from_address=WALLET_ADDRESS)\n", + "else:\n", + " passport_contract_address = PASSPORT_CONTRACT_ADDRESS\n", + "print(f'Passport contract address: {passport_contract_address}')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Set executor in the Subgraph Contracts" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 9, + "outputs": [], + "source": [ + "def set_executor_subgraph(subgraph_contract_address: str, new_executor_address: str, display_data: bool = False):\n", + " return execute_contract_bash(execute_query=f'''{{\"update_executer\":{{\"new_executer\":\"{new_executor_address}\"}}}}''',\n", + " contract_address=subgraph_contract_address,\n", + " gas=600000,\n", + " display_data=display_data)\n", + "\n", + "if INIT_PASSPORT_CONTRACT or INIT_SUBGRAPH_CONTRACTS:\n", + " set_executor_subgraph(subgraph_contract_address=name_subgraph_contract_address, new_executor_address=passport_contract_address)\n", + " set_executor_subgraph(subgraph_contract_address=avatar_subgraph_contract_address, new_executor_address=passport_contract_address)\n", + " set_executor_subgraph(subgraph_contract_address=proof_subgraph_contract_address, new_executor_address=passport_contract_address)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Instantiate Treasury Contract" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 10, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Treasury contract address: bostrom19ttnpna4fpwuhw9dwjukah5esz9ruutquuvhxa7ulyyk047vvpvs7nh8wu\n" + ] + } + ], + "source": [ + "if INIT_TREASURY_CONTRACT:\n", + " treasury_contract_address = \\\n", + " instantiate_contract(\n", + " init_query=f'''{{\"admins\": [\"{WALLET_ADDRESS}\"], \"mutable\": true}}''',\n", + " contract_code_id=TREASURY_CODE_ID,\n", + " amount=INITIAL_BALANCE,\n", + " contract_label='test treasury',\n", + " from_address=WALLET_ADDRESS,\n", + " display_data=False)\n", + "else:\n", + " treasury_contract_address = TREASURY_CONTRACT_ADDRESS\n", + "print(f'Treasury contract address: {treasury_contract_address}')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Instantiate Gift Contract" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 11, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Gift contract address: bostrom1gc5wcdn9ges00w0l2cfxd7r2puyflak5dmkg26rsh083afmnrjxq3q6aaa\n" + ] + } + ], + "source": [ + "if INIT_GIFT_CONTRACT:\n", + " gift_contract_address = \\\n", + " instantiate_contract(\n", + " init_query=f'''{{\"owner\":\"{WALLET_ADDRESS}\", \"passport\":\"{passport_contract_address}\", \"treasury\":\"{treasury_contract_address}\", \"allowed_native\":\"boot\", \"initial_balance\":\"{INITIAL_BALANCE}\", \"coefficient_up\":\"{COEF_UP}\", \"coefficient_down\":\"{COEF_DOWN}\", \"coefficient\":\"{COEF_UP}\", \"target_claim\":\"{TARGET_CLAIM}\"}}''',\n", + " contract_code_id=GIFT_CODE_ID,\n", + " contract_label='test gift',\n", + " from_address=WALLET_ADDRESS,\n", + " display_data=False)\n", + "else:\n", + " gift_contract_address = GIFT_CONTRACT_ADDRESS\n", + "print(f'Gift contract address: {gift_contract_address}')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Initiate Class of Output Parsing" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 12, + "outputs": [], + "source": [ + "contract_utils = ContractUtils(ipfs_client=ipfs_client,\n", + " address_dict= {\n", + " gift_contract_address: 'Gift Contract',\n", + " passport_contract_address: 'Passport Contract',\n", + " treasury_contract_address: 'Treasury Contract',\n", + " WALLET_ADDRESS: 'Passport Owner Address',\n", + " name_subgraph_contract_address: 'Name Subgraph Contract',\n", + " avatar_subgraph_contract_address: 'Avatar Subgraph Contract',\n", + " proof_subgraph_contract_address: 'Proof Subgraph Contract'})" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Set Allowance in Treasury contract" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 13, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Events\n", + "\n", + "coin received\n", + "\treceiver: Treasury Contract\n", + "\tamount: 30000000000boot\n", + "\n", + "coin spent\n", + "\tspender: Passport Owner Address\n", + "\tamount: 30000000000boot\n", + "\n", + "message from bank bostrom1mxdtr8lruutugqtxgpw2sf2tl2mhzlq5fd2du0 /cosmos.bank.v1beta1.MsgMultiSend\n", + "\n", + "transfer\n", + "\trecipient: Treasury Contract\n", + "\tamount: 30000000000boot\n", + "Gas used: 86,568\n", + "Tx hash: 823B7CB39E0B68EDD6ABC409CF595F9923BE936A7E7A386996185C83981C081E\n" + ] + } + ], + "source": [ + "contract_utils.parse_contract_execution_json(\n", + " contract_utils.send_coins(\n", + " from_seed=WALLET_SEED,\n", + " to_addresses=[treasury_contract_address],\n", + " amounts=[INITIAL_BALANCE],\n", + " gas=min(100_000 + 25_000, int(20e6)),\n", + " display_data=DISPLAY_TX_EXECUTION))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 14, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Events\n", + "\n", + "execute\n", + "\texecute contract: Treasury Contract\n", + "\n", + "message from Passport Owner Address wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "wasm\n", + "\t_contract_address: Treasury Contract\n", + "\taction: increase_allowance\n", + "\towner: Passport Owner Address\n", + "\tspender: Gift Contract\n", + "\tdenomination: boot\n", + "\tamount: 30,000,000,000\n", + "Gas used: 135,339\n", + "Tx hash: B680702CE0A4CBF4AA2E2A2CFA0221F6D30DC067528965E94476F1C9137EF913\n" + ] + } + ], + "source": [ + "def set_allowance(amount: str = INITIAL_BALANCE, display_data: bool = False):\n", + " return contract_utils.execute_contract(\n", + " execute_msg={\"increase_allowance\": {\"spender\": gift_contract_address, \"amount\": {\"amount\": amount, \"denom\": 'boot'}}},\n", + " contract_address=treasury_contract_address,\n", + " mnemonic=WALLET_SEED,\n", + " gas=600000,\n", + " display_data=display_data)\n", + "\n", + "contract_utils.parse_contract_execution_json(\n", + " set_allowance(display_data=False))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Register Merkle Root" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 15, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Events\n", + "\n", + "execute\n", + "\texecute contract: Gift Contract\n", + "\n", + "message from Passport Owner Address wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "wasm\n", + "\t_contract_address: Gift Contract\n", + "\taction: register_merkle_root\n", + "\tmerkle_root: 0cd70e3c34ff111de47721ca40ece8fad8d49549bfc16d29f5d074781bdd3ff1\n", + "Gas used: 130,976\n", + "Tx hash: F20198A196F13F8D1FB93E66116A66ADE77C7E43726AA8BEBAF419ADBD316E82\n" + ] + } + ], + "source": [ + "root_register_output = contract_utils.execute_contract(\n", + " execute_msg={\"register_merkle_root\": {\"merkle_root\": root}},\n", + " mnemonic=WALLET_SEED,\n", + " contract_address=gift_contract_address)\n", + "contract_utils.parse_contract_execution_json(root_register_output)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "#### Get Merkle Root form the Gift Contract" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + }, + "execution_count": 156 + }, + { + "cell_type": "code", + "execution_count": 16, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Gift contract bostrom1gc5wcdn9ges00w0l2cfxd7r2puyflak5dmkg26rsh083afmnrjxq3q6aaa\n", + "{'data': {'merkle_root': '0cd70e3c34ff111de47721ca40ece8fad8d49549bfc16d29f5d074781bdd3ff1'}}\n" + ] + } + ], + "source": [ + "print(f'Gift contract {gift_contract_address}')\n", + "print(query_contract(query='''{\"merkle_root\": {}}''',\n", + " contract_address=gift_contract_address))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Send coins to new addresses" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 17, + "outputs": [ + { + "data": { + "text/plain": "0it [00:00, ?it/s]", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "9888696f1aa7491e9d667c08d72ffbac" + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "NUMBER_ADDRESSES_IN_SENDING_CHUNK = 800\n", + "\n", + "bostrom_addresses = claims_with_proofs_df.bostrom_address.to_list()\n", + "bostrom_addresses_chunks = [bostrom_addresses[i: i+ NUMBER_ADDRESSES_IN_SENDING_CHUNK] for i in range(0, len(bostrom_addresses), NUMBER_ADDRESSES_IN_SENDING_CHUNK)]\n", + "\n", + "send_output = []\n", + "for i, bostrom_addresses_item in tqdm(enumerate(bostrom_addresses_chunks)):\n", + " try:\n", + " send_output.append(\n", + " contract_utils.send_coins(\n", + " from_seed=WALLET_SEED,\n", + " to_addresses=bostrom_addresses_item,\n", + " amounts=[1] * len(bostrom_addresses_item),\n", + " gas=min(150_000 + 30_000 * len(bostrom_addresses_item), int(22e6)),\n", + " display_data=DISPLAY_TX_EXECUTION))\n", + " except Exception as e:\n", + " print(f'Error in the {i} chunk: {e}')\n", + " sleep(15)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "contract_utils.parse_contract_execution_json(contract_execution_json=send_output[0])" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Create Passports, Proof Addresses and Claim Gift" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "def execute_participation(row_index: int) -> bool:\n", + " _output, _error = execute_bash(bash_command=f'{PYTHON_PATH} create_passport_and_claim_job.py temp/{row_index//10_000}_{PARTICIPANTS_FILE_NAME.split(\"/\")[1]} {row_index} {gift_contract_address}')\n", + " if _output:\n", + " return _output\n", + " else:\n", + " return _error\n", + "\n", + "NUMBER_OF_THREADS = 100\n", + "\n", + "tasks = list(range(NUMBER_OF_ACTIVATED_PARTICIPANTS))\n", + "print(f'Number of tasks: {len(tasks):>,}')\n", + "print(f'Number of threads: {NUMBER_OF_THREADS:>,}')\n", + "\n", + "\n", + "with Pool(processes=NUMBER_OF_THREADS) as pool:\n", + " res_participation = list(tqdm(pool.imap(execute_participation, tasks), total=len(tasks)))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "import json\n", + "with open('temp/contract_participation_execution_log_2.txt', 'r') as f:\n", + " data = json.load(f)\n", + "data" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "contract_utils.parse_contract_execution_json(data['create'], row=claims_with_proofs_df.loc[1,:])" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "contract_utils.parse_contract_execution_json(data['proof_ethereum'], row=claims_with_proofs_df.loc[1,:])" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "contract_utils.parse_contract_execution_json(data['proof_cosmos'], row=claims_with_proofs_df.loc[1,:])" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "contract_utils.parse_contract_execution_json(data['claim_cosmos'], row=claims_with_proofs_df.loc[1,:])" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Release Gift" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 20, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of tasks: 5,000\n", + "Number of threads: 100\n" + ] + }, + { + "data": { + "text/plain": " 0%| | 0/5000 [00:00 bool:\n", + " _output, _error = execute_bash(bash_command=f'{PYTHON_PATH} create_passport_and_claim_job.py temp/{row_index//10_000}_{PARTICIPANTS_FILE_NAME.split(\"/\")[1]} {row_index} {gift_contract_address} True')\n", + " if _output:\n", + " return _output\n", + " else:\n", + " return _error\n", + "\n", + "\n", + "NUMBER_OF_THREADS = 100\n", + "\n", + "tasks = list(range(NUMBER_OF_ACTIVATED_PARTICIPANTS))\n", + "print(f'Number of tasks: {len(tasks):>,}')\n", + "print(f'Number of threads: {NUMBER_OF_THREADS:>,}')\n", + "\n", + "\n", + "with Pool(processes=NUMBER_OF_THREADS) as pool:\n", + " res_release = list(tqdm(pool.imap(execute_release, tasks), total=len(tasks)))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "with open('temp/contract_release_execution_log_1.txt', 'r') as f:\n", + " release_data = json.load(f)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "release_data" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "contract_utils.parse_contract_execution_json(release_data['release_ethereum'], row=claims_with_proofs_df.loc[1,:])" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "contract_utils.parse_contract_execution_json(release_data['release_cosmos'], row=claims_with_proofs_df.loc[1,:])" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Passport NFT testing" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "def get_passport_id(bostrom_address: str) -> str:\n", + " try:\n", + " return query_contract(query=f'''{{\"tokens\": {{\"owner\": \"{bostrom_address}\"}}}}''',\n", + " contract_address=passport_contract_address)['data']['tokens'][0]\n", + " except (json.JSONDecodeError, IndexError) as e:\n", + " print(f'Error in the get passport: {e}')\n", + " return ''" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "def execute_passport_test(row_index: int, contract_utils=contract_utils) -> bool:\n", + " row = claims_with_proofs_df.iloc[row_index]\n", + " if row_index % 5 == 1:\n", + " transfer_passport_json = contract_utils.transfer_passport(\n", + " row,\n", + " token_id=get_passport_id(row['bostrom_address']),\n", + " to_address=WALLET_ADDRESS,\n", + " display_data=DISPLAY_TX_EXECUTION)\n", + " if row_index < 6:\n", + " print('\\nTRANSFER')\n", + " contract_utils.parse_contract_execution_json(transfer_passport_json, row=row)\n", + " elif row_index % 5 == 2:\n", + " burn_passport_json = contract_utils.burn_passport(\n", + " row,\n", + " token_id=get_passport_id(row['bostrom_address']),\n", + " display_data=DISPLAY_TX_EXECUTION)\n", + " if row_index < 6:\n", + " print('\\nBURN')\n", + " contract_utils.parse_contract_execution_json(burn_passport_json, row=row)\n", + " elif row_index % 5 == 3:\n", + " update_name_json = contract_utils.update_name(\n", + " row,\n", + " new_nickname=row['nickname'] + '_new',\n", + " display_data=DISPLAY_TX_EXECUTION)\n", + " if row_index < 6:\n", + " print('\\nUPDATE NAME')\n", + " contract_utils.parse_contract_execution_json(update_name_json, row=row)\n", + " elif row_index % 5 == 4:\n", + " update_avatar_json = contract_utils.update_avatar(\n", + " row,\n", + " new_avatar=get_ipfs_cid_from_str(row['nickname'] + '_new_avatar'),\n", + " display_data=DISPLAY_TX_EXECUTION)\n", + " if row_index < 6:\n", + " print('\\nUPDATE AVATAR')\n", + " contract_utils.parse_contract_execution_json(update_avatar_json, row=row)\n", + " elif row_index % 5 == 0:\n", + " remove_address_json = contract_utils.remove_address(\n", + " row,\n", + " removed_address=row['cosmos_address'],\n", + " display_data=DISPLAY_TX_EXECUTION)\n", + " if row_index < 6:\n", + " print('\\nREMOVE ADDRESS')\n", + " contract_utils.parse_contract_execution_json(remove_address_json, row=row)\n", + " print(f'Finish: {row_index}')\n", + " return True\n", + "\n", + "NUMBER_OF_THREADS = 100\n", + "\n", + "tasks = list(range(NUMBER_OF_ACTIVATED_PARTICIPANTS))[:20]\n", + "print(f'Number of tasks: {len(tasks):>,}')\n", + "print(f'Number of threads: {NUMBER_OF_THREADS:>,}')\n", + "\n", + "for row_index in tqdm(range(34, NUMBER_OF_ACTIVATED_PARTICIPANTS)):\n", + " execute_passport_test(row_index)\n", + "# with Pool(processes=NUMBER_OF_THREADS) as pool:\n", + "# # res_passport_test = list(tqdm(pool.imap(execute_passport_test, tasks), total=len(tasks)))\n", + "# res_passport_test = list(pool.imap(execute_passport_test, tasks))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "# claims_with_proofs_df\n", + "execute_passport_test(0)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Add tests for Passport contract\n", + "#### create_passport +\n", + "#### update_name +\n", + "#### update_avatar +\n", + "#### proof_address +\n", + "#### remove_address +\n", + "#### set_minter\n", + "#### set_owner\n", + "#### set_active\n", + "#### set_subgraphs +\n", + "#### transfer_nft +\n", + "#### send_nft\n", + "#### mint\n", + "#### burn +\n", + "#### approve\n", + "#### approve_all\n", + "#### revoke\n", + "#### revoke_all\n", + "#### Expirations\n", + "##### at_height\n", + "##### at_time\n", + "##### never\n", + "\n", + "### Add tests for Gift contract\n", + "#### update_owner\n", + "#### update_passport_addr\n", + "#### update_target\n", + "#### register_merkle_root +\n", + "#### claim +\n", + "#### release +\n", + "\n", + "### Add tests for subgraph contract\n", + "#### update_owner\n", + "#### update_executer +" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/contracts/cw-cyber-gift/testdata/generate_test_data/gift_and_passport_contracts_testing.ipynb b/contracts/cw-cyber-gift/testdata/generate_test_data/gift_and_passport_contracts_testing.ipynb new file mode 100644 index 0000000..ff8ab15 --- /dev/null +++ b/contracts/cw-cyber-gift/testdata/generate_test_data/gift_and_passport_contracts_testing.ipynb @@ -0,0 +1,1652 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "## Gift Contract Testing" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 1, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/sergenedashkovsky/Library/Python/3.8/lib/python/site-packages/ipfshttpclient/client/__init__.py:75: VersionMismatch: Unsupported daemon version '0.10.0' (not in range: 0.5.0 ≤ … < 0.9.0)\n", + " warnings.warn(exceptions.VersionMismatch(version, minimum, maximum))\n" + ] + } + ], + "source": [ + "from web3.auto import w3\n", + "import pandas as pd\n", + "from cyberpy import generate_wallet, address_to_address\n", + "from cyberpy._message_signer import Message\n", + "from cyberpy._wallet import seed_to_privkey\n", + "from eth_account.messages import encode_defunct\n", + "import json\n", + "from time import time\n", + "import ipfshttpclient\n", + "from tqdm.notebook import tqdm\n", + "from base64 import b64encode\n", + "from multiprocess import Pool\n", + "from math import ceil\n", + "from dotenv import dotenv_values\n", + "from IPython.core.display import display, HTML\n", + "\n", + "from contract_utils import instantiate_contract, execute_contract_bash, query_contract, get_ipfs_cid_from_str, get_proofs, ContractUtils\n", + "\n", + "\n", + "ipfs_client = ipfshttpclient.connect()\n", + "\n", + "NUMBER_OF_PARTICIPANTS = 10\n", + "NUMBER_OF_ACTIVATED_PARTICIPANTS = 5\n", + "KEY_PHRASE = 'KEY PHRASE'\n", + "NICKNAME_LIST = [f'john{round(time())}{number}' for number in range(NUMBER_OF_PARTICIPANTS)]\n", + "AVATAR_CID_LIST = [get_ipfs_cid_from_str(_nickname + '_avatar') for _nickname in NICKNAME_LIST]\n", + "CLAIM_AMOUNT_LIST = [1_000_000] * NUMBER_OF_PARTICIPANTS\n", + "\n", + "INITIAL_BALANCE = str(1_000_000_000)\n", + "COEF_UP = str(13)\n", + "COEF_DOWN = str(7)\n", + "TARGET_CLAIM = str(2)\n", + "\n", + "WALLET_ADDRESS = dotenv_values('.env')['WALLET_ADDRESS']\n", + "WALLET_SEED = dotenv_values('.env')['WALLET_SEED']\n", + "DISPLAY_TX_EXECUTION = False\n", + "TEST_ACCOUNT_ADDRESS = 'bostrom1mxdtr8lruutugqtxgpw2sf2tl2mhzlq5fd2du0'\n", + "TEST_ACCOUNT_SEED = 'end spread blind steak install glare pride pony switch exit zone faint ' \\\n", + " 'opinion march layer illness can nest fence top debate monitor supreme noble'\n", + "\n", + "INIT_SUBGRAPH_CONTRACTS = False\n", + "SUBGRAPH_CODE_ID = str(40)\n", + "NAME_SUBGRAPH_CONTRACT_ADDRESS = 'bostrom1rncw9n73gm30vhrv6e4p603hav0gue8y5y9fgqa84k4atf5pqvfqcrnpl6'\n", + "AVATAR_SUBGRAPH_CONTRACT_ADDRESS = 'bostrom164w2vl7z7lpuvex6z3ru0v55fgq3dmvxuqt0aejp49w7fyc8g6kshreggq'\n", + "PROOF_SUBGRAPH_CONTRACT_ADDRESS = 'bostrom1543j9n7slzff3curyac7ylf2ctg7rk9zjf9ehj08eqx57xj33zzqdy6ga4'\n", + "\n", + "INIT_PASSPORT_CONTRACT = False\n", + "PASSPORT_CODE_ID = str(43)\n", + "PASSPORT_CONTRACT_ADDRESS = 'bostrom1g59m935w4kxmtfx5hhykre7w9q497ptp66asspz76vhgarss5ensdy35s8'\n", + "\n", + "INIT_GIFT_CONTRACT = True\n", + "GIFT_CODE_ID = str(20)\n", + "GIFT_CONTRACT_ADDRESS = 'bostrom1rt2acjyhs4jfjdq56pftpu7762hy9gfl63je6fnhwrc5p5y4kmuqxg0262'\n", + "\n", + "ROOT_SOURCE_FILE_NAME = 'root_testing_source_' + str(NUMBER_OF_PARTICIPANTS) + '_addresses.json'\n", + "PROOF_FILE_NAME = 'proof_testing_' + str(NUMBER_OF_PARTICIPANTS) + '_addresses.json'\n", + "ROOT_FILE_NAME = 'root_testing_' + str(NUMBER_OF_PARTICIPANTS) + '_addresses'" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Generate addresses and sign messages" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 2, + "outputs": [ + { + "data": { + "text/plain": " 0%| | 0/10 [00:00\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
amountnicknameavatarethereum_addressethereum_private_keybostrom_addresscosmos_addresscosmos_seedmessageethereum_message_signaturecosmos_message_signed_rowcosmos_message_signature
01000000john16495703800QmegFZS691C4pJFaFS3NVDNLd5PtjmMiJHJKtqzSsz2tBZ0x9124d6592264ae9e6693dfa39b8f82f16182419c0xcf156a52fd254b48a87c77926630089376a2e6d507f5...bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04ccosmos1y90fdf63dz6jfahupu4wdqp6v3f69rxpgqhutlmixed february demise clinic master race famou...bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c...0x9a2bbfc7ab3c7d4ed8910b0a495931c8a055c2fc29e7...{'pub_key': 'A89DQdUqRt7dzpi8/1Jxyb1hh+a6Q0bGo...eyJwdWJfa2V5IjoiQTg5RFFkVXFSdDdkenBpOC8xSnh5Yj...
11000000john16495703801QmbAqD21zVJD6D2kvoGnV1Fb8T5XKUG81V8Y7iZVHnpisC0x042a2bb0e4acd457bcadda6e05827aa83ebe91fa0x46a7e94a530954956533e1a923d07c212eafc00a8d23...bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366qcosmos162f8clv5ht6j2kmms0556j0mezuj3e6k679fy8quantum seed good glass spend spread usual sur...bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q...0x5297763d57331039613520d1de1c9f94b0bf104d6fd2...{'pub_key': 'At5DOzq36uQeOGT//9kV3HcsPmy4HTAsO...eyJwdWJfa2V5IjoiQXQ1RE96cTM2dVFlT0dULy85a1YzSG...
21000000john16495703802QmdYTELFKWGpiZ6Qaf6bDHavRMrJ5nmqMTc94mwtnNzr6M0x0329bbdbf79eaf87d8d77ecb71f7f8d8165443590xcdf21ac40a23b30ac2208d9e3f72c7b798068df70f39...bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnfcosmos1he3vaq8knkx56tevl6mkel42eddq507mt0trdwlava protect work pulse guess toss rocket volu...bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnf...0x3b7e242a976bd013862872532234536ea2e3397d3411...{'pub_key': 'Aph+/Mv0rOA0Ft+8iP6s7UJqTdOXK8tsK...eyJwdWJfa2V5IjoiQXBoKy9NdjByT0EwRnQrOGlQNnM3VU...
31000000john16495703803QmTqzfWV7DsGQsyoMHJTVBRkDtKBEjy5hPErMRfpbmimPm0x2c105e1ce5bc66a3ec0eb18089b00a471e3bd0a80x08669cd3a21d69b25991ac891c5d1529b64fc893f846...bostrom1efylm4laztlkc0ydye3k6g6cx95h6khw7z27mpcosmos1efylm4laztlkc0ydye3k6g6cx95h6khwa37d9xmedal medal pull rule blur result gift agree g...bostrom1efylm4laztlkc0ydye3k6g6cx95h6khw7z27mp...0x90d40af4947b8a499c22c79e1ea29cea981be1f819db...{'pub_key': 'A055jXQh2nQUJQQdlssNe0ZWQf9KRkUba...eyJwdWJfa2V5IjoiQTA1NWpYUWgyblFVSlFRZGxzc05lMF...
41000000john16495703804QmNWiHGs9WjmzbcQz96fugBm2Bu6CR3uZVsMuiM1tr4wYD0xcd012b8e0c4a5bd5c9283a0a4281ec553420d1490xb241a6f909672108710c007c1ea3173656139243deaa...bostrom1z4gex65lzw62uqapaxctfku4588ulvgn4yvwmkcosmos1z4gex65lzw62uqapaxctfku4588ulvgnkhca93hope someone gown grain execute reason chaos g...bostrom1z4gex65lzw62uqapaxctfku4588ulvgn4yvwmk...0xcc8656d43b15788c5e0400596f5cebd760da1fd0b75e...{'pub_key': 'Ao00rX4/WgQwgoR9wgwOTEgNq5mh9qfDI...eyJwdWJfa2V5IjoiQW8wMHJYNC9XZ1F3Z29SOXdnd09URW...
51000000john16495703805QmUva19NDC6iVZZ9gGL7jZfsXxpPd6bijzhbUsFvSXTcbV0xc45e14572ad2ec57da27faab28919bbda692037e0xbac8ff0d46365a737ac95be339a9036f861bc371ce14...bostrom13f4sr82dux0kfu7uma3atv7frhzx57ghvm9dxacosmos13f4sr82dux0kfu7uma3atv7frhzx57gh0g37c6bicycle lyrics ring donate price inherit stool...bostrom13f4sr82dux0kfu7uma3atv7frhzx57ghvm9dxa...0xb950237f45061a45796e9f6045ef0675a32ee0df4616...{'pub_key': 'A2Udd3YSUpIPjMuAEmdwCzFYEvHQEHxu3...eyJwdWJfa2V5IjoiQTJVZGQzWVNVcElQak11QUVtZHdDek...
61000000john16495703806QmWXwdS2HtDFxfQ6bwdFvAjJ5HUGS7kQf3pmtkQDzYt9cd0xb66bcd3431cc5b1999f98c608963fa9ff2a5d0d20x02c99ad10dc1e0b4400d72bdca2b9623975de1489887...bostrom1pypc2s9dh0f6yp0xcqwjap99l5k75qgmp2kntwcosmos1pypc2s9dh0f6yp0xcqwjap99l5k75qgmzezq4fleaf try race mango beef notice opera pyramid ...bostrom1pypc2s9dh0f6yp0xcqwjap99l5k75qgmp2kntw...0xd767925b964cff4fb53af0c37b4b8c6b6cfdd8ca4e9a...{'pub_key': 'AxeON8LGKhtXnzWl1yIuWli6bvIQCj9db...eyJwdWJfa2V5IjoiQXhlT044TEdLaHRYbnpXbDF5SXVXbG...
71000000john16495703807QmS8CCPyZkNZtiyyt6KbNVKYvbv2yQHaNPEF9ghhUWUN6X0xcb6babdbc3dd4f702237dfdd644ddbc676055e860x67d44f92161e85defdf10fa79c090b4a579d24ad1a60...bostrom13ugfw2hr7s8f0yttczg8e2v6v04dlwwarsc4rwcosmos13ugfw2hr7s8f0yttczg8e2v6v04dlwwaqrvxafcourse avoid view wrist casino medal rail adap...bostrom13ugfw2hr7s8f0yttczg8e2v6v04dlwwarsc4rw...0xebd149ca2557ecf8a1a8c6db64d41b31c5e14ba706ba...{'pub_key': 'A5BI/jGNrd6zeAhtrhpxewXKHuQwvcDY2...eyJwdWJfa2V5IjoiQTVCSS9qR05yZDZ6ZUFodHJocHhld1...
81000000john16495703808QmbNWqULQTj2CAR34Fr4bYYMTypcEwnHrcfVLsXUf2UsMD0x1b15c1f4711a8f32f299ccc6121bcac89770a13f0xdfc6b8f64235c064afe1724ba46c0c8004ccb2d49bd2...bostrom19pfkg9t5nvsmmjffsjv6k625xy98esgukewtyycosmos19pfkg9t5nvsmmjffsjv6k625xy98esgu426c6rlake thing harvest style luxury ensure notable...bostrom19pfkg9t5nvsmmjffsjv6k625xy98esgukewtyy...0xfe52914b475dd609d4e0baa2c6a5ba3b40aa2e9821e8...{'pub_key': 'A8NrNm4aqTxCiZ3HU2ZKHsOZyE9ID++ls...eyJwdWJfa2V5IjoiQThOck5tNGFxVHhDaVozSFUyWktIc0...
91000000john16495703809QmbtBRQCdJb43phHufuvfKc6oyA6aMH15Rygey8siYXhhu0xcc9f8f54c6f538c6c3a8dc58aef9c591362ae0da0x49f4580779525a07739ab68efb3fa4f2f4edc21feee7...bostrom1kzrdznpsuhj8scw8p734eex07g0779vr8xw0d6cosmos1kzrdznpsuhj8scw8p734eex07g0779vry46unacar grief adapt crisp enroll disagree keep awa...bostrom1kzrdznpsuhj8scw8p734eex07g0779vr8xw0d6...0x7e9b67979c02be2aec144527a8368e91cc31e57cb68b...{'pub_key': 'AuJljuNtsGVzelxAYia98hz89J7bH5rN6...eyJwdWJfa2V5IjoiQXVKbGp1TnRzR1Z6ZWx4QVlpYTk4aH...
\n" + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "claims_list = []\n", + "for i in tqdm(range(NUMBER_OF_PARTICIPANTS)):\n", + " claim_item = {'amount': CLAIM_AMOUNT_LIST[i],\n", + " 'nickname': NICKNAME_LIST[i],\n", + " 'avatar': AVATAR_CID_LIST[i]}\n", + "\n", + " # Generate Ethereum wallet\n", + " ethereum_wallet = w3.eth.account.create(KEY_PHRASE)\n", + " claim_item['ethereum_address'] = ethereum_wallet.address.lower()\n", + " claim_item['ethereum_private_key'] = ethereum_wallet.privateKey.hex()\n", + "\n", + " # Generate Bostrom and Cosmos wallet\n", + " bostrom_wallet = generate_wallet()\n", + " claim_item['bostrom_address'] = bostrom_wallet['address']\n", + " claim_item['cosmos_address'] = address_to_address(bostrom_wallet['address'], 'cosmos')\n", + " claim_item['cosmos_seed'] = bostrom_wallet['seed']\n", + "\n", + " # Create message\n", + " claim_item['message'] = f\"{claim_item['bostrom_address']}:QmRX8qYgeZoYM3M5zzQaWEpVFdpin6FvVXvp6RPQK3oufV\"\n", + "\n", + " # Sign message form Ethereum address\n", + " ethereum_signed_message = \\\n", + " w3.eth.account.sign_message(\n", + " signable_message=encode_defunct(text=claim_item['message']),\n", + " private_key=ethereum_wallet.privateKey)\n", + " # print(signed_message.signature)\n", + " claim_item['ethereum_message_signature'] = ethereum_signed_message.signature.hex()\n", + "\n", + " # Sign message form Cosmos address\n", + " cosmos_msg = Message(privkey=seed_to_privkey(seed=claim_item['cosmos_seed']))\n", + " cosmos_msg.add_message(signing_message=claim_item['message'], signer_prefix='cosmos')\n", + " claim_item['cosmos_message_signed_row'] = cosmos_msg.get_signed_message()\n", + " claim_item['cosmos_message_signature'] = b64encode(json.dumps(claim_item['cosmos_message_signed_row']).replace('\\n', '').replace(' ', '').encode('utf-8')).decode(\"utf-8\")\n", + "\n", + " # Verify message\n", + " assert claim_item['ethereum_address'] == w3.eth.account.recover_message(\n", + " signable_message=encode_defunct(text=claim_item['message']),\n", + " signature=claim_item['ethereum_message_signature']).lower()\n", + "\n", + " claims_list.append(claim_item)\n", + "\n", + "claims_df = pd.DataFrame(claims_list)\n", + "claims_df.to_csv('claims_ethereum_test_data_without_proof_' + str(NUMBER_OF_PARTICIPANTS) + '_addresses.csv')\n", + "claims_df" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Create Merkle Tree, Get Root and Proofs" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 3, + "outputs": [], + "source": [ + "root_source_list = [{'address': _item['ethereum_address'],\n", + " 'amount': str(_item['amount'])} for _item in claims_list]+ \\\n", + " [{'address': _item['cosmos_address'],\n", + " 'amount': str(_item['amount'])} for _item in claims_list]\n", + "root_source_list.append({'address': '0xF2749114FeaAD68854E01C8eE762C7170532FdfD'.lower(), 'amount': '10000000'})\n", + "with open(ROOT_SOURCE_FILE_NAME, 'w') as outfile:\n", + " outfile.write(str(root_source_list).replace(\"'\", '\"'))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 4, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{('root_testing_source_10_addresses.json', 'temp/proofs_0.json', 0, 21)}\n", + "yarn run v1.22.17\n", + "$ ts-node index.ts --input root_testing_source_10_addresses.json --output temp/proofs_0.json --start_index 0 --end_index 21\n", + "Merkle root: 3dc8421e3f4b94c8e3aed45c031e95a002007dfac135ca398314b163f15a2cee\n", + "Number of addresses in the Merkle tree: 21\n", + "Done in 3.58s.\n", + "\n" + ] + } + ], + "source": [ + "NUMBER_OF_THREADS = 10\n", + "BASH_SIZE = 1000\n", + "\n", + "tasks = set(\n", + " (\n", + " (ROOT_SOURCE_FILE_NAME,\n", + " f'temp/proofs_{i}.json',\n", + " i * BASH_SIZE,\n", + " min(NUMBER_OF_PARTICIPANTS * 2 + 1, (i + 1) * BASH_SIZE + 1))\n", + " for i in range(ceil(2 * NUMBER_OF_PARTICIPANTS/BASH_SIZE))\n", + " )\n", + ")\n", + "print(tasks)\n", + "with Pool(processes=NUMBER_OF_THREADS) as pool:\n", + " res = pool.starmap(get_proofs, tasks)\n", + "assert res == [True] * len(res)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 5, + "outputs": [ + { + "data": { + "text/plain": " address amount \\\n0 0x9124d6592264ae9e6693dfa39b8f82f16182419c 1000000 \n1 0x042a2bb0e4acd457bcadda6e05827aa83ebe91fa 1000000 \n2 0x0329bbdbf79eaf87d8d77ecb71f7f8d816544359 1000000 \n3 0x2c105e1ce5bc66a3ec0eb18089b00a471e3bd0a8 1000000 \n4 0xcd012b8e0c4a5bd5c9283a0a4281ec553420d149 1000000 \n5 0xc45e14572ad2ec57da27faab28919bbda692037e 1000000 \n6 0xb66bcd3431cc5b1999f98c608963fa9ff2a5d0d2 1000000 \n7 0xcb6babdbc3dd4f702237dfdd644ddbc676055e86 1000000 \n8 0x1b15c1f4711a8f32f299ccc6121bcac89770a13f 1000000 \n9 0xcc9f8f54c6f538c6c3a8dc58aef9c591362ae0da 1000000 \n10 cosmos1y90fdf63dz6jfahupu4wdqp6v3f69rxpgqhutl 1000000 \n11 cosmos162f8clv5ht6j2kmms0556j0mezuj3e6k679fy8 1000000 \n12 cosmos1he3vaq8knkx56tevl6mkel42eddq507mt0trdw 1000000 \n13 cosmos1efylm4laztlkc0ydye3k6g6cx95h6khwa37d9x 1000000 \n14 cosmos1z4gex65lzw62uqapaxctfku4588ulvgnkhca93 1000000 \n15 cosmos13f4sr82dux0kfu7uma3atv7frhzx57gh0g37c6 1000000 \n16 cosmos1pypc2s9dh0f6yp0xcqwjap99l5k75qgmzezq4f 1000000 \n17 cosmos13ugfw2hr7s8f0yttczg8e2v6v04dlwwaqrvxaf 1000000 \n18 cosmos19pfkg9t5nvsmmjffsjv6k625xy98esgu426c6r 1000000 \n19 cosmos1kzrdznpsuhj8scw8p734eex07g0779vry46una 1000000 \n20 0xf2749114feaad68854e01c8ee762c7170532fdfd 10000000 \n\n proof \n0 [78900d7c06c43ae20970087421f80e49257815363f6a3... \n1 [d0c8f5fc66c5a977340dd92568b1b989992f3b3c5b114... \n2 [9d5777f2aee55468fcd9a426466d35917086cc43855ca... \n3 [5e42909234f9266f1a6cac6f517ca7971472cf36a9308... \n4 [d24b8d0cf08a253a44e55c4c134626b76f66b6b4e61e5... \n5 [e9774d5437498124573647289f2644bf38883f338f882... \n6 [3030215ce1bd034297d5a9c5261eb17c827fdfebf7e83... \n7 [b498a35a47dfd85c3f4e59859952eb3d24041f4deb0fc... \n8 [2c95a091c7f94fc231386ca44cab8f7db16da40dc54cd... \n9 [82773830b6b2d0bc76832c66fc9ac3e2fb9b6e6c84fe3... \n10 [30fdf3cb4f18b08a20a2db3acc5ac961095089672200b... \n11 [c571aa4a3bb95f180e8865ac710746125a1d2936940a1... \n12 [d560e34d04482e9002c51c4155cc578ad1083221e4d87... \n13 [97b75d463fd68f9b16cf586bdf6d0d6dd3044beb7a640... \n14 [1504edb79fb0500537d30e48ee1ad4fda5c61b7ac9922... \n15 [13f37008c6c89394e615b6364285ad49bee702aead436... \n16 [7e9525a33b54415b76ebf3ffafa075390d3f2978b0db5... \n17 [87e33d647e99fb3b55137be5085df925d6a2281d6cdc5... \n18 [f06c989e17e5be69f92632911aefe6c0cceeee318a46a... \n19 [2d45e290725a0cad1c162b89cb246f4ae12aaada24584... \n20 [733457c1598a18d306bfe0827f58d38573d2d35a4e98c... ", + "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
addressamountproof
00x9124d6592264ae9e6693dfa39b8f82f16182419c1000000[78900d7c06c43ae20970087421f80e49257815363f6a3...
10x042a2bb0e4acd457bcadda6e05827aa83ebe91fa1000000[d0c8f5fc66c5a977340dd92568b1b989992f3b3c5b114...
20x0329bbdbf79eaf87d8d77ecb71f7f8d8165443591000000[9d5777f2aee55468fcd9a426466d35917086cc43855ca...
30x2c105e1ce5bc66a3ec0eb18089b00a471e3bd0a81000000[5e42909234f9266f1a6cac6f517ca7971472cf36a9308...
40xcd012b8e0c4a5bd5c9283a0a4281ec553420d1491000000[d24b8d0cf08a253a44e55c4c134626b76f66b6b4e61e5...
50xc45e14572ad2ec57da27faab28919bbda692037e1000000[e9774d5437498124573647289f2644bf38883f338f882...
60xb66bcd3431cc5b1999f98c608963fa9ff2a5d0d21000000[3030215ce1bd034297d5a9c5261eb17c827fdfebf7e83...
70xcb6babdbc3dd4f702237dfdd644ddbc676055e861000000[b498a35a47dfd85c3f4e59859952eb3d24041f4deb0fc...
80x1b15c1f4711a8f32f299ccc6121bcac89770a13f1000000[2c95a091c7f94fc231386ca44cab8f7db16da40dc54cd...
90xcc9f8f54c6f538c6c3a8dc58aef9c591362ae0da1000000[82773830b6b2d0bc76832c66fc9ac3e2fb9b6e6c84fe3...
10cosmos1y90fdf63dz6jfahupu4wdqp6v3f69rxpgqhutl1000000[30fdf3cb4f18b08a20a2db3acc5ac961095089672200b...
11cosmos162f8clv5ht6j2kmms0556j0mezuj3e6k679fy81000000[c571aa4a3bb95f180e8865ac710746125a1d2936940a1...
12cosmos1he3vaq8knkx56tevl6mkel42eddq507mt0trdw1000000[d560e34d04482e9002c51c4155cc578ad1083221e4d87...
13cosmos1efylm4laztlkc0ydye3k6g6cx95h6khwa37d9x1000000[97b75d463fd68f9b16cf586bdf6d0d6dd3044beb7a640...
14cosmos1z4gex65lzw62uqapaxctfku4588ulvgnkhca931000000[1504edb79fb0500537d30e48ee1ad4fda5c61b7ac9922...
15cosmos13f4sr82dux0kfu7uma3atv7frhzx57gh0g37c61000000[13f37008c6c89394e615b6364285ad49bee702aead436...
16cosmos1pypc2s9dh0f6yp0xcqwjap99l5k75qgmzezq4f1000000[7e9525a33b54415b76ebf3ffafa075390d3f2978b0db5...
17cosmos13ugfw2hr7s8f0yttczg8e2v6v04dlwwaqrvxaf1000000[87e33d647e99fb3b55137be5085df925d6a2281d6cdc5...
18cosmos19pfkg9t5nvsmmjffsjv6k625xy98esgu426c6r1000000[f06c989e17e5be69f92632911aefe6c0cceeee318a46a...
19cosmos1kzrdznpsuhj8scw8p734eex07g0779vry46una1000000[2d45e290725a0cad1c162b89cb246f4ae12aaada24584...
200xf2749114feaad68854e01c8ee762c7170532fdfd10000000[733457c1598a18d306bfe0827f58d38573d2d35a4e98c...
\n
" + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "roots = []\n", + "proofs_df = pd.DataFrame(columns=['address', 'amount', 'proof'])\n", + "for task in tasks:\n", + " with open(task[1], 'r') as proof_file:\n", + " root_and_proof_json = json.load(proof_file)\n", + " roots.append(root_and_proof_json['merkle_root'])\n", + " proofs_df = proofs_df.append(pd.DataFrame(root_and_proof_json['proofs']), ignore_index=True)\n", + "assert roots == [roots[0]] * len(roots)\n", + "root = roots[0]\n", + "proofs_df" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 6, + "outputs": [ + { + "data": { + "text/plain": " amount nickname avatar \\\n0 1000000 john16495703800 QmegFZS691C4pJFaFS3NVDNLd5PtjmMiJHJKtqzSsz2tBZ \n1 1000000 john16495703801 QmbAqD21zVJD6D2kvoGnV1Fb8T5XKUG81V8Y7iZVHnpisC \n2 1000000 john16495703802 QmdYTELFKWGpiZ6Qaf6bDHavRMrJ5nmqMTc94mwtnNzr6M \n3 1000000 john16495703803 QmTqzfWV7DsGQsyoMHJTVBRkDtKBEjy5hPErMRfpbmimPm \n4 1000000 john16495703804 QmNWiHGs9WjmzbcQz96fugBm2Bu6CR3uZVsMuiM1tr4wYD \n\n ethereum_address \\\n0 0x9124d6592264ae9e6693dfa39b8f82f16182419c \n1 0x042a2bb0e4acd457bcadda6e05827aa83ebe91fa \n2 0x0329bbdbf79eaf87d8d77ecb71f7f8d816544359 \n3 0x2c105e1ce5bc66a3ec0eb18089b00a471e3bd0a8 \n4 0xcd012b8e0c4a5bd5c9283a0a4281ec553420d149 \n\n ethereum_private_key \\\n0 0xcf156a52fd254b48a87c77926630089376a2e6d507f5... \n1 0x46a7e94a530954956533e1a923d07c212eafc00a8d23... \n2 0xcdf21ac40a23b30ac2208d9e3f72c7b798068df70f39... \n3 0x08669cd3a21d69b25991ac891c5d1529b64fc893f846... \n4 0xb241a6f909672108710c007c1ea3173656139243deaa... \n\n bostrom_address \\\n0 bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c \n1 bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q \n2 bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnf \n3 bostrom1efylm4laztlkc0ydye3k6g6cx95h6khw7z27mp \n4 bostrom1z4gex65lzw62uqapaxctfku4588ulvgn4yvwmk \n\n cosmos_address \\\n0 cosmos1y90fdf63dz6jfahupu4wdqp6v3f69rxpgqhutl \n1 cosmos162f8clv5ht6j2kmms0556j0mezuj3e6k679fy8 \n2 cosmos1he3vaq8knkx56tevl6mkel42eddq507mt0trdw \n3 cosmos1efylm4laztlkc0ydye3k6g6cx95h6khwa37d9x \n4 cosmos1z4gex65lzw62uqapaxctfku4588ulvgnkhca93 \n\n cosmos_seed \\\n0 mixed february demise clinic master race famou... \n1 quantum seed good glass spend spread usual sur... \n2 lava protect work pulse guess toss rocket volu... \n3 medal medal pull rule blur result gift agree g... \n4 hope someone gown grain execute reason chaos g... \n\n message \\\n0 bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c... \n1 bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q... \n2 bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnf... \n3 bostrom1efylm4laztlkc0ydye3k6g6cx95h6khw7z27mp... \n4 bostrom1z4gex65lzw62uqapaxctfku4588ulvgn4yvwmk... \n\n ethereum_message_signature \\\n0 0x9a2bbfc7ab3c7d4ed8910b0a495931c8a055c2fc29e7... \n1 0x5297763d57331039613520d1de1c9f94b0bf104d6fd2... \n2 0x3b7e242a976bd013862872532234536ea2e3397d3411... \n3 0x90d40af4947b8a499c22c79e1ea29cea981be1f819db... \n4 0xcc8656d43b15788c5e0400596f5cebd760da1fd0b75e... \n\n cosmos_message_signed_row \\\n0 {'pub_key': 'A89DQdUqRt7dzpi8/1Jxyb1hh+a6Q0bGo... \n1 {'pub_key': 'At5DOzq36uQeOGT//9kV3HcsPmy4HTAsO... \n2 {'pub_key': 'Aph+/Mv0rOA0Ft+8iP6s7UJqTdOXK8tsK... \n3 {'pub_key': 'A055jXQh2nQUJQQdlssNe0ZWQf9KRkUba... \n4 {'pub_key': 'Ao00rX4/WgQwgoR9wgwOTEgNq5mh9qfDI... \n\n cosmos_message_signature \\\n0 eyJwdWJfa2V5IjoiQTg5RFFkVXFSdDdkenBpOC8xSnh5Yj... \n1 eyJwdWJfa2V5IjoiQXQ1RE96cTM2dVFlT0dULy85a1YzSG... \n2 eyJwdWJfa2V5IjoiQXBoKy9NdjByT0EwRnQrOGlQNnM3VU... \n3 eyJwdWJfa2V5IjoiQTA1NWpYUWgyblFVSlFRZGxzc05lMF... \n4 eyJwdWJfa2V5IjoiQW8wMHJYNC9XZ1F3Z29SOXdnd09URW... \n\n ethereum_proof \\\n0 [78900d7c06c43ae20970087421f80e49257815363f6a3... \n1 [d0c8f5fc66c5a977340dd92568b1b989992f3b3c5b114... \n2 [9d5777f2aee55468fcd9a426466d35917086cc43855ca... \n3 [5e42909234f9266f1a6cac6f517ca7971472cf36a9308... \n4 [d24b8d0cf08a253a44e55c4c134626b76f66b6b4e61e5... \n\n cosmos_proof \n0 [30fdf3cb4f18b08a20a2db3acc5ac961095089672200b... \n1 [c571aa4a3bb95f180e8865ac710746125a1d2936940a1... \n2 [d560e34d04482e9002c51c4155cc578ad1083221e4d87... \n3 [97b75d463fd68f9b16cf586bdf6d0d6dd3044beb7a640... \n4 [1504edb79fb0500537d30e48ee1ad4fda5c61b7ac9922... ", + "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
amountnicknameavatarethereum_addressethereum_private_keybostrom_addresscosmos_addresscosmos_seedmessageethereum_message_signaturecosmos_message_signed_rowcosmos_message_signatureethereum_proofcosmos_proof
01000000john16495703800QmegFZS691C4pJFaFS3NVDNLd5PtjmMiJHJKtqzSsz2tBZ0x9124d6592264ae9e6693dfa39b8f82f16182419c0xcf156a52fd254b48a87c77926630089376a2e6d507f5...bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04ccosmos1y90fdf63dz6jfahupu4wdqp6v3f69rxpgqhutlmixed february demise clinic master race famou...bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c...0x9a2bbfc7ab3c7d4ed8910b0a495931c8a055c2fc29e7...{'pub_key': 'A89DQdUqRt7dzpi8/1Jxyb1hh+a6Q0bGo...eyJwdWJfa2V5IjoiQTg5RFFkVXFSdDdkenBpOC8xSnh5Yj...[78900d7c06c43ae20970087421f80e49257815363f6a3...[30fdf3cb4f18b08a20a2db3acc5ac961095089672200b...
11000000john16495703801QmbAqD21zVJD6D2kvoGnV1Fb8T5XKUG81V8Y7iZVHnpisC0x042a2bb0e4acd457bcadda6e05827aa83ebe91fa0x46a7e94a530954956533e1a923d07c212eafc00a8d23...bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366qcosmos162f8clv5ht6j2kmms0556j0mezuj3e6k679fy8quantum seed good glass spend spread usual sur...bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q...0x5297763d57331039613520d1de1c9f94b0bf104d6fd2...{'pub_key': 'At5DOzq36uQeOGT//9kV3HcsPmy4HTAsO...eyJwdWJfa2V5IjoiQXQ1RE96cTM2dVFlT0dULy85a1YzSG...[d0c8f5fc66c5a977340dd92568b1b989992f3b3c5b114...[c571aa4a3bb95f180e8865ac710746125a1d2936940a1...
21000000john16495703802QmdYTELFKWGpiZ6Qaf6bDHavRMrJ5nmqMTc94mwtnNzr6M0x0329bbdbf79eaf87d8d77ecb71f7f8d8165443590xcdf21ac40a23b30ac2208d9e3f72c7b798068df70f39...bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnfcosmos1he3vaq8knkx56tevl6mkel42eddq507mt0trdwlava protect work pulse guess toss rocket volu...bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnf...0x3b7e242a976bd013862872532234536ea2e3397d3411...{'pub_key': 'Aph+/Mv0rOA0Ft+8iP6s7UJqTdOXK8tsK...eyJwdWJfa2V5IjoiQXBoKy9NdjByT0EwRnQrOGlQNnM3VU...[9d5777f2aee55468fcd9a426466d35917086cc43855ca...[d560e34d04482e9002c51c4155cc578ad1083221e4d87...
31000000john16495703803QmTqzfWV7DsGQsyoMHJTVBRkDtKBEjy5hPErMRfpbmimPm0x2c105e1ce5bc66a3ec0eb18089b00a471e3bd0a80x08669cd3a21d69b25991ac891c5d1529b64fc893f846...bostrom1efylm4laztlkc0ydye3k6g6cx95h6khw7z27mpcosmos1efylm4laztlkc0ydye3k6g6cx95h6khwa37d9xmedal medal pull rule blur result gift agree g...bostrom1efylm4laztlkc0ydye3k6g6cx95h6khw7z27mp...0x90d40af4947b8a499c22c79e1ea29cea981be1f819db...{'pub_key': 'A055jXQh2nQUJQQdlssNe0ZWQf9KRkUba...eyJwdWJfa2V5IjoiQTA1NWpYUWgyblFVSlFRZGxzc05lMF...[5e42909234f9266f1a6cac6f517ca7971472cf36a9308...[97b75d463fd68f9b16cf586bdf6d0d6dd3044beb7a640...
41000000john16495703804QmNWiHGs9WjmzbcQz96fugBm2Bu6CR3uZVsMuiM1tr4wYD0xcd012b8e0c4a5bd5c9283a0a4281ec553420d1490xb241a6f909672108710c007c1ea3173656139243deaa...bostrom1z4gex65lzw62uqapaxctfku4588ulvgn4yvwmkcosmos1z4gex65lzw62uqapaxctfku4588ulvgnkhca93hope someone gown grain execute reason chaos g...bostrom1z4gex65lzw62uqapaxctfku4588ulvgn4yvwmk...0xcc8656d43b15788c5e0400596f5cebd760da1fd0b75e...{'pub_key': 'Ao00rX4/WgQwgoR9wgwOTEgNq5mh9qfDI...eyJwdWJfa2V5IjoiQW8wMHJYNC9XZ1F3Z29SOXdnd09URW...[d24b8d0cf08a253a44e55c4c134626b76f66b6b4e61e5...[1504edb79fb0500537d30e48ee1ad4fda5c61b7ac9922...
\n
" + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cosmos_proofs_df = proofs_df[proofs_df.address.str.startswith('cosmos')][['address', 'proof']]\n", + "ethereum_proofs_df = proofs_df[proofs_df.address.str.startswith('0x')][['address', 'proof']]\n", + "\n", + "claims_with_proofs_df = claims_df\\\n", + " .merge(\n", + " ethereum_proofs_df.rename(columns={'address': 'ethereum_address', 'proof': 'ethereum_proof'}),\n", + " how='left',\n", + " on='ethereum_address')\\\n", + " .merge(\n", + " cosmos_proofs_df.rename(columns={'address': 'cosmos_address', 'proof': 'cosmos_proof'}),\n", + " how='left',\n", + " on='cosmos_address')\n", + "\n", + "claims_with_proofs_df.to_csv('claims_ethereum_test_data_' + str(NUMBER_OF_PARTICIPANTS) + '_addresses.csv')\n", + "claims_with_proofs_df.head()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Instantiate Contracts\n", + "### Instantiate SUBGRAPH Contracts" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 7, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Name subgraph contract address: bostrom1rncw9n73gm30vhrv6e4p603hav0gue8y5y9fgqa84k4atf5pqvfqcrnpl6\n", + "Avatar subgraph contract address: bostrom164w2vl7z7lpuvex6z3ru0v55fgq3dmvxuqt0aejp49w7fyc8g6kshreggq\n", + "Proof subgraph contract address: bostrom1543j9n7slzff3curyac7ylf2ctg7rk9zjf9ehj08eqx57xj33zzqdy6ga4\n" + ] + } + ], + "source": [ + "if INIT_SUBGRAPH_CONTRACTS:\n", + " name_subgraph_contract_address = \\\n", + " instantiate_contract(\n", + " init_query=f'''{{\"owner\":\"{WALLET_ADDRESS}\", \"executer\":\"{WALLET_ADDRESS}\"}}''',\n", + " contract_code_id=SUBGRAPH_CODE_ID,\n", + " contract_label='test name subgraph')\n", + " avatar_subgraph_contract_address = \\\n", + " instantiate_contract(\n", + " init_query=f'''{{\"owner\":\"{WALLET_ADDRESS}\", \"executer\":\"{WALLET_ADDRESS}\"}}''',\n", + " contract_code_id=SUBGRAPH_CODE_ID,\n", + " contract_label='test avatar subgraph')\n", + " proof_subgraph_contract_address = \\\n", + " instantiate_contract(\n", + " init_query=f'''{{\"owner\":\"{WALLET_ADDRESS}\", \"executer\":\"{WALLET_ADDRESS}\"}}''',\n", + " contract_code_id=SUBGRAPH_CODE_ID,\n", + " contract_label='test proof subgraph')\n", + "else:\n", + " name_subgraph_contract_address = NAME_SUBGRAPH_CONTRACT_ADDRESS\n", + " avatar_subgraph_contract_address = AVATAR_SUBGRAPH_CONTRACT_ADDRESS\n", + " proof_subgraph_contract_address = PROOF_SUBGRAPH_CONTRACT_ADDRESS\n", + "print(f'Name subgraph contract address: {name_subgraph_contract_address}\\n'\n", + " f'Avatar subgraph contract address: {avatar_subgraph_contract_address}\\n'\n", + " f'Proof subgraph contract address: {proof_subgraph_contract_address}')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Instantiate Passport Contract" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 8, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Passport contract address: bostrom1g59m935w4kxmtfx5hhykre7w9q497ptp66asspz76vhgarss5ensdy35s8\n" + ] + } + ], + "source": [ + "if INIT_PASSPORT_CONTRACT:\n", + " passport_contract_address = \\\n", + " instantiate_contract(\n", + " init_query=f'''{{\"name\":\"CPT\", \"minter\":\"{WALLET_ADDRESS}\", \"owner\":\"{WALLET_ADDRESS}\", \"symbol\":\"CPT\", \"avatar_subgraph\": \"{avatar_subgraph_contract_address}\", \"name_subgraph\": \"{name_subgraph_contract_address}\", \"proof_subgraph\": \"{proof_subgraph_contract_address}\"}}''',\n", + " contract_code_id=PASSPORT_CODE_ID,\n", + " contract_label='test passport')\n", + "else:\n", + " passport_contract_address = PASSPORT_CONTRACT_ADDRESS\n", + "print(f'Passport contract address: {passport_contract_address}')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Set executor in the Subgraph Contracts" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 9, + "outputs": [], + "source": [ + "def set_executor_subgraph(subgraph_contract_address: str, new_executor_address: str, display_data: bool = False):\n", + " return execute_contract_bash(execute_query=f'''{{\"update_executer\":{{\"new_executer\":\"{new_executor_address}\"}}}}''',\n", + " contract_address=subgraph_contract_address,\n", + " gas=600000,\n", + " display_data=display_data)\n", + "\n", + "if INIT_PASSPORT_CONTRACT or INIT_SUBGRAPH_CONTRACTS:\n", + " set_executor_subgraph(subgraph_contract_address=name_subgraph_contract_address, new_executor_address=passport_contract_address)\n", + " set_executor_subgraph(subgraph_contract_address=avatar_subgraph_contract_address, new_executor_address=passport_contract_address)\n", + " set_executor_subgraph(subgraph_contract_address=proof_subgraph_contract_address, new_executor_address=passport_contract_address)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Instantiate Gift Contract" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 10, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Gift contract address: bostrom1nn0hd8l3fqhxhsgdeqp3rew79w2nrtm5qedrvamd7a54r2c0czjqj5hcsn\n" + ] + } + ], + "source": [ + "if INIT_GIFT_CONTRACT:\n", + " gift_contract_address = \\\n", + " instantiate_contract(\n", + " init_query=f'''{{\"owner\":\"{WALLET_ADDRESS}\", \"passport\":\"{passport_contract_address}\", \"allowed_native\":\"boot\", \"initial_balance\":\"{INITIAL_BALANCE}\", \"coefficient_up\":\"{COEF_UP}\", \"coefficient_down\":\"{COEF_DOWN}\", \"coefficient\":\"{COEF_UP}\", \"target_claim\":\"{TARGET_CLAIM}\"}}''',\n", + " contract_code_id=GIFT_CODE_ID,\n", + " amount=INITIAL_BALANCE,\n", + " contract_label='test gift')\n", + "else:\n", + " gift_contract_address = GIFT_CONTRACT_ADDRESS\n", + "print(f'Gift contract address: {gift_contract_address}')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Initiate Class of Output Parsing" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 11, + "outputs": [], + "source": [ + "contract_utils = ContractUtils(ipfs_client=ipfs_client,\n", + " address_dict= {\n", + " gift_contract_address: 'Gift Contract',\n", + " passport_contract_address: 'Passport Contract',\n", + " WALLET_ADDRESS: 'Passport Owner Address',\n", + " name_subgraph_contract_address: 'Name Subgraph Contract',\n", + " avatar_subgraph_contract_address: 'Avatar Subgraph Contract',\n", + " proof_subgraph_contract_address: 'Proof Subgraph Contract'})" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Register Merkle Root" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 12, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Events\n", + "\n", + "execute\n", + "\texecute contract: Gift Contract\n", + "\n", + "message from Passport Owner Address wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "wasm\n", + "\t_contract_address: Gift Contract\n", + "\taction: register_merkle_root\n", + "\tmerkle_root: 3dc8421e3f4b94c8e3aed45c031e95a002007dfac135ca398314b163f15a2cee\n", + "Gas used: 121,232\n", + "Tx hash: 85B31F8C7895D79C33B890F77B7F87B5F3218E217B4C489D2189575E2C3E34D4\n" + ] + } + ], + "source": [ + "root_register_output = execute_contract_bash(execute_query=f'''{{\"register_merkle_root\":{{\"merkle_root\":\"{root}\"}}}}''',\n", + " from_address=WALLET_ADDRESS,\n", + " contract_address=gift_contract_address)\n", + "contract_utils.parse_contract_execution_json(root_register_output)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "#### Get Merkle Root form the Gift Contract" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + }, + "execution_count": 156 + }, + { + "cell_type": "code", + "execution_count": 13, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Gift contract bostrom1nn0hd8l3fqhxhsgdeqp3rew79w2nrtm5qedrvamd7a54r2c0czjqj5hcsn\n", + "{'data': {'merkle_root': '3dc8421e3f4b94c8e3aed45c031e95a002007dfac135ca398314b163f15a2cee'}}\n" + ] + } + ], + "source": [ + "print(f'Gift contract {gift_contract_address}')\n", + "print(query_contract(query='''{\"merkle_root\": {}}''',\n", + " contract_address=gift_contract_address))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Send coins to new addresses" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 14, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Events\n", + "\n", + "coin received\n", + "\treceiver: bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c\n", + "\tamount: 1boot\n", + "\treceiver: bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q\n", + "\tamount: 1boot\n", + "\treceiver: bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnf\n", + "\tamount: 1boot\n", + "\treceiver: bostrom1efylm4laztlkc0ydye3k6g6cx95h6khw7z27mp\n", + "\tamount: 1boot\n", + "\treceiver: bostrom1z4gex65lzw62uqapaxctfku4588ulvgn4yvwmk\n", + "\tamount: 1boot\n", + "\treceiver: bostrom13f4sr82dux0kfu7uma3atv7frhzx57ghvm9dxa\n", + "\tamount: 1boot\n", + "\treceiver: bostrom1pypc2s9dh0f6yp0xcqwjap99l5k75qgmp2kntw\n", + "\tamount: 1boot\n", + "\treceiver: bostrom13ugfw2hr7s8f0yttczg8e2v6v04dlwwarsc4rw\n", + "\tamount: 1boot\n", + "\treceiver: bostrom19pfkg9t5nvsmmjffsjv6k625xy98esgukewtyy\n", + "\tamount: 1boot\n", + "\treceiver: bostrom1kzrdznpsuhj8scw8p734eex07g0779vr8xw0d6\n", + "\tamount: 1boot\n", + "\n", + "coin spent\n", + "\tspender: Passport Owner Address\n", + "\tamount: 1boot\n", + "\tspender: Passport Owner Address\n", + "\tamount: 1boot\n", + "\tspender: Passport Owner Address\n", + "\tamount: 1boot\n", + "\tspender: Passport Owner Address\n", + "\tamount: 1boot\n", + "\tspender: Passport Owner Address\n", + "\tamount: 1boot\n", + "\tspender: Passport Owner Address\n", + "\tamount: 1boot\n", + "\tspender: Passport Owner Address\n", + "\tamount: 1boot\n", + "\tspender: Passport Owner Address\n", + "\tamount: 1boot\n", + "\tspender: Passport Owner Address\n", + "\tamount: 1boot\n", + "\tspender: Passport Owner Address\n", + "\tamount: 1boot\n", + "\n", + "{'attributes': [{'key': 'action', 'value': '/cosmos.bank.v1beta1.MsgMultiSend'}, {'key': 'sender', 'value': 'bostrom1mxdtr8lruutugqtxgpw2sf2tl2mhzlq5fd2du0'}, {'key': 'sender', 'value': 'bostrom1mxdtr8lruutugqtxgpw2sf2tl2mhzlq5fd2du0'}, {'key': 'sender', 'value': 'bostrom1mxdtr8lruutugqtxgpw2sf2tl2mhzlq5fd2du0'}, {'key': 'sender', 'value': 'bostrom1mxdtr8lruutugqtxgpw2sf2tl2mhzlq5fd2du0'}, {'key': 'sender', 'value': 'bostrom1mxdtr8lruutugqtxgpw2sf2tl2mhzlq5fd2du0'}, {'key': 'sender', 'value': 'bostrom1mxdtr8lruutugqtxgpw2sf2tl2mhzlq5fd2du0'}, {'key': 'sender', 'value': 'bostrom1mxdtr8lruutugqtxgpw2sf2tl2mhzlq5fd2du0'}, {'key': 'sender', 'value': 'bostrom1mxdtr8lruutugqtxgpw2sf2tl2mhzlq5fd2du0'}, {'key': 'sender', 'value': 'bostrom1mxdtr8lruutugqtxgpw2sf2tl2mhzlq5fd2du0'}, {'key': 'sender', 'value': 'bostrom1mxdtr8lruutugqtxgpw2sf2tl2mhzlq5fd2du0'}, {'key': 'module', 'value': 'bank'}], 'type': 'message'}\n", + "\n", + "transfer\n", + "\trecipient: bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c\n", + "\tamount: 1boot\n", + "\trecipient: bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q\n", + "\tamount: 1boot\n", + "\trecipient: bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnf\n", + "\tamount: 1boot\n", + "\trecipient: bostrom1efylm4laztlkc0ydye3k6g6cx95h6khw7z27mp\n", + "\tamount: 1boot\n", + "\trecipient: bostrom1z4gex65lzw62uqapaxctfku4588ulvgn4yvwmk\n", + "\tamount: 1boot\n", + "\trecipient: bostrom13f4sr82dux0kfu7uma3atv7frhzx57ghvm9dxa\n", + "\tamount: 1boot\n", + "\trecipient: bostrom1pypc2s9dh0f6yp0xcqwjap99l5k75qgmp2kntw\n", + "\tamount: 1boot\n", + "\trecipient: bostrom13ugfw2hr7s8f0yttczg8e2v6v04dlwwarsc4rw\n", + "\tamount: 1boot\n", + "\trecipient: bostrom19pfkg9t5nvsmmjffsjv6k625xy98esgukewtyy\n", + "\tamount: 1boot\n", + "\trecipient: bostrom1kzrdznpsuhj8scw8p734eex07g0779vr8xw0d6\n", + "\tamount: 1boot\n", + "Gas used: 291,894\n", + "Tx hash: 55CDAD5B0692ACDD9A5DD1281C0CAD93B0ABEF41D52D521CEB1CC35F87EB1CC0\n" + ] + } + ], + "source": [ + "bostrom_addresses = claims_with_proofs_df.bostrom_address.to_list()\n", + "NUMBER_ADDRESSES_IN_SENDING_CHUNK = 1000\n", + "bostrom_addresses_chunks = [bostrom_addresses[i: i+ NUMBER_ADDRESSES_IN_SENDING_CHUNK] for i in range(0, len(bostrom_addresses), NUMBER_ADDRESSES_IN_SENDING_CHUNK)]\n", + "\n", + "for bostrom_addresses_item in bostrom_addresses_chunks:\n", + " send_output = contract_utils.send_coins(\n", + " from_seed=WALLET_SEED,\n", + " to_addresses=claims_with_proofs_df.bostrom_address.to_list(),\n", + " amounts=[1] * len(claims_with_proofs_df.bostrom_address.to_list()),\n", + " gas=min(71000 * len(claims_with_proofs_df.bostrom_address.to_list()), int(23e6)),\n", + " display_data=DISPLAY_TX_EXECUTION)\n", + " contract_utils.parse_contract_execution_json(contract_execution_json=send_output)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Create Passports" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 15, + "outputs": [ + { + "data": { + "text/plain": "0it [00:00, ?it/s]", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "104e629a228f405caec0d8f478d061f3" + } + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Events\n", + "\n", + "cyberlinks\n", + "\tPassport Owner Address -> Nickname\n", + "\tNickname -> Passport Owner Address\n", + "\tneuron: Name Subgraph Contract\n", + "\n", + "\tNickname -> Avatar\n", + "\tAvatar -> Nickname\n", + "\tneuron: Avatar Subgraph Contract\n", + "\n", + "\n", + "execute\n", + "\texecute contract: Passport Contract\n", + "\texecute contract: Name Subgraph Contract\n", + "\texecute contract: Avatar Subgraph Contract\n", + "\n", + "message from bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "wasm\n", + "\t_contract_address: Passport Contract\n", + "\taction: mint\n", + "\tminter: Passport Contract\n", + "\ttoken_id: 1413\n", + "Gas used: 440,183\n", + "Tx hash: CDA96114D1ED58B64B4A2F38BEF6E35C4181F9CD8236B14F039BCF39D2F30C50\n" + ] + } + ], + "source": [ + "for index, row in tqdm(claims_with_proofs_df[:NUMBER_OF_ACTIVATED_PARTICIPANTS].iterrows()):\n", + " if index == 0:\n", + " create_passport_json = contract_utils.create_passport(row, display_data=DISPLAY_TX_EXECUTION)\n", + " contract_utils.parse_contract_execution_json(create_passport_json, row=row)\n", + " else:\n", + " contract_utils.create_passport(row)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Proof (Add) Address to Passports" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 16, + "outputs": [ + { + "data": { + "text/plain": "0it [00:00, ?it/s]", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "fb6c020e0e4b4ff3a480f87cb525513f" + } + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Events\n", + "\n", + "cyberlinks\n", + "\tNickname -> Ethereum Address\n", + "\tEthereum Address -> Nickname\n", + "\tneuron: Proof Subgraph Contract\n", + "\n", + "\n", + "execute\n", + "\texecute contract: Passport Contract\n", + "\texecute contract: Proof Subgraph Contract\n", + "\n", + "message from bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "wasm\n", + "\t_contract_address: Passport Contract\n", + "\taction: proof_address\n", + "\tnickname: john16495703801\n", + "\taddress: 0x042a2bb0e4acd457bcadda6e05827aa83ebe91fa\n", + "Gas used: 281,090\n", + "Tx hash: 96C7ADA679DC321B73BF5E090CFA8E086FB906E87B1133E3CD03234F4A568115\n", + "\n", + "Events\n", + "\n", + "cyberlinks\n", + "\tNickname -> Cosmos Address\n", + "\tCosmos Address -> Nickname\n", + "\tneuron: Proof Subgraph Contract\n", + "\n", + "\n", + "execute\n", + "\texecute contract: Passport Contract\n", + "\texecute contract: Proof Subgraph Contract\n", + "\n", + "message from bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "wasm\n", + "\t_contract_address: Passport Contract\n", + "\taction: proof_address\n", + "\tnickname: john16495703801\n", + "\taddress: cosmos162f8clv5ht6j2kmms0556j0mezuj3e6k679fy8\n", + "Gas used: 284,525\n", + "Tx hash: 7FC12C2CD87496689399FD468AAA5E536AB9A04A712CCC016EB1F5C18C7B05BD\n" + ] + } + ], + "source": [ + "for index, row in tqdm(claims_with_proofs_df[:NUMBER_OF_ACTIVATED_PARTICIPANTS].iterrows()):\n", + " if index == 1:\n", + " proof_ethereum_address_json = contract_utils.proof_address(row, display_data=DISPLAY_TX_EXECUTION)\n", + " contract_utils.parse_contract_execution_json(proof_ethereum_address_json, row=row)\n", + "\n", + " proof_cosmos_address_json = contract_utils.proof_address(row, network='cosmos', display_data=DISPLAY_TX_EXECUTION)\n", + " contract_utils.parse_contract_execution_json(proof_cosmos_address_json, row=row)\n", + " else:\n", + " contract_utils.proof_address(row)\n", + " contract_utils.proof_address(row, network='cosmos')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Claim" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 17, + "outputs": [ + { + "data": { + "text/plain": "0it [00:00, ?it/s]", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "c00adad4b7754a548dd287666781a483" + } + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'claim': {'nickname': 'john16495703800', 'gift_claiming_address': '0x9124d6592264ae9e6693dfa39b8f82f16182419c', 'gift_amount': '1000000', 'proof': ['78900d7c06c43ae20970087421f80e49257815363f6a3a3563f5ec29d28940d1', '8cfc554eaa1feae2ff77f1307b0341fbdf7edf6be643fbe6083fd025f5cc6328', '788ddba1a2fb402628dd12efa2aafbab89e1dfbd57d5c2b279626936a990ba2b', '8fdbf3af68c6049a91547cdc34c7c7d862bc2445ba8e0688a4d334fa48e8fdf6', 'eee157a02965da2c68325270a3893ccad9644d1a717328f173758f04d9d31620']}}\n", + "\n", + "Events\n", + "\n", + "coin received\n", + "\treceiver: bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c\n", + "\tamount: 100000boot\n", + "\n", + "coin spent\n", + "\tspender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "execute\n", + "\texecute contract: Gift Contract\n", + "\n", + "message from bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "transfer\n", + "\trecipient: bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c\n", + "\tsender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "wasm\n", + "\t_contract_address: Gift Contract\n", + "\taction: claim\n", + "\toriginal: 0x9124d6592264ae9e6693dfa39b8f82f16182419c\n", + "\ttarget: bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c\n", + "\tamount: 13,000,000\n", + "Gas used: 315,015\n", + "Tx hash: E5005F4F9F5489C312145A360E16DB493DE55AD9E50984540318611B09DF9C24\n", + "{'claim': {'nickname': 'john16495703800', 'gift_claiming_address': 'cosmos1y90fdf63dz6jfahupu4wdqp6v3f69rxpgqhutl', 'gift_amount': '1000000', 'proof': ['30fdf3cb4f18b08a20a2db3acc5ac961095089672200b30ca6d313065c5664c5', '68ee8354cf5d2c8231dda926d3f5a80df35e2f9417d067347c4671bc044715da', '788ddba1a2fb402628dd12efa2aafbab89e1dfbd57d5c2b279626936a990ba2b', '8fdbf3af68c6049a91547cdc34c7c7d862bc2445ba8e0688a4d334fa48e8fdf6', 'eee157a02965da2c68325270a3893ccad9644d1a717328f173758f04d9d31620']}}\n", + "\n", + "Events\n", + "\n", + "coin received\n", + "\treceiver: bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c\n", + "\tamount: 100000boot\n", + "\n", + "coin spent\n", + "\tspender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "execute\n", + "\texecute contract: Gift Contract\n", + "\n", + "message from bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "transfer\n", + "\trecipient: bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c\n", + "\tsender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "wasm\n", + "\t_contract_address: Gift Contract\n", + "\taction: claim\n", + "\toriginal: cosmos1y90fdf63dz6jfahupu4wdqp6v3f69rxpgqhutl\n", + "\ttarget: bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c\n", + "\tamount: 13,000,000\n", + "Gas used: 315,552\n", + "Tx hash: 0FC49AF516BFC03324E5A02B639AF8F42C91B67D562EEA501BA4975F56968176\n", + "{'claim': {'nickname': 'john16495703801', 'gift_claiming_address': '0x042a2bb0e4acd457bcadda6e05827aa83ebe91fa', 'gift_amount': '1000000', 'proof': ['d0c8f5fc66c5a977340dd92568b1b989992f3b3c5b114c46455a5b2491bd9d6b', 'c2c29fd45a2eeebaf8c548789bc2c7412444469f8c24164d3d096bae98b198c1', '3793c33e8114594b79a2ac806df7a921d9b111babc33ce26433b3b29c6b8b7a0', '88942d0d4485753dca9f7593b98470a3e310fa20a4ad78b3548e310edc2f9294', 'eee157a02965da2c68325270a3893ccad9644d1a717328f173758f04d9d31620']}}\n", + "\n", + "Events\n", + "\n", + "coin received\n", + "\treceiver: bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q\n", + "\tamount: 100000boot\n", + "\n", + "coin spent\n", + "\tspender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "execute\n", + "\texecute contract: Gift Contract\n", + "\n", + "message from bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "transfer\n", + "\trecipient: bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q\n", + "\tsender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "wasm\n", + "\t_contract_address: Gift Contract\n", + "\taction: claim\n", + "\toriginal: 0x042a2bb0e4acd457bcadda6e05827aa83ebe91fa\n", + "\ttarget: bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q\n", + "\tamount: 12,922,000\n", + "Gas used: 315,296\n", + "Tx hash: A839FC20A445A038B054DAE1604DC9C1F41626B2A23767772259282A818D1752\n", + "{'claim': {'nickname': 'john16495703801', 'gift_claiming_address': 'cosmos162f8clv5ht6j2kmms0556j0mezuj3e6k679fy8', 'gift_amount': '1000000', 'proof': ['c571aa4a3bb95f180e8865ac710746125a1d2936940a1ffafecf98aadb2d41df', 'c2c29fd45a2eeebaf8c548789bc2c7412444469f8c24164d3d096bae98b198c1', '3793c33e8114594b79a2ac806df7a921d9b111babc33ce26433b3b29c6b8b7a0', '88942d0d4485753dca9f7593b98470a3e310fa20a4ad78b3548e310edc2f9294', 'eee157a02965da2c68325270a3893ccad9644d1a717328f173758f04d9d31620']}}\n", + "\n", + "Events\n", + "\n", + "coin received\n", + "\treceiver: bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q\n", + "\tamount: 100000boot\n", + "\n", + "coin spent\n", + "\tspender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "execute\n", + "\texecute contract: Gift Contract\n", + "\n", + "message from bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "transfer\n", + "\trecipient: bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q\n", + "\tsender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "wasm\n", + "\t_contract_address: Gift Contract\n", + "\taction: claim\n", + "\toriginal: cosmos162f8clv5ht6j2kmms0556j0mezuj3e6k679fy8\n", + "\ttarget: bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q\n", + "\tamount: 12,844,000\n", + "Gas used: 315,761\n", + "Tx hash: 3E5B0D8D5F488749C6105A71318FC955DC2ACF818F0B10D8C563B9ADBE942F3F\n", + "{'claim': {'nickname': 'john16495703802', 'gift_claiming_address': '0x0329bbdbf79eaf87d8d77ecb71f7f8d816544359', 'gift_amount': '1000000', 'proof': ['9d5777f2aee55468fcd9a426466d35917086cc43855ca8274b54ab69a3926763', '0f4f00eb184448c07e51dacabbacf44b650bcff804336cd35b53aea2f28b8210', '3793c33e8114594b79a2ac806df7a921d9b111babc33ce26433b3b29c6b8b7a0', '88942d0d4485753dca9f7593b98470a3e310fa20a4ad78b3548e310edc2f9294', 'eee157a02965da2c68325270a3893ccad9644d1a717328f173758f04d9d31620']}}\n", + "\n", + "Events\n", + "\n", + "coin received\n", + "\treceiver: bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnf\n", + "\tamount: 100000boot\n", + "\n", + "coin spent\n", + "\tspender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "execute\n", + "\texecute contract: Gift Contract\n", + "\n", + "message from bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnf wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "transfer\n", + "\trecipient: bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnf\n", + "\tsender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "wasm\n", + "\t_contract_address: Gift Contract\n", + "\taction: claim\n", + "\toriginal: 0x0329bbdbf79eaf87d8d77ecb71f7f8d816544359\n", + "\ttarget: bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnf\n", + "\tamount: 12,766,468\n", + "Gas used: 315,500\n", + "Tx hash: 56B810E9EC7BE15F13838BC24E61A30D3F09AA9DD68808B099EA1EEFC880877A\n", + "{'claim': {'nickname': 'john16495703802', 'gift_claiming_address': 'cosmos1he3vaq8knkx56tevl6mkel42eddq507mt0trdw', 'gift_amount': '1000000', 'proof': ['d560e34d04482e9002c51c4155cc578ad1083221e4d87016061a18fcb63f7406', '66f78f689f1978d3b6988865b455868e67c42f65330e2c75616b791c6d987b76', 'f4ddc1e9906cd06afa3d49b7f201b88b2a91abbcdb5ef77fd96f9064791bca4c', '487e9addb6ac54920bf3b68cab20c089b773e22db2e2b74f65795590cd5421fe']}}\n", + "\n", + "Events\n", + "\n", + "coin received\n", + "\treceiver: bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnf\n", + "\tamount: 100000boot\n", + "\n", + "coin spent\n", + "\tspender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "execute\n", + "\texecute contract: Gift Contract\n", + "\n", + "message from bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnf wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "transfer\n", + "\trecipient: bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnf\n", + "\tsender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "wasm\n", + "\t_contract_address: Gift Contract\n", + "\taction: claim\n", + "\toriginal: cosmos1he3vaq8knkx56tevl6mkel42eddq507mt0trdw\n", + "\ttarget: bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnf\n", + "\tamount: 12,689,404\n", + "Gas used: 314,581\n", + "Tx hash: 57C3DDED0189DFF25682AF6B901383F1E767366773295685FC3AE9199F845C5E\n", + "{'claim': {'nickname': 'john16495703803', 'gift_claiming_address': '0x2c105e1ce5bc66a3ec0eb18089b00a471e3bd0a8', 'gift_amount': '1000000', 'proof': ['5e42909234f9266f1a6cac6f517ca7971472cf36a9308f85e532d7dbb06139bd', '68ee8354cf5d2c8231dda926d3f5a80df35e2f9417d067347c4671bc044715da', '788ddba1a2fb402628dd12efa2aafbab89e1dfbd57d5c2b279626936a990ba2b', '8fdbf3af68c6049a91547cdc34c7c7d862bc2445ba8e0688a4d334fa48e8fdf6', 'eee157a02965da2c68325270a3893ccad9644d1a717328f173758f04d9d31620']}}\n", + "\n", + "Events\n", + "\n", + "coin received\n", + "\treceiver: bostrom1efylm4laztlkc0ydye3k6g6cx95h6khw7z27mp\n", + "\tamount: 100000boot\n", + "\n", + "coin spent\n", + "\tspender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "execute\n", + "\texecute contract: Gift Contract\n", + "\n", + "message from bostrom1efylm4laztlkc0ydye3k6g6cx95h6khw7z27mp wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "transfer\n", + "\trecipient: bostrom1efylm4laztlkc0ydye3k6g6cx95h6khw7z27mp\n", + "\tsender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "wasm\n", + "\t_contract_address: Gift Contract\n", + "\taction: claim\n", + "\toriginal: 0x2c105e1ce5bc66a3ec0eb18089b00a471e3bd0a8\n", + "\ttarget: bostrom1efylm4laztlkc0ydye3k6g6cx95h6khw7z27mp\n", + "\tamount: 12,612,805\n", + "Gas used: 315,704\n", + "Tx hash: F0E35A791B483BB675EE1A23F01D839E6C29DEB1FB0C63A5ACE4CB0A32225D73\n", + "{'claim': {'nickname': 'john16495703803', 'gift_claiming_address': 'cosmos1efylm4laztlkc0ydye3k6g6cx95h6khwa37d9x', 'gift_amount': '1000000', 'proof': ['97b75d463fd68f9b16cf586bdf6d0d6dd3044beb7a640a0b42030f5684554e1e', '8d0ba595bfdde180641f64244d954a8e6f7679f414880a820f9621fd440d7637', '3f5a7595a6098370287df811d2d2af459135cd0a8b7e71c4b5c02cb1cdd6eb28', '88942d0d4485753dca9f7593b98470a3e310fa20a4ad78b3548e310edc2f9294', 'eee157a02965da2c68325270a3893ccad9644d1a717328f173758f04d9d31620']}}\n", + "\n", + "Events\n", + "\n", + "coin received\n", + "\treceiver: bostrom1efylm4laztlkc0ydye3k6g6cx95h6khw7z27mp\n", + "\tamount: 100000boot\n", + "\n", + "coin spent\n", + "\tspender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "execute\n", + "\texecute contract: Gift Contract\n", + "\n", + "message from bostrom1efylm4laztlkc0ydye3k6g6cx95h6khw7z27mp wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "transfer\n", + "\trecipient: bostrom1efylm4laztlkc0ydye3k6g6cx95h6khw7z27mp\n", + "\tsender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "wasm\n", + "\t_contract_address: Gift Contract\n", + "\taction: claim\n", + "\toriginal: cosmos1efylm4laztlkc0ydye3k6g6cx95h6khwa37d9x\n", + "\ttarget: bostrom1efylm4laztlkc0ydye3k6g6cx95h6khw7z27mp\n", + "\tamount: 12,536,668\n", + "Gas used: 315,976\n", + "Tx hash: 7F952323C46F76D62B74468F2675620CA24C369878673EF3E418F4FF3A0173AD\n", + "{'claim': {'nickname': 'john16495703804', 'gift_claiming_address': '0xcd012b8e0c4a5bd5c9283a0a4281ec553420d149', 'gift_amount': '1000000', 'proof': ['d24b8d0cf08a253a44e55c4c134626b76f66b6b4e61e547479585f4f155817b0', '66f78f689f1978d3b6988865b455868e67c42f65330e2c75616b791c6d987b76', 'f4ddc1e9906cd06afa3d49b7f201b88b2a91abbcdb5ef77fd96f9064791bca4c', '487e9addb6ac54920bf3b68cab20c089b773e22db2e2b74f65795590cd5421fe']}}\n", + "\n", + "Events\n", + "\n", + "coin received\n", + "\treceiver: bostrom1z4gex65lzw62uqapaxctfku4588ulvgn4yvwmk\n", + "\tamount: 100000boot\n", + "\n", + "coin spent\n", + "\tspender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "execute\n", + "\texecute contract: Gift Contract\n", + "\n", + "message from bostrom1z4gex65lzw62uqapaxctfku4588ulvgn4yvwmk wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "transfer\n", + "\trecipient: bostrom1z4gex65lzw62uqapaxctfku4588ulvgn4yvwmk\n", + "\tsender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "wasm\n", + "\t_contract_address: Gift Contract\n", + "\taction: claim\n", + "\toriginal: 0xcd012b8e0c4a5bd5c9283a0a4281ec553420d149\n", + "\ttarget: bostrom1z4gex65lzw62uqapaxctfku4588ulvgn4yvwmk\n", + "\tamount: 12,460,991\n", + "Gas used: 314,255\n", + "Tx hash: DD8D790592140D135DDC6811116B74D7FAB2E3BAEEA3E1992BDC12B59761B9B3\n", + "{'claim': {'nickname': 'john16495703804', 'gift_claiming_address': 'cosmos1z4gex65lzw62uqapaxctfku4588ulvgnkhca93', 'gift_amount': '1000000', 'proof': ['1504edb79fb0500537d30e48ee1ad4fda5c61b7ac9922d18f6bcac92d6b51ef6', 'ede12e4ad76960f7b154499c6a8bdad1546c29b6c0c944454948d6d3a3474e00', '4a9eef354ac65a0aea9fe072e011797544eaeb847f9aec2895be9a2709dd9e9f', '8fdbf3af68c6049a91547cdc34c7c7d862bc2445ba8e0688a4d334fa48e8fdf6', 'eee157a02965da2c68325270a3893ccad9644d1a717328f173758f04d9d31620']}}\n", + "\n", + "Events\n", + "\n", + "coin received\n", + "\treceiver: bostrom1z4gex65lzw62uqapaxctfku4588ulvgn4yvwmk\n", + "\tamount: 100000boot\n", + "\n", + "coin spent\n", + "\tspender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "execute\n", + "\texecute contract: Gift Contract\n", + "\n", + "message from bostrom1z4gex65lzw62uqapaxctfku4588ulvgn4yvwmk wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "transfer\n", + "\trecipient: bostrom1z4gex65lzw62uqapaxctfku4588ulvgn4yvwmk\n", + "\tsender: Gift Contract\n", + "\tamount: 100000boot\n", + "\n", + "wasm\n", + "\t_contract_address: Gift Contract\n", + "\taction: claim\n", + "\toriginal: cosmos1z4gex65lzw62uqapaxctfku4588ulvgnkhca93\n", + "\ttarget: bostrom1z4gex65lzw62uqapaxctfku4588ulvgn4yvwmk\n", + "\tamount: 12,385,771\n", + "Gas used: 316,001\n", + "Tx hash: E0C2F2951E77A4CA44F3F16EBA34EC5D41103EB1A9895BC3186C9EC3EC86274A\n" + ] + } + ], + "source": [ + "for index, row in tqdm(claims_with_proofs_df[:NUMBER_OF_ACTIVATED_PARTICIPANTS].iterrows()):\n", + " if index == 0:\n", + " claim_ethereum_json = contract_utils.claim(row, display_data=DISPLAY_TX_EXECUTION)\n", + " contract_utils.parse_contract_execution_json(claim_ethereum_json)\n", + "\n", + " claim_cosmos_json = contract_utils.claim(row, network='cosmos', display_data=DISPLAY_TX_EXECUTION)\n", + " contract_utils.parse_contract_execution_json(claim_cosmos_json)\n", + " else:\n", + " claim_ethereum_json_2 = contract_utils.claim(row)\n", + " contract_utils.parse_contract_execution_json(claim_ethereum_json_2)\n", + "\n", + " claim_cosmos_json_2 = contract_utils.claim(row, network='cosmos')\n", + " contract_utils.parse_contract_execution_json(claim_cosmos_json_2)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Release Gift" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + }, + "execution_count": 11 + }, + { + "cell_type": "code", + "execution_count": 18, + "outputs": [ + { + "data": { + "text/plain": "0it [00:00, ?it/s]", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "880e97937b774e1b9a8bfc16bed1176d" + } + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Events\n", + "\n", + "coin received\n", + "\treceiver: bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c\n", + "\tamount: 1290000boot\n", + "\n", + "coin spent\n", + "\tspender: Gift Contract\n", + "\tamount: 1290000boot\n", + "\n", + "execute\n", + "\texecute contract: Gift Contract\n", + "\n", + "message from bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "transfer\n", + "\trecipient: bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c\n", + "\tsender: Gift Contract\n", + "\tamount: 1290000boot\n", + "\n", + "wasm\n", + "\t_contract_address: Gift Contract\n", + "\taction: release\n", + "\taddress: bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c\n", + "\tgift_address: 0x9124d6592264ae9e6693dfa39b8f82f16182419c\n", + "\tstage: 9\n", + "\tamount: 1,290,000\n", + "Gas used: 157,502\n", + "Tx hash: 07B209093AB4960C86A93450B0023387BA9337A3716C92F83BA92D998130DA9B\n", + "\n", + "Events\n", + "\n", + "coin received\n", + "\treceiver: bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c\n", + "\tamount: 1290000boot\n", + "\n", + "coin spent\n", + "\tspender: Gift Contract\n", + "\tamount: 1290000boot\n", + "\n", + "execute\n", + "\texecute contract: Gift Contract\n", + "\n", + "message from bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "transfer\n", + "\trecipient: bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c\n", + "\tsender: Gift Contract\n", + "\tamount: 1290000boot\n", + "\n", + "wasm\n", + "\t_contract_address: Gift Contract\n", + "\taction: release\n", + "\taddress: bostrom1y90fdf63dz6jfahupu4wdqp6v3f69rxptnr04c\n", + "\tgift_address: cosmos1y90fdf63dz6jfahupu4wdqp6v3f69rxpgqhutl\n", + "\tstage: 9\n", + "\tamount: 1,290,000\n", + "Gas used: 157,681\n", + "Tx hash: 5E702803DE153457E7CA64E1EBEEB5FAC7F05B3469641E6736E515D58CD4B89B\n" + ] + } + ], + "source": [ + "for index, row in tqdm(claims_with_proofs_df[:NUMBER_OF_ACTIVATED_PARTICIPANTS].iterrows()):\n", + " if index == 0:\n", + " release_ethereum_json = contract_utils.release(row, display_data=DISPLAY_TX_EXECUTION)\n", + " contract_utils.parse_contract_execution_json(release_ethereum_json)\n", + "\n", + " release_cosmos_json = contract_utils.release(row, network='cosmos', display_data=DISPLAY_TX_EXECUTION)\n", + " contract_utils.parse_contract_execution_json(release_cosmos_json)\n", + " else:\n", + " contract_utils.release(row)\n", + " contract_utils.release(row, network='cosmos')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Passport NFT testing\n", + "### Transfer Passport" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 19, + "outputs": [], + "source": [ + "def get_passport_id(bostrom_address: str) -> str:\n", + " return query_contract(query=f'''{{\"tokens\": {{\"owner\": \"{bostrom_address}\"}}}}''',\n", + " contract_address=passport_contract_address)['data']['tokens'][0]" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 20, + "outputs": [ + { + "data": { + "text/plain": "0it [00:00, ?it/s]", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "179496bd31d340eba7e93395240234ec" + } + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Events\n", + "\n", + "cyberlinks\n", + "\tQmPcFZbajorUYkkdeowYUD1zJRAWA13N4Koy4rLeZ4cDji -> Nickname\n", + "\tNickname -> QmPcFZbajorUYkkdeowYUD1zJRAWA13N4Koy4rLeZ4cDji\n", + "\tneuron: Name Subgraph Contract\n", + "\n", + "\n", + "execute\n", + "\texecute contract: Passport Contract\n", + "\texecute contract: Name Subgraph Contract\n", + "\texecute contract: Avatar Subgraph Contract\n", + "\n", + "message from bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "reply\n", + "\treply contract: Avatar Subgraph Contract\n", + "\n", + "wasm\n", + "\t_contract_address: Passport Contract\n", + "\taction: transfer_nft\n", + "\tsender: bostrom162f8clv5ht6j2kmms0556j0mezuj3e6ked366q\n", + "\trecipient: Passport Owner Address\n", + "\ttoken_id: 1414\n", + "Gas used: 385,801\n", + "Tx hash: 5F0B12D48BAB7041AACE2E9EA131AC85065B9D8AEE33D44568A4798D84785BC1\n" + ] + } + ], + "source": [ + "for index, row in tqdm(claims_with_proofs_df[:NUMBER_OF_ACTIVATED_PARTICIPANTS].iterrows()):\n", + " if index == 1:\n", + " transfer_passport_json = contract_utils.transfer_passport(\n", + " row,\n", + " token_id=get_passport_id(row['bostrom_address']),\n", + " to_address=WALLET_ADDRESS,\n", + " display_data=DISPLAY_TX_EXECUTION)\n", + " contract_utils.parse_contract_execution_json(\n", + " transfer_passport_json,\n", + " row=row)\n", + " else:\n", + " pass" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Burn" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 21, + "outputs": [ + { + "data": { + "text/plain": "0it [00:00, ?it/s]", + "application/vnd.jupyter.widget-view+json": { + "version_major": 2, + "version_minor": 0, + "model_id": "f296088669f345c6b20f7ac74f137ae8" + } + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Events\n", + "\n", + "cyberlinks\n", + "\tcyberhole -> Nickname\n", + "\tNickname -> cyberhole\n", + "\tneuron: Name Subgraph Contract\n", + "\n", + "\tcyberhole -> Avatar\n", + "\tAvatar -> cyberhole\n", + "\tneuron: Avatar Subgraph Contract\n", + "\n", + "\n", + "execute\n", + "\texecute contract: Passport Contract\n", + "\texecute contract: Name Subgraph Contract\n", + "\texecute contract: Avatar Subgraph Contract\n", + "\n", + "message from bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnf wasm /cosmwasm.wasm.v1.MsgExecuteContract\n", + "\n", + "wasm\n", + "\t_contract_address: Passport Contract\n", + "\taction: burn\n", + "\tsender: bostrom1he3vaq8knkx56tevl6mkel42eddq507mgulsnf\n", + "\ttoken_id: 1415\n", + "Gas used: 381,878\n", + "Tx hash: 23476318EFB8D19BDF644F8DFB81BEFF9C0CA9BB188965FEB5E7E7AE76033C5E\n" + ] + } + ], + "source": [ + "for index, row in tqdm(claims_with_proofs_df[2:NUMBER_OF_ACTIVATED_PARTICIPANTS].iterrows()):\n", + " if index == 2:\n", + " burn_passport_json = contract_utils.burn_passport(row, token_id=get_passport_id(row['bostrom_address']), display_data=DISPLAY_TX_EXECUTION)\n", + " contract_utils.parse_contract_execution_json(burn_passport_json, row=row)\n", + " else:\n", + " pass" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Add tests for Passport contract\n", + "#### create_passport +\n", + "#### update_name\n", + "#### update_avatar\n", + "#### proof_address +\n", + "#### remove_address\n", + "#### set_minter\n", + "#### set_owner\n", + "#### set_active\n", + "#### set_subgraphs\n", + "#### transfer_nft +\n", + "#### send_nft\n", + "#### mint\n", + "#### burn +\n", + "#### approve\n", + "#### approve_all\n", + "#### revoke\n", + "#### revoke_all\n", + "#### Expirations\n", + "##### at_height\n", + "##### at_time\n", + "##### never\n", + "\n", + "### Add tests for Gift contract\n", + "#### update_owner\n", + "#### update_passport_addr\n", + "#### update_target\n", + "#### register_merkle_root +\n", + "#### claim +\n", + "#### release +\n", + "\n", + "### Add tests for subgraph contract\n", + "#### update_owner\n", + "#### update_executer +\n" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/contracts/cw-cyber-gift/testdata/generate_test_data/gift_final_merkle_tree.ipynb b/contracts/cw-cyber-gift/testdata/generate_test_data/gift_final_merkle_tree.ipynb new file mode 100644 index 0000000..61b67a8 --- /dev/null +++ b/contracts/cw-cyber-gift/testdata/generate_test_data/gift_final_merkle_tree.ipynb @@ -0,0 +1,461 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "## Calculation of Merkle Root and Proofs for Final Distribution" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 1, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import os\n", + "from multiprocess import Pool\n", + "from math import ceil\n", + "import json\n", + "from tqdm.notebook import tqdm\n", + "from IPython.core.display import display, HTML\n", + "\n", + "from contract_utils import instantiate_contract, execute_contract, query_contract, get_proofs\n", + "\n", + "INITIAL_BALANCE = str(100_000_000_000)\n", + "\n", + "COEF_UP = str(13)\n", + "COEF_DOWN = str(7)\n", + "TARGET_CLAIM = str(10)\n", + "\n", + "NUMBER_OF_THREADS = 15\n", + "BASH_SIZE = 1000\n", + "\n", + "WALLET_ADDRESS = os.getenv('WALLET_ADDRESS')\n", + "DISPLAY_TX_EXECUTION = False\n", + "\n", + "INIT_SUBGRAPH_CONTRACTS = False\n", + "SUBGRAPH_CODE_ID = str(40)\n", + "NAME_SUBGRAPH_CONTRACT_ADDRESS = 'bostrom1rncw9n73gm30vhrv6e4p603hav0gue8y5y9fgqa84k4atf5pqvfqcrnpl6'\n", + "AVATAR_SUBGRAPH_CONTRACT_ADDRESS = 'bostrom164w2vl7z7lpuvex6z3ru0v55fgq3dmvxuqt0aejp49w7fyc8g6kshreggq'\n", + "PROOF_SUBGRAPH_CONTRACT_ADDRESS = 'bostrom1543j9n7slzff3curyac7ylf2ctg7rk9zjf9ehj08eqx57xj33zzqdy6ga4'\n", + "\n", + "INIT_PASSPORT_CONTRACT = False\n", + "PASSPORT_CODE_ID = str(25)\n", + "PASSPORT_CONTRACT_ADDRESS = 'bostrom15hzg7eaxgs6ecn46gmu4juc9tau2w45l9cnf8n0797nmmtkdv7jscv88ra'\n", + "\n", + "INIT_GIFT_CONTRACT = True\n", + "GIFT_CODE_ID = str(20)\n", + "GIFT_CONTRACT_ADDRESS = 'bostrom1rt2acjyhs4jfjdq56pftpu7762hy9gfl63je6fnhwrc5p5y4kmuqxg0262'\n", + "MERKLE_ROOT = 'd8dfb2c769cac706bb3d9cea1b681ff6ba5f680f0af05cefbe68a74027317ab0'\n", + "\n", + "CALCULATE_PROOFS = False\n", + "STATE_FILE_NAME = 'gift_state_final_220320.csv'\n", + "ROOT_SOURCE_FILE_NAME = 'root_source_final.json'\n", + "PROOF_FILE_NAME = 'proof_final.json'\n", + "ROOT_FILE_NAME = 'root_final'" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Create source data for Merkle Tree" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 2, + "outputs": [], + "source": [ + "if CALCULATE_PROOFS:\n", + " state_df = pd.read_csv(STATE_FILE_NAME)\n", + " display(HTML(state_df.head().to_html(index=False, notebook=True, show_dimensions=False)))\n", + "\n", + " state_agg_by_address = state_df.groupby('address')['gift'].sum().reset_index().rename(columns={'gift': 'amount'})\n", + " state_agg_by_address['amount'] = state_agg_by_address['amount'] * 1_000_000\n", + "\n", + " root_source_list = state_agg_by_address.to_dict(orient='records')\n", + " number_of_addresses = len(root_source_list)\n", + " print(f'Number of addresses: {number_of_addresses:>,}')\n", + "\n", + " with open(ROOT_SOURCE_FILE_NAME, 'w') as outfile:\n", + " outfile.write(str(root_source_list).replace(\"'\", '\"'))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Calculate Merkle Root and Proofs" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 3, + "outputs": [], + "source": [ + "if CALCULATE_PROOFS:\n", + " tasks = list(\n", + " (\n", + " (ROOT_SOURCE_FILE_NAME,\n", + " f'final_temp/proofs_{i}.json',\n", + " i * BASH_SIZE,\n", + " min(number_of_addresses, (i + 1) * BASH_SIZE))\n", + " for i in range(ceil(number_of_addresses/BASH_SIZE))\n", + " )\n", + " )\n", + " print(f'First task: {tasks[0]}\\nLast task: {tasks[-1]}\\nTotal tasks: {len(tasks)}\\nThreads: {NUMBER_OF_THREADS:>,}')\n", + " with Pool(processes=NUMBER_OF_THREADS) as pool:\n", + " res = pool.starmap(get_proofs, tasks, 1)\n", + " assert res == [True] * len(res)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Get proofs and root from temporary files" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 4, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Merkle root: d8dfb2c769cac706bb3d9cea1b681ff6ba5f680f0af05cefbe68a74027317ab0\n", + "Number of proofs: 4426091\n" + ] + }, + { + "data": { + "text/plain": "", + "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
Unnamed: 0addressamountproof
00x000000000000000000000000000000000000002024000000['1c12d0e0e5c8dd03999ed1b49e4f8bdc3deb2238c91c7...
10x00000000000000000000000000000000009a6ca024000000['6f914877a2f44e37e2ad1937ba54e6a69078f40865d90...
20x000000000000000000000000000000000e297bdb24000000['28ab88ce80aa1d602c9273e3c01762a15db4a754326be...
30x000000000000000000000000000000000edd899b137000000['cf5c194a5ddb674c0feb1ed63f82d98ada8848db46759...
40x0000000000000000000000000000000053db6ff11505000000['cd92981c19927509a95c585ccd131219ceccda3093684...
\n
" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "if CALCULATE_PROOFS:\n", + " roots = []\n", + " proofs = []\n", + " for task in tqdm(tasks):\n", + " with open(task[1], 'r') as proof_file:\n", + " root_and_proof_json = json.load(proof_file)\n", + " if len(proofs) == 0:\n", + " proofs = root_and_proof_json['proofs']\n", + " else:\n", + " proofs.extend(root_and_proof_json['proofs'])\n", + " roots.append(root_and_proof_json['merkle_root'])\n", + "\n", + " assert roots == [roots[0]] * len(roots)\n", + " root = roots[0]\n", + " proofs_df = pd.DataFrame(proofs)\n", + " proofs_df.to_csv('proofs_final.csv', header=True, index=True)\n", + "else:\n", + " root = MERKLE_ROOT\n", + " proofs_df = pd.read_csv('proofs_final.csv')\n", + "print(f'Merkle root: {root}')\n", + "print(f'Number of proofs: {len(proofs_df)}')\n", + "display(HTML(proofs_df.head().to_html(index=False, notebook=True, show_dimensions=False)))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Instantiate Contracts\n", + "### Instantiate SUBGRAPH Contracts" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 5, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Name subgraph contract address: bostrom1rncw9n73gm30vhrv6e4p603hav0gue8y5y9fgqa84k4atf5pqvfqcrnpl6\n", + "Avatar subgraph contract address: bostrom164w2vl7z7lpuvex6z3ru0v55fgq3dmvxuqt0aejp49w7fyc8g6kshreggq\n", + "Proof subgraph contract address: bostrom1543j9n7slzff3curyac7ylf2ctg7rk9zjf9ehj08eqx57xj33zzqdy6ga4\n" + ] + } + ], + "source": [ + "if INIT_SUBGRAPH_CONTRACTS:\n", + " name_subgraph_contract_address = \\\n", + " instantiate_contract(\n", + " init_query=f'''{{\"owner\":\"{WALLET_ADDRESS}\", \"executer\":\"{WALLET_ADDRESS}\"}}''',\n", + " contract_code_id=SUBGRAPH_CODE_ID,\n", + " contract_label='test name subgraph')\n", + " avatar_subgraph_contract_address = \\\n", + " instantiate_contract(\n", + " init_query=f'''{{\"owner\":\"{WALLET_ADDRESS}\", \"executer\":\"{WALLET_ADDRESS}\"}}''',\n", + " contract_code_id=SUBGRAPH_CODE_ID,\n", + " contract_label='test avatar subgraph')\n", + " proof_subgraph_contract_address = \\\n", + " instantiate_contract(\n", + " init_query=f'''{{\"owner\":\"{WALLET_ADDRESS}\", \"executer\":\"{WALLET_ADDRESS}\"}}''',\n", + " contract_code_id=SUBGRAPH_CODE_ID,\n", + " contract_label='test proof subgraph')\n", + "else:\n", + " name_subgraph_contract_address = NAME_SUBGRAPH_CONTRACT_ADDRESS\n", + " avatar_subgraph_contract_address = AVATAR_SUBGRAPH_CONTRACT_ADDRESS\n", + " proof_subgraph_contract_address = PROOF_SUBGRAPH_CONTRACT_ADDRESS\n", + "print(f'Name subgraph contract address: {name_subgraph_contract_address}\\n'\n", + " f'Avatar subgraph contract address: {avatar_subgraph_contract_address}\\n'\n", + " f'Proof subgraph contract address: {proof_subgraph_contract_address}')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Instantiate Passport Contract" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 6, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Passport contract address: bostrom15hzg7eaxgs6ecn46gmu4juc9tau2w45l9cnf8n0797nmmtkdv7jscv88ra\n" + ] + } + ], + "source": [ + "if INIT_PASSPORT_CONTRACT:\n", + " passport_contract_address = \\\n", + " instantiate_contract(\n", + " init_query=f'''{{\"name\":\"CPT\", \"minter\":\"{WALLET_ADDRESS}\", \"owner\":\"{WALLET_ADDRESS}\", \"symbol\":\"CPT\", \"avatar_subgraph\": \"{avatar_subgraph_contract_address}\", \"name_subgraph\": \"{name_subgraph_contract_address}\", \"proof_subgraph\": \"{proof_subgraph_contract_address}\"}}''',\n", + " contract_code_id=PASSPORT_CODE_ID,\n", + " contract_label='test passport')\n", + "else:\n", + " passport_contract_address = PASSPORT_CONTRACT_ADDRESS\n", + "print(f'Passport contract address: {passport_contract_address}')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Set executor in the Subgraph Contracts" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 7, + "outputs": [], + "source": [ + "def set_executor_subgraph(subgraph_contract_address: str, new_executor_address: str, display_data: bool = False):\n", + " return execute_contract(execute_query=f'''{{\"update_executer\":{{\"new_executer\":\"{new_executor_address}\"}}}}''',\n", + " contract_address=subgraph_contract_address,\n", + " gas=600000,\n", + " display_data=display_data)\n", + "\n", + "if INIT_PASSPORT_CONTRACT or INIT_SUBGRAPH_CONTRACTS:\n", + " set_executor_subgraph(subgraph_contract_address=name_subgraph_contract_address, new_executor_address=passport_contract_address)\n", + " set_executor_subgraph(subgraph_contract_address=avatar_subgraph_contract_address, new_executor_address=passport_contract_address)\n", + " set_executor_subgraph(subgraph_contract_address=proof_subgraph_contract_address, new_executor_address=passport_contract_address)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Instantiate Gift Contract" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 8, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Gift contract address: bostrom1yhdxsta8qt7guughmerkekkxegrmkgycfyhxz9m4k8mu27su0hnq22ecn3\n" + ] + } + ], + "source": [ + "if INIT_GIFT_CONTRACT:\n", + " gift_contract_address = \\\n", + " instantiate_contract(\n", + " init_query=f'''{{\"owner\":\"{WALLET_ADDRESS}\", \"passport\":\"{passport_contract_address}\", \"allowed_native\":\"boot\", \"initial_balance\":\"{INITIAL_BALANCE}\", \"coefficient_up\":\"{COEF_UP}\", \"coefficient_down\":\"{COEF_DOWN}\", \"coefficient\":\"{COEF_UP}\", \"target_claim\":\"{TARGET_CLAIM}\"}}''',\n", + " contract_code_id=GIFT_CODE_ID,\n", + " amount=INITIAL_BALANCE,\n", + " contract_label='test gift')\n", + "else:\n", + " gift_contract_address = GIFT_CONTRACT_ADDRESS\n", + "print(f'Gift contract address: {gift_contract_address}')" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Register Merkle Root" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 9, + "outputs": [], + "source": [ + "root_register_output = execute_contract(execute_query=f'''{{\"register_merkle_root\":{{\"merkle_root\":\"{root}\"}}}}''',\n", + " contract_address=gift_contract_address)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 10, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Gift contract bostrom1yhdxsta8qt7guughmerkekkxegrmkgycfyhxz9m4k8mu27su0hnq22ecn3\n", + "{'data': {'merkle_root': 'd8dfb2c769cac706bb3d9cea1b681ff6ba5f680f0af05cefbe68a74027317ab0'}}\n" + ] + } + ], + "source": [ + "print(f'Gift contract {gift_contract_address}')\n", + "print(query_contract(query='''{\"merkle_root\": {}}''',\n", + " contract_address=gift_contract_address))\n" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/contracts/cw-cyber-gift/testdata/generate_test_data/index.ts b/contracts/cw-cyber-gift/testdata/generate_test_data/index.ts new file mode 100644 index 0000000..b30a4e6 --- /dev/null +++ b/contracts/cw-cyber-gift/testdata/generate_test_data/index.ts @@ -0,0 +1,102 @@ +import {Command, flags} from '@oclif/command' +import { readFileSync, writeFileSync } from 'fs'; +import CryptoJS from "crypto-js"; +import sha256 from "crypto-js/sha256"; +import { MerkleTree } from "merkletreejs"; + +interface Encoding { + address: string; + amount: string; +} + +class Airdrop { + private tree: MerkleTree; + + constructor(accounts: Array) { + const leaves = accounts.map((a) => this.encode_data(a)); + this.tree = new MerkleTree(leaves, sha256, { sort: true }); + } + + encode_data(data: Encoding): CryptoJS.lib.WordArray { + return sha256( + data.address + data.amount + ); + } + + public getMerkleRoot(): string { + return this.tree.getRoot().toString("hex"); + } + + public getMerkleProof(data: Encoding): string[] { + return this.tree + .getProof(this.encode_data(data).toString()) + .map((v) => v.data.toString("hex")); + } + + public verify(proof: [string], data: Encoding): boolean { + return this.tree.verify( + proof, + this.encode_data(data).toString(), + this.tree.getRoot() + ); + } +} + +class GenerateProofs extends Command { + static description = 'Generates merkle root and proofs for given addresses' + + static examples = [ + `$ generate-merkle-proofs --input root_testing_source.json --output proof.json --start_index 1 --end_index 100`, + ] + + static flags = { + input: flags.string({ + char: 'f', + description: 'airdrop file location' + }), + output: flags.string({ + char: 'o', + description: 'output file location' + }), + start_index: flags.integer({ + char: 's', + description: 'start index in the airdrop file for getting proofs', + default: 1, + required: false + }), + end_index: flags.integer({ + char: 'e', + description: 'end index in the airdrop file for getting proofs', + default: -1, + required: false + }), + } + + async run() { + const {flags} = this.parse(GenerateProofs) + + if (!flags.input) { + this.error(new Error('Airdrop file location not defined')) + } + + if (!flags.output) { + this.error(new Error('Output file location not defined')) + } + + let file = readFileSync(flags.input, 'utf-8'); + let receivers: Array = JSON.parse(file); + let airdrop = new Airdrop(receivers); + + let merkle_root = airdrop.getMerkleRoot() + console.log("Merkle root: " + merkle_root) + + let result = + {"merkle_root": merkle_root, + "proofs": receivers.slice(flags.start_index, flags.end_index).map( + (r) => {return {"address": r.address, "amount": r.amount, "proof": airdrop.getMerkleProof(r)}})}; + writeFileSync(flags.output, JSON.stringify(result)); + console.log(`Number of addresses in the Merkle tree: ${Object.keys(result.proofs).length}`) + } +} +// @ts-ignore +GenerateProofs.run().catch(require('@oclif/core/handle')) diff --git a/contracts/cw-cyber-airdrop/testdata/generate_test_data/package.json b/contracts/cw-cyber-gift/testdata/generate_test_data/package.json similarity index 62% rename from contracts/cw-cyber-airdrop/testdata/generate_test_data/package.json rename to contracts/cw-cyber-gift/testdata/generate_test_data/package.json index 383482d..7428c25 100644 --- a/contracts/cw-cyber-airdrop/testdata/generate_test_data/package.json +++ b/contracts/cw-cyber-gift/testdata/generate_test_data/package.json @@ -1,5 +1,5 @@ { - "name": "generate_test_data", + "name": "generate-merkle-proofs", "version": "1.0.0", "main": "index.ts", "license": "MIT", @@ -12,7 +12,11 @@ "@types/crypto-js": "^4.0.2", "ethereumjs-util": "^7.1.0", "merkletreejs": "^0.2.23", - "tslib": "^1" + "tslib": "^1", + "@oclif/command": "^1", + "@oclif/config": "^1", + "@oclif/core": "^1", + "@oclif/plugin-help": "^3" }, "devDependencies": { "@oclif/dev-cli": "^1", @@ -26,5 +30,17 @@ }, "engines": { "node": ">=8.0.0" + }, + "files": [ + "/bin", + "/lib", + "/npm-shrinkwrap.json", + "/oclif.manifest.json" + ], + "oclif": { + "bin": "generate-merkle-proofs", + "plugins": [ + "@oclif/plugin-help" + ] } } diff --git a/contracts/cw-cyber-airdrop/testdata/generate_test_data/signed_messages.ipynb b/contracts/cw-cyber-gift/testdata/generate_test_data/signed_messages.ipynb similarity index 100% rename from contracts/cw-cyber-airdrop/testdata/generate_test_data/signed_messages.ipynb rename to contracts/cw-cyber-gift/testdata/generate_test_data/signed_messages.ipynb diff --git a/contracts/cw-cyber-airdrop/testdata/generate_test_data/tsconfig.json b/contracts/cw-cyber-gift/testdata/generate_test_data/tsconfig.json similarity index 100% rename from contracts/cw-cyber-airdrop/testdata/generate_test_data/tsconfig.json rename to contracts/cw-cyber-gift/testdata/generate_test_data/tsconfig.json diff --git a/contracts/cw-cyber-airdrop/testdata/generate_test_data/yarn.lock b/contracts/cw-cyber-gift/testdata/generate_test_data/yarn.lock similarity index 95% rename from contracts/cw-cyber-airdrop/testdata/generate_test_data/yarn.lock rename to contracts/cw-cyber-gift/testdata/generate_test_data/yarn.lock index fff1afe..f54b53f 100644 --- a/contracts/cw-cyber-airdrop/testdata/generate_test_data/yarn.lock +++ b/contracts/cw-cyber-gift/testdata/generate_test_data/yarn.lock @@ -264,7 +264,7 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@oclif/command@^1.8.14", "@oclif/command@^1.8.15": +"@oclif/command@^1", "@oclif/command@^1.8.14", "@oclif/command@^1.8.15": version "1.8.16" resolved "https://registry.yarnpkg.com/@oclif/command/-/command-1.8.16.tgz#bea46f81b2061b47e1cda318a0b923e62ca4cc0c" integrity sha512-rmVKYEsKzurfRU0xJz+iHelbi1LGlihIWZ7Qvmb/CBz1EkhL7nOkW4SVXmG2dA5Ce0si2gr88i6q4eBOMRNJ1w== @@ -288,6 +288,53 @@ is-wsl "^2.1.1" tslib "^2.0.0" +"@oclif/config@^1": + version "1.18.3" + resolved "https://registry.yarnpkg.com/@oclif/config/-/config-1.18.3.tgz#ddfc144fdab66b1658c2f1b3478fa7fbfd317e79" + integrity sha512-sBpko86IrTscc39EvHUhL+c++81BVTsIZ3ETu/vG+cCdi0N6vb2DoahR67A9FI2CGnxRRHjnTfa3m6LulwNATA== + dependencies: + "@oclif/errors" "^1.3.5" + "@oclif/parser" "^3.8.0" + debug "^4.1.1" + globby "^11.0.1" + is-wsl "^2.1.1" + tslib "^2.3.1" + +"@oclif/core@^1": + version "1.6.3" + resolved "https://registry.yarnpkg.com/@oclif/core/-/core-1.6.3.tgz#3d1dd4e033f5512ac35963a73878257142390838" + integrity sha512-a3DrPNlOYemwnzxuJ3tINjqpMVIYe56Mg+XaQo0nGsqGSk69wF5Q/hD8plsWrtwdkeIxwxhgl7T699EJypAUwg== + dependencies: + "@oclif/linewrap" "^1.0.0" + "@oclif/screen" "^3.0.2" + ansi-escapes "^4.3.2" + ansi-styles "^4.3.0" + cardinal "^2.1.1" + chalk "^4.1.2" + clean-stack "^3.0.1" + cli-progress "^3.10.0" + debug "^4.3.3" + ejs "^3.1.6" + fs-extra "^9.1.0" + get-package-type "^0.1.0" + globby "^11.1.0" + hyperlinker "^1.0.0" + indent-string "^4.0.0" + is-wsl "^2.2.0" + js-yaml "^3.14.1" + lodash "^4.17.21" + natural-orderby "^2.0.3" + object-treeify "^1.1.33" + password-prompt "^1.1.2" + semver "^7.3.5" + string-width "^4.2.3" + strip-ansi "^6.0.1" + supports-color "^8.1.1" + supports-hyperlinks "^2.2.0" + tslib "^2.3.1" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + "@oclif/dev-cli@^1": version "1.26.10" resolved "https://registry.yarnpkg.com/@oclif/dev-cli/-/dev-cli-1.26.10.tgz#d8df3a79009b68552f5e7f249d1d19ca52278382" @@ -365,11 +412,33 @@ widest-line "^3.1.0" wrap-ansi "^6.2.0" +"@oclif/plugin-help@^3": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@oclif/plugin-help/-/plugin-help-3.3.1.tgz#36adb4e0173f741df409bb4b69036d24a53bfb24" + integrity sha512-QuSiseNRJygaqAdABYFWn/H1CwIZCp9zp/PLid6yXvy6VcQV7OenEFF5XuYaCvSARe2Tg9r8Jqls5+fw1A9CbQ== + dependencies: + "@oclif/command" "^1.8.15" + "@oclif/config" "1.18.2" + "@oclif/errors" "1.3.5" + "@oclif/help" "^1.0.1" + chalk "^4.1.2" + indent-string "^4.0.0" + lodash "^4.17.21" + string-width "^4.2.0" + strip-ansi "^6.0.0" + widest-line "^3.1.0" + wrap-ansi "^6.2.0" + "@oclif/screen@^1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@oclif/screen/-/screen-1.0.4.tgz#b740f68609dfae8aa71c3a6cab15d816407ba493" integrity sha512-60CHpq+eqnTxLZQ4PGHYNwUX572hgpMHGPtTWMjdTMsAvlm69lZV/4ly6O3sAYkomo4NggGcomrDpBe34rxUqw== +"@oclif/screen@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@oclif/screen/-/screen-3.0.2.tgz#969054308fe98d130c02844a45cc792199b75670" + integrity sha512-S/SF/XYJeevwIgHFmVDAFRUvM3m+OjhvCAYMk78ZJQCYCQ5wS7j+LTt1ZEv2jpEEGg2tx/F6TYYWxddNAYHrFQ== + "@tsconfig/node10@^1.0.7": version "1.0.8" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" @@ -532,7 +601,7 @@ ansi-escapes@^3.1.0, ansi-escapes@^3.2.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== -ansi-escapes@^4.3.0: +ansi-escapes@^4.3.0, ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -561,7 +630,7 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.2.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.2.0, ansi-styles@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== @@ -595,6 +664,16 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== +async@0.9.x: + version "0.9.2" + resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" + integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -770,7 +849,7 @@ chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.1.0, chalk@^4.1.2: +chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -808,7 +887,7 @@ clean-regexp@^1.0.0: dependencies: escape-string-regexp "^1.0.5" -clean-stack@^3.0.0: +clean-stack@^3.0.0, clean-stack@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-3.0.1.tgz#155bf0b2221bf5f4fba89528d24c5953f17fe3a8" integrity sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg== @@ -822,6 +901,13 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" +cli-progress@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.10.0.tgz#63fd9d6343c598c93542fdfa3563a8b59887d78a" + integrity sha512-kLORQrhYCAtUPLZxqsAt2YJGOvRdt34+O6jl5cQGb7iF3dM55FQZlTR+rQyIK9JUcO9bBMwZsTlND+3dmFU2Cw== + dependencies: + string-width "^4.2.0" + cli-progress@^3.4.0: version "3.9.1" resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.9.1.tgz#a22eba6a20f53289fdd05d5ee8cb2cc8c28f866e" @@ -969,6 +1055,13 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: dependencies: ms "2.1.2" +debug@^4.3.3: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -998,6 +1091,13 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +ejs@^3.1.6: + version "3.1.6" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.6.tgz#5bfd0a0689743bb5268b3550cceeebbc1702822a" + integrity sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw== + dependencies: + jake "^10.6.1" + electron-to-chromium@^1.4.17: version "1.4.28" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.28.tgz#fef0e92e281df6d568f482d8d53c34ca5374de48" @@ -1440,6 +1540,17 @@ fast-glob@^3.0.3, fast-glob@^3.1.1: merge2 "^1.3.0" micromatch "^4.0.4" +fast-glob@^3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -1471,6 +1582,13 @@ file-entry-cache@^5.0.1: dependencies: flat-cache "^2.0.1" +filelist@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b" + integrity sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ== + dependencies: + minimatch "^3.0.4" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -1530,6 +1648,16 @@ fs-extra@^8.1: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1550,6 +1678,11 @@ gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -1617,6 +1750,18 @@ globby@^11.0.1: merge2 "^1.3.0" slash "^3.0.0" +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.8" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" @@ -1711,7 +1856,7 @@ ignore@^4.0.2, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.1, ignore@^5.1.4: +ignore@^5.1.1, ignore@^5.1.4, ignore@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== @@ -1864,6 +2009,16 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +jake@^10.6.1: + version "10.8.4" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.4.tgz#f6a8b7bf90c6306f768aa82bb7b98bf4ca15e84a" + integrity sha512-MtWeTkl1qGsWUtbl/Jsca/8xSoK3x0UmS82sNbjqxxG/de/M/3b1DntdjHgPMC50enlTNwXOCRqPXLLt5cCfZA== + dependencies: + async "0.9.x" + chalk "^4.0.2" + filelist "^1.0.1" + minimatch "^3.0.4" + js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" @@ -1874,7 +2029,7 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.13.0, js-yaml@^3.13.1: +js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.14.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -1921,6 +2076,15 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + keccak@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0" @@ -2035,7 +2199,7 @@ md5.js@^1.3.4: inherits "^2.0.1" safe-buffer "^5.1.2" -merge2@^1.2.3, merge2@^1.3.0: +merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -2118,7 +2282,7 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -natural-orderby@^2.0.1: +natural-orderby@^2.0.1, natural-orderby@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/natural-orderby/-/natural-orderby-2.0.3.tgz#8623bc518ba162f8ff1cdb8941d74deb0fdcc016" integrity sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q== @@ -2178,7 +2342,7 @@ number-to-bn@1.7.0: bn.js "4.11.6" strip-hex-prefix "1.0.0" -object-treeify@^1.1.4: +object-treeify@^1.1.33, object-treeify@^1.1.4: version "1.1.33" resolved "https://registry.yarnpkg.com/object-treeify/-/object-treeify-1.1.33.tgz#f06fece986830a3cba78ddd32d4c11d1f76cdf40" integrity sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A== @@ -2696,7 +2860,7 @@ string-width@^3.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -2769,14 +2933,14 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.1.0: +supports-color@^8.1.0, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.1.0: +supports-hyperlinks@^2.1.0, supports-hyperlinks@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== @@ -2879,7 +3043,7 @@ tslib@^1, tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.3: +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== @@ -2937,6 +3101,11 @@ universalify@^0.1.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" diff --git a/contracts/cw-cyber-passport/.cargo/config b/contracts/cw-cyber-passport/.cargo/config new file mode 100644 index 0000000..336b618 --- /dev/null +++ b/contracts/cw-cyber-passport/.cargo/config @@ -0,0 +1,4 @@ +[alias] +wasm = "build --release --target wasm32-unknown-unknown" +unit-test = "test --lib" +schema = "run --example schema" diff --git a/contracts/cw-cyber-passport/Cargo.toml b/contracts/cw-cyber-passport/Cargo.toml new file mode 100644 index 0000000..7bc273c --- /dev/null +++ b/contracts/cw-cyber-passport/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "cw-cyber-passport" +version = "1.0.0" +authors = ["CyberHead"] +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[profile.release] +rpath = false +lto = true +overflow-checks = true +opt-level = 3 +debug = false +debug-assertions = false +codegen-units = 1 +incremental = false +panic = 'abort' + +[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-std = { version = "1.0.0", features = ["staking"] } +cosmwasm-storage = { version = "1.0.0" } +cw-storage-plus = "0.13.4" +cw2 = "0.13.4" +cw721 = { version = "0.13.2" } +schemars = "0.8.8" +serde = { version = "1.0.137", default-features = false, features = ["derive"] } +thiserror = { version = "1.0.31" } +cyber-std = { version = "0.2.1" } +cw-cyber-subgraph = { path = "../cw-cyber-subgraph" } +cw-utils = { version = "0.13.4" } +hex = "0.4" +sha2 = { version = "0.9.5", default-features = false } +sha3 = "0.9" +bech32 = "0.8.1" +cw721-base = { version = "0.13.2", features = ["library"] } +primitive-types = {version = "0.10.1", default-features = false, features = ["byteorder"]} +ripemd160 = "0.9.1" +base64 = "0.13" +semver = "1" + +[dev-dependencies] +cosmwasm-schema = { version = "1.0.0" } + diff --git a/contracts/cw-cyber-passport/README.md b/contracts/cw-cyber-passport/README.md new file mode 100644 index 0000000..840918e --- /dev/null +++ b/contracts/cw-cyber-passport/README.md @@ -0,0 +1 @@ +# cw-cyber-passport diff --git a/contracts/cw-cyber-passport/examples/schema.rs b/contracts/cw-cyber-passport/examples/schema.rs new file mode 100644 index 0000000..e77119f --- /dev/null +++ b/contracts/cw-cyber-passport/examples/schema.rs @@ -0,0 +1,24 @@ +use std::env::current_dir; +use std::fs::create_dir_all; + +use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; + +use cw_cyber_passport::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, ConfigResponse, PortidResponse, AddressResponse, SignatureResponse}; +use cw_cyber_passport::state::{Config, PassportMetadata}; + +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(&schema_for!(ExecuteMsg), &out_dir); + export_schema(&schema_for!(QueryMsg), &out_dir); + export_schema(&schema_for!(Config), &out_dir); + export_schema(&schema_for!(PassportMetadata), &out_dir); + export_schema(&schema_for!(ConfigResponse), &out_dir); + export_schema(&schema_for!(PortidResponse), &out_dir); + export_schema(&schema_for!(AddressResponse), &out_dir); + export_schema(&schema_for!(SignatureResponse), &out_dir); +} diff --git a/contracts/cw-cyber-passport/schema/address_response.json b/contracts/cw-cyber-passport/schema/address_response.json new file mode 100644 index 0000000..48242b6 --- /dev/null +++ b/contracts/cw-cyber-passport/schema/address_response.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AddressResponse", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + } +} diff --git a/contracts/cw-cyber-passport/schema/config.json b/contracts/cw-cyber-passport/schema/config.json new file mode 100644 index 0000000..5160bc7 --- /dev/null +++ b/contracts/cw-cyber-passport/schema/config.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Config", + "type": "object", + "required": [ + "avatar_subgraph", + "name_subgraph", + "owner", + "proof_subgraph" + ], + "properties": { + "avatar_subgraph": { + "$ref": "#/definitions/Addr" + }, + "name_subgraph": { + "$ref": "#/definitions/Addr" + }, + "owner": { + "$ref": "#/definitions/Addr" + }, + "proof_subgraph": { + "$ref": "#/definitions/Addr" + } + }, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + } + } +} diff --git a/contracts/cw-cyber-passport/schema/config_response.json b/contracts/cw-cyber-passport/schema/config_response.json new file mode 100644 index 0000000..5d93675 --- /dev/null +++ b/contracts/cw-cyber-passport/schema/config_response.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigResponse", + "type": "object", + "required": [ + "avatar_subgraph", + "name_subgraph", + "owner", + "proof_subgraph" + ], + "properties": { + "avatar_subgraph": { + "type": "string" + }, + "name_subgraph": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "proof_subgraph": { + "type": "string" + } + } +} diff --git a/contracts/cw-cyber-passport/schema/execute_msg.json b/contracts/cw-cyber-passport/schema/execute_msg.json new file mode 100644 index 0000000..bc2c9ee --- /dev/null +++ b/contracts/cw-cyber-passport/schema/execute_msg.json @@ -0,0 +1,1893 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "execute" + ], + "properties": { + "execute": { + "type": "object", + "required": [ + "msgs" + ], + "properties": { + "msgs": { + "type": "array", + "items": { + "$ref": "#/definitions/CosmosMsg_for_CyberMsgWrapper" + } + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "create_passport" + ], + "properties": { + "create_passport": { + "type": "object", + "required": [ + "avatar", + "nickname", + "signature" + ], + "properties": { + "avatar": { + "type": "string" + }, + "nickname": { + "type": "string" + }, + "signature": { + "$ref": "#/definitions/Binary" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_name" + ], + "properties": { + "update_name": { + "type": "object", + "required": [ + "new_nickname", + "old_nickname" + ], + "properties": { + "new_nickname": { + "type": "string" + }, + "old_nickname": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_avatar" + ], + "properties": { + "update_avatar": { + "type": "object", + "required": [ + "new_avatar", + "nickname" + ], + "properties": { + "new_avatar": { + "type": "string" + }, + "nickname": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_data" + ], + "properties": { + "update_data": { + "type": "object", + "required": [ + "nickname" + ], + "properties": { + "data": { + "type": [ + "string", + "null" + ] + }, + "nickname": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_particle" + ], + "properties": { + "update_particle": { + "type": "object", + "required": [ + "nickname" + ], + "properties": { + "nickname": { + "type": "string" + }, + "particle": { + "type": [ + "string", + "null" + ] + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "proof_address" + ], + "properties": { + "proof_address": { + "type": "object", + "required": [ + "address", + "nickname", + "signature" + ], + "properties": { + "address": { + "type": "string" + }, + "nickname": { + "type": "string" + }, + "signature": { + "$ref": "#/definitions/Binary" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "remove_address" + ], + "properties": { + "remove_address": { + "type": "object", + "required": [ + "address", + "nickname" + ], + "properties": { + "address": { + "type": "string" + }, + "nickname": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "set_owner" + ], + "properties": { + "set_owner": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "owner": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "set_active" + ], + "properties": { + "set_active": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "token_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "set_subgraphs" + ], + "properties": { + "set_subgraphs": { + "type": "object", + "required": [ + "avatar_subgraph", + "name_subgraph", + "proof_subgraph" + ], + "properties": { + "avatar_subgraph": { + "type": "string" + }, + "name_subgraph": { + "type": "string" + }, + "proof_subgraph": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "set_address_label" + ], + "properties": { + "set_address_label": { + "type": "object", + "required": [ + "address", + "nickname" + ], + "properties": { + "address": { + "type": "string" + }, + "label": { + "type": [ + "string", + "null" + ] + }, + "nickname": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Transfer is a base message to move a token to another account without triggering actions", + "type": "object", + "required": [ + "transfer_nft" + ], + "properties": { + "transfer_nft": { + "type": "object", + "required": [ + "recipient", + "token_id" + ], + "properties": { + "recipient": { + "type": "string" + }, + "token_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Send is a base message to transfer a token to a contract and trigger an action on the receiving contract.", + "type": "object", + "required": [ + "send_nft" + ], + "properties": { + "send_nft": { + "type": "object", + "required": [ + "contract", + "msg", + "token_id" + ], + "properties": { + "contract": { + "type": "string" + }, + "msg": { + "$ref": "#/definitions/Binary" + }, + "token_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Mint a new NFT, can only be called by the contract minter", + "type": "object", + "required": [ + "mint" + ], + "properties": { + "mint": { + "$ref": "#/definitions/MintMsg_for_PassportMetadata" + } + }, + "additionalProperties": false + }, + { + "description": "Burn an NFT the sender has access to", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "token_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Allows operator to transfer / send the token from the owner's account. If expiration is set, then this allowance has a time/height limit", + "type": "object", + "required": [ + "approve" + ], + "properties": { + "approve": { + "type": "object", + "required": [ + "spender", + "token_id" + ], + "properties": { + "expires": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "spender": { + "type": "string" + }, + "token_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Remove previously granted Approval", + "type": "object", + "required": [ + "revoke" + ], + "properties": { + "revoke": { + "type": "object", + "required": [ + "spender", + "token_id" + ], + "properties": { + "spender": { + "type": "string" + }, + "token_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Allows operator to transfer / send any token from the owner's account. If expiration is set, then this allowance has a time/height limit", + "type": "object", + "required": [ + "approve_all" + ], + "properties": { + "approve_all": { + "type": "object", + "required": [ + "operator" + ], + "properties": { + "expires": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "operator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Remove previously granted ApproveAll permission", + "type": "object", + "required": [ + "revoke_all" + ], + "properties": { + "revoke_all": { + "type": "object", + "required": [ + "operator" + ], + "properties": { + "operator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ], + "definitions": { + "BankMsg": { + "description": "The message types of the bank module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto", + "oneOf": [ + { + "description": "Sends native tokens from the contract to the given address.\n\nThis is translated to a [MsgSend](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L19-L28). `from_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "send" + ], + "properties": { + "send": { + "type": "object", + "required": [ + "amount", + "to_address" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "to_address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will burn the given coins from the contract's account. There is no Cosmos SDK message that performs this, but it can be done by calling the bank keeper. Important if a contract controls significant token supply that must be retired.", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "CosmosMsg_for_CyberMsgWrapper": { + "oneOf": [ + { + "type": "object", + "required": [ + "bank" + ], + "properties": { + "bank": { + "$ref": "#/definitions/BankMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "custom" + ], + "properties": { + "custom": { + "$ref": "#/definitions/CyberMsgWrapper" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "staking" + ], + "properties": { + "staking": { + "$ref": "#/definitions/StakingMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "distribution" + ], + "properties": { + "distribution": { + "$ref": "#/definitions/DistributionMsg" + } + }, + "additionalProperties": false + }, + { + "description": "A Stargate message encoded the same way as a protobuf [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). This is the same structure as messages in `TxBody` from [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md)", + "type": "object", + "required": [ + "stargate" + ], + "properties": { + "stargate": { + "type": "object", + "required": [ + "type_url", + "value" + ], + "properties": { + "type_url": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/Binary" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "ibc" + ], + "properties": { + "ibc": { + "$ref": "#/definitions/IbcMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "wasm" + ], + "properties": { + "wasm": { + "$ref": "#/definitions/WasmMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "gov" + ], + "properties": { + "gov": { + "$ref": "#/definitions/GovMsg" + } + }, + "additionalProperties": false + } + ] + }, + "CyberMsg": { + "oneOf": [ + { + "type": "object", + "required": [ + "cyberlink" + ], + "properties": { + "cyberlink": { + "type": "object", + "required": [ + "links", + "neuron" + ], + "properties": { + "links": { + "type": "array", + "items": { + "$ref": "#/definitions/Link" + } + }, + "neuron": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "investmint" + ], + "properties": { + "investmint": { + "type": "object", + "required": [ + "amount", + "length", + "neuron", + "resource" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "length": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "neuron": { + "type": "string" + }, + "resource": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "create_energy_route" + ], + "properties": { + "create_energy_route": { + "type": "object", + "required": [ + "destination", + "name", + "source" + ], + "properties": { + "destination": { + "type": "string" + }, + "name": { + "type": "string" + }, + "source": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "edit_energy_route" + ], + "properties": { + "edit_energy_route": { + "type": "object", + "required": [ + "destination", + "source", + "value" + ], + "properties": { + "destination": { + "type": "string" + }, + "source": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/Coin" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "edit_energy_route_name" + ], + "properties": { + "edit_energy_route_name": { + "type": "object", + "required": [ + "destination", + "name", + "source" + ], + "properties": { + "destination": { + "type": "string" + }, + "name": { + "type": "string" + }, + "source": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "delete_energy_route" + ], + "properties": { + "delete_energy_route": { + "type": "object", + "required": [ + "destination", + "source" + ], + "properties": { + "destination": { + "type": "string" + }, + "source": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "create_thought" + ], + "properties": { + "create_thought": { + "type": "object", + "required": [ + "load", + "name", + "particle", + "program", + "trigger" + ], + "properties": { + "load": { + "$ref": "#/definitions/Load" + }, + "name": { + "type": "string" + }, + "particle": { + "type": "string" + }, + "program": { + "type": "string" + }, + "trigger": { + "$ref": "#/definitions/Trigger" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "forget_thought" + ], + "properties": { + "forget_thought": { + "type": "object", + "required": [ + "name", + "program" + ], + "properties": { + "name": { + "type": "string" + }, + "program": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "change_thought_input" + ], + "properties": { + "change_thought_input": { + "type": "object", + "required": [ + "input", + "name", + "program" + ], + "properties": { + "input": { + "type": "string" + }, + "name": { + "type": "string" + }, + "program": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "change_thought_period" + ], + "properties": { + "change_thought_period": { + "type": "object", + "required": [ + "name", + "period", + "program" + ], + "properties": { + "name": { + "type": "string" + }, + "period": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "program": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "change_thought_block" + ], + "properties": { + "change_thought_block": { + "type": "object", + "required": [ + "block", + "name", + "program" + ], + "properties": { + "block": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "name": { + "type": "string" + }, + "program": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "create_pool" + ], + "properties": { + "create_pool": { + "type": "object", + "required": [ + "deposit_coins", + "pool_creator_address", + "pool_type_id" + ], + "properties": { + "deposit_coins": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "pool_creator_address": { + "type": "string" + }, + "pool_type_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "deposit_within_batch" + ], + "properties": { + "deposit_within_batch": { + "type": "object", + "required": [ + "deposit_coins", + "depositor_address", + "pool_id" + ], + "properties": { + "deposit_coins": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "depositor_address": { + "type": "string" + }, + "pool_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "withdraw_within_batch" + ], + "properties": { + "withdraw_within_batch": { + "type": "object", + "required": [ + "pool_coin", + "pool_id", + "withdrawer_address" + ], + "properties": { + "pool_coin": { + "$ref": "#/definitions/Coin" + }, + "pool_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "withdrawer_address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "swap_within_batch" + ], + "properties": { + "swap_within_batch": { + "type": "object", + "required": [ + "demand_coin_denom", + "offer_coin", + "offer_coin_fee", + "order_price", + "pool_id", + "swap_requester_address", + "swap_type_id" + ], + "properties": { + "demand_coin_denom": { + "type": "string" + }, + "offer_coin": { + "$ref": "#/definitions/Coin" + }, + "offer_coin_fee": { + "$ref": "#/definitions/Coin" + }, + "order_price": { + "$ref": "#/definitions/Decimal" + }, + "pool_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "swap_requester_address": { + "type": "string" + }, + "swap_type_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + } + ] + }, + "CyberMsgWrapper": { + "type": "object", + "required": [ + "msg_data", + "route" + ], + "properties": { + "msg_data": { + "$ref": "#/definitions/CyberMsg" + }, + "route": { + "$ref": "#/definitions/CyberRoute" + } + } + }, + "CyberRoute": { + "description": "CyberRoute is enum type to represent cyber query route path", + "type": "string", + "enum": [ + "rank", + "graph", + "resources", + "grid", + "dmn", + "bandwidth", + "liquidity" + ] + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "DistributionMsg": { + "description": "The message types of the distribution module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "set_withdraw_address" + ], + "properties": { + "set_withdraw_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The `withdraw_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "withdraw_delegator_reward" + ], + "properties": { + "withdraw_delegator_reward": { + "type": "object", + "required": [ + "validator" + ], + "properties": { + "validator": { + "description": "The `validator_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "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 + } + ] + }, + "GovMsg": { + "oneOf": [ + { + "description": "This maps directly to [MsgVote](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/gov/v1beta1/tx.proto#L46-L56) in the Cosmos SDK with voter set to the contract address.", + "type": "object", + "required": [ + "vote" + ], + "properties": { + "vote": { + "type": "object", + "required": [ + "proposal_id", + "vote" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "vote": { + "$ref": "#/definitions/VoteOption" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcMsg": { + "description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)", + "oneOf": [ + { + "description": "Sends bank tokens owned by the contract to the given address on another chain. The channel must already be established between the ibctransfer module on this chain and a matching module on the remote chain. We cannot select the port_id, this is whatever the local chain has bound the ibctransfer module to.", + "type": "object", + "required": [ + "transfer" + ], + "properties": { + "transfer": { + "type": "object", + "required": [ + "amount", + "channel_id", + "timeout", + "to_address" + ], + "properties": { + "amount": { + "description": "packet data only supports one coin https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "channel_id": { + "description": "exisiting channel to send the tokens over", + "type": "string" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + }, + "to_address": { + "description": "address on the remote chain to receive these tokens", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sends an IBC packet with given data over the existing channel. Data should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.", + "type": "object", + "required": [ + "send_packet" + ], + "properties": { + "send_packet": { + "type": "object", + "required": [ + "channel_id", + "data", + "timeout" + ], + "properties": { + "channel_id": { + "type": "string" + }, + "data": { + "$ref": "#/definitions/Binary" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will close an existing channel that is owned by this contract. Port is auto-assigned to the contract's IBC port", + "type": "object", + "required": [ + "close_channel" + ], + "properties": { + "close_channel": { + "type": "object", + "required": [ + "channel_id" + ], + "properties": { + "channel_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcTimeout": { + "description": "In IBC each package must set at least one type of timeout: the timestamp or the block height. Using this rather complex enum instead of two timeout fields we ensure that at least one timeout is set.", + "type": "object", + "properties": { + "block": { + "anyOf": [ + { + "$ref": "#/definitions/IbcTimeoutBlock" + }, + { + "type": "null" + } + ] + }, + "timestamp": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + } + }, + "IbcTimeoutBlock": { + "description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)", + "type": "object", + "required": [ + "height", + "revision" + ], + "properties": { + "height": { + "description": "block height after which the packet times out. the height within the given revision", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "revision": { + "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, + "LabeledAddress": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + }, + "label": { + "type": [ + "string", + "null" + ] + } + } + }, + "Link": { + "type": "object", + "required": [ + "from", + "to" + ], + "properties": { + "from": { + "type": "string" + }, + "to": { + "type": "string" + } + } + }, + "Load": { + "type": "object", + "required": [ + "gas_price", + "input" + ], + "properties": { + "gas_price": { + "$ref": "#/definitions/Coin" + }, + "input": { + "type": "string" + } + } + }, + "MintMsg_for_PassportMetadata": { + "type": "object", + "required": [ + "extension", + "owner", + "token_id" + ], + "properties": { + "extension": { + "description": "Any custom extension used by this contract", + "allOf": [ + { + "$ref": "#/definitions/PassportMetadata" + } + ] + }, + "owner": { + "description": "The owner of the newly minter NFT", + "type": "string" + }, + "token_id": { + "description": "Unique ID of the NFT", + "type": "string" + }, + "token_uri": { + "description": "Universal resource identifier for this NFT Should point to a JSON file that conforms to the ERC721 Metadata JSON Schema", + "type": [ + "string", + "null" + ] + } + } + }, + "PassportMetadata": { + "type": "object", + "required": [ + "avatar", + "nickname" + ], + "properties": { + "addresses": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/LabeledAddress" + } + }, + "avatar": { + "type": "string" + }, + "data": { + "type": [ + "string", + "null" + ] + }, + "nickname": { + "type": "string" + }, + "particle": { + "type": [ + "string", + "null" + ] + } + } + }, + "StakingMsg": { + "description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "delegate" + ], + "properties": { + "delegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "undelegate" + ], + "properties": { + "undelegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "redelegate" + ], + "properties": { + "redelegate": { + "type": "object", + "required": [ + "amount", + "dst_validator", + "src_validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "dst_validator": { + "type": "string" + }, + "src_validator": { + "type": "string" + } + } + } + }, + "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" + } + ] + }, + "Trigger": { + "type": "object", + "required": [ + "block", + "period" + ], + "properties": { + "block": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "period": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, + "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" + }, + "VoteOption": { + "type": "string", + "enum": [ + "yes", + "no", + "abstain", + "no_with_veto" + ] + }, + "WasmMsg": { + "description": "The message types of the wasm module.\n\nSee https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto", + "oneOf": [ + { + "description": "Dispatches a call to another contract at a known address (with known ABI).\n\nThis is translated to a [MsgExecuteContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L68-L78). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "execute" + ], + "properties": { + "execute": { + "type": "object", + "required": [ + "contract_addr", + "funds", + "msg" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "msg": { + "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Instantiates a new contracts from previously uploaded Wasm code.\n\nThis is translated to a [MsgInstantiateContract](https://github.com/CosmWasm/wasmd/blob/v0.16.0-alpha1/x/wasm/internal/types/tx.proto#L47-L61). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "instantiate" + ], + "properties": { + "instantiate": { + "type": "object", + "required": [ + "code_id", + "funds", + "label", + "msg" + ], + "properties": { + "admin": { + "type": [ + "string", + "null" + ] + }, + "code_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "label": { + "description": "A human-readbale label for the contract", + "type": "string" + }, + "msg": { + "description": "msg is the JSON-encoded InstantiateMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Migrates a given contracts to use new wasm code. Passes a MigrateMsg to allow us to customize behavior.\n\nOnly the contract admin (as defined in wasmd), if any, is able to make this call.\n\nThis is translated to a [MsgMigrateContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L86-L96). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "migrate" + ], + "properties": { + "migrate": { + "type": "object", + "required": [ + "contract_addr", + "msg", + "new_code_id" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "msg": { + "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + }, + "new_code_id": { + "description": "the code_id of the new logic to place in the given contract", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sets a new admin (for migrate) on the given contract. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "update_admin" + ], + "properties": { + "update_admin": { + "type": "object", + "required": [ + "admin", + "contract_addr" + ], + "properties": { + "admin": { + "type": "string" + }, + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Clears the admin on the given contract, so no more migration possible. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "clear_admin" + ], + "properties": { + "clear_admin": { + "type": "object", + "required": [ + "contract_addr" + ], + "properties": { + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + } + } +} diff --git a/contracts/cw-cyber-passport/schema/instantiate_msg.json b/contracts/cw-cyber-passport/schema/instantiate_msg.json new file mode 100644 index 0000000..0a897dc --- /dev/null +++ b/contracts/cw-cyber-passport/schema/instantiate_msg.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "avatar_subgraph", + "minter", + "name", + "name_subgraph", + "owner", + "proof_subgraph", + "symbol" + ], + "properties": { + "avatar_subgraph": { + "type": "string" + }, + "minter": { + "description": "The minter is the only one who can create new NFTs. This is designed for a base NFT that is controlled by an external program or contract. You will likely replace this with custom logic in custom NFTs", + "type": "string" + }, + "name": { + "description": "Name of the NFT contract", + "type": "string" + }, + "name_subgraph": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "proof_subgraph": { + "type": "string" + }, + "symbol": { + "description": "Symbol of the NFT contract", + "type": "string" + } + } +} diff --git a/contracts/cw-cyber-passport/schema/passport_metadata.json b/contracts/cw-cyber-passport/schema/passport_metadata.json new file mode 100644 index 0000000..93bc138 --- /dev/null +++ b/contracts/cw-cyber-passport/schema/passport_metadata.json @@ -0,0 +1,57 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PassportMetadata", + "type": "object", + "required": [ + "avatar", + "nickname" + ], + "properties": { + "addresses": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/LabeledAddress" + } + }, + "avatar": { + "type": "string" + }, + "data": { + "type": [ + "string", + "null" + ] + }, + "nickname": { + "type": "string" + }, + "particle": { + "type": [ + "string", + "null" + ] + } + }, + "definitions": { + "LabeledAddress": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + }, + "label": { + "type": [ + "string", + "null" + ] + } + } + } + } +} diff --git a/contracts/cw-cyber-passport/schema/portid_response.json b/contracts/cw-cyber-passport/schema/portid_response.json new file mode 100644 index 0000000..c3731af --- /dev/null +++ b/contracts/cw-cyber-passport/schema/portid_response.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PortidResponse", + "type": "object", + "required": [ + "portid" + ], + "properties": { + "portid": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } +} diff --git a/contracts/cw-cyber-passport/schema/query_msg.json b/contracts/cw-cyber-passport/schema/query_msg.json new file mode 100644 index 0000000..46d867a --- /dev/null +++ b/contracts/cw-cyber-passport/schema/query_msg.json @@ -0,0 +1,420 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "type": "object" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "last_portid" + ], + "properties": { + "last_portid": { + "type": "object" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "portid_by_nickname" + ], + "properties": { + "portid_by_nickname": { + "type": "object", + "required": [ + "nickname" + ], + "properties": { + "nickname": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "address_by_nickname" + ], + "properties": { + "address_by_nickname": { + "type": "object", + "required": [ + "nickname" + ], + "properties": { + "nickname": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "passport_by_nickname" + ], + "properties": { + "passport_by_nickname": { + "type": "object", + "required": [ + "nickname" + ], + "properties": { + "nickname": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "metadata_by_nickname" + ], + "properties": { + "metadata_by_nickname": { + "type": "object", + "required": [ + "nickname" + ], + "properties": { + "nickname": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "passport_signed" + ], + "properties": { + "passport_signed": { + "type": "object", + "required": [ + "address", + "nickname" + ], + "properties": { + "address": { + "type": "string" + }, + "nickname": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "active_passport" + ], + "properties": { + "active_passport": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "owner_of" + ], + "properties": { + "owner_of": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "token_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "approval" + ], + "properties": { + "approval": { + "type": "object", + "required": [ + "spender", + "token_id" + ], + "properties": { + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "spender": { + "type": "string" + }, + "token_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "approvals" + ], + "properties": { + "approvals": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "token_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "all_operators" + ], + "properties": { + "all_operators": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "type": "string" + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "num_tokens" + ], + "properties": { + "num_tokens": { + "type": "object" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "contract_info" + ], + "properties": { + "contract_info": { + "type": "object" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "nft_info" + ], + "properties": { + "nft_info": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "token_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "all_nft_info" + ], + "properties": { + "all_nft_info": { + "type": "object", + "required": [ + "token_id" + ], + "properties": { + "include_expired": { + "type": [ + "boolean", + "null" + ] + }, + "token_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "tokens" + ], + "properties": { + "tokens": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "type": "string" + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "all_tokens" + ], + "properties": { + "all_tokens": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "minter" + ], + "properties": { + "minter": { + "type": "object" + } + }, + "additionalProperties": false + } + ] +} diff --git a/contracts/cw-cyber-passport/schema/signature_response.json b/contracts/cw-cyber-passport/schema/signature_response.json new file mode 100644 index 0000000..1d4b5cf --- /dev/null +++ b/contracts/cw-cyber-passport/schema/signature_response.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "SignatureResponse", + "type": "object", + "required": [ + "signed" + ], + "properties": { + "signed": { + "type": "boolean" + } + } +} diff --git a/contracts/cw-cyber-passport/src/contract.rs b/contracts/cw-cyber-passport/src/contract.rs new file mode 100644 index 0000000..39f7f3e --- /dev/null +++ b/contracts/cw-cyber-passport/src/contract.rs @@ -0,0 +1,144 @@ +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{Binary, Deps, DepsMut, Empty, Env, MessageInfo, Reply, StdResult, to_binary}; +use cw2::{get_contract_version, set_contract_version}; +use cyber_std::CyberMsgWrapper; +use semver::Version; + +use crate::error::ContractError; +use crate::execute::{execute_burn, execute_create_passport, execute_mint, execute_proof_address, execute_remove_address, execute_send_nft, execute_set_active, execute_set_owner, execute_set_subgraphs, execute_transfer_nft, execute_update_avatar, execute_update_name, CYBERSPACE_ID_MSG, execute_set_address_label, execute_update_data, execute_update_particle, execute_execute}; +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::query::{query_active_passport, query_address_by_nickname, query_config, query_metadata_by_nickname, query_passport_signed, query_passport_by_nickname, query_last_portid, query_portid_by_nickname}; +use crate::state::{Config, CONFIG, PassportContract, PORTID}; + +type Response = cosmwasm_std::Response; + +const CONTRACT_NAME: &str = "cyber-passport"; +const CONTRACT_VERSION: &str = "1.0.0"; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + mut deps: DepsMut, + env: Env, + info: MessageInfo, + mut msg: InstantiateMsg, +) -> StdResult { + let config = Config { + owner: deps.api.addr_validate(&msg.clone().owner)?, + name_subgraph: deps.api.addr_validate(&msg.clone().name_subgraph)?, + avatar_subgraph: deps.api.addr_validate(&msg.clone().avatar_subgraph)?, + proof_subgraph: deps.api.addr_validate(&msg.clone().proof_subgraph)? + }; + + CONFIG.save(deps.storage, &config)?; + PORTID.save(deps.storage, &0u64)?; + + // override minter to contract itself + msg.minter = env.clone().contract.address.into_string(); + let res = PassportContract::default().instantiate(deps.branch(), env, info, msg.into())?; + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + Ok(res) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::Execute { msgs } => execute_execute(deps, env, info, msgs), + ExecuteMsg::CreatePassport { + nickname, + avatar, + signature + } => execute_create_passport(deps, env, info, nickname, avatar, signature), + ExecuteMsg::UpdateName { old_nickname, new_nickname} => execute_update_name(deps, env, info, old_nickname, new_nickname), + ExecuteMsg::UpdateAvatar { nickname, new_avatar} => execute_update_avatar(deps, env, info, nickname, new_avatar), + ExecuteMsg::UpdateData { nickname, data} => execute_update_data(deps, env, info, nickname, data), + ExecuteMsg::UpdateParticle { nickname, particle} => execute_update_particle(deps, env, info, nickname, particle), + ExecuteMsg::ProofAddress { + nickname, + address, + signature + } => execute_proof_address(deps, env, info, nickname, address, signature), + ExecuteMsg::RemoveAddress {nickname, address } => execute_remove_address(deps, env, info, nickname, address), + ExecuteMsg::SetOwner { owner } => execute_set_owner(deps, env, info, owner), + ExecuteMsg::SetActive { token_id } => execute_set_active(deps, env, info, token_id), + ExecuteMsg::SetSubgraphs { + name_subgraph, + avatar_subgraph, + proof_subgraph + } => execute_set_subgraphs(deps, env, info, name_subgraph, avatar_subgraph, proof_subgraph), + ExecuteMsg::SetAddressLabel { + nickname, + address, + label + } => execute_set_address_label(deps, env, info, nickname, address, label), + // Overwrite CW721 methods + ExecuteMsg::TransferNft { recipient, token_id} => execute_transfer_nft(deps, env, info, recipient, token_id), + ExecuteMsg::SendNft { contract, token_id, msg} => execute_send_nft(deps, env, info, contract, token_id, msg), + ExecuteMsg::Burn { token_id } => execute_burn(deps, env, info, token_id), + ExecuteMsg::Mint(mint_msg) => execute_mint(deps, env, info, mint_msg), + // CW721 methods + _ => PassportContract::default() + .execute(deps, env, info, msg.into()) + .map_err(|err| err.into()), + + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::Config {} => to_binary(&query_config(deps)?), + QueryMsg::LastPortid {} => to_binary(&query_last_portid(deps)?), + QueryMsg::PortidByNickname {nickname} => to_binary(&query_portid_by_nickname(deps, nickname)?), + QueryMsg::AddressByNickname {nickname} => to_binary(&query_address_by_nickname(deps, nickname)?), + QueryMsg::PassportByNickname {nickname} => to_binary(&query_passport_by_nickname(deps, nickname)?), + QueryMsg::MetadataByNickname {nickname} => to_binary(&query_metadata_by_nickname(deps, nickname)?), + QueryMsg::PassportSigned {nickname, address} => to_binary(&query_passport_signed(deps, nickname, address)?), + QueryMsg::ActivePassport { address } => to_binary(&query_active_passport(deps, address)?), + // CW721 methods + _ => PassportContract::default().query(deps, env, msg.into()), + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn reply(_deps: DepsMut, _env: Env, reply: Reply) -> Result { + if reply.id != CYBERSPACE_ID_MSG { + return Err(ContractError::UnknownReplyId { id: reply.id }); + } + Ok(Response::new()) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate( + deps: DepsMut, + _env: Env, + _msg: Empty, +) -> Result { + let stored = get_contract_version(deps.storage)?; + if stored.contract != CONTRACT_NAME { + return Err(ContractError::CannotMigrate { + previous_contract: stored.contract, + }); + } + + let version: Version = CONTRACT_VERSION.parse()?; + let storage_version: Version = get_contract_version(deps.storage)?.version.parse()?; + + if storage_version > version { + return Err(ContractError::CannotMigrateVersion { + previous_version: stored.version, + }); + } + + if storage_version < version { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + } + + Ok(Response::new()) +} diff --git a/contracts/cw-cyber-passport/src/error.rs b/contracts/cw-cyber-passport/src/error.rs new file mode 100644 index 0000000..a9ad746 --- /dev/null +++ b/contracts/cw-cyber-passport/src/error.rs @@ -0,0 +1,120 @@ +#[cfg(feature = "backtraces")] +use std::backtrace::Backtrace; + +use cosmwasm_std::{StdError}; +use cw721_base::ContractError as CW721ContractError; +use thiserror::Error; +use cyber_std::particle::ParticleError; + +#[derive(Error, Debug, PartialEq)] +pub enum ContractError { + #[error("{0}")] + Std(#[from] StdError), + + #[error("Disabled functionality")] + DisabledFunctionality {}, + + #[error("Unauthorized")] + Unauthorized {}, + + #[error("token_id already claimed")] + Claimed {}, + + #[error("Cannot set approval that is already expired")] + Expired {}, + + #[error("Approval not found for: {spender}")] + ApprovalNotFound { spender: String }, + + #[error("Address not found")] + AddressNotFound {}, + + #[error("Cannot migrate from different contract type: {previous_contract}")] + CannotMigrate { previous_contract: String }, + + #[error("Cannot migrate from unsupported version: {previous_version}")] + CannotMigrateVersion { previous_version: String }, + + #[error("Semver parsing error: {0}")] + SemVer(String), + + // ----- + + #[error("Got a submessage reply with unknown id: {id}")] + UnknownReplyId { id: u64 }, + + // ----- + + #[error("Invalid data for the particle")] + InvalidParticleData {}, + + #[error("Invalid particle")] + InvalidParticle {}, + + #[error("Invalid particle version")] + InvalidParticleVersion {}, + + #[error("Invalid initialization")] + InvalidInitialization {}, + + #[error("Wrong token amount for this name")] + WrongAmountForName {}, + + #[error("Name is not valid")] + NotValidName {}, + + #[error("Label is not valid")] + NotValidLabel {}, + + #[error("Data is not valid")] + NotValidData {}, + + #[error("Nickname already exists")] + NicknameAlreadyExists {}, + + #[error("Nickname not found")] + NicknameNotFound {}, + + #[error("Token not found")] + TokenNotFound {}, + + #[error("Cannot add the address to the passport, {msg}")] + ErrorAddAddress { msg: String }, + + #[error("Verification failed")] + VerificationFailed { msg: String }, + + #[error("Data parsing failed")] + ErrorDataParse { }, + + #[error("Key recovery failed")] + ErrorKeyRecovery { }, +} + +impl From for ContractError { + fn from(msg: ParticleError) -> ContractError { + match msg { + ParticleError::InvalidParticleData {} => ContractError::InvalidParticleData {}, + ParticleError::InvalidParticle {} => ContractError::InvalidParticle {}, + ParticleError::InvalidParticleVersion {} => ContractError::InvalidParticleVersion {} + } + } +} + +impl From for ContractError { + fn from(msg: CW721ContractError) -> ContractError { + match msg { + CW721ContractError::Std(e) => ContractError::Std(e), + CW721ContractError::Unauthorized {} => ContractError::Unauthorized {}, + CW721ContractError::Claimed {} => ContractError::Claimed {}, + CW721ContractError::Expired {} => ContractError::Expired {}, + CW721ContractError::ApprovalNotFound {spender} => ContractError::ApprovalNotFound {spender} + } + } +} + +impl From for ContractError { + fn from(err: semver::Error) -> Self { + Self::SemVer(err.to_string()) + } +} diff --git a/contracts/cw-cyber-passport/src/execute.rs b/contracts/cw-cyber-passport/src/execute.rs new file mode 100644 index 0000000..40ee833 --- /dev/null +++ b/contracts/cw-cyber-passport/src/execute.rs @@ -0,0 +1,829 @@ +use std::ops::{Add, Mul}; +use cosmwasm_std::{attr, Binary, CosmosMsg, DepsMut, Env, MessageInfo, Uint128}; +use cw721::{Cw721Execute, Cw721Query}; +use cw721_base::MintMsg; +use cw_utils::must_pay; + +use cyber_std::{CyberMsgWrapper, Link}; +use cyber_std::particle::{check_particle, prepare_particle}; + +use crate::error::ContractError; +use crate::helpers::{proof_address_cosmos, proof_address_ethereum, decode_address, prepare_cyberlink_submsg}; +use crate::state::{ACTIVE, AddressPortID, Extension, LabeledAddress, NICKNAMES, PassportContract, PassportMetadata, PORTID}; +use crate::state::{Config, CONFIG}; + +type Response = cosmwasm_std::Response; + +const CONSTITUTION: &str = "QmcHB9GKHAKCLQhmSj71qNJhENJJg8Gymd1PvvsCQBhG7M"; +pub const CYBERSPACE_ID_MSG: u64 = 420; + +pub fn execute_execute( + deps: DepsMut, + _env: Env, + info: MessageInfo, + msgs: Vec>, +) -> Result { + let mut res = Response::new().add_attribute("action", "execute"); + + let config = CONFIG.load(deps.storage)?; + let owner = config.owner; + if info.sender != owner { + return Err(ContractError::Unauthorized {}); + } + + res = res.add_messages(msgs); + + Ok(res) +} + +pub fn execute_create_passport( + deps: DepsMut, + env: Env, + info: MessageInfo, + nickname: String, + avatar: String, + signature: Binary +) -> Result { + let verified = proof_address_cosmos(deps.as_ref(), info.clone().sender.to_string(), info.clone().sender.to_string(), CONSTITUTION.into(), signature)?; + + if !verified { + return Err(ContractError::VerificationFailed { + msg: "Signature verification failed".to_string(), + }); + } + + if NICKNAMES.has(deps.storage, &nickname.clone()) { + return Err(ContractError::NicknameAlreadyExists {}); + } + + let cw721_contract = PassportContract::default(); + + let nickname_length = nickname.clone().len(); + + if nickname_length > 32 || nickname_length < 3 { + return Err(ContractError::NotValidName {}); + } + + for byte in nickname.as_bytes().iter() { + // - && 0-9 && a-z + if (*byte != 45) && (*byte < 48 || *byte > 57) && (*byte < 97 || *byte > 122) { + return Err(ContractError::NotValidName {}); + } + } + + if nickname_length < 8 { + let must_pay = must_pay(&info, "boot").unwrap_or_default(); + let mul = 10u64.checked_pow(8-nickname_length as u32).unwrap(); + let to_pay = Uint128::new(1_000_000).mul(Uint128::from(mul)); + if must_pay != to_pay { + return Err(ContractError::WrongAmountForName {}); + } + } + + let nickname_particle = prepare_particle(nickname.clone())?; + let avatar_particle = check_particle(avatar.clone())?; + let address_particle = prepare_particle(info.clone().sender.into())?; + + let config = CONFIG.load(deps.storage)?; + + // prepare address <- nickname -> avatar cyberlinks + // nickname -> address cyberlink + let name_subgraph_submsg = prepare_cyberlink_submsg( + config.name_subgraph.into(), + vec![ + Link{ + from: nickname_particle.clone().into(), + to: address_particle.clone().into() + }, + ] + ); + + // nickname -> avatar cyberlink + let avatar_subgraph_submsg = prepare_cyberlink_submsg( + config.avatar_subgraph.into(), + vec![ + Link{ + from: nickname_particle.clone().into(), + to: avatar_particle.clone().into() + }, + ] + ); + + let new_last_portid = PORTID.load(deps.storage).unwrap().add(1); + let mint_msg = MintMsg { + token_id: new_last_portid.to_string(), + owner: info.clone().sender.into(), + token_uri: None, + extension: PassportMetadata { + addresses: None, + avatar: avatar.clone(), + nickname: nickname.clone(), + data: None, + particle: None + }, + }; + + PORTID.save(deps.storage, &new_last_portid)?; + + NICKNAMES.save( + deps.storage, + &nickname, + &AddressPortID{ + address: info.clone().sender, + portid: new_last_portid.to_string() + } + )?; + + // set this passport as active if it's the first one + if !ACTIVE.has(deps.storage, &info.clone().sender) { + ACTIVE.save(deps.storage, &info.clone().sender, &new_last_portid.to_string())?; + } + + // contract itself can only mint + let internal_info = MessageInfo { + sender: env.clone().contract.address, + funds: info.funds, + }; + + let response = cw721_contract.mint(deps, env, internal_info, mint_msg)?; + + Ok(response + .add_submessage(name_subgraph_submsg) + .add_submessage(avatar_subgraph_submsg) + ) +} + +pub fn execute_update_name( + deps: DepsMut, + env: Env, + info: MessageInfo, + old_name: String, + new_name: String +) -> Result { + if NICKNAMES.has(deps.storage, &new_name.clone()) { + return Err(ContractError::NicknameAlreadyExists {}); + } + + if !NICKNAMES.has(deps.storage, &old_name.clone()) { + return Err(ContractError::NicknameNotFound {}); + }; + + let nickname_length = new_name.clone().len(); + + if nickname_length > 32 || nickname_length < 3 { + return Err(ContractError::NotValidName {}); + } + + for byte in new_name.as_bytes().iter() { + // - && 0-9 && a-z + if (*byte != 45) && (*byte < 48 || *byte > 57) && (*byte < 97 || *byte > 122) { + return Err(ContractError::NotValidName {}); + } + } + + if nickname_length < 8 { + let must_pay = must_pay(&info, "boot").unwrap_or_default(); + let mul = 10u64.checked_pow(8-nickname_length as u32).unwrap(); + let to_pay = Uint128::new(1_000_000).mul(Uint128::from(mul)); + if must_pay != to_pay { + return Err(ContractError::WrongAmountForName {}); + } + } + + let cw721_contract = PassportContract::default(); + let address_portid = NICKNAMES.load(deps.storage, &old_name.clone())?; + let nft_owner = cw721_contract.owner_of(deps.as_ref(), env.clone(), address_portid.clone().portid, false)?; + if nft_owner.owner != info.clone().sender { + return Err(ContractError::Unauthorized {}); + } + + cw721_contract + .tokens + .update(deps.storage, &address_portid.clone().portid, |token| match token { + Some(mut token_info) => { + token_info.extension.nickname = new_name.clone(); + Ok(token_info) + } + None => return Err(ContractError::TokenNotFound {}), + })?; + + NICKNAMES.remove(deps.storage, old_name.as_str()); + NICKNAMES.save( + deps.storage, + &new_name.clone(), + &AddressPortID{ + address: info.clone().sender, + portid: address_portid.portid + } + )?; + + let nickname_particle = prepare_particle(new_name.clone())?; + let address_particle = prepare_particle(info.clone().sender.into())?; + + let config = CONFIG.load(deps.storage)?; + + // prepare new nickname -> address cyberlink + let name_subgraph_submsg = prepare_cyberlink_submsg( + config.name_subgraph.into(), + vec![ + Link{ + from: nickname_particle.clone().into(), + to: address_particle.clone().into() + }, + ] + ); + + Ok(Response::new() + .add_submessage(name_subgraph_submsg) + .add_attributes(vec![ + attr("action", "update_nickname"), + attr("old_name", old_name), + attr("new_name", new_name), + ])) +} + +pub fn execute_update_avatar( + deps: DepsMut, + env: Env, + info: MessageInfo, + nickname: String, + new_avatar: String +) -> Result { + if !NICKNAMES.has(deps.storage, &nickname.clone()) { + return Err(ContractError::NicknameNotFound {}); + }; + + let cw721_contract = PassportContract::default(); + let address_portid = NICKNAMES.load(deps.storage, &nickname)?; + let nft_owner = cw721_contract.owner_of(deps.as_ref(), env.clone(), address_portid.clone().portid, false)?; + if nft_owner.owner != info.clone().sender { + return Err(ContractError::Unauthorized {}); + } + + cw721_contract + .tokens + .update(deps.storage, &address_portid.clone().portid, |token| match token { + Some(mut token_info) => { + token_info.extension.avatar = new_avatar.clone(); + Ok(token_info) + } + None => return Err(ContractError::TokenNotFound {}), + })?; + + let avatar_particle = check_particle(new_avatar.clone())?; + let nickname_particle = prepare_particle(nickname.clone())?; + + let config = CONFIG.load(deps.storage)?; + + // prepare nickname -> new avatar cyberlink + let avatar_subgraph_submsg = prepare_cyberlink_submsg( + config.avatar_subgraph.into(), + vec![ + Link{ + from: nickname_particle.clone().into(), + to: avatar_particle.clone().into() + }, + ] + ); + + Ok(Response::new() + .add_submessage(avatar_subgraph_submsg) + .add_attributes(vec![ + attr("action", "update_avatar"), + attr("nickname", nickname), + attr("new_avatar", new_avatar), + ])) +} + +pub fn execute_update_data( + deps: DepsMut, + env: Env, + info: MessageInfo, + nickname: String, + new_data: Option +) -> Result { + if new_data.is_some() { + let data_length = new_data.clone().unwrap().len(); + if data_length > 256 || data_length < 3 { + return Err(ContractError::NotValidData {}); + } + } + + if !NICKNAMES.has(deps.storage, &nickname.clone()) { + return Err(ContractError::NicknameNotFound {}); + }; + + let cw721_contract = PassportContract::default(); + let address_portid = NICKNAMES.load(deps.storage, &nickname)?; + let nft_owner = cw721_contract.owner_of(deps.as_ref(), env.clone(), address_portid.clone().portid, false)?; + if nft_owner.owner != info.clone().sender { + return Err(ContractError::Unauthorized {}); + } + + cw721_contract + .tokens + .update(deps.storage, &address_portid.clone().portid, |token| match token { + Some(mut token_info) => { + token_info.extension.data = new_data.clone(); + Ok(token_info) + } + None => return Err(ContractError::TokenNotFound {}), + })?; + + Ok(Response::new() + .add_attributes(vec![ + attr("action", "update_data"), + attr("nickname", nickname), + ])) +} + +pub fn execute_update_particle( + deps: DepsMut, + env: Env, + info: MessageInfo, + nickname: String, + new_particle: Option +) -> Result { + if new_particle.is_some() { + check_particle(new_particle.clone().unwrap())?; + } + + if !NICKNAMES.has(deps.storage, &nickname.clone()) { + return Err(ContractError::NicknameNotFound {}); + }; + + let cw721_contract = PassportContract::default(); + let address_portid = NICKNAMES.load(deps.storage, &nickname)?; + let nft_owner = cw721_contract.owner_of(deps.as_ref(), env.clone(), address_portid.clone().portid, false)?; + if nft_owner.owner != info.clone().sender { + return Err(ContractError::Unauthorized {}); + } + + cw721_contract + .tokens + .update(deps.storage, &address_portid.clone().portid, |token| match token { + Some(mut token_info) => { + token_info.extension.particle = new_particle.clone(); + Ok(token_info) + } + None => return Err(ContractError::TokenNotFound {}), + })?; + + Ok(Response::new() + .add_attributes(vec![ + attr("action", "update_particle"), + attr("nickname", nickname), + ])) +} + +pub fn execute_proof_address( + deps: DepsMut, + env: Env, + info: MessageInfo, + nickname: String, + mut address: String, + signature: Binary +) -> Result { + address = address.to_lowercase(); + + if !NICKNAMES.has(deps.storage, &nickname.clone()) { + return Err(ContractError::NicknameNotFound {}); + }; + + let cw721_contract = PassportContract::default(); + let address_portid = NICKNAMES.load(deps.storage, &nickname)?; + let nft_owner = cw721_contract.owner_of(deps.as_ref(), env.clone(), address_portid.clone().portid, false)?; + if nft_owner.owner != info.clone().sender { + return Err(ContractError::Unauthorized {}); + } + + // check address type and call needed proof function + let proof_res:bool; + if decode_address(&address).is_err() { + proof_res = proof_address_cosmos(deps.as_ref(), address.clone(), info.sender.to_string(), CONSTITUTION.into(), signature)? + } else { + proof_res = proof_address_ethereum(deps.as_ref(), address.clone(), info.sender.to_string(),CONSTITUTION.into(), signature)? + } + + // save address if not exists or there is enought space for address (<=8) + if proof_res { + let mut token_info = cw721_contract.tokens.load(deps.storage, &address_portid.clone().portid)?; + if token_info.extension.addresses.is_some() { + let mut addresses = token_info.extension.addresses.unwrap(); + if addresses.len() > 7 { + return Err(ContractError::ErrorAddAddress { + msg: "Too many addresses".to_string(), + }); + } + if addresses.iter().position(|x| *x.address == address.clone()).is_some() { + return Err(ContractError::ErrorAddAddress { + msg: "Address already exist".to_string(), + }); + } + addresses.push(LabeledAddress { label: None, address: address.clone() }); + token_info.extension.addresses = Some(addresses); + } else { + token_info.extension.addresses = Some(vec![LabeledAddress { label: None, address: address.clone() }]); + }; + cw721_contract.tokens.save(deps.storage, &address_portid.clone().portid, &token_info)?; + } else { + return Err(ContractError::VerificationFailed { + msg: "Signature verification failed".to_string(), + }); + } + + let proved_address_particle = prepare_particle(address.clone())?; + let nickname_particle = prepare_particle(nickname.clone())?; + + let config = CONFIG.load(deps.storage)?; + + // nickname -> proved_address cyberlink + let proof_subgraph_submsg = prepare_cyberlink_submsg( + config.proof_subgraph.into(), + vec![ + Link{ + from: nickname_particle.clone().into(), + to: proved_address_particle.clone().into(), + }, + ] + ); + + Ok(Response::new() + .add_submessage(proof_subgraph_submsg) + .add_attributes(vec![ + attr("action", "proof_address"), + attr("nickname", nickname), + attr("address", address), + ])) +} + +pub fn execute_remove_address( + deps: DepsMut, + env: Env, + info: MessageInfo, + nickname: String, + mut address: String, +) -> Result { + address = address.to_lowercase(); + + if !NICKNAMES.has(deps.storage, &nickname.clone()) { + return Err(ContractError::NicknameNotFound {}); + }; + + let cw721_contract = PassportContract::default(); + let address_portid = NICKNAMES.load(deps.storage, &nickname.clone())?; + let nft_owner = cw721_contract.owner_of(deps.as_ref(), env, address_portid.clone().portid, false)?; + if nft_owner.owner != info.clone().sender { + return Err(ContractError::Unauthorized {}); + } + + // delete given address from passport metadata + cw721_contract + .tokens + .update(deps.storage, &address_portid.clone().portid, |token| match token { + Some(mut token_info) => { + if token_info.clone().extension.addresses.is_none() { + return Err(ContractError::AddressNotFound {}) + } + let mut addresses = token_info.clone().extension.addresses.unwrap(); + let index = addresses.iter().position(|x| *x.address == address.clone()); + if index.is_none() { + return Err(ContractError::AddressNotFound {}) + } + addresses.remove(index.unwrap()); + if addresses.len() == 0 { + token_info.extension.addresses = None; + } else { + token_info.extension.addresses = Some(addresses); + } + Ok(token_info) + } + None => return Err(ContractError::TokenNotFound {}), + })?; + + Ok(Response::new() + .add_attributes(vec![ + attr("action", "remove_address"), + attr("nickname", nickname), + attr("address", address), + ])) +} + +// NOTE disabled +pub fn execute_mint( + _deps: DepsMut, + _env: Env, + _info: MessageInfo, + _mint_msg: MintMsg, +) -> Result { + Err(ContractError::DisabledFunctionality {}) +} + +pub fn execute_transfer_nft( + deps: DepsMut, + env: Env, + info: MessageInfo, + recipient: String, + token_id: String, +) -> Result { + let config = CONFIG.load(deps.storage)?; + + let cw721_contract = PassportContract::default(); + + let mut nickname = String::default(); + + let new_owner = deps.api.addr_validate(&recipient)?; + + // clear proved addresses and data + cw721_contract + .tokens + .update(deps.storage, &token_id.clone(), |token| match token { + Some(mut token_info) => { + nickname = token_info.clone().extension.nickname; + token_info.extension.addresses = Some(vec![]); + token_info.extension.data = None; + token_info.extension.particle = None; + Ok(token_info) + } + None => return Err(ContractError::TokenNotFound {}), + })?; + + if !NICKNAMES.has(deps.storage, &nickname.clone()) { + return Err(ContractError::NicknameNotFound {}); + }; + + // map nickname to new owner + NICKNAMES.save( + deps.storage, + &nickname.clone(), + &AddressPortID{ + address: new_owner.clone(), + portid: token_id.clone() + } + )?; + + // clear this passport as active + if ACTIVE.has(deps.storage, &info.clone().sender) { + let active = ACTIVE.load(deps.storage, &info.clone().sender)?; + if active == token_id { + ACTIVE.remove(deps.storage, &info.clone().sender); + } + } + + let nickname_particle = prepare_particle(nickname.clone())?; + let address_particle = prepare_particle(new_owner.clone().to_string())?; + + // link passport to new owner + // nickname -> new address cyberlink + let name_subgraph_submsg = prepare_cyberlink_submsg( + config.name_subgraph.into(), + vec![ + Link{ + from: nickname_particle.clone().into(), + to: address_particle.clone().into() + }, + ] + ); + + let response = cw721_contract.transfer_nft(deps, env, info, recipient, token_id)?; + Ok(response.add_submessage(name_subgraph_submsg)) +} + +// NOTE disabled +pub fn execute_send_nft( + _deps: DepsMut, + _env: Env, + _info: MessageInfo, + _contract: String, + _token_id: String, + _msg: Binary +) -> Result { + // let cw721_contract = PassportContract::default(); + // + // let mut nickname = String::default(); + // let mut avatar = String::default(); + // cw721_contract + // .tokens + // .update(deps.storage, &token_id.clone(), |token| match token { + // Some(mut token_info) => { + // nickname = token_info.clone().extension.nickname; + // avatar = token_info.clone().extension.avatar; + // token_info.extension.addresses = Some(vec![]); + // token_info.extension.data = None; + // token_info.extension.particle = None; + // Ok(token_info) + // } + // None => return Err(ContractError::TokenNotFound {}), + // })?; + // + // if !NICKNAMES.has(deps.storage, &nickname.clone()) { + // return Err(ContractError::NicknameNotFound {}); + // }; + // + // // map nickname to new owner (contract in this case) + // NICKNAMES.save( + // deps.storage, + // &nickname.clone(), + // &AddressPortID{ + // address: deps.api.addr_validate(&contract)?, + // portid: token_id.clone() + // } + // )?; + // + // if ACTIVE.has(deps.storage, &info.clone().sender) { + // let active = ACTIVE.load(deps.storage, &info.clone().sender)?; + // if active == token_id { + // ACTIVE.remove(deps.storage, &info.clone().sender); + // } + // } + // + // let response = cw721_contract.send_nft(deps, env, info, contract, token_id, msg)?; + // Ok(response) + Err(ContractError::DisabledFunctionality {}) +} + +pub fn execute_burn( + deps: DepsMut, + env: Env, + info: MessageInfo, + token_id: String, +) -> Result { + let cw721_contract = PassportContract::default(); + + let token_info = cw721_contract.tokens.load(deps.storage, &token_id.clone())?; + + // strict access only for owner without approvals + let address_portid = NICKNAMES.load(deps.storage, &token_info.clone().extension.nickname)?; + let nft_owner = cw721_contract.owner_of(deps.as_ref(), env.clone(), address_portid.clone().portid, false)?; + if nft_owner.owner != info.clone().sender { + return Err(ContractError::Unauthorized {}); + } + + if !NICKNAMES.has(deps.storage, &token_info.clone().extension.nickname) { + return Err(ContractError::NicknameNotFound {}); + }; + NICKNAMES.remove(deps.storage, &token_info.clone().extension.nickname); + + let nickname_particle = prepare_particle(token_info.clone().extension.nickname)?; + let cyberhole_particle = prepare_particle("cyberhole".into())?; + + if ACTIVE.has(deps.storage, &info.clone().sender) { + let active = ACTIVE.load(deps.storage, &info.clone().sender)?; + if active == token_id { + ACTIVE.remove(deps.storage, &info.clone().sender); + } + } + + let config = CONFIG.load(deps.storage)?; + + // nickname -> cyberhole cyberlink + let name_subgraph_submsg = prepare_cyberlink_submsg( + config.name_subgraph.into(), + vec![ + Link{ + from: nickname_particle.clone().into(), + to: cyberhole_particle.clone().into() + }, + ] + ); + + let response = cw721_contract.burn(deps, env, info, token_id)?; + Ok(response.add_submessage(name_subgraph_submsg)) +} + +pub fn execute_set_owner( + deps: DepsMut, + _env: Env, + info: MessageInfo, + new_owner: String, +) -> Result { + let config = CONFIG.load(deps.storage)?; + let owner = config.owner; + if info.sender != owner { + return Err(ContractError::Unauthorized {}); + } + + let owner = deps.api.addr_validate(&new_owner)?; + + CONFIG.update( + deps.storage, + |mut config| -> Result { + config.owner = owner; + Ok(config) + }, + )?; + + Ok(Response::new().add_attributes(vec![ + attr("action", "update_owner"), + attr("address", new_owner.to_string()), + ])) +} + +pub fn execute_set_active( + deps: DepsMut, + env: Env, + info: MessageInfo, + token_id: String, +) -> Result { + let cw721_contract = PassportContract::default(); + let nft_owner = cw721_contract.owner_of(deps.as_ref(), env, token_id.clone(), false)?; + if nft_owner.owner != info.clone().sender { + return Err(ContractError::Unauthorized {}); + } + + ACTIVE.save(deps.storage, &info.clone().sender, &token_id.clone())?; + + Ok(Response::new().add_attributes(vec![ + attr("action", "set_active"), + attr("address", info.sender.to_string()), + attr("token_id", token_id) + ])) +} + +pub fn execute_set_subgraphs( + deps: DepsMut, + _env: Env, + info: MessageInfo, + new_name_subgraph: String, + new_avatar_subgraph: String, + new_proof_subgraph: String, +) -> Result { + let config = CONFIG.load(deps.storage)?; + let owner = config.owner; + if info.sender != owner { + return Err(ContractError::Unauthorized {}); + } + + let name_subgraph = deps.api.addr_validate(&new_name_subgraph)?; + let avatar_subgraph = deps.api.addr_validate(&new_avatar_subgraph)?; + let proof_subgraph = deps.api.addr_validate(&new_proof_subgraph)?; + + CONFIG.update( + deps.storage, + |mut config| -> Result { + config.name_subgraph = name_subgraph.clone(); + config.avatar_subgraph = avatar_subgraph.clone(); + config.proof_subgraph = proof_subgraph.clone(); + Ok(config) + }, + )?; + + Ok(Response::new().add_attributes(vec![ + attr("action", "update_subgraphs"), + attr("name_subgraph", name_subgraph.to_string()), + attr("avatar_subgraph", avatar_subgraph.to_string()), + attr("proof_subgraph", proof_subgraph.to_string()), + ])) +} + +pub fn execute_set_address_label( + deps: DepsMut, + env: Env, + info: MessageInfo, + nickname: String, + address: String, + label: Option, +) -> Result { + + if label.is_some() && label.clone().unwrap().len() > 16 { + return Err(ContractError::NotValidLabel {}); + } + + if !NICKNAMES.has(deps.storage, &nickname.clone()) { + return Err(ContractError::NicknameNotFound {}); + }; + + let cw721_contract = PassportContract::default(); + let address_portid = NICKNAMES.load(deps.storage, &nickname.clone())?; + let nft_owner = cw721_contract.owner_of(deps.as_ref(), env, address_portid.clone().portid, false)?; + if nft_owner.owner != info.clone().sender { + return Err(ContractError::Unauthorized {}); + } + + // find needed address and save label + cw721_contract + .tokens + .update(deps.storage, &address_portid.clone().portid, |token| match token { + Some(mut token_info) => { + if token_info.clone().extension.addresses.is_none() { + return Err(ContractError::AddressNotFound {}) + } + let mut addresses = token_info.clone().extension.addresses.unwrap(); + let index = addresses.iter().position(|x| *x.address == address.clone()); + if index.is_none() { + return Err(ContractError::AddressNotFound {}); + } + addresses[index.unwrap()].label = label.clone(); + token_info.extension.addresses = Some(addresses); + Ok(token_info) + } + None => return Err(ContractError::TokenNotFound {}), + })?; + + Ok(Response::new().add_attributes(vec![ + attr("action", "set_address_label"), + attr("nickname", nickname), + attr("address", address), + attr("label", label.unwrap_or_else(|| "".to_string())), + ])) +} diff --git a/contracts/cw-cyber-passport/src/helpers.rs b/contracts/cw-cyber-passport/src/helpers.rs new file mode 100644 index 0000000..717ed6f --- /dev/null +++ b/contracts/cw-cyber-passport/src/helpers.rs @@ -0,0 +1,249 @@ +use std::convert::TryInto; +use std::ops::Add; + +use bech32::{ToBase32, Variant}; +use cosmwasm_std::{Addr, Binary, Deps, from_binary, ReplyOn, StdError, StdResult, SubMsg, to_binary, WasmMsg}; +use primitive_types::H256; +use ripemd160::Digest as Ripemd160Digest; +use ripemd160::Ripemd160; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use sha2::Sha256; +use sha3::Keccak256; +use cyber_std::{CyberMsgWrapper, Link}; +use cw_cyber_subgraph::msg::{ExecuteMsg as SubgraphExecuteMsg}; +use crate::error::ContractError; +use crate::execute::CYBERSPACE_ID_MSG; + +pub fn proof_address_ethereum( + deps: Deps, + address: String, + passport_owner: String, + message: String, + signature: Binary, +) -> Result { + let mut hasher = Keccak256::new(); + + let msg = passport_owner.add(":").add(&message); + hasher.update(format!("\x19Ethereum Signed Message:\n{}", msg.len())); + hasher.update(msg); + let hash = hasher.finalize(); + let sig = decode_signature(&signature.clone().to_string())?; + + // Decompose signature + let (v, rs) = match sig.split_last() { + Some(pair) => pair, + None => { + return Err(ContractError::VerificationFailed { + msg: "Signature must not be empty".to_string(), + }) + } + }; + let recovery = get_recovery_param(*v)?; + + // Verification + let calculated_pubkey:Vec; + let rcv_key = deps.api.secp256k1_recover_pubkey(&hash, rs, recovery); + if rcv_key.is_ok() { + calculated_pubkey = rcv_key.unwrap() + } else { return Err(ContractError::ErrorKeyRecovery {}) } + + let calculated_address = ethereum_address_raw(&calculated_pubkey)?; + let signer_address = decode_address(address.clone().as_str())?; + if signer_address != calculated_address { + return Err(ContractError::VerificationFailed { + msg: "Signer address is not calculated address".to_string(), + }); + } + deps.api + .secp256k1_verify(&hash, rs, &calculated_pubkey) + .map_err(|err| ContractError::VerificationFailed { + msg: err.to_string(), + }) +} + +fn get_recovery_param(v: u8) -> StdResult { + match v { + // 0 and 1 added to support ledger + 0 => Ok(0), + 1 => Ok(1), + 27 => Ok(0), + 28 => Ok(1), + _ => Err(StdError::generic_err("Values of v other than 0, 1, 27 and 28 not supported")) + } +} + +/// Returns a raw 20 byte Ethereum address +fn ethereum_address_raw(pubkey: &[u8]) -> StdResult<[u8; 20]> { + let (tag, data) = match pubkey.split_first() { + Some(pair) => pair, + None => return Err(StdError::generic_err("Public key must not be empty")), + }; + if *tag != 0x04 { + return Err(StdError::generic_err("Public key must start with 0x04")); + } + if data.len() != 64 { + return Err(StdError::generic_err("Public key must be 65 bytes long")); + } + + let hash = Keccak256::digest(data); + Ok(hash[hash.len() - 20..].try_into().unwrap()) +} + +/// Returns a raw 20 byte Ethereum address from hex +pub fn decode_address(input: &str) -> StdResult<[u8; 20]> { + if input.len() != 42 { + return Err(StdError::generic_err( + "Ethereum address must be 42 characters long", + )); + } + if !input.starts_with("0x") { + return Err(StdError::generic_err("Ethereum address must start wit 0x")); + } + let data = hex::decode(&input[2..]).map_err(|_| StdError::generic_err("hex decoding error"))?; + Ok(data.try_into().unwrap()) +} + +/// Returns a raw 65 byte Ethereum signature from hex +pub fn decode_signature(input: &str) -> StdResult<[u8; 65]> { + if input.len() != 132 { + return Err(StdError::generic_err( + "Ethereum signature must be 132 characters long", + )); + } + if !input.starts_with("0x") { + return Err(StdError::generic_err( + "Ethereum signature must start wit 0x", + )); + } + let data = hex::decode(&input[2..]).map_err(|_| StdError::generic_err("hex decoding error"))?; + Ok(data.try_into().unwrap()) +} + +pub fn proof_address_cosmos( + deps: Deps, + address: String, + passport_owner: String, + message: String, + signature: Binary, +) -> Result { + let prefix:String; + let b32 = bech32::decode(&address.clone()); + if b32.is_ok() { + prefix = b32.unwrap().0 + } else { return Err(ContractError::ErrorDataParse {})} + + let sig: CosmosSignature = from_binary(&signature)?; + + let address_sig = pub_key_to_address(&deps, &sig.pub_key, &prefix)?; + + // ADR-36 signed object, need to construct this object part by part and add encoded signed data with signer + // { + // "account_number":"0", + // "chain_id":"", + // "fee":{"amount":[],"gas":"0"}, + // "memo":"", + // "msgs":[ + // { + // "type":"sign/MsgSignData", + // "value":{ + // "data": base64::encode(message), + // "signer": address + // } + // }], + // "sequence":"0" + // }; + + let msg = passport_owner.add(":").add(&message); + let mut msg_adr36:Vec = vec![123,34,97,99,99,111,117,110,116,95,110,117,109,98,101,114,34,58,34,48,34,44,34,99,104,97,105,110,95,105,100,34,58,34,34,44,34,102,101,101,34,58,123,34,97,109,111,117,110,116,34,58,91,93,44,34,103,97,115,34,58,34,48,34,125,44,34,109,101,109,111,34,58,34,34,44,34,109,115,103,115,34,58,91,123,34,116,121,112,101,34,58,34,115,105,103,110,47,77,115,103,83,105,103,110,68,97,116,97,34,44,34,118,97,108,117,101,34,58,123,34,100,97,116,97,34,58,34]; + msg_adr36.append(&mut base64::encode(msg).as_bytes().to_vec()); + msg_adr36.append(&mut vec![34,44,34,115,105,103,110,101,114,34,58,34]); + msg_adr36.append(&mut address.clone().as_bytes().to_vec()); + msg_adr36.append(&mut vec![34,125,125,93,44,34,115,101,113,117,101,110,99,101,34,58,34,48,34,125]); + + let hash = Sha256::digest(&msg_adr36); + + if address != address_sig.to_string() { + return Err(ContractError::Unauthorized {}) + } + + let result = deps + .api + .secp256k1_verify( + hash.as_ref(), + &sig.signature.as_slice(), + &sig.pub_key.as_slice(), + ) + .map_err(|err| ContractError::VerificationFailed { + msg: err.to_string(), + }); + + return result; +} + +/// Converts user pubkey into Addr with given prefix +fn pub_key_to_address(_deps: &Deps, pub_key: &[u8], prefix: &str) -> StdResult { + let compressed_pub_key = to_compressed_pub_key(pub_key)?; + let mut ripemd160_hasher = Ripemd160::new(); + + ripemd160_hasher.update(Sha256::digest(&compressed_pub_key)); + + let addr_bytes = ripemd160_hasher.finalize().to_vec(); + let addr_str = bech32::encode(prefix, addr_bytes.to_base32(), Variant::Bech32).unwrap(); + + Ok(Addr::unchecked(&addr_str)) +} + +/// Converts uncompressed pub key into compressed one +fn to_compressed_pub_key(pub_key: &[u8]) -> StdResult> { + match pub_key.len() { + // compressed + 33 => Ok(pub_key.to_vec()), + // uncompressed + 65 => { + let y = H256::from_slice(&pub_key[33..]); + let mut pub_key_compressed = pub_key[1..33].to_vec(); + + // Check whether even or odd + if y & H256::from_low_u64_be(1) == H256::zero() { + // 0x02 + pub_key_compressed.insert(0, 2); + } else { + // 0x03 + pub_key_compressed.insert(0, 3); + } + + Ok(pub_key_compressed) + } + _ => Err(StdError::generic_err("PubKeyLengthIsNotValid" )) + } +} + +// { +// pub_key: "A+MXFp7YeLMvoVlAU66Uu0z3Wtc9Cuwq0eocUhtNOmnw", +// signature: "9O89CUdRRZj011BphnTs5JnYM9/0O0ch+XLG2DNiWqtYnA4xA5B0wmFQDOQogOxL5xKWILVMnv1IA/7s05QsIA==" +// }; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct CosmosSignature { + pub_key: Binary, + signature: Binary, +} + +pub fn prepare_cyberlink_submsg( + contract_addr: String, + links: Vec +) -> SubMsg { + return SubMsg { + id: CYBERSPACE_ID_MSG, + msg: WasmMsg::Execute { + contract_addr, + msg: to_binary(&SubgraphExecuteMsg::Cyberlink { + links + }).unwrap(), + funds: vec![], + }.into(), + gas_limit: None, + reply_on: ReplyOn::Error + } +} diff --git a/contracts/cw-cyber-passport/src/lib.rs b/contracts/cw-cyber-passport/src/lib.rs new file mode 100644 index 0000000..2ecaaae --- /dev/null +++ b/contracts/cw-cyber-passport/src/lib.rs @@ -0,0 +1,8 @@ +pub mod contract; +pub mod error; +pub mod execute; +pub mod query; +pub mod state; +pub mod msg; +pub mod helpers; +mod tests; diff --git a/contracts/cw-cyber-passport/src/msg.rs b/contracts/cw-cyber-passport/src/msg.rs new file mode 100644 index 0000000..1ab91bf --- /dev/null +++ b/contracts/cw-cyber-passport/src/msg.rs @@ -0,0 +1,318 @@ +use cosmwasm_std::{Binary, CosmosMsg}; +use cw721::Expiration; +use cw721_base::{ + MintMsg as CW721MintMsg, + msg::{ + ExecuteMsg as CW721ExecuteMsg, InstantiateMsg as CW721InstantiateMsg, + QueryMsg as CW721QueryMsg, + }, +}; +use cyber_std::CyberMsgWrapper; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::state::Extension; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct InstantiateMsg { + /// Name of the NFT contract + pub name: String, + /// Symbol of the NFT contract + pub symbol: String, + + /// The minter is the only one who can create new NFTs. + /// This is designed for a base NFT that is controlled by an external program + /// or contract. You will likely replace this with custom logic in custom NFTs + pub minter: String, + + pub owner: String, + + pub name_subgraph: String, + pub avatar_subgraph: String, + pub proof_subgraph: String, +} + +impl From for CW721InstantiateMsg { + fn from(msg: InstantiateMsg) -> CW721InstantiateMsg { + CW721InstantiateMsg { + name: msg.name, + symbol: msg.symbol, + minter: msg.minter, + } + } +} + +pub type MintMsg = CW721MintMsg; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum ExecuteMsg { + Execute { + msgs: Vec>, + }, + CreatePassport { + nickname: String, + avatar: String, + signature: Binary, + }, + UpdateName{ + old_nickname: String, + new_nickname: String + }, + UpdateAvatar{ + nickname: String, + new_avatar: String + }, + UpdateData{ + nickname: String, + data: Option, + }, + UpdateParticle{ + nickname: String, + particle: Option, + }, + ProofAddress{ + nickname: String, + address: String, + signature: Binary, + }, + RemoveAddress{ + nickname: String, + address: String, + }, + SetOwner { owner: String }, + SetActive { token_id: String }, + SetSubgraphs { + name_subgraph: String, + avatar_subgraph: String, + proof_subgraph: String, + }, + SetAddressLabel { + nickname: String, + address: String, + label: Option, + }, + + // Overwrite Standard CW721 ExecuteMsg + + /// Transfer is a base message to move a token to another account without triggering actions + TransferNft { + recipient: String, + token_id: String, + }, + /// Send is a base message to transfer a token to a contract and trigger an action + /// on the receiving contract. + SendNft { + contract: String, + token_id: String, + msg: Binary, + }, + + /// Mint a new NFT, can only be called by the contract minter + Mint(MintMsg), + + /// Burn an NFT the sender has access to + Burn { token_id: String }, + + // Standard CW721 ExecuteMsg + + /// Allows operator to transfer / send the token from the owner's account. + /// If expiration is set, then this allowance has a time/height limit + Approve { + spender: String, + token_id: String, + expires: Option, + }, + /// Remove previously granted Approval + Revoke { + spender: String, + token_id: String, + }, + /// Allows operator to transfer / send any token from the owner's account. + /// If expiration is set, then this allowance has a time/height limit + ApproveAll { + operator: String, + expires: Option, + }, + /// Remove previously granted ApproveAll permission + RevokeAll { + operator: String, + }, +} + +impl From for CW721ExecuteMsg { + fn from(msg: ExecuteMsg) -> CW721ExecuteMsg { + match msg { + ExecuteMsg::Approve { + spender, + token_id, + expires, + } => CW721ExecuteMsg::Approve { + spender, + token_id, + expires, + }, + ExecuteMsg::Revoke { spender, token_id } => { + CW721ExecuteMsg::Revoke { spender, token_id } + } + ExecuteMsg::ApproveAll { operator, expires } => { + CW721ExecuteMsg::ApproveAll { operator, expires } + } + ExecuteMsg::RevokeAll { operator } => CW721ExecuteMsg::RevokeAll { operator }, + _ => panic!("cannot covert {:?} to CW721ExecuteMsg", msg), + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum QueryMsg { + Config {}, + LastPortid {}, + PortidByNickname { nickname: String }, + AddressByNickname { nickname: String }, + PassportByNickname { nickname: String }, + MetadataByNickname { nickname: String }, + PassportSigned { + nickname: String, + address: String + }, + ActivePassport { address: String }, + + // Standard CW721 queries + OwnerOf { + token_id: String, + include_expired: Option, + }, + Approval { + token_id: String, + spender: String, + include_expired: Option, + }, + Approvals { + token_id: String, + include_expired: Option, + }, + AllOperators { + owner: String, + include_expired: Option, + start_after: Option, + limit: Option, + }, + NumTokens {}, + ContractInfo {}, + NftInfo { + token_id: String, + }, + AllNftInfo { + token_id: String, + include_expired: Option, + }, + Tokens { + owner: String, + start_after: Option, + limit: Option, + }, + AllTokens { + start_after: Option, + limit: Option, + }, + Minter {}, +} + +impl From for CW721QueryMsg { + fn from(msg: QueryMsg) -> CW721QueryMsg { + match msg { + QueryMsg::OwnerOf { + token_id, + include_expired, + } => CW721QueryMsg::OwnerOf { + token_id, + include_expired, + }, + QueryMsg::Approval { + token_id, + spender, + include_expired, + } => CW721QueryMsg::Approval { + token_id, + spender, + include_expired + }, + QueryMsg::Approvals { + token_id, + include_expired, + } => CW721QueryMsg::Approvals { + token_id, + include_expired + }, + QueryMsg::AllOperators { + owner, + include_expired, + start_after, + limit, + } => CW721QueryMsg::AllOperators { + owner, + include_expired, + start_after, + limit, + }, + QueryMsg::NumTokens {} => CW721QueryMsg::NumTokens {}, + QueryMsg::ContractInfo {} => CW721QueryMsg::ContractInfo {}, + QueryMsg::NftInfo { token_id } => CW721QueryMsg::NftInfo { token_id }, + QueryMsg::AllNftInfo { + token_id, + include_expired, + } => CW721QueryMsg::AllNftInfo { + token_id, + include_expired, + }, + QueryMsg::Tokens { + owner, + start_after, + limit, + } => CW721QueryMsg::Tokens { + owner, + start_after, + limit, + }, + QueryMsg::AllTokens { start_after, limit } => { + CW721QueryMsg::AllTokens { start_after, limit } + }, + QueryMsg::Minter {} => { + CW721QueryMsg::Minter {} + } + _ => panic!("cannot covert {:?} to CW721QueryMsg", msg), + } + } +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +#[serde(rename_all = "snake_case")] +pub struct ConfigResponse { + pub owner: String, + pub name_subgraph: String, + pub avatar_subgraph: String, + pub proof_subgraph: String, +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +#[serde(rename_all = "snake_case")] +pub struct PortidResponse { + pub portid: u64, +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +#[serde(rename_all = "snake_case")] +pub struct AddressResponse { + pub address: String, +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +#[serde(rename_all = "snake_case")] +pub struct SignatureResponse { + pub signed: bool, +} + + diff --git a/contracts/cw-cyber-passport/src/query.rs b/contracts/cw-cyber-passport/src/query.rs new file mode 100644 index 0000000..9cbb853 --- /dev/null +++ b/contracts/cw-cyber-passport/src/query.rs @@ -0,0 +1,74 @@ +use cosmwasm_std::{Deps, StdResult}; +use cw721_base::state::TokenInfo; +use crate::msg::{ConfigResponse, PortidResponse, AddressResponse, SignatureResponse}; +use crate::state::{ACTIVE, CONFIG, NICKNAMES, PassportContract, PassportMetadata, PORTID}; + +pub fn query_config(deps: Deps) -> StdResult { + let cfg = CONFIG.load(deps.storage)?; + Ok(ConfigResponse { + owner: cfg.owner.into(), + name_subgraph: cfg.name_subgraph.into(), + avatar_subgraph: cfg.avatar_subgraph.into(), + proof_subgraph: cfg.proof_subgraph.into(), + }) +} + +pub fn query_metadata_by_nickname(deps: Deps, nickname: String) -> StdResult { + let address_portid = NICKNAMES.load(deps.storage, &nickname)?; + let cw721_contract = PassportContract::default(); + let token_info = cw721_contract + .tokens + .load(deps.storage, address_portid.portid.as_str())?; + Ok(token_info.extension) +} + +pub fn query_portid_by_nickname(deps: Deps, nickname: String) -> StdResult { + let address_portid = NICKNAMES.load(deps.storage, &nickname)?; + let portid = address_portid.portid.parse::().unwrap(); + Ok(PortidResponse { portid }) +} + +pub fn query_passport_by_nickname(deps: Deps, nickname: String) -> StdResult> { + let address_portid = NICKNAMES.load(deps.storage, &nickname)?; + let cw721_contract = PassportContract::default(); + let token_info = cw721_contract + .tokens + .load(deps.storage, address_portid.portid.as_str())?; + Ok(token_info) +} + +pub fn query_address_by_nickname(deps: Deps, nickname: String) -> StdResult { + let address_portid = NICKNAMES.load(deps.storage, &nickname)?; + Ok(AddressResponse { address: address_portid.address.into() }) +} + +pub fn query_last_portid(deps: Deps) -> StdResult { + let portid = PORTID.load(deps.storage)?; + Ok(PortidResponse { portid: portid.into() }) +} + +pub fn query_active_passport(deps: Deps, address: String) -> StdResult> { + let active = ACTIVE.load(deps.storage, &deps.api.addr_validate(&address)?)?; + let cw721_contract = PassportContract::default(); + let token_info = cw721_contract + .tokens + .load(deps.storage, &active)?; + Ok(token_info) +} + +pub fn query_passport_signed( + deps: Deps, + nickname: String, + address: String +) -> StdResult { + let address_portid = NICKNAMES.load(deps.storage, &nickname)?; + let cw721_contract = PassportContract::default(); + let token_info = cw721_contract + .tokens + .load(deps.storage, address_portid.portid.as_str())?; + let mut result = false; + if token_info.clone().extension.addresses.is_some() { + result = token_info.clone().extension.addresses.unwrap().iter().any(|i| i.address == address); + } + Ok(SignatureResponse { signed: result }) +} diff --git a/contracts/cw-cyber-passport/src/state.rs b/contracts/cw-cyber-passport/src/state.rs new file mode 100644 index 0000000..e56e8a3 --- /dev/null +++ b/contracts/cw-cyber-passport/src/state.rs @@ -0,0 +1,45 @@ +use cosmwasm_std::Addr; +use cw_storage_plus::{Item, Map}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use cyber_std::CyberMsgWrapper; + +pub type PassportContract<'a> = cw721_base::Cw721Contract<'a, Extension, CyberMsgWrapper>; +pub type Extension = PassportMetadata; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct Config { + pub owner: Addr, + pub name_subgraph: Addr, + pub avatar_subgraph: Addr, + pub proof_subgraph: Addr, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct AddressPortID { + pub address: Addr, + pub portid: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct LabeledAddress { + pub label: Option, + pub address: String, +} + +pub const CONFIG: Item = Item::new("config"); +pub const PORTID: Item = Item::new("portid"); + +pub const ACTIVE: Map<&Addr, String> = Map::new("active"); +pub const NICKNAMES: Map<&str, AddressPortID> = Map::new("nicknames"); + +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug, Default)] +#[serde(rename_all = "snake_case")] +pub struct PassportMetadata { + pub addresses: Option>, + pub avatar: String, + pub nickname: String, + pub data: Option, + pub particle: Option +} diff --git a/contracts/cw-cyber-passport/src/tests.rs b/contracts/cw-cyber-passport/src/tests.rs new file mode 100644 index 0000000..ff37657 --- /dev/null +++ b/contracts/cw-cyber-passport/src/tests.rs @@ -0,0 +1,247 @@ +#[cfg(test)] +mod tests { + use cosmwasm_std::{Api, Binary, coin}; + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cw721_base::state::TokenInfo; + use crate::msg::{ConfigResponse, ExecuteMsg, InstantiateMsg, PortidResponse}; + use crate::state::{LabeledAddress, PassportMetadata}; + use crate::contract::{execute, instantiate}; + use crate::error::ContractError; + use crate::query::{query_active_passport, query_config, query_metadata_by_nickname, query_passport_by_nickname, query_last_portid, query_portid_by_nickname}; + + #[test] + fn proper_flow() { + // NOTE to run tests change CONSTITUTION to QmRX8qYgeZoYM3M5zzQaWEpVFdpin6FvVXvp6RPQK3oufV + + let mut deps = mock_dependencies(); + + let owner = "owner"; + let citizen = "bostrom19nk207agguzdvpj9nqsf4zrjw8mcuu9afun3fv"; + let name_subgraph = "name_subgraph"; + let avatar_subgraph = "avatar_subgraph"; + let proof_subgraph = "proof_subgraph"; + + // instantiate the contract + let instantiate_msg = InstantiateMsg { + name: "MoonPassport".to_string(), + symbol: "MOON".to_string(), + minter: owner.to_string(), + owner: owner.to_string(), + name_subgraph: name_subgraph.to_string(), + avatar_subgraph: avatar_subgraph.to_string(), + proof_subgraph: proof_subgraph.to_string(), + }; + let info = mock_info(&owner, &[]); + instantiate(deps.as_mut(), mock_env(), info, instantiate_msg).unwrap(); + + let expected_config = ConfigResponse { + owner: owner.to_string(), + name_subgraph: name_subgraph.to_string(), + avatar_subgraph: avatar_subgraph.to_string(), + proof_subgraph: proof_subgraph.to_string(), + }; + assert_eq!(query_config(deps.as_ref()).unwrap(), expected_config); + + let expected_portid = PortidResponse { + portid: 0u64 + }; + assert_eq!(query_last_portid(deps.as_ref()).unwrap(), expected_portid); + + let create_passport_msg = ExecuteMsg::CreatePassport { + nickname: "test-nickname".to_string(), + avatar: "QmVPRR3i2oFRjgMKS5dw4QbGNwdXNoYxfcpS3C9pVxHEbb".to_string(), + signature: Binary::from_base64("eyJwdWJfa2V5IjoiQStNWEZwN1llTE12b1ZsQVU2NlV1MHozV3RjOUN1d3EwZW9jVWh0Tk9tbnciLAoic2lnbmF0dXJlIjoicGRWNHhVY1RCT3loMFNFY2dWRnJxYUc4cXBOSHJocktLZGRxdzJ5d3Eyb2NVWGpybDNDdW8rZlRtUjR4bUpucGVIQi90blM4NEF2K0FuUnlRSlJ1S0E9PSJ9").unwrap(), + }; + let info = mock_info(&citizen, &[]); + execute(deps.as_mut(), mock_env(), info, create_passport_msg).unwrap(); + + let expected_portid = PortidResponse { + portid: 1u64 + }; + assert_eq!(query_last_portid(deps.as_ref()).unwrap(), expected_portid); + assert_eq!(query_portid_by_nickname(deps.as_ref(), "test-nickname".to_string()).unwrap(), PortidResponse{ portid: 1 }); + + let expected_passport_metadata = PassportMetadata { + nickname: "test-nickname".to_string(), + avatar: "QmVPRR3i2oFRjgMKS5dw4QbGNwdXNoYxfcpS3C9pVxHEbb".to_string(), + addresses: None, + data: None, + particle: None, + }; + assert_eq!(query_metadata_by_nickname(deps.as_ref(), "test-nickname".into()).unwrap(), expected_passport_metadata); + + let expected_passport = TokenInfo:: { + owner: deps.api.addr_validate(&citizen).unwrap(), + approvals: vec![], + token_uri: None, + extension: PassportMetadata { + nickname: "test-nickname".to_string(), + avatar: "QmVPRR3i2oFRjgMKS5dw4QbGNwdXNoYxfcpS3C9pVxHEbb".to_string(), + addresses: None, + data: None, + particle: None, + } + }; + assert_eq!(query_passport_by_nickname(deps.as_ref(), "test-nickname".into()).unwrap(), expected_passport); + assert_eq!(query_active_passport(deps.as_ref(), citizen.into()).unwrap(), expected_passport); + + // check that is available to change name + + let update_name_msg = ExecuteMsg::UpdateName { + old_nickname: "test-nickname".to_string(), + new_nickname: "test-nickname-new".to_string(), + }; + + let info = mock_info(&citizen, &[]); + execute(deps.as_mut(), mock_env(), info, update_name_msg).unwrap(); + + assert_eq!(query_metadata_by_nickname(deps.as_ref(), "test-nickname".into()).is_err(), true); + let expected_passport_metadata = PassportMetadata { + nickname: "test-nickname-new".to_string(), + avatar: "QmVPRR3i2oFRjgMKS5dw4QbGNwdXNoYxfcpS3C9pVxHEbb".to_string(), + addresses: None, + data: None, + particle: None, + }; + assert_eq!(query_metadata_by_nickname(deps.as_ref(), "test-nickname-new".into()).unwrap(), expected_passport_metadata); + + // check that is available to change avatar + + let update_avatar_msg = ExecuteMsg::UpdateAvatar { + nickname: "test-nickname-new".to_string(), + new_avatar: "QmWfy5AzuaTLh4CtPcymE85KgBR36FNfokMmoGqYJoLALt".to_string(), + }; + + let info = mock_info(&citizen, &[]); + execute(deps.as_mut(), mock_env(), info, update_avatar_msg).unwrap(); + + let expected_passport_metadata = PassportMetadata { + nickname: "test-nickname-new".to_string(), + avatar: "QmWfy5AzuaTLh4CtPcymE85KgBR36FNfokMmoGqYJoLALt".to_string(), + addresses: None, + data: None, + particle: None, + }; + assert_eq!(query_metadata_by_nickname(deps.as_ref(), "test-nickname-new".into()).unwrap(), expected_passport_metadata); + + // check that is available to proof address + + let proof_address_msg = ExecuteMsg::ProofAddress { + nickname: "test-nickname-new".to_string(), + address: "0x0408522089294b8b3f0c9514086e6ae1df00394c".to_string(), + signature: Binary::from_base64("0xa3b7b3adee5805488a62d96ca58ccee80a65a3f74343d1e6f19b0b597afe65da123c020cb968ca141d48b844b098ee33ad5aa827b0da89fb3b89ea272f9a42b01b").unwrap(), + }; + + let info = mock_info(&citizen, &[]); + execute(deps.as_mut(), mock_env(), info, proof_address_msg).unwrap(); + + let expected_passport_metadata = PassportMetadata { + nickname: "test-nickname-new".to_string(), + avatar: "QmWfy5AzuaTLh4CtPcymE85KgBR36FNfokMmoGqYJoLALt".to_string(), + addresses: Option::from(vec![LabeledAddress{ label: None, address: "0x0408522089294b8b3f0c9514086e6ae1df00394c".to_string() }]), + data: None, + particle: None, + }; + assert_eq!(query_metadata_by_nickname(deps.as_ref(), "test-nickname-new".into()).unwrap(), expected_passport_metadata); + + let proof_address_msg = ExecuteMsg::ProofAddress { + nickname: "test-nickname-new".to_string(), + address: "bostrom19nk207agguzdvpj9nqsf4zrjw8mcuu9afun3fv".to_string(), + signature: Binary::from_base64("eyJwdWJfa2V5IjoiQStNWEZwN1llTE12b1ZsQVU2NlV1MHozV3RjOUN1d3EwZW9jVWh0Tk9tbnciLAoic2lnbmF0dXJlIjoicGRWNHhVY1RCT3loMFNFY2dWRnJxYUc4cXBOSHJocktLZGRxdzJ5d3Eyb2NVWGpybDNDdW8rZlRtUjR4bUpucGVIQi90blM4NEF2K0FuUnlRSlJ1S0E9PSJ9").unwrap(), + }; + + let info = mock_info(&citizen, &[]); + execute(deps.as_mut(), mock_env(), info, proof_address_msg).unwrap(); + let expected_passport_metadata = PassportMetadata { + nickname: "test-nickname-new".to_string(), + avatar: "QmWfy5AzuaTLh4CtPcymE85KgBR36FNfokMmoGqYJoLALt".to_string(), + addresses: Option::from(vec![ + LabeledAddress{ label: None, address: "0x0408522089294b8b3f0c9514086e6ae1df00394c".to_string() }, + LabeledAddress{ label: None, address: "bostrom19nk207agguzdvpj9nqsf4zrjw8mcuu9afun3fv".to_string() } + ]), + data: None, + particle: None, + }; + assert_eq!(query_metadata_by_nickname(deps.as_ref(), "test-nickname-new".into()).unwrap(), expected_passport_metadata); + + // check that is not available to proof same address twice + + let proof_address_msg = ExecuteMsg::ProofAddress { + nickname: "test-nickname-new".to_string(), + address: "0x0408522089294b8b3f0c9514086e6ae1df00394c".to_string(), + signature: Binary::from_base64("0xa3b7b3adee5805488a62d96ca58ccee80a65a3f74343d1e6f19b0b597afe65da123c020cb968ca141d48b844b098ee33ad5aa827b0da89fb3b89ea272f9a42b01b").unwrap(), + }; + + let info = mock_info(&citizen, &[]); + let err = execute(deps.as_mut(), mock_env(), info, proof_address_msg).unwrap_err(); + + assert_eq!(err, ContractError::ErrorAddAddress { msg: "Address already exist".to_string() }); + + let proof_address_msg = ExecuteMsg::ProofAddress { + nickname: "test-nickname-new".to_string(), + address: "bostrom19nk207agguzdvpj9nqsf4zrjw8mcuu9afun3fv".to_string(), + signature: Binary::from_base64("eyJwdWJfa2V5IjoiQStNWEZwN1llTE12b1ZsQVU2NlV1MHozV3RjOUN1d3EwZW9jVWh0Tk9tbnciLAoic2lnbmF0dXJlIjoicGRWNHhVY1RCT3loMFNFY2dWRnJxYUc4cXBOSHJocktLZGRxdzJ5d3Eyb2NVWGpybDNDdW8rZlRtUjR4bUpucGVIQi90blM4NEF2K0FuUnlRSlJ1S0E9PSJ9").unwrap(), + }; + + let info = mock_info(&citizen, &[]); + let err = execute(deps.as_mut(), mock_env(), info, proof_address_msg).unwrap_err(); + + assert_eq!(err, ContractError::ErrorAddAddress { msg: "Address already exist".to_string() }); + + // check that is available to delete proved addresses + + let remove_address_msg = ExecuteMsg::RemoveAddress { + nickname: "test-nickname-new".to_string(), + address: "0x0408522089294b8b3f0c9514086e6ae1df00394c".to_string(), + }; + + let info = mock_info(&citizen, &[]); + execute(deps.as_mut(), mock_env(), info, remove_address_msg).unwrap(); + + let expected_passport_metadata = PassportMetadata { + nickname: "test-nickname-new".to_string(), + avatar: "QmWfy5AzuaTLh4CtPcymE85KgBR36FNfokMmoGqYJoLALt".to_string(), + addresses: Option::from(vec![LabeledAddress{ label: None, address: "bostrom19nk207agguzdvpj9nqsf4zrjw8mcuu9afun3fv".to_string() }]), + data: None, + particle: None, + }; + assert_eq!(query_metadata_by_nickname(deps.as_ref(), "test-nickname-new".into()).unwrap(), expected_passport_metadata); + + let remove_address_msg = ExecuteMsg::RemoveAddress { + nickname: "test-nickname-new".to_string(), + address: "bostrom19nk207agguzdvpj9nqsf4zrjw8mcuu9afun3fv".to_string(), + }; + + let info = mock_info(&citizen, &[]); + execute(deps.as_mut(), mock_env(), info, remove_address_msg).unwrap(); + + let expected_passport_metadata = PassportMetadata { + nickname: "test-nickname-new".to_string(), + avatar: "QmWfy5AzuaTLh4CtPcymE85KgBR36FNfokMmoGqYJoLALt".to_string(), + addresses: None, + data: None, + particle: None, + }; + assert_eq!(query_metadata_by_nickname(deps.as_ref(), "test-nickname-new".into()).unwrap(), expected_passport_metadata); + + // check that is possible to buy name + + let update_name_msg = ExecuteMsg::UpdateName { + old_nickname: "test-nickname-new".to_string(), + new_nickname: "name".to_string(), + }; + + let info = mock_info(&citizen, &[coin(10_000_000_000, "boot")]); + execute(deps.as_mut(), mock_env(), info, update_name_msg).unwrap(); + + assert_eq!(query_metadata_by_nickname(deps.as_ref(), "test-nickname".into()).is_err(), true); + let expected_passport_metadata = PassportMetadata { + nickname: "name".to_string(), + avatar: "QmWfy5AzuaTLh4CtPcymE85KgBR36FNfokMmoGqYJoLALt".to_string(), + addresses: None, + data: None, + particle: None, + }; + assert_eq!(query_metadata_by_nickname(deps.as_ref(), "name".into()).unwrap(), expected_passport_metadata); + } +} diff --git a/contracts/cw-cyber-subgraph/Cargo.toml b/contracts/cw-cyber-subgraph/Cargo.toml new file mode 100644 index 0000000..0ecc014 --- /dev/null +++ b/contracts/cw-cyber-subgraph/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "cw-cyber-subgraph" +version = "1.0.0" +authors = ["CyberHead"] +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[profile.release] +rpath = false +lto = true +overflow-checks = true +opt-level = 3 +debug = false +debug-assertions = false +codegen-units = 1 +incremental = false +panic = 'abort' + +[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] +anyhow = "1" +cw2 = "0.13.4" +cosmwasm-std = { version = "1.0.0" } +cyber-std = { version = "0.2.1" } +cw-storage-plus = "0.13.4" +schemars = "0.8.8" +serde = { version = "1.0.137", default-features = false, features = ["derive"] } +thiserror = { version = "1.0.31" } +semver = "1" + +[dev-dependencies] +cosmwasm-schema = { version = "1.0.0" } diff --git a/contracts/cw-cyber-subgraph/README.md b/contracts/cw-cyber-subgraph/README.md new file mode 100644 index 0000000..6896f0e --- /dev/null +++ b/contracts/cw-cyber-subgraph/README.md @@ -0,0 +1 @@ +# cw-cyber-subgraph diff --git a/contracts/cw-cyber-airdrop/examples/schema.rs b/contracts/cw-cyber-subgraph/examples/schema.rs similarity index 63% rename from contracts/cw-cyber-airdrop/examples/schema.rs rename to contracts/cw-cyber-subgraph/examples/schema.rs index 51f4e6e..6b182cd 100644 --- a/contracts/cw-cyber-airdrop/examples/schema.rs +++ b/contracts/cw-cyber-subgraph/examples/schema.rs @@ -2,20 +2,19 @@ use std::env::current_dir; use std::fs::create_dir_all; use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; -use cw_cyber_airdrop::msg::{ - ConfigResponse, ExecuteMsg, InstantiateMsg, IsClaimedResponse, MerkleRootResponse, QueryMsg, +use cw_cyber_subgraph::msg::{ + ExecuteMsg, InstantiateMsg, QueryMsg, }; +use cw_cyber_subgraph::query::ConfigResponse; fn main() { let mut out_dir = current_dir().unwrap(); - out_dir.push("../schema"); + out_dir.push("schema"); create_dir_all(&out_dir).unwrap(); remove_schemas(&out_dir).unwrap(); export_schema(&schema_for!(InstantiateMsg), &out_dir); export_schema(&schema_for!(ExecuteMsg), &out_dir); export_schema(&schema_for!(QueryMsg), &out_dir); - export_schema(&schema_for!(MerkleRootResponse), &out_dir); - export_schema(&schema_for!(IsClaimedResponse), &out_dir); export_schema(&schema_for!(ConfigResponse), &out_dir); } diff --git a/contracts/cw-cyber-subgraph/schema/config_response.json b/contracts/cw-cyber-subgraph/schema/config_response.json new file mode 100644 index 0000000..1f341cb --- /dev/null +++ b/contracts/cw-cyber-subgraph/schema/config_response.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigResponse", + "type": "object", + "required": [ + "admins", + "executors" + ], + "properties": { + "admins": { + "type": "array", + "items": { + "type": "string" + } + }, + "executors": { + "type": "array", + "items": { + "type": "string" + } + } + } +} diff --git a/contracts/cw-cyber-subgraph/schema/execute_msg.json b/contracts/cw-cyber-subgraph/schema/execute_msg.json new file mode 100644 index 0000000..b513dbe --- /dev/null +++ b/contracts/cw-cyber-subgraph/schema/execute_msg.json @@ -0,0 +1,92 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "cyberlink" + ], + "properties": { + "cyberlink": { + "type": "object", + "required": [ + "links" + ], + "properties": { + "links": { + "type": "array", + "items": { + "$ref": "#/definitions/Link" + } + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_admins" + ], + "properties": { + "update_admins": { + "type": "object", + "required": [ + "new_admins" + ], + "properties": { + "new_admins": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_executors" + ], + "properties": { + "update_executors": { + "type": "object", + "required": [ + "new_executors" + ], + "properties": { + "new_executors": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Link": { + "type": "object", + "required": [ + "from", + "to" + ], + "properties": { + "from": { + "type": "string" + }, + "to": { + "type": "string" + } + } + } + } +} diff --git a/contracts/cw-cyber-subgraph/schema/instantiate_msg.json b/contracts/cw-cyber-subgraph/schema/instantiate_msg.json new file mode 100644 index 0000000..cce995a --- /dev/null +++ b/contracts/cw-cyber-subgraph/schema/instantiate_msg.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "admins", + "executers" + ], + "properties": { + "admins": { + "type": "array", + "items": { + "type": "string" + } + }, + "executers": { + "type": "array", + "items": { + "type": "string" + } + } + } +} diff --git a/contracts/cw-cyber-subgraph/schema/query_msg.json b/contracts/cw-cyber-subgraph/schema/query_msg.json new file mode 100644 index 0000000..3df1ec1 --- /dev/null +++ b/contracts/cw-cyber-subgraph/schema/query_msg.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "type": "object" + } + }, + "additionalProperties": false + } + ] +} diff --git a/contracts/cw-cyber-subgraph/src/contract.rs b/contracts/cw-cyber-subgraph/src/contract.rs new file mode 100644 index 0000000..06c3e96 --- /dev/null +++ b/contracts/cw-cyber-subgraph/src/contract.rs @@ -0,0 +1,98 @@ +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, StdResult, MessageInfo, Reply, Api, Addr, Empty}; +use cw2::{get_contract_version, set_contract_version}; + +use crate::error::ContractError; +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::state::{Config, CONFIG}; +use crate::execute::{CYBERLINK_ID_MSG, execute_cyberlink, execute_update_admins, execute_update_executors}; +use crate::query::query_config; + +use cyber_std::CyberMsgWrapper; +use semver::Version; + +type Response = cosmwasm_std::Response; + +const CONTRACT_NAME: &str = "cyber-subgraph"; +const CONTRACT_VERSION: &str = "1.0.0"; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + let config = Config { + admins: map_validate(deps.api, &msg.admins)?, + executors: map_validate(deps.api, &msg.executers)?, + }; + CONFIG.save(deps.storage, &config)?; + + Ok(Response::default()) +} + +pub fn map_validate(api: &dyn Api, admins: &[String]) -> StdResult> { + admins.iter().map(|addr| api.addr_validate(addr)).collect() +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::UpdateAdmins { new_admins } => execute_update_admins(deps, env, info, new_admins), + ExecuteMsg::UpdateExecutors { new_executors } => execute_update_executors(deps, env, info, new_executors), + ExecuteMsg::Cyberlink { links } => execute_cyberlink(deps, env, info, links), + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::Config {} => to_binary(&query_config(deps)?), + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn reply(_deps: DepsMut, _env: Env, reply: Reply) -> Result { + if reply.id != CYBERLINK_ID_MSG { + return Err(ContractError::UnknownReplyId { id: reply.id }); + } + Ok(Response::new()) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate( + deps: DepsMut, + _env: Env, + _msg: Empty, +) -> Result { + let stored = get_contract_version(deps.storage)?; + if stored.contract != CONTRACT_NAME { + return Err(ContractError::CannotMigrate { + previous_contract: stored.contract, + }); + } + + let version: Version = CONTRACT_VERSION.parse()?; + let storage_version: Version = get_contract_version(deps.storage)?.version.parse()?; + + if storage_version > version { + return Err(ContractError::CannotMigrateVersion { + previous_version: stored.version, + }); + } + + if storage_version < version { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + } + + Ok(Response::new()) +} diff --git a/contracts/cw-cyber-subgraph/src/error.rs b/contracts/cw-cyber-subgraph/src/error.rs new file mode 100644 index 0000000..a0f3f5f --- /dev/null +++ b/contracts/cw-cyber-subgraph/src/error.rs @@ -0,0 +1,29 @@ +use cosmwasm_std::StdError; +use thiserror::Error; + +#[derive(Error, Debug, PartialEq)] +pub enum ContractError { + #[error("{0}")] + Std(#[from] StdError), + + #[error("Unauthorized")] + Unauthorized {}, + + #[error("Cannot migrate from different contract type: {previous_contract}")] + CannotMigrate { previous_contract: String }, + + #[error("Cannot migrate from unsupported version: {previous_version}")] + CannotMigrateVersion { previous_version: String }, + + #[error("Got a submessage reply with unknown id: {id}")] + UnknownReplyId { id: u64 }, + + #[error("Semver parsing error: {0}")] + SemVer(String), +} + +impl From for ContractError { + fn from(err: semver::Error) -> Self { + Self::SemVer(err.to_string()) + } +} diff --git a/contracts/cw-cyber-subgraph/src/execute.rs b/contracts/cw-cyber-subgraph/src/execute.rs new file mode 100644 index 0000000..4b40f67 --- /dev/null +++ b/contracts/cw-cyber-subgraph/src/execute.rs @@ -0,0 +1,65 @@ +use cosmwasm_std::{attr, DepsMut, Env, MessageInfo, StdResult, SubMsg}; + +use crate::error::ContractError; +use crate::state::CONFIG; +use cyber_std::{create_cyberlink_msg, Link, CyberMsgWrapper}; +use crate::contract::map_validate; + +type Response = cosmwasm_std::Response; +pub const CYBERLINK_ID_MSG: u64 = 42; + +pub fn execute_update_admins( + deps: DepsMut, + _env: Env, + info: MessageInfo, + new_admins: Vec, +) -> Result { + let cfg = CONFIG.load(deps.storage)?; + if !cfg.can_modify(info.sender.as_ref()) { + return Err(ContractError::Unauthorized {}); + } + + let admins = map_validate(deps.api, &new_admins)?; + CONFIG.update(deps.storage, |mut cfg| -> StdResult<_> { + cfg.admins = admins; + Ok(cfg) + })?; + + Ok(Response::new().add_attributes(vec![attr("action", "update_admins")])) +} + +pub fn execute_update_executors( + deps: DepsMut, + _env: Env, + info: MessageInfo, + new_executors: Vec, +) -> Result { + let cfg = CONFIG.load(deps.storage)?; + if !cfg.can_modify(info.sender.as_ref()) { + return Err(ContractError::Unauthorized {}); + } + + let executors = map_validate(deps.api, &new_executors)?; + CONFIG.update(deps.storage, |mut cfg| -> StdResult<_> { + cfg.executors = executors; + Ok(cfg) + })?; + + Ok(Response::new().add_attributes(vec![attr("action", "update_executors")])) +} + +pub fn execute_cyberlink( + deps: DepsMut, + env: Env, + info: MessageInfo, + cyberlink: Vec, +) -> Result { + let cfg = CONFIG.load(deps.storage)?; + if !cfg.can_execute(info.sender.as_ref()) { + return Err(ContractError::Unauthorized {}); + } + + let msg = create_cyberlink_msg(env.contract.address.to_string(), cyberlink); + Ok(Response::new().add_submessage(SubMsg::reply_on_error(msg, CYBERLINK_ID_MSG))) +} + diff --git a/contracts/cw-cyber-airdrop/src/lib.rs b/contracts/cw-cyber-subgraph/src/lib.rs similarity index 66% rename from contracts/cw-cyber-airdrop/src/lib.rs rename to contracts/cw-cyber-subgraph/src/lib.rs index bac79b1..c41b715 100644 --- a/contracts/cw-cyber-airdrop/src/lib.rs +++ b/contracts/cw-cyber-subgraph/src/lib.rs @@ -1,8 +1,9 @@ -mod error; +pub mod contract; +pub mod error; pub mod execute; -mod helpers; pub mod msg; pub mod state; +pub mod query; mod tests; pub use crate::error::ContractError; diff --git a/contracts/cw-cyber-subgraph/src/msg.rs b/contracts/cw-cyber-subgraph/src/msg.rs new file mode 100644 index 0000000..d3596e6 --- /dev/null +++ b/contracts/cw-cyber-subgraph/src/msg.rs @@ -0,0 +1,29 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use cyber_std::Link; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct InstantiateMsg { + pub admins: Vec, + pub executers: Vec, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum ExecuteMsg { + Cyberlink { + links: Vec + }, + UpdateAdmins { + new_admins: Vec + }, + UpdateExecutors { + new_executors: Vec + }, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum QueryMsg { + Config {}, +} diff --git a/contracts/cw-cyber-subgraph/src/query.rs b/contracts/cw-cyber-subgraph/src/query.rs new file mode 100644 index 0000000..f447b64 --- /dev/null +++ b/contracts/cw-cyber-subgraph/src/query.rs @@ -0,0 +1,18 @@ +use cosmwasm_std::{Deps, StdResult}; +use crate::state::{CONFIG}; +use serde::{Deserialize, Serialize}; +use schemars::JsonSchema; + +pub fn query_config(deps: Deps) -> StdResult { + let cfg = CONFIG.load(deps.storage)?; + Ok(ConfigResponse { + admins: cfg.admins.into_iter().map(|a| a.into()).collect(), + executors: cfg.executors.into_iter().map(|a| a.into()).collect() + }) +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +pub struct ConfigResponse { + pub admins: Vec, + pub executors: Vec, +} diff --git a/contracts/cw-cyber-subgraph/src/state.rs b/contracts/cw-cyber-subgraph/src/state.rs new file mode 100644 index 0000000..1ed5ef1 --- /dev/null +++ b/contracts/cw-cyber-subgraph/src/state.rs @@ -0,0 +1,34 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use cosmwasm_std::Addr; +use cw_storage_plus::Item; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct Config { + pub admins: Vec, + pub executors : Vec +} + +impl Config { + pub fn is_admin(&self, addr: impl AsRef) -> bool { + let addr = addr.as_ref(); + self.admins.iter().any(|a| a.as_ref() == addr) + } + + pub fn is_executor(&self, addr: impl AsRef) -> bool { + let addr = addr.as_ref(); + self.executors.iter().any(|a| a.as_ref() == addr) + } + + pub fn can_modify(&self, addr: &str) -> bool { + self.is_admin(addr) + } + + pub fn can_execute(&self, addr: &str) -> bool { + self.is_executor(addr) + } +} + +pub const CONFIG_KEY: &str = "config"; +pub const CONFIG: Item = Item::new(CONFIG_KEY); diff --git a/contracts/cw-cyber-subgraph/src/tests.rs b/contracts/cw-cyber-subgraph/src/tests.rs new file mode 100644 index 0000000..f5931c7 --- /dev/null +++ b/contracts/cw-cyber-subgraph/src/tests.rs @@ -0,0 +1,12 @@ +#[cfg(test)] +mod tests { + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use crate::execute::*; + use crate::msg::*; + use crate::ContractError; + + #[test] + fn proper_flow() { + assert_eq!(true, true) + } +} diff --git a/img/claim_gift.png b/img/claim_gift.png new file mode 100644 index 0000000..433c7df Binary files /dev/null and b/img/claim_gift.png differ diff --git a/img/contract_initiation_and_functions.png b/img/contract_initiation_and_functions.png new file mode 100644 index 0000000..06fba20 Binary files /dev/null and b/img/contract_initiation_and_functions.png differ diff --git a/img/create_passport.png b/img/create_passport.png new file mode 100644 index 0000000..5562302 Binary files /dev/null and b/img/create_passport.png differ diff --git a/img/gift_execution.png b/img/gift_execution.png new file mode 100644 index 0000000..05b1fd1 Binary files /dev/null and b/img/gift_execution.png differ diff --git a/img/prove_address.png b/img/prove_address.png new file mode 100644 index 0000000..c15b3c4 Binary files /dev/null and b/img/prove_address.png differ diff --git a/img/release_gift.png b/img/release_gift.png new file mode 100644 index 0000000..d4e37e4 Binary files /dev/null and b/img/release_gift.png differ