diff --git a/Cargo.lock b/Cargo.lock index 4af7305..baeb661 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,7 +42,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", "once_cell", "version_check", ] @@ -54,20 +54,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", - "getrandom 0.2.9", + "getrandom 0.2.10", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -167,9 +173,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "8868f09ff8cea88b079da74ae569d9b8c62a23c68c746240b704ee6f7525c89c" [[package]] name = "async-lock" @@ -188,7 +194,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -219,7 +225,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object 0.30.3", + "object 0.30.4", "rustc-demangle", ] @@ -237,9 +243,20 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f1e31e207a6b8fb791a38ea3105e6cb541f55e4d029902d3039a4ad07cc4105" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "basic_contract_caller" +version = "4.2.0" +dependencies = [ + "ink 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_e2e", + "other_contract", + "parity-scale-codec", + "scale-info", +] [[package]] name = "beef" @@ -277,6 +294,17 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + [[package]] name = "blake2" version = "0.10.6" @@ -293,7 +321,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", - "arrayvec 0.7.2", + "arrayvec 0.7.3", "constant_time_eq", ] @@ -338,9 +366,9 @@ dependencies = [ [[package]] name = "bounded-collections" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07fbd1d11282a1eb134d3c3b7cf8ce213b5161c6e5f73fb1b98618482c606b64" +checksum = "eb5b05133427c07c4776906f673ccf36c21b102c9829c641a5b56bd151d44fd6" dependencies = [ "log", "parity-scale-codec", @@ -402,6 +430,20 @@ dependencies = [ "serde", ] +[[package]] +name = "cargo_metadata" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "081e3f0755c1f380c2d010481b6fa2e02973586d5f2b24eebb7a2a1d98b143d8" +dependencies = [ + "camino", + "cargo-platform", + "semver 0.11.0", + "semver-parser", + "serde", + "serde_json", +] + [[package]] name = "cargo_metadata" version = "0.15.4" @@ -410,7 +452,7 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver", + "semver 1.0.17", "serde", "serde_json", "thiserror", @@ -433,21 +475,21 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.24" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ + "android-tzdata", "iana-time-zone", - "num-integer", "num-traits", "winapi", ] [[package]] name = "clap" -version = "4.3.0" +version = "4.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc" +checksum = "ca8f255e4b8027970e78db75e78831229c9815fdbfa67eb1a1b777a62e24b4a0" dependencies = [ "clap_builder", "clap_derive", @@ -456,9 +498,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.0" +version = "4.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990" +checksum = "acd4f3c17c83b0ba34ffbc4f8bbd74f079413f747f84a6f89292f138057e36ab" dependencies = [ "anstream", "anstyle", @@ -469,14 +511,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.0" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -512,11 +554,31 @@ dependencies = [ "winapi", ] +[[package]] +name = "const_format" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c990efc7a285731f9a4378d81aff2f0e85a2c8781a05ef0f8baa8dac54d0ff48" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e026b6ce194a874cb9cf32cd5772d1ef9767cc8fcb5765948d74f37a9d8b2bf6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "constant_time_eq" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" +checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" [[package]] name = "contract-build" @@ -525,18 +587,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb8253488f391d647a46fe67a6f09ad2547ee01a9acf6eba4b75778fde18ba7a" dependencies = [ "anyhow", - "blake2", - "cargo_metadata", + "blake2 0.10.6", + "cargo_metadata 0.15.4", "clap", "colored", "contract-metadata", - "heck", + "heck 0.4.1", "hex", "impl-serde 0.4.0", "parity-scale-codec", "parity-wasm", "rustc_version", - "semver", + "semver 1.0.17", "serde", "serde_json", "tempfile", @@ -557,7 +619,7 @@ checksum = "e6aa9a99669a8f4eba55782175659dbb20459698c5a65a9f3efe7b9330dd667b" dependencies = [ "anyhow", "impl-serde 0.4.0", - "semver", + "semver 1.0.17", "serde", "serde_json", "url", @@ -623,9 +685,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -694,9 +756,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" +checksum = "109308c20e8445959c2792e81871054c6a17e6976489a93d2769641a2ba5839c" dependencies = [ "cc", "cxxbridge-flags", @@ -706,9 +768,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" +checksum = "daf4c6755cdf10798b97510e0e2b3edb9573032bd9379de8fffa59d68165494f" dependencies = [ "cc", "codespan-reporting", @@ -716,24 +778,24 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] name = "cxxbridge-flags" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" +checksum = "882074421238e84fe3b4c65d0081de34e5b323bf64555d3e61991f76eb64a7bb" [[package]] name = "cxxbridge-macro" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" +checksum = "4a076022ece33e7686fb76513518e219cca4fce5750a8ae6d1ce6c0f48fd1af9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -981,7 +1043,7 @@ dependencies = [ name = "flipper" version = "0.1.0" dependencies = [ - "ink", + "ink 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "ink_e2e", "parity-scale-codec", "scale-info", @@ -995,9 +1057,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -1014,6 +1076,16 @@ dependencies = [ "serde", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "funty" version = "2.0.0" @@ -1077,7 +1149,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -1148,9 +1220,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "js-sys", @@ -1227,6 +1299,15 @@ dependencies = [ "ahash 0.8.3", ] +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "heck" version = "0.4.1" @@ -1385,9 +1466,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1414,9 +1495,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1478,12 +1559,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b374fcb3c9b91f8293bdc93d69292e2ec6ea19e97e00725806d79bf563fa5e55" dependencies = [ "derive_more", - "ink_env", - "ink_macro", - "ink_metadata", - "ink_prelude", - "ink_primitives", - "ink_storage", + "ink_env 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_macro 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_metadata 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_prelude 4.2.1", + "ink_primitives 4.2.1", + "ink_storage 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec", +] + +[[package]] +name = "ink" +version = "4.2.0" +source = "git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number#d372cced1ec74143b7aabfaa4ca5ae08bb5f7f2c" +dependencies = [ + "derive_more", + "ink_env 4.2.0 (git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number)", + "ink_macro 4.2.0 (git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number)", + "ink_metadata 4.2.0 (git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number)", + "ink_prelude 4.2.0", + "ink_primitives 4.2.0", + "ink_storage 4.2.0 (git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number)", "parity-scale-codec", ] @@ -1496,20 +1592,28 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "ink_allocator" +version = "4.2.0" +source = "git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number#d372cced1ec74143b7aabfaa4ca5ae08bb5f7f2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "ink_codegen" version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "251e64f2dd74e98329a05812f067f34e2b494a7b1325e538165e481378a72747" dependencies = [ - "blake2", + "blake2 0.10.6", "derive_more", "either", "env_logger", - "heck", + "heck 0.4.1", "impl-serde 0.4.0", - "ink_ir", - "ink_primitives", + "ink_ir 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_primitives 4.2.1", "itertools", "log", "parity-scale-codec", @@ -1517,7 +1621,28 @@ dependencies = [ "quote", "serde", "serde_json", - "syn 2.0.16", + "syn 2.0.18", +] + +[[package]] +name = "ink_codegen" +version = "4.2.0" +source = "git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number#d372cced1ec74143b7aabfaa4ca5ae08bb5f7f2c" +dependencies = [ + "blake2 0.10.6", + "derive_more", + "either", + "heck 0.4.1", + "impl-serde 0.4.0", + "ink_ir 4.2.0 (git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number)", + "ink_primitives 4.2.0", + "itertools", + "parity-scale-codec", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 2.0.18", ] [[package]] @@ -1529,10 +1654,10 @@ dependencies = [ "env_logger", "funty", "impl-serde 0.3.2", - "ink", + "ink 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "ink_e2e_macro", - "ink_env", - "ink_primitives", + "ink_env 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_primitives 4.2.1", "jsonrpsee 0.17.1", "log", "pallet-contracts-primitives", @@ -1553,16 +1678,16 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b10565389b24f8aa5bbd5bab2c38136f7e6f5067876f50a3941a8d78c30c4d8" dependencies = [ - "cargo_metadata", + "cargo_metadata 0.15.4", "contract-build", "derive_more", "env_logger", - "ink_ir", + "ink_ir 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log", "proc-macro2", "quote", "serde_json", - "syn 2.0.16", + "syn 2.0.18", "which", ] @@ -1572,9 +1697,23 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c4af49e092a93e65d1161ce31d23c2e57d724462c96a8944acd647a52787f0" dependencies = [ - "blake2", + "blake2 0.10.6", "derive_more", - "ink_primitives", + "ink_primitives 4.2.1", + "parity-scale-codec", + "secp256k1 0.27.0", + "sha2 0.10.6", + "sha3", +] + +[[package]] +name = "ink_engine" +version = "4.2.0" +source = "git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number#d372cced1ec74143b7aabfaa4ca5ae08bb5f7f2c" +dependencies = [ + "blake2 0.10.6", + "derive_more", + "ink_primitives 4.2.0", "parity-scale-codec", "secp256k1 0.27.0", "sha2 0.10.6", @@ -1588,14 +1727,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d5e1505c5deb7280743e4d7df72a91f52bbf4cb063f464c73f89dce00c70f92" dependencies = [ "arrayref", - "blake2", + "blake2 0.10.6", "cfg-if", "derive_more", - "ink_allocator", - "ink_engine", - "ink_prelude", - "ink_primitives", - "ink_storage_traits", + "ink_allocator 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_engine 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_prelude 4.2.1", + "ink_primitives 4.2.1", + "ink_storage_traits 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits", + "parity-scale-codec", + "paste", + "rlibc", + "scale-decode", + "scale-encode", + "scale-info", + "secp256k1 0.27.0", + "sha2 0.10.6", + "sha3", + "static_assertions", +] + +[[package]] +name = "ink_env" +version = "4.2.0" +source = "git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number#d372cced1ec74143b7aabfaa4ca5ae08bb5f7f2c" +dependencies = [ + "arrayref", + "blake2 0.10.6", + "cfg-if", + "derive_more", + "ink_allocator 4.2.0 (git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number)", + "ink_engine 4.2.0 (git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number)", + "ink_prelude 4.2.0", + "ink_primitives 4.2.0", + "ink_storage_traits 4.2.0 (git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number)", "num-traits", "parity-scale-codec", "paste", @@ -1615,12 +1781,26 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c0e2d96fc6e5b5cb1696b8057e72958315076dbe3f427e8f7722a346344f27a" dependencies = [ - "blake2", + "blake2 0.10.6", + "either", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "ink_ir" +version = "4.2.0" +source = "git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number#d372cced1ec74143b7aabfaa4ca5ae08bb5f7f2c" +dependencies = [ + "blake2 0.10.6", "either", + "ink_prelude 4.2.0", "itertools", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -1629,14 +1809,29 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15abf802e89909c65b6c15d0c655beb3c7ab86309626effb5d9b330d97308114" dependencies = [ - "ink_codegen", - "ink_ir", - "ink_primitives", + "ink_codegen 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_ir 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_primitives 4.2.1", "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.16", - "synstructure", + "syn 2.0.18", + "synstructure 0.13.0", +] + +[[package]] +name = "ink_macro" +version = "4.2.0" +source = "git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number#d372cced1ec74143b7aabfaa4ca5ae08bb5f7f2c" +dependencies = [ + "ink_codegen 4.2.0 (git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number)", + "ink_ir 4.2.0 (git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number)", + "ink_primitives 4.2.0", + "parity-scale-codec", + "proc-macro2", + "quote", + "syn 2.0.18", + "synstructure 0.13.0", ] [[package]] @@ -1647,17 +1842,39 @@ checksum = "4af082b4c2eb246d27b358411ef950811f851c1099aa507ba4bcdd7214d40ccd" dependencies = [ "derive_more", "impl-serde 0.4.0", - "ink_prelude", - "ink_primitives", + "ink_prelude 4.2.1", + "ink_primitives 4.2.1", + "scale-info", + "serde", +] + +[[package]] +name = "ink_metadata" +version = "4.2.0" +source = "git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number#d372cced1ec74143b7aabfaa4ca5ae08bb5f7f2c" +dependencies = [ + "derive_more", + "impl-serde 0.4.0", + "ink_prelude 4.2.0", + "ink_primitives 4.2.0", "scale-info", + "schemars", "serde", ] [[package]] name = "ink_prelude" version = "4.2.0" +source = "git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number#d372cced1ec74143b7aabfaa4ca5ae08bb5f7f2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ink_prelude" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0662ba1d4aa26f0fea36ce6ef9ef1e510e24c900597d703b148c8c23b675b9" +checksum = "6a6f174d742ff929abe66716ad8159f324441b4ff5161a3b0e282f416afbbac1" dependencies = [ "cfg-if", ] @@ -1665,11 +1882,25 @@ dependencies = [ [[package]] name = "ink_primitives" version = "4.2.0" +source = "git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number#d372cced1ec74143b7aabfaa4ca5ae08bb5f7f2c" +dependencies = [ + "derive_more", + "ink_prelude 4.2.0", + "parity-scale-codec", + "scale-decode", + "scale-encode", + "scale-info", + "xxhash-rust", +] + +[[package]] +name = "ink_primitives" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52693c5e74600f5bd4c0f6d447ba9c4e491e4edf685eaf9f9f347a3cb1cde66b" +checksum = "14b4e4772e1b9384233103c1f488df9854d24b3c16168bcf23613b7d98fb363f" dependencies = [ "derive_more", - "ink_prelude", + "ink_prelude 4.2.1", "parity-scale-codec", "scale-decode", "scale-encode", @@ -1686,11 +1917,28 @@ dependencies = [ "array-init", "cfg-if", "derive_more", - "ink_env", - "ink_metadata", - "ink_prelude", - "ink_primitives", - "ink_storage_traits", + "ink_env 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_metadata 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_prelude 4.2.1", + "ink_primitives 4.2.1", + "ink_storage_traits 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "ink_storage" +version = "4.2.0" +source = "git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number#d372cced1ec74143b7aabfaa4ca5ae08bb5f7f2c" +dependencies = [ + "array-init", + "cfg-if", + "derive_more", + "ink_env 4.2.0 (git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number)", + "ink_metadata 4.2.0 (git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number)", + "ink_prelude 4.2.0", + "ink_primitives 4.2.0", + "ink_storage_traits 4.2.0 (git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number)", "parity-scale-codec", "scale-info", ] @@ -1701,9 +1949,21 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec58d70937c1e1490a00b84e2eb9a799f1a5331dd0e5cc68d550de1dbf6a8f4" dependencies = [ - "ink_metadata", - "ink_prelude", - "ink_primitives", + "ink_metadata 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_prelude 4.2.1", + "ink_primitives 4.2.1", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "ink_storage_traits" +version = "4.2.0" +source = "git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number#d372cced1ec74143b7aabfaa4ca5ae08bb5f7f2c" +dependencies = [ + "ink_metadata 4.2.0 (git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number)", + "ink_prelude 4.2.0", + "ink_primitives 4.2.0", "parity-scale-codec", "scale-info", ] @@ -1745,7 +2005,7 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix 0.37.19", + "rustix 0.37.20", "windows-sys 0.48.0", ] @@ -1775,9 +2035,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -1840,7 +2100,7 @@ dependencies = [ "soketto", "thiserror", "tokio", - "tokio-rustls 0.24.0", + "tokio-rustls 0.24.1", "tokio-util", "tracing", ] @@ -1966,9 +2226,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" [[package]] name = "libm" @@ -2047,9 +2307,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -2057,12 +2317,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "mach" @@ -2094,7 +2351,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" dependencies = [ - "rustix 0.37.19", + "rustix 0.37.20", ] [[package]] @@ -2144,14 +2401,24 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "windows-sys 0.48.0", +] + +[[package]] +name = "my_token" +version = "0.1.0" +dependencies = [ + "ink 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_e2e", + "openbrush", + "parity-scale-codec", + "scale-info", ] [[package]] @@ -2177,7 +2444,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.3", "itoa", ] @@ -2222,6 +2489,42 @@ dependencies = [ "libc", ] +[[package]] +name = "obce" +version = "0.1.0" +source = "git+https://github.com/727-Ventures/obce?branch=polkadot-v0.9.37#d452f6eda1bc1ecb36e7e332d61529ad440d5a89" +dependencies = [ + "ink 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_engine 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "obce-macro", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "obce-codegen" +version = "0.1.0" +source = "git+https://github.com/727-Ventures/obce?branch=polkadot-v0.9.37#d452f6eda1bc1ecb36e7e332d61529ad440d5a89" +dependencies = [ + "blake2 0.10.6", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", + "tuple", +] + +[[package]] +name = "obce-macro" +version = "0.1.0" +source = "git+https://github.com/727-Ventures/obce?branch=polkadot-v0.9.37#d452f6eda1bc1ecb36e7e332d61529ad440d5a89" +dependencies = [ + "obce-codegen", + "proc-macro2", + "syn 1.0.109", + "synstructure 0.12.6", +] + [[package]] name = "object" version = "0.29.0" @@ -2236,18 +2539,18 @@ dependencies = [ [[package]] name = "object" -version = "0.30.3" +version = "0.30.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -2261,12 +2564,111 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openbrush" +version = "3.1.1" +source = "git+https://github.com/727-Ventures/openbrush-contracts/?tag=3.1.1#13e82f3f02e8ea65a95ea02c381106ea738e1fb8" +dependencies = [ + "ink 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "openbrush_contracts", + "openbrush_lang", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "openbrush_contracts" +version = "3.1.1" +source = "git+https://github.com/727-Ventures/openbrush-contracts/?tag=3.1.1#13e82f3f02e8ea65a95ea02c381106ea738e1fb8" +dependencies = [ + "ink 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "openbrush_lang", + "pallet-assets-chain-extension", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "openbrush_lang" +version = "3.1.1" +source = "git+https://github.com/727-Ventures/openbrush-contracts/?tag=3.1.1#13e82f3f02e8ea65a95ea02c381106ea738e1fb8" +dependencies = [ + "const_format", + "ink 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "openbrush_lang_macro", + "parity-scale-codec", + "scale-info", + "xxhash-rust", +] + +[[package]] +name = "openbrush_lang_codegen" +version = "3.1.1" +source = "git+https://github.com/727-Ventures/openbrush-contracts/?tag=3.1.1#13e82f3f02e8ea65a95ea02c381106ea738e1fb8" +dependencies = [ + "blake2 0.9.2", + "cargo_metadata 0.13.1", + "fs2", + "heck 0.3.3", + "ink_ir 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_primitives 4.2.1", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 1.0.109", + "synstructure 0.12.6", + "unwrap", +] + +[[package]] +name = "openbrush_lang_macro" +version = "3.1.1" +source = "git+https://github.com/727-Ventures/openbrush-contracts/?tag=3.1.1#13e82f3f02e8ea65a95ea02c381106ea738e1fb8" +dependencies = [ + "openbrush_lang_codegen", + "proc-macro2", + "syn 1.0.109", + "synstructure 0.12.6", +] + [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "oracle_contract" +version = "0.1.0" +dependencies = [ + "ink 4.2.0 (git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number)", + "ink_e2e", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "other_contract" +version = "4.2.0" +dependencies = [ + "ink 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ink_e2e", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "pallet-assets-chain-extension" +version = "0.1.1" +source = "git+https://github.com/727-ventures/pallet-assets-chain-extension?branch=polkadot-v0.9.37#f8ea374186df2a3fc139c8d585719e58d83df582" +dependencies = [ + "ink 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "obce", + "parity-scale-codec", + "scale-info", +] + [[package]] name = "pallet-contracts-primitives" version = "23.0.0" @@ -2287,7 +2689,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.3", "bitvec", "byte-slice-cast", "bytes", @@ -2326,15 +2728,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall", "smallvec", - "windows-sys 0.45.0", + "windows-targets 0.48.0", ] [[package]] @@ -2363,9 +2765,19 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pest" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16833386b02953ca926d19f64af613b9bf742c48dcd5e09b32fbfc9740bf84e2" +dependencies = [ + "thiserror", + "ucd-trie", +] [[package]] name = "pin-project" @@ -2384,7 +2796,7 @@ checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -2454,9 +2866,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.58" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" dependencies = [ "unicode-ident", ] @@ -2472,9 +2884,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -2544,7 +2956,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", ] [[package]] @@ -2556,15 +2968,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - [[package]] name = "redox_syscall" version = "0.3.5" @@ -2591,14 +2994,14 @@ checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] name = "regex" -version = "1.8.2" +version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1a59b5d8e97dee33696bf13c5ba8ab85341c002922fba050069326b9c498974" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" dependencies = [ "aho-corasick", "memchr", @@ -2671,7 +3074,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver", + "semver 1.0.17", ] [[package]] @@ -2690,9 +3093,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.19" +version = "0.37.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" dependencies = [ "bitflags", "errno", @@ -2744,7 +3147,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64 0.21.1", + "base64 0.21.2", ] [[package]] @@ -2854,6 +3257,7 @@ dependencies = [ "derive_more", "parity-scale-codec", "scale-info-derive", + "schemars", "serde", ] @@ -2896,6 +3300,30 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "schemars" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + [[package]] name = "schnellru" version = "0.2.1" @@ -3015,6 +3443,16 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", + "serde", +] + [[package]] name = "semver" version = "1.0.17" @@ -3024,24 +3462,44 @@ dependencies = [ "serde", ] +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "serde" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -3215,7 +3673,7 @@ checksum = "7789372146f8ad40d0b40fad0596cb1db5771187a258eabe19b06f00767fcbd6" dependencies = [ "array-bytes", "bitflags", - "blake2", + "blake2 0.10.6", "bounded-collections", "bs58", "dyn-clonable", @@ -3573,7 +4031,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -3606,12 +4064,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53b9c4ddefcb2d87eb18a6336f65635c29208f766d0deefaa2a1a19f7426a993" dependencies = [ "base58", - "blake2", + "blake2 0.10.6", "derivative", "either", "frame-metadata", "futures", - "getrandom 0.2.9", + "getrandom 0.2.10", "hex", "impl-serde 0.4.0", "jsonrpsee 0.16.2", @@ -3642,7 +4100,7 @@ checksum = "e924f41069e9273236398ff89662d6d336468a5d94faac812129d44547db0e7f" dependencies = [ "darling", "frame-metadata", - "heck", + "heck 0.4.1", "hex", "jsonrpsee 0.16.2", "parity-scale-codec", @@ -3692,15 +4150,27 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.16" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + [[package]] name = "synstructure" version = "0.13.0" @@ -3709,7 +4179,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", "unicode-xid", ] @@ -3727,15 +4197,16 @@ checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" [[package]] name = "tempfile" -version = "3.5.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" dependencies = [ + "autocfg", "cfg-if", "fastrand", - "redox_syscall 0.3.5", - "rustix 0.37.19", - "windows-sys 0.45.0", + "redox_syscall", + "rustix 0.37.20", + "windows-sys 0.48.0", ] [[package]] @@ -3764,7 +4235,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -3813,9 +4284,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.1" +version = "1.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ "autocfg", "bytes", @@ -3836,7 +4307,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -3852,9 +4323,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ "rustls 0.21.1", "tokio", @@ -3946,7 +4417,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -4030,6 +4501,16 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "tuple" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a40ba241047e1174c927dc5f61c141a166b938d61a2ff61838441368cc7d0e" +dependencies = [ + "num-traits", + "serde", +] + [[package]] name = "twox-hash" version = "1.6.3" @@ -4048,6 +4529,12 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + [[package]] name = "uint" version = "0.9.5" @@ -4081,6 +4568,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "unicode-width" version = "0.1.10" @@ -4093,17 +4586,34 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "unnamed" +version = "0.1.0" +dependencies = [ + "ink 4.2.0 (git+https://github.com/ltfschoen/ink?branch=ltfschoen-env-offchain-test-set-block-number)", + "ink_e2e", + "oracle_contract", + "parity-scale-codec", + "scale-info", +] + [[package]] name = "untrusted" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "unwrap" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e33648dd74328e622c7be51f3b40a303c63f93e6fa5f08778b6203a4c25c20f" + [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -4163,9 +4673,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -4173,24 +4683,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4198,22 +4708,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.86" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "wasm-opt" @@ -4432,9 +4942,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -4704,7 +5214,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 861f543..0b9abc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,7 @@ exclude = [] members = [ "dapps/ink-rust/wasm-flipper/contract/flipper", + "dapps/ink-rust/IPSP22/contract/IPSP22", + "dapps/xcm/unnamed", + "dapps/basic_contract_caller", ] diff --git a/README.md b/README.md index eca59df..ae3cf9f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -### Smart Contract in ink! +### Smart Contracts using XCM ## Table of Contents @@ -6,9 +6,14 @@ * [Setup Docker Container](#setup-container) * [Run Cargo Contracts Node in Docker Container](#run-cargo-contracts-node) * Build & Upload - * [**Quickstart** Build & Upload ink! Rust Flipper Smart Contract to Local Testnet (using Cargo Contract)](#quick-build-upload) - * [Build & Upload ink! Rust Flipper Smart Contract to Local Testnet (using Cargo Contract)](#build-upload) - * [Build & Upload ink! Rust Flipper Smart Contract to Local Testnet (using Swanky CLI)](#build-upload-swanky) + * [**Quickstart** Build & Upload "Flipper" ink! Rust Smart Contract to Local Testnet (using Cargo Contract)](#quick-build-upload) + * [**Quickstart** Build & Upload "Basic Contract Caller" ink! Rust Smart Contract to Local Testnet (using Cargo Contract)](#quick-basic-contract-caller) + * [**Quickstart** Build & Upload "IPSP22" ink! Rust Smart Contract to Local Testnet (using Cargo Contract)](#quick-ipsp22) + * [**Quickstart** Build & Upload "Unnamed" ink! Rust Smart Contract to Local Testnet (using Cargo Contract)](#quick-unnamed) + * [Build & Upload Moonbeam VRF Randomness Precompile Solidity Smart Contract to Moonbase Alpha Testnet (using Truffle)](#moonbase-vrf) + * [Build & Upload Chainlink VRFD20 Randomness Solidity Smart Contract to Ethereum Sepolia Testnet (using Truffle)](#vrfd20) + * [Build & Upload "Flipper" ink! Rust Smart Contract to Local Testnet (using Cargo Contract)](#build-upload) + * [Build & Upload "Flipper" ink! Rust Smart Contract to Local Testnet (using Swanky CLI)](#build-upload-swanky) * Interact * [Interact with ink! Python Smart Contract](#interact-python) * [Interact with ink! Rust Flipper Smart Contract using Polkadot.js API](#interact-polkadot-js-flipper) @@ -114,6 +119,7 @@ substrate-contracts-node --version ```bash SCN_PORT=$(docker exec -it ink lsof -ti:30333) && \ docker exec -it ink echo $(kill -9 $SCN_PORT) && \ +docker exec -it ink /app/docker/reset.sh && \ docker exec -it ink /app/docker/quickstart.sh ``` @@ -125,6 +131,7 @@ docker exec -it ink /app/docker/quickstart.sh ``` * Run quickstart ```bash + ./docker/reset.sh ./docker/quickstart.sh ``` @@ -135,6 +142,66 @@ docker exec -it ink /app/docker/quickstart.sh * Redeploys the Flipper contract * Interacts with the Flipper contract +### **Demo Quickstart** Build & Upload ink! Rust "Basic Contract Caller" Smart Contract to Local Testnet (using Cargo Contract) + +#### Run from shell inside Docker container + + * Enter shell of Docker container + ```bash + docker exec -it ink /bin/bash + ``` + * Run in terminal tab 1 + ```bash + ./docker/reset.sh + ``` + * Run in terminal tab 2 + ``` + ./docker/quickstart-basic-contract-caller.sh + ``` + +### **Demo Quickstart** Build & Upload ink! Rust "IPSP22" Smart Contract to Local Testnet (using Cargo Contract) + +#### Run from shell inside Docker container + + * Enter shell of Docker container + ```bash + docker exec -it ink /bin/bash + ``` + * Run in terminal tab 1 + ```bash + ./docker/reset.sh + ``` + * Run in terminal tab 2 + ```bash + cd /app + ./docker/quickstart-ipsp22.sh + ``` + +### **Demo Quickstart** Build & Upload ink! Rust "Unnamed" Smart Contract to Local Testnet (using Cargo Contract) + +#### Run from shell inside Docker container + + * Enter shell of Docker container + ```bash + docker exec -it ink /bin/bash + ``` + * Run in terminal tab 1 + ```bash + ./docker/reset.sh + ``` + * Run in terminal tab 2 + ```` + ./docker/quickstart-unnamed.sh + ``` + +### Build & Upload Moonbeam VRF Randomness Precompile Solidity Smart Contract to Moonbase Alpha Testnet (using Truffle) + +* Follow the instructions in the [VRF example README](./dapps/evm2/randomness/README.md) + +### Build & Upload Chainlink VRFD20 Randomness Solidity Smart Contract to Ethereum Sepolia Testnet (using Truffle) + +* Follow the instructions in the [VRF20 example README](./dapps/evm2/randomness/README.md) + ### Build & Upload ink! Rust Flipper Smart Contract to Local Testnet (using Cargo Contract) * Create Rust project with template @@ -249,12 +316,12 @@ Note: Try `rustup update` if you face error Local Testnet ```bash -swanky contract deploy flipper --account alice -g 100000000000 -a true +swanky contract deploy flipper --account alice -g 1000000000000 -a true ``` Shibuya Testnet ```bash -swanky contract deploy flipper --account alice --gas 100000000000 --args true --network shibuya +swanky contract deploy flipper --account alice --gas 1000000000000 --args true --network shibuya ``` Copy paste the contract address. @@ -359,8 +426,8 @@ cargo contract call \ * Note: If you don't build in "debug" mode with `cargo contract build ...` instead of `cargo contract build --release ...` and you run it using **dry run** by running extra options like the following, or if you execute as a transaction, then you won't be able to see node terminal debug logs like `tokio-runtime-worker runtime::contracts Execution finished with debug buffer...` from your use of `ink::env::debug_println!` in the smart contract ```bash --skip-dry-run \ - --gas 100000000000 \ - --proof-size 100000000000 + --gas 1000000000000 \ + --proof-size 1000000000000 ``` ### Tips Docker Commands @@ -424,6 +491,9 @@ docker buildx rm --all-inactive * Contracts pallet allows deployment and execution of WebAssembly-based smart contracts * What trait do smart contract accounts used in the Contracts pallet of Substrate extend? * `Currency` trait + * How to resolve `ERROR: This contract has already been uploaded with code hash` + * It may be because you ran a Substrate contract node on your host machine and then tried running another one in your Docker container. So it may be necessary to run `kill -9 $(lsof -ti:30333)` on + both the host machine and inside the Docker container. Or just restart Docker. * Link diff --git a/dapps/basic_contract_caller/.gitignore b/dapps/basic_contract_caller/.gitignore new file mode 100755 index 0000000..8de8f87 --- /dev/null +++ b/dapps/basic_contract_caller/.gitignore @@ -0,0 +1,9 @@ +# Ignore build artifacts from the local tests sub-crate. +/target/ + +# Ignore backup files creates by cargo fmt. +**/*.rs.bk + +# Remove Cargo.lock when creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/dapps/basic_contract_caller/Cargo.toml b/dapps/basic_contract_caller/Cargo.toml new file mode 100755 index 0000000..dd93c15 --- /dev/null +++ b/dapps/basic_contract_caller/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "basic_contract_caller" +version = "4.2.0" +authors = ["Parity Technologies "] +edition = "2021" +publish = false + +[dependencies] +ink = { version = "4.2", default-features = false } + +scale = { package = "parity-scale-codec", version = "3.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.7", default-features = false, features = ["derive"], optional = true } + +# Note: We **need** to specify the `ink-as-dependency` feature. +# +# If we don't we will end up with linking errors! +other_contract = { path = "other_contract", default-features = false, features = ["ink-as-dependency"] } + +[dev-dependencies] +ink_e2e = { version = "4.2.0" } + +[lib] +path = "lib.rs" + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", + + # Note: The metadata generation step requires `std`. If we don't specify this the metadata + # generation for our contract will fail! + "other_contract/std", +] +ink-as-dependency = [] +e2e-tests = [] diff --git a/dapps/basic_contract_caller/lib.rs b/dapps/basic_contract_caller/lib.rs new file mode 100755 index 0000000..e8b58e7 --- /dev/null +++ b/dapps/basic_contract_caller/lib.rs @@ -0,0 +1,252 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] + +#[ink::contract] +mod basic_contract_caller { + use ink::env::{ + call::{ + build_create, + build_call, + ExecutionInput, + Selector, + }, + Call, + DefaultEnvironment, + }; + + /// We import the generated `ContractRef` of our other contract. + /// + /// Note that the other contract must have re-exported it (`pub use + /// OtherContractRef`) for us to have access to it. + use other_contract::OtherContractRef; + + #[ink(storage)] + pub struct BasicContractCaller { + /// We specify that our contract will store a reference to the `OtherContract`. + other_contract: Option, + other_contract_address: Option, + } + + /// Errors that can occur upon calling this contract. + #[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)] + #[cfg_attr(feature = "std", derive(::scale_info::TypeInfo))] + pub enum Error { + /// Returned if not other contract address exists. + NoOtherContractAddress, + ResponseError, + } + + /// Type alias for the contract's result type. + pub type Result = core::result::Result; + + impl BasicContractCaller { + /// In order to use the `OtherContract` we first need to **instantiate** it. + /// + /// To do this we will use the uploaded `code_hash` of `OtherContract`. + #[ink(constructor)] + pub fn new(other_contract_code_hash: Hash, other_contract_address: AccountId) -> Self { + // using `CreateBuilder` to instantiate contract + // https://use.ink/basics/cross-contract-calling#createbuilder + let other_contract: OtherContractRef = build_create::() + .code_hash(other_contract_code_hash) + // https://substrate.stackexchange.com/questions/3992/i-get-a-the-executed-contract-exhausted-its-gas-limit-when-attempting-to-inst + .gas_limit(100000000000) + // https://substrate.stackexchange.com/questions/8445/cross-contract-instantiation-failed-with-transferfailed/8447#8447 + .endowment(0) + .exec_input( + ExecutionInput::new(Selector::new(ink::selector_bytes!("new"))) + .push_arg(true) + ) + .salt_bytes(&[0xDE, 0xAD, 0xBE, 0xEF]) + .returns::() + .instantiate(); + + Self { + other_contract: Some(other_contract), + other_contract_address: Some(other_contract_address), + } + } + + /// Reference: https://github.com/hyperledger/solang/blob/main/integration/substrate/ink/caller/lib.rs#L33-L38 + /// Do a proxy call to `callee` and return its result. + #[ink(message)] + pub fn u32_proxy( + &self, + callee: AccountId, // contract address + selector: [u8; 4], // method + // arg: u32, // args + max_gas: Option, + transfer_value: Option, + ) -> bool { + let res = build_call::() + .call_type(Call::new(callee).gas_limit(max_gas.unwrap_or_default())) + // .transferred_value(transfer_value.unwrap_or_default()) + .transferred_value(0) + .exec_input(ExecutionInput::new( + Selector::new(selector)) + // .push_arg(arg) + ) + .returns::() // FIXME: This should be Result to respect LanguageError + .try_invoke() + .expect("Error calling get."); + + ink::env::debug_println!("res {:?}", res); + + match res { + // Contract Success + EnvResult::Ok(MessageResult::Ok(ContractResult::Ok(tuple))) => { + ink::env::debug_println!("contract success tuple {:?}", tuple); + return Ok(tuple); + }, + // Contract Error + EnvResult::Ok(MessageResult::Ok(ContractResult::Err(e))) => { + ink::env::debug_println!("contract error {:?}", e); + return Err(Error::ResponseError); + }, + // Lang Error + EnvResult::Ok(MessageResult::Err(ink::LangError::CouldNotReadInput)) => { + ink::env::debug_println!("LangError::CouldNotReadInput"); + return Err(Error::ResponseError); + }, + // Environment Error + EnvResult::Err(e) => { + ink::env::debug_println!("environment error occurred {:?}", e); + return Err(Error::ResponseError); + }, + // Unimplemented Error + _ => { + ink::env::debug_println!("unimplemented error in u32_proxy"); + return unimplemented!(); + }, + }; + } + + #[ink(message)] + pub fn get(&mut self) -> Result { + match &self.other_contract_address { + Some(c) => { + let res = build_call::() + .call(c.clone()) + .gas_limit(100000000000) + // .transferred_value(10) // TransferFailed + .exec_input( + ExecutionInput::new(Selector::new(ink::selector_bytes!("get"))) + ) + .returns::() + // https://use.ink/basics/cross-contract-calling#builder-error-handling + .try_invoke() + .expect("Error calling get."); + match res { + Ok(is_flipped) => { + ink::env::debug_println!("is_flipped {:?}", is_flipped); + return Ok(is_flipped); + }, + Err(e) => { + ink::env::debug_println!("error {:?}", e); + return Err(Error::ResponseError); + }, + }; + }, + None => return Err(Error::NoOtherContractAddress), + } + } + + #[ink(message)] + pub fn flip(&mut self) -> Result { + match &self.other_contract_address { + Some(c) => { + // using CallBuilder + // https://use.ink/basics/cross-contract-calling#callbuilder + let res = build_call::() + .call(c.clone()) + .gas_limit(100000000000) + .transferred_value(0) // TransferFailed if non-zero + .exec_input( + ExecutionInput::new(Selector::new(ink::selector_bytes!("flip"))) + ) + .returns::() + .try_invoke() + .expect("Error calling flip."); + match res { + Ok(new_value) => { + ink::env::debug_println!("new_value {:?}", new_value); + return Ok(new_value); + }, + Err(e) => { + ink::env::debug_println!("error {:?}", e); + return Err(Error::ResponseError); + }, + }; + }, + None => return Err(Error::NoOtherContractAddress), + } + } + + #[ink(message)] + pub fn flip_and_get(&mut self) -> Result { + match &self.other_contract_address { + Some(c) => { + let _ = build_call::() + .call(c.clone()) + .gas_limit(100000000000) + .transferred_value(0) // TransferFailed if non-zero + .exec_input( + ExecutionInput::new(Selector::new(ink::selector_bytes!("flip"))) + ) + .returns::() + .try_invoke() + .expect("Error calling flip."); + let res = build_call::() + .call(c.clone()) + .gas_limit(100000000000) + .exec_input( + ExecutionInput::new(Selector::new(ink::selector_bytes!("get"))) + ) + .returns::() + .try_invoke() + .expect("Error calling get."); + match res { + Ok(is_flipped) => { + ink::env::debug_println!("is_flipped {:?}", is_flipped); + return Ok(is_flipped); + }, + Err(e) => { + ink::env::debug_println!("error {:?}", e); + return Err(Error::ResponseError); + }, + }; + }, + None => return Err(Error::NoOtherContractAddress), + } + } + + #[ink(message)] + pub fn get_other_contract_address(&self) -> Result { + match &self.other_contract_address { + Some(c) => { + let res = + build_call::() + .call(c.clone()) + .gas_limit(100000000000) + // .transferred_value(10) // TransferFailed + .exec_input( + ExecutionInput::new(Selector::new(ink::selector_bytes!("get_other_contract_address"))) + ) + .returns::() + .try_invoke() + .expect("Error calling get_other_contract_address."); + match res { + Ok(contract_address) => { + ink::env::debug_println!("contract_address {:?}", contract_address); + return Ok(contract_address); + }, + Err(e) => { + ink::env::debug_println!("error {:?}", e); + return Err(Error::ResponseError); + }, + }; + }, + None => return Err(Error::NoOtherContractAddress), + } + } + } +} diff --git a/dapps/basic_contract_caller/other_contract/.gitignore b/dapps/basic_contract_caller/other_contract/.gitignore new file mode 100755 index 0000000..8de8f87 --- /dev/null +++ b/dapps/basic_contract_caller/other_contract/.gitignore @@ -0,0 +1,9 @@ +# Ignore build artifacts from the local tests sub-crate. +/target/ + +# Ignore backup files creates by cargo fmt. +**/*.rs.bk + +# Remove Cargo.lock when creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/dapps/basic_contract_caller/other_contract/Cargo.toml b/dapps/basic_contract_caller/other_contract/Cargo.toml new file mode 100755 index 0000000..0c1a55d --- /dev/null +++ b/dapps/basic_contract_caller/other_contract/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "other_contract" +version = "4.2.0" +authors = ["Parity Technologies "] +edition = "2021" +publish = false + +[dependencies] +ink = { version = "4.2", default-features = false } + +scale = { package = "parity-scale-codec", version = "3.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.7", default-features = false, features = ["derive"], optional = true } + +[dev-dependencies] +ink_e2e = "4.2.0" + +[lib] +path = "lib.rs" + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", +] +ink-as-dependency = [] +e2e-tests = [] diff --git a/dapps/basic_contract_caller/other_contract/lib.rs b/dapps/basic_contract_caller/other_contract/lib.rs new file mode 100755 index 0000000..1b2ae52 --- /dev/null +++ b/dapps/basic_contract_caller/other_contract/lib.rs @@ -0,0 +1,56 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] + +/// Re-export the `ContractRef` generated by the ink! codegen. +/// +/// This let's other crates which pull this contract in as a dependency to interact +/// with this contract in a type-safe way. +pub use self::other_contract::OtherContractRef; + +#[ink::contract] +mod other_contract { + + /// Errors that can occur upon calling this contract. + #[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)] + #[cfg_attr(feature = "std", derive(::scale_info::TypeInfo))] + pub enum Error { + UnknownError, + } + + /// Type alias for the contract's result type. + pub type Result = core::result::Result; + + #[ink(storage)] + pub struct OtherContract { + value: bool, + } + + impl OtherContract { + #[ink(constructor)] + pub fn new(init_value: bool) -> Self { + Self { value: init_value } + } + + #[ink(message)] + #[ink(payable)] + pub fn flip(&mut self) -> Result { + ink::env::debug_println!( + "received payment with flip: {}", + self.env().transferred_value() + ); + + self.value = !self.value; + return Ok(self.value) + } + + #[ink(message)] + pub fn get(&self) -> bool { + self.value + } + + #[ink(message)] + pub fn get_other_contract_address(&self) -> AccountId { + ink::env::debug_println!("oracle contract address {:?}", self.env().account_id()); + self.env().account_id() + } + } +} diff --git a/dapps/evm2/randomness/.env.example b/dapps/evm2/randomness/.env.example new file mode 100644 index 0000000..ac359b7 --- /dev/null +++ b/dapps/evm2/randomness/.env.example @@ -0,0 +1,8 @@ +# https://faucet.moonbeam.network/ +MOONBASE_PRIVATE_KEY='0x' +MOONSCAN_API_KEY='' +# https://blastapi.io/ +MOONBASE_BLASTAPI_ENDPOINT='' +# obtain from https://dashboard.alchemy.com/ under Ethereum > Ethereum Sepolia +CHAINLINK_SEPOLIA_ENDPOINT='https://rpc.sepolia.org' +ALCHEMY_API_KEY='' diff --git a/dapps/evm2/randomness/.gitignore b/dapps/evm2/randomness/.gitignore new file mode 100644 index 0000000..f332a0a --- /dev/null +++ b/dapps/evm2/randomness/.gitignore @@ -0,0 +1,5 @@ +build +node_modules +package-lock.json +release-version.json +.env diff --git a/dapps/evm2/randomness/LICENSE b/dapps/evm2/randomness/LICENSE new file mode 100644 index 0000000..bb98c92 --- /dev/null +++ b/dapps/evm2/randomness/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2018 Truffle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/dapps/evm2/randomness/README.md b/dapps/evm2/randomness/README.md new file mode 100644 index 0000000..0c64d75 --- /dev/null +++ b/dapps/evm2/randomness/README.md @@ -0,0 +1,217 @@ +# Randomness + +Note: The following was based upon Moonbeam Truffle Box https://docs.moonbeam.network/builders/build/eth-api/dev-env/truffle/ + +## Getting started + +* Install Node.js v18.x (i.e. v18.16.0) since has LTS https://nodejs.org/en/download/releases +* Use an LTS version of NPM to avoid error `This version of µWS is not compatible with your Node.js build` https://stackoverflow.com/questions/71081725/this-version-of-%C2%B5ws-is-not-compatible-with-your-node-js-build-error-node-loade + +```bash +apt-get remove -y nodejs +rm -rf /usr/lib/node_modules/ +curl -fsSL https://deb.nodesource.com/setup_18.x | bash - +apt-get install -y nodejs +curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarnkey.gpg >/dev/null +echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian stable main" | tee /etc/apt/sources.list.d/yarn.list +apt-get update && apt-get install yarn +npm config rm proxy +npm config rm https-proxy +``` +* Install Truffle dependency: +```bash +npm install -g truffle +``` +* Check if works +```bash +truffle --version +``` +* Fix permissions error since /usr/bin/truffle symlinks to below but below didn't have execute permissions for any users +```bash +chmod 755 /usr/lib/node_modules/truffle/build/cli.bundled.js +``` +* Setup Moonbeam Truffle Box template that this has been based upon +```bash +mkdir -p /app/dapps/evm2/randomness +DAPP_PATH=/app/dapps/evm2/randomness +git clone https://github.com/PureStake/moonbeam-truffle-box $DAPP_PATH +cd $DAPP_PATH +```bash +* Replace template contracts with just https://github.com/hyperledger/solang/blob/main/examples/substrate/flipper.sol and add `pragma solidity ^0.8.0;` + +* Update all dependencies in package.json to new major version +```bash +npm outdated +npm install -g npm-check-updates +ncu -u +npm update +``` +* Install dependencies +```bash +npm install +``` + +* Install Docker in Docker container +```bash +apt-get remove docker docker-engine docker.io containerd runc +apt-get update && apt-get upgrade -y +apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common -y +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - +apt-key fingerprint 0EBFCD88 +add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" +apt-get update +apt-get install docker-ce docker-ce-cli containerd.io -y +adduser user +usermod -aG docker user +systemctl restart docker +systemctl enable docker +apt-get install -y docker-compose-plugin +apt-get install docker-compose +``` + +Note: It is necessary to run Docker within a Docker container https://devopscube.com/run-docker-in-docker/ in order to use `truffle run moonbeam ...` commands from https://trufflesuite.com/boxes/moonbeam-truffle-box/, which use the Moonbeam Truffle Box plugin https://github.com/PureStake/moonbeam-truffle-plugin, which uses images from Docker hub https://hub.docker.com/r/purestake/moonbeam/tags, or setup manually by following https://docs.moonbeam.network/node-operators/networks/run-a-node/flags/, as follows: + +To run a local node run similar to the following: +```bash +mkdir /opt/moonbeam +git clone https://github.com/PureStake/moonbeam /opt/moonbeam +cd /opt/moonbeam +git checkout tags/$(git describe --tags) +cargo build +./target/debug/moonbeam --help +``` + +Then configure Truffle to connect to it Moonbeam Development `node_modules/.bin/truffle migrate --network dev`. Note that use of Ganache does not include pre-compiles https://docs.moonbeam.network/builders/build/eth-api/dev-env/truffle/. + +Preferably use Moonbase Alpha TestNet `node_modules/.bin/truffle migrate --network moonbase` that requires testnet tokens from the faucet https://faucet.moonbeam.network/. + +Note: When running tests against Moonbase Alpha TestNet. Disconnect VPN. Try to avoid encountering errors like `Too Many Requests`, `ProviderError`, `ETIMEDOUT`, `32603`, it is important to use a dedicated endpoint that you can get for free here https://blastapi.io/ by creating an account, creating a project, choosing Moonbase Alpha Testnet from their available endpoints and clicking "Activate", and then copying either the RPC endpoint. +Note: I got error `TypeError: Cannot create property 'gasLimit' on string '0x464aff'` when tried using Blastapi WSS endpoint instead of RPC (https) endpoint. Note: If you change to WSS then you need to use `WebsocketProvider` instead of `HttpProvider`. Solved this error by using `gasLimit` in truffle-config.js (it was not necessary for HTTPS) + +This is important because the public endpoint https://rpc.api.moonbase.moonbeam.network has stricter rate limiting. Ensure that you replace the public Moonbase Alpha endpoint in the truffle-config.js file with the dedicated endpoint. + +Run tests +``` +truffle test +``` + +* Generate account with Moonkey https://docs.moonbeam.network/node-operators/networks/collators/requirements/#generating-an-account-with-moonkey +``` +cd /opt +wget https://github.com/PureStake/moonbeam/releases/download/v0.8.0/moonkey /opt +shasum -a 256 moonkey +``` +* Verify output is `019c3de832ded3fccffae950835bb455482fca92714448cc0086a7c5f3d48d3e` +* Generate account `./moonkey --w12` +* Obtain Moonbase Alpha tokens from faucet + * https://faucet.moonbeam.network/ + +* Compile contracts on network of choice (i.e. "moonbase") defined in truffle.js + * Compile full `truffle compile --compile-all` +* Migrate + * Migrate full `truffle migrate --reset --compile-all --network moonbase` + * Migrate full `truffle migrate --reset --compile-all --network sepolia` +* Test + * `truffle test ./test/test_MoonbaseVRF.js --verbose-rpc --network moonbase` + * `truffle test ./test/test_ChainlinkVRF.js --network sepolia` +* Verify Contract - Moonbase Precompile + +``` +# truffle run verify Flipper --network moonbase +Verifying contracts on moonscan + Verifying Flipper + Pass - Verified: https://moonbase.moonscan.io/address/0x1c440D264DcCBe9b7AC84edCEC99De926Db98753#code + Successfully verified 1 contract(s). +Verifying contracts on sourcify + Failed to connect to Sourcify API at url https://sourcify.dev/server/chains +root@ink:/app/dapps/evm2/randomness# truffle run verify RandomNumber --network moonbase +Verifying contracts on moonscan + Verifying RandomNumber + Pass - Verified: https://moonbase.moonscan.io/address/0x4027755C05514421fe00f4Fde0bD3F8475ce8A6b#code + Successfully verified 1 contract(s). +Verifying contracts on sourcify + Failed to connect to Sourcify API at url https://sourcify.dev/server/chains +``` + +* Verify Contract - Chainlink VRF +``` +# cd flipper +# truffle run verify VRFD20 --network sepolia + +Verifying contracts on etherscan + No etherscan or sepolia_etherscan API Key provided +Verifying contracts on sourcify + Verifying VRFD20 + Pass - Verified: https://sourcify.dev/#/lookup/0xe22cdfA9d8C8e942B498696ef54584426d2f5Dd6 + Successfully verified 1 contract(s). +``` + +* Chainlink VRF https://docs.chain.link/getting-started/intermediates-tutorial + * View token balance https://sepolia.etherscan.io/address/0x1dd907abb024e17d196de0d7fe8eb507b6ccaae7 + * Create and fund a subscription https://docs.chain.link/vrf/v2/subscription/examples/get-a-random-number/#create-and-fund-a-subscription + * Go to https://vrf.chain.link/sepolia/new. + * **Important** Must be in a private browser tab to clear cache + * https://vrf.chain.link/sepolia/3350 + * After clicking "Add Consume" and adding a consumer address in this [tx](https://sepolia.etherscan.io/tx/0x4bfc7fe73fd20f524daf07875502f2d1e7d65032c71a0fe736d3a8ddc6cb388f) it said `Important: Your consumer contract must use the Subscription ID 3350 in the VRF request to make use of these funds.` + * Go to https://vrf.chain.link/sepolia/3350, click "Actions" > "Fund Subscription" to prepay subscription. [tx](https://sepolia.etherscan.io/tx/0xf2a34026f513c29f6e8a12f5b6ac7d7bae2adc619e09dcc6b011c1a6c4f89350), relates to contract deployed to 0x9912FEa426655B1d723ADFFa5dbD915C78198c49 with that subscription id 3350 + + * NOTE: + * I had already created a subscription id https://vrf.chain.link/sepolia/3217 to deploy through migrations file but had not added the consumer contract of it deployed, which i later did at https://sepolia.etherscan.io/tx/0xc689e64aca1c531fab582bbbbd86e835bb465c227d6068e001f1692f30eab3f6, and funded it in this tx https://sepolia.etherscan.io/tx/0x23e4464e74fbfb30dfd3a65042e86f37d915e143b63105f5391bcd23d45d93f6, so this is the one to use since that's the subscription id passed to the constructor when deployed + * Fix + * After being unsuccessful in trying to generate a random number with subscription id 3217 and 3350, I got help in #vrf room of Chainlink on Discord from Lucas Archangelo who suggested to use the existing subscription id and redeploy and add the new contract address as the consumer. So I deployed VRFD20 to 0xE265f9a30c72A78C4b89Fc2e2C60e9327704Fa5e that has tx hash https://dashboard.tenderly.co/tx/sepolia/0x0122f8b4efb1f66dea8482edf7d4e9089946cd13a60559504a4dc63fdd0a727b?trace=0.0, so then i went to https://vrf.chain.link/sepolia/3350, and added new consumer address 0xE265f9a30c72A78C4b89Fc2e2C60e9327704Fa5e in this tx 0x397c8aedab55e475d27b87de8d63cc1f866b007698f94cea49ab2ae75f016104, then i imported all the contracts into Remix to access that contract and verified that i was interacting with that new contract. then made transaction rollDice passing my account address 0x1dd907abb024e17d196de0d7fe8eb507b6ccaae7 as a parameter in this tx https://sepolia.etherscan.io/tx/0x558a0567c5c71ec378e6919d75d8cd02b60e97c6c87205f77f5146233a58affa, then finally in Remix i called getRolledValueForPlayer passing my account address 0x1dd907abb024e17d196de0d7fe8eb507b6ccaae7 as a parameter, and that output the following in Remix 0: uint256: 8, so it successfully generated the random number 8. I also modified ./scripts/demo-chainlink-vrf-on-ethereum-sepolia.js to load that new address and to not run rollDice since we'd already done that, and it also returned `8`. + +* Run + * node ./scripts/demo-chainlink-vrf-on-ethereum-sepolia.js + +* Chainlink VRF https://docs.chain.link/getting-started/intermediates-tutorial + * View token balance https://sepolia.etherscan.io/address/0x1dd907abb024e17d196de0d7fe8eb507b6ccaae7 + * Create and fund a subscription https://docs.chain.link/vrf/v2/subscription/examples/get-a-random-number/#create-and-fund-a-subscription + * Prepay Subscription https://vrf.chain.link/ + * Receipt https://sepolia.etherscan.io/tx/0xcc2cd9edf90e0f3351f3398b7013a7259c0acc7cfbfc38454192324fcfdb7d6a + * Reference v2 contract (not implemented): https://remix.ethereum.org/#url=https://docs.chain.link/samples/VRF/VRFv2Consumer.sol&lang=en&optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.18+commit.87f61d96.js + +* TODO + * allowing the owner of the VRFD20 contract to specify what block number the roll the dice relates to, since the block number will represent the start of each game + * allowing a random number to be rolled by a roller account calling rollDice multiple times each time there is a new game set by the owner of the VRFD20 + +* Debugging + * Details of each tx https://dashboard.tenderly.co/tx/sepolia/0x???? +* Troubleshooting + * `Client network socket disconnected before secure TLS connection was established` + * Try fixing by running `unset https_proxy && unset http_proxy`, but this didn't actually work for me + * If you get error `PollingBlockTracker` then try connecting to a different ISP and disable VPN and stop using a proxy and restart access to your internet + * Sometimes when you run `truffle test --network moonbase` after changing some CLI options it outputs `Error: The network id specified in the truffle config (1287) does not match the one returned by the network (4619453). Ensure that both the network and the provider are properly configured`, even though the network id in the truffle-config.js is in fact 1287 (for some reason it confuses the block number for the network id), but when you run it again it might works. So just keep running the command again until it. Or run `truffle test --network sepolia` instead of + `truffle test --verbose-rpc --network sepolia` + works or change internet connection. +* References + * Hackathon submission + * https://moonbeam.hackerearth.com/challenges/hackathon/moonbeam-hackathon-2/dashboard + * https://github.com/trufflesuite/truffle/blob/develop/packages/contract/README.md + * https://docs.web3js.org/ + * https://evmdocs.acala.network/tutorials/hardhat-tutorials/precompiledtoken-tutorial + * Chainlink + * https://vrf.chain.link/ + * VRF https://docs.chain.link/getting-started/intermediates-tutorial + * Faucet https://faucets.chain.link/ + * https://docs.chain.link/getting-started/advanced-tutorial + * https://docs.chain.link/vrf/v2/subscription/supported-networks/#configurations + * https://docs.chain.link/vrf/v2/subscription/examples/get-a-random-number/#create-and-fund-a-subscription + * Alchemy + * https://dashboard.alchemy.com/apps + * https://docs.alchemy.com/reference/api-overview + * https://github.com/alchemyplatform/alchemy-sdk-js + * https://docs.alchemy.com/docs/smart-contract-basics + * https://docs.alchemy.com/docs/ethers-js-provider + * https://docs.alchemy.com/docs/interacting-with-a-smart-contract + * Ethers + * v5 https://docs.ethers.org/v5/api/utils/hdnode/ + * v6 https://docs.ethers.org/v6/api/providers/#WebSocketProvider + * XCM + * https://docs.moonbeam.network/builders/interoperability/xcm/overview/ + * https://github.com/AstarNetwork/ink-xvm-sdk/tree/main + * Polkadot.js + * https://docs.moonbeam.network/builders/build/substrate-api/polkadot-js-api/ + + * Other + * https://github.com/HCastano/urban-planning-in-the-paraverse-with-ink + * https://www.rob.tech/blog/hybrid-chains/ diff --git a/dapps/evm2/randomness/contracts/Flipper.sol b/dapps/evm2/randomness/contracts/Flipper.sol new file mode 100644 index 0000000..bf04d0c --- /dev/null +++ b/dapps/evm2/randomness/contracts/Flipper.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.3; + +import "./lib/RandomNumber.sol"; + +contract Flipper { + bool private value; + // address public randomNumberContractAddress; + + /// Constructor that initializes the `bool` value to the given `init_value`. + constructor(bool initvalue) { + value = initvalue; + } + + // function setRandomNumberContractAddress(address _randomNumberAddress) public { + // randomNumberContractAddress = _randomNumberAddress; + // } + + /// A message that can be called on instantiated contracts. + /// This one flips the value of the stored `bool` from `true` + /// to `false` and vice versa. + function flip() public payable { + value = !value; + } + + /// Simply returns the current value of our `bool`. + function get() public view returns (bool) { + return value; + } +} diff --git a/dapps/evm2/randomness/contracts/Migrations.sol b/dapps/evm2/randomness/contracts/Migrations.sol new file mode 100644 index 0000000..d1b7e6a --- /dev/null +++ b/dapps/evm2/randomness/contracts/Migrations.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.3; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + constructor() { + owner = msg.sender; + } + + modifier restricted() { + if (msg.sender == owner) _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } +} diff --git a/dapps/evm2/randomness/contracts/lib/RandomNumber.sol b/dapps/evm2/randomness/contracts/lib/RandomNumber.sol new file mode 100644 index 0000000..16bf121 --- /dev/null +++ b/dapps/evm2/randomness/contracts/lib/RandomNumber.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity >=0.8.3; + +// compiling with truffle automatically generates the respective ABI files for these precompiles +// https://github.com/PureStake/moonbeam/blob/master/precompiles/randomness/Randomness.sol +// import "../precompiles/randomness/Randomness.sol"; +import {Randomness, MIN_VRF_BLOCKS_DELAY} from "../precompiles/randomness/Randomness.sol"; +// https://github.com/PureStake/moonbeam/blob/master/precompiles/randomness/RandomnessConsumer.sol +import {RandomnessConsumer} from "../precompiles/randomness/RandomnessConsumer.sol"; + +contract RandomNumber is RandomnessConsumer { + // The Randomness Precompile Interface + + // create a wrapper to access the randomness precompile + Randomness public theRandomness; + address public constant randomnessPrecompileAddress = 0x0000000000000000000000000000000000000809; + + // Variables required for randomness requests + uint256 public requiredDeposit; + uint64 public FULFILLMENT_GAS_LIMIT = 100000; + // The fee can be set to any value as long as it is enough to cover + // the fulfillment costs. Any leftover fees will be refunded to the + // refund address specified in the requestRandomness function below + uint256 public MIN_FEE = FULFILLMENT_GAS_LIMIT * 5 gwei; // 0.000000001 Ether == 1 gwei + uint32 public VRF_BLOCKS_DELAY; + bytes32 public SALT_PREFIX = "change-me-to-anything"; + + uint256 private constant ROLL_IN_PROGRESS = 42; + + // Storage variables for the current request + uint256 public requestId; + uint256[] public random; + + // address public s_owner; + + // map rollers to requestIds + mapping(uint256 => address) private s_rollers; + // map vrf results to rollers + mapping(address => uint256) private s_results; + + event DiceRolled(uint256 indexed requestId, address indexed roller); + event DiceLanded(uint256 indexed requestId, uint256 indexed result); + + constructor() payable RandomnessConsumer() { + // Initialize use of Randomness dependency before trying to access it + theRandomness = Randomness(randomnessPrecompileAddress); + requiredDeposit = theRandomness.requiredDeposit(); + // Because this contract can only perform 1 random request at a time, + // We only need to have 1 required deposit. + require(msg.value >= requiredDeposit); + // s_owner = msg.sender; + VRF_BLOCKS_DELAY = MIN_VRF_BLOCKS_DELAY; + } + + function requestRandomness( + address roller + ) public payable { + // ) public onlyOwner payable { + require(s_results[roller] == 0, "Already rolled"); + // Make sure that the value sent is enough + require(msg.value >= MIN_FEE); + // Request local VRF randomness + requestId = theRandomness.requestLocalVRFRandomWords( + msg.sender, // Refund address + msg.value, // Fulfillment fee + FULFILLMENT_GAS_LIMIT, // Gas limit for the fulfillment + SALT_PREFIX ^ bytes32(requestId++), // A salt to generate unique results + 1, // Number of random words + VRF_BLOCKS_DELAY // Delay before request can be fulfilled + ); + + s_rollers[requestId] = roller; + s_results[roller] = ROLL_IN_PROGRESS; + emit DiceRolled(requestId, roller); + } + + function getRequestStatus() public view returns(Randomness.RequestStatus) { + Randomness.RequestStatus requestStatus = theRandomness.getRequestStatus(requestId); + return requestStatus; + } + + function fulfillRequest() public { + theRandomness.fulfillRequest(requestId); + } + + function fulfillRandomWords( + uint256 reqId, /* requestId */ + uint256[] memory randomWords + ) internal override { + // Save the randomness results + uint256 d20Value = (randomWords[0] % 20) + 1; + s_results[s_rollers[reqId]] = d20Value; + // Save the latest for comparison + random = randomWords; + } + + /** + * @notice Get the value rolled by a player once the address has rolled + * @param player address + * @return id as a string + */ + function getRolledValueForPlayer(address player) public view returns (uint256) { + require(s_results[player] != 0, "Dice not rolled"); + require(s_results[player] != ROLL_IN_PROGRESS, "Roll in progress"); + return s_results[player]; + } + + // modifier onlyOwner() { + // require(msg.sender == s_owner); + // _; + // } +} diff --git a/dapps/evm2/randomness/contracts/lib/VRFD20.sol b/dapps/evm2/randomness/contracts/lib/VRFD20.sol new file mode 100644 index 0000000..415c70c --- /dev/null +++ b/dapps/evm2/randomness/contracts/lib/VRFD20.sol @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.7; + +// Reference: https://remix.ethereum.org/#url=https://docs.chain.link/samples/VRF/VRFD20.sol&lang=en&optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.18+commit.87f61d96.js +import "../vrf/interfaces/VRFCoordinatorV2Interface.sol"; +import "../vrf/VRFConsumerBaseV2.sol"; + +/** + * @notice A Chainlink VRF consumer which uses randomness to mimic the rolling + * of a 20 sided dice + */ + +/** + * Request testnet LINK and ETH here: https://faucets.chain.link/ + * Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/ + */ + +/** + * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY. + * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. + * DO NOT USE THIS CODE IN PRODUCTION. + */ + +contract VRFD20 is VRFConsumerBaseV2 { + uint256 private constant ROLL_IN_PROGRESS = 42; + + VRFCoordinatorV2Interface COORDINATOR; + + // Your subscription ID. + uint64 public s_subscriptionId; + + // Sepolia coordinator. For other networks, + // see https://docs.chain.link/docs/vrf-contracts/#configurations + address vrfCoordinator = 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625; + + // The gas lane to use, which specifies the maximum gas price to bump to. + // For a list of available gas lanes on each network, + // see https://docs.chain.link/docs/vrf-contracts/#configurations + bytes32 s_keyHash = + 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c; + + // Depends on the number of requested values that you want sent to the + // fulfillRandomWords() function. Storing each word costs about 20,000 gas, + // so 40,000 is a safe default for this example contract. Test and adjust + // this limit based on the network that you select, the size of the request, + // and the processing of the callback request in the fulfillRandomWords() + // function. + uint32 callbackGasLimit = 40000; + + // The default is 3, but you can set this higher. + uint16 requestConfirmations = 3; + + // For this example, retrieve 1 random value in one request. + // Cannot exceed VRFCoordinatorV2.MAX_NUM_WORDS. + uint32 numWords = 1; + address public s_owner; + + // map rollers to requestIds + mapping(uint256 => address) private s_rollers; + // map vrf results to rollers + mapping(address => uint256) private s_results; + + event DiceRolled(uint256 indexed requestId, address indexed roller); + event DiceLanded(uint256 indexed requestId, uint256 indexed result); + + /** + * @notice Constructor inherits VRFConsumerBaseV2 + * + * @dev NETWORK: Sepolia + * + * @param subscriptionId subscription id that this consumer contract can use + */ + constructor(uint64 subscriptionId) payable VRFConsumerBaseV2(vrfCoordinator) { + COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); + // require(msg.value > 0); + s_owner = msg.sender; + s_subscriptionId = subscriptionId; + } + + /** + * @notice Requests randomness + * @dev Warning: if the VRF response is delayed, avoid calling requestRandomness repeatedly + * as that would give miners/VRF operators latitude about which VRF response arrives first. + * @dev You must review your implementation details with extreme care. + * + * @param roller address of the roller + */ + function rollDice( + address roller + ) public onlyOwner returns (uint256 requestId) { + require(s_results[roller] == 0, "Already rolled"); + // Will revert if subscription is not set and funded. + requestId = COORDINATOR.requestRandomWords( + s_keyHash, + s_subscriptionId, + requestConfirmations, + callbackGasLimit, + numWords + ); + + s_rollers[requestId] = roller; + s_results[roller] = ROLL_IN_PROGRESS; + emit DiceRolled(requestId, roller); + } + + /** + * @notice Callback function used by VRF Coordinator to return the random number to this contract. + * + * @dev Some action on the contract state should be taken here, like storing the result. + * @dev WARNING: take care to avoid having multiple VRF requests in flight if their order of arrival would result + * in contract states with different outcomes. Otherwise miners or the VRF operator would could take advantage + * by controlling the order. + * @dev The VRF Coordinator will only send this function verified responses, and the parent VRFConsumerBaseV2 + * contract ensures that this method only receives randomness from the designated VRFCoordinator. + * + * @param requestId uint256 + * @param randomWords uint256[] The random result returned by the oracle. + */ + function fulfillRandomWords( + uint256 requestId, + uint256[] memory randomWords + ) internal override { + uint256 d20Value = (randomWords[0] % 20) + 1; + s_results[s_rollers[requestId]] = d20Value; + emit DiceLanded(requestId, d20Value); + } + + /** + * @notice Get the value rolled by a player once the address has rolled + * @param player address + * @return id as a string + */ + function getRolledValueForPlayer(address player) public view returns (uint256) { + require(s_results[player] != 0, "Dice not rolled"); + require(s_results[player] != ROLL_IN_PROGRESS, "Roll in progress"); + return s_results[player]; + } + + modifier onlyOwner() { + require(msg.sender == s_owner); + _; + } +} diff --git a/dapps/evm2/randomness/contracts/precompiles/randomness/Randomness.sol b/dapps/evm2/randomness/contracts/precompiles/randomness/Randomness.sol new file mode 100644 index 0000000..efe86b0 --- /dev/null +++ b/dapps/evm2/randomness/contracts/precompiles/randomness/Randomness.sol @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity >=0.8.3; + +/// @dev The Randomness contract's address. +address constant RANDOMNESS_ADDRESS = 0x0000000000000000000000000000000000000809; + +/// @dev The Randomness contract's instance. +Randomness constant RANDOMNESS_CONTRACT = Randomness(RANDOMNESS_ADDRESS); + +/// @dev Maximum number of random words being requested +uint32 constant MAX_RANDOM_WORDS = 100; +/// @dev Minimum number of blocks before a request can be fulfilled for Local VRF Request +uint32 constant MIN_VRF_BLOCKS_DELAY = 2; +/// @dev Maximum number of blocks before a request can be fulfilled for Local VRF Request +uint32 constant MAX_VRF_BLOCKS_DELAY = 2000; +/// @dev The deposit amount needed to request random words. There is 1 deposit per request +uint256 constant REQUEST_DEPOSIT_AMOUNT = 1000000000000000000; + +/// @author The Moonbeam Team +/// @title Pallet Randomness Interface +/// @dev The interface through which solidity contracts will interact with Randomness +/// @custom:address 0x0000000000000000000000000000000000000809 +interface Randomness { + /// @notice Event emitted when the request has been successfully executed + event FulFillmentSucceeded(); + /// @notice Event emitted when the request has failed to execute fulfillment + event FulFillmentFailed(); + + /// @notice The status of the request + /// @param DoesNotExist The request doesn't exist + /// @param Pending The request cannot be fulfilled yet + /// @param Ready The request is ready to be fulfilled + /// @param Expired The request has expired + enum RequestStatus { + DoesNotExist, + Pending, + Ready, + Expired + } + + /// @notice The type of randomness source + /// @param LocalVRF Randomness VRF using the parachain material as seed + /// @param RelayBabeEpoch Randomness VRF using relay material from previous epoch + enum RandomnessSource { + LocalVRF, + RelayBabeEpoch + } + + /// @notice The request details + /// @param id The id of the request (is always < 2**64) + /// @param refundAddress The address receiving the left-over fees after the fulfillment + /// @param contractAddress The address of the contract being called back during fulfillment + /// @param fee The amount to set aside to pay for the fulfillment + /// @param gasLimit The gas limit to use for the fulfillment + /// @param salt A string being mixed with the randomness seed to obtain different random words. This should be as unique as possible; using the same salt will lead to same randomness result. + /// @param numWords The number of random words requested (from 1 to MAX_RANDOM_WORDS) + /// @param randomnessSource The type of randomness source used to generate the random words + /// @param fulfillmentBlock The parachain block number at which the request can be fulfilled (for LocalVRF only) + /// @param fulfillmentEpochIndex The relay epoch index at which the request can be fulfilled (for RelayBabeEpoch) + /// @param expirationBlock The parachain block number at which the request expires (for LocalVRF only) + /// @param expirationEpochIndex The relay epoch index at which the request expires (for RelayBabeEpoch) + /// @param status The current status of the request + struct Request { + uint256 id; + address refundAddress; + address contractAddress; + uint256 fee; + uint256 gasLimit; + bytes32 salt; + uint32 numWords; + RandomnessSource randomnessSource; + uint32 fulfillmentBlock; + uint64 fulfillmentEpochIndex; + uint32 expirationBlock; + uint64 expirationEpochIndex; + RequestStatus status; + } + + /// Return the current relay epoch index + /// @dev An epoch represents real time and not a block number + /// @dev Currently, time between epoch changes cannot be longer than: + /// @dev - Kusama/Westend/Rococo: 600 relay blocks (1 hour) + /// @dev - Polkadot: 2400 relay blocks (4 hours) + /// @custom:selector 81797566 + function relayEpochIndex() external view returns (uint64); + + /// Return the deposit required to perform a request + /// @dev Each request will need a deposit. + /// @custom:selector fb7cfdd7 + function requiredDeposit() external view returns (uint256); + + /// @notice Returns the request status + /// @param requestId The id of the request to check (must be < 2**64) + /// @return status Status of the request + /// @custom:selector d8a4676f + function getRequestStatus(uint256 requestId) + external + view + returns (RequestStatus status); + + /// @notice Returns the request or revert + /// @param requestId The id of the request to check (must be < 2**64) + /// @return request The request + /// @custom:selector c58343ef + function getRequest(uint256 requestId) + external + view + returns (Request memory request); + + /// @notice Request random words generated from the parachain VRF + /// @dev This is using pseudo-random VRF executed by the collator at the fulfillment + /// @dev Warning: + /// @dev The collator in charge of producing the block at fulfillment can decide to skip + /// @dev producing the block in order to have a different random word generated by the next + /// @dev collator, at the cost of a block reward. It is therefore economically viable to use + /// @dev this randomness source only if the financial reward at stake is lower than the block + /// @dev reward. + /// @dev In order to reduce the risk of a collator being able to predict the random words + /// @dev when the request is performed, it is possible to increase the delay to multiple blocks + /// @dev The higher the delay is, the less likely the collator will be able to know which + /// @dev collator will be in charge of fulfilling the request. + /// @dev Fulfillment is manual and can be executed by anyone (for free) after the given delay + /// @param refundAddress The address receiving the left-over fees after the fulfillment + /// @param fee The amount to set aside to pay for the fulfillment + /// @param gasLimit The gas limit to use for the fulfillment + /// @param salt A string being mixed with the randomness seed to obtain different random words + /// @param numWords The number of random words requested (from 1 to MAX_RANDOM_WORDS) + /// @param delay The number of blocks until the request can be fulfilled (between MIN_DELAY_BLOCKS and MAX_DELAY_BLOCKS) + /// @return requestId The id of the request requestLocalVRFRandomWords + /// @custom:selector 9478430c + function requestLocalVRFRandomWords( + address refundAddress, + uint256 fee, + uint64 gasLimit, + bytes32 salt, + uint8 numWords, + uint64 delay + ) external returns (uint256); + + /// @notice Request random words generated from the relaychain Babe consensus + /// @dev The random words are generated from the hash of the all the VRF provided by the + /// @dev relaychain validator during 1 epoch. + /// @dev It requires a delay of at least 1 epoch after the current epoch to be unpredictable + /// @dev at the time the request is performed. + /// @dev Warning: + /// @dev The validator (on the relaychain) of the last block of an epoch can decide to skip + /// @dev producing the block in order to choose the previous generated epoch random number + /// @dev at the cost of a relaychain block rewards. It is therefore economically viable to use + /// @dev this randomness source only if the financial reward at stake is lower than the relaychain + /// @dev block reward. + /// @dev (see https://crates.parity.io/pallet_babe/struct.RandomnessFromOneEpochAgo.html) + /// @dev Fulfillment is manual and can be executed by anyone (for free) at + /// @dev the beginning of the 2nd relay epoch following the current one + /// @param refundAddress The address receiving the left-over fees after the fulfillment + /// @param fee Amount to set aside to pay for the fulfillment. Those fees are taken from the contract + /// @param gasLimit Gas limit for the fulfillment + /// @param salt Salt to be mixed with raw randomness to get output + /// @param numWords Number of random words to be returned (limited to MAX_RANDOM_WORDS) + /// @return requestId The id of the request + /// @custom:selector 33c14a63 + function requestRelayBabeEpochRandomWords( + address refundAddress, + uint256 fee, + uint64 gasLimit, + bytes32 salt, + uint8 numWords + ) external returns (uint256); + + /// @dev fulFill the request which will call the contract method "fulfillRandomWords" + /// @dev Fees of the caller are refunded if the request is fulfillable + /// @param requestId Request to be fulfilled (must be < 2**64) + /// @custom:selector 9a91eb0d + function fulfillRequest(uint256 requestId) external; + + /// @param requestId Request receiving the additional fees (must be < 2**64) + /// @param feeIncrease Amount to increase + /// @custom:selector d0408a7f + function increaseRequestFee(uint256 requestId, uint256 feeIncrease) + external; + + /// @param requestId Request to be purged (must be < 2**64) + /// @custom:selector 1d26cbab + function purgeExpiredRequest(uint256 requestId) external; +} diff --git a/dapps/evm2/randomness/contracts/precompiles/randomness/RandomnessConsumer.sol b/dapps/evm2/randomness/contracts/precompiles/randomness/RandomnessConsumer.sol new file mode 100644 index 0000000..590ce40 --- /dev/null +++ b/dapps/evm2/randomness/contracts/precompiles/randomness/RandomnessConsumer.sol @@ -0,0 +1,126 @@ +// Inspired by: https://raw.githubusercontent.com/smartcontractkit/chainlink/8e8a996fd882c0861bdc9824c1ca27c857c87d03/contracts/src/v0.8/VRFConsumerBaseV2.sol +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.8.3; + +/// @dev The Randomness contract's address. +address constant RANDOMNESS_ADDRESS = 0x0000000000000000000000000000000000000809; + +/** **************************************************************************** + * @notice Interface for contracts using VRF randomness + * ***************************************************************************** + * @dev PURPOSE + * + * @dev The purpose of this contract is to make it easy for contracts to talk to + * @dev the Randomness Precompile. It ensures 2 things: + * @dev 1. The fulfillment came from the Randomness Precompile + * @dev 2. The consumer contract implements fulfillRandomWords. + * ***************************************************************************** + * @dev USAGE + * + * @dev Calling contracts must inherit from RandomnessConsumer + * + * @dev Call one of the randomness request functions: + * @dev 1. requestLocalVRFRandomWords(refundAddress, fee, gasLimit, salt + * @dev numWords, delay), + * @dev 2. requestRelayBabeEpochRandomWords(refundAddress, fee, gasLimit, salt + * @dev numWords), + * @dev see (Randomness.sol for a description of each function and their arguments). + * + * @dev Once the request has been registered and the minimum delay is passed, the + * @dev request then can be executed (for 0 fee) by anyone. it will call your + * @dev contract's fulfillRandomWords method. + * + * @dev The randomness argument to fulfillRandomWords is a set of random words + * @dev generated from your requestId. + * + * @dev If your contract could have concurrent requests open, you can use the + * @dev requestId returned from requestRandomWords to track which response is associated + * @dev with which randomness request. + * @dev See "SECURITY CONSIDERATIONS" for principles to keep in mind, + * @dev if your contract could have multiple requests in flight simultaneously. + * + * @dev Colliding `requestId`s are cryptographically impossible as long as seeds + * @dev differ. + * + * ***************************************************************************** + * @dev SECURITY CONSIDERATIONS + * + * @dev A method with the ability to call your fulfillRandomness method directly + * @dev could spoof a VRF response with any random value, so it's critical that + * @dev it cannot be directly called by anything other than this base contract + * @dev (specifically, by the RandomnessConsumer.rawFulfillRandomness method). + * + * @dev For your users to trust that your contract's random behavior is free + * @dev from malicious interference, it's best if you can write it so that all + * @dev behaviors implied by a VRF response are executed *during* your + * @dev fulfillRandomness method. If your contract must store the response (or + * @dev anything derived from it) and use it later, you must ensure that any + * @dev user-significant behavior which depends on that stored value cannot be + * @dev manipulated by a subsequent VRF request. + * + * @dev Similarly, the collators have some influence over the order in which + * @dev VRF responses appear on the blockchain, so if your contract could have + * @dev multiple VRF requests in flight simultaneously, you must ensure that + * @dev the order in which the VRF responses arrive cannot be used to manipulate + * @dev your contract's user-significant behavior. + * + * @dev Since the output of the random words generated for + * @dev *requestLocalVRFRandomWords* is dependant of the collator producing the + * @dev block at fulfillment, the collator could skip its block forcing the + * @dev fulfillment to be executed by a different collator, and therefore + * @dev generating a different VRF. + * @dev However, such an attack would incur the cost of losing the block reward to + * @dev the collator. + * @dev It is also possible for a collator to be able to predict some of the + * @dev possible outcome of the VRF if the delay between the request and the + * @dev fulfillment is too short. It is for this reason that we allow to provide + * @dev a higher delay + * + * @dev Since the output of the random words generated for + * @dev *requestRelayBabeEpochRandomWords* is dependant of the relaychain + * @dev validator producing the blocks during an epoch, it is possible for + * @dev the last validator of an epoch to choose between 2 possible VRF + * @dev outputs by skipping the production of a block. + * @dev However, such an attack would incur the cost of losing the block reward to + * @dev the validator + * @dev It is not possible for a parachain collator to predict nor influence + * @dev the output of the relaychain VRF, not to censor the fulfillment as long as + * @dev there is one honest collator. + */ +abstract contract RandomnessConsumer { + error OnlyRandomnessPrecompileCanFulfill(address have, address want); + + /** + * @notice fulfillRandomness handles the VRF response. Your contract must + * @notice implement it. See "SECURITY CONSIDERATIONS" above for important + * @notice principles to keep in mind when implementing your fulfillRandomness + * @notice method. + * + * @dev RandomnessConsumer expects its subcontracts to have a method with this + * @dev signature, and will call it once it has verified the proof + * @dev associated with the randomness. (It is triggered via a call to + * @dev rawFulfillRandomness, below.) + * + * @param requestId The Id initially returned by requestLocalVRFRandomWords or requestRelayBabeEpochRandomWords + * @param randomWords The VRF output expanded to the requested number of words + */ + function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) + internal + virtual; + + // rawFulfillRandomness is called by Randomness Precompile when the executeFulfillement + // is called. rawFulfillRandomness then calls fulfillRandomness, after validating + // the origin of the call + function rawFulfillRandomWords( + uint256 requestId, + uint256[] memory randomWords + ) external { + if (msg.sender != RANDOMNESS_ADDRESS) { + revert OnlyRandomnessPrecompileCanFulfill( + msg.sender, + RANDOMNESS_ADDRESS + ); + } + fulfillRandomWords(requestId, randomWords); + } +} diff --git a/dapps/evm2/randomness/contracts/vrf/VRFConsumerBaseV2.sol b/dapps/evm2/randomness/contracts/vrf/VRFConsumerBaseV2.sol new file mode 100644 index 0000000..f8d81a0 --- /dev/null +++ b/dapps/evm2/randomness/contracts/vrf/VRFConsumerBaseV2.sol @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +// Reference: https://github.com/smartcontractkit/chainlink/blob/master/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol + +/** **************************************************************************** + * @notice Interface for contracts using VRF randomness + * ***************************************************************************** + * @dev PURPOSE + * + * @dev Reggie the Random Oracle (not his real job) wants to provide randomness + * @dev to Vera the verifier in such a way that Vera can be sure he's not + * @dev making his output up to suit himself. Reggie provides Vera a public key + * @dev to which he knows the secret key. Each time Vera provides a seed to + * @dev Reggie, he gives back a value which is computed completely + * @dev deterministically from the seed and the secret key. + * + * @dev Reggie provides a proof by which Vera can verify that the output was + * @dev correctly computed once Reggie tells it to her, but without that proof, + * @dev the output is indistinguishable to her from a uniform random sample + * @dev from the output space. + * + * @dev The purpose of this contract is to make it easy for unrelated contracts + * @dev to talk to Vera the verifier about the work Reggie is doing, to provide + * @dev simple access to a verifiable source of randomness. It ensures 2 things: + * @dev 1. The fulfillment came from the VRFCoordinator + * @dev 2. The consumer contract implements fulfillRandomWords. + * ***************************************************************************** + * @dev USAGE + * + * @dev Calling contracts must inherit from VRFConsumerBase, and can + * @dev initialize VRFConsumerBase's attributes in their constructor as + * @dev shown: + * + * @dev contract VRFConsumer { + * @dev constructor(, address _vrfCoordinator, address _link) + * @dev VRFConsumerBase(_vrfCoordinator) public { + * @dev + * @dev } + * @dev } + * + * @dev The oracle will have given you an ID for the VRF keypair they have + * @dev committed to (let's call it keyHash). Create subscription, fund it + * @dev and your consumer contract as a consumer of it (see VRFCoordinatorInterface + * @dev subscription management functions). + * @dev Call requestRandomWords(keyHash, subId, minimumRequestConfirmations, + * @dev callbackGasLimit, numWords), + * @dev see (VRFCoordinatorInterface for a description of the arguments). + * + * @dev Once the VRFCoordinator has received and validated the oracle's response + * @dev to your request, it will call your contract's fulfillRandomWords method. + * + * @dev The randomness argument to fulfillRandomWords is a set of random words + * @dev generated from your requestId and the blockHash of the request. + * + * @dev If your contract could have concurrent requests open, you can use the + * @dev requestId returned from requestRandomWords to track which response is associated + * @dev with which randomness request. + * @dev See "SECURITY CONSIDERATIONS" for principles to keep in mind, + * @dev if your contract could have multiple requests in flight simultaneously. + * + * @dev Colliding `requestId`s are cryptographically impossible as long as seeds + * @dev differ. + * + * ***************************************************************************** + * @dev SECURITY CONSIDERATIONS + * + * @dev A method with the ability to call your fulfillRandomness method directly + * @dev could spoof a VRF response with any random value, so it's critical that + * @dev it cannot be directly called by anything other than this base contract + * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method). + * + * @dev For your users to trust that your contract's random behavior is free + * @dev from malicious interference, it's best if you can write it so that all + * @dev behaviors implied by a VRF response are executed *during* your + * @dev fulfillRandomness method. If your contract must store the response (or + * @dev anything derived from it) and use it later, you must ensure that any + * @dev user-significant behavior which depends on that stored value cannot be + * @dev manipulated by a subsequent VRF request. + * + * @dev Similarly, both miners and the VRF oracle itself have some influence + * @dev over the order in which VRF responses appear on the blockchain, so if + * @dev your contract could have multiple VRF requests in flight simultaneously, + * @dev you must ensure that the order in which the VRF responses arrive cannot + * @dev be used to manipulate your contract's user-significant behavior. + * + * @dev Since the block hash of the block which contains the requestRandomness + * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful + * @dev miner could, in principle, fork the blockchain to evict the block + * @dev containing the request, forcing the request to be included in a + * @dev different block with a different hash, and therefore a different input + * @dev to the VRF. However, such an attack would incur a substantial economic + * @dev cost. This cost scales with the number of blocks the VRF oracle waits + * @dev until it calls responds to a request. It is for this reason that + * @dev that you can signal to an oracle you'd like them to wait longer before + * @dev responding to the request (however this is not enforced in the contract + * @dev and so remains effective only in the case of unmodified oracle software). + */ +abstract contract VRFConsumerBaseV2 { + error OnlyCoordinatorCanFulfill(address have, address want); + address private immutable vrfCoordinator; + + /** + * @param _vrfCoordinator address of VRFCoordinator contract + */ + constructor(address _vrfCoordinator) { + vrfCoordinator = _vrfCoordinator; + } + + /** + * @notice fulfillRandomness handles the VRF response. Your contract must + * @notice implement it. See "SECURITY CONSIDERATIONS" above for important + * @notice principles to keep in mind when implementing your fulfillRandomness + * @notice method. + * + * @dev VRFConsumerBaseV2 expects its subcontracts to have a method with this + * @dev signature, and will call it once it has verified the proof + * @dev associated with the randomness. (It is triggered via a call to + * @dev rawFulfillRandomness, below.) + * + * @param requestId The Id initially returned by requestRandomness + * @param randomWords the VRF output expanded to the requested number of words + */ + function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual; + + // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF + // proof. rawFulfillRandomness then calls fulfillRandomness, after validating + // the origin of the call + function rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external { + if (msg.sender != vrfCoordinator) { + revert OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator); + } + fulfillRandomWords(requestId, randomWords); + } +} diff --git a/dapps/evm2/randomness/contracts/vrf/interfaces/VRFCoordinatorV2Interface.sol b/dapps/evm2/randomness/contracts/vrf/interfaces/VRFCoordinatorV2Interface.sol new file mode 100644 index 0000000..f5afd5f --- /dev/null +++ b/dapps/evm2/randomness/contracts/vrf/interfaces/VRFCoordinatorV2Interface.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +// Reference: https://github.com/smartcontractkit/chainlink/blob/master/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol + +interface VRFCoordinatorV2Interface { + /** + * @notice Get configuration relevant for making requests + * @return minimumRequestConfirmations global min for request confirmations + * @return maxGasLimit global max for request gas limit + * @return s_provingKeyHashes list of registered key hashes + */ + function getRequestConfig() external view returns (uint16, uint32, bytes32[] memory); + + /** + * @notice Request a set of random words. + * @param keyHash - Corresponds to a particular oracle job which uses + * that key for generating the VRF proof. Different keyHash's have different gas price + * ceilings, so you can select a specific one to bound your maximum per request cost. + * @param subId - The ID of the VRF subscription. Must be funded + * with the minimum subscription balance required for the selected keyHash. + * @param minimumRequestConfirmations - How many blocks you'd like the + * oracle to wait before responding to the request. See SECURITY CONSIDERATIONS + * for why you may want to request more. The acceptable range is + * [minimumRequestBlockConfirmations, 200]. + * @param callbackGasLimit - How much gas you'd like to receive in your + * fulfillRandomWords callback. Note that gasleft() inside fulfillRandomWords + * may be slightly less than this amount because of gas used calling the function + * (argument decoding etc.), so you may need to request slightly more than you expect + * to have inside fulfillRandomWords. The acceptable range is + * [0, maxGasLimit] + * @param numWords - The number of uint256 random values you'd like to receive + * in your fulfillRandomWords callback. Note these numbers are expanded in a + * secure way by the VRFCoordinator from a single random value supplied by the oracle. + * @return requestId - A unique identifier of the request. Can be used to match + * a request to a response in fulfillRandomWords. + */ + function requestRandomWords( + bytes32 keyHash, + uint64 subId, + uint16 minimumRequestConfirmations, + uint32 callbackGasLimit, + uint32 numWords + ) external returns (uint256 requestId); + + /** + * @notice Create a VRF subscription. + * @return subId - A unique subscription id. + * @dev You can manage the consumer set dynamically with addConsumer/removeConsumer. + * @dev Note to fund the subscription, use transferAndCall. For example + * @dev LINKTOKEN.transferAndCall( + * @dev address(COORDINATOR), + * @dev amount, + * @dev abi.encode(subId)); + */ + function createSubscription() external returns (uint64 subId); + + /** + * @notice Get a VRF subscription. + * @param subId - ID of the subscription + * @return balance - LINK balance of the subscription in juels. + * @return reqCount - number of requests for this subscription, determines fee tier. + * @return owner - owner of the subscription. + * @return consumers - list of consumer address which are able to use this subscription. + */ + function getSubscription( + uint64 subId + ) external view returns (uint96 balance, uint64 reqCount, address owner, address[] memory consumers); + + /** + * @notice Request subscription owner transfer. + * @param subId - ID of the subscription + * @param newOwner - proposed new owner of the subscription + */ + function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external; + + /** + * @notice Request subscription owner transfer. + * @param subId - ID of the subscription + * @dev will revert if original owner of subId has + * not requested that msg.sender become the new owner. + */ + function acceptSubscriptionOwnerTransfer(uint64 subId) external; + + /** + * @notice Add a consumer to a VRF subscription. + * @param subId - ID of the subscription + * @param consumer - New consumer which can use the subscription + */ + function addConsumer(uint64 subId, address consumer) external; + + /** + * @notice Remove a consumer from a VRF subscription. + * @param subId - ID of the subscription + * @param consumer - Consumer to remove from the subscription + */ + function removeConsumer(uint64 subId, address consumer) external; + + /** + * @notice Cancel a subscription + * @param subId - ID of the subscription + * @param to - Where to send the remaining LINK to + */ + function cancelSubscription(uint64 subId, address to) external; + + /* + * @notice Check to see if there exists a request commitment consumers + * for all consumers and keyhashes for a given sub. + * @param subId - ID of the subscription + * @return true if there exists at least one unfulfilled request for the subscription, false + * otherwise. + */ + function pendingRequestExists(uint64 subId) external view returns (bool); +} diff --git a/dapps/evm2/randomness/migrations/1_initial_migration.js b/dapps/evm2/randomness/migrations/1_initial_migration.js new file mode 100644 index 0000000..877c9fa --- /dev/null +++ b/dapps/evm2/randomness/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +const Migrations = artifacts.require("Migrations"); + +module.exports = deployer => { + deployer.deploy(Migrations); +}; diff --git a/dapps/evm2/randomness/migrations/2_deploy_contracts.js b/dapps/evm2/randomness/migrations/2_deploy_contracts.js new file mode 100644 index 0000000..4182b16 --- /dev/null +++ b/dapps/evm2/randomness/migrations/2_deploy_contracts.js @@ -0,0 +1,42 @@ +require('dotenv').config() +const { Web3 } = require('web3'); +const BN = require('bn.js'); + +console.log('deploying...'); +module.exports = async function (deployer) { + let providerInstance; + let web3; + if (deployer.network == "moonbase") { + console.log('deploy_contracts to Moonbase Alpha'); + + const RandomNumber = artifacts.require("../build/contracts/RandomNumber"); + const Flipper = artifacts.require("../build/contracts/Flipper"); + + providerInstance = new Web3.providers.WebsocketProvider(process.env.MOONBASE_BLASTAPI_ENDPOINT, {}, { delay: 500, autoReconnect: true, maxAttempts: 100 }); + console.log('providerInstance: ', providerInstance); + + web3 = new Web3(providerInstance); + console.log('web3.currentProvider: ', web3.currentProvider); + // must be at least 1 ether since it must be greater than `REQUEST_DEPOSIT_AMOUNT` + deployer.deploy(RandomNumber, { value: web3.utils.toWei('1', 'ether') }); + deployer.deploy(Flipper, false); + } else if (deployer.network == "sepolia") { + console.log('deploy_contracts to Chainlink Sepolia'); + + const VRFD20 = artifacts.require("../build/contracts/VRFD20"); + + providerInstance = new Web3.providers.WebsocketProvider(process.env.CHAINLINK_SEPOLIA_ENDPOINT, {}, { delay: 500, autoReconnect: true, maxAttempts: 100 }); + console.log('providerInstance: ', providerInstance); + web3 = new Web3(providerInstance); + console.log('web3.currentProvider: ', web3.currentProvider); + // **Important** It is necessary to add the deployed address as the + // consumer address and fund the subscription id otherwise it will not + // be possible to roll the dice and you will get error + // `CALL_EXCEPTION Dice not rolled` or similar + const subscriptionId = 3350; // https://vrf.chain.link/ + // wants 128620983229604640 wei + const value = web3.utils.toWei('0.000001', 'ether'); + const amount = new BN(value, 10); + deployer.deploy(VRFD20, subscriptionId, { value: amount }); + } +}; diff --git a/dapps/evm2/randomness/package.json b/dapps/evm2/randomness/package.json new file mode 100644 index 0000000..2ec6446 --- /dev/null +++ b/dapps/evm2/randomness/package.json @@ -0,0 +1,15 @@ +{ + "name": "flipper", + "version": "1.0.0", + "dependencies": { + "@truffle/contract": "^4.6.25", + "@truffle/hdwallet-provider": "^2.1.12", + "alchemy-sdk": "^2.9.1", + "bn.js": "^5.2.1", + "dotenv": "^16.3.1", + "moonbeam-truffle-plugin": "github:PureStake/moonbeam-truffle-plugin", + "truffle": "^5.10.0", + "truffle-plugin-verify": "^0.6.3", + "web3": "^4.0.1" + } +} diff --git a/dapps/evm2/randomness/scripts/demo-chainlink-vrf-on-ethereum-sepolia.js b/dapps/evm2/randomness/scripts/demo-chainlink-vrf-on-ethereum-sepolia.js new file mode 100644 index 0000000..b7489ee --- /dev/null +++ b/dapps/evm2/randomness/scripts/demo-chainlink-vrf-on-ethereum-sepolia.js @@ -0,0 +1,101 @@ +const dotenv = require('dotenv'); +// note: change the below to '../.env' if run from in the ./scripts directory +// otherwise get error `TypeError: Cannot read properties of undefined (reading 'toHexString')` +// since unable to load variabels from .env file +dotenv.config({path : './.env'}); +// https://docs.alchemy.com/reference/alchemy-sdk-api-surface-overview#api-surface +const { Network, Alchemy, Contract, Utils, Wallet } = require('alchemy-sdk'); +const VRFD20ContractBuilt = require("../build/contracts/VRFD20.json"); + +// https://www.npmjs.com/package/alchemy-sdk +const config = { + apiKey: process.env.ALCHEMY_API_KEY, + network: Network.ETH_SEPOLIA, +}; + +const alchemyProvider = new Alchemy(config); +console.log('alchemyProvider', alchemyProvider); + +// Listen to all new pending transactions +// Note: Alchemy wraps ethers API +// https://docs.alchemy.com/reference/alchemy-sdk-api-surface-overview#alchemy-websockets +// https://docs.ethers.org/v6/api/providers/#WebSocketProvider +alchemyProvider.ws.on( + { + method: "alchemy_pendingTransactions", + fromAddress: "0x1dd907ABb024E17d196de0D7Fe8EB507b6cCaae7" + }, + (res) => console.log('detected pending tx from account:', res), +); + +// Signer +const signer = new Wallet(process.env.MOONBASE_PRIVATE_KEY, alchemyProvider); +console.log('signer', signer); + +// Contract +// const VRFD20DeployedAtAddress = '0xe22cdfA9d8C8e942B498696ef54584426d2f5Dd6'; +const VRFD20DeployedAtAddress = '0xE265f9a30c72A78C4b89Fc2e2C60e9327704Fa5e'; +// https://docs.ethers.org/v5/api/contract/example/#example-erc-20-contract--connecting-to-a-contract +const VRFD20ContractRW = new Contract(VRFD20DeployedAtAddress, VRFD20ContractBuilt.abi, signer); +// console.log('VRFD20ContractRW', VRFD20ContractRW); + +// VRFD20ContractRW.on('DiceRolled', +// (res) => console.log('detected DiceRolled:', res)); + +// VRFD20ContractRW.on('DiceLanded', +// (res) => console.log('detected DiceLanded:', res)); + +const setAsyncTimeout = (cb, timeout = 0) => new Promise(resolve => { + setTimeout(() => { + cb(); + resolve(); + }, timeout); +}); + +const getRolledValueForPlayer = async () => { + // Get the latest block + let latestBlock = await alchemyProvider.core.getBlockNumber(); + console.log('latestBlock', latestBlock); + + const valueRolled = await VRFD20ContractRW + .getRolledValueForPlayer(signer.address); + console.log(`The valueRolled by ${signer.address} is: `, valueRolled.toString()); +} + +// Example of using the call method +const main = async () => { + const ethersProvider = await alchemyProvider.config.getProvider(); + // console.log('ethersProvider: ', ethersProvider.formatter); + + // Interact with VRFD20 + const s_owner = await VRFD20ContractRW.s_owner(); + console.log("The s_owner is: ", s_owner); + + // let gasLimit = await VRFD20ContractRW.estimateGas.rollDice(signer.address); + const overrides = { + from: signer.address, + // gasLimit: gasLimit, + gasLimit: 600000, + // gasPrice: 100000, + maxPriorityFeePerGas: 2, + }; + // Important: Must have added the latest VRFD20 contract as an approved consumer + // contract so it can use the subscription balance when requesting randomness + // https://vrf.chain.link/ + // tx 0xc1ccfd1ea081c846fa3912cb1f610c23fab3ef3ddc5151cea6fa8bd2297e9b84 + const requestId = await VRFD20ContractRW.rollDice(signer.address, overrides); + console.log("The requestId is: ", requestId.value.toString()); + + // Get the latest block + let latestBlock = await alchemyProvider.core.getBlockNumber(); + console.log('latestBlock', latestBlock); + + // Wait a few blocks before getting the rolled value for a player + await setAsyncTimeout(async () => { + console.log('getRolledValueForPlayer'); + + await getRolledValueForPlayer(); + }, 60000); +}; + +main(); diff --git a/dapps/evm2/randomness/scripts/demo-moonbeam-vrf-on-moonbase-alpha.js b/dapps/evm2/randomness/scripts/demo-moonbeam-vrf-on-moonbase-alpha.js new file mode 100644 index 0000000..334498e --- /dev/null +++ b/dapps/evm2/randomness/scripts/demo-moonbeam-vrf-on-moonbase-alpha.js @@ -0,0 +1,116 @@ +require('dotenv').config({ path: '../.env'}) +const ethers = require('ethers'); +const { Wallet } = require('ethers'); +const BN = require('bn.js'); + +// https://docs.moonbeam.network/builders/build/eth-api/libraries/ethersjs/ +// Note: ethers v6.6.2 not yet supported by Moonbase Alpha, use v5 +// https://docs.ethers.org/v5/api/providers/#WebSocketProvider +// so use `ethers.providers.JsonRpcProvider` instead of +// `ethers.JsonRpcProvider` +// const providerRPCMoonbaseAlphaConfig = { +// moonbase: { +// name: 'moonbase-alpha', +// rpc: 'https://rpc.api.moonbase.moonbeam.network', +// chainId: 1287, // 0x507 in hex, +// }, +// }; +// const providerMoonbaseAlphaRPC = new ethers.providers.JsonRpcProvider( +// providerRPCMoonbaseAlphaConfig.moonbase.rpc, +// { +// chainId: providerRPCMoonbaseAlphaConfig.moonbase.chainId, +// name: providerRPCMoonbaseAlphaConfig.moonbase.name, +// } +// ); +// console.log('moonbase alpha provider RPC: ', providerMoonbaseAlphaRPC); + +const providerMoonbaseAlphaWS = new ethers.providers.WebSocketProvider( + // process.env.MOONBASE_BLASTAPI_ENDPOINT, // need auth for this endpoint + "wss://moonbeam-alpha.api.onfinality.io/public-ws", + { + name: "moonbase-alphanet", // or "moonbase-alpha" + chainId: 1287, // 0x507 in hex, + }, +); +console.log('moonbase alpha provider WS: ', providerMoonbaseAlphaWS); + +// Signer +const signer = new Wallet(process.env.MOONBASE_PRIVATE_KEY, providerMoonbaseAlphaWS); +console.log('signer', signer); + +const RandomNumberContractBuilt = require('../build/contracts/RandomNumber.json'); + +const setAsyncTimeout = (cb, timeout = 0) => new Promise(resolve => { + setTimeout(() => { + cb(); + resolve(); + }, timeout); +}); + +const main = async () => { + const contractAddressMoonbaseAlpha = '0x4027755C05514421fe00f4Fde0bD3F8475ce8A6b'; + const randomNumberInstance = new ethers.Contract( + contractAddressMoonbaseAlpha, RandomNumberContractBuilt.abi, signer); + console.log('randomNumberInstance: ', randomNumberInstance); + const fulfillmentFee = await randomNumberInstance.MIN_FEE.call(); + console.log('fulfillmentFee: ', fulfillmentFee.toString()); + console.log('fulfillmentFee is bn', BN.isBN(fulfillmentFee)); + + console.log('x: ', ethers.utils.formatEther(fulfillmentFee)); + + let roller = '0x1dd907ABb024E17d196de0D7Fe8EB507b6cCaae7'; + let res = await randomNumberInstance.requestRandomness( + roller, + { + from: signer.address, + gasLimit: 600000, + maxPriorityFeePerGas: 2, + value: fulfillmentFee + } + ); + console.log('res: ', await res); + // debugging receipt + console.log('res: ', await res.wait()); + + const requestId = await randomNumberInstance.requestId.call(); + console.log('requestId: ', requestId.toString()); + // Check status of request id from the randomness precompile + // https://github.com/PureStake/moonbeam/blob/master/precompiles/randomness/Randomness.sol#L96 + let requestStatus = await randomNumberInstance.getRequestStatus.call(); + console.log('requestStatus: ', requestStatus.toString()); + + // Wait for at least MIN_VRF_BLOCKS_DELAY but less than MAX_VRF_BLOCKS_DELAY + // https://github.com/PureStake/moonbeam/blob/master/precompiles/randomness/Randomness.sol#L13 + // https://github.com/PureStake/moonbeam/blob/master/precompiles/randomness/Randomness.sol#L15 + const MIN_VRF_BLOCKS_DELAY = await randomNumberInstance.VRF_BLOCKS_DELAY.call(); + console.log('MIN_VRF_BLOCKS_DELAY: ', MIN_VRF_BLOCKS_DELAY); + + let currentBlockNumber = await providerMoonbaseAlphaWS.getBlockNumber(); + console.log('currentBlockNumber: ', currentBlockNumber.toString()); + + // assert.equal(requestStatus, 1, 'should still be pending'); // where 1 in enum is 'PENDING' + + // Wait a few blocks before fulfilling the request + // by calling the consumer contract method fulfillRandomWords + await setAsyncTimeout(async () => { + console.log('fulfillRequest'); + + // Error: insufficient funds for gas * price + value + await randomNumberInstance.fulfillRequest( + { + from: signer.address, + gasLimit: 600000, + // gasPrice: 600000, + maxPriorityFeePerGas: 2, + } + ); + }, 10000); + + // requestStatus = await randomNumberInstance.getRequestStatus.call(); + // console.log('requestStatus: ', requestStatus.toString()); + + // const random = await randomNumberInstance.random.call(); + // console.log('random number: ', random[0]); +} + +main(); diff --git a/dapps/evm2/randomness/scripts/package.json b/dapps/evm2/randomness/scripts/package.json new file mode 100644 index 0000000..11efcf1 --- /dev/null +++ b/dapps/evm2/randomness/scripts/package.json @@ -0,0 +1,18 @@ +{ + "name": "flipper", + "version": "1.0.0", + "description": "", + "main": "demo-chainlink-vrf-on-ethereum-sepolia.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "ltfschoen", + "license": "MIT", + "dependencies": { + "alchemy-sdk": "^2.9.1", + "bn.js": "^5.2.1", + "dotenv": "^16.3.1", + "ethers": "^5.6.1", + "web3": "^4.0.2" + } +} diff --git a/dapps/evm2/randomness/test/test_ChainlinkVRF.js b/dapps/evm2/randomness/test/test_ChainlinkVRF.js new file mode 100644 index 0000000..9bd18a5 --- /dev/null +++ b/dapps/evm2/randomness/test/test_ChainlinkVRF.js @@ -0,0 +1,45 @@ +require('dotenv').config() +const { Web3 } = require('web3'); +const BN = require('bn.js'); + +// Uses Mocha and Ganache +const VRFD20 = artifacts.require("../build/contracts/VRFD20"); + +console.log('test_ChainlinkVRF'); + +let providerInstance = new Web3.providers.WebsocketProvider(process.env.CHAINLINK_SEPOLIA_ENDPOINT, {}, { delay: 500, autoReconnect: true, maxAttempts: 100 }); +console.log('providerInstance: ', providerInstance); +let web3 = new Web3(providerInstance); +console.log('web3.currentProvider: ', web3.currentProvider); + +contract('VRFD20', accounts => { + console.log('accounts: ', accounts); + let vrfChainlinkInstance; + + beforeEach(async () => { + // error `Uncaught Error: invalid 1st argument: block_number value was not valid block tag or block number` + + console.log('beforeEach'); + + vrfChainlinkInstance = await VRFD20.deployed(); + console.log('vrfChainlinkInstance.address:', vrfChainlinkInstance.address); + }); + + it("requests random VRF", async () => { + try { + let s_subscriptionId = await vrfChainlinkInstance.s_subscriptionId.call(); + console.log('s_subscriptionId: ', s_subscriptionId.toString()); + console.log('s_subscriptionId is bn', BN.isBN(s_subscriptionId)); + console.log('accounts', accounts); + + let s_owner = await vrfChainlinkInstance.s_owner.call(); + console.log('s_owner: ', s_owner.toString()); + + let roller = '0x1dd907ABb024E17d196de0D7Fe8EB507b6cCaae7'; + requestId = await randomNumberInstance.rollDice(roller, { from: accounts[0], value: fulfillmentFee }); + console.log('requestId: ', requestId); + } catch (e) { + console.log('error in requests random VRF: ', e); + } + }); +}); diff --git a/dapps/evm2/randomness/test/test_Flipper.js b/dapps/evm2/randomness/test/test_Flipper.js new file mode 100644 index 0000000..cd262e5 --- /dev/null +++ b/dapps/evm2/randomness/test/test_Flipper.js @@ -0,0 +1,50 @@ +require('dotenv').config() +const { Web3 } = require('web3'); +const BN = require('bn.js'); + +// Uses Mocha and Ganache +const Flipper = artifacts.require("../contracts/lib/Flipper"); + +console.log('test_Flipper'); + +let providerInstance = new Web3.providers.WebsocketProvider(process.env.MOONBASE_BLASTAPI_ENDPOINT, {}, { delay: 500, autoReconnect: true, maxAttempts: 100 }); +console.log('providerInstance: ', providerInstance); + +contract('Flipper', accounts => { + console.log('accounts: ', accounts); + + let flipperInstance; + // https://docs.web3js.org/api/web3-utils/function/toWei + const initValue = false; + beforeEach(async () => { + console.log('beforeEach'); + + flipperInstance = await Flipper.deployed(); //.new(initValue, { from: accounts[0] }); + // console.log('flipperInstance.address:', flipperInstance.address); + // delay each test to simulate throttle that isn't available in truffle + // setTimeout(function(){ done(); }, 5000); + // await flipperInstance.setRandomNumberContractAddress(randomNumberInstance.address); + }); + // Check stored value + it("checks stored value", async () => { + const value = await flipperInstance.get.call(); + console.log('value:', value); + assert.equal(value, initValue, 'value stored does not match initial value'); + }); + + // Set flipped value of existing value + it("should flip the value", async () => { + try { + const previousValue = await flipperInstance.get.call(); + const oneWei = Web3.utils.toWei('1', 'wei'); + const value = new BN(oneWei, 10); // 1 wei (so do not use up all my testnet DEV tokens) + // do not use `.call` when doing state changes to blockchain + await flipperInstance.flip({ from: accounts[0], value: value }); + // setTimeout(function(){ done(); }, 5000); + const newValue = await flipperInstance.get.call(); + assert.notEqual(previousValue, newValue, 'newValue is not opposite of previousValue'); + } catch (e) { + console.log('error in flip: ', e); + } + }); +}); diff --git a/dapps/evm2/randomness/test/test_MoonbaseVRF.js b/dapps/evm2/randomness/test/test_MoonbaseVRF.js new file mode 100644 index 0000000..5eade82 --- /dev/null +++ b/dapps/evm2/randomness/test/test_MoonbaseVRF.js @@ -0,0 +1,165 @@ +require('dotenv').config() +const { Web3 } = require('web3'); +const BN = require('bn.js'); + +// Uses Mocha and Ganache +const Randomness = artifacts.require("../build/contracts/Randomness"); +const RandomnessConsumer = artifacts.require("../build/contracts/RandomnessConsumer"); +const RandomNumber = artifacts.require("../contracts/lib/RandomNumber"); + +console.log('test_MoonbaseVRF'); + +let providerInstance = new Web3.providers.WebsocketProvider(process.env.MOONBASE_BLASTAPI_ENDPOINT, {}, { delay: 500, autoReconnect: true, maxAttempts: 100 }); +console.log('providerInstance: ', providerInstance); +let web3 = new Web3(providerInstance); +// when using BlastAPI WSS endpoint I get error `TypeError: Cannot create property 'gasLimit' on string"` +// https://github.com/web3/web3.js/issues/3573 +console.log('web3.currentProvider: ', web3.currentProvider); +// Randomness.setProvider(providerInstance); +// RandomnessConsumer.setProvider(providerInstance); +// RandomNumber.setProvider(providerInstance); + +// advanceBlock = () => { +// return new Promise((resolve, reject) => { +// web3.currentProvider.send({ +// jsonrpc: '2.0', +// method: 'evm_mine', +// id: new Date().getTime() +// }, (err, result) => { +// console.log('result: ', result); +// console.log('err: ', err); +// if (err) { return reject(err) } +// const newBlockHash = web3.eth.getBlock('latest').hash; +// console.log('newBlockHash: ', newBlockHash); + +// return resolve(newBlockHash); +// }) +// }) +// } + +contract('RandomNumber', accounts => { + console.log('accounts: ', accounts); + let randomnessInstance; + let randomNumberInstance; + let fulfillmentFee; + let refundAddress; + let gas; + let gasLimit; + let gasPrice; + let currentBlockNumber; + let nextBlockNumber; + // https://github.com/PureStake/moonbeam/blob/master/precompiles/randomness/Randomness.sol#L17C43-L17C62 + // https://docs.web3js.org/api/web3-utils/function/toWei + const requiredDeposit = Web3.utils.toWei('1', 'ether'); + const blockTimeout = 1000000; + const initValue = false; + beforeEach(async () => { + console.log('beforeEach'); + randomnessInstance = await Randomness.at("0x0000000000000000000000000000000000000809"); + // console.log('randomnessInstance.address:', randomnessInstance.address); + + // RandomnessConsumer.link(randomnessInstance); + // RandomNumber.link(randomnessInstance); + + // gas = Web3.utils.toWei('1000000', 'wei'); + // gasLimit = Web3.utils.toWei('600000', 'wei'); + // gasPrice = Web3.utils.toWei('2000000', 'wei'); + // gas = Web3.utils.toHex(70000); + // gasLimit = Web3.utils.toHex(600000); // gwei + // gasPrice = Web3.utils.toHex(21000); + + // Create contract with 1 Ether (contract must be payable) + randomNumberInstance = await RandomNumber.deployed(); //.new({ from: accounts[0], value: requiredDeposit }); + // randomNumberInstance = await RandomNumber.new( + // { from: accounts[0], value: requiredDeposit, + // gas: gas, gasLimit: gasLimit, gasPrice: gasPrice, syncWithContext: true } + // ); + console.log('randomNumberInstance.address:', randomNumberInstance.address); + + // delay each test to simulate throttle that isn't available in truffle + // setTimeout(function(){ done(); }, 5000); + }); + + it("requests randomness", async () => { + try { + fulfillmentFee = await randomNumberInstance.MIN_FEE.call(); + console.log('fulfillmentFee: ', fulfillmentFee.toString()); + console.log('fulfillmentFee is bn', BN.isBN(fulfillmentFee)); + console.log('accounts', accounts); + + // console.log('web3.currentProvider: ', web3.currentProvider); + // do not use `.call` when doing state changes to blockchain + // gas = Web3.utils.toWei('1000000', 'wei'); + // gasLimit = Web3.utils.toWei('600000', 'wei'); + // gasPrice = Web3.utils.toWei('2000000', 'wei'); + gas = Web3.utils.toHex(150000); + gasLimit = Web3.utils.toHex(600000); + gasPrice = Web3.utils.toHex(21000); + let roller = '0x1dd907ABb024E17d196de0D7Fe8EB507b6cCaae7'; + refundAddress = await randomNumberInstance.requestRandomness(roller, { from: accounts[0], value: fulfillmentFee }); + // refundAddress = await randomNumberInstance.requestRandomness( + // { + // from: accounts[0], + // value: fulfillmentFee, + // gas: gas, gasLimit: gasLimit, gasPrice: gasPrice, + // syncWithContext: true + // } + // ); + // console.log('refundAddress: ', refundAddress); + + const requestId = await randomNumberInstance.requestId.call(); + console.log('requestId: ', requestId); + // Check status of request id from the randomness precompile + // https://github.com/PureStake/moonbeam/blob/master/precompiles/randomness/Randomness.sol#L96 + const requestStatus = await randomNumberInstance.getRequestStatus.call(); + console.log('requestStatus: ', requestStatus.toString()); + + // Wait for at least MIN_VRF_BLOCKS_DELAY but less than MAX_VRF_BLOCKS_DELAY + // https://github.com/PureStake/moonbeam/blob/master/precompiles/randomness/Randomness.sol#L13 + // https://github.com/PureStake/moonbeam/blob/master/precompiles/randomness/Randomness.sol#L15 + const MIN_VRF_BLOCKS_DELAY = await randomNumberInstance.VRF_BLOCKS_DELAY.call(); + console.log('MIN_VRF_BLOCKS_DELAY: ', MIN_VRF_BLOCKS_DELAY); + // let currentBlock = await web3.eth.getBlock("latest"); + currentBlockNumber = await web3.eth.getBlockNumber(); + console.log('currentBlockNumber: ', currentBlockNumber.toString()); + // remove 'n' character from end of blocknumber + currentBlockNumber = currentBlockNumber.toString().replace(/[^0-9.]/g, ''); + let firstBlockNumber = currentBlockNumber; + console.log('firstBlockNumber: ', firstBlockNumber); + assert.equal(requestStatus, 1, 'should still be pending'); // where 1 in enum is 'PENDING' + // evm_mine not defined, since can only do on Ganache not live testnet + // for (i=0; i Please change this to your own Private Key with funds +// NOTE: Do not store your private key in plaintext files +// this is only for demostration purposes only +const privateKeyMoonbase = process.env.MOONBASE_PRIVATE_KEY; + +const defaultMoonbaseEndpoint = 'https://rpc.api.moonbase.moonbeam.network'; +const defaultMoonbaseNetworkId = 1287; + +module.exports = { + networks: { + // Moonbeam Development Network + dev: { + provider: () => { + if (!privateKeyDev.trim()) { + throw new Error( + 'Please enter a private key with funds, you can use the default one' + ); + } + let args = { + privateKeys: [privateKeyDev], + providerOrUrl: 'http://localhost:9944/', + }; + return new HDWalletProvider(args); + }, + network_id: 1281, + }, + // Moonbase Alpha TestNet + moonbase: { + provider: () => { + if (!privateKeyMoonbase.trim()) { + throw new Error( + 'Please enter a private key with funds to send transactions to TestNet' + ); + } + if (privateKeyDev == privateKeyMoonbase) { + throw new Error( + 'Please change the private key used for Moonbase to your own with funds' + ); + } + // First argument to new HDWalletProvider() must be a mnemonic phrase, + // a single private key, or a list of private keys. + // Expected private key is a Uint8Array with length 32 + // https://github.com/trufflesuite/truffle/tree/develop/packages/hdwallet-provider + let args = { + privateKeys: [privateKeyMoonbase], + // providerOrUrl: defaultMoonbaseEndpoint, + providerOrUrl: process.env.MOONBASE_BLASTAPI_ENDPOINT, + // pollingInterval: 20000, // default only 4000, modifying doesn't help + retryTimeout: 10000, // default 400, modifying doesn't help + }; + return new HDWalletProvider(args); + }, + // Try to overcome error, changes below might help pass more tests but not as good as + // using BlastApi + // `Uncaught Error: PollingBlockTracker - encountered an error while attempting to update latest block: + // undefined + // https://ethereum.stackexchange.com/questions/97773/truffle-migrate-rinkeby-error-pollingblocktracker-encountered-an-error-whil + // confirmations: 10, + // timeoutBlocks: 900000, + // skipDryRun: true, + websocket: true, + // gas: 5000000, + // gasPrice: 50000000000, // 50 Gwei + // gasLimit is required when using `WebsocketsProvider` instead of `HttpProvider` + // else get error `TypeError: Cannot create property 'gasLimit' on string '0x467b05'`. + // if this error appears even with the property set, then try changing to a different + // internet connection + gasLimit: 5000000, + networkCheckTimeout: 1000000000, + // deploymentPollingInterval: 8000, + network_id: defaultMoonbaseNetworkId, + }, + // Chainlink Sepolia + sepolia: { + provider: () => { + let args = { + privateKeys: [privateKeyMoonbase], + providerOrUrl: process.env.CHAINLINK_SEPOLIA_ENDPOINT, + retryTimeout: 10000, + }; + return new HDWalletProvider(args); + }, + websocket: true, + gasLimit: 5000000, + networkCheckTimeout: 1000000000, + network_id: 11155111, + }, + // faucet for SBY https://docs.astar.network/docs/build/environment/faucet + astar_shibuya: { + provider: () => { + let args = { + privateKeys: [privateKeyAstarShibuya], + providerOrUrl: 'https://evm.shibuya.astar.network', + }; + return new HDWalletProvider(args); + }, + network_id: 81, + }, + }, + mocha: { + timeout: 1000000000, // milliseconds + enableTimeouts: false, + bail: false, + retries: 100, + }, + // Solidity >=0.8.3 Compiler + compilers: { + solc: { + version: '>=0.8.3', + settings: { + // Fixes `"Migrations" -- evm error: InvalidCode(Opcode(95))` + // https://docs.moonbeam.network/tutorials/eth-api/truffle-start-to-end/ + // https://docs.moonbeam.network/builders/build/eth-api/dev-env/remix/ + evmVersion: 'london', + }, + }, + }, + // Moonbeam Truffle Plugin & Truffle Plugin for Verifying Smart Contracts + plugins: ['moonbeam-truffle-plugin', 'truffle-plugin-verify'], + // https://docs.moonbeam.network/builders/build/eth-api/verify-contracts/etherscan-plugins/#using-the-truffle-verify-plugin + api_keys: { + moonscan: process.env.MOONSCAN_API_KEY + } +}; diff --git a/dapps/evm2/randomness/tx.log b/dapps/evm2/randomness/tx.log new file mode 100644 index 0000000..23b3dcf --- /dev/null +++ b/dapps/evm2/randomness/tx.log @@ -0,0 +1,155 @@ +# truffle migrate --reset --compile-all --network moonbase + +Compiling your contracts... +=========================== +✓ Fetching solc version list from solc-bin. Attempt #1 +> Compiling ./contracts/Flipper.sol +> Compiling ./contracts/Migrations.sol +> Artifacts written to /app/dapps/evm2/randomness/build/contracts +> Compiled successfully using: + - solc: 0.8.20+commit.a1b79de6.Emscripten.clang + + +Starting migrations... +====================== +> Network name: 'moonbase' +> Network id: 1287 +> Block gas limit: 15000000 (0xe4e1c0) + + +1_initial_migration.js +====================== + + Deploying 'Migrations' + ---------------------- + > transaction hash: 0x298584422c67c41b93f62adb5c828fd3690d8bc6ccbf37d82dcab78e12fad8cd + > Blocks: 1 Seconds: 24 + > contract address: 0xE691d9f144Ae51139f2f8571CA3b0cd17Db8D5A0 + > block number: 4583829 + > block timestamp: 1687339224 + > account: 0x1dd907ABb024E17d196de0D7Fe8EB507b6cCaae7 + > balance: 1.099467316625 + > gas used: 202927 (0x318af) + > gas price: 2.625 gwei + > value sent: 0 ETH + > total cost: 0.000532683375 ETH + + > Saving migration to chain. + > Saving artifacts + ------------------------------------- + > Total cost: 0.000532683375 ETH + + +2_deploy_contracts.js +===================== + + Deploying 'Flipper' + ------------------- + > transaction hash: 0x5e64f8df75775a48abe17c4c82cbca6188800d9df529f2cd7d276266aefd0c0b + > Blocks: 1 Seconds: 32 + > contract address: 0xa59A2606CFDAE99a18977987655a995415C39013 + > block number: 4583832 + > block timestamp: 1687339284 + > account: 0x1dd907ABb024E17d196de0D7Fe8EB507b6cCaae7 + > balance: 1.099048020125 + > gas used: 113819 (0x1bc9b) + > gas price: 2.625 gwei + > value sent: 0 ETH + > total cost: 0.000298774875 ETH + + > Saving migration to chain. + > Saving artifacts + ------------------------------------- + > Total cost: 0.000298774875 ETH + +Summary +======= +> Total deployments: 2 +> Final cost: 0.00083145825 ETH + +# truffle migrate --reset --compile-all --network moonbase + +Compiling your contracts... +=========================== +✓ Fetching solc version list from solc-bin. Attempt #1 +> Compiling ./contracts/Flipper.sol +> Compiling ./contracts/Migrations.sol +> Compiling ./contracts/lib/RandomNumber.sol +> Compiling ./contracts/precompiles/randomness/Randomness.sol +> Compiling ./contracts/precompiles/randomness/RandomnessConsumer.sol +> Artifacts written to /app/dapps/evm2/randomness/build/contracts +> Compiled successfully using: + - solc: 0.8.20+commit.a1b79de6.Emscripten.clang + + +Starting migrations... +====================== +> Network name: 'moonbase' +> Network id: 1287 +> Block gas limit: 15000000 (0xe4e1c0) + + +1_initial_migration.js +====================== + + Replacing 'Migrations' + ---------------------- + > transaction hash: 0x3d1e501f5a9ab4f7d2ceb15bec3557c13eced685cea926c1ddf0ee5bf7904fc8 + > Blocks: 1 Seconds: 20 + > contract address: 0xceD293233Fc7e338017Fbb52F1cc997E9cfD3556 + > block number: 4600259 + > block timestamp: 1687564692 + > account: 0x1dd907ABb024E17d196de0D7Fe8EB507b6cCaae7 + > balance: 2.196479615972105182 + > gas used: 202971 (0x318db) + > gas price: 2.625 gwei + > value sent: 0 ETH + > total cost: 0.000532798875 ETH + + > Saving migration to chain. + > Saving artifacts + ------------------------------------- + > Total cost: 0.000532798875 ETH + + +2_deploy_contracts.js +===================== + + Deploying 'RandomNumber' + ------------------------ + > transaction hash: 0x86c46f35c890608b5eea11b9a2cbb31f02c8a5cb41a5f7f9b337f780104875d8 + > Blocks: 1 Seconds: 14 + > contract address: 0x7ae29716433406B30FB252790716a7471c1B1bBD + > block number: 4600263 + > block timestamp: 1687564746 + > account: 0x1dd907ABb024E17d196de0D7Fe8EB507b6cCaae7 + > balance: 2.193912539222105182 + > gas used: 932021 (0xe38b5) + > gas price: 2.625041913 gwei + > value sent: 0 ETH + > total cost: 0.002446594188796173 ETH + + + Replacing 'Flipper' + ------------------- + > transaction hash: 0xc6605dfc04eefefa841502cfc6decf38dacdd9c381a75d4fd5b9e441530e9029 + > Blocks: 2 Seconds: 20 + > contract address: 0x610CE4456D5b7a988F1894397755F19D3918F699 + > block number: 4600265 + > block timestamp: 1687564770 + > account: 0x1dd907ABb024E17d196de0D7Fe8EB507b6cCaae7 + > balance: 3.293613680347105182 + > gas used: 113851 (0x1bcbb) + > gas price: 2.625 gwei + > value sent: 0 ETH + > total cost: 0.000298858875 ETH + + > Saving migration to chain. + > Saving artifacts + ------------------------------------- + > Total cost: 0.002745453063796173 ETH + +Summary +======= +> Total deployments: 3 +> Final cost: 0.003278251938796173 ETH \ No newline at end of file diff --git a/dapps/ink-python/example/src/app.py b/dapps/ink-python/example/src/app.py index fc4ebc5..bc593f2 100644 --- a/dapps/ink-python/example/src/app.py +++ b/dapps/ink-python/example/src/app.py @@ -81,7 +81,7 @@ args={'init_value': True}, value=0, # endowment=0, - gas_limit={'ref_time': 50_000_000_000, 'proof_size': 10_000_000}, # gas_limit=1000000000000, # gas_limit: dict = None, + gas_limit={'ref_time': 50_000_000_000, 'proof_size': 10_000_000}, # gas_limit=10000000000000, # gas_limit: dict = None, deployment_salt="", upload_code=True, storage_deposit_limit=1_000_000_000_000 diff --git a/dapps/ink-rust/IPSP22/contract/IPSP22/Cargo.toml b/dapps/ink-rust/IPSP22/contract/IPSP22/Cargo.toml new file mode 100644 index 0000000..1cc7e69 --- /dev/null +++ b/dapps/ink-rust/IPSP22/contract/IPSP22/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "my_token" +version = "0.1.0" +authors = ["0xD1x0n"] +edition = "2021" +license = "Apache 2.0" + +[dependencies] +ink = { version = "4.2", default-features = false } +openbrush = { git = "https://github.com/727-Ventures/openbrush-contracts/", tag = "3.1.1", default-features = false, features = ["psp22", "ownable"] } + +scale = { package = "parity-scale-codec", version = "3.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.7", default-features = false, features = ["derive"], optional = true } + + +[dev-dependencies] +ink_e2e = { version = "4.2.0" } + +[lib] +name = "my_token" +path = "lib.rs" + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", + "openbrush/std", +] +ink-as-dependency = [] +e2e-tests = [] diff --git a/dapps/ink-rust/IPSP22/contract/IPSP22/lib.rs b/dapps/ink-rust/IPSP22/contract/IPSP22/lib.rs new file mode 100644 index 0000000..406f7c8 --- /dev/null +++ b/dapps/ink-rust/IPSP22/contract/IPSP22/lib.rs @@ -0,0 +1,29 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] +#![feature(min_specialization)] + +#[openbrush::contract] +pub mod my_psp22 { + + // imports from openbrush + use openbrush::contracts::psp22::*; + use openbrush::traits::Storage; + + #[ink(storage)] + #[derive(Default, Storage)] + pub struct Ipsp22 { + #[storage_field] + psp22: psp22::Data, + } + + // Section contains default implementation without any modifications + impl PSP22 for Ipsp22 {} + + impl Ipsp22 { + #[ink(constructor)] + pub fn new(initial_supply: Balance) -> Self { + let mut _instance = Self::default(); + _instance._mint_to(_instance.env().caller(), initial_supply).expect("Should mint"); + _instance + } + } +} diff --git a/dapps/ink-rust/wasm-flipper/contract/flipper/Cargo.toml b/dapps/ink-rust/wasm-flipper/contract/flipper/Cargo.toml index 3802f2d..61dd1c0 100755 --- a/dapps/ink-rust/wasm-flipper/contract/flipper/Cargo.toml +++ b/dapps/ink-rust/wasm-flipper/contract/flipper/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" # https://github.com/paritytech/ink/releases ink = { version = "4.2.0", default-features = false } -scale = { package = "parity-scale-codec", version = "3.4.0", default-features = false, features = ["derive"] } +scale = { package = "parity-scale-codec", version = "3.5.0", default-features = false, features = ["derive"] } scale-info = { version = "2.7", default-features = false, features = ["derive"], optional = true } [dev-dependencies] diff --git a/dapps/xcm/unnamed/.gitignore b/dapps/xcm/unnamed/.gitignore new file mode 100755 index 0000000..8de8f87 --- /dev/null +++ b/dapps/xcm/unnamed/.gitignore @@ -0,0 +1,9 @@ +# Ignore build artifacts from the local tests sub-crate. +/target/ + +# Ignore backup files creates by cargo fmt. +**/*.rs.bk + +# Remove Cargo.lock when creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/dapps/xcm/unnamed/Cargo.toml b/dapps/xcm/unnamed/Cargo.toml new file mode 100755 index 0000000..3870d06 --- /dev/null +++ b/dapps/xcm/unnamed/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "unnamed" +version = "0.1.0" +authors = ["[your_name] <[your_email]>"] +edition = "2021" + +[dependencies] +# ink = { version = "4.2.0", default-features = false } +ink = { git = 'https://github.com/ltfschoen/ink', branch = 'ltfschoen-env-offchain-test-set-block-number', default-features = false } + +scale = { package = "parity-scale-codec", version = "3.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.7", default-features = false, features = ["derive"], optional = true } + +oracle_contract = { path = "oracle_contract", default-features = false, features = ["ink-as-dependency"] } + +[dev-dependencies] +ink_e2e = "4.2.0" + +[lib] +path = "lib.rs" + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", + + # Note: The metadata generation step requires `std`. If we don't specify this the metadata + # generation for our contract will fail with error: + # the trait bound `OracleContractRef: StorageLayout` is not satisfied + "oracle_contract/std", +] +ink-as-dependency = [] +e2e-tests = [] diff --git a/dapps/xcm/unnamed/README.md b/dapps/xcm/unnamed/README.md new file mode 100644 index 0000000..230f870 --- /dev/null +++ b/dapps/xcm/unnamed/README.md @@ -0,0 +1,22 @@ +* Install latest Cargo Contract version +* Install Rustup 1.69. See https://github.com/paritytech/cargo-contract/blob/master/.github/workflows/ci.yml#L185. If you do not use 1.69 you will get error `ERROR: Loading of original wasm failed` when running `cargo contract build` +``` +rustup update +rustup default stable +rustup install 1.69 +rustup default 1.69 +rustup override set 1.69 +rustup component add rust-src --toolchain 1.69 +rustup toolchain list +rustup show +``` +* Update Cargo Contract to latest version. Check latest version on Github then: +``` +cargo-contract --version +cargo install --force --locked cargo-contract +``` +* Run the following to start a contracts-node, upload and instantiate the contracts, +and call the relevant functions + +* Links + * use [rand-extension](https://github.com/paritytech/ink-examples/blob/main/rand-extension/lib.rs) to get random number on-chain diff --git a/dapps/xcm/unnamed/lib.rs b/dapps/xcm/unnamed/lib.rs new file mode 100755 index 0000000..4cd207c --- /dev/null +++ b/dapps/xcm/unnamed/lib.rs @@ -0,0 +1,358 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] + +#[ink::contract] +mod unnamed { + use ink::{ + env::{ + call::{ + build_create, + build_call, + ExecutionInput, + Selector, + }, + DefaultEnvironment, + Result as EnvResult, + }, + MessageResult, + }; + + use oracle_contract::OracleContractRef; + use oracle_contract::Error; + + use ink::prelude::string::String; + use ink::prelude::vec::Vec; + + // refactor into types file + pub type OracleOwner = AccountId; + + #[derive(Clone, Debug, scale::Encode, scale::Decode)] + #[cfg_attr( + feature = "std", + derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout) + )] + pub struct EntropyData(BlockNumber, String, i16, i16); + + /// Defines the storage of your contract. + /// Add new fields to the below struct in order + /// to add new static storage fields to your contract. + #[derive(Default)] + #[ink(storage)] + pub struct Unnamed { + /// Store a reference to the `OracleContract`. + oracle_contract: Option, + oracle_contract_address: Option, + owner: Option, + } + + /// Type alias for the contract's result type. + pub type ContractResult = core::result::Result; + + impl Unnamed { + /// Constructor that instantiates the OracleContract using its uploaded `code_hash` + #[ink(constructor)] + pub fn new( + oracle_contract_code_hash: Hash, + oracle_contract_address: AccountId, + id_market: String, + block_number_guessed: BlockNumber, + block_number_entropy: BlockNumber, + block_number_end: BlockNumber, + ) -> Self { + let instance = Self::default(); + let caller = instance.env().caller(); + let oracle_contract = build_create::() + .code_hash(oracle_contract_code_hash) + .gas_limit(100000000000) + .endowment(0) + .exec_input( + ExecutionInput::new(Selector::new(ink::selector_bytes!("new"))) + .push_arg(id_market) + .push_arg(block_number_guessed) + .push_arg(block_number_entropy) + .push_arg(block_number_end) + ) + .salt_bytes([0xDE, 0xAD, 0xBE, 0xEF]) + .returns::() + .instantiate(); + + Self { + oracle_contract: Some(oracle_contract), + oracle_contract_address: Some(oracle_contract_address), + owner: Some(caller), + } + } + + /// Using the `OracleContractRef` we can call all the messages of the `OracleContract` + #[ink(message)] + pub fn set_block_for_entropy_for_market_id( + &mut self, + id_market: String, + block_number_entropy: BlockNumber, // always require this even though it may not have changed + block_hash_entropy: String, // Hash + ) -> ContractResult<()> { + ink::env::debug_println!("&self.oracle_contract_address {:?}", &self.oracle_contract_address); + match &self.oracle_contract_address { + Some(c) => { + // using CallBuilder + // https://use.ink/basics/cross-contract-calling#callbuilder + build_call::() + .call(c.clone()) + .gas_limit(100000000000) + .transferred_value(0) + .exec_input( + ExecutionInput::new(Selector::new(ink::selector_bytes!("set_block_for_entropy_for_market_id"))) + .push_arg(id_market) + .push_arg(block_number_entropy) + .push_arg(block_hash_entropy.clone()) + ) + .returns::<()>() + .invoke(); + Ok(()) + }, + None => return Err(Error::NoOracleContractAddress), + } + } + + /// Using the `OracleContractRef` we can call all the messages of the `OracleContract` + #[ink(message)] + pub fn set_entropy_for_market_id( + &mut self, + id_market: String, + block_number_entropy: BlockNumber, // always require this even though it may not have changed + block_hash_entropy: String, // Hash + c1_entropy: i16, + c2_entropy: i16, + ) -> ContractResult<()> { + ink::env::debug_println!("&self.oracle_contract_address {:?}", &self.oracle_contract_address); + match &self.oracle_contract_address { + Some(c) => { + // using CallBuilder + // https://use.ink/basics/cross-contract-calling#callbuilder + build_call::() + .call(c.clone()) + .gas_limit(100000000000) + .transferred_value(0) + .exec_input( + ExecutionInput::new(Selector::new(ink::selector_bytes!("set_entropy_for_market_id"))) + .push_arg(id_market) + .push_arg(block_number_entropy) + .push_arg(block_hash_entropy.clone()) + .push_arg(c1_entropy) + .push_arg(c2_entropy) + ) + .returns::<()>() + .invoke(); + Ok(()) + }, + None => return Err(Error::NoOracleContractAddress), + } + } + + /// Using the `OracleContractRef` we can call all the messages of the `OracleContract` + #[ink(message)] + pub fn get_entropy_for_market_id(&self, id_market: String) -> ContractResult { + match &self.oracle_contract_address { + Some(c) => { + let res = + build_call::() + .call(c.clone()) + .gas_limit(100000000000) + .transferred_value(0) + .exec_input( + ExecutionInput::new(Selector::new(ink::selector_bytes!("get_entropy_for_market_id"))) + .push_arg(id_market) + ) + .returns::>() + .try_invoke(); + ink::env::debug_println!("res {:?}", res); + + match res { + // + // Reference: https://substrate.stackexchange.com/questions/7634/how-to-properly-handle-cross-contract-call-errors/7843#7843 + // Note: if you get `Decode(Error)` then likely an issue in callee method + // + // Contract Success + // + EnvResult::Ok(MessageResult::Ok(ContractResult::Ok(tuple))) => { + ink::env::debug_println!("contract success tuple {:?}", tuple); + return Ok(tuple); + }, + // Contract Error + // + // About: Contract errors include handling `Err(())` responses from callee cross-contract method + // e.g. `return Err(Error::CouldNotReadInput)` + // + EnvResult::Ok(MessageResult::Ok(ContractResult::Err(e))) => { + ink::env::debug_println!("contract error {:?}", e); + return Err(Error::ResponseError); + }, + // Lang Error + // + // About: Lang errors include calling a method that does not exist + // e.g. caller calling callee method `get_entropy_for_market_id_xyz` that does not exist + // + // See https://docs.rs/ink/latest/ink/enum.LangError.html + EnvResult::Ok(MessageResult::Err(ink::LangError::CouldNotReadInput)) => { + ink::env::debug_println!("LangError::CouldNotReadInput"); + return Err(Error::ResponseError); + }, + // Environment Error + // + // About: These are triggered by failed assertions and panics in callee cross-contract method + // Example error message: + // panicked at '', /app/dapps/xcm/unnamed/oracle_contract/lib.rs:377:13 + // environment error occurred CalleeTrapped + // + // Note: Unable to find anything about `EnvError`s or `ink::env::Error` at + // https://docs.rs/ink/latest/ink/?search=EnvError + // + // Error codes duplicated in two places: + // - https://github.com/paritytech/ink/blob/master/crates/engine/src/ext.rs#L73 + // - https://github.com/paritytech/ink/blob/master/crates/env/src/engine/on_chain/ext.rs#L64 + // + EnvResult::Err(e) => { + ink::env::debug_println!("environment error occurred {:?}", e); + + return Err(Error::ResponseError); + }, + // Unimplemented Error + _ => { + ink::env::debug_println!("unimplemented error in get_entropy_for_market_id"); + + return unimplemented!(); + }, + }; + }, + None => return Err(Error::NoOracleContractAddress), + } + } + + /// Using the `OracleContractRef` we can call all the messages of the `OracleContract` + #[ink(message)] + pub fn get_oracle_contract_address(&self) -> ContractResult { + match &self.oracle_contract_address { + Some(c) => { + let res = + build_call::() + .call(c.clone()) + .gas_limit(100000000000) + .exec_input( + ExecutionInput::new(Selector::new(ink::selector_bytes!("get_oracle_contract_address"))) + ) + .returns::() + .try_invoke() + .expect("Error calling get_oracle_contract_address."); + match res { + Ok(contract_address) => { + ink::env::debug_println!("contract_address {:?}", contract_address); + return Ok(contract_address); + }, + Err(e) => { + ink::env::debug_println!("error {:?}", e); + return Err(Error::ResponseError); + }, + }; + }, + None => return Err(Error::NoOracleContractAddress), + } + } + } + + /// Unit tests in Rust are normally defined within such a `#[cfg(test)]` + /// module and test functions are marked with a `#[test]` attribute. + /// The below code is technically just normal Rust code. + #[cfg(test)] + mod tests { + /// Imports all the definitions from the outer scope so we can use them here. + use super::*; + + // /// We test if the default constructor does its job. + // #[ink::test] + // fn default_works() { + // let unnamed = Unnamed::default(); + // assert_eq!(unnamed.get(), false); + // } + + // /// We test a simple use case of our contract. + // #[ink::test] + // fn it_works() { + // let mut unnamed = Unnamed::new(false); + // assert_eq!(unnamed.get(), false); + // unnamed.flip(); + // assert_eq!(unnamed.get(), true); + // } + } + + + /// This is how you'd write end-to-end (E2E) or integration tests for ink! contracts. + /// + /// When running these you need to make sure that you: + /// - Compile the tests with the `e2e-tests` feature flag enabled (`--features e2e-tests`) + /// - Are running a Substrate node which contains `pallet-contracts` in the background + #[cfg(all(test, feature = "e2e-tests"))] + mod e2e_tests { + /// Imports all the definitions from the outer scope so we can use them here. + use super::*; + + /// A helper function used for calling contract messages. + use ink_e2e::build_message; + + /// The End-to-End test `Result` type. + type E2EResult = std::result::Result>; + + /// We test that we can upload and instantiate the contract using its default constructor. + #[ink_e2e::test] + async fn default_works(mut client: ink_e2e::Client) -> E2EResult<()> { + // // Given + // let constructor = UnnamedRef::default(); + + // // When + // let contract_account_id = client + // .instantiate("unnamed", &ink_e2e::alice(), constructor, 0, None) + // .await + // .expect("instantiate failed") + // .account_id; + + // // Then + // let get = build_message::(contract_account_id.clone()) + // .call(|unnamed| unnamed.get()); + // let get_result = client.call_dry_run(&ink_e2e::alice(), &get, 0, None).await; + // assert!(matches!(get_result.return_value(), false)); + + Ok(()) + } + + /// We test that we can read and write a value from the on-chain contract contract. + #[ink_e2e::test] + async fn it_works(mut client: ink_e2e::Client) -> E2EResult<()> { + // // Given + // let constructor = UnnamedRef::new(false); + // let contract_account_id = client + // .instantiate("unnamed", &ink_e2e::bob(), constructor, 0, None) + // .await + // .expect("instantiate failed") + // .account_id; + + // let get = build_message::(contract_account_id.clone()) + // .call(|unnamed| unnamed.get()); + // let get_result = client.call_dry_run(&ink_e2e::bob(), &get, 0, None).await; + // assert!(matches!(get_result.return_value(), false)); + + // // When + // let flip = build_message::(contract_account_id.clone()) + // .call(|unnamed| unnamed.flip()); + // let _flip_result = client + // .call(&ink_e2e::bob(), flip, 0, None) + // .await + // .expect("flip failed"); + + // // Then + // let get = build_message::(contract_account_id.clone()) + // .call(|unnamed| unnamed.get()); + // let get_result = client.call_dry_run(&ink_e2e::bob(), &get, 0, None).await; + // assert!(matches!(get_result.return_value(), true)); + + Ok(()) + } + } +} diff --git a/dapps/xcm/unnamed/oracle_contract/.gitignore b/dapps/xcm/unnamed/oracle_contract/.gitignore new file mode 100644 index 0000000..8de8f87 --- /dev/null +++ b/dapps/xcm/unnamed/oracle_contract/.gitignore @@ -0,0 +1,9 @@ +# Ignore build artifacts from the local tests sub-crate. +/target/ + +# Ignore backup files creates by cargo fmt. +**/*.rs.bk + +# Remove Cargo.lock when creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/dapps/xcm/unnamed/oracle_contract/Cargo.toml b/dapps/xcm/unnamed/oracle_contract/Cargo.toml new file mode 100644 index 0000000..2ab3897 --- /dev/null +++ b/dapps/xcm/unnamed/oracle_contract/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "oracle_contract" +version = "0.1.0" +authors = ["[your_name] <[your_email]>"] +edition = "2021" + +[dependencies] +# ink = { version = "4.2.0", default-features = false } +ink = { git = 'https://github.com/ltfschoen/ink', branch = 'ltfschoen-env-offchain-test-set-block-number', default-features = false } +# hex = { version = "0.4.3", default-features = false } +# ethers-core = { version = "2.0.7", default-features = false } + +scale = { package = "parity-scale-codec", version = "3.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.7", default-features = false, features = ["derive"], optional = true } + +[dev-dependencies] +ink_e2e = "4.2.0" + +[lib] +path = "lib.rs" + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", +] +ink-as-dependency = [] +e2e-tests = [] diff --git a/dapps/xcm/unnamed/oracle_contract/lib.rs b/dapps/xcm/unnamed/oracle_contract/lib.rs new file mode 100644 index 0000000..da458a9 --- /dev/null +++ b/dapps/xcm/unnamed/oracle_contract/lib.rs @@ -0,0 +1,457 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] + +pub use self::oracle_contract::OracleContractRef; +pub use self::oracle_contract::Error; + +#[ink::contract] +mod oracle_contract { + use ink::prelude::string::String; + use ink::prelude::vec::Vec; + use ink::LangError::CouldNotReadInput; + use ink::storage::{ + Lazy, + Mapping, + // traits::ManualKey, + }; + use ink::storage::traits::{ + StorableHint, + StorageKey, + Storable, + }; + use core::str; + // use hex; + + // refactor into types file + pub type MarketGuessId = Vec; + pub type OracleOwner = AccountId; + + #[derive(Clone, Debug, scale::Encode, scale::Decode)] + #[cfg_attr( + feature = "std", + derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout) + )] + pub struct EntropyData(BlockNumber, String, i16, i16); + + /// Emitted when create new market guess for market id. + #[ink(event)] + pub struct NewOracleMarketGuessForMarketId { + #[ink(topic)] + id_market: String, + #[ink(topic)] + oracle_owner: OracleOwner, + #[ink(topic)] + block_number_guessed: BlockNumber, + // Use 3x topics instead of max 4x topics else get error. + // https://github.com/paritytech/cargo-contract/issues/1160 + // #[ink(topic)] + block_number_entropy: BlockNumber, + // #[ink(topic)] + block_number_end: BlockNumber, + } + + /// Emitted when set block hash entropy for market id. + #[ink(event)] + pub struct SetBlockHashEntropyForMarketId { + #[ink(topic)] + id_market: MarketGuessId, + oracle_owner: OracleOwner, + #[ink(topic)] + block_number_entropy: BlockNumber, + #[ink(topic)] + block_hash_entropy: Option, // Hash + } + + /// Emitted when set entropy for market id. + #[ink(event)] + pub struct SetEntropyForMarketId { + #[ink(topic)] + id_market: MarketGuessId, + oracle_owner: OracleOwner, + #[ink(topic)] + block_number_entropy: BlockNumber, + block_hash_entropy: Option, // Hash + c1_entropy: i16, + c2_entropy: i16 + } + + /// Emitted when set block hash entropy for market id. + #[ink(event)] + pub struct GeneratedEntropyForMarketId { + #[ink(topic)] + id_market: MarketGuessId, + oracle_owner: OracleOwner, + #[ink(topic)] + block_number_entropy: BlockNumber, + block_hash_entropy: Option, // Hash + c1_entropy: i16, + c2_entropy: i16, + } + + // https://docs.rs/ink/latest/ink/attr.storage_item.html + // Non-packed types like `Mapping` require calculating the storage key during compilation + // and it is best to rely on automatic storage key calculation via `ink::storage_item` + // and must be called before `derive` + #[derive(scale::Decode, scale::Encode)] + #[cfg_attr( + feature = "std", + derive(scale_info::TypeInfo, ink::storage::traits::StorageLayout) + )] + #[derive(Default, Debug)] + pub struct MarketGuess { + /// Market guess id. + id_market: MarketGuessId, + /// Oracle account owner + oracle_owner: Option, + /// Block number when market guesses were made. + block_number_guessed: BlockNumber, + /// Block number in the future to use the block hash of for entropy. + block_number_entropy: BlockNumber, + /// Block hash associated with `block_number_entropy` when finalized + /// to use for entropy. + block_hash_entropy: Option, // Hash + /// Market guess period end block number + block_number_end: BlockNumber, + /// Entropy random number for coin 1 + c1_entropy: Option, + /// Entropy random number for coin 2 + c2_entropy: Option, + } + + #[derive(Default)] + #[ink(storage)] + pub struct OracleContract { + /// Assign an owner and block number for entropy to every market guess id. + market_data: Mapping, // , ManualKey<123> + } + + /// Errors that can occur upon calling this contract. + #[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)] + #[cfg_attr(feature = "std", derive(::scale_info::TypeInfo))] + pub enum Error { + /// Returned if the no data exists for given market guess id. + NoDataForMarketGuessId, + NoOracleContractAddress, + /// Returned if caller is not oracle owner of market guess id. + CallerIsNotOracleOwner, + InvalidUTF8Sequence, + InvalidDigit, + ResponseError, + CouldNotReadInput, + } + + /// Type alias for the contract's result type. + pub type ContractResult = core::result::Result; + + impl OracleContract { + #[ink(constructor)] + pub fn new( + id_market: String, + block_number_guessed: BlockNumber, + block_number_entropy: BlockNumber, + block_number_end: BlockNumber, + ) -> Self { + ink::env::debug_println!("new"); + let mut instance = Self::default(); + let caller = instance.env().caller(); + assert!(instance.exists_market_data_for_id(id_market.as_bytes()).is_ok(), "market data for given id already exists"); + let block_number_current = Self::env().block_number(); + // TODO - we need to verify that the block hash exists for the block number + // when they say guessing occurred + // assert!( + // block_number_current > block_number_guessed, + // "block number when guessing occurred must be before the current block number" + // ); + // TODO - 100 and 200 are magic numbers, need something more meaningful + // assert!( + // block_number_entropy - block_number_current > 100, + // "block used for entropy must allow sufficient block confirmations after the current \ + // block and block when guessing occurred for assurance that epoch gets finalized \ + // incase head susceptible to reorganization for safety" + // ); + // assert!( + // block_number_end - block_number_entropy > 200, + // "block when market ends must be allow sufficient block confirmations after the \ + // block used for entropy" + // ); + let new_market_guess = MarketGuess { + id_market: id_market.clone().into_bytes(), + // must be set to Option to avoid error: + // the trait `Default` is not implemented for `ink::ink_primitives::AccountId` + oracle_owner: Some(caller), + block_number_guessed, + block_number_entropy, + block_hash_entropy: None, + block_number_end, + c1_entropy: None, + c2_entropy: None, + }; + instance.market_data.insert(id_market.clone().into_bytes(), &new_market_guess); + instance.env().emit_event(NewOracleMarketGuessForMarketId { + id_market: id_market.clone(), + oracle_owner: caller, + block_number_guessed, + block_number_entropy, + block_number_end, + }); + instance + } + + #[ink(message)] + pub fn set_block_for_entropy_for_market_id( + &mut self, + id_market: String, + block_number_entropy: BlockNumber, // always require this even though it may not have changed + block_hash_entropy: String, // Hash + ) -> ContractResult<()> { + let caller: AccountId = self.env().caller(); + ink::env::debug_println!("set_block_for_entropy_for_market_id"); + // assert!(self.exists_market_data_for_id(id_market.as_bytes()).is_ok(), "unable to find market data for given id"); + // TODO - convert Vec to &str to avoid use of .clone() + let market_guess = match self.market_data.get(id_market.clone().into_bytes()) { + Some(data) => data, + None => return Err(Error::NoDataForMarketGuessId), + }; + ink::env::debug_println!("block_hash_entropy: {:?}\n", block_hash_entropy); + ink::env::debug_println!("block_hash_entropy.len(): {:?}\n", block_hash_entropy.len()); + ink::env::debug_println!("market_guess: {:?}\n", market_guess); + // note: oracle_owner may need to run this function more than once incase entropy block number missed or chain reorg + // // singleton change of block hash entropy from the value set at instantiation of the contract + // assert!( + // market_guess.block_hash_entropy == None, + // "unable to set block hash entropy for market id more than once" + // ); + + let block_number_current = Self::env().block_number(); + // assert!( + // block_number_current - market_guess.block_number_entropy > 128, + // "unable to update block number entropy for market id again until after \ + // waiting sufficient blocks after previous so guarantee of waiting until \ + // validators change after a certain amount of epochs" + // ); + ink::env::debug_println!("111"); + let block_hash_entropy_no_prefix = block_hash_entropy.replace("0x", ""); + ink::env::debug_println!("222"); + ink::env::debug_println!("block_hash_entropy_no_prefix {:?}", block_hash_entropy_no_prefix); + assert!(block_hash_entropy_no_prefix.len() == 64, "block hash should be a 256 bit block hash"); + ink::env::debug_println!("333 {:?}, {:?}", market_guess.oracle_owner, caller); + // FIXME - can't do this or get errors. if called from the main contract then the caller won't be the oracle_owner, + // instead it'll be the main contract's address + // if market_guess.oracle_owner != Some(caller) { + // return Err(Error::CallerIsNotOracleOwner); + // } + ink::env::debug_println!("444"); + let new_market_guess = MarketGuess { + block_number_entropy, + block_hash_entropy: Some(block_hash_entropy_no_prefix.clone()), + ..market_guess + }; + ink::env::debug_println!("new_market_guess: {:?}\n", new_market_guess); + self.market_data.insert(id_market.clone().into_bytes(), &new_market_guess); + self.env().emit_event(SetBlockHashEntropyForMarketId { + id_market: id_market.clone().into_bytes(), + oracle_owner: caller, + block_number_entropy, + block_hash_entropy: Some(block_hash_entropy_no_prefix.clone()), + }); + Ok(()) + } + + #[ink(message)] + pub fn set_entropy_for_market_id( + &mut self, + id_market: String, + block_number_entropy: BlockNumber, // always require this even though it may not have changed + block_hash_entropy: String, // Hash + c1_entropy: i16, + c2_entropy: i16, + ) -> ContractResult<()> { + ink::env::debug_println!("set_entropy_for_market_id"); + let caller: AccountId = self.env().caller(); + let market_guess = match self.market_data.get(id_market.clone().into_bytes()) { + Some(data) => data, + None => return Err(Error::NoDataForMarketGuessId), + }; + // FIXME - can't do this or get errors. + // if market_guess.oracle_owner != Some(caller) { + // return Err(Error::CallerIsNotOracleOwner); + // } + let block_hash_entropy_no_prefix = block_hash_entropy.replace("0x", ""); + assert!(block_hash_entropy_no_prefix.len() == 64, "block hash should be a 256 bit block hash"); + + // TODO - replace with `match` + assert!( + block_number_entropy == market_guess.block_number_entropy && + block_hash_entropy_no_prefix == market_guess.block_hash_entropy.unwrap(), + "block_number entropy and block hash storage must be set prior to setting entropy for the market" + ); + assert!(self.exists_market_data_for_id(id_market.as_bytes()).is_ok(), "unable to find market data for given id"); + let new_market_guess = MarketGuess { + block_number_entropy, + block_hash_entropy: Some(block_hash_entropy_no_prefix.clone()), + c1_entropy: Some(c1_entropy), + c2_entropy: Some(c2_entropy), + ..market_guess + }; + ink::env::debug_println!("new_market_guess: {:?}\n", new_market_guess); + self.market_data.insert(id_market.clone().into_bytes(), &new_market_guess); + self.env().emit_event(SetEntropyForMarketId { + id_market: id_market.into_bytes(), + oracle_owner: caller, + block_number_entropy, + block_hash_entropy: Some(block_hash_entropy_no_prefix.clone()), + c1_entropy, + c2_entropy, + }); + Ok(()) + } + + #[ink(message)] + pub fn get_oracle_contract_address(&self) -> AccountId { + ink::env::debug_println!("oracle contract address {:?}", self.env().account_id()); + self.env().account_id() + } + + #[ink(message)] + #[ink(payable)] + pub fn get_entropy_for_market_id(&self, id_market: String) -> ContractResult { + let caller: AccountId = self.env().caller(); + let market_guess = match self.market_data.get(id_market.clone().into_bytes()) { + Some(data) => data, + None => return Err(Error::NoDataForMarketGuessId), + }; + ink::env::debug_println!("market_guess.oracle_owner {:?}", market_guess.oracle_owner); + ink::env::debug_println!("caller {:?}", caller); + // FIXME - causes `Decode(Error)` since caller account id is smart contract, + // which differs from the account id of Alice + // if market_guess.oracle_owner != Some(caller) { + // return Err(Error::CallerIsNotOracleOwner) + // } + // note: oracle_owner may need to run this function more than once incase entropy block number missed or chain reorg + // assert!( + // market_guess.block_hash_entropy != None, + // "block hash entropy must be set prior to obtaining entropy" + // ); + ink::env::debug_println!("market_guess.block_hash_entropy {:?}", market_guess.block_hash_entropy); + let block_number_entropy = market_guess.block_number_entropy; + // "0xaef6eca62ae61934a7ab5ad3814f6e319abd3e4e4aa1a3386466ad197d1c4dea" + // note: Hash is [u8; 32] 32 bytes (&[u8]) without 0x prefix and 64 symbols, 32 bytes, 256 bits + // TODO - replace with `match` + let block_hash_entropy: String = market_guess.block_hash_entropy.unwrap(); + // let block_hash_entropy: &[u8] = + // "aef6eca62ae61934a7ab5ad3814f6e319abd3e4e4aa1a3386466ad197d1c4dea".as_bytes(); + // note: changed `block_hash_entropy` to `[u8; 32]` instead of `Hash` so we can get the `.len()` + assert!(block_hash_entropy.len() == 64, "block hash should be a 256 bit block hash"); + ink::env::debug_println!("block_hash_entropy: {:?}\n", block_hash_entropy); + // https://peterlyons.com/problog/2017/12/rust-converting-bytes-chars-and-strings/ + let (c1_str, c2_str): (&str, &str) = self.split_last_bytes(block_hash_entropy.as_str()); + ink::env::debug_println!("c1_str: {:?}\n", c1_str); + ink::env::debug_println!("c2_str: {:?}\n", c2_str); + let c1_hex = String::from(c1_str); + let c2_hex = String::from(c2_str); + ink::env::debug_println!("c1_hex: {:?}", c1_hex); + ink::env::debug_println!("c2_hex: {:?}", c2_hex); + // use u16 since max value 65535 + // let without_prefix = hex.trim_start_matches("0x"); + let c1_decimal = match i16::from_str_radix(&c1_hex, 16) { + Ok(d) => d, + Err(_e) => return Err(Error::InvalidDigit), + }; + let c2_decimal = match i16::from_str_radix(&c2_hex, 16) { + Ok(d) => d, + Err(_e) => return Err(Error::InvalidDigit), + }; + ink::env::debug_println!("c1_decimal {:?}", c1_decimal); + ink::env::debug_println!("c2_decimal {:?}", c2_decimal); + // remainders are 0 or 1 and represent the random side the coin flipped on + let c1_rem = c1_decimal % 2i16; + let c2_rem = c2_decimal % 2i16; + ink::env::debug_println!("c1_rem {:?}", c1_rem); + ink::env::debug_println!("c2_rem {:?}", c2_rem); + + self.env().emit_event(GeneratedEntropyForMarketId { + id_market: id_market.clone().into_bytes(), + oracle_owner: caller, + block_number_entropy: market_guess.block_number_entropy, + block_hash_entropy: Some(block_hash_entropy.clone()), + c1_entropy: c1_rem, + c2_entropy: c2_rem, + }); + + let entropy_data = EntropyData(block_number_entropy, block_hash_entropy.clone(), c1_rem, c2_rem); + Ok(entropy_data) + } + + // get symbols 61-64 for coin1 and 57-60 for coin2 fro the block hash + fn split_last_bytes<'a>(&'a self, slice: &'a str) -> (&str, &str) { + let len = String::from(slice).len(); + let sub_slice= &slice[len-8..]; + let (c1, c2) = sub_slice.split_at(4); + (c1, c2) + } + + // helper methods + fn exists_market_data_for_id(&self, id_market: &[u8]) -> ContractResult { + let id_market_str = match str::from_utf8(id_market) { + Ok(v) => v, + Err(_e) => return Err(Error::InvalidUTF8Sequence), + }; + Ok(self.market_data.contains(id_market_str.as_bytes().to_vec())) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::oracle_contract::OracleContract; + use std::str::FromStr; + + type BlockNumber = u32; + + /// We test a simple use case of our contract. + #[ink::test] + fn it_works() { + let id_market: String = String::from("my_id"); + let block_number_guessed = 50; + // override `Self::env().block_number();` for tests + // See https://substrate.stackexchange.com/questions/8867/how-to-stub-ink-contract-environment-to-produce-fake-values-for-use-in-tests + let mut new_block_number: BlockNumber = 100; + ink::env::test::set_block_number::(new_block_number); + let block_number_entropy = 228; // must be more than 100 blocks after current block_number + let block_number_end = 500; // must be more than 200 blocks after block_number_entropy + + let mut oracle_contract = OracleContract::new( + id_market.clone(), + block_number_guessed.clone(), + block_number_entropy.clone(), + block_number_end.clone(), + ); + let str_block_hash_entropy: String = + "aef6eca62ae61934a7ab5ad3814f6e319abd3e4e4aa1a3386466ad197d1c4dea".to_string(); + + new_block_number = 357; // >128 after block_number_entropy (228 + 128 + 1 = 357) + ink::env::test::set_block_number::(new_block_number); + assert_eq!(oracle_contract.set_block_for_entropy_for_market_id( + id_market.clone(), + block_number_entropy.clone(), + str_block_hash_entropy.clone(), + ), Ok(())); + + let oracle_contract_address = oracle_contract.get_oracle_contract_address(); + + let c1_entropy = 0i16; + let c2_entropy = 0i16; + assert_eq!( + oracle_contract.get_entropy_for_market_id( + id_market.clone(), + ).unwrap(), + ( + block_number_entropy.clone(), + str_block_hash_entropy.clone(), + c1_entropy.clone(), + c2_entropy.clone(), + ) + ); + } +} \ No newline at end of file diff --git a/docker/quickstart-basic-contract-caller.sh b/docker/quickstart-basic-contract-caller.sh new file mode 100755 index 0000000..2c0cdbd --- /dev/null +++ b/docker/quickstart-basic-contract-caller.sh @@ -0,0 +1,203 @@ +#!/bin/bash + +trap "echo; exit" INT +trap "echo; exit" HUP + +# if they call this script from project root or from within docker/ folder then +# in both cases the PARENT_DIR will refer to the project root where the .env file is. +PARENT_DIR=$( echo $(dirname "$(dirname "$(realpath "${BASH_SOURCE[0]}")")") ) + +echo "Building contracts..." +cd $PARENT_DIR/dapps/basic_contract_caller +PROJECT_ROOT=$PARENT_DIR/dapps/basic_contract_caller +cargo contract build \ + --manifest-path $PARENT_DIR/dapps/basic_contract_caller/Cargo.toml +cargo contract build \ + --manifest-path $PARENT_DIR/dapps/basic_contract_caller/other_contract/Cargo.toml + +cd $PROJECT_ROOT +echo "Uploading sub-contract..." +# upload sub-contract +# +# note: provide args variable so able to comment out all `--skip-dry-run` options in bulk +# since it breaks command if comment out a multiline command option `#--skip-dry-run \` +# see https://stackoverflow.com/a/9522766/3208553 +args=( + --suri //Alice + --execute + # --skip-dry-run + --skip-confirm + $PARENT_DIR/target/ink/other_contract/other_contract.wasm +) +OUTPUT_CODE_HASH_SUB=$( + cargo contract upload "${args[@]}" | tail -1 +) +echo "Finished uploading contract..." +# example: ' Code hash "0x..."' +echo $OUTPUT_CODE_HASH_SUB +# remove text 'Code hash' and the outer double quotes of the code hash +OUTPUT_CODE_HASH_SUB_REMOVED_LABEL=$(echo "$OUTPUT_CODE_HASH_SUB" | sed 's/Code hash//;s/$//' | tr -d '"') +# trim whitespace +CODE_HASH_SUB=$(echo $OUTPUT_CODE_HASH_SUB_REMOVED_LABEL) +echo $CODE_HASH_SUB + +echo "Uploading main-contract..." +# upload main-contract +# +args=( + --suri //Alice + --execute + # --skip-dry-run + --skip-confirm + $PARENT_DIR/target/ink/basic_contract_caller/basic_contract_caller.wasm +) +OUTPUT_CODE_HASH_MAIN=$( + cargo contract upload "${args[@]}" | tail -1 +) +echo "Finished uploading contract..." +# example: ' Code hash "0x..."' +echo $OUTPUT_CODE_HASH_MAIN +# remove text 'Code hash' and the outer double quotes of the code hash +OUTPUT_CODE_HASH_MAIN_REMOVED_LABEL=$(echo "$OUTPUT_CODE_HASH_MAIN" | sed 's/Code hash//;s/$//' | tr -d '"') +# trim whitespace +CODE_HASH_MAIN=$(echo $OUTPUT_CODE_HASH_MAIN_REMOVED_LABEL) +echo $CODE_HASH_MAIN + +cd $PROJECT_ROOT + +echo "Instantiating sub-contract..." +args=( + --manifest-path $PARENT_DIR/dapps/basic_contract_caller/other_contract/Cargo.toml + --suri //Alice + --constructor new + --args true + --execute + # unlimited gas is 0 + --gas 100000000000 + --proof-size 100000000000 + # --skip-dry-run + --skip-confirm +) +OUTPUT_CONTRACT_ADDR_SUB=$( + cargo contract instantiate "${args[@]}" | tail -1 +) + +# example: ' Contract 5...' +echo $OUTPUT_CONTRACT_ADDR_SUB +# remove text 'Contract' +OUTPUT_CONTRACT_ADDR_SUB_REMOVED_LABEL=$(echo "$OUTPUT_CONTRACT_ADDR_SUB" | sed 's/Contract//;s/$//') +# trim whitespace using `echo ...` +CONTRACT_ADDR_SUB=$(echo $OUTPUT_CONTRACT_ADDR_SUB_REMOVED_LABEL) +echo $CONTRACT_ADDR_SUB + +# instantiate "main" contract, providing the code hash generated from uploading the "sub" contract +echo "Instantiating main-contract..." + +args=( + --manifest-path $PARENT_DIR/dapps/basic_contract_caller/Cargo.toml + --suri //Alice + --constructor new + --args $CODE_HASH_SUB $CONTRACT_ADDR_SUB + --execute + # unlimited gas is 0 + # --storage-deposit-limit 50000000000 \ + # https://substrate.stackexchange.com/questions/3992/i-get-a-the-executed-contract-exhausted-its-gas-limit-when-attempting-to-inst + --gas 200000000000 \ + --proof-size 100000000000 + # --skip-dry-run + --skip-confirm +) +OUTPUT_CONTRACT_ADDR_MAIN=$( + cargo contract instantiate "${args[@]}" | tail -1 +) + +# example: ' Contract 5...' +echo $OUTPUT_CONTRACT_ADDR_MAIN +# remove text 'Contract' +OUTPUT_CONTRACT_ADDR_MAIN_REMOVED_LABEL=$(echo "$OUTPUT_CONTRACT_ADDR_MAIN" | sed 's/Contract//;s/$//') +# trim whitespace using `echo ...` +CONTRACT_ADDR_MAIN=$(echo $OUTPUT_CONTRACT_ADDR_MAIN_REMOVED_LABEL) +echo $CONTRACT_ADDR_MAIN + +echo "Calling contract method flip..." +args=( + --suri //Alice + --contract $CONTRACT_ADDR_MAIN + --message flip + --execute + --gas 200000000000 + --proof-size 100000000000 + # --skip-dry-run + --skip-confirm +) +cargo contract call "${args[@]}" | grep --color=always -z 'data' + +echo "Calling contract method get ..." +args=( + --suri //Alice + --contract $CONTRACT_ADDR_SUB + --message get + --execute + # --skip-dry-run + --skip-confirm +) +cargo contract call "${args[@]}" | grep --color=always -z 'data' + +echo "Calling contract method get ..." +args=( + --suri //Alice + --contract $CONTRACT_ADDR_MAIN + --message get + --execute + # --gas 200000000000 + # --proof-size 100000000000 + # --skip-dry-run + --skip-confirm +) +cargo contract call "${args[@]}" | grep --color=always -z 'data' + +echo "Calling contract method flip_and_get ..." +args=( + --suri //Alice + --contract $CONTRACT_ADDR_MAIN + --message flip_and_get + --execute + --gas 200000000000 + --proof-size 100000000000 + --skip-dry-run + --skip-confirm +) +cargo contract call "${args[@]}" | grep --color=always -z 'data' + +# highlight the `data` line in output containing the value of the emitted `Retrieve` event +echo "Calling contract method get_other_contract_address ..." +args=( + --suri //Alice + --contract $CONTRACT_ADDR_MAIN + --message get_other_contract_address + --execute + # --gas 200000000000 + # --proof-size 100000000000 + # --skip-dry-run + --skip-confirm +) +cargo contract call "${args[@]}" | grep --color=always -z 'data' + +# CALLEE= # contract address +# SELECTOR="get" # method +# ARG="" # arg +# MAX_GAS="200000000000" +# TRANSFER_VALUE="0" +# echo "Calling contract method u32_proxy ..." +# args=( +# --suri //Alice +# --contract $CONTRACT_ADDR_MAIN +# --message u32_proxy +# --args $CALLEE $SELECTOR $ARG $MAX_GAS $TRANSFER_VALUE +# --execute +# # --gas 200000000000 +# # --proof-size 100000000000 +# # --skip-dry-run +# --skip-confirm +# ) +# cargo contract call "${args[@]}" | grep --color=always -z 'data' diff --git a/docker/quickstart-chainlink-vrfd20.sh b/docker/quickstart-chainlink-vrfd20.sh new file mode 100755 index 0000000..c0db783 --- /dev/null +++ b/docker/quickstart-chainlink-vrfd20.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +trap "echo; exit" INT +trap "echo; exit" HUP + +# if they call this script from project root or from within docker/ folder then +# in both cases the PARENT_DIR will refer to the project root where the .env file is. +PARENT_DIR=$( echo $(dirname "$(dirname "$(realpath "${BASH_SOURCE[0]}")")") ) + +# TODO - install all necessary dependencies as mentioned in the dapps/evm2/randomness/README.md + +# echo "Compiling contracts..." +# cd $PARENT_DIR/dapps/evm2/randomness +# PROJECT_ROOT=$PARENT_DIR/dapps/evm2/randomness +# truffle compile --compile-all + +# echo "Migrating contracts..." +# truffle migrate --reset --compile-all --network moonbase + +# TODO - get the deployed contract address from the output +# and pass that as a variable to the demo-chainlink-vrf-on-ethereum-sepolia.js script +# (which gets the contract at that address and then call `rollDice` +# and waits some blocks before getting the random number) + +# echo "Rolling the dice and obtaining the random number" +# cd dapps/evm2/randomness +# node ./scripts/demo-chainlink-vrf-on-ethereum-sepolia.js diff --git a/docker/quickstart-ipsp22.sh b/docker/quickstart-ipsp22.sh new file mode 100755 index 0000000..cd41b9c --- /dev/null +++ b/docker/quickstart-ipsp22.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +trap "echo; exit" INT +trap "echo; exit" HUP + +# if they call this script from project root or from within docker/ folder then +# in both cases the PARENT_DIR will refer to the project root where the .env file is. +PARENT_DIR=$( echo $(dirname "$(dirname "$(realpath "${BASH_SOURCE[0]}")")") ) + +echo "Building contracts..." +cd $PARENT_DIR/dapps/basic_contract_caller +PROJECT_ROOT=$PARENT_DIR/dapps/ink-rust/IPSP22/contract/IPSP22 +cargo +nightly-2023-03-21-x86_64-unknown-linux-gnu contract build \ + --manifest-path $PARENT_DIR/dapps/ink-rust/IPSP22/contract/IPSP22/Cargo.toml + +cd $PROJECT_ROOT + +echo "Uploading contract..." +args=( + --suri //Alice + --execute + # --skip-dry-run + --skip-confirm + $PARENT_DIR/target/ink/my_token/my_token.wasm +) +OUTPUT_CODE_HASH=$( + cargo contract upload "${args[@]}" | tail -1 +) +echo "Finished uploading contract..." +# example: ' Code hash "0x..."' +echo $OUTPUT_CODE_HASH +# remove text 'Code hash' and the outer double quotes of the code hash +OUTPUT_CODE_HASH_REMOVED_LABEL=$(echo "$OUTPUT_CODE_HASH" | sed 's/Code hash//;s/$//' | tr -d '"') +# trim whitespace +CODE_HASH_MAIN=$(echo $OUTPUT_CODE_HASH_REMOVED_LABEL) +echo $CODE_HASH_MAIN + +cd $PROJECT_ROOT + +echo "Instantiating contract..." + +INITIAL_SUPPLY="100000000" +args=( + --manifest-path $PARENT_DIR/dapps/ink-rust/IPSP22/contract/IPSP22/Cargo.toml + --suri //Alice + --constructor new + --args $INITIAL_SUPPLY + --execute + --gas 1000000000000 + --proof-size 1000000000000 + # --skip-dry-run + --skip-confirm +) +OUTPUT_CONTRACT_ADDR=$( + cargo contract instantiate "${args[@]}" | tail -1 +) + +# example: ' Contract 5...' +echo $OUTPUT_CONTRACT_ADDR +# remove text 'Contract' +OUTPUT_CONTRACT_ADDR_REMOVED_LABEL=$(echo "$OUTPUT_CONTRACT_ADDR" | sed 's/Contract//;s/$//') +# trim whitespace using `echo ...` +CONTRACT_ADDR_MAIN=$(echo $OUTPUT_CONTRACT_ADDR_REMOVED_LABEL) +echo $CONTRACT_ADDR_MAIN diff --git a/docker/quickstart-moonbeam-vrf-precompile.sh b/docker/quickstart-moonbeam-vrf-precompile.sh new file mode 100755 index 0000000..59ef2a0 --- /dev/null +++ b/docker/quickstart-moonbeam-vrf-precompile.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +trap "echo; exit" INT +trap "echo; exit" HUP + +# if they call this script from project root or from within docker/ folder then +# in both cases the PARENT_DIR will refer to the project root where the .env file is. +PARENT_DIR=$( echo $(dirname "$(dirname "$(realpath "${BASH_SOURCE[0]}")")") ) + +# TODO - install all necessary dependencies as mentioned in the dapps/evm2/randomness/README.md + +# echo "Compiling contracts..." +# cd $PARENT_DIR/dapps/evm2/randomness +# PROJECT_ROOT=$PARENT_DIR/dapps/evm2/randomness +# truffle compile --compile-all + +# echo "Migrating contracts..." +# truffle migrate --reset --compile-all --network sepolia + +# TODO - get the deployed contract address from the output +# and pass that as a variable to the demo-chainlink-vrf-on-ethereum-sepolia.js script +# (which gets the contract at that address and then call `rollDice` +# and waits some blocks before getting the random number) + +# echo "Request randomness then obtain a randomness status then a random number" +# cd dapps/evm2/randomness +# node ./scripts/demo-moonbeam-vrf-on-moonbase-alpha.js diff --git a/docker/quickstart-unnamed.sh b/docker/quickstart-unnamed.sh new file mode 100755 index 0000000..15f89c3 --- /dev/null +++ b/docker/quickstart-unnamed.sh @@ -0,0 +1,197 @@ +#!/bin/bash + +# compatibility cargo-contract v3.0.1 +# +# start a fresh substrate-contracts-node and upload the "unnamed" ink! +# smart contracts to it and then instantiate and call a method +# +# if you want to sky dry run then find/replace `--skip-dry-run` with `#--skip-dry-run` +# to comment those lines out + +trap "echo; exit" INT +trap "echo; exit" HUP + +# if they call this script from project root or from within docker/ folder then +# in both cases the PARENT_DIR will refer to the project root where the .env file is. +PARENT_DIR=$( echo $(dirname "$(dirname "$(realpath "${BASH_SOURCE[0]}")")") ) + +echo "Building contracts..." +cd $PARENT_DIR/dapps/xcm/unnamed +PROJECT_ROOT=$PARENT_DIR/dapps/xcm/unnamed +cargo contract build \ + --manifest-path $PARENT_DIR/dapps/xcm/unnamed/Cargo.toml +cargo contract build \ + --manifest-path $PARENT_DIR/dapps/xcm/unnamed/oracle_contract/Cargo.toml + +cd $PROJECT_ROOT +echo "Uploading sub-contract..." +# upload sub-contract +# +# note: provide args variable so able to comment out all `--skip-dry-run` options in bulk +# since it breaks command if comment out a multiline command option `#--skip-dry-run \` +# see https://stackoverflow.com/a/9522766/3208553 +args=( + --suri //Alice + --execute + --skip-dry-run + --skip-confirm + $PARENT_DIR/target/ink/oracle_contract/oracle_contract.wasm +) +OUTPUT_CODE_HASH_SUB=$( + cargo contract upload "${args[@]}" | tail -1 +) +echo "Finished uploading contract..." +# example: ' Code hash "0x..."' +echo $OUTPUT_CODE_HASH_SUB +# remove text 'Code hash' and the outer double quotes of the code hash +OUTPUT_CODE_HASH_SUB_REMOVED_LABEL=$(echo "$OUTPUT_CODE_HASH_SUB" | sed 's/Code hash//;s/$//' | tr -d '"') +# trim whitespace +CODE_HASH_SUB=$(echo $OUTPUT_CODE_HASH_SUB_REMOVED_LABEL) +echo $CODE_HASH_SUB + +echo "Uploading main-contract..." +# upload main-contract +# +args=( + --suri //Alice + --execute + # --skip-dry-run + --skip-confirm + $PARENT_DIR/target/ink/unnamed/unnamed.wasm +) +OUTPUT_CODE_HASH_MAIN=$( + cargo contract upload "${args[@]}" | tail -1 +) +echo "Finished uploading contract..." +# example: ' Code hash "0x..."' +echo $OUTPUT_CODE_HASH_MAIN +# remove text 'Code hash' and the outer double quotes of the code hash +OUTPUT_CODE_HASH_MAIN_REMOVED_LABEL=$(echo "$OUTPUT_CODE_HASH_MAIN" | sed 's/Code hash//;s/$//' | tr -d '"') +# trim whitespace +CODE_HASH_MAIN=$(echo $OUTPUT_CODE_HASH_MAIN_REMOVED_LABEL) +echo $CODE_HASH_MAIN + +ARG_ID_MARKET="\"my_id\"" + +# Note: The id_market is stored as Vec format instead of String. +# Paste the following at https://play.rust-lang.org/?version=stable&mode=debug&edition=2021 +# then press the "Run" button and it will output `[109, 121, 95, 105, 100]` +# ```rust +# fn main() -> Result<(), std::io::Error> { +# let s = "my_id".to_string(); +# let b = s.as_bytes(); +# println!("{:?}", b); +# let _y = String::from_utf8(b.to_vec()); +# Ok(()) +# } +# ``` + +cd $PROJECT_ROOT + +echo "Instantiating sub-contract..." +args=( + --manifest-path $PARENT_DIR/dapps/xcm/unnamed/oracle_contract/Cargo.toml + --suri //Alice + --constructor new + --args $ARG_ID_MARKET "100" "228" "500" + --execute + --gas 1000000000000 + --proof-size 1000000000000 + # --skip-dry-run + --skip-confirm +) +OUTPUT_CONTRACT_ADDR_SUB=$( + cargo contract instantiate "${args[@]}" | tail -1 +) + +# example: ' Contract 5...' +echo $OUTPUT_CONTRACT_ADDR_SUB +# remove text 'Contract' +OUTPUT_CONTRACT_ADDR_SUB_REMOVED_LABEL=$(echo "$OUTPUT_CONTRACT_ADDR_SUB" | sed 's/Contract//;s/$//') +# trim whitespace using `echo ...` +CONTRACT_ADDR_SUB=$(echo $OUTPUT_CONTRACT_ADDR_SUB_REMOVED_LABEL) +echo $CONTRACT_ADDR_SUB + +# instantiate "main" contract, providing the code hash generated from uploading the "sub" contract +echo "Instantiating main-contract..." + +args=( + --manifest-path $PARENT_DIR/dapps/xcm/unnamed/Cargo.toml + --suri //Alice + --constructor new + --args $CODE_HASH_SUB $CONTRACT_ADDR_SUB $ARG_ID_MARKET "100" "228" "500" + --execute + --gas 1000000000000 + --proof-size 1000000000000 + # --skip-dry-run + --skip-confirm +) +OUTPUT_CONTRACT_ADDR_MAIN=$( + cargo contract instantiate "${args[@]}" | tail -1 +) + +# example: ' Contract 5...' +echo $OUTPUT_CONTRACT_ADDR_MAIN +# remove text 'Contract' +OUTPUT_CONTRACT_ADDR_MAIN_REMOVED_LABEL=$(echo "$OUTPUT_CONTRACT_ADDR_MAIN" | sed 's/Contract//;s/$//') +# trim whitespace using `echo ...` +CONTRACT_ADDR_MAIN=$(echo $OUTPUT_CONTRACT_ADDR_MAIN_REMOVED_LABEL) +echo $CONTRACT_ADDR_MAIN + +ARG_BLOCK_HASH_ENTROPY="\"aef6eca62ae61934a7ab5ad3814f6e319abd3e4e4aa1a3386466ad197d1c4dea\"" + +echo "Calling contract method set_block_for_entropy_for_market_id..." +args=( + --suri //Alice + --contract $CONTRACT_ADDR_MAIN + --message set_block_for_entropy_for_market_id + --args $ARG_ID_MARKET "228" $ARG_BLOCK_HASH_ENTROPY + --execute + --gas 1000000000000 + --proof-size 1000000000000 + # --skip-dry-run + --skip-confirm +) +cargo contract call "${args[@]}" | grep --color=always -z 'data' + +# # TODO - change this to try and make it work via `$CONTRACT_ADDR_MAIN` +# echo "Calling contract method set_entropy_for_market_id ..." +# args=( +# --suri //Alice +# --contract $CONTRACT_ADDR_MAIN +# --message set_entropy_for_market_id +# --args $ARG_ID_MARKET "228" $ARG_BLOCK_HASH_ENTROPY "0" "0" +# --execute +# --gas 1000000000000 +# --proof-size 1000000000000 +# # --skip-dry-run +# --skip-confirm +# ) +# cargo contract call "${args[@]}" | grep --color=always -z 'data' + +# TODO - change this to try and make it work via `$CONTRACT_ADDR_MAIN` +echo "Calling contract method get_entropy_for_market_id ..." +args=( + --suri //Alice + --contract $CONTRACT_ADDR_MAIN + --message get_entropy_for_market_id + --args $ARG_ID_MARKET + --execute + --gas 1000000000000 + --proof-size 1000000000000 + # --skip-dry-run + --skip-confirm +) +cargo contract call "${args[@]}" | grep --color=always -z 'data' + +# highlight the `data` line in output containing the value of the emitted `Retrieve` event +echo "Calling contract method get_oracle_contract_address ..." +args=( + --suri //Alice + --contract $CONTRACT_ADDR_MAIN + --message get_oracle_contract_address + --execute + # --skip-dry-run + --skip-confirm +) +cargo contract call "${args[@]}" | grep --color=always -z 'data' diff --git a/docker/quickstart.sh b/docker/quickstart.sh index 110da70..ee1dfb5 100755 --- a/docker/quickstart.sh +++ b/docker/quickstart.sh @@ -7,22 +7,6 @@ trap "echo; exit" HUP # in both cases the PARENT_DIR will refer to the project root where the .env file is. PARENT_DIR=$( echo $(dirname "$(dirname "$(realpath "${BASH_SOURCE[0]}")")") ) -# kill the existing substrate-contracts-node that is running on port 30333 -kill -9 $(lsof -ti:30333) - -# delete the blockchain database so we don't get this error when we redeploy the -# smart contract `ERROR: This contract has already been uploaded with code hash:` -rm -rf /tmp/ink - -cd ${PARENT_DIR}/docker/ -# https://www.maketecheasier.com/run-bash-commands-background-linux/ -nohup ./run-scn.sh &>/dev/null & -# wait for the blockchain node to start before we interact with it -# note: if you try to interact with it before it is ready then you will get error -# `ERROR: Rpc error: RPC error: Networking or low-level protocol error: -# Error when opening the TCP socket: Cannot assign requested address (os error 99)` -sleep 15 - cd $PARENT_DIR/dapps/ink-rust/wasm-flipper/contract cargo contract new flipper cd flipper diff --git a/docker/reset.sh b/docker/reset.sh index c0232df..b6e24aa 100755 --- a/docker/reset.sh +++ b/docker/reset.sh @@ -2,8 +2,8 @@ # restart the substrate-contracts-node and remove the chain database -trap "echo; exit" INT -trap "echo; exit" HUP +# trap "echo; exit" INT +# trap "echo; exit" HUP # if they call this script from project root or from within docker/ folder then # in both cases the PARENT_DIR will refer to the project root where the .env file is. @@ -18,7 +18,9 @@ rm -rf /tmp/ink cd ${PARENT_DIR}/docker/ # https://www.maketecheasier.com/run-bash-commands-background-linux/ -nohup ./run-scn.sh &>/dev/null & +# nohup ./run-scn.sh &>/dev/null & +./run-scn.sh +cd $PARENT_DIR # wait for the blockchain node to start before we interact with it # note: if you try to interact with it before it is ready then you will get error # `ERROR: Rpc error: RPC error: Networking or low-level protocol error: diff --git a/docker/restart.sh b/docker/restart.sh new file mode 100755 index 0000000..84cf60c --- /dev/null +++ b/docker/restart.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# only restart the substrate-contracts-node + +# trap "echo; exit" INT +# trap "echo; exit" HUP + +# if they call this script from project root or from within docker/ folder then +# in both cases the PARENT_DIR will refer to the project root where the .env file is. +PARENT_DIR=$( echo $(dirname "$(dirname "$(realpath "${BASH_SOURCE[0]}")")") ) + +# kill the existing substrate-contracts-node that is running on port 30333 +kill -9 $(lsof -ti:30333) + +cd ${PARENT_DIR}/docker/ +# https://www.maketecheasier.com/run-bash-commands-background-linux/ +# nohup ./run-scn.sh &>/dev/null & +./run-scn.sh +cd $PARENT_DIR +# wait for the blockchain node to start before we interact with it +# note: if you try to interact with it before it is ready then you will get error +# `ERROR: Rpc error: RPC error: Networking or low-level protocol error: +# Error when opening the TCP socket: Cannot assign requested address (os error 99)` +sleep 15