--- a/Cargo.lock +++ b/Cargo.lock @@ -172,9 +172,9 @@ [[package]] name = "anyhow" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] name = "app-promotion-rpc" @@ -343,9 +343,9 @@ [[package]] name = "async-trait" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff18d764974428cf3a9328e23fc5c986f5fbed46e6cd4cdf42544df5d297ec1" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" dependencies = [ "proc-macro2", "quote", @@ -706,20 +706,9 @@ [[package]] name = "bstr" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", -] - -[[package]] -name = "bstr" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b" +checksum = "b7f0778972c64420fdedc63f09919c8a88bda7b25135357fd25a5d9f3257e832" dependencies = [ "memchr", "serde", @@ -760,9 +749,9 @@ [[package]] name = "bytes" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "bzip2-sys" @@ -808,9 +797,9 @@ [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" dependencies = [ "jobserver", ] @@ -1027,18 +1016,6 @@ ] [[package]] -name = "console" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "windows-sys 0.42.0", -] - -[[package]] name = "const-oid" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1221,9 +1198,9 @@ [[package]] name = "crc" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" dependencies = [ "crc-catalog", ] @@ -1874,9 +1851,9 @@ [[package]] name = "curve25519-dalek" -version = "4.0.0-pre.5" +version = "4.0.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67bc65846be335cb20f4e52d49a437b773a2c1fdb42b19fc84e79e6f6771536f" +checksum = "8da00a7a9a4eb92a0a0f8e75660926d48f0d0f3c537e455c457bcdaa1e16b1ac" dependencies = [ "cfg-if", "fiat-crypto", @@ -1888,9 +1865,9 @@ [[package]] name = "cxx" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b61a7545f753a88bcbe0a70de1fcc0221e10bfc752f576754fa91e663db1622e" +checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" dependencies = [ "cc", "cxxbridge-flags", @@ -1900,9 +1877,9 @@ [[package]] name = "cxx-build" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f464457d494b5ed6905c63b0c4704842aba319084a0a3561cdc1359536b53200" +checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" dependencies = [ "cc", "codespan-reporting", @@ -1915,15 +1892,15 @@ [[package]] name = "cxxbridge-flags" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c7119ce3a3701ed81aca8410b9acf6fc399d2629d057b87e2efa4e63a3aaea" +checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" [[package]] name = "cxxbridge-macro" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e07508b90551e610910fa648a1878991d367064997a596135b86df30daf07e" +checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" dependencies = [ "proc-macro2", "quote", @@ -2268,9 +2245,9 @@ [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" @@ -2295,12 +2272,6 @@ ] [[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - -[[package]] name = "enum-as-inner" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2474,24 +2445,20 @@ [[package]] name = "evm-coder" -version = "0.1.6" +version = "0.3.0" +source = "git+https://github.com/uniquenetwork/evm-coder?tag=v0.3.0#c4bfdf39d84fbe74c6178417177f2a7f81d92bdd" dependencies = [ "ethereum", "evm-coder-procedural", - "evm-core", - "frame-support", - "hex", - "hex-literal", "impl-trait-for-tuples", "primitive-types", "sha3-const", - "similar-asserts", - "trybuild", ] [[package]] name = "evm-coder-procedural" -version = "0.2.3" +version = "0.3.0" +source = "git+https://github.com/uniquenetwork/evm-coder?tag=v0.3.0#c4bfdf39d84fbe74c6178417177f2a7f81d92bdd" dependencies = [ "Inflector", "hex", @@ -2502,6 +2469,15 @@ ] [[package]] +name = "evm-coder-substrate-procedural" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "evm-core" version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3227,9 +3203,9 @@ [[package]] name = "fs_extra" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "funty" @@ -3239,9 +3215,9 @@ [[package]] name = "futures" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" dependencies = [ "futures-channel", "futures-core", @@ -3254,9 +3230,9 @@ [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" dependencies = [ "futures-core", "futures-sink", @@ -3264,15 +3240,15 @@ [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] name = "futures-executor" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" dependencies = [ "futures-core", "futures-task", @@ -3282,9 +3258,9 @@ [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" [[package]] name = "futures-lite" @@ -3303,9 +3279,9 @@ [[package]] name = "futures-macro" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ "proc-macro2", "quote", @@ -3325,15 +3301,15 @@ [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] name = "futures-timer" @@ -3343,9 +3319,9 @@ [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ "futures-channel", "futures-core", @@ -3469,7 +3445,7 @@ checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ "aho-corasick", - "bstr 1.1.0", + "bstr", "fnv", "log", "regex", @@ -3545,9 +3521,9 @@ [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -3568,6 +3544,12 @@ ] [[package]] +name = "hermit-abi" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" + +[[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3697,9 +3679,9 @@ [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" dependencies = [ "bytes", "futures-channel", @@ -3914,12 +3896,12 @@ [[package]] name = "io-lifetimes" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" dependencies = [ "libc", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -3948,14 +3930,14 @@ [[package]] name = "is-terminal" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" dependencies = [ - "hermit-abi 0.2.6", - "io-lifetimes 1.0.4", - "rustix 0.36.7", - "windows-sys 0.42.0", + "hermit-abi 0.3.0", + "io-lifetimes 1.0.5", + "rustix 0.36.8", + "windows-sys 0.45.0", ] [[package]] @@ -3984,9 +3966,9 @@ [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -4974,7 +4956,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b20a59d985586e4a5aef64564ac77299f8586d8be6cf9106a5a40207e8908efb" dependencies = [ - "rustix 0.36.7", + "rustix 0.36.8", ] [[package]] @@ -5284,9 +5266,9 @@ [[package]] name = "netlink-packet-utils" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25af9cf0dc55498b7bd94a1508af7a78706aa0ab715a73c5169273e03c84845e" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" dependencies = [ "anyhow", "byteorder", @@ -6248,6 +6230,8 @@ version = "0.1.3" dependencies = [ "evm-coder", + "evm-coder-substrate-procedural", + "evm-core", "frame-benchmarking", "frame-support", "frame-system", @@ -6256,6 +6240,7 @@ "scale-info", "sp-core", "sp-std", + "spez", "up-data-structs", ] @@ -7272,9 +7257,9 @@ [[package]] name = "parity-scale-codec" -version = "3.2.2" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ab01d0f889e957861bc65888d5ccbe82c158d0270136ba46820d43837cdf72" +checksum = "c3840933452adf7b3b9145e27086a5a3376c619dca1a21b1e5a5af0d54979bed" dependencies = [ "arrayvec 0.7.2", "bitvec", @@ -7333,7 +7318,7 @@ checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.6", + "parking_lot_core 0.9.7", ] [[package]] @@ -7352,15 +7337,15 @@ [[package]] name = "parking_lot_core" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -8838,10 +8823,16 @@ ] [[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] name = "proc-macro2" -version = "1.0.50" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] @@ -9675,16 +9666,16 @@ [[package]] name = "rustix" -version = "0.36.7" +version = "0.36.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" dependencies = [ "bitflags", "errno", - "io-lifetimes 1.0.4", + "io-lifetimes 1.0.5", "libc", "linux-raw-sys 0.1.4", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -10996,9 +10987,9 @@ [[package]] name = "security-framework" -version = "2.8.1" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c4437699b6d34972de58652c68b98cb5b53a4199ab126db8e20ec8ded29a721" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ "bitflags", "core-foundation", @@ -11072,9 +11063,9 @@ [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a" dependencies = [ "itoa", "ryu", @@ -11206,26 +11197,6 @@ ] [[package]] -name = "similar" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" -dependencies = [ - "bstr 0.2.17", - "unicode-segmentation", -] - -[[package]] -name = "similar-asserts" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf644ad016b75129f01a34a355dcb8d66a5bc803e417c7a77cc5d5ee9fa0f18" -dependencies = [ - "console", - "similar", -] - -[[package]] name = "slab" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -11275,14 +11246,14 @@ [[package]] name = "snow" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774d05a3edae07ce6d68ea6984f3c05e9bba8927e3dd591e3b479e5b03213d0d" +checksum = "12ba5f4d4ff12bdb6a169ed51b7c48c0e0ac4b0b4b31012b2571e97d78d3201d" dependencies = [ "aes-gcm 0.9.4", "blake2", "chacha20poly1305", - "curve25519-dalek 4.0.0-pre.5", + "curve25519-dalek 4.0.0-rc.0", "rand_core 0.6.4", "ring", "rustc_version 0.4.0", @@ -12031,6 +12002,28 @@ ] [[package]] +name = "spez" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "304d2fd6a79879f77b207ba8cd5079c22f872eaad65a3fb8d71799e5bacf368b" +dependencies = [ + "proc-macro-hack", + "spez-macros", +] + +[[package]] +name = "spez-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fa06037d8ccb6cdadc151129062ebc9e4281c1ea44b2c704582245d017d1dc" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -12612,15 +12605,15 @@ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.24.2" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" dependencies = [ "autocfg", "bytes", @@ -12702,9 +12695,9 @@ [[package]] name = "toml_edit" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729bfd096e40da9c001f778f5cdecbd2957929a24e10e5883d9392220a751581" +checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" dependencies = [ "indexmap", "nom8", @@ -12976,21 +12969,6 @@ "sp-weights", "substrate-rpc-client", "zstd", -] - -[[package]] -name = "trybuild" -version = "1.0.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed2c57956f91546d4d33614265a85d55c8e1ab91484853a10335894786d7db6" -dependencies = [ - "glob", - "once_cell", - "serde", - "serde_derive", - "serde_json", - "termcolor", - "toml", ] [[package]] @@ -13108,12 +13086,6 @@ dependencies = [ "tinyvec", ] - -[[package]] -name = "unicode-segmentation" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "unicode-width" @@ -13446,9 +13418,9 @@ [[package]] name = "uuid" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" +checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" dependencies = [ "getrandom 0.2.8", ] @@ -13543,9 +13515,9 @@ [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -13553,9 +13525,9 @@ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", @@ -13568,9 +13540,9 @@ [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ "cfg-if", "js-sys", @@ -13580,9 +13552,9 @@ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -13590,9 +13562,9 @@ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", @@ -13603,9 +13575,9 @@ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "wasm-instrument" @@ -13886,9 +13858,9 @@ [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -14338,6 +14310,30 @@ ] [[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] name = "windows_aarch64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -14644,9 +14640,9 @@ [[package]] name = "zstd-sys" -version = "2.0.5+zstd.1.5.2" +version = "2.0.6+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc50ffce891ad571e9f9afe5039c4837bede781ac4bb13052ed7ae695518596" +checksum = "68a3f9792c0c3dc6c165840a75f47ae1f4da402c2d006881129579f6597e801b" dependencies = [ "cc", "libc", --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ [workspace.dependencies] # Unique -evm-coder = { default-features = false, path = "crates/evm-coder" } +evm-coder = { git = "https://github.com/uniquenetwork/evm-coder", tag = "v0.3.0", default-features = false } struct-versioning = { path = "crates/struct-versioning" } precompile-utils-macro = { path = "runtime/common/ethereum/precompiles/utils/macro" } up-common = { path = "primitives/common", default-features = false } --- a/crates/evm-coder/CHANGELOG.md +++ /dev/null @@ -1,48 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. - - -## [v0.1.6] - 2023-01-12 - -### Added -- Support Option type. -### Removed -- Frontier dependency. - -## [v0.1.5] - 2022-11-30 - -### Added -- Derive macro to support structures and enums. - -## [v0.1.4] - 2022-11-02 - -### Added - -- Named structures support. - -## [v0.1.3] - 2022-08-29 - -### Fixed - -- Parsing simple values. - -## [v0.1.2] 2022-08-19 - -### Added - -- Implementation `AbiWrite` for tuples. - -### Fixes - -- Tuple generation for solidity. - -## [v0.1.1] 2022-08-16 - -### Other changes - -- build: Upgrade polkadot to v0.9.27 2c498572636f2b34d53b1c51b7283a761a7dc90a - -- build: Upgrade polkadot to v0.9.26 85515e54c4ca1b82a2630034e55dcc804c643bf8 - -- build: Upgrade polkadot to v0.9.25 cdfb9bdc7b205ff1b5134f034ef9973d769e5e6b --- a/crates/evm-coder/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -edition = "2021" -license = "GPLv3" -name = "evm-coder" -version = "0.1.6" - -[dependencies] -sha3-const = { version = "0.1.1", default-features = false } -# evm-coder reexports those proc-macro -evm-coder-procedural = { path = "./procedural" } -# Evm uses primitive-types for H160, H256 and others -primitive-types = { workspace = true } -# Evm doesn't have reexports for log and others -ethereum = { workspace = true } -frame-support = { workspace = true } -# Error types for execution -evm-core = { workspace = true } -# We have tuple-heavy code in solidity.rs -impl-trait-for-tuples = { workspace = true } - -[dev-dependencies] -# We want to assert some large binary blobs equality in tests -hex = "0.4.3" -hex-literal = { workspace = true } -similar-asserts = "1.4.2" -trybuild = "1.0" - -[features] -default = ["std"] -std = ["ethereum/std", "evm-core/std", "frame-support/std", "primitive-types/std"] -# Stub/interface generation -stubgen = [] --- a/crates/evm-coder/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# evm-coder - -Library for seamless call translation between Rust and Solidity code - -By encoding solidity definitions in Rust, this library also provides generation of -solidity interfaces for ethereum developers - -## Overview - -Most of this library functionality shouldn't be used directly, but via macros - -- [`solidity_interface`] -- [`ToLog`] - - \ No newline at end of file --- a/crates/evm-coder/procedural/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -edition = "2021" -license = "GPLv3" -name = "evm-coder-procedural" -version = "0.2.3" - -[lib] -proc-macro = true - -[dependencies] -# Ethereum uses keccak (=sha3) for selectors -sha3 = "0.10.1" -# Value formatting -Inflector = "0.11.4" -hex = "0.4.3" -# General proc-macro utilities -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "1.0", features = ["full"] } --- a/crates/evm-coder/procedural/src/abi_derive/derive_enum.rs +++ /dev/null @@ -1,199 +0,0 @@ -use quote::quote; - -use super::extract_docs; - -pub fn impl_solidity_option<'a>( - docs: Vec, - name: &proc_macro2::Ident, - enum_options: impl Iterator + Clone, -) -> proc_macro2::TokenStream { - let variant_names = enum_options.clone().map(|opt| { - let opt = &opt.ident; - let s = name.to_string() + "." + opt.to_string().as_str(); - let as_string = proc_macro2::Literal::string(s.as_str()); - quote!(#name::#opt => #as_string,) - }); - let solidity_name = name.to_string(); - - let solidity_fields = enum_options.map(|v| { - let docs = extract_docs(&v.attrs).expect("TODO: handle bad docs"); - let name = v.ident.to_string(); - quote! { - SolidityEnumVariant { - docs: &[#(#docs),*], - name: #name, - } - } - }); - - quote!( - #[cfg(feature = "stubgen")] - impl ::evm_coder::solidity::SolidityEnumTy for #name { - fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector) -> String { - use evm_coder::solidity::*; - use core::fmt::Write; - let interface = SolidityEnum { - docs: &[#(#docs),*], - name: #solidity_name, - fields: &[#( - #solidity_fields, - )*], - }; - let mut out = String::new(); - let _ = interface.format(&mut out, tc); - tc.collect(out); - #solidity_name.to_string() - } - fn solidity_option(&self) -> &str { - match self { - #(#variant_names)* - } - } - } - ) -} - -pub fn impl_enum_from_u8<'a>( - name: &proc_macro2::Ident, - enum_options: impl Iterator, -) -> proc_macro2::TokenStream { - let error_str = format!("Value not convertible into enum \"{name}\""); - let error_str = proc_macro2::Literal::string(&error_str); - let enum_options = enum_options.enumerate().map(|(i, opt)| { - let opt = &opt.ident; - let n = proc_macro2::Literal::u8_suffixed(i as u8); - quote! {#n => Ok(#name::#opt),} - }); - - quote!( - impl TryFrom for #name { - type Error = &'static str; - - fn try_from(value: u8) -> ::core::result::Result { - const err: &'static str = #error_str; - match value { - #(#enum_options)* - _ => Err(err) - } - } - } - ) -} - -pub fn impl_enum_abi_type(name: &syn::Ident) -> proc_macro2::TokenStream { - quote! { - impl ::evm_coder::abi::AbiType for #name { - const SIGNATURE: ::evm_coder::custom_signature::SignatureUnit = ::SIGNATURE; - - fn is_dynamic() -> bool { - ::is_dynamic() - } - fn size() -> usize { - ::size() - } - } - } -} - -pub fn impl_enum_abi_read(name: &syn::Ident) -> proc_macro2::TokenStream { - quote!( - impl ::evm_coder::abi::AbiRead for #name { - fn abi_read(reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result { - Ok( - ::abi_read(reader)? - .try_into()? - ) - } - } - ) -} - -pub fn impl_enum_abi_write(name: &syn::Ident) -> proc_macro2::TokenStream { - quote!( - impl ::evm_coder::abi::AbiWrite for #name { - fn abi_write(&self, writer: &mut ::evm_coder::abi::AbiWriter) { - ::evm_coder::abi::AbiWrite::abi_write(&(*self as u8), writer); - } - } - ) -} - -pub fn impl_enum_solidity_type_name(name: &syn::Ident) -> proc_macro2::TokenStream { - quote!( - #[cfg(feature = "stubgen")] - impl ::evm_coder::solidity::SolidityTypeName for #name { - fn solidity_name( - writer: &mut impl ::core::fmt::Write, - tc: &::evm_coder::solidity::TypeCollector, - ) -> ::core::fmt::Result { - write!(writer, "{}", tc.collect_enum::()) - } - - fn is_simple() -> bool { - true - } - - fn solidity_default( - writer: &mut impl ::core::fmt::Write, - tc: &::evm_coder::solidity::TypeCollector, - ) -> ::core::fmt::Result { - write!(writer, "{}", <#name as ::evm_coder::solidity::SolidityEnumTy>::solidity_option(&<#name>::default())) - } - } - ) -} - -pub fn check_enum_fields(de: &syn::DataEnum) -> syn::Result<()> { - for v in de.variants.iter() { - if !v.fields.is_empty() { - return Err(syn::Error::new( - v.ident.span(), - "Enumeration parameters should not have fields", - )); - } else if v.discriminant.is_some() { - return Err(syn::Error::new( - v.ident.span(), - "Enumeration options should not have an explicit specified value", - )); - } - } - - Ok(()) -} - -pub fn check_repr_u8(name: &syn::Ident, attrs: &[syn::Attribute]) -> syn::Result<()> { - let mut has_repr = false; - for attr in attrs.iter() { - if attr.path.is_ident("repr") { - has_repr = true; - let meta = attr.parse_meta()?; - check_meta_u8(&meta)?; - } - } - - if !has_repr { - return Err(syn::Error::new(name.span(), "Enum is not \"repr(u8)\"")); - } - - Ok(()) -} - -fn check_meta_u8(meta: &syn::Meta) -> Result<(), syn::Error> { - if let syn::Meta::List(p) = meta { - for nm in p.nested.iter() { - if let syn::NestedMeta::Meta(syn::Meta::Path(p)) = nm { - if !p.is_ident("u8") { - return Err(syn::Error::new( - p.segments - .first() - .expect("repr segments are empty") - .ident - .span(), - "Enum is not \"repr(u8)\"", - )); - } - } - } - } - Ok(()) -} --- a/crates/evm-coder/procedural/src/abi_derive/derive_struct.rs +++ /dev/null @@ -1,231 +0,0 @@ -use super::extract_docs; -use quote::quote; -use syn::Field; - -pub fn tuple_type<'a>( - field_types: impl Iterator + Clone, -) -> proc_macro2::TokenStream { - let field_types = field_types.map(|ty| quote!(#ty,)); - quote! {(#(#field_types)*)} -} - -pub fn tuple_ref_type<'a>( - field_types: impl Iterator + Clone, -) -> proc_macro2::TokenStream { - let field_types = field_types.map(|ty| quote!(&#ty,)); - quote! {(#(#field_types)*)} -} - -pub fn tuple_data_as_ref( - is_named_fields: bool, - field_names: impl Iterator + Clone, -) -> proc_macro2::TokenStream { - let field_names = field_names.enumerate().map(|(i, field)| { - if is_named_fields { - quote!(&self.#field,) - } else { - let field = proc_macro2::Literal::usize_unsuffixed(i); - quote!(&self.#field,) - } - }); - quote! {(#(#field_names)*)} -} - -pub fn tuple_names( - is_named_fields: bool, - field_names: impl Iterator + Clone, -) -> proc_macro2::TokenStream { - let field_names = field_names.enumerate().map(|(i, field)| { - if is_named_fields { - quote!(#field,) - } else { - let field = proc_macro2::Ident::new( - format!("field{}", i).as_str(), - proc_macro2::Span::call_site(), - ); - quote!(#field,) - } - }); - quote! {(#(#field_names)*)} -} - -pub fn struct_from_tuple( - name: &syn::Ident, - is_named_fields: bool, - field_names: impl Iterator + Clone, -) -> proc_macro2::TokenStream { - let field_names = field_names.enumerate().map(|(i, field)| { - if is_named_fields { - quote!(#field,) - } else { - let field = proc_macro2::Ident::new( - format!("field{}", i).as_str(), - proc_macro2::Span::call_site(), - ); - quote!(#field,) - } - }); - - if is_named_fields { - quote! {#name {#(#field_names)*}} - } else { - quote! {#name (#(#field_names)*)} - } -} - -pub fn map_field_to_name(field: (usize, &syn::Field)) -> syn::Ident { - match field.1.ident.as_ref() { - Some(name) => name.clone(), - None => { - let mut name = "field".to_string(); - name.push_str(field.0.to_string().as_str()); - syn::Ident::new(name.as_str(), proc_macro2::Span::call_site()) - } - } -} - -pub fn map_field_to_type(field: &syn::Field) -> &syn::Type { - &field.ty -} - -pub fn impl_can_be_placed_in_vec(ident: &syn::Ident) -> proc_macro2::TokenStream { - quote! { - impl ::evm_coder::sealed::CanBePlacedInVec for #ident {} - } -} - -pub fn impl_struct_abi_type( - name: &syn::Ident, - tuple_type: proc_macro2::TokenStream, -) -> proc_macro2::TokenStream { - quote! { - impl ::evm_coder::abi::AbiType for #name { - const SIGNATURE: ::evm_coder::custom_signature::SignatureUnit = <#tuple_type as ::evm_coder::abi::AbiType>::SIGNATURE; - fn is_dynamic() -> bool { - <#tuple_type as ::evm_coder::abi::AbiType>::is_dynamic() - } - fn size() -> usize { - <#tuple_type as ::evm_coder::abi::AbiType>::size() - } - } - } -} - -pub fn impl_struct_abi_read( - name: &syn::Ident, - tuple_type: proc_macro2::TokenStream, - tuple_names: proc_macro2::TokenStream, - struct_from_tuple: proc_macro2::TokenStream, -) -> proc_macro2::TokenStream { - quote!( - impl ::evm_coder::abi::AbiRead for #name { - fn abi_read(reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result { - let #tuple_names = <#tuple_type as ::evm_coder::abi::AbiRead>::abi_read(reader)?; - Ok(#struct_from_tuple) - } - } - ) -} - -pub fn impl_struct_abi_write( - name: &syn::Ident, - _is_named_fields: bool, - tuple_type: proc_macro2::TokenStream, - tuple_data: proc_macro2::TokenStream, -) -> proc_macro2::TokenStream { - quote!( - impl ::evm_coder::abi::AbiWrite for #name { - fn abi_write(&self, writer: &mut ::evm_coder::abi::AbiWriter) { - <#tuple_type as ::evm_coder::abi::AbiWrite>::abi_write(&#tuple_data, writer) - } - } - ) -} - -pub fn impl_struct_solidity_type<'a>( - name: &syn::Ident, - docs: Vec, - fields: impl Iterator + Clone, -) -> proc_macro2::TokenStream { - let solidity_name = name.to_string(); - let solidity_fields = fields.enumerate().map(|(i, f)| { - let name = f - .ident - .as_ref() - .map(|i| i.to_string()) - .unwrap_or_else(|| format!("field_{i}")); - let ty = &f.ty; - let docs = extract_docs(&f.attrs).expect("TODO: handle bad docs"); - quote! { - SolidityStructField::<#ty> { - docs: &[#(#docs),*], - name: #name, - ty: ::core::marker::PhantomData, - } - } - }); - quote! { - #[cfg(feature = "stubgen")] - impl ::evm_coder::solidity::SolidityStructTy for #name { - /// Generate solidity definitions for methods described in this struct - fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector) -> String { - use evm_coder::solidity::*; - use core::fmt::Write; - let interface = SolidityStruct { - docs: &[#(#docs),*], - name: #solidity_name, - fields: (#( - #solidity_fields, - )*), - }; - let mut out = String::new(); - let _ = interface.format(&mut out, tc); - tc.collect(out); - #solidity_name.to_string() - } - } - } -} - -pub fn impl_struct_solidity_type_name<'a>( - name: &syn::Ident, - field_types: impl Iterator + Clone, - params_count: usize, -) -> proc_macro2::TokenStream { - let arg_dafaults = field_types.enumerate().map(|(i, ty)| { - let mut defult_value = quote!(<#ty as ::evm_coder::solidity::SolidityTypeName - >::solidity_default(writer, tc)?;); - let last_item = params_count - 1; - if i != last_item { - defult_value.extend(quote! {write!(writer, ",")?;}) - } - defult_value - }); - - quote! { - #[cfg(feature = "stubgen")] - impl ::evm_coder::solidity::SolidityTypeName for #name { - fn solidity_name( - writer: &mut impl ::core::fmt::Write, - tc: &::evm_coder::solidity::TypeCollector, - ) -> ::core::fmt::Result { - write!(writer, "{}", tc.collect_struct::()) - } - - fn is_simple() -> bool { - false - } - - fn solidity_default( - writer: &mut impl ::core::fmt::Write, - tc: &::evm_coder::solidity::TypeCollector, - ) -> ::core::fmt::Result { - write!(writer, "{}(", tc.collect_struct::())?; - - #(#arg_dafaults)* - - write!(writer, ")") - } - } - } -} --- a/crates/evm-coder/procedural/src/abi_derive/mod.rs +++ /dev/null @@ -1,118 +0,0 @@ -mod derive_enum; -mod derive_struct; - -use quote::quote; -use derive_struct::*; -use derive_enum::*; - -pub(crate) fn impl_abi_macro(ast: &syn::DeriveInput) -> syn::Result { - let name = &ast.ident; - match &ast.data { - syn::Data::Struct(ds) => expand_struct(ds, ast), - syn::Data::Enum(de) => expand_enum(de, ast), - syn::Data::Union(_) => Err(syn::Error::new(name.span(), "Unions not supported")), - } -} - -fn expand_struct( - ds: &syn::DataStruct, - ast: &syn::DeriveInput, -) -> syn::Result { - let name = &ast.ident; - let docs = extract_docs(&ast.attrs)?; - let (is_named_fields, field_names, field_types, params_count) = match ds.fields { - syn::Fields::Named(ref fields) => Ok(( - true, - fields.named.iter().enumerate().map(map_field_to_name), - fields.named.iter().map(map_field_to_type), - fields.named.len(), - )), - syn::Fields::Unnamed(ref fields) => Ok(( - false, - fields.unnamed.iter().enumerate().map(map_field_to_name), - fields.unnamed.iter().map(map_field_to_type), - fields.unnamed.len(), - )), - syn::Fields::Unit => Err(syn::Error::new(name.span(), "Unit structs not supported")), - }?; - - if params_count == 0 { - return Err(syn::Error::new(name.span(), "Empty structs not supported")); - }; - - let tuple_type = tuple_type(field_types.clone()); - let tuple_ref_type = tuple_ref_type(field_types.clone()); - let tuple_data = tuple_data_as_ref(is_named_fields, field_names.clone()); - let tuple_names = tuple_names(is_named_fields, field_names.clone()); - let struct_from_tuple = struct_from_tuple(name, is_named_fields, field_names.clone()); - - let can_be_plcaed_in_vec = impl_can_be_placed_in_vec(name); - let abi_type = impl_struct_abi_type(name, tuple_type.clone()); - let abi_read = impl_struct_abi_read(name, tuple_type, tuple_names, struct_from_tuple); - let abi_write = impl_struct_abi_write(name, is_named_fields, tuple_ref_type, tuple_data); - let solidity_type = impl_struct_solidity_type(name, docs, ds.fields.iter()); - let solidity_type_name = - impl_struct_solidity_type_name(name, field_types.clone(), params_count); - - Ok(quote! { - #can_be_plcaed_in_vec - #abi_type - #abi_read - #abi_write - #solidity_type - #solidity_type_name - }) -} - -fn expand_enum( - de: &syn::DataEnum, - ast: &syn::DeriveInput, -) -> syn::Result { - let name = &ast.ident; - check_repr_u8(name, &ast.attrs)?; - check_enum_fields(de)?; - let docs = extract_docs(&ast.attrs)?; - let enum_options = de.variants.iter(); - - let from = impl_enum_from_u8(name, enum_options.clone()); - let solidity_option = impl_solidity_option(docs, name, enum_options.clone()); - let can_be_plcaed_in_vec = impl_can_be_placed_in_vec(name); - let abi_type = impl_enum_abi_type(name); - let abi_read = impl_enum_abi_read(name); - let abi_write = impl_enum_abi_write(name); - let solidity_type_name = impl_enum_solidity_type_name(name); - - Ok(quote! { - #from - #solidity_option - #can_be_plcaed_in_vec - #abi_type - #abi_read - #abi_write - #solidity_type_name - }) -} - -fn extract_docs(attrs: &[syn::Attribute]) -> syn::Result> { - attrs - .iter() - .filter_map(|attr| { - if let Some(ps) = attr.path.segments.first() { - if ps.ident == "doc" { - let meta = match attr.parse_meta() { - Ok(meta) => meta, - Err(e) => return Some(Err(e)), - }; - match meta { - syn::Meta::NameValue(mnv) => match &mnv.lit { - syn::Lit::Str(ls) => return Some(Ok(ls.value())), - _ => unreachable!(), - }, - _ => unreachable!(), - } - } - } - None - }) - .collect() -} --- a/crates/evm-coder/procedural/src/lib.rs +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -#![allow(dead_code)] - -use inflector::cases; -use proc_macro::TokenStream; -use quote::quote; -use sha3::{Digest, Keccak256}; -use syn::{ - DeriveInput, GenericArgument, Ident, ItemImpl, Pat, Path, PathArguments, PathSegment, Type, - parse_macro_input, spanned::Spanned, -}; - -mod abi_derive; -mod solidity_interface; -mod to_log; - -fn fn_selector_str(input: &str) -> u32 { - let mut hasher = Keccak256::new(); - hasher.update(input.as_bytes()); - let result = hasher.finalize(); - - let mut selector_bytes = [0; 4]; - selector_bytes.copy_from_slice(&result[0..4]); - - u32::from_be_bytes(selector_bytes) -} - -/// Returns solidity function selector (first 4 bytes of hash) by its -/// textual representation -/// -/// ```ignore -/// use evm_coder_macros::fn_selector; -/// -/// assert_eq!(fn_selector!(transfer(address, uint256)), 0xa9059cbb); -/// ``` -#[proc_macro] -pub fn fn_selector(input: TokenStream) -> TokenStream { - let input = input.to_string().replace(' ', ""); - let selector = fn_selector_str(&input); - - (quote! { - #selector - }) - .into() -} - -fn event_selector_str(input: &str) -> [u8; 32] { - let mut hasher = Keccak256::new(); - hasher.update(input.as_bytes()); - let result = hasher.finalize(); - - let mut selector_bytes = [0; 32]; - selector_bytes.copy_from_slice(&result[0..32]); - selector_bytes -} - -/// Returns solidity topic (hash) by its textual representation -/// -/// ```ignore -/// use evm_coder_macros::event_topic; -/// -/// assert_eq!( -/// format!("{:x}", event_topic!(Transfer(address, address, uint256))), -/// "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", -/// ); -/// ``` -#[proc_macro] -pub fn event_topic(stream: TokenStream) -> TokenStream { - let input = stream.to_string().replace(' ', ""); - let selector_bytes = event_selector_str(&input); - - (quote! { - ::primitive_types::H256([#( - #selector_bytes, - )*]) - }) - .into() -} - -fn parse_path(ty: &Type) -> syn::Result<&Path> { - match &ty { - syn::Type::Path(pat) => { - if let Some(qself) = &pat.qself { - return Err(syn::Error::new(qself.ty.span(), "no receiver expected")); - } - Ok(&pat.path) - } - _ => Err(syn::Error::new(ty.span(), "expected ty to be path")), - } -} - -fn parse_path_segment(path: &Path) -> syn::Result<&PathSegment> { - if path.segments.len() != 1 { - return Err(syn::Error::new( - path.span(), - "expected path to have only one segment", - )); - } - let last_segment = &path.segments.last().unwrap(); - Ok(last_segment) -} - -fn parse_ident_from_pat(pat: &Pat) -> syn::Result<&Ident> { - match pat { - Pat::Ident(i) => Ok(&i.ident), - _ => Err(syn::Error::new(pat.span(), "expected pat ident")), - } -} - -fn parse_ident_from_segment(segment: &PathSegment, allow_generics: bool) -> syn::Result<&Ident> { - if segment.arguments != PathArguments::None && !allow_generics { - return Err(syn::Error::new( - segment.arguments.span(), - "unexpected generic type", - )); - } - Ok(&segment.ident) -} - -fn parse_ident_from_path(path: &Path, allow_generics: bool) -> syn::Result<&Ident> { - let segment = parse_path_segment(path)?; - parse_ident_from_segment(segment, allow_generics) -} - -fn parse_ident_from_type(ty: &Type, allow_generics: bool) -> syn::Result<&Ident> { - let path = parse_path(ty)?; - parse_ident_from_path(path, allow_generics) -} - -// Gets T out of Result -fn parse_result_ok(ty: &Type) -> syn::Result<&Type> { - let path = parse_path(ty)?; - let segment = parse_path_segment(path)?; - - if segment.ident != "Result" { - return Err(syn::Error::new( - ty.span(), - "expected Result as return type (no renamed aliases allowed)", - )); - } - let args = match &segment.arguments { - PathArguments::AngleBracketed(e) => e, - _ => { - return Err(syn::Error::new( - segment.arguments.span(), - "missing Result generics", - )) - } - }; - - let args = &args.args; - let arg = args.first().unwrap(); - - let ty = match arg { - GenericArgument::Type(ty) => ty, - _ => { - return Err(syn::Error::new( - arg.span(), - "expected first generic to be type", - )) - } - }; - - Ok(ty) -} - -fn pascal_ident_to_call(ident: &Ident) -> Ident { - let name = format!("{}Call", ident); - Ident::new(&name, ident.span()) -} -fn snake_ident_to_pascal(ident: &Ident) -> Ident { - let name = ident.to_string(); - let name = cases::pascalcase::to_pascal_case(&name); - Ident::new(&name, ident.span()) -} -fn snake_ident_to_screaming(ident: &Ident) -> Ident { - let name = ident.to_string(); - let name = cases::screamingsnakecase::to_screaming_snake_case(&name); - Ident::new(&name, ident.span()) -} -fn pascal_ident_to_snake_call(ident: &Ident) -> Ident { - let name = ident.to_string(); - let name = cases::snakecase::to_snake_case(&name); - let name = format!("call_{}", name); - Ident::new(&name, ident.span()) -} - -/// See documentation for this proc-macro reexported in `evm-coder` crate -#[proc_macro_attribute] -pub fn solidity_interface(args: TokenStream, stream: TokenStream) -> TokenStream { - let args = parse_macro_input!(args as solidity_interface::InterfaceInfo); - - let input: ItemImpl = match syn::parse(stream) { - Ok(t) => t, - Err(e) => return e.to_compile_error().into(), - }; - - let expanded = match solidity_interface::SolidityInterface::try_from(args, &input) { - Ok(v) => v.expand(), - Err(e) => e.to_compile_error(), - }; - - (quote! { - #input - - #expanded - }) - .into() -} - -#[proc_macro_attribute] -pub fn solidity(_args: TokenStream, stream: TokenStream) -> TokenStream { - stream -} -#[proc_macro_attribute] -pub fn weight(_args: TokenStream, stream: TokenStream) -> TokenStream { - stream -} - -/// See documentation for this proc-macro reexported in `evm-coder` crate -#[proc_macro_derive(ToLog, attributes(indexed))] -pub fn to_log(value: TokenStream) -> TokenStream { - let input = parse_macro_input!(value as DeriveInput); - - match to_log::Events::try_from(&input) { - Ok(e) => e.expand(), - Err(e) => e.to_compile_error(), - } - .into() -} - -#[proc_macro_derive(AbiCoder)] -pub fn abi_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let ast = syn::parse(input).unwrap(); - let ts = match abi_derive::impl_abi_macro(&ast) { - Ok(e) => e, - Err(e) => e.to_compile_error(), - }; - ts.into() -} --- a/crates/evm-coder/procedural/src/solidity_interface.rs +++ /dev/null @@ -1,1099 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -#![allow(dead_code)] - -// NOTE: In order to understand this Rust macro better, first read this chapter -// about Procedural Macros in Rust book: -// https://doc.rust-lang.org/reference/procedural-macros.html - -use proc_macro2::TokenStream; -use quote::{quote, format_ident}; -use inflector::cases; -use syn::{ - Expr, FnArg, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Lit, Meta, MetaNameValue, - PatType, ReturnType, Type, - spanned::Spanned, - parse::{Parse, ParseStream}, - parenthesized, Token, LitInt, LitStr, -}; - -use crate::{ - parse_ident_from_pat, parse_ident_from_path, parse_path, parse_path_segment, parse_result_ok, - pascal_ident_to_call, pascal_ident_to_snake_call, snake_ident_to_pascal, - snake_ident_to_screaming, -}; - -struct Is { - name: Ident, - pascal_call_name: Ident, - snake_call_name: Ident, - via: Option<(Type, Ident)>, - condition: Option, -} -impl Is { - fn expand_call_def(&self, gen_ref: &proc_macro2::TokenStream) -> proc_macro2::TokenStream { - let name = &self.name; - let pascal_call_name = &self.pascal_call_name; - quote! { - #name(#pascal_call_name #gen_ref) - } - } - - fn expand_interface_id(&self) -> proc_macro2::TokenStream { - let pascal_call_name = &self.pascal_call_name; - quote! { - interface_id ^= u32::from_be_bytes(#pascal_call_name::interface_id()); - } - } - - fn expand_supports_interface( - &self, - generics: &proc_macro2::TokenStream, - ) -> proc_macro2::TokenStream { - let pascal_call_name = &self.pascal_call_name; - let condition = self.condition.as_ref().map(|condition| { - quote! { - (#condition) && - } - }); - quote! { - #condition <#pascal_call_name #generics>::supports_interface(this, interface_id) - } - } - - fn expand_variant_weight(&self) -> proc_macro2::TokenStream { - let name = &self.name; - quote! { - Self::#name(call) => call.weight() - } - } - - fn expand_variant_call( - &self, - call_name: &proc_macro2::Ident, - generics: &proc_macro2::TokenStream, - ) -> proc_macro2::TokenStream { - let name = &self.name; - let pascal_call_name = &self.pascal_call_name; - let via_typ = self - .via - .as_ref() - .map(|(t, _)| quote! {#t}) - .unwrap_or_else(|| quote! {Self}); - let via_map = self - .via - .as_ref() - .map(|(_, i)| quote! {.#i()}) - .unwrap_or_default(); - let condition = self.condition.as_ref().map(|condition| { - quote! { - if ({let this = &self; (#condition)}) - } - }); - quote! { - #call_name::#name(call) #condition => return <#via_typ as ::evm_coder::Callable<#pascal_call_name #generics>>::call(self #via_map, Msg { - call, - caller: c.caller, - value: c.value, - }) - } - } - - fn expand_parse(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream { - let name = &self.name; - let pascal_call_name = &self.pascal_call_name; - quote! { - if let Some(parsed_call) = <#pascal_call_name #generics>::parse(method_id, reader)? { - return Ok(Some(Self::#name(parsed_call))) - } - } - } - - fn expand_generator(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream { - let pascal_call_name = &self.pascal_call_name; - quote! { - <#pascal_call_name #generics>::generate_solidity_interface(tc, is_impl); - } - } - - fn expand_event_generator(&self) -> proc_macro2::TokenStream { - let name = &self.name; - quote! { - #name::generate_solidity_interface(tc, is_impl); - } - } -} - -#[derive(Default)] -struct IsList(Vec); -impl Parse for IsList { - fn parse(input: ParseStream) -> syn::Result { - let mut out = vec![]; - loop { - if input.is_empty() { - break; - } - let name = input.parse::()?; - let lookahead = input.lookahead1(); - - let mut condition: Option = None; - let mut via: Option<(Type, Ident)> = None; - - if lookahead.peek(syn::token::Paren) { - let contents; - parenthesized!(contents in input); - let input = contents; - - while !input.is_empty() { - let lookahead = input.lookahead1(); - if lookahead.peek(Token![if]) { - input.parse::()?; - let contents; - parenthesized!(contents in input); - let contents = contents.parse::()?; - - if condition.replace(contents).is_some() { - return Err(syn::Error::new(input.span(), "condition is already set")); - } - } else if lookahead.peek(kw::via) { - input.parse::()?; - let contents; - parenthesized!(contents in input); - - let method = contents.parse::()?; - contents.parse::()?; - let ty = contents.parse::()?; - - if via.replace((ty, method)).is_some() { - return Err(syn::Error::new(input.span(), "via is already set")); - } - } else { - return Err(lookahead.error()); - } - - if input.peek(Token![,]) { - input.parse::()?; - } else if !input.is_empty() { - return Err(syn::Error::new(input.span(), "expected end")); - } - } - } else if lookahead.peek(Token![,]) || input.is_empty() { - // Pass - } else { - return Err(lookahead.error()); - }; - out.push(Is { - pascal_call_name: pascal_ident_to_call(&name), - snake_call_name: pascal_ident_to_snake_call(&name), - name, - via, - condition, - }); - if input.peek(Token![,]) { - input.parse::()?; - continue; - } else { - break; - } - } - Ok(Self(out)) - } -} - -pub struct InterfaceInfo { - name: Ident, - is: IsList, - inline_is: IsList, - events: IsList, - expect_selector: Option, -} -impl Parse for InterfaceInfo { - fn parse(input: ParseStream) -> syn::Result { - let mut name = None; - let mut is = None; - let mut inline_is = None; - let mut events = None; - let mut expect_selector = None; - // TODO: create proc-macro to optimize proc-macro boilerplate? :D - loop { - let lookahead = input.lookahead1(); - if lookahead.peek(kw::name) { - let k = input.parse::()?; - input.parse::()?; - if name.replace(input.parse::()?).is_some() { - return Err(syn::Error::new(k.span(), "name is already set")); - } - } else if lookahead.peek(kw::is) { - let k = input.parse::()?; - let contents; - parenthesized!(contents in input); - if is.replace(contents.parse::()?).is_some() { - return Err(syn::Error::new(k.span(), "is is already set")); - } - } else if lookahead.peek(kw::inline_is) { - let k = input.parse::()?; - let contents; - parenthesized!(contents in input); - if inline_is.replace(contents.parse::()?).is_some() { - return Err(syn::Error::new(k.span(), "inline_is is already set")); - } - } else if lookahead.peek(kw::events) { - let k = input.parse::()?; - let contents; - parenthesized!(contents in input); - if events.replace(contents.parse::()?).is_some() { - return Err(syn::Error::new(k.span(), "events is already set")); - } - } else if lookahead.peek(kw::expect_selector) { - let k = input.parse::()?; - input.parse::()?; - let value = input.parse::()?; - if expect_selector - .replace(value.base10_parse::()?) - .is_some() - { - return Err(syn::Error::new(k.span(), "expect_selector is already set")); - } - } else if input.is_empty() { - break; - } else { - return Err(lookahead.error()); - } - if input.peek(Token![,]) { - input.parse::()?; - } else { - break; - } - } - Ok(Self { - name: name.ok_or_else(|| syn::Error::new(input.span(), "missing name"))?, - is: is.unwrap_or_default(), - inline_is: inline_is.unwrap_or_default(), - events: events.unwrap_or_default(), - expect_selector, - }) - } -} - -struct MethodInfo { - rename_selector: Option, - hide: bool, -} -impl Parse for MethodInfo { - fn parse(input: ParseStream) -> syn::Result { - let mut rename_selector = None; - let mut hide = false; - while !input.is_empty() { - let lookahead = input.lookahead1(); - if lookahead.peek(kw::rename_selector) { - let k = input.parse::()?; - input.parse::()?; - if rename_selector - .replace(input.parse::()?.value()) - .is_some() - { - return Err(syn::Error::new(k.span(), "rename_selector is already set")); - } - } else if lookahead.peek(kw::hide) { - input.parse::()?; - hide = true; - } else { - return Err(lookahead.error()); - } - - if input.peek(Token![,]) { - input.parse::()?; - } else if !input.is_empty() { - return Err(syn::Error::new(input.span(), "expected end")); - } - } - Ok(Self { - rename_selector, - hide, - }) - } -} - -trait AbiTypeHelper { - fn plain(&self) -> syn::Result<&Ident>; - fn is_value(&self) -> bool; - fn is_caller(&self) -> bool; - fn is_special(&self) -> bool; -} - -impl AbiTypeHelper for Type { - fn plain(&self) -> syn::Result<&Ident> { - let path = parse_path(self)?; - let segment = parse_path_segment(path)?; - if !segment.arguments.is_empty() { - return Err(syn::Error::new(self.span(), "Not plain type")); - } - Ok(&segment.ident) - } - - fn is_value(&self) -> bool { - if let Ok(ident) = self.plain() { - return ident == "Value"; - } - false - } - - fn is_caller(&self) -> bool { - if let Ok(ident) = self.plain() { - return ident == "Caller"; - } - false - } - - fn is_special(&self) -> bool { - self.is_caller() || self.is_value() - } -} - -#[derive(Debug)] -struct MethodArg { - name: Ident, - camel_name: String, - ty: Type, -} -impl MethodArg { - fn try_from(value: &PatType) -> syn::Result { - let name = parse_ident_from_pat(&value.pat)?.clone(); - Ok(Self { - camel_name: cases::camelcase::to_camel_case(&name.to_string()), - name, - ty: value.ty.as_ref().clone(), - }) - } - fn is_value(&self) -> bool { - self.ty.is_value() - } - fn is_caller(&self) -> bool { - self.ty.is_caller() - } - fn is_special(&self) -> bool { - self.ty.is_special() - } - - fn expand_call_def(&self) -> proc_macro2::TokenStream { - assert!(!self.is_special()); - let name = &self.name; - let ty = &self.ty; - - quote! { - #name: #ty - } - } - - fn expand_parse(&self) -> proc_macro2::TokenStream { - assert!(!self.is_special()); - let name = &self.name; - let ty = &self.ty; - quote! { - #name: { - let value = <#ty as ::evm_coder::abi::AbiRead>::abi_read(reader)?; - if !is_dynamic {reader.bytes_read(<#ty as ::evm_coder::abi::AbiType>::size())}; - value - } - } - } - - fn expand_call_arg(&self) -> proc_macro2::TokenStream { - if self.is_value() { - quote! { - c.value.clone() - } - } else if self.is_caller() { - quote! { - c.caller.clone() - } - } else { - let name = &self.name; - quote! { - #name - } - } - } - - fn expand_solidity_argument(&self) -> proc_macro2::TokenStream { - let camel_name = &self.camel_name.to_string(); - let ty = &self.ty; - quote! { - >::new(#camel_name) - } - } -} - -#[derive(PartialEq)] -enum Mutability { - Mutable, - View, - Pure, -} - -/// Group all keywords for this macro. Usage example: -/// #[solidity_interface(name = "B", inline_is(A))] -mod kw { - syn::custom_keyword!(weight); - - syn::custom_keyword!(via); - syn::custom_keyword!(returns); - syn::custom_keyword!(name); - syn::custom_keyword!(is); - syn::custom_keyword!(inline_is); - syn::custom_keyword!(events); - syn::custom_keyword!(expect_selector); - - syn::custom_keyword!(rename_selector); - syn::custom_keyword!(hide); -} - -/// Rust methods are parsed into this structure when Solidity code is generated -struct Method { - name: Ident, - camel_name: String, - pascal_name: Ident, - screaming_name: Ident, - hide: bool, - args: Vec, - has_normal_args: bool, - has_value_args: bool, - mutability: Mutability, - result: Type, - weight: Option, - docs: Vec, -} -impl Method { - fn try_from(value: &ImplItemMethod) -> syn::Result { - let mut info = MethodInfo { - rename_selector: None, - hide: false, - }; - let mut docs = Vec::new(); - let mut weight = None; - for attr in &value.attrs { - let ident = parse_ident_from_path(&attr.path, false)?; - if ident == "solidity" { - info = attr.parse_args::()?; - } else if ident == "doc" { - let args = attr.parse_meta().unwrap(); - let value = match args { - Meta::NameValue(MetaNameValue { - lit: Lit::Str(str), .. - }) => str.value(), - _ => unreachable!(), - }; - docs.push(value); - } else if ident == "weight" { - weight = Some(attr.parse_args::()?); - } - } - let ident = &value.sig.ident; - let ident_str = ident.to_string(); - if !cases::snakecase::is_snake_case(&ident_str) { - return Err(syn::Error::new(ident.span(), "method name should be snake_cased\nif alternative solidity name needs to be set - use #[solidity] attribute")); - } - - let mut mutability = Mutability::Pure; - - if let Some(FnArg::Receiver(receiver)) = value - .sig - .inputs - .iter() - .find(|arg| matches!(arg, FnArg::Receiver(_))) - { - if receiver.reference.is_none() { - return Err(syn::Error::new( - receiver.span(), - "receiver should be by ref", - )); - } - if receiver.mutability.is_some() { - mutability = Mutability::Mutable; - } else { - mutability = Mutability::View; - } - } - let mut args = Vec::new(); - for typ in value - .sig - .inputs - .iter() - .filter(|arg| matches!(arg, FnArg::Typed(_))) - { - let typ = match typ { - FnArg::Typed(typ) => typ, - _ => unreachable!(), - }; - args.push(MethodArg::try_from(typ)?); - } - - if mutability != Mutability::Mutable && args.iter().any(|arg| arg.is_value()) { - return Err(syn::Error::new( - args.iter().find(|arg| arg.is_value()).unwrap().ty.span(), - "payable function should be mutable", - )); - } - - let result = match &value.sig.output { - ReturnType::Type(_, ty) => ty, - _ => return Err(syn::Error::new(value.sig.output.span(), "interface method should return Result\nif there is no value to return - specify void (which is alias to unit)")), - }; - let result = parse_result_ok(result)?; - - let camel_name = info - .rename_selector - .unwrap_or_else(|| cases::camelcase::to_camel_case(&ident.to_string())); - let has_normal_args = args.iter().filter(|arg| !arg.is_special()).count() != 0; - let has_value_args = args.iter().any(|a| a.is_value()); - - Ok(Self { - name: ident.clone(), - camel_name, - pascal_name: snake_ident_to_pascal(ident), - screaming_name: snake_ident_to_screaming(ident), - hide: info.hide, - args, - has_normal_args, - has_value_args, - mutability, - result: result.clone(), - weight, - docs, - }) - } - fn expand_call_def(&self) -> proc_macro2::TokenStream { - let defs = self - .args - .iter() - .filter(|a| !a.is_special()) - .map(|a| a.expand_call_def()); - let pascal_name = &self.pascal_name; - let docs = &self.docs; - - if self.has_normal_args { - quote! { - #(#[doc = #docs])* - #[allow(missing_docs)] - #pascal_name { - #( - #defs, - )* - } - } - } else { - quote! { - #(#[doc = #docs])* - #[allow(missing_docs)] - #pascal_name - } - } - } - - fn expand_const(&self) -> proc_macro2::TokenStream { - let screaming_name = &self.screaming_name; - let screaming_name_signature = format_ident!("{}_SIGNATURE", &self.screaming_name); - let custom_signature = self.expand_custom_signature(); - quote! { - const #screaming_name_signature: ::evm_coder::custom_signature::SignatureUnit = #custom_signature; - const #screaming_name: ::evm_coder::types::Bytes4 = { - let mut sum = ::evm_coder::sha3_const::Keccak256::new(); - let mut pos = 0; - while pos < Self::#screaming_name_signature.len { - sum = sum.update(&[Self::#screaming_name_signature.data[pos]; 1]); - pos += 1; - } - let a = sum.finalize(); - [a[0], a[1], a[2], a[3]] - }; - } - } - - fn expand_interface_id(&self) -> proc_macro2::TokenStream { - let screaming_name = &self.screaming_name; - quote! { - interface_id ^= u32::from_be_bytes(Self::#screaming_name); - } - } - - fn expand_parse(&self) -> proc_macro2::TokenStream { - let pascal_name = &self.pascal_name; - let screaming_name = &self.screaming_name; - if self.has_normal_args { - let args_iter = self.args.iter().filter(|a| !a.is_special()); - let arg_type = args_iter.clone().map(|a| &a.ty); - let parsers = args_iter.map(|a| a.expand_parse()); - quote! { - Self::#screaming_name => { - let is_dynamic = false #(|| <#arg_type as ::evm_coder::abi::AbiType>::is_dynamic())*; - return Ok(Some(Self::#pascal_name { - #( - #parsers, - )* - })) - } - } - } else { - quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) } - } - } - - fn expand_variant_call(&self, call_name: &proc_macro2::Ident) -> proc_macro2::TokenStream { - let pascal_name = &self.pascal_name; - let name = &self.name; - - let matcher = if self.has_normal_args { - let names = self - .args - .iter() - .filter(|a| !a.is_special()) - .map(|a| &a.name); - - quote! {{ - #( - #names, - )* - }} - } else { - quote! {} - }; - - let receiver = match self.mutability { - Mutability::Mutable | Mutability::View => quote! {self.}, - Mutability::Pure => quote! {Self::}, - }; - let args = self.args.iter().map(|a| a.expand_call_arg()); - - quote! { - #call_name::#pascal_name #matcher => { - #[allow(deprecated)] - let result = #receiver #name( - #( - #args, - )* - )?; - (&result).to_result() - } - } - } - - fn expand_variant_weight(&self) -> proc_macro2::TokenStream { - let pascal_name = &self.pascal_name; - if let Some(weight) = &self.weight { - let matcher = if self.has_normal_args { - let names = self - .args - .iter() - .filter(|a| !a.is_special()) - .map(|a| &a.name); - - quote! {{ - #( - #names, - )* - }} - } else { - quote! {} - }; - quote! { - Self::#pascal_name #matcher => (#weight).into() - } - } else { - let matcher = if self.has_normal_args { - quote! {{..}} - } else { - quote! {} - }; - quote! { - Self::#pascal_name #matcher => ().into() - } - } - } - - fn expand_custom_signature(&self) -> proc_macro2::TokenStream { - let mut args = TokenStream::new(); - - let mut has_params = false; - for arg in self.args.iter().filter(|a| !a.is_special()) { - has_params = true; - let ty = &arg.ty; - args.extend(quote! {nameof(<#ty>::SIGNATURE)}); - args.extend(quote! {fixed(",")}) - } - - // Remove trailing comma - if has_params { - args.extend(quote! {shift_left(1)}) - } - - let func_name = self.camel_name.clone(); - quote! { ::evm_coder::make_signature!(new fixed(#func_name) fixed("(") #args fixed(")")) } - } - - fn expand_solidity_function(&self) -> proc_macro2::TokenStream { - let camel_name = &self.camel_name; - let mutability = match self.mutability { - Mutability::Mutable => quote! {SolidityMutability::Mutable}, - Mutability::View => quote! { SolidityMutability::View }, - Mutability::Pure => quote! {SolidityMutability::Pure}, - }; - let result = &self.result; - - let args = self - .args - .iter() - .filter(|a| !a.is_special()) - .map(MethodArg::expand_solidity_argument); - let docs = &self.docs; - let screaming_name = &self.screaming_name; - let hide = self.hide; - let custom_signature = self.expand_custom_signature(); - let is_payable = self.has_value_args; - - quote! { - SolidityFunction { - docs: &[#(#docs),*], - hide: #hide, - selector: u32::from_be_bytes(Self::#screaming_name), - custom_signature: #custom_signature, - name: #camel_name, - mutability: #mutability, - is_payable: #is_payable, - args: ( - #( - #args, - )* - ), - result: >::default(), - } - } - } -} - -fn generics_list(gen: &Generics) -> proc_macro2::TokenStream { - if gen.params.is_empty() { - return quote! {}; - } - let params = gen.params.iter().map(|p| match p { - syn::GenericParam::Type(id) => { - let v = &id.ident; - quote! {#v} - } - syn::GenericParam::Lifetime(lt) => { - let v = <.lifetime; - quote! {#v} - } - syn::GenericParam::Const(c) => { - let i = &c.ident; - quote! {#i} - } - }); - quote! { #(#params),* } -} -fn generics_reference(gen: &Generics) -> proc_macro2::TokenStream { - if gen.params.is_empty() { - return quote! {}; - } - let list = generics_list(gen); - quote! { <#list> } -} -fn generics_stub(gen: &Generics) -> proc_macro2::TokenStream { - if gen.params.is_empty() { - return quote! {}; - } - let params = (0..gen.params.len()).map(|_| quote! {()}); - quote! {<#(#params,)*>} -} -fn generics_data(gen: &Generics) -> proc_macro2::TokenStream { - let list = generics_list(gen); - if gen.params.len() == 1 { - quote! {#list} - } else { - quote! { (#list) } - } -} - -pub struct SolidityInterface { - generics: Generics, - name: Box, - info: InterfaceInfo, - methods: Vec, - docs: Vec, -} -impl SolidityInterface { - pub fn try_from(info: InterfaceInfo, value: &ItemImpl) -> syn::Result { - let mut methods = Vec::new(); - - for item in &value.items { - if let ImplItem::Method(method) = item { - methods.push(Method::try_from(method)?) - } - } - let mut docs = vec![]; - for attr in &value.attrs { - let ident = parse_ident_from_path(&attr.path, false)?; - if ident == "doc" { - let args = attr.parse_meta().unwrap(); - let value = match args { - Meta::NameValue(MetaNameValue { - lit: Lit::Str(str), .. - }) => str.value(), - _ => unreachable!(), - }; - docs.push(value); - } - } - Ok(Self { - generics: value.generics.clone(), - name: value.self_ty.clone(), - info, - methods, - docs, - }) - } - pub fn expand(self) -> proc_macro2::TokenStream { - let name = self.name; - - let solidity_name = self.info.name.to_string(); - let call_name = pascal_ident_to_call(&self.info.name); - let generics = self.generics; - let gen_ref = generics_reference(&generics); - let gen_data = generics_data(&generics); - let gen_stub = generics_stub(&generics); - let gen_where = &generics.where_clause; - - let call_sub = self - .info - .inline_is - .0 - .iter() - .chain(self.info.is.0.iter()) - .map(|c| Is::expand_call_def(c, &gen_ref)); - let call_parse = self - .info - .inline_is - .0 - .iter() - .chain(self.info.is.0.iter()) - .map(|is| Is::expand_parse(is, &gen_ref)); - let call_variants = self - .info - .inline_is - .0 - .iter() - .chain(self.info.is.0.iter()) - .map(|c| Is::expand_variant_call(c, &call_name, &gen_ref)); - let weight_variants = self - .info - .inline_is - .0 - .iter() - .chain(self.info.is.0.iter()) - .map(Is::expand_variant_weight); - - let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id); - let supports_interface = self - .info - .is - .0 - .iter() - .map(|is| Is::expand_supports_interface(is, &gen_ref)); - - let calls = self.methods.iter().map(Method::expand_call_def); - let consts = self.methods.iter().map(Method::expand_const); - let interface_id = self.methods.iter().map(Method::expand_interface_id); - let parsers = self.methods.iter().map(Method::expand_parse); - let call_variants_this = self - .methods - .iter() - .map(|m| Method::expand_variant_call(m, &call_name)); - let weight_variants_this = self.methods.iter().map(Method::expand_variant_weight); - let solidity_functions = self.methods.iter().map(Method::expand_solidity_function); - - // TODO: Inline inline_is - let solidity_is = self - .info - .is - .0 - .iter() - .chain(self.info.inline_is.0.iter()) - .map(|is| is.name.to_string()); - let solidity_events_is = self.info.events.0.iter().map(|is| is.name.to_string()); - let solidity_generators = self - .info - .is - .0 - .iter() - .chain(self.info.inline_is.0.iter()) - .map(|is| Is::expand_generator(is, &gen_ref)); - let solidity_event_generators = self.info.events.0.iter().map(Is::expand_event_generator); - let solidity_events_idents = self.info.events.0.iter().map(|is| is.name.clone()); - let docs = &self.docs; - - let expect_selector = self.info.expect_selector.map(|s| { - quote! { - const _: () = assert!(#s == u32::from_be_bytes(<#call_name #gen_stub>::interface_id()), "selector mismatch, review contained function selectors"); - } - }); - - quote! { - #( - const _: ::core::marker::PhantomData<#solidity_events_idents> = ::core::marker::PhantomData; - )* - #[derive(Debug)] - #(#[doc = #docs])* - pub enum #call_name #gen_ref { - /// Inherited method - ERC165Call(::evm_coder::ERC165Call, ::core::marker::PhantomData<#gen_data>), - #( - #calls, - )* - #( - #call_sub, - )* - } - - #expect_selector - - impl #gen_ref #call_name #gen_ref { - #( - #consts - )* - /// Return this call ERC165 selector - pub const fn interface_id() -> ::evm_coder::types::Bytes4 { - let mut interface_id = 0; - #(#interface_id)* - #(#inline_interface_id)* - u32::to_be_bytes(interface_id) - } - /// Generate solidity definitions for methods described in this interface - #[cfg(feature = "stubgen")] - pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) { - use evm_coder::solidity::*; - use core::fmt::Write; - let interface = SolidityInterface { - docs: &[#(#docs),*], - name: #solidity_name, - selector: Self::interface_id(), - is: &["Dummy", "ERC165", #( - #solidity_is, - )* #( - #solidity_events_is, - )* ], - functions: (#( - #solidity_functions, - )*), - }; - - let mut out = ::evm_coder::types::String::new(); - if #solidity_name.starts_with("Inline") { - out.push_str("/// @dev inlined interface\n"); - } - let _ = interface.format(is_impl, &mut out, tc); - tc.collect(out); - #( - #solidity_event_generators - )* - #( - #solidity_generators - )* - if is_impl { - tc.collect("/// @dev common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\ncontract ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool) {\n\t\trequire(false, stub_error);\n\t\tinterfaceID;\n\t\treturn true;\n\t}\n}\n".into()); - } else { - tc.collect("/// @dev common stubs holder\ninterface Dummy {\n}\ninterface ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);\n}\n".into()); - } - } - } - impl #gen_ref ::evm_coder::Call for #call_name #gen_ref { - fn parse(method_id: ::evm_coder::types::Bytes4, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result> { - use ::evm_coder::abi::AbiRead; - match method_id { - ::evm_coder::ERC165Call::INTERFACE_ID => return Ok( - ::evm_coder::ERC165Call::parse(method_id, reader)? - .map(|c| Self::ERC165Call(c, ::core::marker::PhantomData)) - ), - #( - #parsers, - )* - _ => {}, - } - #( - #call_parse - )else* - return Ok(None); - } - } - impl #generics #call_name #gen_ref - #gen_where - { - /// Is this contract implements specified ERC165 selector - pub fn supports_interface(this: &#name, interface_id: ::evm_coder::types::Bytes4) -> bool { - interface_id != u32::to_be_bytes(0xffffff) && ( - interface_id == ::evm_coder::ERC165Call::INTERFACE_ID || - interface_id == Self::interface_id() - #( - || #supports_interface - )* - ) - } - } - impl #generics ::evm_coder::Weighted for #call_name #gen_ref - #gen_where - { - #[allow(unused_variables)] - fn weight(&self) -> ::evm_coder::execution::DispatchInfo { - match self { - #( - #weight_variants, - )* - // TODO: It should be very cheap, but not free - Self::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {..}, _) => ::frame_support::weights::Weight::from_ref_time(100).into(), - #( - #weight_variants_this, - )* - } - } - } - impl #generics ::evm_coder::Callable<#call_name #gen_ref> for #name - #gen_where - { - #[allow(unreachable_code)] // In case of no inner calls - fn call(&mut self, c: Msg<#call_name #gen_ref>) -> ::evm_coder::execution::ResultWithPostInfo<::evm_coder::abi::AbiWriter> { - use ::evm_coder::abi::AbiWrite; - match c.call { - #( - #call_variants, - )* - #call_name::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {interface_id}, _) => { - let mut writer = ::evm_coder::abi::AbiWriter::default(); - writer.bool(&<#call_name #gen_ref>::supports_interface(self, interface_id)); - return Ok(writer.into()); - } - _ => {}, - } - let mut writer = ::evm_coder::abi::AbiWriter::default(); - match c.call { - #( - #call_variants_this, - )* - _ => Err(::evm_coder::execution::Error::from("method is not available").into()), - } - } - } - } - } -} --- a/crates/evm-coder/procedural/src/to_log.rs +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -use inflector::cases; -use syn::{Data, DeriveInput, Field, Fields, Ident, Variant, spanned::Spanned}; -use quote::quote; - -use crate::{parse_ident_from_path, parse_ident_from_type, snake_ident_to_screaming}; - -struct EventField { - name: Ident, - camel_name: String, - ty: Ident, - indexed: bool, -} - -impl EventField { - fn try_from(field: &Field) -> syn::Result { - let name = field.ident.as_ref().unwrap(); - let ty = parse_ident_from_type(&field.ty, false)?; - let mut indexed = false; - for attr in &field.attrs { - if let Ok(ident) = parse_ident_from_path(&attr.path, false) { - if ident == "indexed" { - indexed = true; - } - } - } - Ok(Self { - name: name.to_owned(), - camel_name: cases::camelcase::to_camel_case(&name.to_string()), - ty: ty.to_owned(), - indexed, - }) - } - fn expand_solidity_argument(&self) -> proc_macro2::TokenStream { - let camel_name = &self.camel_name; - let ty = &self.ty; - let indexed = self.indexed; - quote! { - >::new(#indexed, #camel_name) - } - } -} - -struct Event { - name: Ident, - name_screaming: Ident, - fields: Vec, - selector: proc_macro2::TokenStream, -} - -impl Event { - fn try_from(variant: &Variant) -> syn::Result { - let name = &variant.ident; - let name_lit = proc_macro2::Literal::string(name.to_string().as_str()); - let name_screaming = snake_ident_to_screaming(name); - - let named = match &variant.fields { - Fields::Named(named) => named, - _ => { - return Err(syn::Error::new( - variant.fields.span(), - "expected named fields", - )) - } - }; - let mut fields = Vec::new(); - for field in &named.named { - fields.push(EventField::try_from(field)?); - } - if fields.iter().filter(|f| f.indexed).count() > 3 { - return Err(syn::Error::new( - variant.fields.span(), - "events can have at most 4 indexed fields (1 indexed field is reserved for event signature)" - )); - } - - let args = fields.iter().map(|f| { - let ty = &f.ty; - quote! {nameof(<#ty as ::evm_coder::abi::AbiType>::SIGNATURE) fixed(",")} - }); - // Remove trailing comma - let shift = (!fields.is_empty()).then(|| quote! {shift_left(1)}); - - let signature = quote! { ::evm_coder::make_signature!(new fixed(#name_lit) fixed("(") #(#args)* #shift fixed(")")) }; - let selector = quote! { - { - let signature = #signature; - let mut sum = ::evm_coder::sha3_const::Keccak256::new(); - let mut pos = 0; - while pos < signature.len { - sum = sum.update(&[signature.data[pos]; 1]); - pos += 1; - } - let a = sum.finalize(); - let mut selector_bytes = [0; 32]; - let mut i = 0; - while i != 32 { - selector_bytes[i] = a[i]; - i += 1; - } - selector_bytes - } - }; - - Ok(Self { - name: name.to_owned(), - name_screaming, - fields, - selector, - }) - } - - fn expand_serializers(&self) -> proc_macro2::TokenStream { - let name = &self.name; - let name_screaming = &self.name_screaming; - let fields = self.fields.iter().map(|f| &f.name); - - let indexed = self.fields.iter().filter(|f| f.indexed).map(|f| &f.name); - let plain = self.fields.iter().filter(|f| !f.indexed).map(|f| &f.name); - - quote! { - Self::#name {#( - #fields, - )*} => { - topics.push(::evm_coder::types::Topic::from(Self::#name_screaming)); - #( - topics.push(#indexed.to_topic()); - )* - #( - #plain.abi_write(&mut writer); - )* - } - } - } - - fn expand_consts(&self) -> proc_macro2::TokenStream { - let name_screaming = &self.name_screaming; - let selector = &self.selector; - - quote! { - const #name_screaming: [u8; 32] = #selector; - } - } - - fn expand_solidity_function(&self) -> proc_macro2::TokenStream { - let name = self.name.to_string(); - let args = self.fields.iter().map(EventField::expand_solidity_argument); - quote! { - SolidityEvent { - name: #name, - args: ( - #( - #args, - )* - ), - } - } - } -} - -pub struct Events { - name: Ident, - events: Vec, -} - -impl Events { - pub fn try_from(data: &DeriveInput) -> syn::Result { - let name = &data.ident; - let en = match &data.data { - Data::Enum(en) => en, - _ => return Err(syn::Error::new(data.span(), "expected enum")), - }; - let mut events = Vec::new(); - for variant in &en.variants { - events.push(Event::try_from(variant)?); - } - Ok(Self { - name: name.to_owned(), - events, - }) - } - pub fn expand(&self) -> proc_macro2::TokenStream { - let name = &self.name; - - let consts = self.events.iter().map(Event::expand_consts); - let serializers = self.events.iter().map(Event::expand_serializers); - let solidity_name = self.name.to_string(); - let solidity_functions = self.events.iter().map(Event::expand_solidity_function); - - quote! { - impl #name { - #( - #consts - )* - - /// Generate solidity definitions for methods described in this interface - #[cfg(feature = "stubgen")] - pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) { - use evm_coder::solidity::*; - use core::fmt::Write; - let interface = SolidityInterface { - docs: &[], - selector: [0; 4], - name: #solidity_name, - is: &[], - functions: (#( - #solidity_functions, - )*), - }; - let mut out = ::evm_coder::types::String::new(); - out.push_str("/// @dev inlined interface\n"); - let _ = interface.format(is_impl, &mut out, tc); - tc.collect(out); - } - } - - #[automatically_derived] - impl ::evm_coder::events::ToLog for #name { - fn to_log(&self, contract: Address) -> ::evm_coder::ethereum::Log { - use ::evm_coder::events::ToTopic; - use ::evm_coder::abi::AbiWrite; - let mut writer = ::evm_coder::abi::AbiWriter::new(); - let mut topics = Vec::new(); - match self { - #( - #serializers, - )* - } - ::evm_coder::ethereum::Log { - address: contract, - topics, - data: writer.finish(), - } - } - } - } - } -} --- a/crates/evm-coder/src/abi/impls.rs +++ /dev/null @@ -1,296 +0,0 @@ -use crate::{ - custom_signature::SignatureUnit, - execution::{Result, ResultWithPostInfo, WithPostDispatchInfo}, - make_signature, sealed, - types::*, -}; -use super::{traits::*, ABI_ALIGNMENT, AbiReader, AbiWriter}; -use primitive_types::{U256, H160}; - -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; - -macro_rules! impl_abi_type { - ($ty:ty, $name:ident, $dynamic:literal) => { - impl sealed::CanBePlacedInVec for $ty {} - - impl AbiType for $ty { - const SIGNATURE: SignatureUnit = make_signature!(new fixed(stringify!($name))); - - fn is_dynamic() -> bool { - $dynamic - } - - fn size() -> usize { - ABI_ALIGNMENT - } - } - }; -} - -macro_rules! impl_abi_readable { - ($ty:ty, $method:ident) => { - impl AbiRead for $ty { - fn abi_read(reader: &mut AbiReader) -> Result<$ty> { - reader.$method() - } - } - }; -} - -macro_rules! impl_abi_writeable { - ($ty:ty, $method:ident) => { - impl AbiWrite for $ty { - fn abi_write(&self, writer: &mut AbiWriter) { - writer.$method(&self) - } - } - }; -} - -macro_rules! impl_abi { - ($ty:ty, $method:ident, $dynamic:literal) => { - impl_abi_type!($ty, $method, $dynamic); - impl_abi_readable!($ty, $method); - impl_abi_writeable!($ty, $method); - }; -} - -impl_abi!(bool, bool, false); -impl_abi!(u8, uint8, false); -impl_abi!(u32, uint32, false); -impl_abi!(u64, uint64, false); -impl_abi!(u128, uint128, false); -impl_abi!(U256, uint256, false); -impl_abi!(H160, address, false); -impl_abi!(String, string, true); - -impl_abi_writeable!(&str, string); - -impl_abi_type!(Bytes, bytes, true); - -impl AbiRead for Bytes { - fn abi_read(reader: &mut AbiReader) -> Result { - Ok(Bytes(reader.bytes()?)) - } -} - -impl AbiWrite for Bytes { - fn abi_write(&self, writer: &mut AbiWriter) { - writer.bytes(self.0.as_slice()) - } -} - -impl_abi_type!(Bytes4, bytes4, false); -impl AbiRead for Bytes4 { - fn abi_read(reader: &mut AbiReader) -> Result { - reader.bytes4() - } -} - -impl AbiWrite for &T { - fn abi_write(&self, writer: &mut AbiWriter) { - T::abi_write(self, writer); - } -} - -impl AbiType for &T { - const SIGNATURE: SignatureUnit = T::SIGNATURE; - - fn is_dynamic() -> bool { - T::is_dynamic() - } - - fn size() -> usize { - T::size() - } -} - -impl AbiRead for Vec { - fn abi_read(reader: &mut AbiReader) -> Result> { - let mut sub = reader.subresult(None)?; - let size = sub.uint32()? as usize; - sub.subresult_offset = sub.offset; - let is_dynamic = ::is_dynamic(); - let mut out = Vec::with_capacity(size); - for _ in 0..size { - out.push(::abi_read(&mut sub)?); - if !is_dynamic { - sub.bytes_read(::size()); - }; - } - Ok(out) - } -} - -impl AbiType for Vec { - const SIGNATURE: SignatureUnit = make_signature!(new nameof(T::SIGNATURE) fixed("[]")); - - fn is_dynamic() -> bool { - true - } - - fn size() -> usize { - ABI_ALIGNMENT - } -} - -impl AbiWrite for Vec { - fn abi_write(&self, writer: &mut AbiWriter) { - let is_dynamic = T::is_dynamic(); - let mut sub = if is_dynamic { - AbiWriter::new_dynamic(is_dynamic) - } else { - AbiWriter::new() - }; - - // Write items count - (self.len() as u32).abi_write(&mut sub); - - for item in self { - item.abi_write(&mut sub); - } - writer.write_subresult(sub); - } -} - -impl AbiWrite for () { - fn abi_write(&self, _writer: &mut AbiWriter) {} -} - -/// This particular AbiWrite implementation should be split to another trait, -/// which only implements `to_result`, but due to lack of specialization feature -/// in stable Rust, we can't have blanket impl of this trait `for T where T: AbiWrite`, -/// so here we abusing default trait methods for it -impl AbiWrite for ResultWithPostInfo { - fn abi_write(&self, _writer: &mut AbiWriter) { - debug_assert!(false, "shouldn't be called, see comment") - } - fn to_result(&self) -> ResultWithPostInfo { - match self { - Ok(v) => Ok(WithPostDispatchInfo { - post_info: v.post_info.clone(), - data: { - let mut out = AbiWriter::new(); - v.data.abi_write(&mut out); - out - }, - }), - Err(e) => Err(e.clone()), - } - } -} - -macro_rules! impl_tuples { - ($($ident:ident)+) => { - impl<$($ident: AbiType,)+> AbiType for ($($ident,)+) - where - $( - $ident: AbiType, - )+ - { - const SIGNATURE: SignatureUnit = make_signature!( - new fixed("(") - $(nameof(<$ident>::SIGNATURE) fixed(","))+ - shift_left(1) - fixed(")") - ); - - fn is_dynamic() -> bool { - false - $( - || <$ident>::is_dynamic() - )* - } - - fn size() -> usize { - 0 $(+ <$ident>::size())+ - } - } - - impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {} - - impl<$($ident),+> AbiRead for ($($ident,)+) - where - Self: AbiType, - $($ident: AbiRead + AbiType,)+ - { - fn abi_read(reader: &mut AbiReader) -> Result<($($ident,)+)> { - let is_dynamic = ::is_dynamic(); - let size = if !is_dynamic { Some(::size()) } else { None }; - let mut subresult = reader.subresult(size)?; - Ok(( - $({ - let value = <$ident>::abi_read(&mut subresult)?; - if !is_dynamic {subresult.bytes_read(<$ident as AbiType>::size())}; - value - },)+ - )) - } - } - - #[allow(non_snake_case)] - impl<$($ident),+> AbiWrite for ($($ident,)+) - where - $($ident: AbiWrite + AbiType,)+ - { - fn abi_write(&self, writer: &mut AbiWriter) { - let ($($ident,)+) = self; - if ::is_dynamic() { - let mut sub = AbiWriter::new(); - $($ident.abi_write(&mut sub);)+ - writer.write_subresult(sub); - } else { - $($ident.abi_write(writer);)+ - } - } - } - }; -} - -impl_tuples! {A} -impl_tuples! {A B} -impl_tuples! {A B C} -impl_tuples! {A B C D} -impl_tuples! {A B C D E} -impl_tuples! {A B C D E F} -impl_tuples! {A B C D E F G} -impl_tuples! {A B C D E F G H} -impl_tuples! {A B C D E F G H I} -impl_tuples! {A B C D E F G H I J} - -//----- impls for Option ----- -impl AbiType for Option { - const SIGNATURE: SignatureUnit = <(bool, T)>::SIGNATURE; - - fn is_dynamic() -> bool { - <(bool, T)>::is_dynamic() - } - - fn size() -> usize { - <(bool, T)>::size() - } -} - -impl AbiWrite for Option { - fn abi_write(&self, writer: &mut AbiWriter) { - match self { - Some(value) => (true, value).abi_write(writer), - None => (false, T::default()).abi_write(writer), - } - } -} - -impl AbiRead for Option -where - Self: AbiType, - T: AbiRead + AbiType, -{ - fn abi_read(reader: &mut AbiReader) -> Result - where - Self: Sized, - { - let (status, value) = <(bool, T)>::abi_read(reader)?; - Ok(if status { Some(value) } else { None }) - } -} --- a/crates/evm-coder/src/abi/mod.rs +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -//! Implementation of EVM RLP reader/writer - -#![allow(dead_code)] - -mod traits; -pub use traits::*; -mod impls; - -#[cfg(test)] -mod test; - -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; -use evm_core::ExitError; -use primitive_types::{H160, U256}; - -use crate::{ - execution::{Result, Error}, - types::*, -}; - -const ABI_ALIGNMENT: usize = 32; - -/// View into RLP data, which provides method to read typed items from it -#[derive(Clone)] -pub struct AbiReader<'i> { - buf: &'i [u8], - subresult_offset: usize, - offset: usize, -} -impl<'i> AbiReader<'i> { - /// Start reading RLP buffer, assuming there is no padding bytes - pub fn new(buf: &'i [u8]) -> Self { - Self { - buf, - subresult_offset: 0, - offset: 0, - } - } - /// Start reading RLP buffer, parsing first 4 bytes as selector - pub fn new_call(buf: &'i [u8]) -> Result<(Bytes4, Self)> { - if buf.len() < 4 { - return Err(Error::Error(ExitError::OutOfOffset)); - } - let mut method_id = [0; 4]; - method_id.copy_from_slice(&buf[0..4]); - - Ok(( - method_id, - Self { - buf, - subresult_offset: 4, - offset: 4, - }, - )) - } - - fn read_pad( - buf: &[u8], - offset: usize, - pad_start: usize, - pad_end: usize, - block_start: usize, - block_end: usize, - ) -> Result<[u8; S]> { - if buf.len() - offset < ABI_ALIGNMENT { - return Err(Error::Error(ExitError::OutOfOffset)); - } - let mut block = [0; S]; - let is_pad_zeroed = buf[pad_start..pad_end].iter().all(|&v| v == 0); - if !is_pad_zeroed { - return Err(Error::Error(ExitError::InvalidRange)); - } - block.copy_from_slice(&buf[block_start..block_end]); - Ok(block) - } - - fn read_padleft(&mut self) -> Result<[u8; S]> { - let offset = self.offset; - self.offset += ABI_ALIGNMENT; - Self::read_pad( - self.buf, - offset, - offset, - offset + ABI_ALIGNMENT - S, - offset + ABI_ALIGNMENT - S, - offset + ABI_ALIGNMENT, - ) - } - - fn read_padright(&mut self) -> Result<[u8; S]> { - let offset = self.offset; - self.offset += ABI_ALIGNMENT; - Self::read_pad( - self.buf, - offset, - offset + S, - offset + ABI_ALIGNMENT, - offset, - offset + S, - ) - } - - /// Read [`H160`] at current position, then advance - pub fn address(&mut self) -> Result { - Ok(H160(self.read_padleft()?)) - } - - /// Read [`bool`] at current position, then advance - pub fn bool(&mut self) -> Result { - let data: [u8; 1] = self.read_padleft()?; - match data[0] { - 0 => Ok(false), - 1 => Ok(true), - _ => Err(Error::Error(ExitError::InvalidRange)), - } - } - - /// Read [`[u8; 4]`] at current position, then advance - pub fn bytes4(&mut self) -> Result<[u8; 4]> { - self.read_padright() - } - - /// Read [`Vec`] at current position, then advance - pub fn bytes(&mut self) -> Result> { - let mut subresult = self.subresult(None)?; - let length = subresult.uint32()? as usize; - if subresult.buf.len() < subresult.offset + length { - return Err(Error::Error(ExitError::OutOfOffset)); - } - Ok(subresult.buf[subresult.offset..subresult.offset + length].into()) - } - - /// Read [`string`] at current position, then advance - pub fn string(&mut self) -> Result { - String::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange)) - } - - /// Read [`u8`] at current position, then advance - pub fn uint8(&mut self) -> Result { - Ok(self.read_padleft::<1>()?[0]) - } - - /// Read [`u32`] at current position, then advance - pub fn uint32(&mut self) -> Result { - Ok(u32::from_be_bytes(self.read_padleft()?)) - } - - /// Read [`u128`] at current position, then advance - pub fn uint128(&mut self) -> Result { - Ok(u128::from_be_bytes(self.read_padleft()?)) - } - - /// Read [`U256`] at current position, then advance - pub fn uint256(&mut self) -> Result { - let buf: [u8; 32] = self.read_padleft()?; - Ok(U256::from_big_endian(&buf)) - } - - /// Read [`u64`] at current position, then advance - pub fn uint64(&mut self) -> Result { - Ok(u64::from_be_bytes(self.read_padleft()?)) - } - - /// Read [`usize`] at current position, then advance - #[deprecated = "dangerous, as usize may have different width in wasm and native execution"] - pub fn read_usize(&mut self) -> Result { - Ok(usize::from_be_bytes(self.read_padleft()?)) - } - - /// Slice recursive buffer, advance one word for buffer offset - /// If `size` is [`None`] then [`Self::offset`] and [`Self::subresult_offset`] evals from [`Self::buf`]. - pub fn subresult(&mut self, size: Option) -> Result> { - let subresult_offset = self.subresult_offset; - let offset = if let Some(size) = size { - self.offset += size; - 0 - } else { - self.uint32()? as usize - }; - - if offset + self.subresult_offset > self.buf.len() { - return Err(Error::Error(ExitError::InvalidRange)); - } - - let new_offset = offset + subresult_offset; - Ok(AbiReader { - buf: self.buf, - subresult_offset: new_offset, - offset: new_offset, - }) - } - - /// Notify about readed data portion. - pub fn bytes_read(&mut self, size: usize) { - self.subresult_offset += size; - } - - /// Is this parser reached end of buffer? - pub fn is_finished(&self) -> bool { - self.buf.len() == self.offset - } -} - -/// Writer for RLP encoded data -#[derive(Default)] -pub struct AbiWriter { - static_part: Vec, - dynamic_part: Vec<(usize, AbiWriter)>, - had_call: bool, - is_dynamic: bool, -} -impl AbiWriter { - /// Initialize internal buffers for output data, assuming no padding required - pub fn new() -> Self { - Self::default() - } - - /// Initialize internal buffers with data size - pub fn new_dynamic(is_dynamic: bool) -> Self { - Self { - is_dynamic, - ..Default::default() - } - } - /// Initialize internal buffers, inserting method selector at beginning - pub fn new_call(method_id: u32) -> Self { - let mut val = Self::new(); - val.static_part.extend(&method_id.to_be_bytes()); - val.had_call = true; - val - } - - fn write_padleft(&mut self, block: &[u8]) { - assert!(block.len() <= ABI_ALIGNMENT); - self.static_part - .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - block.len()]); - self.static_part.extend(block); - } - - fn write_padright(&mut self, block: &[u8]) { - assert!(block.len() <= ABI_ALIGNMENT); - self.static_part.extend(block); - self.static_part - .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - block.len()]); - } - - /// Write [`H160`] to end of buffer - pub fn address(&mut self, address: &H160) { - self.write_padleft(&address.0) - } - - /// Write [`bool`] to end of buffer - pub fn bool(&mut self, value: &bool) { - self.write_padleft(&[if *value { 1 } else { 0 }]) - } - - /// Write [`u8`] to end of buffer - pub fn uint8(&mut self, value: &u8) { - self.write_padleft(&[*value]) - } - - /// Write [`u32`] to end of buffer - pub fn uint32(&mut self, value: &u32) { - self.write_padleft(&u32::to_be_bytes(*value)) - } - - /// Write [`u64`] to end of buffer - pub fn uint64(&mut self, value: &u64) { - self.write_padleft(&u64::to_be_bytes(*value)) - } - - /// Write [`u128`] to end of buffer - pub fn uint128(&mut self, value: &u128) { - self.write_padleft(&u128::to_be_bytes(*value)) - } - - /// Write [`U256`] to end of buffer - pub fn uint256(&mut self, value: &U256) { - let mut out = [0; 32]; - value.to_big_endian(&mut out); - self.write_padleft(&out) - } - - /// Write [`usize`] to end of buffer - #[deprecated = "dangerous, as usize may have different width in wasm and native execution"] - pub fn write_usize(&mut self, value: &usize) { - self.write_padleft(&usize::to_be_bytes(*value)) - } - - /// Append recursive data, writing pending offset at end of buffer - pub fn write_subresult(&mut self, result: Self) { - self.dynamic_part.push((self.static_part.len(), result)); - // Empty block, to be filled later - self.write_padleft(&[]); - } - - fn memory(&mut self, value: &[u8]) { - let mut sub = Self::new(); - sub.uint32(&(value.len() as u32)); - for chunk in value.chunks(ABI_ALIGNMENT) { - sub.write_padright(chunk); - } - self.write_subresult(sub); - } - - /// Append recursive [`str`] at end of buffer - pub fn string(&mut self, value: &str) { - self.memory(value.as_bytes()) - } - - /// Append recursive [`[u8]`] at end of buffer - pub fn bytes(&mut self, value: &[u8]) { - self.memory(value) - } - - /// Finish writer, concatenating all internal buffers - pub fn finish(mut self) -> Vec { - for (static_offset, part) in self.dynamic_part { - let part_offset = self.static_part.len() - - if self.had_call { 4 } else { 0 } - - if self.is_dynamic { ABI_ALIGNMENT } else { 0 }; - - let encoded_dynamic_offset = usize::to_be_bytes(part_offset); - let start = static_offset + ABI_ALIGNMENT - encoded_dynamic_offset.len(); - let stop = static_offset + ABI_ALIGNMENT; - self.static_part[start..stop].copy_from_slice(&encoded_dynamic_offset); - self.static_part.extend(part.finish()) - } - self.static_part - } -} --- a/crates/evm-coder/src/abi/test.rs +++ /dev/null @@ -1,605 +0,0 @@ -use crate::{ - abi::{AbiRead, AbiWrite}, - types::*, -}; - -use super::{AbiReader, AbiWriter}; -use hex_literal::hex; -use primitive_types::{H160, U256}; - -fn test_impl(function_identifier: u32, decoded_data: T, encoded_data: &[u8]) -where - T: AbiWrite + AbiRead + std::cmp::PartialEq + std::fmt::Debug, -{ - let (call, mut decoder) = AbiReader::new_call(encoded_data).unwrap(); - assert_eq!(call, u32::to_be_bytes(function_identifier)); - let data = ::abi_read(&mut decoder).unwrap(); - assert_eq!(data, decoded_data); - - let mut writer = AbiWriter::new_call(function_identifier); - decoded_data.abi_write(&mut writer); - let ed = writer.finish(); - similar_asserts::assert_eq!(encoded_data, ed.as_slice()); -} - -macro_rules! test_impl_uint { - ($type:ident) => { - test_impl::<$type>( - 0xdeadbeef, - 255 as $type, - &hex!( - " - deadbeef - 00000000000000000000000000000000000000000000000000000000000000ff - " - ), - ); - }; -} - -#[test] -fn encode_decode_uint8() { - test_impl_uint!(u8); -} - -#[test] -fn encode_decode_uint32() { - test_impl_uint!(u32); -} - -#[test] -fn encode_decode_uint128() { - test_impl_uint!(u128); -} - -#[test] -fn encode_decode_uint256() { - test_impl::( - 0xdeadbeef, - U256([255, 0, 0, 0]), - &hex!( - " - deadbeef - 00000000000000000000000000000000000000000000000000000000000000ff - " - ), - ); -} - -#[test] -fn encode_decode_string() { - test_impl::( - 0xdeadbeef, - "some string".to_string(), - &hex!( - " - deadbeef - 0000000000000000000000000000000000000000000000000000000000000020 - 000000000000000000000000000000000000000000000000000000000000000b - 736f6d6520737472696e67000000000000000000000000000000000000000000 - " - ), - ); -} - -#[test] -fn encode_decode_tuple_string() { - test_impl::<(String,)>( - 0xdeadbeef, - ("some string".to_string(),), - &hex!( - " - deadbeef - 0000000000000000000000000000000000000000000000000000000000000020 - 0000000000000000000000000000000000000000000000000000000000000020 - 000000000000000000000000000000000000000000000000000000000000000b - 736f6d6520737472696e67000000000000000000000000000000000000000000 - " - ), - ); -} - -#[test] -fn encode_decode_vec_tuple_address_uint256() { - test_impl::>( - 0x1ACF2D55, - vec![ - ( - H160(hex!("2D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC")), - U256([10, 0, 0, 0]), - ), - ( - H160(hex!("AB8E3D9134955566483B11E6825C9223B6737B10")), - U256([20, 0, 0, 0]), - ), - ( - H160(hex!("8C582BDF2953046705FC56F189385255EFC1BE18")), - U256([30, 0, 0, 0]), - ), - ], - &hex!( - " - 1ACF2D55 - 0000000000000000000000000000000000000000000000000000000000000020 // offset of (address, uint256)[] - 0000000000000000000000000000000000000000000000000000000000000003 // length of (address, uint256)[] - - 0000000000000000000000002D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC // address - 000000000000000000000000000000000000000000000000000000000000000A // uint256 - - 000000000000000000000000AB8E3D9134955566483B11E6825C9223B6737B10 // address - 0000000000000000000000000000000000000000000000000000000000000014 // uint256 - - 0000000000000000000000008C582BDF2953046705FC56F189385255EFC1BE18 // address - 000000000000000000000000000000000000000000000000000000000000001E // uint256 - " - ) - ); -} - -#[test] -fn encode_decode_vec_tuple_uint256_string() { - test_impl::>( - 0xdeadbeef, - vec![ - (1.into(), "Test URI 0".to_string()), - (11.into(), "Test URI 1".to_string()), - (12.into(), "Test URI 2".to_string()), - ], - &hex!( - " - deadbeef - 0000000000000000000000000000000000000000000000000000000000000020 // offset of (uint256, string)[] - 0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[] - - 0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem - 00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem - 0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem - - 0000000000000000000000000000000000000000000000000000000000000001 // first token id? #60 - 0000000000000000000000000000000000000000000000000000000000000040 // offset of string - 000000000000000000000000000000000000000000000000000000000000000a // size of string - 5465737420555249203000000000000000000000000000000000000000000000 // string - - 000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11? #e0 - 0000000000000000000000000000000000000000000000000000000000000040 // offset of string - 000000000000000000000000000000000000000000000000000000000000000a // size of string - 5465737420555249203100000000000000000000000000000000000000000000 // string - - 000000000000000000000000000000000000000000000000000000000000000c // third token id? Why ==12? #160 - 0000000000000000000000000000000000000000000000000000000000000040 // offset of string - 000000000000000000000000000000000000000000000000000000000000000a // size of string - 5465737420555249203200000000000000000000000000000000000000000000 // string - " - ) - ); -} - -#[test] -fn dynamic_after_static() { - let mut encoder = AbiWriter::new(); - encoder.bool(&true); - encoder.string("test"); - let encoded = encoder.finish(); - - let mut encoder = AbiWriter::new(); - encoder.bool(&true); - // Offset to subresult - encoder.uint32(&(32 * 2)); - // Len of "test" - encoder.uint32(&4); - encoder.write_padright(&[b't', b'e', b's', b't']); - let alternative_encoded = encoder.finish(); - - assert_eq!(encoded, alternative_encoded); - - let mut decoder = AbiReader::new(&encoded); - assert!(decoder.bool().unwrap()); - assert_eq!(decoder.string().unwrap(), "test"); -} - -#[test] -fn mint_sample() { - let (call, mut decoder) = AbiReader::new_call(&hex!( - " - 50bb4e7f - 000000000000000000000000ad2c0954693c2b5404b7e50967d3481bea432374 - 0000000000000000000000000000000000000000000000000000000000000001 - 0000000000000000000000000000000000000000000000000000000000000060 - 0000000000000000000000000000000000000000000000000000000000000008 - 5465737420555249000000000000000000000000000000000000000000000000 - " - )) - .unwrap(); - assert_eq!(call, u32::to_be_bytes(0x50bb4e7f)); - assert_eq!( - format!("{:?}", decoder.address().unwrap()), - "0xad2c0954693c2b5404b7e50967d3481bea432374" - ); - assert_eq!(decoder.uint32().unwrap(), 1); - assert_eq!(decoder.string().unwrap(), "Test URI"); -} - -#[test] -fn parse_vec_with_dynamic_type() { - let decoded_data = ( - 0x36543006, - vec![ - (1.into(), "Test URI 0".to_string()), - (11.into(), "Test URI 1".to_string()), - (12.into(), "Test URI 2".to_string()), - ], - ); - - let encoded_data = &hex!( - " - 36543006 - 00000000000000000000000053744e6da587ba10b32a2554d2efdcd985bc27a3 // address - 0000000000000000000000000000000000000000000000000000000000000040 // offset of (uint256, string)[] - 0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[] - - 0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem - 00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem - 0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem - - 0000000000000000000000000000000000000000000000000000000000000001 // first token id? #60 - 0000000000000000000000000000000000000000000000000000000000000040 // offset of string - 000000000000000000000000000000000000000000000000000000000000000a // size of string - 5465737420555249203000000000000000000000000000000000000000000000 // string - - 000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11? #e0 - 0000000000000000000000000000000000000000000000000000000000000040 // offset of string - 000000000000000000000000000000000000000000000000000000000000000a // size of string - 5465737420555249203100000000000000000000000000000000000000000000 // string - - 000000000000000000000000000000000000000000000000000000000000000c // third token id? Why ==12? #160 - 0000000000000000000000000000000000000000000000000000000000000040 // offset of string - 000000000000000000000000000000000000000000000000000000000000000a // size of string - 5465737420555249203200000000000000000000000000000000000000000000 // string - " - ); - - let (call, mut decoder) = AbiReader::new_call(encoded_data).unwrap(); - assert_eq!(call, u32::to_be_bytes(decoded_data.0)); - let address = decoder.address().unwrap(); - let data = >::abi_read(&mut decoder).unwrap(); - assert_eq!(data, decoded_data.1); - - let mut writer = AbiWriter::new_call(decoded_data.0); - address.abi_write(&mut writer); - decoded_data.1.abi_write(&mut writer); - let ed = writer.finish(); - similar_asserts::assert_eq!(encoded_data, ed.as_slice()); -} - -#[test] -fn encode_decode_vec_tuple_string_bytes() { - test_impl::>( - 0xdeadbeef, - vec![ - ( - "Test URI 0".to_string(), - Bytes(vec![ - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - ]), - ), - ( - "Test URI 1".to_string(), - Bytes(vec![ - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - ]), - ), - ("Test URI 2".to_string(), Bytes(vec![0x33, 0x33])), - ], - &hex!( - " - deadbeef - 0000000000000000000000000000000000000000000000000000000000000020 - 0000000000000000000000000000000000000000000000000000000000000003 - - 0000000000000000000000000000000000000000000000000000000000000060 - 0000000000000000000000000000000000000000000000000000000000000140 - 0000000000000000000000000000000000000000000000000000000000000220 - - 0000000000000000000000000000000000000000000000000000000000000040 - 0000000000000000000000000000000000000000000000000000000000000080 - 000000000000000000000000000000000000000000000000000000000000000a - 5465737420555249203000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000030 - 1111111111111111111111111111111111111111111111111111111111111111 - 1111111111111111111111111111111100000000000000000000000000000000 - - 0000000000000000000000000000000000000000000000000000000000000040 - 0000000000000000000000000000000000000000000000000000000000000080 - 000000000000000000000000000000000000000000000000000000000000000a - 5465737420555249203100000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000002f - 2222222222222222222222222222222222222222222222222222222222222222 - 2222222222222222222222222222220000000000000000000000000000000000 - - 0000000000000000000000000000000000000000000000000000000000000040 - 0000000000000000000000000000000000000000000000000000000000000080 - 000000000000000000000000000000000000000000000000000000000000000a - 5465737420555249203200000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000002 - 3333000000000000000000000000000000000000000000000000000000000000 - " - ), - ); -} - -#[test] -// #[ignore = "reason"] -fn encode_decode_tuple0_tuple1_uint8_tuple1_string_bytes_tuple1_uint8_bytes() { - let int = 0xff; - let by = Bytes(vec![0x11, 0x22, 0x33]); - let string = "some string".to_string(); - - test_impl::<((u8,), (String, Bytes), (u8, Bytes))>( - 0xdeadbeef, - ((int,), (string.clone(), by.clone()), (int, by)), - &hex!( - " - deadbeef - 0000000000000000000000000000000000000000000000000000000000000020 - 00000000000000000000000000000000000000000000000000000000000000ff - 0000000000000000000000000000000000000000000000000000000000000060 - 0000000000000000000000000000000000000000000000000000000000000120 - 0000000000000000000000000000000000000000000000000000000000000040 - 0000000000000000000000000000000000000000000000000000000000000080 - 000000000000000000000000000000000000000000000000000000000000000b - 736f6d6520737472696e67000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000003 - 1122330000000000000000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000000000000000000000ff - 0000000000000000000000000000000000000000000000000000000000000040 - 0000000000000000000000000000000000000000000000000000000000000003 - 1122330000000000000000000000000000000000000000000000000000000000 - " - ), - ); -} - -#[test] -fn encode_decode_tuple0_tuple1_uint8_tuple1_uint8_uint8_tuple1_uint8_uint8() { - test_impl::<((u8,), (u8, u8), (u8, u8))>( - 0xdeadbeef, - ((43,), (44, 45), (46, 47)), - &hex!( - " - deadbeef - 000000000000000000000000000000000000000000000000000000000000002b - 000000000000000000000000000000000000000000000000000000000000002c - 000000000000000000000000000000000000000000000000000000000000002d - 000000000000000000000000000000000000000000000000000000000000002e - 000000000000000000000000000000000000000000000000000000000000002f - " - ), - ); -} - -#[test] -fn encode_decode_tuple0_tuple1_uint8_tuple1_uint8() { - test_impl::<((u8,), (u8,))>( - 0xdeadbeef, - ((43,), (44,)), - &hex!( - " - deadbeef - 000000000000000000000000000000000000000000000000000000000000002b - 000000000000000000000000000000000000000000000000000000000000002c - " - ), - ); -} - -#[test] -fn encode_decode_tuple0_tuple1_uint8_uint8() { - test_impl::<((u8, u8),)>( - 0xdeadbeef, - ((43, 44),), - &hex!( - " - deadbeef - 000000000000000000000000000000000000000000000000000000000000002b - 000000000000000000000000000000000000000000000000000000000000002c - " - ), - ); -} - -#[test] -fn encode_decode_tuple_uint8_uint8() { - test_impl::<(u8, u8)>( - 0xdeadbeef, - (43, 44), - &hex!( - " - deadbeef - 000000000000000000000000000000000000000000000000000000000000002b - 000000000000000000000000000000000000000000000000000000000000002c - " - ), - ); -} - -#[test] -fn encode_decode_tuple0_tuple1_uint8_uint8_tuple1_uint8_uint8_and_uint8() { - test_impl::<((u8, u8), (u8, u8), u8)>( - 0xdeadbeef, - ((10, 11), (12, 13), 14), - &hex!( - " - deadbeef - 000000000000000000000000000000000000000000000000000000000000000a - 000000000000000000000000000000000000000000000000000000000000000b - 000000000000000000000000000000000000000000000000000000000000000c - 000000000000000000000000000000000000000000000000000000000000000d - 000000000000000000000000000000000000000000000000000000000000000e - " - ), - ); -} - -#[test] -fn encode_decode_tuple0_tuple1_string() { - test_impl::<((String,),)>( - 0xdeadbeef, - (("some string".to_string(),),), - &hex!( - " - deadbeef - 0000000000000000000000000000000000000000000000000000000000000020 - 0000000000000000000000000000000000000000000000000000000000000020 - 0000000000000000000000000000000000000000000000000000000000000020 - 000000000000000000000000000000000000000000000000000000000000000b - 736f6d6520737472696e67000000000000000000000000000000000000000000 - " - ), - ); -} - -#[test] -fn encode_decode_tuple0_tuple1_uint8_string() { - test_impl::<((u8, String),)>( - 0xdeadbeef, - ((0xff, "some string".to_string()),), - &hex!( - " - deadbeef - 0000000000000000000000000000000000000000000000000000000000000020 - 0000000000000000000000000000000000000000000000000000000000000020 - 00000000000000000000000000000000000000000000000000000000000000ff - 0000000000000000000000000000000000000000000000000000000000000040 - 000000000000000000000000000000000000000000000000000000000000000b - 736f6d6520737472696e67000000000000000000000000000000000000000000 - " - ), - ); -} - -#[test] -fn encode_decode_tuple0_tuple1_string_bytes() { - test_impl::<((String, Bytes),)>( - 0xdeadbeef, - (("some string".to_string(), Bytes(vec![1, 2, 3])),), - &hex!( - " - deadbeef - 0000000000000000000000000000000000000000000000000000000000000020 - 0000000000000000000000000000000000000000000000000000000000000020 - 0000000000000000000000000000000000000000000000000000000000000040 - 0000000000000000000000000000000000000000000000000000000000000080 - 000000000000000000000000000000000000000000000000000000000000000b - 736f6d6520737472696e67000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000003 - 0102030000000000000000000000000000000000000000000000000000000000 - " - ), - ); -} - -#[test] -fn encode_decode_tuple0_tuple1_uint8_tuple1_string() { - test_impl::<((u8,), (String,))>( - 0xdeadbeef, - ((0xff,), ("some string".to_string(),)), - &hex!( - " - deadbeef - 0000000000000000000000000000000000000000000000000000000000000020 - 00000000000000000000000000000000000000000000000000000000000000ff - 0000000000000000000000000000000000000000000000000000000000000040 - 0000000000000000000000000000000000000000000000000000000000000020 - 000000000000000000000000000000000000000000000000000000000000000b - 736f6d6520737472696e67000000000000000000000000000000000000000000 - " - ), - ); -} - -#[test] -fn parse_multiple_params() { - let encoded_data = hex!( - " - deadbeef - 000000000000000000000000000000000000000000000000000000000000000a - 000000000000000000000000000000000000000000000000000000000000000b - " - ); - let (_, mut decoder) = AbiReader::new_call(&encoded_data).unwrap(); - let p1 = ::abi_read(&mut decoder).unwrap(); - let p2 = ::abi_read(&mut decoder).unwrap(); - assert_eq!(p1, 0x0a); - assert_eq!(p2, 0x0b); -} - -#[test] -fn encode_decode_option_uint8_some() { - test_impl::>( - 0xdeadbeef, - Some(44), - &hex!( - " - deadbeef - 0000000000000000000000000000000000000000000000000000000000000001 - 000000000000000000000000000000000000000000000000000000000000002c - " - ), - ); -} - -#[test] -fn encode_decode_option_uint8_none() { - test_impl::>( - 0xdeadbeef, - None, - &hex!( - " - deadbeef - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - " - ), - ); -} - -#[test] -fn encode_decode_option_string_some() { - test_impl::>( - 0xdeadbeef, - Some("some string".to_string()), - &hex!( - " - deadbeef - 0000000000000000000000000000000000000000000000000000000000000020 - 0000000000000000000000000000000000000000000000000000000000000001 - 0000000000000000000000000000000000000000000000000000000000000040 - 000000000000000000000000000000000000000000000000000000000000000b - 736f6d6520737472696e67000000000000000000000000000000000000000000 - " - ), - ); -} - -#[test] -fn encode_decode_option_string_none() { - test_impl::>( - 0xdeadbeef, - None, - &hex!( - " - deadbeef - 0000000000000000000000000000000000000000000000000000000000000020 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000040 - 0000000000000000000000000000000000000000000000000000000000000000 - " - ), - ); -} --- a/crates/evm-coder/src/abi/traits.rs +++ /dev/null @@ -1,45 +0,0 @@ -use super::{AbiReader, AbiWriter}; -use crate::{ - custom_signature::*, - execution::{Result, ResultWithPostInfo}, -}; -use core::str::from_utf8; - -/// Helper for type. -pub trait AbiType { - /// Signature for Etherium ABI. - const SIGNATURE: SignatureUnit; - - /// Signature as str. - fn as_str() -> &'static str { - from_utf8(&Self::SIGNATURE.data[..Self::SIGNATURE.len]).expect("bad utf-8") - } - - /// Is type dynamic sized. - fn is_dynamic() -> bool; - - /// Size for type aligned to [`ABI_ALIGNMENT`]. - fn size() -> usize; -} - -/// [`AbiReader`] implements reading of many types. -pub trait AbiRead { - /// Read item from current position, advanding decoder - fn abi_read(reader: &mut AbiReader) -> Result - where - Self: Sized; -} - -/// For questions about inability to provide custom implementations, -/// see [`AbiRead`] -pub trait AbiWrite { - /// Write value to end of specified encoder - fn abi_write(&self, writer: &mut AbiWriter); - /// Specialization for [`crate::solidity_interface`] implementation, - /// see comment in `impl AbiWrite for ResultWithPostInfo` - fn to_result(&self) -> ResultWithPostInfo { - let mut writer = AbiWriter::new(); - self.abi_write(&mut writer); - Ok(writer.into()) - } -} --- a/crates/evm-coder/src/custom_signature.rs +++ /dev/null @@ -1,326 +0,0 @@ -//! # A module for custom signature support. -//! -//! ## Overview -//! This module allows you to create arbitrary signatures for types and functions in compile time. -//! -//! ### Type signatures -//! To create the desired type signature, you need to create your own trait with the `SIGNATURE` constant. -//! Then in the implementation, for the required type, use the macro [`make_signature`] -//! #### Example -//! ``` -//! use std::str::from_utf8; -//! use evm_coder::{custom_signature::SignatureUnit, make_signature}; -//! -//! // Create trait for our signature -//! trait SoliditySignature { -//! const SIGNATURE: SignatureUnit; -//! -//! fn name() -> &'static str { -//! from_utf8(&Self::SIGNATURE.data[..Self::SIGNATURE.len]).expect("bad utf-8") -//! } -//! } -//! -//! // Make signatures for some types -//! impl SoliditySignature for u8 { -//! const SIGNATURE: SignatureUnit = make_signature!(new fixed("uint8")); -//! } -//! impl SoliditySignature for u32 { -//! const SIGNATURE: SignatureUnit = make_signature!(new fixed("uint32")); -//! } -//! impl SoliditySignature for Vec { -//! const SIGNATURE: SignatureUnit = make_signature!(new nameof(T::SIGNATURE) fixed("[]")); -//! } -//! impl SoliditySignature for (A, B) { -//! const SIGNATURE: SignatureUnit = make_signature!(new fixed("(") nameof(A::SIGNATURE) fixed(",") nameof(B::SIGNATURE) fixed(")")); -//! } -//! impl SoliditySignature for (A,) { -//! const SIGNATURE: SignatureUnit = make_signature!(new fixed("(") nameof(A::SIGNATURE) fixed(",") shift_left(1) fixed(")")); -//! } -//! -//! assert_eq!(u8::name(), "uint8"); -//! assert_eq!(>::name(), "uint8[]"); -//! assert_eq!(<(u32, u8)>::name(), "(uint32,uint8)"); -//! ``` -//! -//! ### Function signatures -//! To create a function signature, the macro [`make_signature`] is also used, which accepts -//! settings for the function format [`SignaturePreferences`] and function parameters [`SignatureUnit`] -//! #### Example -//! ``` -//! use core::str::from_utf8; -//! use evm_coder::{custom_signature::SignatureUnit, make_signature}; -//! // Trait for our signature -//! trait SoliditySignature { -//! const SIGNATURE: SignatureUnit; -//! -//! fn name() -> &'static str { -//! from_utf8(&Self::SIGNATURE.data[..Self::SIGNATURE.len]).expect("bad utf-8") -//! } -//! } -//! -//! // Make signatures for some types -//! impl SoliditySignature for u8 { -//! const SIGNATURE: SignatureUnit = make_signature!(new fixed("uint8")); -//! } -//! impl SoliditySignature for Vec { -//! const SIGNATURE: SignatureUnit = make_signature!(new nameof(T::SIGNATURE) fixed("[]")); -//! } -//! ``` - -/// The maximum length of the signature. -pub const SIGNATURE_SIZE_LIMIT: usize = 256; - -/// Storage for the signature or its elements. -#[derive(Debug)] -pub struct SignatureUnit { - /// Signature data. - pub data: [u8; SIGNATURE_SIZE_LIMIT], - /// The actual size of the data. - pub len: usize, -} - -impl SignatureUnit { - /// Create a signature from `&str'. - pub const fn new(name: &'static str) -> SignatureUnit { - let mut signature = [0_u8; SIGNATURE_SIZE_LIMIT]; - let name = name.as_bytes(); - let name_len = name.len(); - let mut dst_offset = 0; - crate::make_signature!(@copy(name, signature, name_len, dst_offset)); - SignatureUnit { - data: signature, - len: name_len, - } - } - /// String conversion - pub fn as_str(&self) -> Option<&str> { - core::str::from_utf8(&self.data[0..self.len]).ok() - } -} - -/// ### Macro to create signatures of types and functions. -/// -/// Format for creating a type of signature: -/// ```ignore -/// make_signature!(new fixed("uint8")); // Simple type -/// make_signature!(new fixed("(") nameof(u8) fixed(",") nameof(u8) fixed(")")); // Composite type -/// ``` -#[macro_export] -macro_rules! make_signature { - (new $($tt:tt)*) => { - ($crate::custom_signature::SignatureUnit { - data: { - let mut out = [0u8; $crate::custom_signature::SIGNATURE_SIZE_LIMIT]; - let mut dst_offset = 0; - $crate::make_signature!(@data(out, dst_offset); $($tt)*); - out - }, - len: {0 + $crate::make_signature!(@size; $($tt)*)}, - }) - }; - - (@size;) => { - 0 - }; - (@size; fixed($expr:expr) $($tt:tt)*) => { - $expr.len() + $crate::make_signature!(@size; $($tt)*) - }; - (@size; nameof($expr:expr) $($tt:tt)*) => { - $expr.len + $crate::make_signature!(@size; $($tt)*) - }; - (@size; numof($expr:expr) $($tt:tt)*) => { - { - let mut out = 0; - let mut v = $expr; - if v == 0 { - out = 1; - } else { - while v > 0 { - out += 1; - v /= 10; - } - } - out - } + $crate::make_signature!(@size; $($tt)*) - }; - (@size; shift_left($expr:expr) $($tt:tt)*) => { - $crate::make_signature!(@size; $($tt)*) - $expr - }; - - (@data($dst:ident, $dst_offset:ident);) => {}; - (@data($dst:ident, $dst_offset:ident); fixed($expr:expr) $($tt:tt)*) => { - { - let data = $expr.as_bytes(); - let data_len = data.len(); - $crate::make_signature!(@copy(data, $dst, data_len, $dst_offset)); - } - $crate::make_signature!(@data($dst, $dst_offset); $($tt)*) - }; - (@data($dst:ident, $dst_offset:ident); nameof($expr:expr) $($tt:tt)*) => { - { - $crate::make_signature!(@copy(&$expr.data, $dst, $expr.len, $dst_offset)); - } - $crate::make_signature!(@data($dst, $dst_offset); $($tt)*) - }; - (@data($dst:ident, $dst_offset:ident); numof($expr:expr) $($tt:tt)*) => { - { - let mut v = $expr; - let mut need_to_swap = 0; - if v == 0 { - $dst[$dst_offset] = b'0'; - $dst_offset += 1; - } else { - while v > 0 { - let n = (v % 10) as u8; - $dst[$dst_offset] = b'0' + n; - v /= 10; - need_to_swap += 1; - $dst_offset += 1; - } - } - let mut i = 0; - #[allow(clippy::manual_swap)] - while i < need_to_swap / 2 { - let a = $dst_offset - i - 1; - let b = $dst_offset - need_to_swap + i; - let v = $dst[a]; - $dst[a] = $dst[b]; - $dst[b] = v; - i += 1; - } - } - $crate::make_signature!(@data($dst, $dst_offset); $($tt)*) - }; - (@data($dst:ident, $dst_offset:ident); shift_left($expr:expr) $($tt:tt)*) => { - $dst_offset -= $expr; - $crate::make_signature!(@data($dst, $dst_offset); $($tt)*) - }; - - (@copy($src:expr, $dst:expr, $src_len:expr, $dst_offset:ident)) => { - { - let mut src_offset = 0; - let src_len: usize = $src_len; - while src_offset < src_len { - $dst[$dst_offset] = $src[src_offset]; - $dst_offset += 1; - src_offset += 1; - } - } - } -} - -#[cfg(test)] -mod test { - use core::str::from_utf8; - - use super::{SIGNATURE_SIZE_LIMIT, SignatureUnit}; - - trait Name { - const NAME: SignatureUnit; - - fn name() -> &'static str { - from_utf8(&Self::NAME.data[..Self::NAME.len]).expect("bad utf-8") - } - } - - impl Name for u8 { - const NAME: SignatureUnit = make_signature!(new fixed("uint8")); - } - impl Name for u32 { - const NAME: SignatureUnit = make_signature!(new fixed("uint32")); - } - impl Name for Vec { - const NAME: SignatureUnit = make_signature!(new nameof(T::NAME) fixed("[]")); - } - impl Name for (A, B) { - const NAME: SignatureUnit = - make_signature!(new fixed("(") nameof(A::NAME) fixed(",") nameof(B::NAME) fixed(")")); - } - impl Name for (A,) { - const NAME: SignatureUnit = - make_signature!(new fixed("(") nameof(A::NAME) fixed(",") shift_left(1) fixed(")")); - } - impl Name for [A; SIZE] { - const NAME: SignatureUnit = - make_signature!(new nameof(A::NAME) fixed("[") numof(SIZE) fixed("]")); - } - - struct MaxSize(); - impl Name for MaxSize { - const NAME: SignatureUnit = SignatureUnit { - data: [b'!'; SIGNATURE_SIZE_LIMIT], - len: SIGNATURE_SIZE_LIMIT, - }; - } - - #[test] - fn simple() { - assert_eq!(u8::name(), "uint8"); - assert_eq!(u32::name(), "uint32"); - } - - #[test] - fn vector_of_simple() { - assert_eq!(>::name(), "uint8[]"); - assert_eq!(>::name(), "uint32[]"); - } - - #[test] - fn vector_of_vector() { - assert_eq!(>>::name(), "uint8[][]"); - } - - #[test] - fn tuple_of_simple() { - assert_eq!(<(u32, u8)>::name(), "(uint32,uint8)"); - } - - #[test] - fn tuple_of_tuple() { - assert_eq!( - <((u32, u8), (u8, u32))>::name(), - "((uint32,uint8),(uint8,uint32))" - ); - } - - #[test] - fn vector_of_tuple() { - assert_eq!(>::name(), "(uint32,uint8)[]"); - } - - #[test] - fn tuple_of_vector() { - assert_eq!(<(Vec, u8)>::name(), "(uint32[],uint8)"); - } - - #[test] - fn complex() { - assert_eq!( - <(Vec, (u32, Vec))>::name(), - "(uint32[],(uint32,uint8[]))" - ); - } - - #[test] - fn max_size() { - assert_eq!(::name(), "!".repeat(SIGNATURE_SIZE_LIMIT)); - } - - #[test] - fn shift() { - assert_eq!(<(u32,)>::name(), "(uint32)"); - } - - #[test] - fn num() { - assert_eq!(<[u8; 0]>::name(), "uint8[0]"); - assert_eq!(<[u8; 1234]>::name(), "uint8[1234]"); - assert_eq!(<[u8; 12345]>::name(), "uint8[12345]"); - } - - #[test] - fn over_max_size() { - let t = trybuild::TestCases::new(); - t.compile_fail("tests/build_failed/custom_signature_over_max_size.rs"); - } -} --- a/crates/evm-coder/src/events.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -use ethereum::Log; -use primitive_types::{H160, H256, U256}; - -use crate::types::*; - -/// Implementation of this trait should not be written manually, -/// instead use [`crate::ToLog`] proc macros. -/// -/// See also [`evm_coder_procedural::ToLog`], [solidity docs on events](https://docs.soliditylang.org/en/develop/contracts.html#events) -pub trait ToLog { - /// Convert event to [`ethereum::Log`]. - /// Because event by itself doesn't contains current contract - /// address, it should be specified manually. - fn to_log(&self, contract: H160) -> Log; -} - -/// Only items implementing `ToTopic` may be used as `#[indexed]` field -/// in [`crate::ToLog`] macro usage. -/// -/// See also (solidity docs on events)[] -pub trait ToTopic { - /// Convert value to topic to be used in [`ethereum::Log`] - fn to_topic(&self) -> H256; -} - -impl ToTopic for H256 { - fn to_topic(&self) -> H256 { - *self - } -} - -impl ToTopic for U256 { - fn to_topic(&self) -> H256 { - let mut out = [0u8; 32]; - self.to_big_endian(&mut out); - H256(out) - } -} - -impl ToTopic for Address { - fn to_topic(&self) -> H256 { - let mut out = [0u8; 32]; - out[12..32].copy_from_slice(&self.0); - H256(out) - } -} - -impl ToTopic for u32 { - fn to_topic(&self) -> H256 { - let mut out = [0u8; 32]; - out[28..32].copy_from_slice(&self.to_be_bytes()); - H256(out) - } -} --- a/crates/evm-coder/src/execution.rs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -//! Contract execution related types - -#[cfg(not(feature = "std"))] -use alloc::string::{String, ToString}; -use evm_core::{ExitError, ExitFatal}; -#[cfg(feature = "std")] -use std::string::{String, ToString}; - -use crate::Weight; - -/// Execution error, should be convertible between EVM and Substrate. -#[derive(Debug, Clone)] -pub enum Error { - /// Non-fatal contract error occured - Revert(String), - /// EVM fatal error - Fatal(ExitFatal), - /// EVM normal error - Error(ExitError), -} - -impl From for Error -where - E: ToString, -{ - fn from(e: E) -> Self { - Self::Revert(e.to_string()) - } -} - -/// To be used in [`crate::solidity_interface`] implementation. -pub type Result = core::result::Result; - -/// Static information collected from [`crate::weight`]. -pub struct DispatchInfo { - /// Statically predicted call weight - pub weight: Weight, -} - -impl From for DispatchInfo { - fn from(weight: Weight) -> Self { - Self { weight } - } -} -impl From for DispatchInfo { - fn from(weight: u64) -> Self { - Self { - weight: Weight::from_ref_time(weight), - } - } -} -impl From<()> for DispatchInfo { - fn from(_: ()) -> Self { - Self { - weight: Weight::zero(), - } - } -} - -/// Weight information that is only available post dispatch. -/// Note: This can only be used to reduce the weight or fee, not increase it. -#[derive(Default, Clone)] -pub struct PostDispatchInfo { - /// Actual weight consumed by call - actual_weight: Option, -} - -impl PostDispatchInfo { - /// Calculate amount to be returned back to user - pub fn calc_unspent(&self, info: &DispatchInfo) -> Weight { - info.weight - self.calc_actual_weight(info) - } - - /// Calculate actual consumed weight, saturating to weight reported - /// pre-dispatch - pub fn calc_actual_weight(&self, info: &DispatchInfo) -> Weight { - if let Some(actual_weight) = self.actual_weight { - actual_weight.min(info.weight) - } else { - info.weight - } - } -} - -/// Wrapper for PostDispatchInfo and any user-provided data -#[derive(Clone)] -pub struct WithPostDispatchInfo { - /// User provided data - pub data: T, - /// Info known after dispatch - pub post_info: PostDispatchInfo, -} - -impl From for WithPostDispatchInfo { - fn from(data: T) -> Self { - Self { - data, - post_info: Default::default(), - } - } -} - -/// Return type of items in [`crate::solidity_interface`] definition -pub type ResultWithPostInfo = - core::result::Result, WithPostDispatchInfo>; --- a/crates/evm-coder/src/lib.rs +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -#![doc = include_str!("../README.md")] -#![deny(missing_docs)] -#![macro_use] -#![cfg_attr(not(feature = "std"), no_std)] -#[cfg(not(feature = "std"))] -extern crate alloc; - -use abi::{AbiRead, AbiReader, AbiWriter}; -pub use evm_coder_procedural::{event_topic, fn_selector}; -pub mod abi; -pub use events::{ToLog, ToTopic}; -use execution::DispatchInfo; -pub mod execution; -#[macro_use] -pub mod custom_signature; - -/// Derives call enum implementing [`crate::Callable`], [`crate::Weighted`] -/// and [`crate::Call`] from impl block. -/// -/// ## Macro syntax -/// -/// `#[solidity_interface(name, is, inline_is, events)]` -/// - *name* - used in generated code, and for Call enum name -/// - *is* - used to provide inheritance in Solidity -/// - *inline_is* - same as `is`, but ERC165::SupportsInterface will work differently: For `is` SupportsInterface(A) will return true -/// if A is one of the interfaces the contract is inherited from (e.g. B is created as `is(A)`). If B is created as `inline_is(A)` -/// SupportsInterface(A) will internally create a new interface that combines all methods of A and B, so SupportsInterface(A) will return -/// false. -/// -/// `#[weight(value)]` -/// Can be added to every method of impl block, used for deriving [`crate::Weighted`], which -/// is used by substrate bridge. -/// - *value*: expression, which evaluates to weight required to call this method. -/// This expression can use call arguments to calculate non-constant execution time. -/// This expression should evaluate faster than actual execution does, and may provide worse case -/// than one is called. -/// -/// `#[solidity_interface(rename_selector)]` -/// - *rename_selector* - by default, selector name will be generated by transforming method name -/// from snake_case to camelCase. Use this option, if other naming convention is required. -/// I.e: method `token_uri` will be automatically renamed to `tokenUri` in selector, but name -/// required by ERC721 standard is `tokenURI`, thus we need to specify `rename_selector = "tokenURI"` -/// explicitly. -/// -/// Both contract and contract methods may have doccomments, which will end up in a generated -/// solidity interface file, thus you should use [solidity syntax](https://docs.soliditylang.org/en/latest/natspec-format.html) for writing documentation in this macro -/// -/// ## Example -/// -/// ```ignore -/// struct SuperContract; -/// struct InlineContract; -/// struct Contract; -/// -/// #[derive(ToLog)] -/// enum ContractEvents { -/// Event(#[indexed] uint32), -/// } -/// -/// /// @dev This contract provides function to multiply two numbers -/// #[solidity_interface(name = MyContract, is(SuperContract), inline_is(InlineContract))] -/// impl Contract { -/// /// Multiply two numbers -/// /// @param a First number -/// /// @param b Second number -/// /// @return uint32 Product of two passed numbers -/// /// @dev This function returns error in case of overflow -/// #[weight(200 + a + b)] -/// #[solidity_interface(rename_selector = "mul")] -/// fn mul(&mut self, a: uint32, b: uint32) -> Result { -/// Ok(a.checked_mul(b).ok_or("overflow")?) -/// } -/// } -/// ``` -pub use evm_coder_procedural::solidity_interface; -/// See [`solidity_interface`] -pub use evm_coder_procedural::solidity; -/// See [`solidity_interface`] -pub use evm_coder_procedural::weight; -pub use evm_coder_procedural::AbiCoder; -pub use sha3_const; - -/// macro reexports -pub use ethereum; - -/// Derives [`ToLog`] for enum -/// -/// Selectors will be derived from variant names, there is currently no way to have custom naming -/// for them -/// -/// `#[indexed]` -/// Marks this field as indexed, so it will appear in [`ethereum::Log`] topics instead of data -pub use evm_coder_procedural::ToLog; - -// Api of those modules shouldn't be consumed directly, it is only exported for usage in proc macros -#[doc(hidden)] -pub mod events; -#[doc(hidden)] -#[cfg(feature = "stubgen")] -pub mod solidity; - -/// Sealed traits. -pub mod sealed { - /// Not every type should be directly placed in vec. - /// Vec encoding is not memory efficient, as every item will be padded - /// to 32 bytes. - /// Instead you should use specialized types (`bytes` in case of `Vec`) - pub trait CanBePlacedInVec {} -} - -/// Solidity type definitions (aliases from solidity name to rust type) -/// To be used in [`solidity_interface`] definitions, to make sure there is no -/// type conflict between Rust code and generated definitions -pub mod types { - #![allow(non_camel_case_types, missing_docs)] - - #[cfg(not(feature = "std"))] - use alloc::{vec::Vec}; - use primitive_types::{U256, H160, H256}; - - pub type Address = H160; - pub type Bytes4 = [u8; 4]; - pub type Topic = H256; - - #[cfg(not(feature = "std"))] - pub type String = ::alloc::string::String; - #[cfg(feature = "std")] - pub type String = ::std::string::String; - - #[derive(Default, Debug, PartialEq, Eq, Clone)] - pub struct Bytes(pub Vec); - - //#region Special types - /// Makes function payable - pub type Value = U256; - /// Makes function caller-sensitive - pub type Caller = Address; - //#endregion - - /// Ethereum typed call message, similar to solidity - /// `msg` object. - pub struct Msg { - pub call: C, - /// Address of user, which called this contract. - pub caller: H160, - /// Payment amount to contract. - /// Contract should reject payment, if target call is not payable, - /// and there is no `receiver()` function defined. - pub value: U256, - } - - impl From> for Bytes { - fn from(src: Vec) -> Self { - Self(src) - } - } - - #[allow(clippy::from_over_into)] - impl Into> for Bytes { - fn into(self) -> Vec { - self.0 - } - } - - impl Bytes { - #[must_use] - pub fn len(&self) -> usize { - self.0.len() - } - - #[must_use] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - } -} - -/// Parseable EVM call, this trait should be implemented with [`solidity_interface`] macro -pub trait Call: Sized { - /// Parse call buffer into typed call enum - fn parse(selector: types::Bytes4, input: &mut AbiReader) -> execution::Result>; -} - -/// Intended to be used as `#[weight]` output type -/// Should be same between evm-coder and substrate to avoid confusion -/// -/// Isn't same thing as gas, some mapping is required between those types -pub type Weight = frame_support::pallet_prelude::Weight; - -/// In substrate, we have benchmarking, which allows -/// us to not rely on gas metering, but instead predict amount of gas to execute call -pub trait Weighted: Call { - /// Predict weight of this call - fn weight(&self) -> DispatchInfo; -} - -/// Type callable with ethereum message, may be implemented by [`solidity_interface`] macro -/// on interface implementation, or for externally-owned real EVM contract -pub trait Callable { - /// Call contract using specified call data - fn call(&mut self, call: types::Msg) -> execution::ResultWithPostInfo; -} - -/// Implementation of ERC165 is implicitly generated for all interfaces in [`solidity_interface`], -/// this structure holds parsed data for ERC165Call subvariant -/// -/// Note: no [`Callable`] implementation is provided, call implementation is inlined into every -/// implementing contract -/// -/// See -#[derive(Debug)] -pub enum ERC165Call { - /// ERC165 provides single method, which returns true, if contract - /// implements specified interface - SupportsInterface { - /// Requested interface - interface_id: types::Bytes4, - }, -} - -impl ERC165Call { - /// ERC165 selector is provided by standard - pub const INTERFACE_ID: types::Bytes4 = u32::to_be_bytes(0x01ffc9a7); -} - -impl Call for ERC165Call { - fn parse(selector: types::Bytes4, input: &mut AbiReader) -> execution::Result> { - if selector != Self::INTERFACE_ID { - return Ok(None); - } - Ok(Some(Self::SupportsInterface { - interface_id: types::Bytes4::abi_read(input)?, - })) - } -} - -/// Generate "tests", which will generate solidity code on execution and print it to stdout -/// Script at .maintain/scripts/generate_api.sh can split this output from test runtime -/// -/// This macro receives type usage as second argument, but you can use anything as generics, -/// because no bounds are implied -#[macro_export] -macro_rules! generate_stubgen { - ($name:ident, $decl:ty, $is_impl:literal) => { - #[cfg(feature = "stubgen")] - #[test] - #[ignore] - fn $name() { - use evm_coder::solidity::TypeCollector; - let mut out = TypeCollector::new(); - <$decl>::generate_solidity_interface(&mut out, $is_impl); - println!("=== SNIP START ==="); - println!("// SPDX-License-Identifier: OTHER"); - println!("// This code is automatically generated"); - println!(); - println!("pragma solidity >=0.8.0 <0.9.0;"); - println!(); - for b in out.finish() { - println!("{}", b); - } - println!("=== SNIP END ==="); - } - }; -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn function_selector_generation() { - assert_eq!(fn_selector!(transfer(address, uint256)), 0xa9059cbb); - } - - #[test] - fn event_topic_generation() { - assert_eq!( - hex::encode(&event_topic!(Transfer(address, address, uint256))[..]), - "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - ); - } -} --- a/crates/evm-coder/src/solidity/impls.rs +++ /dev/null @@ -1,179 +0,0 @@ -use super::{TypeCollector, SolidityTypeName, SolidityTupleTy}; -use crate::{sealed, types::*}; -use core::fmt; -use primitive_types::{U256, H160}; - -macro_rules! solidity_type_name { - ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => { - $( - impl SolidityTypeName for $ty { - fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result { - write!(writer, $name) - } - fn is_simple() -> bool { - $simple - } - fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result { - write!(writer, $default) - } - } - )* - }; -} - -solidity_type_name! { - u8 => "uint8" true = "0", - u32 => "uint32" true = "0", - u64 => "uint64" true = "0", - u128 => "uint128" true = "0", - U256 => "uint256" true = "0", - Bytes4 => "bytes4" true = "bytes4(0)", - H160 => "address" true = "0x0000000000000000000000000000000000000000", - String => "string" false = "\"\"", - Bytes => "bytes" false = "hex\"\"", - bool => "bool" true = "false", -} - -impl SolidityTypeName for () { - fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result { - Ok(()) - } - fn is_simple() -> bool { - true - } - fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result { - Ok(()) - } - fn is_void() -> bool { - true - } -} - -impl SolidityTypeName for Vec { - fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - T::solidity_name(writer, tc)?; - write!(writer, "[]") - } - fn is_simple() -> bool { - false - } - fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - write!(writer, "new ")?; - T::solidity_name(writer, tc)?; - write!(writer, "[](0)") - } -} - -macro_rules! count { - () => (0usize); - ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*)); -} - -macro_rules! impl_tuples { - ($($ident:ident)+) => { - impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleTy for ($($ident,)+) { - fn fields(tc: &TypeCollector) -> Vec { - let mut collected = Vec::with_capacity(Self::len()); - $({ - let mut out = String::new(); - $ident::solidity_name(&mut out, tc).expect("no fmt error"); - collected.push(out); - })*; - collected - } - - fn len() -> usize { - count!($($ident)*) - } - } - impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) { - fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - write!(writer, "{}", tc.collect_tuple::()) - } - fn is_simple() -> bool { - false - } - #[allow(unused_assignments)] - fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - write!(writer, "{}(", tc.collect_tuple::())?; - let mut first = true; - $( - if !first { - write!(writer, ",")?; - } else { - first = false; - } - <$ident>::solidity_default(writer, tc)?; - )* - write!(writer, ")") - } - } - }; -} - -impl_tuples! {A} -impl_tuples! {A B} -impl_tuples! {A B C} -impl_tuples! {A B C D} -impl_tuples! {A B C D E} -impl_tuples! {A B C D E F} -impl_tuples! {A B C D E F G} -impl_tuples! {A B C D E F G H} -impl_tuples! {A B C D E F G H I} -impl_tuples! {A B C D E F G H I J} - -//----- impls for Option ----- -impl SolidityTypeName for Option { - fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - write!(writer, "{}", tc.collect_struct::()) - } - fn is_simple() -> bool { - false - } - fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - write!(writer, "{}(", tc.collect_struct::())?; - bool::solidity_default(writer, tc)?; - write!(writer, ", "); - T::solidity_default(writer, tc)?; - write!(writer, ")") - } -} - -impl super::SolidityStructTy for Option { - fn generate_solidity_interface(tc: &TypeCollector) -> String { - let mut solidity_name = "Option".to_string(); - let mut generic_name = String::new(); - T::solidity_name(&mut generic_name, tc); - solidity_name.push( - generic_name - .chars() - .next() - .expect("Generic name is empty") - .to_ascii_uppercase(), - ); - solidity_name.push_str(&generic_name[1..]); - - let interface = super::SolidityStruct { - docs: &[" Optional value"], - name: solidity_name.as_str(), - fields: ( - super::SolidityStructField:: { - docs: &[" Shows the status of accessibility of value"], - name: "status", - ty: ::core::marker::PhantomData, - }, - super::SolidityStructField:: { - docs: &[" Actual value if `status` is true"], - name: "value", - ty: ::core::marker::PhantomData, - }, - ), - }; - - let mut out = String::new(); - let _ = interface.format(&mut out, tc); - tc.collect(out); - - solidity_name.to_string() - } -} --- a/crates/evm-coder/src/solidity/mod.rs +++ /dev/null @@ -1,515 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -//! Implementation detail of [`crate::solidity_interface`] macro code-generation. -//! You should not rely on any public item from this module, as it is only intended to be used -//! by procedural macro, API and output format may be changed at any time. -//! -//! Purpose of this module is to receive solidity contract definition in module-specified -//! format, and then output string, representing interface of this contract in solidity language - -mod traits; -pub use traits::*; -mod impls; - -#[cfg(not(feature = "std"))] -use alloc::{vec::Vec, collections::BTreeMap, format}; -#[cfg(feature = "std")] -use std::collections::BTreeMap; -use core::{ - fmt::{self, Write}, - marker::PhantomData, - cell::{Cell, RefCell}, - cmp::Reverse, -}; -use impl_trait_for_tuples::impl_for_tuples; -use crate::{types::*, custom_signature::SignatureUnit}; - -#[derive(Default)] -pub struct TypeCollector { - /// Code => id - /// id ordering is required to perform topo-sort on the resulting data - structs: RefCell>, - anonymous: RefCell, usize>>, - // generic: RefCell>, - id: Cell, -} -impl TypeCollector { - pub fn new() -> Self { - Self::default() - } - pub fn collect(&self, item: String) { - let id = self.next_id(); - self.structs.borrow_mut().insert(item, id); - } - pub fn next_id(&self) -> usize { - let v = self.id.get(); - self.id.set(v + 1); - v - } - /// Collect typle, deduplicating it by type, and returning generated name - pub fn collect_tuple(&self) -> String { - let names = T::fields(self); - if let Some(id) = self.anonymous.borrow().get(&names).cloned() { - return format!("Tuple{}", id); - } - let id = self.next_id(); - let mut str = String::new(); - writeln!(str, "/// @dev anonymous struct").unwrap(); - writeln!(str, "struct Tuple{} {{", id).unwrap(); - for (i, name) in names.iter().enumerate() { - writeln!(str, "\t{} field_{};", name, i).unwrap(); - } - writeln!(str, "}}").unwrap(); - self.collect(str); - self.anonymous.borrow_mut().insert(names, id); - format!("Tuple{}", id) - } - pub fn collect_struct(&self) -> String { - T::generate_solidity_interface(self) - } - pub fn collect_enum(&self) -> String { - T::generate_solidity_interface(self) - } - pub fn finish(self) -> Vec { - let mut data = self.structs.into_inner().into_iter().collect::>(); - data.sort_by_key(|(_, id)| Reverse(*id)); - data.into_iter().map(|(code, _)| code).collect() - } -} -#[derive(Default)] -pub struct UnnamedArgument(PhantomData<*const T>); - -impl SolidityArguments for UnnamedArgument { - fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - if !T::is_void() { - T::solidity_name(writer, tc)?; - if !T::is_simple() { - write!(writer, " memory")?; - } - Ok(()) - } else { - Ok(()) - } - } - fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result { - Ok(()) - } - fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - T::solidity_default(writer, tc) - } - fn len(&self) -> usize { - if T::is_void() { - 0 - } else { - 1 - } - } -} - -pub struct NamedArgument(&'static str, PhantomData<*const T>); - -impl NamedArgument { - pub fn new(name: &'static str) -> Self { - Self(name, Default::default()) - } -} - -impl SolidityArguments for NamedArgument { - fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - if !T::is_void() { - T::solidity_name(writer, tc)?; - if !T::is_simple() { - write!(writer, " memory")?; - } - write!(writer, " {}", self.0) - } else { - Ok(()) - } - } - fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result { - writeln!(writer, "\t{prefix}\t{};", self.0) - } - fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - T::solidity_default(writer, tc) - } - fn len(&self) -> usize { - if T::is_void() { - 0 - } else { - 1 - } - } -} - -pub struct SolidityEventArgument(pub bool, &'static str, PhantomData<*const T>); - -impl SolidityEventArgument { - pub fn new(indexed: bool, name: &'static str) -> Self { - Self(indexed, name, Default::default()) - } -} - -impl SolidityArguments for SolidityEventArgument { - fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - if !T::is_void() { - T::solidity_name(writer, tc)?; - if self.0 { - write!(writer, " indexed")?; - } - write!(writer, " {}", self.1) - } else { - Ok(()) - } - } - fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result { - writeln!(writer, "\t{prefix}\t{};", self.1) - } - fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - T::solidity_default(writer, tc) - } - fn len(&self) -> usize { - if T::is_void() { - 0 - } else { - 1 - } - } -} - -impl SolidityArguments for () { - fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result { - Ok(()) - } - fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result { - Ok(()) - } - fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result { - Ok(()) - } - fn len(&self) -> usize { - 0 - } -} - -#[impl_for_tuples(1, 12)] -impl SolidityArguments for Tuple { - for_tuples!( where #( Tuple: SolidityArguments ),* ); - - fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - let mut first = true; - for_tuples!( #( - if !Tuple.is_empty() { - if !first { - write!(writer, ", ")?; - } - first = false; - Tuple.solidity_name(writer, tc)?; - } - )* ); - Ok(()) - } - fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result { - for_tuples!( #( - Tuple.solidity_get(prefix, writer)?; - )* ); - Ok(()) - } - fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - if self.is_empty() { - Ok(()) - } else if self.len() == 1 { - for_tuples!( #( - Tuple.solidity_default(writer, tc)?; - )* ); - Ok(()) - } else { - write!(writer, "(")?; - let mut first = true; - for_tuples!( #( - if !Tuple.is_empty() { - if !first { - write!(writer, ", ")?; - } - first = false; - Tuple.solidity_default(writer, tc)?; - } - )* ); - write!(writer, ")")?; - Ok(()) - } - } - fn len(&self) -> usize { - for_tuples!( #( Tuple.len() )+* ) - } -} - -pub enum SolidityMutability { - Pure, - View, - Mutable, -} -pub struct SolidityFunction { - pub docs: &'static [&'static str], - pub selector: u32, - pub hide: bool, - pub custom_signature: SignatureUnit, - pub name: &'static str, - pub args: A, - pub result: R, - pub mutability: SolidityMutability, - pub is_payable: bool, -} -impl SolidityFunctions for SolidityFunction { - fn solidity_name( - &self, - is_impl: bool, - writer: &mut impl fmt::Write, - tc: &TypeCollector, - ) -> fmt::Result { - let hide_comment = self.hide.then_some("// ").unwrap_or(""); - for doc in self.docs { - writeln!(writer, "\t{hide_comment}///{}", doc)?; - } - writeln!( - writer, - "\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},", - self.selector - )?; - writeln!( - writer, - "\t{hide_comment}/// or in textual repr: {}", - self.custom_signature.as_str().expect("bad utf-8") - )?; - write!(writer, "\t{hide_comment}function {}(", self.name)?; - self.args.solidity_name(writer, tc)?; - write!(writer, ")")?; - if is_impl { - write!(writer, " public")?; - } else { - write!(writer, " external")?; - } - match &self.mutability { - SolidityMutability::Pure => write!(writer, " pure")?, - SolidityMutability::View => write!(writer, " view")?, - SolidityMutability::Mutable => {} - } - if self.is_payable { - write!(writer, " payable")?; - } - if !self.result.is_empty() { - write!(writer, " returns (")?; - self.result.solidity_name(writer, tc)?; - write!(writer, ")")?; - } - if is_impl { - writeln!(writer, " {{")?; - writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?; - self.args.solidity_get(hide_comment, writer)?; - match &self.mutability { - SolidityMutability::Pure => {} - SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?, - SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?, - } - if !self.result.is_empty() { - write!(writer, "\t{hide_comment}\treturn ")?; - self.result.solidity_default(writer, tc)?; - writeln!(writer, ";")?; - } - writeln!(writer, "\t{hide_comment}}}")?; - } else { - writeln!(writer, ";")?; - } - if self.hide { - writeln!(writer, "// FORMATTING: FORCE NEWLINE")?; - } - Ok(()) - } -} - -#[impl_for_tuples(0, 48)] -impl SolidityFunctions for Tuple { - for_tuples!( where #( Tuple: SolidityFunctions ),* ); - - fn solidity_name( - &self, - is_impl: bool, - writer: &mut impl fmt::Write, - tc: &TypeCollector, - ) -> fmt::Result { - let mut first = false; - for_tuples!( #( - Tuple.solidity_name(is_impl, writer, tc)?; - )* ); - Ok(()) - } -} - -pub struct SolidityInterface { - pub docs: &'static [&'static str], - pub selector: Bytes4, - pub name: &'static str, - pub is: &'static [&'static str], - pub functions: F, -} - -impl SolidityInterface { - pub fn format( - &self, - is_impl: bool, - out: &mut impl fmt::Write, - tc: &TypeCollector, - ) -> fmt::Result { - const ZERO_BYTES: [u8; 4] = [0; 4]; - for doc in self.docs { - writeln!(out, "///{}", doc)?; - } - if self.selector != ZERO_BYTES { - writeln!( - out, - "/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}", - u32::from_be_bytes(self.selector) - )?; - } - if is_impl { - write!(out, "contract ")?; - } else { - write!(out, "interface ")?; - } - write!(out, "{}", self.name)?; - if !self.is.is_empty() { - write!(out, " is")?; - for (i, n) in self.is.iter().enumerate() { - if i != 0 { - write!(out, ",")?; - } - write!(out, " {}", n)?; - } - } - writeln!(out, " {{")?; - self.functions.solidity_name(is_impl, out, tc)?; - writeln!(out, "}}")?; - Ok(()) - } -} - -pub struct SolidityEvent { - pub name: &'static str, - pub args: A, -} - -impl SolidityFunctions for SolidityEvent { - fn solidity_name( - &self, - _is_impl: bool, - writer: &mut impl fmt::Write, - tc: &TypeCollector, - ) -> fmt::Result { - write!(writer, "\tevent {}(", self.name)?; - self.args.solidity_name(writer, tc)?; - writeln!(writer, ");") - } -} - -#[impl_for_tuples(0, 48)] -impl SolidityItems for Tuple { - for_tuples!( where #( Tuple: SolidityItems ),* ); - - fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - for_tuples!( #( - Tuple.solidity_name(writer, tc)?; - )* ); - Ok(()) - } -} - -pub struct SolidityStructField { - pub docs: &'static [&'static str], - pub name: &'static str, - pub ty: PhantomData<*const T>, -} - -impl SolidityItems for SolidityStructField -where - T: SolidityTypeName, -{ - fn solidity_name(&self, out: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - for doc in self.docs { - writeln!(out, "///{}", doc)?; - } - write!(out, "\t")?; - T::solidity_name(out, tc)?; - writeln!(out, " {};", self.name)?; - Ok(()) - } -} -pub struct SolidityStruct<'a, F> { - pub docs: &'a [&'a str], - // pub generics: - pub name: &'a str, - pub fields: F, -} -impl SolidityStruct<'_, F> -where - F: SolidityItems, -{ - pub fn format(&self, out: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - for doc in self.docs { - writeln!(out, "///{}", doc)?; - } - writeln!(out, "struct {} {{", self.name)?; - self.fields.solidity_name(out, tc)?; - writeln!(out, "}}")?; - Ok(()) - } -} - -pub struct SolidityEnumVariant { - pub docs: &'static [&'static str], - pub name: &'static str, -} -impl SolidityItems for SolidityEnumVariant { - fn solidity_name(&self, out: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result { - for doc in self.docs { - writeln!(out, "///{}", doc)?; - } - write!(out, "\t{}", self.name)?; - Ok(()) - } -} -pub struct SolidityEnum { - pub docs: &'static [&'static str], - pub name: &'static str, - pub fields: &'static [SolidityEnumVariant], -} -impl SolidityEnum { - pub fn format(&self, out: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result { - for doc in self.docs { - writeln!(out, "///{}", doc)?; - } - write!(out, "enum {} {{", self.name)?; - for (i, field) in self.fields.iter().enumerate() { - if i != 0 { - write!(out, ",")?; - } - writeln!(out)?; - field.solidity_name(out, tc)?; - } - writeln!(out)?; - writeln!(out, "}}")?; - Ok(()) - } -} --- a/crates/evm-coder/src/solidity/traits.rs +++ /dev/null @@ -1,50 +0,0 @@ -use super::TypeCollector; -use core::fmt; - -pub trait SolidityTypeName: 'static { - fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result; - /// "simple" types are stored inline, no `memory` modifier should be used in solidity - fn is_simple() -> bool; - fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result; - /// Specialization - fn is_void() -> bool { - false - } -} - -pub trait SolidityTupleTy: 'static { - fn fields(tc: &TypeCollector) -> Vec; - fn len() -> usize; -} -pub trait SolidityStructTy: 'static { - fn generate_solidity_interface(tc: &TypeCollector) -> String; -} -pub trait SolidityEnumTy: 'static { - fn generate_solidity_interface(tc: &TypeCollector) -> String; - fn solidity_option(&self) -> &str; -} - -pub trait SolidityArguments { - fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result; - fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result; - fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result; - fn is_empty(&self) -> bool { - self.len() == 0 - } - fn len(&self) -> usize; -} - -pub trait SolidityFunctions { - fn solidity_name( - &self, - is_impl: bool, - writer: &mut impl fmt::Write, - tc: &TypeCollector, - ) -> fmt::Result; -} - -pub trait SolidityItems { - fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result; - // For PhantomData fields - // fn is_void() -} --- a/crates/evm-coder/tests/abi_derive_generation.rs +++ /dev/null @@ -1,765 +0,0 @@ -mod test_struct { - use evm_coder_procedural::AbiCoder; - use evm_coder::types::Bytes; - - #[test] - fn empty_struct() { - let t = trybuild::TestCases::new(); - t.compile_fail("tests/build_failed/abi_derive_struct_generation.rs"); - } - - #[derive(AbiCoder, PartialEq, Debug)] - struct TypeStruct1SimpleParam { - _a: u8, - } - - #[derive(AbiCoder, PartialEq, Debug)] - struct TypeStruct1DynamicParam { - _a: String, - } - - #[derive(AbiCoder, PartialEq, Debug)] - struct TypeStruct2SimpleParam { - _a: u8, - _b: u32, - } - - #[derive(AbiCoder, PartialEq, Debug)] - struct TypeStruct2DynamicParam { - _a: String, - _b: Bytes, - } - - #[derive(AbiCoder, PartialEq, Debug)] - struct TypeStruct2MixedParam { - _a: u8, - _b: Bytes, - } - - #[derive(AbiCoder, PartialEq, Debug)] - struct TypeStruct1DerivedSimpleParam { - _a: TypeStruct1SimpleParam, - } - - #[derive(AbiCoder, PartialEq, Debug)] - struct TypeStruct2DerivedSimpleParam { - _a: TypeStruct1SimpleParam, - _b: TypeStruct2SimpleParam, - } - - #[derive(AbiCoder, PartialEq, Debug)] - struct TypeStruct1DerivedDynamicParam { - _a: TypeStruct1DynamicParam, - } - - #[derive(AbiCoder, PartialEq, Debug)] - struct TypeStruct2DerivedDynamicParam { - _a: TypeStruct1DynamicParam, - _b: TypeStruct2DynamicParam, - } - - /// Some docs - /// At multi - /// line - #[derive(AbiCoder, PartialEq, Debug)] - struct TypeStruct3DerivedMixedParam { - /// Docs for A - /// multi - /// line - _a: TypeStruct1SimpleParam, - /// Docs for B - _b: TypeStruct2DynamicParam, - /// Docs for C - _c: TypeStruct2MixedParam, - } - - #[test] - fn impl_abi_type_signature() { - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - "(uint8)" - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - "(string)" - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - "(uint8,uint32)" - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - "(string,bytes)" - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - "(uint8,bytes)" - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - "((uint8))" - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - "((uint8),(uint8,uint32))" - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - "((string))" - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - "((string),(string,bytes))" - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - "((uint8),(string,bytes),(uint8,bytes))" - ); - } - - #[test] - fn impl_abi_type_is_dynamic() { - assert_eq!( - ::is_dynamic(), - false - ); - assert_eq!( - ::is_dynamic(), - true - ); - assert_eq!( - ::is_dynamic(), - false - ); - assert_eq!( - ::is_dynamic(), - true - ); - assert_eq!( - ::is_dynamic(), - true - ); - assert_eq!( - ::is_dynamic(), - false - ); - assert_eq!( - ::is_dynamic(), - false - ); - assert_eq!( - ::is_dynamic(), - true - ); - assert_eq!( - ::is_dynamic(), - true - ); - assert_eq!( - ::is_dynamic(), - true - ); - } - - #[test] - fn impl_abi_type_size() { - const ABI_ALIGNMENT: usize = 32; - assert_eq!( - ::size(), - ABI_ALIGNMENT - ); - assert_eq!( - ::size(), - ABI_ALIGNMENT - ); - assert_eq!( - ::size(), - ABI_ALIGNMENT * 2 - ); - assert_eq!( - ::size(), - ABI_ALIGNMENT * 2 - ); - assert_eq!( - ::size(), - ABI_ALIGNMENT * 2 - ); - assert_eq!( - ::size(), - ABI_ALIGNMENT - ); - assert_eq!( - ::size(), - ABI_ALIGNMENT * 3 - ); - assert_eq!( - ::size(), - ABI_ALIGNMENT - ); - assert_eq!( - ::size(), - ABI_ALIGNMENT * 3 - ); - assert_eq!( - ::size(), - ABI_ALIGNMENT * 5 - ); - } - - #[derive(AbiCoder, PartialEq, Debug)] - struct TupleStruct1SimpleParam(u8); - - #[derive(AbiCoder, PartialEq, Debug)] - struct TupleStruct1DynamicParam(String); - - #[derive(AbiCoder, PartialEq, Debug)] - struct TupleStruct2SimpleParam(u8, u32); - - #[derive(AbiCoder, PartialEq, Debug)] - struct TupleStruct2DynamicParam(String, Bytes); - - #[derive(AbiCoder, PartialEq, Debug)] - struct TupleStruct2MixedParam(u8, Bytes); - - #[derive(AbiCoder, PartialEq, Debug)] - struct TupleStruct1DerivedSimpleParam(TupleStruct1SimpleParam); - - #[derive(AbiCoder, PartialEq, Debug)] - struct TupleStruct2DerivedSimpleParam(TupleStruct1SimpleParam, TupleStruct2SimpleParam); - - #[derive(AbiCoder, PartialEq, Debug)] - struct TupleStruct1DerivedDynamicParam(TupleStruct1DynamicParam); - - #[derive(AbiCoder, PartialEq, Debug)] - struct TupleStruct2DerivedDynamicParam(TupleStruct1DynamicParam, TupleStruct2DynamicParam); - - /// Some docs - /// At multi - /// line - #[derive(AbiCoder, PartialEq, Debug)] - struct TupleStruct3DerivedMixedParam( - /// Docs for A - /// multi - /// line - TupleStruct1SimpleParam, - TupleStruct2DynamicParam, - /// Docs for C - TupleStruct2MixedParam, - ); - - #[test] - fn impl_abi_type_signature_same_for_structs() { - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - ::SIGNATURE - .as_str() - .unwrap() - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - ::SIGNATURE - .as_str() - .unwrap() - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - ::SIGNATURE - .as_str() - .unwrap() - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - ::SIGNATURE - .as_str() - .unwrap() - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - ::SIGNATURE - .as_str() - .unwrap(), - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - ::SIGNATURE - .as_str() - .unwrap(), - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - ::SIGNATURE - .as_str() - .unwrap(), - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - ::SIGNATURE - .as_str() - .unwrap(), - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - ::SIGNATURE - .as_str() - .unwrap(), - ); - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - ::SIGNATURE - .as_str() - .unwrap(), - ); - } - - #[test] - fn impl_abi_type_is_dynamic_same_for_structs() { - assert_eq!( - ::is_dynamic(), - ::is_dynamic() - ); - assert_eq!( - ::is_dynamic(), - ::is_dynamic() - ); - assert_eq!( - ::is_dynamic(), - ::is_dynamic() - ); - assert_eq!( - ::is_dynamic(), - ::is_dynamic() - ); - assert_eq!( - ::is_dynamic(), - ::is_dynamic() - ); - assert_eq!( - ::is_dynamic(), - ::is_dynamic() - ); - assert_eq!( - ::is_dynamic(), - ::is_dynamic() - ); - assert_eq!( - ::is_dynamic(), - ::is_dynamic() - ); - assert_eq!( - ::is_dynamic(), - ::is_dynamic() - ); - assert_eq!( - ::is_dynamic(), - ::is_dynamic() - ); - } - - #[test] - fn impl_abi_type_size_same_for_structs() { - assert_eq!( - ::size(), - ::size() - ); - assert_eq!( - ::size(), - ::size() - ); - assert_eq!( - ::size(), - ::size() - ); - assert_eq!( - ::size(), - ::size() - ); - assert_eq!( - ::size(), - ::size() - ); - assert_eq!( - ::size(), - ::size() - ); - assert_eq!( - ::size(), - ::size() - ); - assert_eq!( - ::size(), - ::size() - ); - assert_eq!( - ::size(), - ::size() - ); - assert_eq!( - ::size(), - ::size() - ); - } - - const FUNCTION_IDENTIFIER: u32 = 0xdeadbeef; - - fn test_impl( - tuple_data: Tuple, - tuple_struct_data: TupleStruct, - type_struct_data: TypeStruct, - ) where - TypeStruct: evm_coder::abi::AbiWrite - + evm_coder::abi::AbiRead - + std::cmp::PartialEq - + std::fmt::Debug, - TupleStruct: evm_coder::abi::AbiWrite - + evm_coder::abi::AbiRead - + std::cmp::PartialEq - + std::fmt::Debug, - Tuple: evm_coder::abi::AbiWrite - + evm_coder::abi::AbiRead - + std::cmp::PartialEq - + std::fmt::Debug, - { - let encoded_type_struct = test_abi_write_impl(&type_struct_data); - let encoded_tuple_struct = test_abi_write_impl(&tuple_struct_data); - let encoded_tuple = test_abi_write_impl(&tuple_data); - - similar_asserts::assert_eq!(encoded_tuple, encoded_type_struct); - similar_asserts::assert_eq!(encoded_tuple, encoded_tuple_struct); - - { - let (_, mut decoder) = evm_coder::abi::AbiReader::new_call(&encoded_tuple).unwrap(); - let restored_struct_data = ::abi_read(&mut decoder).unwrap(); - assert_eq!(restored_struct_data, type_struct_data); - } - { - let (_, mut decoder) = evm_coder::abi::AbiReader::new_call(&encoded_tuple).unwrap(); - let restored_struct_data = ::abi_read(&mut decoder).unwrap(); - assert_eq!(restored_struct_data, tuple_struct_data); - } - - { - let (_, mut decoder) = - evm_coder::abi::AbiReader::new_call(&encoded_type_struct).unwrap(); - let restored_tuple_data = ::abi_read(&mut decoder).unwrap(); - assert_eq!(restored_tuple_data, tuple_data); - } - { - let (_, mut decoder) = - evm_coder::abi::AbiReader::new_call(&encoded_tuple_struct).unwrap(); - let restored_tuple_data = ::abi_read(&mut decoder).unwrap(); - assert_eq!(restored_tuple_data, tuple_data); - } - } - - fn test_abi_write_impl(data: &A) -> Vec - where - A: evm_coder::abi::AbiWrite - + evm_coder::abi::AbiRead - + std::cmp::PartialEq - + std::fmt::Debug, - { - let mut writer = evm_coder::abi::AbiWriter::new_call(FUNCTION_IDENTIFIER); - data.abi_write(&mut writer); - let encoded_tuple = writer.finish(); - encoded_tuple - } - - #[test] - fn codec_struct_1_simple() { - let _a = 0xff; - test_impl::<(u8,), TupleStruct1SimpleParam, TypeStruct1SimpleParam>( - (_a,), - TupleStruct1SimpleParam(_a), - TypeStruct1SimpleParam { _a }, - ); - } - - #[test] - fn codec_struct_1_dynamic() { - let _a: String = "some string".into(); - test_impl::<(String,), TupleStruct1DynamicParam, TypeStruct1DynamicParam>( - (_a.clone(),), - TupleStruct1DynamicParam(_a.clone()), - TypeStruct1DynamicParam { _a }, - ); - } - - #[test] - fn codec_struct_1_derived_simple() { - let _a: u8 = 0xff; - test_impl::<((u8,),), TupleStruct1DerivedSimpleParam, TypeStruct1DerivedSimpleParam>( - ((_a,),), - TupleStruct1DerivedSimpleParam(TupleStruct1SimpleParam(_a)), - TypeStruct1DerivedSimpleParam { - _a: TypeStruct1SimpleParam { _a }, - }, - ); - } - - #[test] - fn codec_struct_1_derived_dynamic() { - let _a: String = "some string".into(); - test_impl::<((String,),), TupleStruct1DerivedDynamicParam, TypeStruct1DerivedDynamicParam>( - ((_a.clone(),),), - TupleStruct1DerivedDynamicParam(TupleStruct1DynamicParam(_a.clone())), - TypeStruct1DerivedDynamicParam { - _a: TypeStruct1DynamicParam { _a }, - }, - ); - } - - #[test] - fn codec_struct_2_simple() { - let _a = 0xff; - let _b = 0xbeefbaba; - test_impl::<(u8, u32), TupleStruct2SimpleParam, TypeStruct2SimpleParam>( - (_a, _b), - TupleStruct2SimpleParam(_a, _b), - TypeStruct2SimpleParam { _a, _b }, - ); - } - - #[test] - fn codec_struct_2_dynamic() { - let _a: String = "some string".into(); - let _b: Bytes = Bytes(vec![0x11, 0x22, 0x33]); - test_impl::<(String, Bytes), TupleStruct2DynamicParam, TypeStruct2DynamicParam>( - (_a.clone(), _b.clone()), - TupleStruct2DynamicParam(_a.clone(), _b.clone()), - TypeStruct2DynamicParam { _a, _b }, - ); - } - - #[test] - fn codec_struct_2_mixed() { - let _a: u8 = 0xff; - let _b: Bytes = Bytes(vec![0x11, 0x22, 0x33]); - test_impl::<(u8, Bytes), TupleStruct2MixedParam, TypeStruct2MixedParam>( - (_a.clone(), _b.clone()), - TupleStruct2MixedParam(_a.clone(), _b.clone()), - TypeStruct2MixedParam { _a, _b }, - ); - } - - #[test] - fn codec_struct_2_derived_simple() { - let _a = 0xff; - let _b = 0xbeefbaba; - test_impl::< - ((u8,), (u8, u32)), - TupleStruct2DerivedSimpleParam, - TypeStruct2DerivedSimpleParam, - >( - ((_a,), (_a, _b)), - TupleStruct2DerivedSimpleParam( - TupleStruct1SimpleParam(_a), - TupleStruct2SimpleParam(_a, _b), - ), - TypeStruct2DerivedSimpleParam { - _a: TypeStruct1SimpleParam { _a }, - _b: TypeStruct2SimpleParam { _a, _b }, - }, - ); - } - - #[test] - fn codec_struct_2_derived_dynamic() { - let _a = "some string".to_string(); - let _b = Bytes(vec![0x11, 0x22, 0x33]); - test_impl::< - ((String,), (String, Bytes)), - TupleStruct2DerivedDynamicParam, - TypeStruct2DerivedDynamicParam, - >( - ((_a.clone(),), (_a.clone(), _b.clone())), - TupleStruct2DerivedDynamicParam( - TupleStruct1DynamicParam(_a.clone()), - TupleStruct2DynamicParam(_a.clone(), _b.clone()), - ), - TypeStruct2DerivedDynamicParam { - _a: TypeStruct1DynamicParam { _a: _a.clone() }, - _b: TypeStruct2DynamicParam { _a, _b }, - }, - ); - } - - #[test] - fn codec_struct_3_derived_mixed() { - let int = 0xff; - let by = Bytes(vec![0x11, 0x22, 0x33]); - let string = "some string".to_string(); - test_impl::< - ((u8,), (String, Bytes), (u8, Bytes)), - TupleStruct3DerivedMixedParam, - TypeStruct3DerivedMixedParam, - >( - ((int,), (string.clone(), by.clone()), (int, by.clone())), - TupleStruct3DerivedMixedParam( - TupleStruct1SimpleParam(int), - TupleStruct2DynamicParam(string.clone(), by.clone()), - TupleStruct2MixedParam(int, by.clone()), - ), - TypeStruct3DerivedMixedParam { - _a: TypeStruct1SimpleParam { _a: int }, - _b: TypeStruct2DynamicParam { - _a: string.clone(), - _b: by.clone(), - }, - _c: TypeStruct2MixedParam { _a: int, _b: by }, - }, - ); - } - - #[derive(AbiCoder, PartialEq, Debug)] - struct TypeStruct2SimpleStruct1Simple { - _a: TypeStruct2SimpleParam, - _b: TypeStruct2SimpleParam, - _c: u8, - } - #[derive(AbiCoder, PartialEq, Debug)] - struct TupleStruct2SimpleStruct1Simple(TupleStruct2SimpleParam, TupleStruct2SimpleParam, u8); - - #[test] - fn codec_struct_2_struct_simple_1_simple() { - let _a = 0xff; - let _b = 0xbeefbaba; - test_impl::< - ((u8, u32), (u8, u32), u8), - TupleStruct2SimpleStruct1Simple, - TypeStruct2SimpleStruct1Simple, - >( - ((_a, _b), (_a, _b), _a), - TupleStruct2SimpleStruct1Simple( - TupleStruct2SimpleParam(_a, _b), - TupleStruct2SimpleParam(_a, _b), - _a, - ), - TypeStruct2SimpleStruct1Simple { - _a: TypeStruct2SimpleParam { _a, _b }, - _b: TypeStruct2SimpleParam { _a, _b }, - _c: _a, - }, - ); - } -} - -mod test_enum { - use evm_coder::AbiCoder; - - /// Some docs - /// At multi - /// line - #[derive(AbiCoder, Debug, PartialEq, Default, Clone, Copy)] - #[repr(u8)] - enum Color { - /// Docs for Red - /// multi - /// line - Red, - Green, - /// Docs for Blue - #[default] - Blue, - } - - #[test] - fn empty() {} - - #[test] - fn bad_enums() { - let t = trybuild::TestCases::new(); - t.compile_fail("tests/build_failed/abi_derive_enum_generation.rs"); - } - - #[test] - fn impl_abi_type_signature_same_for_structs() { - assert_eq!( - ::SIGNATURE - .as_str() - .unwrap(), - ::SIGNATURE.as_str().unwrap() - ); - } - - #[test] - fn impl_abi_type_is_dynamic_same_for_structs() { - assert_eq!( - ::is_dynamic(), - ::is_dynamic() - ); - } - - #[test] - fn impl_abi_type_size_same_for_structs() { - assert_eq!( - ::size(), - ::size() - ); - } - - #[test] - fn test_coder() { - const FUNCTION_IDENTIFIER: u32 = 0xdeadbeef; - - let encoded_enum = { - let mut writer = evm_coder::abi::AbiWriter::new_call(FUNCTION_IDENTIFIER); - ::abi_write(&Color::Green, &mut writer); - writer.finish() - }; - - let encoded_u8 = { - let mut writer = evm_coder::abi::AbiWriter::new_call(FUNCTION_IDENTIFIER); - ::abi_write(&(Color::Green as u8), &mut writer); - writer.finish() - }; - - similar_asserts::assert_eq!(encoded_enum, encoded_u8); - - { - let (_, mut decoder) = evm_coder::abi::AbiReader::new_call(&encoded_enum).unwrap(); - let restored_enum_data = - ::abi_read(&mut decoder).unwrap(); - assert_eq!(restored_enum_data, Color::Green); - } - } -} --- a/crates/evm-coder/tests/build_failed/abi_derive_enum_generation.rs +++ /dev/null @@ -1,36 +0,0 @@ -use evm_coder_procedural::AbiCoder; - -#[derive(AbiCoder)] -enum NonRepr { - A, - B, - C, -} - -#[derive(AbiCoder)] -#[repr(u32)] -enum NonReprU8 { - A, - B, - C, -} - -#[derive(AbiCoder)] -#[repr(u8)] -enum RustEnum { - A(u128), - B, - C, -} - -#[derive(AbiCoder)] -#[repr(u8)] -enum WithExplicit { - A = 128, - B, - C, -} - -fn main() { - assert!(false); -} --- a/crates/evm-coder/tests/build_failed/abi_derive_enum_generation.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: Enum is not "repr(u8)" - --> tests/build_failed/abi_derive_enum_generation.rs:4:6 - | -4 | enum NonRepr { - | ^^^^^^^ - -error: Enum is not "repr(u8)" - --> tests/build_failed/abi_derive_enum_generation.rs:11:8 - | -11 | #[repr(u32)] - | ^^^ - -error: Enumeration parameters should not have fields - --> tests/build_failed/abi_derive_enum_generation.rs:21:2 - | -21 | A(u128), - | ^ - -error: Enumeration options should not have an explicit specified value - --> tests/build_failed/abi_derive_enum_generation.rs:29:2 - | -29 | A = 128, - | ^ --- a/crates/evm-coder/tests/build_failed/abi_derive_struct_generation.rs +++ /dev/null @@ -1,11 +0,0 @@ -use evm_coder_procedural::AbiCoder; - -#[derive(AbiCoder, PartialEq, Debug)] -struct EmptyStruct {} - -#[derive(AbiCoder, PartialEq, Debug)] -struct EmptyTupleStruct(); - -fn main() { - assert!(false); -} --- a/crates/evm-coder/tests/build_failed/abi_derive_struct_generation.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: Empty structs not supported - --> tests/build_failed/abi_derive_struct_generation.rs:4:8 - | -4 | struct EmptyStruct {} - | ^^^^^^^^^^^ - -error: Empty structs not supported - --> tests/build_failed/abi_derive_struct_generation.rs:7:8 - | -7 | struct EmptyTupleStruct(); - | ^^^^^^^^^^^^^^^^ --- a/crates/evm-coder/tests/build_failed/custom_signature_over_max_size.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![allow(dead_code)] -use std::str::from_utf8; - -use evm_coder::{ - make_signature, - custom_signature::{SignatureUnit, SIGNATURE_SIZE_LIMIT}, -}; - -trait Name { - const SIGNATURE: SignatureUnit; - - fn name() -> &'static str { - from_utf8(&Self::SIGNATURE.data[..Self::SIGNATURE.len]).expect("bad utf-8") - } -} - -impl Name for Vec { - const SIGNATURE: SignatureUnit = - evm_coder::make_signature!(new nameof(T::SIGNATURE) fixed("[]")); -} - -struct MaxSize(); -impl Name for MaxSize { - const SIGNATURE: SignatureUnit = SignatureUnit { - data: [b'!'; SIGNATURE_SIZE_LIMIT], - len: SIGNATURE_SIZE_LIMIT, - }; -} - -const NAME: SignatureUnit = >::SIGNATURE; - -fn main() { - assert!(false); -} --- a/crates/evm-coder/tests/build_failed/custom_signature_over_max_size.stderr +++ /dev/null @@ -1,21 +0,0 @@ -warning: unused import: `make_signature` - --> tests/build_failed/custom_signature_over_max_size.rs:5:2 - | -5 | make_signature, - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(unused_imports)]` on by default - -error[E0080]: evaluation of ` as Name>::SIGNATURE` failed - --> tests/build_failed/custom_signature_over_max_size.rs:19:3 - | -19 | evm_coder::make_signature!(new nameof(T::SIGNATURE) fixed("[]")); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ index out of bounds: the length is 256 but the index is 256 - | - = note: this error originates in the macro `$crate::make_signature` which comes from the expansion of the macro `evm_coder::make_signature` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0080]: evaluation of constant value failed - --> tests/build_failed/custom_signature_over_max_size.rs:30:29 - | -30 | const NAME: SignatureUnit = >::SIGNATURE; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors --- a/crates/evm-coder/tests/conditional_is.rs +++ /dev/null @@ -1,44 +0,0 @@ -use evm_coder::{types::*, solidity_interface, execution::Result}; - -pub struct Contract(bool); - -#[solidity_interface(name = A)] -impl Contract { - fn method_a() -> Result<()> { - Ok(()) - } -} - -#[solidity_interface(name = B)] -impl Contract { - fn method_b() -> Result<()> { - Ok(()) - } -} - -#[solidity_interface(name = Contract, is( - A(if(this.0)), - B(if(!this.0)), -))] -impl Contract {} - -#[test] -fn conditional_erc165() { - assert!(ContractCall::supports_interface( - &Contract(true), - ACall::METHOD_A - )); - assert!(!ContractCall::supports_interface( - &Contract(false), - ACall::METHOD_A - )); - - assert!(ContractCall::supports_interface( - &Contract(false), - BCall::METHOD_B - )); - assert!(!ContractCall::supports_interface( - &Contract(true), - BCall::METHOD_B - )); -} --- a/crates/evm-coder/tests/generics.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -use std::marker::PhantomData; -use evm_coder::{execution::Result, generate_stubgen, solidity_interface, types::*}; -use primitive_types::U256; - -pub struct Generic(PhantomData); - -#[solidity_interface(name = GenericIs)] -impl Generic { - fn test_1(&self) -> Result { - unreachable!() - } -} - -#[solidity_interface(name = Generic, is(GenericIs))] -impl> Generic { - fn test_2(&self) -> Result { - unreachable!() - } -} - -generate_stubgen!(gen_iface, GenericCall<()>, false); - -#[solidity_interface(name = GenericWhere)] -impl Generic -where - T: core::fmt::Debug, -{ - fn test_3(&self) -> Result { - unreachable!() - } -} - -generate_stubgen!(gen_where_iface, GenericWhereCall<()>, false); --- a/crates/evm-coder/tests/log.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -#![allow(dead_code)] - -use evm_coder::{ToLog, types::*}; -use primitive_types::U256; - -#[derive(ToLog)] -enum ERC721Log { - Transfer { - #[indexed] - from: Address, - #[indexed] - to: Address, - value: U256, - }, - Eee { - #[indexed] - aaa: Address, - bbb: U256, - }, -} --- a/crates/evm-coder/tests/random.rs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -#![allow(dead_code)] // This test only checks that macros is not panicking - -use evm_coder::{ - abi::AbiType, ToLog, execution::Result, solidity_interface, types::*, solidity, weight, -}; -use primitive_types::U256; - -pub struct Impls; - -#[solidity_interface(name = OurInterface)] -impl Impls { - fn fn_a(&self, _input: U256) -> Result { - unreachable!() - } -} - -#[solidity_interface(name = OurInterface1)] -impl Impls { - fn fn_b(&self, _input: u128) -> Result { - unreachable!() - } -} - -#[derive(ToLog)] -enum OurEvents { - Event1 { - field1: u32, - }, - Event2 { - field1: u32, - #[indexed] - field2: u32, - }, -} - -#[solidity_interface( - name = OurInterface2, - is(OurInterface), - inline_is(OurInterface1), - events(OurEvents) -)] -impl Impls { - #[solidity(rename_selector = "fnK")] - fn fn_c(&self, _input: u32) -> Result { - unreachable!() - } - fn fn_d(&self, _value: u32) -> Result { - unreachable!() - } - - fn caller_sensitive(&self, _caller: Caller) -> Result { - unreachable!() - } - fn payable(&mut self, _value: Value) -> Result { - unreachable!() - } - - #[weight(*_weight)] - fn with_weight(&self, _weight: u64) -> Result<()> { - unreachable!() - } - - /// Doccoment example - fn with_doc(&self) -> Result<()> { - unreachable!() - } -} - -#[solidity_interface( - name = ValidSelector, - expect_selector = 0x00000000, -)] -impl Impls {} --- a/crates/evm-coder/tests/solidity_generation.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. -// This file is part of Unique Network. - -// Unique Network is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Unique Network is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Unique Network. If not, see . - -use evm_coder::{abi::AbiType, execution::Result, generate_stubgen, solidity_interface, types::*}; -use primitive_types::U256; - -pub struct ERC20; - -#[solidity_interface(name = ERC20)] -impl ERC20 { - fn decimals(&self) -> Result { - unreachable!() - } - /// Get balance of specified owner - fn balance_of(&self, _owner: Address) -> Result { - unreachable!() - } - fn transfer(&mut self, _caller: Caller, _to: Address, _value: U256) -> Result { - unreachable!() - } - fn transfer_from( - &mut self, - _caller: Caller, - _from: Address, - _to: Address, - _value: U256, - ) -> Result { - unreachable!() - } - fn approve(&mut self, _caller: Caller, _spender: Address, _value: U256) -> Result { - unreachable!() - } - fn allowance(&self, _owner: Address, _spender: Address) -> Result { - unreachable!() - } -} - -generate_stubgen!(gen_impl, ERC20Call, true); -generate_stubgen!(gen_iface, ERC20Call, false); --- a/crates/struct-versioning/src/lib.rs +++ b/crates/struct-versioning/src/lib.rs @@ -223,7 +223,7 @@ }; let current_fields = fields .iter() - .filter_map(|(ver, field)| ver.exists_on(version).then(|| field)); + .filter_map(|(ver, field)| ver.exists_on(version).then_some(field)); let mut doc = Vec::new(); if version > attr.first_version { --- a/pallets/common/src/erc.rs +++ b/pallets/common/src/erc.rs @@ -17,12 +17,12 @@ //! This module contains the implementation of pallet methods for evm. pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId}; -use evm_coder::{ +use pallet_evm_coder_substrate::{ abi::AbiType, - solidity_interface, solidity, ToLog, + solidity_interface, ToLog, types::*, - execution::{Result, Error}, - weight, + execution::{Result, Error, PreDispatch}, + frontier_contract, }; use pallet_evm_coder_substrate::dispatch_to_evm; use sp_std::{vec, vec::Vec}; @@ -36,6 +36,11 @@ Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf, eth, weights::WeightInfo, }; +frontier_contract! { + macro_rules! CollectionHandle_result {...} + impl Contract for CollectionHandle {...} +} + /// Events for ethereum collection helper. #[derive(ToLog)] pub enum CollectionHelpersEvents { @@ -83,7 +88,7 @@ } /// @title A contract that allows you to work with collections. -#[solidity_interface(name = Collection)] +#[solidity_interface(name = Collection, enum(derive(PreDispatch)), enum_attr(weight))] impl CollectionHandle where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>, @@ -272,7 +277,7 @@ None => return Ok(Default::default()), }; - Ok(eth::CrossAddress::from_sub::(&sponsor)) + Ok(eth::CrossAddress::from_sub::(sponsor)) } /// Get current collection limits. @@ -280,11 +285,6 @@ /// @return Array of collection limits fn collection_limits(&self) -> Result> { let limits = &self.collection.limits; - - let convert_value_from_bool = |ob: Option| match ob { - Some(b) => Some(b as u32), - None => None, - }; Ok(vec![ eth::CollectionLimit::new( @@ -307,10 +307,12 @@ None } }) - .unwrap_or(eth::CollectionLimit::new( - eth::CollectionLimitField::SponsoredDataRateLimit, - Default::default(), - )), + .unwrap_or_else(|| { + eth::CollectionLimit::new( + eth::CollectionLimitField::SponsoredDataRateLimit, + Default::default(), + ) + }), eth::CollectionLimit::new(eth::CollectionLimitField::TokenLimit, limits.token_limit), eth::CollectionLimit::new( eth::CollectionLimitField::SponsorTransferTimeout, @@ -322,15 +324,15 @@ ), eth::CollectionLimit::new( eth::CollectionLimitField::OwnerCanTransfer, - convert_value_from_bool(limits.owner_can_transfer), + limits.owner_can_transfer.map(u32::from), ), eth::CollectionLimit::new( eth::CollectionLimitField::OwnerCanDestroy, - convert_value_from_bool(limits.owner_can_destroy), + limits.owner_can_destroy.map(u32::from), ), eth::CollectionLimit::new( eth::CollectionLimitField::TransferEnabled, - convert_value_from_bool(limits.transfers_enabled), + limits.transfers_enabled.map(u32::from), ), ]) } @@ -680,7 +682,7 @@ /// Contains static property keys and values. pub mod static_property { - use evm_coder::{ + use pallet_evm_coder_substrate::{ execution::{Result, Error}, }; use alloc::format; --- a/pallets/common/src/eth.rs +++ b/pallets/common/src/eth.rs @@ -105,7 +105,9 @@ } } /// Converts [`CrossAddress`] to `CrossAccountId`. - pub fn into_sub_cross_account(&self) -> evm_coder::execution::Result + pub fn into_sub_cross_account( + &self, + ) -> pallet_evm_coder_substrate::execution::Result where T: pallet_evm::Config, T::AccountId: From<[u8; 32]>, @@ -130,7 +132,7 @@ } impl TryFrom for Property { - type Error = evm_coder::execution::Error; + type Error = pallet_evm_coder_substrate::execution::Error; fn try_from(from: up_data_structs::Property) -> Result { let key = evm_coder::types::String::from_utf8(from.key.into()) @@ -141,7 +143,7 @@ } impl TryInto for Property { - type Error = evm_coder::execution::Error; + type Error = pallet_evm_coder_substrate::execution::Error; fn try_into(self) -> Result { let key = >::from(self.key) @@ -212,7 +214,7 @@ } impl TryInto for CollectionLimit { - type Error = evm_coder::execution::Error; + type Error = pallet_evm_coder_substrate::execution::Error; fn try_into(self) -> Result { let value = self @@ -378,7 +380,8 @@ /// Convert vector of [`TokenPropertyPermission`] into vector of [`up_data_structs::PropertyKeyPermission`]. pub fn into_property_key_permissions( permissions: Vec, - ) -> evm_coder::execution::Result> { + ) -> pallet_evm_coder_substrate::execution::Result> + { let mut perms = Vec::new(); for TokenPropertyPermission { key, permissions } in permissions { --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -192,7 +192,10 @@ } /// Consume gas for reading. - pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> { + pub fn consume_store_reads( + &self, + reads: u64, + ) -> pallet_evm_coder_substrate::execution::Result<()> { self.recorder .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time( ::DbWeight::get() @@ -202,7 +205,10 @@ } /// Consume gas for writing. - pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> { + pub fn consume_store_writes( + &self, + writes: u64, + ) -> pallet_evm_coder_substrate::execution::Result<()> { self.recorder .consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_ref_time( ::DbWeight::get() @@ -216,7 +222,7 @@ &self, reads: u64, writes: u64, - ) -> evm_coder::execution::Result<()> { + ) -> pallet_evm_coder_substrate::execution::Result<()> { let weight = ::DbWeight::get(); let reads = weight.read.saturating_mul(reads); let writes = weight.read.saturating_mul(writes); --- a/pallets/evm-coder-substrate/Cargo.toml +++ b/pallets/evm-coder-substrate/Cargo.toml @@ -7,10 +7,11 @@ [dependencies] # Note: `package = "parity-scale-codec"` must be supplied since the `Encode` macro searches for it. codec = { workspace = true, package = "parity-scale-codec" } - scale-info = { workspace = true } evm-coder = { workspace = true } +evm-core = { workspace = true } + frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } @@ -19,6 +20,9 @@ sp-std = { workspace = true } up-data-structs = { workspace = true } +evm-coder-substrate-procedural = { path = "./procedural" } +spez = "0.1.1" + [features] default = ["std"] runtime-benchmarks = ['frame-benchmarking'] --- /dev/null +++ b/pallets/evm-coder-substrate/procedural/Cargo.toml @@ -0,0 +1,12 @@ +[package] +edition = "2021" +name = "evm-coder-substrate-procedural" +version = "0.1.0" + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = "1.0" + +[lib] +proc-macro = true --- /dev/null +++ b/pallets/evm-coder-substrate/procedural/src/lib.rs @@ -0,0 +1,135 @@ +use std::result; + +use proc_macro2::{TokenStream, Ident}; +use quote::quote; +use syn::{ + Error, DeriveInput, Data, Attribute, + parse::{Parse, ParseBuffer}, + spanned::Spanned, + Expr, parenthesized, +}; + +type Result = result::Result; + +fn parse_attr(attrs: &[Attribute], ident: I) -> Result> +where + Ident: PartialEq, +{ + let attrs = attrs + .iter() + .filter(|a| a.path.is_ident(&ident)) + .collect::>(); + if attrs.len() > 1 { + return Err(Error::new( + attrs[1].span(), + "this attribute may be specified only once", + )); + } else if attrs.is_empty() { + return Ok(None); + } + let attr = attrs[0]; + + let parsed = syn::parse2(attr.tokens.clone())?; + Ok(Some(parsed)) +} + +struct WeightAttr { + handler: Expr, +} +impl Parse for WeightAttr { + fn parse(input: &ParseBuffer) -> Result { + let expr; + parenthesized!(expr in input); + let handler = Expr::parse(&expr)?; + + Ok(Self { handler }) + } +} + +#[proc_macro_derive(PreDispatch, attributes(pre_dispatch, weight))] +pub fn derive_predispatch(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + fn inner(input: DeriveInput) -> Result { + let (impl_generics, ty_generics, _where_clause) = input.generics.split_for_impl(); + let ident = &input.ident; + let Data::Enum(data) = &input.data else { + return Err(Error::new_spanned(input, "PreDispatch input is enum")) + }; + + let matching = data + .variants + .iter() + .map(|var| { + let name = &var.ident; + let handler = match &var.fields { + syn::Fields::Named(named) => { + if let Some(weight) = parse_attr::(&var.attrs, "weight")? { + let weight = &weight.handler; + let fields = named.named.iter().flat_map(|n| &n.ident); + quote! { + {#(#fields,)*} => { + ::pallet_evm_coder_substrate::execution::DispatchInfo { + weight: ::pallet_evm_coder_substrate::execution::Weight::from(#weight), + } + } + } + } else { + quote! { + {..} => { + ::pallet_evm_coder_substrate::execution::DispatchInfo { + weight: ::pallet_evm_coder_substrate::execution::Weight::zero() + } + } + } + } + } + syn::Fields::Unit => { + if let Some(weight) = parse_attr::(&var.attrs, "weight")? { + let weight = &weight.handler; + quote! { + => { + ::pallet_evm_coder_substrate::execution::DispatchInfo { + weight: ::pallet_evm_coder_substrate::execution::Weight::from(#weight), + } + } + } + } else { + quote! { + => { + ::pallet_evm_coder_substrate::execution::DispatchInfo { + weight: ::pallet_evm_coder_substrate::execution::Weight::zero() + } + } + } + } + } + syn::Fields::Unnamed(_) => { + quote! { + (call, ..) => { + call.dispatch_info() + } + } + } + }; + Ok(quote! { + Self::#name #handler, + }) + }) + .collect::()?; + + Ok(quote! { + #[allow(unused_variables)] + impl #impl_generics ::pallet_evm_coder_substrate::execution::PreDispatch for #ident #ty_generics where T: crate::Config { + fn dispatch_info(&self) -> ::pallet_evm_coder_substrate::execution::DispatchInfo { + match self { + #matching + } + } + } + }) + } + let item = syn::parse_macro_input!(item as DeriveInput); + match inner(item) { + Ok(o) => o.into(), + Err(e) => e.into_compile_error().into(), + } +} --- /dev/null +++ b/pallets/evm-coder-substrate/src/execution.rs @@ -0,0 +1,186 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +//! Contract execution related types + +#[cfg(not(feature = "std"))] +use alloc::string::{String, ToString}; +#[cfg(feature = "std")] +use std::string::{String, ToString}; + +use evm_coder::ERC165Call; +use evm_core::{ExitError, ExitFatal}; + +pub use frame_support::weights::Weight; +pub use evm_coder_substrate_procedural::PreDispatch; + +/// Execution error, should be convertible between EVM and Substrate. +#[derive(Debug, Clone)] +pub enum Error { + /// Non-fatal contract error occured + Revert(String), + /// EVM fatal error + Fatal(ExitFatal), + /// EVM normal error + Error(ExitError), +} + +impl From for Error +where + E: ToString, +{ + fn from(e: E) -> Self { + Self::Revert(e.to_string()) + } +} + +/// To be used in [`crate::solidity_interface`] implementation. +pub type Result = core::result::Result; +/// Return type of items in [`crate::solidity_interface`] definition +pub type ResultWithPostInfo = + core::result::Result, WithPostDispatchInfo>; + +pub trait PreDispatch { + fn dispatch_info(&self) -> DispatchInfo; +} + +impl PreDispatch for ERC165Call { + fn dispatch_info(&self) -> DispatchInfo { + DispatchInfo { + // ERC165 impl should be cheap + weight: Weight::from_ref_time(200), + } + } +} + +/// Static information collected from [`crate::weight`]. +pub struct DispatchInfo { + /// Statically predicted call weight + pub weight: Weight, +} + +impl From for DispatchInfo { + fn from(weight: Weight) -> Self { + Self { weight } + } +} +impl From for DispatchInfo { + fn from(weight: u64) -> Self { + Self { + weight: Weight::from_ref_time(weight), + } + } +} +impl From<()> for DispatchInfo { + fn from(_: ()) -> Self { + Self { + weight: Weight::zero(), + } + } +} + +/// Weight information that is only available post dispatch. +/// Note: This can only be used to reduce the weight or fee, not increase it. +#[derive(Default, Clone)] +pub struct PostDispatchInfo { + /// Actual weight consumed by call + pub actual_weight: Option, +} + +impl PostDispatchInfo { + /// Calculate amount to be returned back to user + pub fn calc_unspent(&self, info: &DispatchInfo) -> Weight { + info.weight - self.calc_actual_weight(info) + } + + /// Calculate actual consumed weight, saturating to weight reported + /// pre-dispatch + pub fn calc_actual_weight(&self, info: &DispatchInfo) -> Weight { + if let Some(actual_weight) = self.actual_weight { + actual_weight.min(info.weight) + } else { + info.weight + } + } +} + +/// Wrapper for PostDispatchInfo and any user-provided data +#[derive(Clone)] +pub struct WithPostDispatchInfo { + /// User provided data + pub data: T, + /// Info known after dispatch + pub post_info: PostDispatchInfo, +} + +impl From for WithPostDispatchInfo { + fn from(data: T) -> Self { + Self { + data, + post_info: Default::default(), + } + } +} +#[allow(clippy::crate_in_macro_def)] +#[macro_export] +macro_rules! frontier_contract { + ( + macro_rules! $res:ident {...} + impl$(<$($gen:ident $(: $($(+)? $bound:ty)*)?),+ $(,)?>)? Contract for $ty:ty {...} + ) => { + /// Generate macro to convert function return value into Contract result + macro_rules! $res { + ($i:expr) => {{ + pallet_evm_coder_substrate::spez! { + for res = $i; + match ::pallet_evm_coder_substrate::execution::ResultWithPostInfo -> ::pallet_evm_coder_substrate::execution::ResultWithPostInfo { + res + } + match ::pallet_evm_coder_substrate::execution::Result -> ::pallet_evm_coder_substrate::execution::ResultWithPostInfo { + res + .map(pallet_evm_coder_substrate::execution::WithPostDispatchInfo::from) + .map_err(pallet_evm_coder_substrate::execution::WithPostDispatchInfo::from) + } + match T -> ::pallet_evm_coder_substrate::execution::ResultWithPostInfo { + Ok(pallet_evm_coder_substrate::execution::WithPostDispatchInfo::from(res)) + } + } + }}; + } + impl $(<$($gen),+>)? $crate::Contract for $ty where T: crate::Config { + type Error = ::pallet_evm_coder_substrate::execution::Error; + type WithPostInfo = ::pallet_evm_coder_substrate::execution::WithPostDispatchInfo; + type Result = core::result::Result; + fn map_post( + v: Self::WithPostInfo, + mapper: impl FnOnce(I) -> O, + ) -> Self::WithPostInfo { + ::pallet_evm_coder_substrate::execution::WithPostDispatchInfo { + data: mapper(v.data), + post_info: v.post_info, + } + } + fn with_default_post(v: V) -> Self::WithPostInfo { + ::pallet_evm_coder_substrate::execution::WithPostDispatchInfo { + data: v, + post_info: ::pallet_evm_coder_substrate::execution::PostDispatchInfo { + actual_weight: None, + }, + } + } + } + }; +} --- a/pallets/evm-coder-substrate/src/lib.rs +++ b/pallets/evm-coder-substrate/src/lib.rs @@ -16,10 +16,13 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate self as pallet_evm_coder_substrate; + #[cfg(not(feature = "std"))] extern crate alloc; #[cfg(not(feature = "std"))] use alloc::format; +use execution::PreDispatch; use frame_support::dispatch::Weight; use core::marker::PhantomData; @@ -37,10 +40,14 @@ use sp_core::H160; // #[cfg(feature = "runtime-benchmarks")] // pub mod benchmarking; +pub mod execution; +pub use evm_coder::*; +#[doc(hidden)] +pub use spez::spez; + use evm_coder::{ abi::{AbiReader, AbiWrite, AbiWriter}, - execution, types::{Msg, Value}, }; @@ -175,7 +182,7 @@ handle: &mut impl PrecompileHandle, result: execution::Result>, ) -> Option { - use evm_coder::execution::Error; + use execution::Error; // We ignore error here, as it should not occur, as we have our own bookkeeping of gas let _ = handle.record_cost(self.initial_gas - self.gas_left()); Some(match result { @@ -200,7 +207,7 @@ } pub fn dispatch_to_evm(err: DispatchError) -> execution::Error { - use evm_coder::execution::Error as ExError; + use execution::Error as ExError; match err { DispatchError::Module(ModuleError { index, error, .. }) if index @@ -231,15 +238,14 @@ } /// Helper to simplify implementing bridge between evm-coder definitions and pallet-evm -pub fn call< +pub fn call(handle: &mut H, mut e: E) -> Option +where T: Config, - C: evm_coder::Call + evm_coder::Weighted, + C: evm_coder::Call + PreDispatch, E: evm_coder::Callable + WithRecorder, H: PrecompileHandle, ->( - handle: &mut H, - mut e: E, -) -> Option { + execution::ResultWithPostInfo: From>, +{ let result = call_internal( handle.context().caller, &mut e, @@ -249,16 +255,18 @@ e.into_recorder().evm_to_precompile_output(handle, result) } -fn call_internal< - T: Config, - C: evm_coder::Call + evm_coder::Weighted, - E: evm_coder::Callable + WithRecorder, ->( +fn call_internal( caller: H160, e: &mut E, value: Value, input: &[u8], -) -> execution::Result> { +) -> execution::Result> +where + T: Config, + C: evm_coder::Call + PreDispatch, + E: Contract + evm_coder::Callable + WithRecorder, + execution::ResultWithPostInfo: From>, +{ let (selector, mut reader) = AbiReader::new_call(input)?; let call = C::parse(selector, &mut reader)?; if call.is_none() { @@ -267,15 +275,15 @@ } let call = call.unwrap(); - let dispatch_info = call.weight(); + let dispatch_info = call.dispatch_info(); e.recorder() .consume_gas(T::GasWeightMapping::weight_to_gas(dispatch_info.weight))?; - match e.call(Msg { + match execution::ResultWithPostInfo::from(e.call(Msg { call, caller, value, - }) { + })) { Ok(v) => { let unspent = v.post_info.calc_unspent(&dispatch_info); e.recorder() @@ -290,3 +298,26 @@ } } } + +#[cfg(test)] +#[allow(dead_code)] +mod tests { + use core::marker::PhantomData; + + use evm_coder::ERC165Call; + use frame_support::weights::Weight; + + use crate::execution::PreDispatch; + + #[derive(PreDispatch)] + enum ExampleCall { + ERC165Call(ERC165Call, PhantomData T>), + OtherCall(ERC165Call), + + #[weight(Weight::from_ref_time(a + b))] + Example { + a: u64, + b: u64, + }, + } +} --- a/pallets/evm-contract-helpers/src/eth.rs +++ b/pallets/evm-contract-helpers/src/eth.rs @@ -20,7 +20,6 @@ use core::marker::PhantomData; use evm_coder::{ abi::{AbiWriter, AbiType}, - execution::Result, generate_stubgen, solidity_interface, types::*, ToLog, @@ -30,7 +29,11 @@ ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle, account::CrossAccountId, }; -use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder, dispatch_to_evm}; +use pallet_evm_coder_substrate::{ + SubstrateRecorder, WithRecorder, dispatch_to_evm, + execution::{Result, PreDispatch}, + frontier_contract, +}; use pallet_evm_transaction_payment::CallContext; use sp_core::{H160, U256}; use up_data_structs::SponsorshipState; @@ -42,6 +45,11 @@ use up_sponsorship::SponsorshipHandler; use sp_std::vec::Vec; +frontier_contract! { + macro_rules! ContractHelpers_result {...} + impl Contract for ContractHelpers {...} +} + /// Pallet events. #[derive(ToLog)] pub enum ContractHelpersEvents { @@ -84,7 +92,7 @@ } /// @title Magic contract, which allows users to reconfigure other contracts -#[solidity_interface(name = ContractHelpers, events(ContractHelpersEvents))] +#[solidity_interface(name = ContractHelpers, events(ContractHelpersEvents), enum(derive(PreDispatch)))] impl ContractHelpers where T::AccountId: AsRef<[u8; 32]>, --- a/pallets/fungible/src/erc.rs +++ b/pallets/fungible/src/erc.rs @@ -20,10 +20,7 @@ use core::char::{REPLACEMENT_CHARACTER, decode_utf16}; use core::convert::TryInto; use evm_coder::AbiCoder; -use evm_coder::{ - abi::AbiType, ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, - weight, -}; +use evm_coder::{abi::AbiType, ToLog, generate_stubgen, solidity_interface, types::*}; use up_data_structs::CollectionMode; use pallet_common::{ CollectionHandle, @@ -32,7 +29,11 @@ }; use sp_std::vec::Vec; use pallet_evm::{account::CrossAccountId, PrecompileHandle}; -use pallet_evm_coder_substrate::{call, dispatch_to_evm}; +use pallet_evm_coder_substrate::{ + call, dispatch_to_evm, + execution::{PreDispatch, Result}, + frontier_contract, +}; use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _}; use sp_core::{U256, Get}; @@ -41,6 +42,11 @@ weights::WeightInfo, }; +frontier_contract! { + macro_rules! FungibleHandle_result {...} + impl Contract for FungibleHandle {...} +} + #[derive(ToLog)] pub enum ERC20Events { Transfer { @@ -65,7 +71,7 @@ amount: U256, } -#[solidity_interface(name = ERC20, events(ERC20Events), expect_selector = 0x942e8b22)] +#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x942e8b22)] impl FungibleHandle { fn name(&self) -> Result { Ok(decode_utf16(self.name.iter().copied()) @@ -145,7 +151,7 @@ } } -#[solidity_interface(name = ERC20Mintable)] +#[solidity_interface(name = ERC20Mintable, enum(derive(PreDispatch)), enum_attr(weight))] impl FungibleHandle { /// Mint tokens for `to` account. /// @param to account that will receive minted tokens @@ -158,13 +164,13 @@ let budget = self .recorder .weight_calls_budget(>::find_parent()); - >::create_item(&self, &caller, (to, amount), &budget) + >::create_item(self, &caller, (to, amount), &budget) .map_err(dispatch_to_evm::)?; Ok(true) } } -#[solidity_interface(name = ERC20UniqueExtensions)] +#[solidity_interface(name = ERC20UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))] impl FungibleHandle where T::AccountId: From<[u8; 32]>, @@ -181,10 +187,10 @@ } /// @notice A description for the collection. - fn description(&self) -> Result { - Ok(decode_utf16(self.description.iter().copied()) + fn description(&self) -> String { + decode_utf16(self.description.iter().copied()) .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)) - .collect::()) + .collect::() } #[weight(>::create_item())] @@ -278,7 +284,7 @@ }) .collect::>()?; - >::create_multiple_items(&self, &caller, amounts, &budget) + >::create_multiple_items(self, &caller, amounts, &budget) .map_err(dispatch_to_evm::)?; Ok(true) } @@ -330,7 +336,8 @@ ERC20Mintable, ERC20UniqueExtensions, Collection(via(common_mut returns CollectionHandle)), - ) + ), + enum(derive(PreDispatch)) )] impl FungibleHandle where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {} --- a/pallets/nonfungible/src/erc.rs +++ b/pallets/nonfungible/src/erc.rs @@ -24,16 +24,16 @@ char::{REPLACEMENT_CHARACTER, decode_utf16}, convert::TryInto, }; -use evm_coder::{ - abi::AbiType, ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, - weight, -}; +use evm_coder::{abi::AbiType, ToLog, generate_stubgen, solidity_interface, types::*}; use frame_support::BoundedVec; use up_data_structs::{ TokenId, PropertyPermission, PropertyKeyPermission, Property, CollectionId, PropertyKey, CollectionPropertiesVec, }; -use pallet_evm_coder_substrate::dispatch_to_evm; +use pallet_evm_coder_substrate::{ + dispatch_to_evm, frontier_contract, + execution::{Result, PreDispatch, Error}, +}; use sp_std::{vec::Vec, vec}; use pallet_common::{ CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations, @@ -50,8 +50,13 @@ SelfWeightOf, weights::WeightInfo, TokenProperties, }; +frontier_contract! { + macro_rules! NonfungibleHandle_result {...} + impl Contract for NonfungibleHandle {...} +} + /// @title A contract that allows to set and delete token properties and change token property permissions. -#[solidity_interface(name = TokenProperties)] +#[solidity_interface(name = TokenProperties, enum(derive(PreDispatch)), enum_attr(weight))] impl NonfungibleHandle { /// @notice Set permissions for token property. /// @dev Throws error if `msg.sender` is not admin or owner of the collection. @@ -59,8 +64,8 @@ /// @param isMutable Permission to mutate property. /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable. /// @param tokenOwner Permission to mutate property by token owner if property is mutable. + #[solidity(hide)] #[weight(>::set_token_property_permissions(1))] - #[solidity(hide)] fn set_token_property_permission( &mut self, caller: Caller, @@ -293,7 +298,7 @@ /// @title ERC-721 Non-Fungible Token Standard, optional metadata extension /// @dev See https://eips.ethereum.org/EIPS/eip-721 -#[solidity_interface(name = ERC721Metadata, expect_selector = 0x5b5e139f)] +#[solidity_interface(name = ERC721Metadata, expect_selector = 0x5b5e139f, enum(derive(PreDispatch)), enum_attr(weight))] impl NonfungibleHandle where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>, @@ -301,14 +306,14 @@ /// @notice A descriptive name for a collection of NFTs in this contract /// @dev real implementation of this function lies in `ERC721UniqueExtensions` #[solidity(hide, rename_selector = "name")] - fn name_proxy(&self) -> Result { + fn name_proxy(&self) -> String { self.name() } /// @notice An abbreviated name for NFTs in this contract /// @dev real implementation of this function lies in `ERC721UniqueExtensions` #[solidity(hide, rename_selector = "symbol")] - fn symbol_proxy(&self) -> Result { + fn symbol_proxy(&self) -> String { self.symbol() } @@ -362,14 +367,14 @@ /// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension /// @dev See https://eips.ethereum.org/EIPS/eip-721 -#[solidity_interface(name = ERC721Enumerable, expect_selector = 0x780e9d63)] +#[solidity_interface(name = ERC721Enumerable, expect_selector = 0x780e9d63, enum(derive(PreDispatch)), enum_attr(weight))] impl NonfungibleHandle { /// @notice Enumerate valid NFTs /// @param index A counter less than `totalSupply()` /// @return The token identifier for the `index`th NFT, /// (sort order not specified) - fn token_by_index(&self, index: U256) -> Result { - Ok(index) + fn token_by_index(&self, index: U256) -> U256 { + index } /// @dev Not implemented @@ -389,7 +394,7 @@ /// @title ERC-721 Non-Fungible Token Standard /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md -#[solidity_interface(name = ERC721, events(ERC721Events), expect_selector = 0x80ac58cd)] +#[solidity_interface(name = ERC721, events(ERC721Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x80ac58cd)] impl NonfungibleHandle { /// @notice Count all NFTs assigned to an owner /// @dev NFTs assigned to the zero address are considered invalid, and this @@ -516,7 +521,7 @@ } /// @title ERC721 Token that can be irreversibly burned (destroyed). -#[solidity_interface(name = ERC721Burnable)] +#[solidity_interface(name = ERC721Burnable, enum(derive(PreDispatch)), enum_attr(weight))] impl NonfungibleHandle { /// @notice Burns a specific ERC721 token. /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized @@ -533,7 +538,7 @@ } /// @title ERC721 minting logic. -#[solidity_interface(name = ERC721UniqueMintable)] +#[solidity_interface(name = ERC721UniqueMintable, enum(derive(PreDispatch)), enum_attr(weight))] impl NonfungibleHandle { /// @notice Function to mint a token. /// @param to The new owner @@ -698,28 +703,28 @@ } /// @title Unique extensions for ERC721. -#[solidity_interface(name = ERC721UniqueExtensions)] +#[solidity_interface(name = ERC721UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))] impl NonfungibleHandle where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>, { /// @notice A descriptive name for a collection of NFTs in this contract - fn name(&self) -> Result { - Ok(decode_utf16(self.name.iter().copied()) + fn name(&self) -> String { + decode_utf16(self.name.iter().copied()) .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)) - .collect::()) + .collect::() } /// @notice An abbreviated name for NFTs in this contract - fn symbol(&self) -> Result { - Ok(String::from_utf8_lossy(&self.token_prefix).into()) + fn symbol(&self) -> String { + String::from_utf8_lossy(&self.token_prefix).into() } /// @notice A description for the collection. - fn description(&self) -> Result { - Ok(decode_utf16(self.description.iter().copied()) + fn description(&self) -> String { + decode_utf16(self.description.iter().copied()) .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)) - .collect::()) + .collect::() } /// Returns the owner (in cross format) of the token. @@ -1035,8 +1040,8 @@ } /// @notice Returns collection helper contract address - fn collection_helper_address(&self) -> Result
{ - Ok(T::ContractAddress::get()) + fn collection_helper_address(&self) -> Address { + T::ContractAddress::get() } } @@ -1051,7 +1056,8 @@ ERC721Metadata(if(this.flags.erc721metadata)), Collection(via(common_mut returns CollectionHandle)), TokenProperties, - ) + ), + enum(derive(PreDispatch)), )] impl NonfungibleHandle where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {} --- a/pallets/refungible/src/erc.rs +++ b/pallets/refungible/src/erc.rs @@ -25,10 +25,7 @@ char::{REPLACEMENT_CHARACTER, decode_utf16}, convert::TryInto, }; -use evm_coder::{ - abi::AbiType, ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, - weight, -}; +use evm_coder::{abi::AbiType, ToLog, generate_stubgen, solidity_interface, types::*}; use frame_support::{BoundedBTreeMap, BoundedVec}; use pallet_common::{ CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations, @@ -37,7 +34,11 @@ eth::{self, TokenUri}, }; use pallet_evm::{account::CrossAccountId, PrecompileHandle}; -use pallet_evm_coder_substrate::{call, dispatch_to_evm}; +use pallet_evm_coder_substrate::{ + call, dispatch_to_evm, + execution::{PreDispatch, Result, Error}, + frontier_contract, +}; use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _}; use sp_core::{H160, U256, Get}; use sp_std::{collections::btree_map::BTreeMap, vec::Vec, vec}; @@ -51,10 +52,15 @@ TokenProperties, TokensMinted, TotalSupply, weights::WeightInfo, }; +frontier_contract! { + macro_rules! RefungibleHandle_result {...} + impl Contract for RefungibleHandle {...} +} + pub const ADDRESS_FOR_PARTIALLY_OWNED_TOKENS: H160 = H160::repeat_byte(0xff); /// @title A contract that allows to set and delete token properties and change token property permissions. -#[solidity_interface(name = TokenProperties)] +#[solidity_interface(name = TokenProperties, enum(derive(PreDispatch)), enum_attr(weight))] impl RefungibleHandle { /// @notice Set permissions for token property. /// @dev Throws error if `msg.sender` is not admin or owner of the collection. @@ -62,8 +68,8 @@ /// @param isMutable Permission to mutate property. /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable. /// @param tokenOwner Permission to mutate property by token owner if property is mutable. + #[solidity(hide)] #[weight(>::set_token_property_permissions(1))] - #[solidity(hide)] fn set_token_property_permission( &mut self, caller: Caller, @@ -290,7 +296,7 @@ /// @title ERC-721 Non-Fungible Token Standard, optional metadata extension /// @dev See https://eips.ethereum.org/EIPS/eip-721 -#[solidity_interface(name = ERC721Metadata, expect_selector = 0x5b5e139f)] +#[solidity_interface(name = ERC721Metadata, enum(derive(PreDispatch)), expect_selector = 0x5b5e139f)] impl RefungibleHandle where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>, @@ -359,14 +365,14 @@ /// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension /// @dev See https://eips.ethereum.org/EIPS/eip-721 -#[solidity_interface(name = ERC721Enumerable, expect_selector = 0x780e9d63)] +#[solidity_interface(name = ERC721Enumerable, enum(derive(PreDispatch)), expect_selector = 0x780e9d63)] impl RefungibleHandle { /// @notice Enumerate valid RFTs /// @param index A counter less than `totalSupply()` /// @return The token identifier for the `index`th NFT, /// (sort order not specified) - fn token_by_index(&self, index: U256) -> Result { - Ok(index) + fn token_by_index(&self, index: U256) -> U256 { + index } /// Not implemented @@ -386,7 +392,7 @@ /// @title ERC-721 Non-Fungible Token Standard /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md -#[solidity_interface(name = ERC721, events(ERC721Events), expect_selector = 0x80ac58cd)] +#[solidity_interface(name = ERC721, events(ERC721Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x80ac58cd)] impl RefungibleHandle { /// @notice Count all RFTs assigned to an owner /// @dev RFTs assigned to the zero address are considered invalid, and this @@ -547,7 +553,7 @@ } /// @title ERC721 Token that can be irreversibly burned (destroyed). -#[solidity_interface(name = ERC721Burnable)] +#[solidity_interface(name = ERC721Burnable, enum(derive(PreDispatch)), enum_attr(weight))] impl RefungibleHandle { /// @notice Burns a specific ERC721 token. /// @dev Throws unless `msg.sender` is the current RFT owner, or an authorized @@ -567,7 +573,7 @@ } /// @title ERC721 minting logic. -#[solidity_interface(name = ERC721UniqueMintable)] +#[solidity_interface(name = ERC721UniqueMintable, enum(derive(PreDispatch)), enum_attr(weight))] impl RefungibleHandle { /// @notice Function to mint a token. /// @param to The new owner @@ -739,7 +745,7 @@ } /// @title Unique extensions for ERC721. -#[solidity_interface(name = ERC721UniqueExtensions)] +#[solidity_interface(name = ERC721UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))] impl RefungibleHandle where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>, @@ -1120,7 +1126,8 @@ ERC721Metadata(if(this.flags.erc721metadata)), Collection(via(common_mut returns CollectionHandle)), TokenProperties, - ) + ), + enum(derive(PreDispatch)), )] impl RefungibleHandle where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {} --- a/pallets/refungible/src/erc_token.rs +++ b/pallets/refungible/src/erc_token.rs @@ -24,40 +24,41 @@ convert::TryInto, ops::Deref, }; -use evm_coder::{ - abi::AbiType, ToLog, execution::*, generate_stubgen, solidity_interface, solidity, types::*, - weight, -}; +use evm_coder::{abi::AbiType, ToLog, generate_stubgen, solidity_interface, types::*}; use pallet_common::{ - CommonWeightInfo, erc::{CommonEvmHandler, PrecompileResult}, eth::{collection_id_to_address, CrossAddress}, }; use pallet_evm::{account::CrossAccountId, PrecompileHandle}; -use pallet_evm_coder_substrate::{call, dispatch_to_evm, WithRecorder}; +use pallet_evm_coder_substrate::{ + call, dispatch_to_evm, WithRecorder, frontier_contract, + execution::{Result, PreDispatch}, +}; use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _}; use sp_std::vec::Vec; use sp_core::U256; use up_data_structs::TokenId; -use crate::{ - Allowance, Balance, common::CommonWeights, Config, Pallet, RefungibleHandle, SelfWeightOf, - TotalSupply, weights::WeightInfo, -}; +use crate::{Allowance, Balance, Config, Pallet, RefungibleHandle, TotalSupply}; /// Refungible token handle contains information about token's collection and id /// /// RefungibleTokenHandle doesn't check token's existance upon creation pub struct RefungibleTokenHandle(pub RefungibleHandle, pub TokenId); -#[solidity_interface(name = ERC1633)] +frontier_contract! { + macro_rules! RefungibleTokenHandle_result {...} + impl Contract for RefungibleTokenHandle {...} +} + +#[solidity_interface(name = ERC1633, enum(derive(PreDispatch)), enum_attr(weight))] impl RefungibleTokenHandle { - fn parent_token(&self) -> Result
{ - Ok(collection_id_to_address(self.id)) + fn parent_token(&self) -> Address { + collection_id_to_address(self.id) } - fn parent_token_id(&self) -> Result { - Ok(self.1.into()) + fn parent_token_id(&self) -> U256 { + self.1.into() } } @@ -89,18 +90,18 @@ /// /// @dev Implementation of the basic standard token. /// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md -#[solidity_interface(name = ERC20, events(ERC20Events))] +#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight))] impl RefungibleTokenHandle { /// @return the name of the token. - fn name(&self) -> Result { - Ok(decode_utf16(self.name.iter().copied()) + fn name(&self) -> String { + decode_utf16(self.name.iter().copied()) .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)) - .collect::()) + .collect::() } /// @return the symbol of the token. - fn symbol(&self) -> Result { - Ok(String::from_utf8_lossy(&self.token_prefix).into()) + fn symbol(&self) -> String { + String::from_utf8_lossy(&self.token_prefix).into() } /// @dev Total number of tokens in existence @@ -198,7 +199,7 @@ } } -#[solidity_interface(name = ERC20UniqueExtensions)] +#[solidity_interface(name = ERC20UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))] impl RefungibleTokenHandle where T::AccountId: From<[u8; 32]>, @@ -361,7 +362,8 @@ #[solidity_interface( name = UniqueRefungibleToken, - is(ERC20, ERC20UniqueExtensions, ERC1633) + is(ERC20, ERC20UniqueExtensions, ERC1633), + enum(derive(PreDispatch)), )] impl RefungibleTokenHandle where T::AccountId: From<[u8; 32]> {} --- a/pallets/unique/src/eth/mod.rs +++ b/pallets/unique/src/eth/mod.rs @@ -18,9 +18,7 @@ use core::marker::PhantomData; use ethereum as _; -use evm_coder::{ - abi::AbiType, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight, -}; +use evm_coder::{abi::AbiType, generate_stubgen, solidity_interface, types::*}; use frame_support::traits::Get; use crate::Pallet; @@ -32,7 +30,11 @@ Pallet as PalletCommon, }; use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult}; -use pallet_evm_coder_substrate::{dispatch_to_evm, SubstrateRecorder, WithRecorder}; +use pallet_evm_coder_substrate::{ + dispatch_to_evm, SubstrateRecorder, WithRecorder, + execution::{PreDispatch, Result, Error}, + frontier_contract, +}; use up_data_structs::{ CollectionDescription, CollectionMode, CollectionName, CollectionTokenPrefix, CreateCollectionData, @@ -43,6 +45,10 @@ use alloc::format; use sp_std::vec::Vec; +frontier_contract! { + macro_rules! EvmCollectionHelpers_result {...} + impl Contract for EvmCollectionHelpers {...} +} /// See [`CollectionHelpersCall`] pub struct EvmCollectionHelpers(SubstrateRecorder); impl WithRecorder for EvmCollectionHelpers { @@ -135,7 +141,7 @@ } /// @title Contract, which allows users to operate with collections -#[solidity_interface(name = CollectionHelpers, events(CollectionHelpersEvents))] +#[solidity_interface(name = CollectionHelpers, events(CollectionHelpersEvents), enum(derive(PreDispatch)), enum_attr(weight))] impl EvmCollectionHelpers where T: Config + pallet_common::Config + pallet_nonfungible::Config + pallet_refungible::Config, @@ -183,9 +189,9 @@ /// @param description Informative description of the collection /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications /// @return address Address of the newly created collection - #[weight(>::create_collection())] #[deprecated(note = "mathod was renamed to `create_nft_collection`, prefer it instead")] #[solidity(hide)] + #[weight(>::create_collection())] fn create_nonfungible_collection( &mut self, caller: Caller, --- a/runtime/tests/Cargo.toml +++ b/runtime/tests/Cargo.toml @@ -40,5 +40,5 @@ parity-scale-codec = { version = "3.2.2", default-features = false, features = ["derive"] } scale-info = "*" -evm-coder = { default-features = false, path = "../../crates/evm-coder" } +evm-coder = { workspace = true } up-sponsorship = { default-features = false, git = "https://github.com/uniquenetwork/pallet-sponsoring", branch = "polkadot-v0.9.37" }