diff --git a/substreams/Cargo.lock b/substreams/Cargo.lock index 63e0b59..ba774ea 100644 --- a/substreams/Cargo.lock +++ b/substreams/Cargo.lock @@ -4,30 +4,30 @@ version = 4 [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "base64" @@ -48,9 +48,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.4.2" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitvec" @@ -87,9 +87,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cfg-if" @@ -99,9 +99,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -140,9 +140,9 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "either" -version = "1.10.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "equivalent" @@ -152,9 +152,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", "windows-sys", @@ -173,7 +173,7 @@ dependencies = [ "serde", "serde_json", "sha3", - "thiserror", + "thiserror 1.0.69", "uint", ] @@ -190,7 +190,7 @@ dependencies = [ "serde", "serde_json", "sha3", - "thiserror", + "thiserror 1.0.69", "uint", ] @@ -367,11 +367,32 @@ dependencies = [ "tycho-substreams 0.2.0 (git+https://github.com/propeller-heads/tycho-protocol-sdk.git?rev=b8aeaa3)", ] +[[package]] +name = "ethereum-uniswap-v4" +version = "0.1.1" +dependencies = [ + "anyhow", + "ethabi 18.0.0", + "getrandom", + "hex", + "hex-literal 0.4.1", + "itertools 0.13.0", + "num-bigint", + "prost 0.11.9", + "rstest", + "substreams", + "substreams-entity-change", + "substreams-ethereum", + "substreams-helper 0.0.2 (git+https://github.com/propeller-heads/tycho-protocol-sdk.git?rev=b8aeaa3)", + "tiny-keccak", + "tycho-substreams 0.2.0 (git+https://github.com/propeller-heads/tycho-protocol-sdk.git?rev=b8aeaa3)", +] + [[package]] name = "fastrand" -version = "2.0.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fixed-hash" @@ -409,6 +430,49 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -421,9 +485,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -431,10 +495,16 @@ dependencies = [ ] [[package]] -name = "hashbrown" -version = "0.14.3" +name = "glob" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" @@ -462,9 +532,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ "windows-sys", ] @@ -507,20 +577,20 @@ dependencies = [ [[package]] name = "impl-trait-for-tuples" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.90", ] [[package]] name = "indexmap" -version = "2.2.5" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown", @@ -555,9 +625,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "keccak" @@ -570,33 +640,33 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "multimap" @@ -606,11 +676,10 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -626,18 +695,18 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "pad" @@ -650,9 +719,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.9" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" dependencies = [ "arrayvec", "bitvec", @@ -664,9 +733,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.9" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -682,20 +751,20 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror", + "thiserror 2.0.7", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" dependencies = [ "pest", "pest_generator", @@ -703,22 +772,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.90", ] [[package]] name = "pest_meta" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" dependencies = [ "once_cell", "pest", @@ -727,19 +796,34 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", "indexmap", ] [[package]] -name = "ppv-lite86" -version = "0.2.17" +name = "pin-project-lite" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "prettyplease" @@ -779,19 +863,18 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.2" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_datetime", "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -861,7 +944,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.90", ] [[package]] @@ -884,9 +967,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -929,9 +1012,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -941,9 +1024,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -952,9 +1035,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "rlp" @@ -966,6 +1055,36 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rstest" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e905296805ab93e13c1ec3a03f4b6c4f35e9498a3d5fa96dc626d22c03cd89" +dependencies = [ + "futures-timer", + "futures-util", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef0053bbffce09062bee4bcc499b0fbe7a57b879f1efe088d6d8d4c7adcdef9b" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn 2.0.90", + "unicode-ident", +] + [[package]] name = "rustc-hex" version = "2.1.0" @@ -973,10 +1092,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] -name = "rustix" -version = "0.38.31" +name = "rustc_version" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags", "errno", @@ -987,37 +1115,44 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "semver" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.90", ] [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -1030,7 +1165,7 @@ checksum = "cd34f36fe4c5ba9654417139a9b3a20d2e1de6012ee678ad14d240c22c78d8d6" dependencies = [ "percent-encoding", "serde", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1054,6 +1189,15 @@ dependencies = [ "keccak", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -1080,7 +1224,7 @@ dependencies = [ "prost-build", "prost-types 0.11.9", "substreams-macro", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1097,9 +1241,9 @@ dependencies = [ [[package]] name = "substreams-ethereum" -version = "0.9.9" +version = "0.9.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f45dc04be50b7ca08d6d5c4560ee3eeba16ccaa1c124d0361bb30b5b84e28b" +checksum = "3d033738be190ad9c9a63712cf121e4f33ae9c57845c64021e421c45aac688b5" dependencies = [ "getrandom", "num-bigint", @@ -1111,9 +1255,9 @@ dependencies = [ [[package]] name = "substreams-ethereum-abigen" -version = "0.9.9" +version = "0.9.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c04307913a355aaf2a1bb7186d4bc7e36875f3d4aff77b47e83f1b63b24da55" +checksum = "27ef0adda971fbbd085545e73ccd7efbc12e31139cd301252f478e6b9743ff65" dependencies = [ "anyhow", "ethabi 17.2.0", @@ -1146,9 +1290,9 @@ dependencies = [ [[package]] name = "substreams-ethereum-core" -version = "0.9.9" +version = "0.9.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db9048cc9a66873ab7069ef958c2684994e6ee323da49c186b19156fdb4ca131" +checksum = "0a38e740018de8c2453f08621bab84ac55b0f1bfd42d56cf8b43ad340e670862" dependencies = [ "bigdecimal", "ethabi 17.2.0", @@ -1162,9 +1306,9 @@ dependencies = [ [[package]] name = "substreams-ethereum-derive" -version = "0.9.9" +version = "0.9.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e862928bee8653f5c9291ac619c8dc0da14ca61d8cd8d89b3acdbbde4d0bf304" +checksum = "55c934c70e6ab1fe11a89f401f36e7d9be3fe087687395767a4c13ab3d6bd055" dependencies = [ "ethabi 17.2.0", "heck", @@ -1213,7 +1357,7 @@ dependencies = [ "substreams", "substreams-entity-change", "substreams-ethereum", - "thiserror", + "thiserror 1.0.69", "tiny-keccak", ] @@ -1236,7 +1380,7 @@ dependencies = [ "substreams", "substreams-entity-change", "substreams-ethereum", - "thiserror", + "thiserror 1.0.69", "tiny-keccak", ] @@ -1249,7 +1393,7 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1265,9 +1409,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -1282,34 +1426,55 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", "windows-sys", ] [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767" +dependencies = [ + "thiserror-impl 2.0.7", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.90", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] @@ -1323,15 +1488,15 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.20.2" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "toml_datetime", @@ -1411,21 +1576,21 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -1447,22 +1612,23 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", + "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", @@ -1471,51 +1637,57 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.5.40" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] @@ -1528,3 +1700,24 @@ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] diff --git a/substreams/Cargo.toml b/substreams/Cargo.toml index 0b4d12c..cbe5d9d 100644 --- a/substreams/Cargo.toml +++ b/substreams/Cargo.toml @@ -10,6 +10,7 @@ members = [ "ethereum-sfrax", "ethereum-sfraxeth", "ethereum-uniswap-v3-logs-only", + "ethereum-uniswap-v4" ] resolver = "2" diff --git a/substreams/ethereum-uniswap-v4/Cargo.toml b/substreams/ethereum-uniswap-v4/Cargo.toml new file mode 100644 index 0000000..8dba988 --- /dev/null +++ b/substreams/ethereum-uniswap-v4/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "ethereum-uniswap-v4" +version = "0.1.1" +edition = "2021" + +[lib] +name = "ethereum_uniswap_v4" +crate-type = ["cdylib"] + +[dependencies] +substreams = "0.5.22" +substreams-ethereum = "0.9.9" +prost = "0.11" +ethabi = "18.0.0" +anyhow = "1.0.75" +hex-literal = "0.4.1" +substreams-helper = { git = "https://github.com/propeller-heads/tycho-protocol-sdk.git", rev = "b8aeaa3" } +tycho-substreams = { git = "https://github.com/propeller-heads/tycho-protocol-sdk.git", rev = "b8aeaa3" } +num-bigint = "0.4.4" +hex = "0.4.3" +tiny-keccak = "2.0" +substreams-entity-change = "1.3" +itertools = "0.13.0" + +[dev-dependencies] +rstest = "0.24.0" + +[target.wasm32-unknown-unknown.dependencies] +getrandom = { version = "0.2", features = ["custom"] } + +[build-dependencies] +anyhow = "1.0.75" +substreams-ethereum = "0.9.9" diff --git a/substreams/ethereum-uniswap-v4/abi/PoolManager.json b/substreams/ethereum-uniswap-v4/abi/PoolManager.json new file mode 100644 index 0000000..7b1dee8 --- /dev/null +++ b/substreams/ethereum-uniswap-v4/abi/PoolManager.json @@ -0,0 +1,1355 @@ +[ + { + "inputs": [], + "name": "AlreadyUnlocked", + "type": "error" + }, + { + "inputs": [], + "name": "ContractUnlocked", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "currency0", + "type": "address" + }, + { + "internalType": "address", + "name": "currency1", + "type": "address" + } + ], + "name": "CurrenciesOutOfOrderOrEqual", + "type": "error" + }, + { + "inputs": [], + "name": "CurrencyNotSettled", + "type": "error" + }, + { + "inputs": [], + "name": "DelegateCallNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCaller", + "type": "error" + }, + { + "inputs": [], + "name": "ManagerLocked", + "type": "error" + }, + { + "inputs": [], + "name": "MustClearExactPositiveDelta", + "type": "error" + }, + { + "inputs": [], + "name": "NonzeroNativeValue", + "type": "error" + }, + { + "inputs": [], + "name": "PoolNotInitialized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + } + ], + "name": "ProtocolFeeTooLarge", + "type": "error" + }, + { + "inputs": [], + "name": "SwapAmountCannotBeZero", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "name": "TickSpacingTooLarge", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "name": "TickSpacingTooSmall", + "type": "error" + }, + { + "inputs": [], + "name": "UnauthorizedDynamicLPFeeUpdate", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "PoolId", + "name": "id", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Donate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "PoolId", + "name": "id", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "indexed": true, + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "indexed": false, + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "Initialize", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "PoolId", + "name": "id", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "int256", + "name": "liquidityDelta", + "type": "int256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "name": "ModifyLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "OperatorSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "protocolFeeController", + "type": "address" + } + ], + "name": "ProtocolFeeControllerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "PoolId", + "name": "id", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "protocolFee", + "type": "uint24" + } + ], + "name": "ProtocolFeeUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "PoolId", + "name": "id", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "int128", + "name": "amount0", + "type": "int128" + }, + { + "indexed": false, + "internalType": "int128", + "name": "amount1", + "type": "int128" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + } + ], + "name": "Swap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Currency", + "name": "currency", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "clear", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "collectProtocolFees", + "outputs": [ + { + "internalType": "uint256", + "name": "amountCollected", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "name": "donate", + "outputs": [ + { + "internalType": "BalanceDelta", + "name": "delta", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "extsload", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "startSlot", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "nSlots", + "type": "uint256" + } + ], + "name": "extsload", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "slots", + "type": "bytes32[]" + } + ], + "name": "extsload", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "slots", + "type": "bytes32[]" + } + ], + "name": "exttload", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "exttload", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + } + ], + "name": "initialize", + "outputs": [ + { + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isOperator", + "outputs": [ + { + "internalType": "bool", + "name": "isOperator", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "int256", + "name": "liquidityDelta", + "type": "int256" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "internalType": "struct IPoolManager.ModifyLiquidityParams", + "name": "params", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "name": "modifyLiquidity", + "outputs": [ + { + "internalType": "BalanceDelta", + "name": "callerDelta", + "type": "int256" + }, + { + "internalType": "BalanceDelta", + "name": "feesAccrued", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolFeeController", + "outputs": [ + { + "internalType": "contract IProtocolFeeController", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Currency", + "name": "currency", + "type": "address" + } + ], + "name": "protocolFeesAccrued", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setOperator", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "internalType": "uint24", + "name": "newProtocolFee", + "type": "uint24" + } + ], + "name": "setProtocolFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IProtocolFeeController", + "name": "controller", + "type": "address" + } + ], + "name": "setProtocolFeeController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "settle", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "settleFor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "int256", + "name": "amountSpecified", + "type": "int256" + }, + { + "internalType": "uint160", + "name": "sqrtPriceLimitX96", + "type": "uint160" + } + ], + "internalType": "struct IPoolManager.SwapParams", + "name": "params", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "name": "swap", + "outputs": [ + { + "internalType": "BalanceDelta", + "name": "swapDelta", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Currency", + "name": "currency", + "type": "address" + } + ], + "name": "sync", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Currency", + "name": "currency", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "take", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "unlock", + "outputs": [ + { + "internalType": "bytes", + "name": "result", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "internalType": "uint24", + "name": "newDynamicLPFee", + "type": "uint24" + } + ], + "name": "updateDynamicLPFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/substreams/ethereum-uniswap-v4/buf.gen.yaml b/substreams/ethereum-uniswap-v4/buf.gen.yaml new file mode 100644 index 0000000..cde865e --- /dev/null +++ b/substreams/ethereum-uniswap-v4/buf.gen.yaml @@ -0,0 +1,12 @@ + +version: v1 +plugins: + - plugin: buf.build/community/neoeinstein-prost:v0.2.2 + out: src/pb + opt: + - file_descriptor_set=false + + - plugin: buf.build/community/neoeinstein-prost-crate:v0.3.1 + out: src/pb + opt: + - no_features diff --git a/substreams/ethereum-uniswap-v4/build.rs b/substreams/ethereum-uniswap-v4/build.rs new file mode 100644 index 0000000..dcbc0b8 --- /dev/null +++ b/substreams/ethereum-uniswap-v4/build.rs @@ -0,0 +1,9 @@ +use anyhow::{Ok, Result}; +use substreams_ethereum::Abigen; + +fn main() -> Result<(), anyhow::Error> { + Abigen::new("Factory", "abi/PoolManager.json")? + .generate()? + .write_to_file("src/abi/pool_manager.rs")?; + Ok(()) +} diff --git a/substreams/ethereum-uniswap-v4/ethereum-uniswap-v4.yaml b/substreams/ethereum-uniswap-v4/ethereum-uniswap-v4.yaml new file mode 100644 index 0000000..69633bf --- /dev/null +++ b/substreams/ethereum-uniswap-v4/ethereum-uniswap-v4.yaml @@ -0,0 +1,138 @@ +specVersion: v0.1.0 +package: + name: "ethereum_uniswap_v4" + version: v0.1.1 + +protobuf: + files: + - tycho/evm/v1/entity.proto + - tycho/evm/v1/common.proto + - tycho/evm/v1/utils.proto + - uniswap.proto + importPaths: + - ../../proto + - ./proto + excludePaths: + - sf/ethereum + - sf/substreams + - google + +binaries: + default: + type: wasm/rust-v1 + file: ../target/wasm32-unknown-unknown/release/ethereum_uniswap_v4.wasm + +modules: + - name: map_pools_created + kind: map + initialBlock: 21688329 + inputs: + - params: string + - source: sf.ethereum.type.v2.Block + output: + type: proto:tycho.evm.v1.BlockEntityChanges + + - name: store_pools + kind: store + initialBlock: 21688329 + updatePolicy: set_if_not_exists + valueType: proto:uniswap.v4.Pool + inputs: + - map: map_pools_created + + - name: map_events + kind: map + initialBlock: 21688329 + inputs: + - source: sf.ethereum.type.v2.Block + - store: store_pools + output: + type: proto:uniswap.v4.Events + + - name: store_pool_current_tick + kind: store + initialBlock: 21688329 + updatePolicy: set + valueType: int64 + inputs: + - map: map_events + + - name: store_pool_current_sqrt_price + kind: store + initialBlock: 21688329 + updatePolicy: set + valueType: bigint + inputs: + - map: map_events + + - name: map_balance_changes + kind: map + initialBlock: 21688329 + inputs: + - map: map_events + - store: store_pool_current_sqrt_price + output: + type: proto:tycho.evm.v1.BlockBalanceDeltas + + - name: store_pools_balances + kind: store + initialBlock: 21688329 + updatePolicy: add + valueType: bigint + inputs: + - map: map_balance_changes + + - name: map_ticks_changes + kind: map + initialBlock: 21688329 + inputs: + - map: map_events + output: + type: proto:uniswap.v4.TickDeltas + + - name: store_ticks_liquidity + kind: store + initialBlock: 21688329 + updatePolicy: add + valueType: bigint + inputs: + - map: map_ticks_changes + + - name: map_liquidity_changes + kind: map + initialBlock: 21688329 + inputs: + - map: map_events + - store: store_pool_current_tick + output: + type: proto:uniswap.v4.LiquidityChanges + + - name: store_liquidity + kind: store + initialBlock: 21688329 + updatePolicy: set_sum + valueType: bigint + inputs: + - map: map_liquidity_changes + + - name: map_protocol_changes + kind: map + initialBlock: 21688329 + inputs: + - source: sf.ethereum.type.v2.Block + - map: map_pools_created + - map: map_events + - map: map_balance_changes + - store: store_pools_balances + mode: deltas + - map: map_ticks_changes + - store: store_ticks_liquidity + mode: deltas + - map: map_liquidity_changes + - store: store_liquidity + mode: deltas + output: + type: proto:tycho.evm.v1.BlockChanges + +params: + map_pools_created: "000000000004444c5dc75cB358380D2e3dE08A90" diff --git a/substreams/ethereum-uniswap-v4/proto/uniswap.proto b/substreams/ethereum-uniswap-v4/proto/uniswap.proto new file mode 100644 index 0000000..5041bd3 --- /dev/null +++ b/substreams/ethereum-uniswap-v4/proto/uniswap.proto @@ -0,0 +1,124 @@ +syntax = "proto3"; + +package uniswap.v4; + +message Pool { + // // The pool address. + bytes id = 1; + // The token0 address. + bytes currency0 = 2; + // The token1 address. + bytes currency1 = 3; + // The transaction where the pool was created. + bytes created_tx_hash = 4; +} + +// A struct describing a transaction. +message Transaction { + // The transaction hash. + bytes hash = 1; + // The sender of the transaction. + bytes from = 2; + // The receiver of the transaction. + bytes to = 3; + // The transactions index within the block. + uint64 index = 4; +} + +// A change to a pool's tick. +message TickDelta { + // The address of the pool. + bytes pool_address = 1; + // The index of the tick. + int32 tick_index = 2; + // The liquidity net delta of this tick. Bigint encoded as signed little endian bytes. + bytes liquidity_net_delta = 3; + // Used to determine the order of the balance changes. Necessary for the balance store. + uint64 ordinal = 4; + Transaction transaction = 5; +} + +// A group of TickDelta +message TickDeltas { + repeated TickDelta deltas = 1; +} + +// A change to a pool's liquidity. +message LiquidityChange { + // The address of the pool. + bytes pool_address = 1; + // The liquidity changed amount. Bigint encoded as signed little endian bytes. + bytes value = 2; + // The type of update, can be absolute or delta. + LiquidityChangeType change_type = 3; + // Used to determine the order of the balance changes. Necessary for the balance store. + uint64 ordinal = 4; + Transaction transaction = 5; +} + +// A group of LiquidityChange +message LiquidityChanges { + repeated LiquidityChange changes = 1; +} + + +enum LiquidityChangeType { + DELTA = 0; + ABSOLUTE = 1; +} + +message Events { + repeated PoolEvent pool_events = 3; + + message PoolEvent { + oneof type { + Initialize initialize = 1; + ModifyLiquidity modify_liquidity = 2; + Swap swap = 3; + Donate donate = 4; + ProtocolFeeUpdated protocol_fee_updated = 5; + } + uint64 log_ordinal = 100; + string pool_id = 102; // Changed from pool_address to pool_id as V4 uses PoolId + string currency0 = 103; // Changed from token0 to currency0 + string currency1 = 104; // Changed from token1 to currency1 + Transaction transaction = 105; + + message Initialize { + string sqrt_price_x96 = 1; + int32 tick = 2; + uint32 fee = 3; + int32 tick_spacing = 4; + string hooks = 5; // Address of the hooks contract + } + + message ModifyLiquidity { + string sender = 1; + int32 tick_lower = 2; + int32 tick_upper = 3; + string liquidity_delta = 4; // Changed to support signed integers + string salt = 5; // Added salt field + } + + message Swap { + string sender = 1; + string amount0 = 2; // Signed int128 + string amount1 = 3; // Signed int128 + string sqrt_price_x96 = 4; + string liquidity = 5; + int32 tick = 6; + uint32 fee = 7; // Added fee field + } + + message Donate { + string sender = 1; + string amount0 = 2; + string amount1 = 3; + } + + message ProtocolFeeUpdated { + string pool_id = 1; + uint32 protocol_fee = 2; + } + } +} \ No newline at end of file diff --git a/substreams/ethereum-uniswap-v4/sepolia-uniswap-v4.yaml b/substreams/ethereum-uniswap-v4/sepolia-uniswap-v4.yaml new file mode 100644 index 0000000..29dd0c4 --- /dev/null +++ b/substreams/ethereum-uniswap-v4/sepolia-uniswap-v4.yaml @@ -0,0 +1,138 @@ +specVersion: v0.1.0 +package: + name: "ethereum_uniswap_v4" + version: v0.1.1 + +protobuf: + files: + - tycho/evm/v1/entity.proto + - tycho/evm/v1/common.proto + - tycho/evm/v1/utils.proto + - uniswap.proto + importPaths: + - ../../proto + - ./proto + excludePaths: + - sf/ethereum + - sf/substreams + - google + +binaries: + default: + type: wasm/rust-v1 + file: ../target/wasm32-unknown-unknown/release/ethereum_uniswap_v4.wasm + +modules: + - name: map_pools_created + kind: map + initialBlock: 6894393 + inputs: + - params: string + - source: sf.ethereum.type.v2.Block + output: + type: proto:tycho.evm.v1.BlockEntityChanges + + - name: store_pools + kind: store + initialBlock: 6894393 + updatePolicy: set_if_not_exists + valueType: proto:uniswap.v4.Pool + inputs: + - map: map_pools_created + + - name: map_events + kind: map + initialBlock: 6894393 + inputs: + - source: sf.ethereum.type.v2.Block + - store: store_pools + output: + type: proto:uniswap.v4.Events + + - name: store_pool_current_tick + kind: store + initialBlock: 6894393 + updatePolicy: set + valueType: int64 + inputs: + - map: map_events + + - name: store_pool_current_sqrt_price + kind: store + initialBlock: 6894393 + updatePolicy: set + valueType: bigint + inputs: + - map: map_events + + - name: map_balance_changes + kind: map + initialBlock: 6894393 + inputs: + - map: map_events + - store: store_pool_current_sqrt_price + output: + type: proto:tycho.evm.v1.BlockBalanceDeltas + + - name: store_pools_balances + kind: store + initialBlock: 6894393 + updatePolicy: add + valueType: bigint + inputs: + - map: map_balance_changes + + - name: map_ticks_changes + kind: map + initialBlock: 6894393 + inputs: + - map: map_events + output: + type: proto:uniswap.v4.TickDeltas + + - name: store_ticks_liquidity + kind: store + initialBlock: 6894393 + updatePolicy: add + valueType: bigint + inputs: + - map: map_ticks_changes + + - name: map_liquidity_changes + kind: map + initialBlock: 6894393 + inputs: + - map: map_events + - store: store_pool_current_tick + output: + type: proto:uniswap.v4.LiquidityChanges + + - name: store_liquidity + kind: store + initialBlock: 6894393 + updatePolicy: set_sum + valueType: bigint + inputs: + - map: map_liquidity_changes + + - name: map_protocol_changes + kind: map + initialBlock: 6894393 + inputs: + - source: sf.ethereum.type.v2.Block + - map: map_pools_created + - map: map_events + - map: map_balance_changes + - store: store_pools_balances + mode: deltas + - map: map_ticks_changes + - store: store_ticks_liquidity + mode: deltas + - map: map_liquidity_changes + - store: store_liquidity + mode: deltas + output: + type: proto:tycho.evm.v1.BlockChanges + +params: + map_pools_created: "8c4bcbe6b9ef47855f97e675296fa3f6fafa5f1a" diff --git a/substreams/ethereum-uniswap-v4/src/abi/mod.rs b/substreams/ethereum-uniswap-v4/src/abi/mod.rs new file mode 100644 index 0000000..578e458 --- /dev/null +++ b/substreams/ethereum-uniswap-v4/src/abi/mod.rs @@ -0,0 +1,3 @@ +#![allow(clippy::all, clippy::pedantic, clippy::nursery)] + +pub mod pool_manager; \ No newline at end of file diff --git a/substreams/ethereum-uniswap-v4/src/abi/pool_manager.rs b/substreams/ethereum-uniswap-v4/src/abi/pool_manager.rs new file mode 100644 index 0000000..2c9f8e0 --- /dev/null +++ b/substreams/ethereum-uniswap-v4/src/abi/pool_manager.rs @@ -0,0 +1,5656 @@ +const INTERNAL_ERR: &'static str = "`ethabi_derive` internal error"; +/// Contract's functions. +#[allow(dead_code, unused_imports, unused_variables)] +pub mod functions { + use super::INTERNAL_ERR; + #[derive(Debug, Clone, PartialEq)] + pub struct Allowance { + pub owner: Vec, + pub spender: Vec, + pub id: substreams::scalar::BigInt, + } + impl Allowance { + const METHOD_ID: [u8; 4] = [89u8, 138u8, 249u8, 231u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Address, + ethabi::ParamType::Uint(256usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + owner: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + spender: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + id: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address(ethabi::Address::from_slice(&self.owner)), + ethabi::Token::Address(ethabi::Address::from_slice(&self.spender)), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.id.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Allowance { + const NAME: &'static str = "allowance"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for Allowance { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Approve { + pub spender: Vec, + pub id: substreams::scalar::BigInt, + pub amount: substreams::scalar::BigInt, + } + impl Approve { + const METHOD_ID: [u8; 4] = [66u8, 106u8, 132u8, 147u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + spender: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + id: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address(ethabi::Address::from_slice(&self.spender)), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.id.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode(&[ethabi::ParamType::Bool], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_bool() + .expect(INTERNAL_ERR), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Approve { + const NAME: &'static str = "approve"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for Approve { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct BalanceOf { + pub owner: Vec, + pub id: substreams::scalar::BigInt, + } + impl BalanceOf { + const METHOD_ID: [u8; 4] = [0u8, 253u8, 213u8, 142u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address, ethabi::ParamType::Uint(256usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + owner: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + id: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address(ethabi::Address::from_slice(&self.owner)), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.id.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for BalanceOf { + const NAME: &'static str = "balanceOf"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for BalanceOf { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Burn { + pub from: Vec, + pub id: substreams::scalar::BigInt, + pub amount: substreams::scalar::BigInt, + } + impl Burn { + const METHOD_ID: [u8; 4] = [245u8, 41u8, 138u8, 202u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + from: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + id: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address(ethabi::Address::from_slice(&self.from)), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.id.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for Burn { + const NAME: &'static str = "burn"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Clear { + pub currency: Vec, + pub amount: substreams::scalar::BigInt, + } + impl Clear { + const METHOD_ID: [u8; 4] = [128u8, 240u8, 180u8, 76u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address, ethabi::ParamType::Uint(256usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + currency: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address(ethabi::Address::from_slice(&self.currency)), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for Clear { + const NAME: &'static str = "clear"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct CollectProtocolFees { + pub recipient: Vec, + pub currency: Vec, + pub amount: substreams::scalar::BigInt, + } + impl CollectProtocolFees { + const METHOD_ID: [u8; 4] = [129u8, 97u8, 184u8, 116u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Address, + ethabi::ParamType::Uint(256usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + recipient: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + currency: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address(ethabi::Address::from_slice(&self.recipient)), + ethabi::Token::Address(ethabi::Address::from_slice(&self.currency)), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for CollectProtocolFees { + const NAME: &'static str = "collectProtocolFees"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for CollectProtocolFees { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Donate { + pub key: ( + Vec, + Vec, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + Vec, + ), + pub amount0: substreams::scalar::BigInt, + pub amount1: substreams::scalar::BigInt, + pub hook_data: Vec, + } + impl Donate { + const METHOD_ID: [u8; 4] = [35u8, 66u8, 102u8, 215u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Tuple( + vec![ + ethabi::ParamType::Address, ethabi::ParamType::Address, + ethabi::ParamType::Uint(24usize), + ethabi::ParamType::Int(24usize), ethabi::ParamType::Address + ], + ), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Bytes, + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + key: { + let tuple_elements = values + .pop() + .expect(INTERNAL_ERR) + .into_tuple() + .expect(INTERNAL_ERR); + ( + tuple_elements[0usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + tuple_elements[1usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + { + let mut v = [0 as u8; 32]; + tuple_elements[2usize] + .clone() + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + tuple_elements[3usize] + .clone() + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + tuple_elements[4usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + }, + amount0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + hook_data: values + .pop() + .expect(INTERNAL_ERR) + .into_bytes() + .expect(INTERNAL_ERR), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Tuple( + vec![ + ethabi::Token::Address(ethabi::Address::from_slice(& self.key + .0)), ethabi::Token::Address(ethabi::Address::from_slice(& + self.key.1)), + ethabi::Token::Uint(ethabi::Uint::from_big_endian(match self + .key.2.clone().to_bytes_be() { (num_bigint::Sign::Plus, + bytes) => bytes, (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") }, } + .as_slice(),),), { let non_full_signed_bytes = self.key.3 + .to_signed_bytes_be(); let full_signed_bytes_init = if + non_full_signed_bytes[0] & 0x80 == 0x80 { 0xff } else { 0x00 + }; let mut full_signed_bytes = [full_signed_bytes_init as u8; + 32]; non_full_signed_bytes.into_iter().rev().enumerate() + .for_each(| (i, byte) | full_signed_bytes[31 - i] = byte); + ethabi::Token::Int(ethabi::Int::from_big_endian(full_signed_bytes + .as_ref())) }, + ethabi::Token::Address(ethabi::Address::from_slice(& self.key + .4)) + ], + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount0.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount1.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Bytes(self.hook_data.clone()), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Int(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Donate { + const NAME: &'static str = "donate"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for Donate { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Extsload1 { + pub slot: [u8; 32usize], + } + impl Extsload1 { + const METHOD_ID: [u8; 4] = [30u8, 46u8, 174u8, 175u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::FixedBytes(32usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + slot: { + let mut result = [0u8; 32]; + let v = values + .pop() + .expect(INTERNAL_ERR) + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::FixedBytes(self.slot.as_ref().to_vec())], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result<[u8; 32usize], String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result<[u8; 32usize], String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::FixedBytes(32usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut result = [0u8; 32]; + let v = values + .pop() + .expect("one output data should have existed") + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option<[u8; 32usize]> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Extsload1 { + const NAME: &'static str = "extsload"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable<[u8; 32usize]> for Extsload1 { + fn output(data: &[u8]) -> Result<[u8; 32usize], String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Extsload2 { + pub start_slot: [u8; 32usize], + pub n_slots: substreams::scalar::BigInt, + } + impl Extsload2 { + const METHOD_ID: [u8; 4] = [53u8, 253u8, 99u8, 26u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::FixedBytes(32usize), + ethabi::ParamType::Uint(256usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + start_slot: { + let mut result = [0u8; 32]; + let v = values + .pop() + .expect(INTERNAL_ERR) + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }, + n_slots: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::FixedBytes(self.start_slot.as_ref().to_vec()), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.n_slots.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Array( + Box::new(ethabi::ParamType::FixedBytes(32usize)), + ), + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_array() + .expect(INTERNAL_ERR) + .into_iter() + .map(|inner| { + let mut result = [0u8; 32]; + let v = inner.into_fixed_bytes().expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }) + .collect(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Extsload2 { + const NAME: &'static str = "extsload"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Extsload2 { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Extsload3 { + pub slots: Vec<[u8; 32usize]>, + } + impl Extsload3 { + const METHOD_ID: [u8; 4] = [219u8, 208u8, 53u8, 255u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Array( + Box::new(ethabi::ParamType::FixedBytes(32usize)), + ), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + slots: values + .pop() + .expect(INTERNAL_ERR) + .into_array() + .expect(INTERNAL_ERR) + .into_iter() + .map(|inner| { + let mut result = [0u8; 32]; + let v = inner.into_fixed_bytes().expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }) + .collect(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + { + let v = self + .slots + .iter() + .map(|inner| ethabi::Token::FixedBytes( + inner.as_ref().to_vec(), + )) + .collect(); + ethabi::Token::Array(v) + }, + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Array( + Box::new(ethabi::ParamType::FixedBytes(32usize)), + ), + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_array() + .expect(INTERNAL_ERR) + .into_iter() + .map(|inner| { + let mut result = [0u8; 32]; + let v = inner.into_fixed_bytes().expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }) + .collect(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Extsload3 { + const NAME: &'static str = "extsload"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Extsload3 { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Exttload1 { + pub slots: Vec<[u8; 32usize]>, + } + impl Exttload1 { + const METHOD_ID: [u8; 4] = [155u8, 246u8, 100u8, 95u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Array( + Box::new(ethabi::ParamType::FixedBytes(32usize)), + ), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + slots: values + .pop() + .expect(INTERNAL_ERR) + .into_array() + .expect(INTERNAL_ERR) + .into_iter() + .map(|inner| { + let mut result = [0u8; 32]; + let v = inner.into_fixed_bytes().expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }) + .collect(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + { + let v = self + .slots + .iter() + .map(|inner| ethabi::Token::FixedBytes( + inner.as_ref().to_vec(), + )) + .collect(); + ethabi::Token::Array(v) + }, + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Array( + Box::new(ethabi::ParamType::FixedBytes(32usize)), + ), + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_array() + .expect(INTERNAL_ERR) + .into_iter() + .map(|inner| { + let mut result = [0u8; 32]; + let v = inner.into_fixed_bytes().expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }) + .collect(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Exttload1 { + const NAME: &'static str = "exttload"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Exttload1 { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Exttload2 { + pub slot: [u8; 32usize], + } + impl Exttload2 { + const METHOD_ID: [u8; 4] = [241u8, 53u8, 186u8, 170u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::FixedBytes(32usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + slot: { + let mut result = [0u8; 32]; + let v = values + .pop() + .expect(INTERNAL_ERR) + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::FixedBytes(self.slot.as_ref().to_vec())], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result<[u8; 32usize], String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result<[u8; 32usize], String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::FixedBytes(32usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut result = [0u8; 32]; + let v = values + .pop() + .expect("one output data should have existed") + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option<[u8; 32usize]> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Exttload2 { + const NAME: &'static str = "exttload"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable<[u8; 32usize]> for Exttload2 { + fn output(data: &[u8]) -> Result<[u8; 32usize], String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Initialize { + pub key: ( + Vec, + Vec, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + Vec, + ), + pub sqrt_price_x96: substreams::scalar::BigInt, + } + impl Initialize { + const METHOD_ID: [u8; 4] = [98u8, 118u8, 203u8, 190u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Tuple( + vec![ + ethabi::ParamType::Address, ethabi::ParamType::Address, + ethabi::ParamType::Uint(24usize), + ethabi::ParamType::Int(24usize), ethabi::ParamType::Address + ], + ), + ethabi::ParamType::Uint(160usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + key: { + let tuple_elements = values + .pop() + .expect(INTERNAL_ERR) + .into_tuple() + .expect(INTERNAL_ERR); + ( + tuple_elements[0usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + tuple_elements[1usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + { + let mut v = [0 as u8; 32]; + tuple_elements[2usize] + .clone() + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + tuple_elements[3usize] + .clone() + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + tuple_elements[4usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + }, + sqrt_price_x96: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Tuple( + vec![ + ethabi::Token::Address(ethabi::Address::from_slice(& self.key + .0)), ethabi::Token::Address(ethabi::Address::from_slice(& + self.key.1)), + ethabi::Token::Uint(ethabi::Uint::from_big_endian(match self + .key.2.clone().to_bytes_be() { (num_bigint::Sign::Plus, + bytes) => bytes, (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") }, } + .as_slice(),),), { let non_full_signed_bytes = self.key.3 + .to_signed_bytes_be(); let full_signed_bytes_init = if + non_full_signed_bytes[0] & 0x80 == 0x80 { 0xff } else { 0x00 + }; let mut full_signed_bytes = [full_signed_bytes_init as u8; + 32]; non_full_signed_bytes.into_iter().rev().enumerate() + .for_each(| (i, byte) | full_signed_bytes[31 - i] = byte); + ethabi::Token::Int(ethabi::Int::from_big_endian(full_signed_bytes + .as_ref())) }, + ethabi::Token::Address(ethabi::Address::from_slice(& self.key + .4)) + ], + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.sqrt_price_x96.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Int(24usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Initialize { + const NAME: &'static str = "initialize"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for Initialize { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct IsOperator { + pub owner: Vec, + pub operator: Vec, + } + impl IsOperator { + const METHOD_ID: [u8; 4] = [182u8, 54u8, 60u8, 242u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address, ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + owner: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + operator: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address(ethabi::Address::from_slice(&self.owner)), + ethabi::Token::Address(ethabi::Address::from_slice(&self.operator)), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode(&[ethabi::ParamType::Bool], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_bool() + .expect(INTERNAL_ERR), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for IsOperator { + const NAME: &'static str = "isOperator"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for IsOperator { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Mint { + pub to: Vec, + pub id: substreams::scalar::BigInt, + pub amount: substreams::scalar::BigInt, + } + impl Mint { + const METHOD_ID: [u8; 4] = [21u8, 110u8, 41u8, 246u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + to: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + id: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address(ethabi::Address::from_slice(&self.to)), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.id.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for Mint { + const NAME: &'static str = "mint"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct ModifyLiquidity { + pub key: ( + Vec, + Vec, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + Vec, + ), + pub params: ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + [u8; 32usize], + ), + pub hook_data: Vec, + } + impl ModifyLiquidity { + const METHOD_ID: [u8; 4] = [90u8, 107u8, 207u8, 218u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Tuple( + vec![ + ethabi::ParamType::Address, ethabi::ParamType::Address, + ethabi::ParamType::Uint(24usize), + ethabi::ParamType::Int(24usize), ethabi::ParamType::Address + ], + ), + ethabi::ParamType::Tuple( + vec![ + ethabi::ParamType::Int(24usize), + ethabi::ParamType::Int(24usize), + ethabi::ParamType::Int(256usize), + ethabi::ParamType::FixedBytes(32usize) + ], + ), + ethabi::ParamType::Bytes, + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + key: { + let tuple_elements = values + .pop() + .expect(INTERNAL_ERR) + .into_tuple() + .expect(INTERNAL_ERR); + ( + tuple_elements[0usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + tuple_elements[1usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + { + let mut v = [0 as u8; 32]; + tuple_elements[2usize] + .clone() + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + tuple_elements[3usize] + .clone() + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + tuple_elements[4usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + }, + params: { + let tuple_elements = values + .pop() + .expect(INTERNAL_ERR) + .into_tuple() + .expect(INTERNAL_ERR); + ( + { + let mut v = [0 as u8; 32]; + tuple_elements[0usize] + .clone() + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + tuple_elements[1usize] + .clone() + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + tuple_elements[2usize] + .clone() + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + { + let mut result = [0u8; 32]; + let v = tuple_elements[3usize] + .clone() + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }, + ) + }, + hook_data: values + .pop() + .expect(INTERNAL_ERR) + .into_bytes() + .expect(INTERNAL_ERR), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Tuple( + vec![ + ethabi::Token::Address(ethabi::Address::from_slice(& self.key + .0)), ethabi::Token::Address(ethabi::Address::from_slice(& + self.key.1)), + ethabi::Token::Uint(ethabi::Uint::from_big_endian(match self + .key.2.clone().to_bytes_be() { (num_bigint::Sign::Plus, + bytes) => bytes, (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") }, } + .as_slice(),),), { let non_full_signed_bytes = self.key.3 + .to_signed_bytes_be(); let full_signed_bytes_init = if + non_full_signed_bytes[0] & 0x80 == 0x80 { 0xff } else { 0x00 + }; let mut full_signed_bytes = [full_signed_bytes_init as u8; + 32]; non_full_signed_bytes.into_iter().rev().enumerate() + .for_each(| (i, byte) | full_signed_bytes[31 - i] = byte); + ethabi::Token::Int(ethabi::Int::from_big_endian(full_signed_bytes + .as_ref())) }, + ethabi::Token::Address(ethabi::Address::from_slice(& self.key + .4)) + ], + ), + ethabi::Token::Tuple( + vec![ + { let non_full_signed_bytes = self.params.0 + .to_signed_bytes_be(); let full_signed_bytes_init = if + non_full_signed_bytes[0] & 0x80 == 0x80 { 0xff } else { 0x00 + }; let mut full_signed_bytes = [full_signed_bytes_init as u8; + 32]; non_full_signed_bytes.into_iter().rev().enumerate() + .for_each(| (i, byte) | full_signed_bytes[31 - i] = byte); + ethabi::Token::Int(ethabi::Int::from_big_endian(full_signed_bytes + .as_ref())) }, { let non_full_signed_bytes = self.params.1 + .to_signed_bytes_be(); let full_signed_bytes_init = if + non_full_signed_bytes[0] & 0x80 == 0x80 { 0xff } else { 0x00 + }; let mut full_signed_bytes = [full_signed_bytes_init as u8; + 32]; non_full_signed_bytes.into_iter().rev().enumerate() + .for_each(| (i, byte) | full_signed_bytes[31 - i] = byte); + ethabi::Token::Int(ethabi::Int::from_big_endian(full_signed_bytes + .as_ref())) }, { let non_full_signed_bytes = self.params.2 + .to_signed_bytes_be(); let full_signed_bytes_init = if + non_full_signed_bytes[0] & 0x80 == 0x80 { 0xff } else { 0x00 + }; let mut full_signed_bytes = [full_signed_bytes_init as u8; + 32]; non_full_signed_bytes.into_iter().rev().enumerate() + .for_each(| (i, byte) | full_signed_bytes[31 - i] = byte); + ethabi::Token::Int(ethabi::Int::from_big_endian(full_signed_bytes + .as_ref())) }, ethabi::Token::FixedBytes(self.params.3 + .as_ref().to_vec()) + ], + ), + ethabi::Token::Bytes(self.hook_data.clone()), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result<(substreams::scalar::BigInt, substreams::scalar::BigInt), String> { + Self::output(call.return_data.as_ref()) + } + pub fn output( + data: &[u8], + ) -> Result<(substreams::scalar::BigInt, substreams::scalar::BigInt), String> { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Int(256usize), + ethabi::ParamType::Int(256usize), + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + values.reverse(); + Ok(( + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + )) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call( + &self, + address: Vec, + ) -> Option<(substreams::scalar::BigInt, substreams::scalar::BigInt)> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for ModifyLiquidity { + const NAME: &'static str = "modifyLiquidity"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + > for ModifyLiquidity { + fn output( + data: &[u8], + ) -> Result<(substreams::scalar::BigInt, substreams::scalar::BigInt), String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Owner {} + impl Owner { + const METHOD_ID: [u8; 4] = [141u8, 165u8, 203u8, 91u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode(&[ethabi::ParamType::Address], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Owner { + const NAME: &'static str = "owner"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Owner { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct ProtocolFeeController {} + impl ProtocolFeeController { + const METHOD_ID: [u8; 4] = [240u8, 45u8, 227u8, 178u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode(&[ethabi::ParamType::Address], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for ProtocolFeeController { + const NAME: &'static str = "protocolFeeController"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for ProtocolFeeController { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct ProtocolFeesAccrued { + pub currency: Vec, + } + impl ProtocolFeesAccrued { + const METHOD_ID: [u8; 4] = [151u8, 232u8, 205u8, 78u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + currency: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::Address(ethabi::Address::from_slice(&self.currency))], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for ProtocolFeesAccrued { + const NAME: &'static str = "protocolFeesAccrued"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for ProtocolFeesAccrued { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct SetOperator { + pub operator: Vec, + pub approved: bool, + } + impl SetOperator { + const METHOD_ID: [u8; 4] = [85u8, 138u8, 114u8, 151u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address, ethabi::ParamType::Bool], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + operator: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + approved: values + .pop() + .expect(INTERNAL_ERR) + .into_bool() + .expect(INTERNAL_ERR), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address(ethabi::Address::from_slice(&self.operator)), + ethabi::Token::Bool(self.approved.clone()), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode(&[ethabi::ParamType::Bool], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_bool() + .expect(INTERNAL_ERR), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for SetOperator { + const NAME: &'static str = "setOperator"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for SetOperator { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct SetProtocolFee { + pub key: ( + Vec, + Vec, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + Vec, + ), + pub new_protocol_fee: substreams::scalar::BigInt, + } + impl SetProtocolFee { + const METHOD_ID: [u8; 4] = [126u8, 135u8, 206u8, 125u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Tuple( + vec![ + ethabi::ParamType::Address, ethabi::ParamType::Address, + ethabi::ParamType::Uint(24usize), + ethabi::ParamType::Int(24usize), ethabi::ParamType::Address + ], + ), + ethabi::ParamType::Uint(24usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + key: { + let tuple_elements = values + .pop() + .expect(INTERNAL_ERR) + .into_tuple() + .expect(INTERNAL_ERR); + ( + tuple_elements[0usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + tuple_elements[1usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + { + let mut v = [0 as u8; 32]; + tuple_elements[2usize] + .clone() + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + tuple_elements[3usize] + .clone() + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + tuple_elements[4usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + }, + new_protocol_fee: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Tuple( + vec![ + ethabi::Token::Address(ethabi::Address::from_slice(& self.key + .0)), ethabi::Token::Address(ethabi::Address::from_slice(& + self.key.1)), + ethabi::Token::Uint(ethabi::Uint::from_big_endian(match self + .key.2.clone().to_bytes_be() { (num_bigint::Sign::Plus, + bytes) => bytes, (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") }, } + .as_slice(),),), { let non_full_signed_bytes = self.key.3 + .to_signed_bytes_be(); let full_signed_bytes_init = if + non_full_signed_bytes[0] & 0x80 == 0x80 { 0xff } else { 0x00 + }; let mut full_signed_bytes = [full_signed_bytes_init as u8; + 32]; non_full_signed_bytes.into_iter().rev().enumerate() + .for_each(| (i, byte) | full_signed_bytes[31 - i] = byte); + ethabi::Token::Int(ethabi::Int::from_big_endian(full_signed_bytes + .as_ref())) }, + ethabi::Token::Address(ethabi::Address::from_slice(& self.key + .4)) + ], + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.new_protocol_fee.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for SetProtocolFee { + const NAME: &'static str = "setProtocolFee"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct SetProtocolFeeController { + pub controller: Vec, + } + impl SetProtocolFeeController { + const METHOD_ID: [u8; 4] = [45u8, 119u8, 19u8, 137u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + controller: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::Address(ethabi::Address::from_slice(&self.controller))], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for SetProtocolFeeController { + const NAME: &'static str = "setProtocolFeeController"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Settle {} + impl Settle { + const METHOD_ID: [u8; 4] = [17u8, 218u8, 96u8, 180u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Settle { + const NAME: &'static str = "settle"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for Settle { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct SettleFor { + pub recipient: Vec, + } + impl SettleFor { + const METHOD_ID: [u8; 4] = [61u8, 212u8, 90u8, 219u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + recipient: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::Address(ethabi::Address::from_slice(&self.recipient))], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for SettleFor { + const NAME: &'static str = "settleFor"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for SettleFor { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct SupportsInterface { + pub interface_id: [u8; 4usize], + } + impl SupportsInterface { + const METHOD_ID: [u8; 4] = [1u8, 255u8, 201u8, 167u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::FixedBytes(4usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + interface_id: { + let mut result = [0u8; 4]; + let v = values + .pop() + .expect(INTERNAL_ERR) + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::FixedBytes(self.interface_id.as_ref().to_vec())], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode(&[ethabi::ParamType::Bool], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_bool() + .expect(INTERNAL_ERR), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for SupportsInterface { + const NAME: &'static str = "supportsInterface"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for SupportsInterface { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Swap { + pub key: ( + Vec, + Vec, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + Vec, + ), + pub params: (bool, substreams::scalar::BigInt, substreams::scalar::BigInt), + pub hook_data: Vec, + } + impl Swap { + const METHOD_ID: [u8; 4] = [243u8, 205u8, 145u8, 76u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Tuple( + vec![ + ethabi::ParamType::Address, ethabi::ParamType::Address, + ethabi::ParamType::Uint(24usize), + ethabi::ParamType::Int(24usize), ethabi::ParamType::Address + ], + ), + ethabi::ParamType::Tuple( + vec![ + ethabi::ParamType::Bool, ethabi::ParamType::Int(256usize), + ethabi::ParamType::Uint(160usize) + ], + ), + ethabi::ParamType::Bytes, + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + key: { + let tuple_elements = values + .pop() + .expect(INTERNAL_ERR) + .into_tuple() + .expect(INTERNAL_ERR); + ( + tuple_elements[0usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + tuple_elements[1usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + { + let mut v = [0 as u8; 32]; + tuple_elements[2usize] + .clone() + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + tuple_elements[3usize] + .clone() + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + tuple_elements[4usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + }, + params: { + let tuple_elements = values + .pop() + .expect(INTERNAL_ERR) + .into_tuple() + .expect(INTERNAL_ERR); + ( + tuple_elements[0usize].clone().into_bool().expect(INTERNAL_ERR), + { + let mut v = [0 as u8; 32]; + tuple_elements[1usize] + .clone() + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + tuple_elements[2usize] + .clone() + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + ) + }, + hook_data: values + .pop() + .expect(INTERNAL_ERR) + .into_bytes() + .expect(INTERNAL_ERR), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Tuple( + vec![ + ethabi::Token::Address(ethabi::Address::from_slice(& self.key + .0)), ethabi::Token::Address(ethabi::Address::from_slice(& + self.key.1)), + ethabi::Token::Uint(ethabi::Uint::from_big_endian(match self + .key.2.clone().to_bytes_be() { (num_bigint::Sign::Plus, + bytes) => bytes, (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") }, } + .as_slice(),),), { let non_full_signed_bytes = self.key.3 + .to_signed_bytes_be(); let full_signed_bytes_init = if + non_full_signed_bytes[0] & 0x80 == 0x80 { 0xff } else { 0x00 + }; let mut full_signed_bytes = [full_signed_bytes_init as u8; + 32]; non_full_signed_bytes.into_iter().rev().enumerate() + .for_each(| (i, byte) | full_signed_bytes[31 - i] = byte); + ethabi::Token::Int(ethabi::Int::from_big_endian(full_signed_bytes + .as_ref())) }, + ethabi::Token::Address(ethabi::Address::from_slice(& self.key + .4)) + ], + ), + ethabi::Token::Tuple( + vec![ + ethabi::Token::Bool(self.params.0.clone()), { let + non_full_signed_bytes = self.params.1.to_signed_bytes_be(); + let full_signed_bytes_init = if non_full_signed_bytes[0] & + 0x80 == 0x80 { 0xff } else { 0x00 }; let mut + full_signed_bytes = [full_signed_bytes_init as u8; 32]; + non_full_signed_bytes.into_iter().rev().enumerate() + .for_each(| (i, byte) | full_signed_bytes[31 - i] = byte); + ethabi::Token::Int(ethabi::Int::from_big_endian(full_signed_bytes + .as_ref())) }, + ethabi::Token::Uint(ethabi::Uint::from_big_endian(match self + .params.2.clone().to_bytes_be() { (num_bigint::Sign::Plus, + bytes) => bytes, (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") }, } + .as_slice(),),) + ], + ), + ethabi::Token::Bytes(self.hook_data.clone()), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Int(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Swap { + const NAME: &'static str = "swap"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for Swap { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Sync { + pub currency: Vec, + } + impl Sync { + const METHOD_ID: [u8; 4] = [165u8, 132u8, 17u8, 148u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + currency: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::Address(ethabi::Address::from_slice(&self.currency))], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for Sync { + const NAME: &'static str = "sync"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Take { + pub currency: Vec, + pub to: Vec, + pub amount: substreams::scalar::BigInt, + } + impl Take { + const METHOD_ID: [u8; 4] = [11u8, 13u8, 156u8, 9u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Address, + ethabi::ParamType::Uint(256usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + currency: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + to: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address(ethabi::Address::from_slice(&self.currency)), + ethabi::Token::Address(ethabi::Address::from_slice(&self.to)), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for Take { + const NAME: &'static str = "take"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Transfer { + pub receiver: Vec, + pub id: substreams::scalar::BigInt, + pub amount: substreams::scalar::BigInt, + } + impl Transfer { + const METHOD_ID: [u8; 4] = [9u8, 91u8, 205u8, 182u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + receiver: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + id: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address(ethabi::Address::from_slice(&self.receiver)), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.id.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode(&[ethabi::ParamType::Bool], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_bool() + .expect(INTERNAL_ERR), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Transfer { + const NAME: &'static str = "transfer"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for Transfer { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct TransferFrom { + pub sender: Vec, + pub receiver: Vec, + pub id: substreams::scalar::BigInt, + pub amount: substreams::scalar::BigInt, + } + impl TransferFrom { + const METHOD_ID: [u8; 4] = [254u8, 153u8, 4u8, 154u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Address, + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + sender: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + receiver: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + id: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address(ethabi::Address::from_slice(&self.sender)), + ethabi::Token::Address(ethabi::Address::from_slice(&self.receiver)), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.id.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode(&[ethabi::ParamType::Bool], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_bool() + .expect(INTERNAL_ERR), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for TransferFrom { + const NAME: &'static str = "transferFrom"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for TransferFrom { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct TransferOwnership { + pub new_owner: Vec, + } + impl TransferOwnership { + const METHOD_ID: [u8; 4] = [242u8, 253u8, 227u8, 139u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + new_owner: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::Address(ethabi::Address::from_slice(&self.new_owner))], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for TransferOwnership { + const NAME: &'static str = "transferOwnership"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Unlock { + pub data: Vec, + } + impl Unlock { + const METHOD_ID: [u8; 4] = [72u8, 200u8, 148u8, 145u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Bytes], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + data: values.pop().expect(INTERNAL_ERR).into_bytes().expect(INTERNAL_ERR), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[ethabi::Token::Bytes(self.data.clone())]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode(&[ethabi::ParamType::Bytes], data.as_ref()) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_bytes() + .expect(INTERNAL_ERR), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![rpc::RpcCall { to_addr : address, data : self.encode(), }], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses.get(0).expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Unlock { + const NAME: &'static str = "unlock"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Unlock { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct UpdateDynamicLpFee { + pub key: ( + Vec, + Vec, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + Vec, + ), + pub new_dynamic_lp_fee: substreams::scalar::BigInt, + } + impl UpdateDynamicLpFee { + const METHOD_ID: [u8; 4] = [82u8, 117u8, 150u8, 81u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Tuple( + vec![ + ethabi::ParamType::Address, ethabi::ParamType::Address, + ethabi::ParamType::Uint(24usize), + ethabi::ParamType::Int(24usize), ethabi::ParamType::Address + ], + ), + ethabi::ParamType::Uint(24usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + key: { + let tuple_elements = values + .pop() + .expect(INTERNAL_ERR) + .into_tuple() + .expect(INTERNAL_ERR); + ( + tuple_elements[0usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + tuple_elements[1usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + { + let mut v = [0 as u8; 32]; + tuple_elements[2usize] + .clone() + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + tuple_elements[3usize] + .clone() + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + tuple_elements[4usize] + .clone() + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + }, + new_dynamic_lp_fee: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Tuple( + vec![ + ethabi::Token::Address(ethabi::Address::from_slice(& self.key + .0)), ethabi::Token::Address(ethabi::Address::from_slice(& + self.key.1)), + ethabi::Token::Uint(ethabi::Uint::from_big_endian(match self + .key.2.clone().to_bytes_be() { (num_bigint::Sign::Plus, + bytes) => bytes, (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") }, } + .as_slice(),),), { let non_full_signed_bytes = self.key.3 + .to_signed_bytes_be(); let full_signed_bytes_init = if + non_full_signed_bytes[0] & 0x80 == 0x80 { 0xff } else { 0x00 + }; let mut full_signed_bytes = [full_signed_bytes_init as u8; + 32]; non_full_signed_bytes.into_iter().rev().enumerate() + .for_each(| (i, byte) | full_signed_bytes[31 - i] = byte); + ethabi::Token::Int(ethabi::Int::from_big_endian(full_signed_bytes + .as_ref())) }, + ethabi::Token::Address(ethabi::Address::from_slice(& self.key + .4)) + ], + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.new_dynamic_lp_fee.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for UpdateDynamicLpFee { + const NAME: &'static str = "updateDynamicLPFee"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } +} +/// Contract's events. +#[allow(dead_code, unused_imports, unused_variables)] +pub mod events { + use super::INTERNAL_ERR; + #[derive(Debug, Clone, PartialEq)] + pub struct Approval { + pub owner: Vec, + pub spender: Vec, + pub id: substreams::scalar::BigInt, + pub amount: substreams::scalar::BigInt, + } + impl Approval { + const TOPIC_ID: [u8; 32] = [ + 179u8, + 253u8, + 80u8, + 113u8, + 131u8, + 88u8, + 135u8, + 86u8, + 122u8, + 6u8, + 113u8, + 21u8, + 17u8, + 33u8, + 137u8, + 77u8, + 220u8, + 204u8, + 40u8, + 66u8, + 241u8, + 209u8, + 11u8, + 237u8, + 173u8, + 19u8, + 224u8, + 209u8, + 124u8, + 172u8, + 233u8, + 167u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 4usize { + return false; + } + if log.data.len() != 32usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + owner: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'owner' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + spender: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'spender' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + id: { + let mut v = [0 as u8; 32]; + ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + log.topics[3usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'id' from topic of type 'uint256': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Approval { + const NAME: &'static str = "Approval"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode(log: &substreams_ethereum::pb::eth::v2::Log) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Donate { + pub id: [u8; 32usize], + pub sender: Vec, + pub amount0: substreams::scalar::BigInt, + pub amount1: substreams::scalar::BigInt, + } + impl Donate { + const TOPIC_ID: [u8; 32] = [ + 41u8, + 239u8, + 5u8, + 202u8, + 175u8, + 249u8, + 64u8, + 75u8, + 124u8, + 182u8, + 209u8, + 192u8, + 233u8, + 187u8, + 174u8, + 158u8, + 170u8, + 122u8, + 178u8, + 84u8, + 31u8, + 235u8, + 161u8, + 169u8, + 196u8, + 36u8, + 133u8, + 148u8, + 192u8, + 129u8, + 86u8, + 203u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 64usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + id: { + let mut result = [0u8; 32]; + let v = ethabi::decode( + &[ethabi::ParamType::FixedBytes(32usize)], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'id' from topic of type 'bytes32': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }, + sender: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'sender' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Donate { + const NAME: &'static str = "Donate"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode(log: &substreams_ethereum::pb::eth::v2::Log) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Initialize { + pub id: [u8; 32usize], + pub currency0: Vec, + pub currency1: Vec, + pub fee: substreams::scalar::BigInt, + pub tick_spacing: substreams::scalar::BigInt, + pub hooks: Vec, + pub sqrt_price_x96: substreams::scalar::BigInt, + pub tick: substreams::scalar::BigInt, + } + impl Initialize { + const TOPIC_ID: [u8; 32] = [ + 221u8, + 70u8, + 110u8, + 103u8, + 78u8, + 165u8, + 87u8, + 245u8, + 98u8, + 149u8, + 226u8, + 208u8, + 33u8, + 138u8, + 18u8, + 94u8, + 164u8, + 180u8, + 240u8, + 246u8, + 243u8, + 48u8, + 123u8, + 149u8, + 248u8, + 94u8, + 97u8, + 16u8, + 131u8, + 141u8, + 100u8, + 56u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 4usize { + return false; + } + if log.data.len() != 160usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(24usize), + ethabi::ParamType::Int(24usize), + ethabi::ParamType::Address, + ethabi::ParamType::Uint(160usize), + ethabi::ParamType::Int(24usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + id: { + let mut result = [0u8; 32]; + let v = ethabi::decode( + &[ethabi::ParamType::FixedBytes(32usize)], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'id' from topic of type 'bytes32': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }, + currency0: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'currency0' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + currency1: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[3usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'currency1' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + fee: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + tick_spacing: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + hooks: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + sqrt_price_x96: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + tick: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Initialize { + const NAME: &'static str = "Initialize"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode(log: &substreams_ethereum::pb::eth::v2::Log) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct ModifyLiquidity { + pub id: [u8; 32usize], + pub sender: Vec, + pub tick_lower: substreams::scalar::BigInt, + pub tick_upper: substreams::scalar::BigInt, + pub liquidity_delta: substreams::scalar::BigInt, + pub salt: [u8; 32usize], + } + impl ModifyLiquidity { + const TOPIC_ID: [u8; 32] = [ + 242u8, + 8u8, + 244u8, + 145u8, + 39u8, + 130u8, + 253u8, + 37u8, + 199u8, + 241u8, + 20u8, + 202u8, + 55u8, + 35u8, + 162u8, + 213u8, + 221u8, + 111u8, + 59u8, + 204u8, + 58u8, + 200u8, + 219u8, + 90u8, + 246u8, + 59u8, + 170u8, + 133u8, + 247u8, + 17u8, + 213u8, + 236u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 128usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Int(24usize), + ethabi::ParamType::Int(24usize), + ethabi::ParamType::Int(256usize), + ethabi::ParamType::FixedBytes(32usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + id: { + let mut result = [0u8; 32]; + let v = ethabi::decode( + &[ethabi::ParamType::FixedBytes(32usize)], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'id' from topic of type 'bytes32': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }, + sender: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'sender' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + tick_lower: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + tick_upper: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + liquidity_delta: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + salt: { + let mut result = [0u8; 32]; + let v = values + .pop() + .expect(INTERNAL_ERR) + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }, + }) + } + } + impl substreams_ethereum::Event for ModifyLiquidity { + const NAME: &'static str = "ModifyLiquidity"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode(log: &substreams_ethereum::pb::eth::v2::Log) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct OperatorSet { + pub owner: Vec, + pub operator: Vec, + pub approved: bool, + } + impl OperatorSet { + const TOPIC_ID: [u8; 32] = [ + 206u8, + 181u8, + 118u8, + 217u8, + 241u8, + 94u8, + 78u8, + 32u8, + 15u8, + 219u8, + 80u8, + 150u8, + 214u8, + 77u8, + 93u8, + 253u8, + 102u8, + 126u8, + 22u8, + 222u8, + 242u8, + 12u8, + 30u8, + 239u8, + 209u8, + 66u8, + 86u8, + 216u8, + 227u8, + 250u8, + 162u8, + 103u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 32usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Bool], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + owner: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'owner' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + operator: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'operator' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + approved: values + .pop() + .expect(INTERNAL_ERR) + .into_bool() + .expect(INTERNAL_ERR), + }) + } + } + impl substreams_ethereum::Event for OperatorSet { + const NAME: &'static str = "OperatorSet"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode(log: &substreams_ethereum::pb::eth::v2::Log) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct OwnershipTransferred { + pub user: Vec, + pub new_owner: Vec, + } + impl OwnershipTransferred { + const TOPIC_ID: [u8; 32] = [ + 139u8, + 224u8, + 7u8, + 156u8, + 83u8, + 22u8, + 89u8, + 20u8, + 19u8, + 68u8, + 205u8, + 31u8, + 208u8, + 164u8, + 242u8, + 132u8, + 25u8, + 73u8, + 127u8, + 151u8, + 34u8, + 163u8, + 218u8, + 175u8, + 227u8, + 180u8, + 24u8, + 111u8, + 107u8, + 100u8, + 87u8, + 224u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 0usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Ok(Self { + user: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'user' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + new_owner: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'new_owner' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + } + impl substreams_ethereum::Event for OwnershipTransferred { + const NAME: &'static str = "OwnershipTransferred"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode(log: &substreams_ethereum::pb::eth::v2::Log) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct ProtocolFeeControllerUpdated { + pub protocol_fee_controller: Vec, + } + impl ProtocolFeeControllerUpdated { + const TOPIC_ID: [u8; 32] = [ + 180u8, + 189u8, + 142u8, + 245u8, + 61u8, + 246u8, + 144u8, + 185u8, + 148u8, + 61u8, + 51u8, + 24u8, + 153u8, + 96u8, + 6u8, + 219u8, + 184u8, + 42u8, + 37u8, + 245u8, + 71u8, + 25u8, + 216u8, + 200u8, + 3u8, + 91u8, + 81u8, + 106u8, + 42u8, + 91u8, + 138u8, + 204u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 2usize { + return false; + } + if log.data.len() != 0usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Ok(Self { + protocol_fee_controller: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'protocol_fee_controller' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + } + impl substreams_ethereum::Event for ProtocolFeeControllerUpdated { + const NAME: &'static str = "ProtocolFeeControllerUpdated"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode(log: &substreams_ethereum::pb::eth::v2::Log) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct ProtocolFeeUpdated { + pub id: [u8; 32usize], + pub protocol_fee: substreams::scalar::BigInt, + } + impl ProtocolFeeUpdated { + const TOPIC_ID: [u8; 32] = [ + 233u8, + 196u8, + 37u8, + 147u8, + 231u8, + 31u8, + 132u8, + 64u8, + 59u8, + 132u8, + 53u8, + 44u8, + 209u8, + 104u8, + 214u8, + 147u8, + 226u8, + 201u8, + 252u8, + 209u8, + 253u8, + 188u8, + 195u8, + 254u8, + 178u8, + 29u8, + 146u8, + 180u8, + 62u8, + 102u8, + 150u8, + 249u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 2usize { + return false; + } + if log.data.len() != 32usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(24usize)], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + id: { + let mut result = [0u8; 32]; + let v = ethabi::decode( + &[ethabi::ParamType::FixedBytes(32usize)], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'id' from topic of type 'bytes32': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }, + protocol_fee: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for ProtocolFeeUpdated { + const NAME: &'static str = "ProtocolFeeUpdated"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode(log: &substreams_ethereum::pb::eth::v2::Log) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Swap { + pub id: [u8; 32usize], + pub sender: Vec, + pub amount0: substreams::scalar::BigInt, + pub amount1: substreams::scalar::BigInt, + pub sqrt_price_x96: substreams::scalar::BigInt, + pub liquidity: substreams::scalar::BigInt, + pub tick: substreams::scalar::BigInt, + pub fee: substreams::scalar::BigInt, + } + impl Swap { + const TOPIC_ID: [u8; 32] = [ + 64u8, + 233u8, + 206u8, + 203u8, + 159u8, + 95u8, + 31u8, + 28u8, + 91u8, + 156u8, + 151u8, + 222u8, + 194u8, + 145u8, + 123u8, + 126u8, + 233u8, + 46u8, + 87u8, + 186u8, + 85u8, + 99u8, + 112u8, + 141u8, + 172u8, + 169u8, + 77u8, + 216u8, + 74u8, + 215u8, + 17u8, + 47u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 192usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Int(128usize), + ethabi::ParamType::Int(128usize), + ethabi::ParamType::Uint(160usize), + ethabi::ParamType::Uint(128usize), + ethabi::ParamType::Int(24usize), + ethabi::ParamType::Uint(24usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + id: { + let mut result = [0u8; 32]; + let v = ethabi::decode( + &[ethabi::ParamType::FixedBytes(32usize)], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'id' from topic of type 'bytes32': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }, + sender: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'sender' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + amount1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + sqrt_price_x96: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + liquidity: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + tick: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + fee: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Swap { + const NAME: &'static str = "Swap"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode(log: &substreams_ethereum::pb::eth::v2::Log) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Transfer { + pub caller: Vec, + pub from: Vec, + pub to: Vec, + pub id: substreams::scalar::BigInt, + pub amount: substreams::scalar::BigInt, + } + impl Transfer { + const TOPIC_ID: [u8; 32] = [ + 27u8, + 61u8, + 126u8, + 219u8, + 46u8, + 156u8, + 11u8, + 14u8, + 124u8, + 82u8, + 91u8, + 32u8, + 170u8, + 174u8, + 240u8, + 245u8, + 148u8, + 13u8, + 46u8, + 215u8, + 22u8, + 99u8, + 199u8, + 211u8, + 146u8, + 102u8, + 236u8, + 175u8, + 172u8, + 114u8, + 136u8, + 89u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 4usize { + return false; + } + if log.data.len() != 64usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Address, ethabi::ParamType::Uint(256usize)], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + from: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'from' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + to: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'to' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + id: { + let mut v = [0 as u8; 32]; + ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + log.topics[3usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'id' from topic of type 'uint256': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + caller: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Transfer { + const NAME: &'static str = "Transfer"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode(log: &substreams_ethereum::pb::eth::v2::Log) -> Result { + Self::decode(log) + } + } +} \ No newline at end of file diff --git a/substreams/ethereum-uniswap-v4/src/lib.rs b/substreams/ethereum-uniswap-v4/src/lib.rs new file mode 100644 index 0000000..c8d3443 --- /dev/null +++ b/substreams/ethereum-uniswap-v4/src/lib.rs @@ -0,0 +1,4 @@ +#![allow(clippy::not_unsafe_ptr_arg_deref)] +mod abi; +mod modules; +mod pb; diff --git a/substreams/ethereum-uniswap-v4/src/modules/1_map_pool_created.rs b/substreams/ethereum-uniswap-v4/src/modules/1_map_pool_created.rs new file mode 100644 index 0000000..eb1904a --- /dev/null +++ b/substreams/ethereum-uniswap-v4/src/modules/1_map_pool_created.rs @@ -0,0 +1,146 @@ +use std::str::FromStr; + +use ethabi::ethereum_types::Address; +use substreams::scalar::BigInt; +use substreams_ethereum::pb::eth::v2::{self as eth}; + +use substreams_helper::{event_handler::EventHandler, hex::Hexable}; + +use crate::abi::pool_manager::events::Initialize; + +use tycho_substreams::prelude::*; +#[substreams::handlers::map] +pub fn map_pools_created( + params: String, + block: eth::Block, +) -> Result { + let mut new_pools: Vec = vec![]; + let pool_manager = params.as_str(); + + get_new_pools(&block, &mut new_pools, pool_manager); + + Ok(BlockEntityChanges { block: None, changes: new_pools }) +} + +// Extract new pools initialized on the pool manager contract +fn get_new_pools( + block: ð::Block, + new_pools: &mut Vec, + pool_manager_address: &str, +) { + // Extract new pools from Initialize events + let mut on_pool_created = |event: Initialize, _tx: ð::TransactionTrace, _log: ð::Log| { + let tycho_tx: Transaction = _tx.into(); + + new_pools.push(TransactionEntityChanges { + tx: Some(tycho_tx.clone()), + entity_changes: vec![EntityChanges { + component_id: event.id.to_vec().to_hex(), + attributes: vec![ + // Represents the pool's LP Fee. The fee is either static or dynamic. + // Static fees are represented in hundredths of a bip, and can be set to a + // value between 0 and 1000000 (100%) and are immutable. If + // the value is set to 0x800000 (1 in the most significant bit of uint24) then + // the pool has a dynamic fee. This pool is initialized + // with 0 fee which can be changed later via hooks (dynamic fee). + Attribute { + name: "balance_owner".to_string(), + value: hex::decode(pool_manager_address).unwrap(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "fee".to_string(), + value: event.fee.to_signed_bytes_be(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "liquidity".to_string(), + value: BigInt::from(0).to_signed_bytes_be(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "tick".to_string(), + value: event.tick.to_signed_bytes_be(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "sqrt_price_x96".to_string(), + value: event + .sqrt_price_x96 + .to_signed_bytes_be(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "protocol_fees/zero2one".to_string(), + value: BigInt::from(0).to_signed_bytes_be(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "protocol_fees/one2zero".to_string(), + value: BigInt::from(0).to_signed_bytes_be(), + change: ChangeType::Creation.into(), + }, + ], + }], + component_changes: vec![ProtocolComponent { + id: event.id.to_vec().to_hex(), + tokens: vec![event.currency0.clone(), event.currency1.clone()], + contracts: vec![], + static_att: vec![ + Attribute { + name: "tick_spacing".to_string(), + value: event.tick_spacing.to_signed_bytes_be(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "pool_id".to_string(), + value: event.id.to_vec(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "hooks".to_string(), + value: event.hooks.to_vec(), + change: ChangeType::Creation.into(), + }, + ], + change: i32::from(ChangeType::Creation), + protocol_type: Some(ProtocolType { + name: "uniswap_v4_pool".to_string(), + financial_type: FinancialType::Swap.into(), + attribute_schema: vec![], + implementation_type: ImplementationType::Custom.into(), + }), + tx: Some(tycho_tx), + }], + balance_changes: vec![ + BalanceChange { + token: event.currency0, + balance: BigInt::from(0).to_signed_bytes_be(), + component_id: event + .id + .to_vec() + .to_hex() + .as_bytes() + .to_vec(), + }, + BalanceChange { + token: event.currency1, + balance: BigInt::from(0).to_signed_bytes_be(), + component_id: event + .id + .to_vec() + .to_hex() + .as_bytes() + .to_vec(), + }, + ], + }) + }; + + let mut eh = EventHandler::new(block); + + eh.filter_by_address(vec![Address::from_str(pool_manager_address).unwrap()]); + + eh.on::(&mut on_pool_created); + eh.handle_events(); +} diff --git a/substreams/ethereum-uniswap-v4/src/modules/2_store_pools.rs b/substreams/ethereum-uniswap-v4/src/modules/2_store_pools.rs new file mode 100644 index 0000000..27c972b --- /dev/null +++ b/substreams/ethereum-uniswap-v4/src/modules/2_store_pools.rs @@ -0,0 +1,24 @@ +use std::str; + +use substreams::store::{StoreNew, StoreSetIfNotExists, StoreSetIfNotExistsProto}; +use tycho_substreams::models::BlockEntityChanges; + +use crate::pb::uniswap::v4::Pool; + +#[substreams::handlers::store] +pub fn store_pools(pools_created: BlockEntityChanges, store: StoreSetIfNotExistsProto) { + // Store pools. Required so the next maps can match any event to a known pool by their address + + for change in pools_created.changes { + for component_change in &change.component_changes { + let pool_address: &str = &component_change.id; + let pool: Pool = Pool { + id: hex::decode(pool_address.trim_start_matches("0x")).unwrap(), + currency0: component_change.tokens[0].clone(), + currency1: component_change.tokens[1].clone(), + created_tx_hash: change.tx.as_ref().unwrap().hash.clone(), + }; + store.set_if_not_exists(0, format!("{}:{}", "pool", pool_address), &pool); + } + } +} diff --git a/substreams/ethereum-uniswap-v4/src/modules/3_map_events.rs b/substreams/ethereum-uniswap-v4/src/modules/3_map_events.rs new file mode 100644 index 0000000..6a7e0ad --- /dev/null +++ b/substreams/ethereum-uniswap-v4/src/modules/3_map_events.rs @@ -0,0 +1,138 @@ +use crate::{ + abi::pool_manager::events::{Initialize, ModifyLiquidity, ProtocolFeeUpdated, Swap}, + pb::uniswap::v4::{ + events::{pool_event, pool_event::Type, PoolEvent}, + Events, Pool, + }, +}; +use anyhow::Ok; +use substreams::store::{StoreGet, StoreGetProto}; +use substreams_ethereum::{ + pb::eth::v2::{self as eth, Log, TransactionTrace}, + Event, +}; +use substreams_helper::hex::Hexable; + +#[substreams::handlers::map] +pub fn map_events( + block: eth::Block, + pools_store: StoreGetProto, +) -> Result { + let mut pool_manager_events = block + .transaction_traces + .into_iter() + .filter(|tx| tx.status == 1) + .flat_map(|tx| { + tx.clone() + .receipt + .into_iter() + .flat_map(|receipt| receipt.logs) + .filter_map(|log| log_to_event(&log, tx.clone(), &pools_store)) + .collect::>() + }) + .collect::>(); + + pool_manager_events.sort_unstable_by_key(|e| e.log_ordinal); + + Ok(Events { pool_events: pool_manager_events }) +} + +fn log_to_event( + event: &Log, + tx: TransactionTrace, + pools_store: &StoreGetProto, +) -> Option { + if let Some(init) = Initialize::match_and_decode(event) { + // We need to track initialization again to keep track of pool current tick, which is set on + // initialization and changed on swaps. + let pool_id = init.id.to_vec().to_hex(); + let pool = pools_store.get_last(format!("{}:{}", "pool", &pool_id))?; + Some(PoolEvent { + log_ordinal: event.ordinal, + pool_id, + currency0: pool.currency0.to_hex(), + currency1: pool.currency1.to_hex(), + transaction: Some(tx.into()), + r#type: Some(Type::Initialize(pool_event::Initialize { + sqrt_price_x96: init.sqrt_price_x96.to_string(), + tick: init.tick.into(), + fee: init.fee.into(), + tick_spacing: init.tick_spacing.into(), + hooks: init.hooks.to_vec().to_hex(), + })), + }) + } else if let Some(swap) = Swap::match_and_decode(event) { + let pool_id = swap.id.to_vec().to_hex(); + let pool = pools_store.get_last(format!("{}:{}", "pool", &pool_id))?; + Some(PoolEvent { + log_ordinal: event.ordinal, + pool_id, + currency0: pool.currency0.to_hex(), + currency1: pool.currency1.to_hex(), + transaction: Some(tx.into()), + r#type: Some(Type::Swap(pool_event::Swap { + sender: swap.sender.to_hex(), + amount0: swap.amount0.to_string(), + amount1: swap.amount1.to_string(), + sqrt_price_x96: swap.sqrt_price_x96.to_string(), + liquidity: swap.liquidity.to_string(), + tick: swap.tick.into(), + fee: swap.fee.into(), + })), + }) + // Skipped because Donate doesn't seem to affect pool liquidity? + // } else if let Some(flash) = Donate::match_and_decode(event) { + // let pool_id = flash.id.to_vec().to_hex(); + // let pool = pools_store.get_last(format!("{}:{}", "pool", &pool_id))?; + // Some(PoolEvent { + // log_ordinal: event.ordinal, + // pool_id, + // currency0: pool.currency0.to_hex(), + // currency1: pool.currency1.to_hex(), + // transaction: Some(tx.into()), + // r#type: Some(Type::Donate(pool_event::Donate { + // sender: flash.sender.to_hex(), + // amount0: flash.amount0.to_string(), + // amount1: flash.amount1.to_string(), + // })), + // }) + } else if let Some(modify_liquidity) = ModifyLiquidity::match_and_decode(event) { + let pool_id = modify_liquidity.id.to_vec().to_hex(); + let pool = pools_store.get_last(format!("{}:{}", "pool", &pool_id))?; + Some(PoolEvent { + log_ordinal: event.ordinal, + pool_id, + currency0: pool.currency0.to_hex(), + currency1: pool.currency1.to_hex(), + transaction: Some(tx.into()), + r#type: Some(Type::ModifyLiquidity(pool_event::ModifyLiquidity { + sender: modify_liquidity.sender.to_hex(), + tick_lower: modify_liquidity.tick_lower.into(), + tick_upper: modify_liquidity.tick_upper.into(), + liquidity_delta: modify_liquidity + .liquidity_delta + .to_string(), + salt: modify_liquidity.salt.to_vec().to_hex(), + })), + }) + } else if let Some(protocol_fee_updated) = ProtocolFeeUpdated::match_and_decode(event) { + let pool_id = protocol_fee_updated + .id + .to_vec() + .to_hex(); + let pool = pools_store.get_last(format!("{}:{}", "pool", &pool_id))?; + Some(PoolEvent { + log_ordinal: event.ordinal, + pool_id: pool_id.clone(), + currency0: pool.currency0.to_hex(), + currency1: pool.currency1.to_hex(), + transaction: Some(tx.into()), + r#type: Some(Type::ProtocolFeeUpdated(pool_event::ProtocolFeeUpdated { + pool_id, + protocol_fee: protocol_fee_updated.protocol_fee.into(), + })), + }) + } else { + None + } +} diff --git a/substreams/ethereum-uniswap-v4/src/modules/4_store_current_sqrtprice.rs b/substreams/ethereum-uniswap-v4/src/modules/4_store_current_sqrtprice.rs new file mode 100644 index 0000000..9add61c --- /dev/null +++ b/substreams/ethereum-uniswap-v4/src/modules/4_store_current_sqrtprice.rs @@ -0,0 +1,39 @@ +use substreams::{ + scalar::BigInt, + store::{StoreSet, StoreSetBigInt}, +}; + +use crate::pb::uniswap::v4::{ + events::{pool_event, PoolEvent}, + Events, +}; + +use substreams::store::StoreNew; + +#[substreams::handlers::store] +pub fn store_pool_current_sqrt_price(events: Events, store: StoreSetBigInt) { + events + .pool_events + .into_iter() + .filter_map(event_to_current_sqrt_price) + .for_each(|(pool, ordinal, new_tick_index)| { + store.set(ordinal, format!("pool:{0}", pool), &new_tick_index) + }); +} + +fn event_to_current_sqrt_price(event: PoolEvent) -> Option<(String, u64, BigInt)> { + match event.r#type.as_ref().unwrap() { + pool_event::Type::Initialize(initialize) => Some(( + event.pool_id, + event.log_ordinal, + BigInt::try_from(&initialize.sqrt_price_x96) + .expect("cannot convert sqrt_price to bigint"), + )), + pool_event::Type::Swap(swap) => Some(( + event.pool_id, + event.log_ordinal, + BigInt::try_from(&swap.sqrt_price_x96).expect("cannot convert sqrt_price to bigint"), + )), + _ => None, + } +} diff --git a/substreams/ethereum-uniswap-v4/src/modules/4_store_current_tick.rs b/substreams/ethereum-uniswap-v4/src/modules/4_store_current_tick.rs new file mode 100644 index 0000000..d81c055 --- /dev/null +++ b/substreams/ethereum-uniswap-v4/src/modules/4_store_current_tick.rs @@ -0,0 +1,28 @@ +use substreams::store::{StoreSet, StoreSetInt64}; + +use crate::pb::uniswap::v4::{ + events::{pool_event, PoolEvent}, + Events, +}; + +use substreams::store::StoreNew; + +#[substreams::handlers::store] +pub fn store_pool_current_tick(events: Events, store: StoreSetInt64) { + events + .pool_events + .into_iter() + .filter_map(event_to_current_tick) + .for_each(|(pool, ordinal, new_tick_index)| { + store.set(ordinal, format!("pool:{0}", pool), &new_tick_index.into()) + }); +} +fn event_to_current_tick(event: PoolEvent) -> Option<(String, u64, i32)> { + match event.r#type.as_ref().unwrap() { + pool_event::Type::Initialize(initialize) => { + Some((event.pool_id, event.log_ordinal, initialize.tick)) + } + pool_event::Type::Swap(swap) => Some((event.pool_id, event.log_ordinal, swap.tick)), + _ => None, + } +} diff --git a/substreams/ethereum-uniswap-v4/src/modules/5_map_store_balance_changes.rs b/substreams/ethereum-uniswap-v4/src/modules/5_map_store_balance_changes.rs new file mode 100644 index 0000000..1e2e497 --- /dev/null +++ b/substreams/ethereum-uniswap-v4/src/modules/5_map_store_balance_changes.rs @@ -0,0 +1,185 @@ +use std::str::FromStr; + +use anyhow::Ok; +use tycho_substreams::models::{BalanceDelta, BlockBalanceDeltas}; + +use crate::{ + modules::uni_math::calculate_token_amounts, + pb::uniswap::v4::{ + events::{pool_event, PoolEvent}, + Events, + }, +}; +use substreams::{ + prelude::StoreGet, + scalar::BigInt, + store::{StoreAddBigInt, StoreGetBigInt, StoreNew}, +}; + +#[substreams::handlers::map] +pub fn map_balance_changes( + events: Events, + pools_current_sqrt_price_store: StoreGetBigInt, +) -> Result { + let balance_deltas = events + .pool_events + .into_iter() + .filter(PoolEvent::can_introduce_balance_changes) + .map(|e| { + ( + pools_current_sqrt_price_store + .get_at(e.log_ordinal, format!("pool:{0}", &e.pool_id)) + .unwrap_or(BigInt::zero()), + e, + ) + }) + .filter_map(|(current_sqrt_price, event)| { + event_to_balance_deltas(current_sqrt_price, event) + }) + .flatten() + .collect(); + + Ok(BlockBalanceDeltas { balance_deltas }) +} + +#[substreams::handlers::store] +pub fn store_pools_balances(balances_deltas: BlockBalanceDeltas, store: StoreAddBigInt) { + tycho_substreams::balances::store_balance_changes(balances_deltas, store); +} + +fn event_to_balance_deltas( + current_sqrt_price: BigInt, + event: PoolEvent, +) -> Option> { + let address = event.pool_id.as_bytes().to_vec(); + match event.r#type.unwrap() { + pool_event::Type::ModifyLiquidity(e) => { + let (delta0, delta1) = + get_amount_delta(current_sqrt_price, e.tick_lower, e.tick_upper, e.liquidity_delta); + Some(vec![ + BalanceDelta { + token: hex::decode( + event + .currency0 + .clone() + .trim_start_matches("0x"), + ) + .unwrap(), + delta: delta0.to_signed_bytes_be(), + component_id: address.clone(), + ord: event.log_ordinal, + tx: event + .transaction + .clone() + .map(Into::into), + }, + BalanceDelta { + token: hex::decode( + event + .currency1 + .clone() + .trim_start_matches("0x"), + ) + .unwrap(), + delta: delta1.to_signed_bytes_be(), + component_id: address, + ord: event.log_ordinal, + tx: event.transaction.map(Into::into), + }, + ]) + } + + pool_event::Type::Swap(e) => { + let mut delta0 = BigInt::from_str(&e.amount0) + .unwrap() + .neg(); + let mut delta1 = BigInt::from_str(&e.amount1) + .unwrap() + .neg(); + + // Adjusting deltas to exclude collected fees. + // Collected fees are not considered part of the component balance since they aren't + // accounted for during swaps. Thus, they should be excluded from the Total Value Locked + // (TVL). + // To achieve this, we subtract the fee portion from each delta. + + // We perform integer division and round to the nearest integer based on the + // remainder. If the remainder is at least half of the divisor, we + // round up. + let bips_divisor = BigInt::from(1_000_000); + let half_divisor = BigInt::from(500_000); + if delta0 > BigInt::zero() { + let fee_part = delta0.clone() * e.fee; + let (quotient, remainder) = fee_part.div_rem(&bips_divisor); + delta0 = + delta0 - if remainder >= half_divisor { quotient + 1u32 } else { quotient }; + } + if delta1 > BigInt::zero() { + let fee_part = delta1.clone() * e.fee; + let (quotient, remainder) = fee_part.div_rem(&bips_divisor); + delta1 = + delta1 - if remainder >= half_divisor { quotient + 1u32 } else { quotient }; + } + + Some(vec![ + BalanceDelta { + token: hex::decode( + event + .currency0 + .clone() + .trim_start_matches("0x"), + ) + .unwrap(), + delta: delta0.to_signed_bytes_be(), + component_id: address.clone(), + ord: event.log_ordinal, + tx: event + .transaction + .clone() + .map(Into::into), + }, + BalanceDelta { + token: hex::decode( + event + .currency1 + .clone() + .trim_start_matches("0x"), + ) + .unwrap(), + delta: delta1.to_signed_bytes_be(), + component_id: address.clone(), + ord: event.log_ordinal, + tx: event.transaction.map(Into::into), + }, + ]) + } + _ => None, + } +} + +impl PoolEvent { + fn can_introduce_balance_changes(&self) -> bool { + matches!( + self.r#type.as_ref().unwrap(), + pool_event::Type::ModifyLiquidity(_) | pool_event::Type::Swap(_) + ) + } +} + +fn get_amount_delta( + current_sqrt_price: BigInt, + tick_lower: i32, + tick_upper: i32, + liquidity_delta: String, +) -> (BigInt, BigInt) { + // This should never fail because the liquidity delta is a string encoded signed int128 (from + // the contract) + let liquidity_delta: i128 = liquidity_delta + .parse() + .expect("Failed to parse liquidity delta"); + + let (amount0, amount1) = + calculate_token_amounts(current_sqrt_price.into(), tick_lower, tick_upper, liquidity_delta) + .expect("Failed to calculate token amounts from liquidity delta"); + (BigInt::from(amount0), BigInt::from(amount1)) +} diff --git a/substreams/ethereum-uniswap-v4/src/modules/5_map_store_liquidity.rs b/substreams/ethereum-uniswap-v4/src/modules/5_map_store_liquidity.rs new file mode 100644 index 0000000..96a7477 --- /dev/null +++ b/substreams/ethereum-uniswap-v4/src/modules/5_map_store_liquidity.rs @@ -0,0 +1,101 @@ +use std::str::FromStr; + +use substreams::store::{StoreGet, StoreGetInt64, StoreSetSum, StoreSetSumBigInt}; + +use crate::pb::uniswap::v4::{ + events::{pool_event, PoolEvent}, + Events, LiquidityChange, LiquidityChangeType, LiquidityChanges, +}; + +use substreams::scalar::BigInt; + +use anyhow::Ok; +use substreams_helper::hex::Hexable; + +#[substreams::handlers::map] +pub fn map_liquidity_changes( + events: Events, + pools_current_tick_store: StoreGetInt64, +) -> Result { + let mut changes = events + .pool_events + .into_iter() + .filter(PoolEvent::can_introduce_liquidity_changes) + .map(|e| { + ( + pools_current_tick_store + .get_at(e.log_ordinal, format!("pool:{0}", &e.pool_id)) + .unwrap_or(0), + e, + ) + }) + .filter_map(|(current_tick, event)| event_to_liquidity_deltas(current_tick, event)) + .collect::>(); + + changes.sort_unstable_by_key(|l| l.ordinal); + Ok(LiquidityChanges { changes }) +} + +#[substreams::handlers::store] +pub fn store_liquidity(ticks_deltas: LiquidityChanges, store: StoreSetSumBigInt) { + ticks_deltas + .changes + .iter() + .for_each(|changes| match changes.change_type() { + LiquidityChangeType::Delta => { + store.sum( + changes.ordinal, + format!("pool:{0}", &changes.pool_address.to_hex()), + BigInt::from_signed_bytes_be(&changes.value), + ); + } + LiquidityChangeType::Absolute => { + store.set( + changes.ordinal, + format!("pool:{0}", &changes.pool_address.to_hex()), + BigInt::from_signed_bytes_be(&changes.value), + ); + } + }); +} + +fn event_to_liquidity_deltas(current_tick: i64, event: PoolEvent) -> Option { + match event.r#type.as_ref().unwrap() { + pool_event::Type::ModifyLiquidity(mod_liquidity) => { + if current_tick >= mod_liquidity.tick_lower.into() && + current_tick < mod_liquidity.tick_upper.into() + { + Some(LiquidityChange { + pool_address: hex::decode(event.pool_id.trim_start_matches("0x")).unwrap(), + value: BigInt::from_str(&mod_liquidity.liquidity_delta) + .unwrap() + .to_signed_bytes_be(), + change_type: LiquidityChangeType::Delta.into(), + ordinal: event.log_ordinal, + transaction: Some(event.transaction.unwrap()), + }) + } else { + None + } + } + pool_event::Type::Swap(swap) => Some(LiquidityChange { + pool_address: hex::decode(event.pool_id.trim_start_matches("0x")).unwrap(), + value: BigInt::from_str(&swap.liquidity) + .unwrap() + .to_signed_bytes_be(), + change_type: LiquidityChangeType::Absolute.into(), + ordinal: event.log_ordinal, + transaction: Some(event.transaction.unwrap()), + }), + _ => None, + } +} + +impl PoolEvent { + fn can_introduce_liquidity_changes(&self) -> bool { + matches!( + self.r#type.as_ref().unwrap(), + pool_event::Type::ModifyLiquidity(_) | pool_event::Type::Swap(_) + ) + } +} diff --git a/substreams/ethereum-uniswap-v4/src/modules/5_map_store_ticks.rs b/substreams/ethereum-uniswap-v4/src/modules/5_map_store_ticks.rs new file mode 100644 index 0000000..f60a09e --- /dev/null +++ b/substreams/ethereum-uniswap-v4/src/modules/5_map_store_ticks.rs @@ -0,0 +1,73 @@ +use std::str::FromStr; + +use substreams::store::StoreAddBigInt; + +use crate::pb::uniswap::v4::{ + events::{pool_event, PoolEvent}, + Events, TickDelta, TickDeltas, +}; + +use substreams::{ + scalar::BigInt, + store::{StoreAdd, StoreNew}, +}; + +use anyhow::Ok; +use substreams_helper::hex::Hexable; + +#[substreams::handlers::map] +pub fn map_ticks_changes(events: Events) -> Result { + let ticks_deltas = events + .pool_events + .into_iter() + .flat_map(event_to_ticks_deltas) + .collect(); + + Ok(TickDeltas { deltas: ticks_deltas }) +} + +#[substreams::handlers::store] +pub fn store_ticks_liquidity(ticks_deltas: TickDeltas, store: StoreAddBigInt) { + let mut deltas = ticks_deltas.deltas.clone(); + + deltas.sort_unstable_by_key(|delta| delta.ordinal); + + deltas.iter().for_each(|delta| { + store.add( + delta.ordinal, + format!("pool:{0}:tick:{1}", &delta.pool_address.to_hex(), delta.tick_index,), + BigInt::from_signed_bytes_be(&delta.liquidity_net_delta), + ); + }); +} + +fn event_to_ticks_deltas(event: PoolEvent) -> Vec { + // On UniswapV4, the only event that changes liquidity is ModifyLiquidity. Liquidity Delta is + // now expressed as a signed int256. A positive number indicates a mint, while a negative + // indicates a burn. + // Mint events will have negative deltas for the upper tick and positive deltas for the lower. + // Burn events will have positive deltas for the upper tick and negative deltas for the lower. + match event.r#type.as_ref().unwrap() { + pool_event::Type::ModifyLiquidity(liq_change) => { + let amount = + BigInt::from_str(&liq_change.liquidity_delta).expect("Failed to parse BigInt"); + vec![ + TickDelta { + pool_address: hex::decode(event.pool_id.trim_start_matches("0x")).unwrap(), + tick_index: liq_change.tick_lower, + liquidity_net_delta: amount.to_signed_bytes_be(), + ordinal: event.log_ordinal, + transaction: event.transaction.clone(), + }, + TickDelta { + pool_address: hex::decode(event.pool_id.trim_start_matches("0x")).unwrap(), + tick_index: liq_change.tick_upper, + liquidity_net_delta: amount.neg().to_signed_bytes_be(), + ordinal: event.log_ordinal, + transaction: event.transaction, + }, + ] + } + _ => vec![], + } +} diff --git a/substreams/ethereum-uniswap-v4/src/modules/6_map_protocol_changes.rs b/substreams/ethereum-uniswap-v4/src/modules/6_map_protocol_changes.rs new file mode 100644 index 0000000..b0b9c2c --- /dev/null +++ b/substreams/ethereum-uniswap-v4/src/modules/6_map_protocol_changes.rs @@ -0,0 +1,240 @@ +use crate::pb::uniswap::v4::{ + events::{pool_event, PoolEvent}, + Events, LiquidityChanges, TickDeltas, +}; +use itertools::Itertools; +use std::{collections::HashMap, str::FromStr, vec}; +use substreams::{pb::substreams::StoreDeltas, scalar::BigInt}; +use substreams_ethereum::pb::eth::v2::{self as eth}; +use substreams_helper::hex::Hexable; +use tycho_substreams::{balances::aggregate_balances_changes, prelude::*}; + +type PoolAddress = Vec; + +#[substreams::handlers::map] +pub fn map_protocol_changes( + block: eth::Block, + created_pools: BlockEntityChanges, + events: Events, + balances_map_deltas: BlockBalanceDeltas, + balances_store_deltas: StoreDeltas, + ticks_map_deltas: TickDeltas, + ticks_store_deltas: StoreDeltas, + pool_liquidity_changes: LiquidityChanges, + pool_liquidity_store_deltas: StoreDeltas, +) -> Result { + // We merge contract changes by transaction (identified by transaction index) making it easy to + // sort them at the very end. + let mut transaction_changes: HashMap<_, TransactionChangesBuilder> = HashMap::new(); + + // Add created pools to the tx_changes_map + for change in created_pools.changes.into_iter() { + let tx = change.tx.as_ref().unwrap(); + let builder = transaction_changes + .entry(tx.index) + .or_insert_with(|| TransactionChangesBuilder::new(tx)); + change + .component_changes + .iter() + .for_each(|c| { + builder.add_protocol_component(c); + }); + change + .entity_changes + .iter() + .for_each(|ec| { + builder.add_entity_change(ec); + }); + change + .balance_changes + .iter() + .for_each(|bc| { + builder.add_balance_change(bc); + }); + } + + // Balance changes are gathered by the `StoreDelta` based on `PoolBalanceChanged` creating + // `BlockBalanceDeltas`. We essentially just process the changes that occurred to the `store` + // this block. Then, these balance changes are merged onto the existing map of tx contract + // changes, inserting a new one if it doesn't exist. + aggregate_balances_changes(balances_store_deltas, balances_map_deltas) + .into_iter() + .for_each(|(_, (tx, balances))| { + let builder = transaction_changes + .entry(tx.index) + .or_insert_with(|| TransactionChangesBuilder::new(&tx)); + balances + .values() + .for_each(|token_bc_map| { + token_bc_map + .values() + .for_each(|bc| builder.add_balance_change(bc)) + }); + }); + + // Insert ticks net-liquidity changes + ticks_store_deltas + .deltas + .into_iter() + .zip(ticks_map_deltas.deltas) + .for_each(|(store_delta, tick_delta)| { + let new_value_bigint = + BigInt::from_str(&String::from_utf8(store_delta.new_value).unwrap()).unwrap(); + + // If old value is empty or the int value is 0, it's considered as a creation. + let is_creation = store_delta.old_value.is_empty() || + BigInt::from_str(&String::from_utf8(store_delta.old_value).unwrap()) + .unwrap() + .is_zero(); + let attribute_name = format!("ticks/{}/net-liquidity", tick_delta.tick_index); + let attribute = Attribute { + name: attribute_name, + value: new_value_bigint.to_signed_bytes_be(), + change: if is_creation { + ChangeType::Creation.into() + } else if new_value_bigint.is_zero() { + ChangeType::Deletion.into() + } else { + ChangeType::Update.into() + }, + }; + let tx = tick_delta.transaction.unwrap(); + let builder = transaction_changes + .entry(tx.index) + .or_insert_with(|| TransactionChangesBuilder::new(&tx.into())); + + builder.add_entity_change(&EntityChanges { + component_id: tick_delta.pool_address.to_hex(), + attributes: vec![attribute], + }); + }); + + // Insert liquidity changes + pool_liquidity_store_deltas + .deltas + .into_iter() + .zip(pool_liquidity_changes.changes) + .for_each(|(store_delta, change)| { + let new_value_bigint = BigInt::from_str( + String::from_utf8(store_delta.new_value) + .unwrap() + .split(':') + .nth(1) + .unwrap(), + ) + .unwrap(); + let tx = change.transaction.unwrap(); + let builder = transaction_changes + .entry(tx.index) + .or_insert_with(|| TransactionChangesBuilder::new(&tx.into())); + + builder.add_entity_change(&EntityChanges { + component_id: change.pool_address.to_hex(), + attributes: vec![Attribute { + name: "liquidity".to_string(), + value: new_value_bigint.to_signed_bytes_be(), + change: ChangeType::Update.into(), + }], + }); + }); + + // Insert others changes + events + .pool_events + .into_iter() + .flat_map(event_to_attributes_updates) + .for_each(|(tx, pool_address, attr)| { + let builder = transaction_changes + .entry(tx.index) + .or_insert_with(|| TransactionChangesBuilder::new(&tx)); + builder.add_entity_change(&EntityChanges { + component_id: pool_address.to_hex(), + attributes: vec![attr], + }); + }); + + Ok(BlockChanges { + block: Some((&block).into()), + changes: transaction_changes + .drain() + .sorted_unstable_by_key(|(index, _)| *index) + .filter_map(|(_, builder)| builder.build()) + .collect::>(), + }) +} + +fn event_to_attributes_updates(event: PoolEvent) -> Vec<(Transaction, PoolAddress, Attribute)> { + match event.r#type.as_ref().unwrap() { + pool_event::Type::Swap(swap) => vec![ + ( + event + .transaction + .clone() + .unwrap() + .into(), + hex::decode( + event + .pool_id + .clone() + .trim_start_matches("0x"), + ) + .unwrap(), + Attribute { + name: "sqrt_price_x96".to_string(), + value: BigInt::from_str(&swap.sqrt_price_x96) + .unwrap() + .to_signed_bytes_be(), + change: ChangeType::Update.into(), + }, + ), + ( + event.transaction.unwrap().into(), + hex::decode(event.pool_id.trim_start_matches("0x")).unwrap(), + Attribute { + name: "tick".to_string(), + value: BigInt::from(swap.tick).to_signed_bytes_be(), + change: ChangeType::Update.into(), + }, + ), + ], + pool_event::Type::ProtocolFeeUpdated(sfp) => { + // Mask to extract the lower 12 bits (0xFFF corresponds to 12 bits set to 1) + let lower_12_bits = sfp.protocol_fee & 0xFFF; + + // Shift right by 12 bits and mask again to get the next 12 bits + let upper_12_bits = (sfp.protocol_fee >> 12) & 0xFFF; + + vec![ + ( + event + .transaction + .clone() + .unwrap() + .into(), + hex::decode( + event + .pool_id + .clone() + .trim_start_matches("0x"), + ) + .unwrap(), + Attribute { + name: "protocol_fees/zero2one".to_string(), + value: BigInt::from(lower_12_bits).to_signed_bytes_be(), + change: ChangeType::Update.into(), + }, + ), + ( + event.transaction.unwrap().into(), + hex::decode(event.pool_id.trim_start_matches("0x")).unwrap(), + Attribute { + name: "protocol_fees/one2zero".to_string(), + value: BigInt::from(upper_12_bits).to_signed_bytes_be(), + change: ChangeType::Update.into(), + }, + ), + ] + } + _ => vec![], + } +} diff --git a/substreams/ethereum-uniswap-v4/src/modules/mod.rs b/substreams/ethereum-uniswap-v4/src/modules/mod.rs new file mode 100644 index 0000000..cd855e9 --- /dev/null +++ b/substreams/ethereum-uniswap-v4/src/modules/mod.rs @@ -0,0 +1,43 @@ +use substreams_ethereum::pb::eth::v2::TransactionTrace; + +use crate::pb::uniswap::v4::Transaction; + +#[path = "1_map_pool_created.rs"] +mod map_pool_created; + +#[path = "2_store_pools.rs"] +mod store_pools; + +#[path = "3_map_events.rs"] +mod map_events; + +#[path = "4_store_current_tick.rs"] +mod store_current_tick; + +#[path = "4_store_current_sqrtprice.rs"] +mod store_current_sqrtprice; + +#[path = "5_map_store_balance_changes.rs"] +mod map_store_balance_changes; + +#[path = "5_map_store_ticks.rs"] +mod map_store_ticks; + +#[path = "5_map_store_liquidity.rs"] +mod map_store_liquidity; + +#[path = "6_map_protocol_changes.rs"] +mod map_protocol_changes; +mod uni_math; + +impl From for Transaction { + fn from(value: TransactionTrace) -> Self { + Self { hash: value.hash, from: value.from, to: value.to, index: value.index.into() } + } +} + +impl From for tycho_substreams::prelude::Transaction { + fn from(value: Transaction) -> Self { + Self { hash: value.hash, from: value.from, to: value.to, index: value.index } + } +} diff --git a/substreams/ethereum-uniswap-v4/src/modules/uni_math.rs b/substreams/ethereum-uniswap-v4/src/modules/uni_math.rs new file mode 100644 index 0000000..2fcdede --- /dev/null +++ b/substreams/ethereum-uniswap-v4/src/modules/uni_math.rs @@ -0,0 +1,469 @@ +use num_bigint::BigInt; +use std::ops::Shr; +/// Calculates the amounts of token0 and token1 for a given position +/// +/// Source: https://github.com/Uniswap/v4-core/blob/main/src/libraries/Pool.sol +/// Function: modifyLiquidity +/// +/// # Arguments +/// * `current_sqrt_price` - Current square root price +/// * `tick_lower` - Lower tick of the position +/// * `tick_upper` - Upper tick of the position +/// * `liquidity_delta` - Amount of liquidity to add/remove +/// +/// # Returns +/// * `Result<(BigInt, BigInt), String>` - Token amounts (amount0, amount1) +pub fn calculate_token_amounts( + current_sqrt_price: BigInt, + tick_lower: i32, + tick_upper: i32, + liquidity_delta: i128, +) -> Result<(BigInt, BigInt), String> { + let sqrt_price_lower_x96: BigInt = get_sqrt_ratio_at_tick(tick_lower)?; + let sqrt_price_upper_x96: BigInt = get_sqrt_ratio_at_tick(tick_upper)?; + + // Calculate amounts based on current price relative to the range + let (amount0, amount1) = if current_sqrt_price < sqrt_price_lower_x96 { + // Current price is below the range: position in token0 + let amount0 = + get_amount_0_delta_signed(sqrt_price_lower_x96, sqrt_price_upper_x96, liquidity_delta)?; + (amount0, BigInt::from(0)) + } else if current_sqrt_price < sqrt_price_upper_x96 { + // Current price is within the range: position in both tokens + let amount0 = get_amount_0_delta_signed( + current_sqrt_price.clone(), + sqrt_price_upper_x96, + liquidity_delta, + )?; + + let amount1 = + get_amount_1_delta_signed(sqrt_price_lower_x96, current_sqrt_price, liquidity_delta)?; + + (amount0, amount1) + } else { + // Current price is above the range: position in token1 + let amount1 = + get_amount_1_delta_signed(sqrt_price_lower_x96, sqrt_price_upper_x96, liquidity_delta)?; + + (BigInt::from(0), amount1) + }; + + Ok((amount0, amount1)) +} + +const MAX_TICK: i32 = 887272; + +/// Returns the sqrt ratio as a Q64.96 for the given tick. The sqrt ratio is computed as +/// sqrt(1.0001)^tick +/// Adapted from: https://github.com/shuhuiluo/uniswap-v3-sdk-rs/blob/v2.9.1/src/utils/tick_math.rs#L57 +fn get_sqrt_ratio_at_tick(tick: i32) -> Result { + let abs_tick = tick.abs(); + + if abs_tick > MAX_TICK { + return Err("Tick out of bounds".to_string()); + } + + // Initialize ratio with either 2^128 / sqrt(1.0001) or 2^128 + let mut ratio = if abs_tick & 0x1 != 0 { + BigInt::parse_bytes(b"fffcb933bd6fad37aa2d162d1a594001", 16).unwrap() + } else { + BigInt::from(1) << 128 + }; + + if abs_tick & 0x2 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"fff97272373d413259a46990580e213a", 16).unwrap()) + .shr(128); + } + if abs_tick & 0x4 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"fff2e50f5f656932ef12357cf3c7fdcc", 16).unwrap()) + .shr(128); + } + if abs_tick & 0x8 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"ffe5caca7e10e4e61c3624eaa0941cd0", 16).unwrap()) + .shr(128); + } + if abs_tick & 0x10 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"ffcb9843d60f6159c9db58835c926644", 16).unwrap()) + .shr(128); + } + if abs_tick & 0x20 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"ff973b41fa98c081472e6896dfb254c0", 16).unwrap()) + .shr(128); + } + if abs_tick & 0x40 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"ff2ea16466c96a3843ec78b326b52861", 16).unwrap()) + .shr(128); + } + if abs_tick & 0x80 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"fe5dee046a99a2a811c461f1969c3053", 16).unwrap()) + .shr(128); + } + if abs_tick & 0x100 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"fcbe86c7900a88aedcffc83b479aa3a4", 16).unwrap()) + .shr(128); + } + if abs_tick & 0x200 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"f987a7253ac413176f2b074cf7815e54", 16).unwrap()) + .shr(128); + } + if abs_tick & 0x400 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"f3392b0822b70005940c7a398e4b70f3", 16).unwrap()) + .shr(128); + } + if abs_tick & 0x800 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"e7159475a2c29b7443b29c7fa6e889d9", 16).unwrap()) + .shr(128); + } + if abs_tick & 0x1000 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"d097f3bdfd2022b8845ad8f792aa5825", 16).unwrap()) + .shr(128); + } + if abs_tick & 0x2000 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"a9f746462d870fdf8a65dc1f90e061e5", 16).unwrap()) + .shr(128); + } + if abs_tick & 0x4000 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"70d869a156d2a1b890bb3df62baf32f7", 16).unwrap()) + .shr(128); + } + if abs_tick & 0x8000 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"31be135f97d08fd981231505542fcfa6", 16).unwrap()) + .shr(128); + } + if abs_tick & 0x10000 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"9aa508b5b7a84e1c677de54f3e99bc9", 16).unwrap()) + .shr(128); + } + if abs_tick & 0x20000 != 0 { + ratio = + (&ratio * BigInt::parse_bytes(b"5d6af8dedb81196699c329225ee604", 16).unwrap()).shr(128); + } + if abs_tick & 0x40000 != 0 { + ratio = + (&ratio * BigInt::parse_bytes(b"2216e584f5fa1ea926041bedfe98", 16).unwrap()).shr(128); + } + if abs_tick & 0x80000 != 0 { + ratio = (&ratio * BigInt::parse_bytes(b"48a170391f7dc42444e8fa2", 16).unwrap()).shr(128); + } + + if tick > 0 { + let max = (BigInt::from(1) << 256) - 1; + ratio = max / ratio; + } + // Add 2^32 - 1 and shift right by 32 + ratio = (ratio + ((BigInt::from(1) << 32) - 1)) >> 32; + + Ok(ratio) +} + +/// Helper that gets signed token0 delta +/// Source: https://github.com/shuhuiluo/uniswap-v3-sdk-rs/blob/v2.9.1/src/utils/sqrt_price_math.rs#L422 +/// +/// ## Arguments +/// +/// * `sqrt_ratio_a_x96`: A sqrt price +/// * `sqrt_ratio_b_x96`: Another sqrt price +/// * `liquidity`: The change in liquidity for which to compute the amount0 delta +/// +/// ## Returns +/// +/// Amount of token0 corresponding to the passed liquidityDelta between the two prices +fn get_amount_0_delta_signed( + sqrt_ratio_a_x96: BigInt, + sqrt_ratio_b_x96: BigInt, + liquidity: i128, +) -> Result { + let sign = !liquidity.is_negative(); + // Create mask for negative numbers + let mask = if sign { 0u128 } else { u128::MAX }; + + // Get absolute value of liquidity using XOR and addition + let liquidity = mask ^ mask.wrapping_add_signed(liquidity); + + // Convert mask to BigInt (all 1s or all 0s) + let mask = if sign { BigInt::from(0) } else { -BigInt::from(1) }; + + let amount_0 = get_amount_0_delta(sqrt_ratio_a_x96, sqrt_ratio_b_x96, liquidity, sign)?; + + // Apply the mask using XOR and subtraction to restore the sign + Ok((amount_0 ^ &mask) - mask) +} + +/// Gets the amount0 delta between two prices +/// +/// Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper), +/// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower)) +/// +/// ## Arguments +/// +/// * `sqrt_ratio_a_x96`: A sqrt price assumed to be lower otherwise swapped +/// * `sqrt_ratio_b_x96`: Another sqrt price +/// * `liquidity`: The amount of usable liquidity +/// * `round_up`: Whether to round the amount up or down +/// +/// ## Returns +/// +/// Amount of token0 required to cover a position of size liquidity between the two passed prices +fn get_amount_0_delta( + sqrt_ratio_a_x96: BigInt, + sqrt_ratio_b_x96: BigInt, + liquidity: u128, + round_up: bool, +) -> Result { + let (sqrt_ratio_a_x96, sqrt_ratio_b_x96) = if sqrt_ratio_a_x96 < sqrt_ratio_b_x96 { + (sqrt_ratio_a_x96, sqrt_ratio_b_x96) + } else { + (sqrt_ratio_b_x96, sqrt_ratio_a_x96) + }; + + if sqrt_ratio_a_x96 == BigInt::from(0) { + return Err("Price cannot be zero".to_string()); + } + + let numerator_1 = BigInt::from(liquidity) << 96; + let numerator_2 = &sqrt_ratio_b_x96 - &sqrt_ratio_a_x96; + + if round_up { + // For rounding up: ceil(ceil(numerator_1 * numerator_2 / sqrt_ratio_b_x96) / + // sqrt_ratio_a_x96) + let temp = + (&numerator_1 * &numerator_2 + &sqrt_ratio_b_x96 - BigInt::from(1)) / &sqrt_ratio_b_x96; + Ok((&temp + &sqrt_ratio_a_x96 - BigInt::from(1)) / sqrt_ratio_a_x96) + } else { + // For rounding down: floor(floor(numerator_1 * numerator_2 / sqrt_ratio_b_x96) / + // sqrt_ratio_a_x96) + Ok((&numerator_1 * &numerator_2) / &sqrt_ratio_b_x96 / sqrt_ratio_a_x96) + } +} + +const Q96: u128 = 1 << 96; + +/// Helper that gets signed token1 delta +/// +/// ## Arguments +/// +/// * `sqrt_ratio_a_x96`: A sqrt price +/// * `sqrt_ratio_b_x96`: Another sqrt price +/// * `liquidity`: The change in liquidity for which to compute the amount1 delta +/// +/// ## Returns +/// +/// Amount of token1 corresponding to the passed liquidityDelta between the two prices +fn get_amount_1_delta_signed( + sqrt_ratio_a_x96: BigInt, + sqrt_ratio_b_x96: BigInt, + liquidity: i128, +) -> Result { + let sign = !liquidity.is_negative(); + + // Create mask for negative numbers + let mask = if sign { 0u128 } else { u128::MAX }; + + // Get absolute value of liquidity using XOR and addition + let liquidity = mask ^ mask.wrapping_add_signed(liquidity); + + // Convert mask to BigInt (all 1s or all 0s) + let mask = if sign { BigInt::from(0) } else { -BigInt::from(1) }; + + let amount_1 = get_amount_1_delta(sqrt_ratio_a_x96, sqrt_ratio_b_x96, liquidity, sign)?; + + // Apply the mask using XOR and subtraction to restore the sign + Ok((amount_1 ^ &mask) - mask) +} + +/// Gets the amount1 delta between two prices +/// +/// Calculates liquidity * (sqrt(upper) - sqrt(lower)) +/// +/// ## Arguments +/// +/// * `sqrt_ratio_a_x96`: A sqrt price assumed to be lower otherwise swapped +/// * `sqrt_ratio_b_x96`: Another sqrt price +/// * `liquidity`: The amount of usable liquidity +/// * `round_up`: Whether to round the amount up, or down +/// +/// ## Returns +/// +/// Amount of token1 required to cover a position of size liquidity between the two passed prices +fn get_amount_1_delta( + sqrt_ratio_a_x96: BigInt, + sqrt_ratio_b_x96: BigInt, + liquidity: u128, + round_up: bool, +) -> Result { + let (sqrt_ratio_a_x96, sqrt_ratio_b_x96) = if sqrt_ratio_a_x96 < sqrt_ratio_b_x96 { + (sqrt_ratio_a_x96, sqrt_ratio_b_x96) + } else { + (sqrt_ratio_b_x96, sqrt_ratio_a_x96) + }; + + let numerator = &sqrt_ratio_b_x96 - &sqrt_ratio_a_x96; + let denominator = BigInt::from(Q96); + + let liquidity = BigInt::from(liquidity); + let amount_1 = &liquidity * &numerator / &denominator; + + // Calculate if there's a remainder + let remainder = (&liquidity * &numerator) % &denominator; + let carry = remainder > BigInt::from(0) && round_up; + + Ok(if carry { amount_1 + 1 } else { amount_1 }) +} + +#[cfg(test)] +mod tests { + use super::*; + use num_bigint::BigInt; + use rstest::rstest; + use std::str::FromStr; + const MIN_TICK: i32 = -887272; + + #[test] + fn test_get_sqrt_ratio_at_tick_is_valid_min_tick() { + assert_eq!(get_sqrt_ratio_at_tick(MIN_TICK).unwrap(), BigInt::from(4295128739_u128)); + } + + #[test] + fn test_get_sqrt_ratio_at_tick_is_valid_min_tick_add_one() { + assert_eq!(get_sqrt_ratio_at_tick(MIN_TICK + 1).unwrap(), BigInt::from(4295343490_u128)); + } + + #[test] + fn test_get_sqrt_ratio_at_tick_is_valid_max_tick() { + assert_eq!( + get_sqrt_ratio_at_tick(MAX_TICK).unwrap(), + BigInt::from_str("1461446703485210103287273052203988822378723970342").unwrap() + ); + } + + #[test] + fn test_get_sqrt_ratio_at_tick_is_valid_max_tick_sub_one() { + assert_eq!( + get_sqrt_ratio_at_tick(MAX_TICK - 1).unwrap(), + BigInt::from_str("1461373636630004318706518188784493106690254656249").unwrap() + ); + } + + #[test] + fn test_get_sqrt_ratio_at_tick_is_valid_tick_zero() { + assert_eq!( + get_sqrt_ratio_at_tick(0).unwrap(), + BigInt::from_str("79228162514264337593543950336").unwrap() + ); + } + + #[test] + fn test_get_sqrt_ratio_at_tick_is_less_than_js_impl_min_tick() { + let js_min_sqrt_price = BigInt::from(6085630636u64); + let sol_min_sqrt_price = get_sqrt_ratio_at_tick(MIN_TICK).unwrap(); + assert!(sol_min_sqrt_price < js_min_sqrt_price); + } + + #[test] + fn test_get_sqrt_ratio_at_tick_is_greater_than_js_impl_max_tick() { + let js_max_sqrt_price = + BigInt::from_str("1033437718471923706666374484006904511252097097914").unwrap(); + let sol_max_sqrt_price = get_sqrt_ratio_at_tick(MAX_TICK).unwrap(); + assert!(sol_max_sqrt_price > js_max_sqrt_price); + } + #[test] + fn test_get_amount_0_delta_returns_0_if_liquidity_is_0() { + let result = get_amount_0_delta( + BigInt::from_str("79228162514264337593543950336").unwrap(), + BigInt::from_str("112045541949572279837463876454").unwrap(), + 0, + true, + ) + .unwrap(); + assert_eq!(result, BigInt::from(0)); + } + + #[test] + fn test_get_amount_0_delta_returns_0_if_prices_are_equal() { + let result = get_amount_0_delta( + BigInt::from_str("79228162514264337593543950336").unwrap(), + BigInt::from_str("79228162514264337593543950336").unwrap(), + 0, + true, + ) + .unwrap(); + assert_eq!(result, BigInt::from(0)); + } + + #[test] + fn test_get_amount_0_delta_reverts_if_price_is_zero() { + let result = get_amount_0_delta(BigInt::from(0), BigInt::from(1), 1, true); + assert!(result.is_err()); + } + + #[test] + fn test_get_amount_0_delta_1_amount_1_for_price_of_1_to_1_21() { + let sqrt_price_1_1 = BigInt::from_str("79228162514264337593543950336").unwrap(); + let sqrt_price_121_100 = BigInt::from_str("87150978765690771352898345369").unwrap(); + + let amount_0 = get_amount_0_delta( + sqrt_price_1_1.clone(), + sqrt_price_121_100.clone(), + 1_000_000_000_000_000_000, + true, + ) + .unwrap(); + assert_eq!(amount_0, BigInt::from(90_909_090_909_090_910u128)); + + let amount_0_rounded_down = get_amount_0_delta( + sqrt_price_1_1, + sqrt_price_121_100, + 1_000_000_000_000_000_000, + false, + ) + .unwrap(); + assert_eq!(amount_0_rounded_down, amount_0 - 1); + } + + #[test] + fn test_get_amount_0_delta_works_for_prices_that_overflow() { + let sqrt_p_1 = BigInt::from_str("2787593149816327892691964784081045188247552").unwrap(); + let sqrt_p_2 = BigInt::from_str("22300745198530623141535718272648361505980416").unwrap(); + + let amount_0_up = + get_amount_0_delta(sqrt_p_1.clone(), sqrt_p_2.clone(), 1_000_000_000_000_000_000, true) + .unwrap(); + let amount_0_down = + get_amount_0_delta(sqrt_p_1, sqrt_p_2, 1_000_000_000_000_000_000, false).unwrap(); + + assert_eq!(amount_0_up, amount_0_down + 1); + } + + #[rstest] + #[case( + BigInt::from(79228162514264337593543950336_i128), + -887270, + 887270, + 2779504125, + BigInt::from(2779504125_u128), + BigInt::from(2779504125_u128) + )] + #[case( + BigInt::from(17189630842187678489986852982138_i128), + -138200, + 107600, + -79381057257465377800177, + BigInt::from(-445577797388351_i128), + BigInt::from(-17222724212376326765220041_i128) + )] + fn test_calculate_token_amounts( + #[case] current_sqrtprice: BigInt, + #[case] tick_lower: i32, + #[case] tick_upper: i32, + #[case] liquidity_delta: i128, + #[case] expected_amount0: BigInt, + #[case] expected_amount1: BigInt, + ) { + let (amount0, amount1) = + calculate_token_amounts(current_sqrtprice, tick_lower, tick_upper, liquidity_delta) + .unwrap(); + + assert_eq!(amount0, expected_amount0); + assert_eq!(amount1, expected_amount1); + } +} diff --git a/substreams/ethereum-uniswap-v4/src/pb/mod.rs b/substreams/ethereum-uniswap-v4/src/pb/mod.rs new file mode 100644 index 0000000..ce1c554 --- /dev/null +++ b/substreams/ethereum-uniswap-v4/src/pb/mod.rs @@ -0,0 +1,8 @@ +// @generated +pub mod uniswap { + // @@protoc_insertion_point(attribute:uniswap.v4) + pub mod v4 { + include!("uniswap.v4.rs"); + // @@protoc_insertion_point(uniswap.v4) + } +} diff --git a/substreams/ethereum-uniswap-v4/src/pb/uniswap.v4.rs b/substreams/ethereum-uniswap-v4/src/pb/uniswap.v4.rs new file mode 100644 index 0000000..cbb729a --- /dev/null +++ b/substreams/ethereum-uniswap-v4/src/pb/uniswap.v4.rs @@ -0,0 +1,228 @@ +// @generated +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Pool { + /// // The pool address. + #[prost(bytes="vec", tag="1")] + pub id: ::prost::alloc::vec::Vec, + /// The token0 address. + #[prost(bytes="vec", tag="2")] + pub currency0: ::prost::alloc::vec::Vec, + /// The token1 address. + #[prost(bytes="vec", tag="3")] + pub currency1: ::prost::alloc::vec::Vec, + /// The transaction where the pool was created. + #[prost(bytes="vec", tag="4")] + pub created_tx_hash: ::prost::alloc::vec::Vec, +} +/// A struct describing a transaction. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Transaction { + /// The transaction hash. + #[prost(bytes="vec", tag="1")] + pub hash: ::prost::alloc::vec::Vec, + /// The sender of the transaction. + #[prost(bytes="vec", tag="2")] + pub from: ::prost::alloc::vec::Vec, + /// The receiver of the transaction. + #[prost(bytes="vec", tag="3")] + pub to: ::prost::alloc::vec::Vec, + /// The transactions index within the block. + #[prost(uint64, tag="4")] + pub index: u64, +} +/// A change to a pool's tick. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TickDelta { + /// The address of the pool. + #[prost(bytes="vec", tag="1")] + pub pool_address: ::prost::alloc::vec::Vec, + /// The index of the tick. + #[prost(int32, tag="2")] + pub tick_index: i32, + /// The liquidity net delta of this tick. Bigint encoded as signed little endian bytes. + #[prost(bytes="vec", tag="3")] + pub liquidity_net_delta: ::prost::alloc::vec::Vec, + /// Used to determine the order of the balance changes. Necessary for the balance store. + #[prost(uint64, tag="4")] + pub ordinal: u64, + #[prost(message, optional, tag="5")] + pub transaction: ::core::option::Option, +} +/// A group of TickDelta +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TickDeltas { + #[prost(message, repeated, tag="1")] + pub deltas: ::prost::alloc::vec::Vec, +} +/// A change to a pool's liquidity. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LiquidityChange { + /// The address of the pool. + #[prost(bytes="vec", tag="1")] + pub pool_address: ::prost::alloc::vec::Vec, + /// The liquidity changed amount. Bigint encoded as signed little endian bytes. + #[prost(bytes="vec", tag="2")] + pub value: ::prost::alloc::vec::Vec, + /// The type of update, can be absolute or delta. + #[prost(enumeration="LiquidityChangeType", tag="3")] + pub change_type: i32, + /// Used to determine the order of the balance changes. Necessary for the balance store. + #[prost(uint64, tag="4")] + pub ordinal: u64, + #[prost(message, optional, tag="5")] + pub transaction: ::core::option::Option, +} +/// A group of LiquidityChange +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LiquidityChanges { + #[prost(message, repeated, tag="1")] + pub changes: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Events { + #[prost(message, repeated, tag="3")] + pub pool_events: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `Events`. +pub mod events { + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct PoolEvent { + #[prost(uint64, tag="100")] + pub log_ordinal: u64, + /// Changed from pool_address to pool_id as V4 uses PoolId + #[prost(string, tag="102")] + pub pool_id: ::prost::alloc::string::String, + /// Changed from token0 to currency0 + #[prost(string, tag="103")] + pub currency0: ::prost::alloc::string::String, + /// Changed from token1 to currency1 + #[prost(string, tag="104")] + pub currency1: ::prost::alloc::string::String, + #[prost(message, optional, tag="105")] + pub transaction: ::core::option::Option, + #[prost(oneof="pool_event::Type", tags="1, 2, 3, 4, 5")] + pub r#type: ::core::option::Option, + } + /// Nested message and enum types in `PoolEvent`. + pub mod pool_event { + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct Initialize { + #[prost(string, tag="1")] + pub sqrt_price_x96: ::prost::alloc::string::String, + #[prost(int32, tag="2")] + pub tick: i32, + #[prost(uint32, tag="3")] + pub fee: u32, + #[prost(int32, tag="4")] + pub tick_spacing: i32, + /// Address of the hooks contract + #[prost(string, tag="5")] + pub hooks: ::prost::alloc::string::String, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct ModifyLiquidity { + #[prost(string, tag="1")] + pub sender: ::prost::alloc::string::String, + #[prost(int32, tag="2")] + pub tick_lower: i32, + #[prost(int32, tag="3")] + pub tick_upper: i32, + /// Changed to support signed integers + #[prost(string, tag="4")] + pub liquidity_delta: ::prost::alloc::string::String, + /// Added salt field + #[prost(string, tag="5")] + pub salt: ::prost::alloc::string::String, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct Swap { + #[prost(string, tag="1")] + pub sender: ::prost::alloc::string::String, + /// Signed int128 + #[prost(string, tag="2")] + pub amount0: ::prost::alloc::string::String, + /// Signed int128 + #[prost(string, tag="3")] + pub amount1: ::prost::alloc::string::String, + #[prost(string, tag="4")] + pub sqrt_price_x96: ::prost::alloc::string::String, + #[prost(string, tag="5")] + pub liquidity: ::prost::alloc::string::String, + #[prost(int32, tag="6")] + pub tick: i32, + /// Added fee field + #[prost(uint32, tag="7")] + pub fee: u32, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct Donate { + #[prost(string, tag="1")] + pub sender: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub amount0: ::prost::alloc::string::String, + #[prost(string, tag="3")] + pub amount1: ::prost::alloc::string::String, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct ProtocolFeeUpdated { + #[prost(string, tag="1")] + pub pool_id: ::prost::alloc::string::String, + #[prost(uint32, tag="2")] + pub protocol_fee: u32, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Type { + #[prost(message, tag="1")] + Initialize(Initialize), + #[prost(message, tag="2")] + ModifyLiquidity(ModifyLiquidity), + #[prost(message, tag="3")] + Swap(Swap), + #[prost(message, tag="4")] + Donate(Donate), + #[prost(message, tag="5")] + ProtocolFeeUpdated(ProtocolFeeUpdated), + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum LiquidityChangeType { + Delta = 0, + Absolute = 1, +} +impl LiquidityChangeType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + LiquidityChangeType::Delta => "DELTA", + LiquidityChangeType::Absolute => "ABSOLUTE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "DELTA" => Some(Self::Delta), + "ABSOLUTE" => Some(Self::Absolute), + _ => None, + } + } +} +// @@protoc_insertion_point(module) diff --git a/substreams/rust-toolchain.toml b/substreams/rust-toolchain.toml new file mode 100644 index 0000000..15b4897 --- /dev/null +++ b/substreams/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "1.83.0" +components = [ "rustfmt" ] +targets = [ "wasm32-unknown-unknown" ] diff --git a/substreams/rustfmt.toml b/substreams/rustfmt.toml index 1c06912..42cdd4f 100644 --- a/substreams/rustfmt.toml +++ b/substreams/rustfmt.toml @@ -18,4 +18,5 @@ ignore = [ "ethereum-uniswap-v2/src/abi", "ethereum-uniswap-v3/src/abi", "ethereum-uniswap-v3-logs-only/src/abi", + "ethereum-uniswap-v4/src/abi", ]