From 9f231e8c32081d5d5533a81c6bd8106a8eb9797f Mon Sep 17 00:00:00 2001 From: starkbamse <139136798+starkbamse@users.noreply.github.com> Date: Sat, 18 May 2024 23:56:49 +0200 Subject: [PATCH 1/2] Added eip1559 switch to allow specification of Todo: refactor it to an enum so that we have: eip1559 and legacy for now to support major L2s and bsc --- Cargo.lock | 984 +---------------------------- Cargo.toml | 1 - src/gateway/mod.rs | 2 +- src/lib.rs | 2 + src/transfers/gas_transfers/mod.rs | 52 +- src/types/mod.rs | 38 +- 6 files changed, 99 insertions(+), 980 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9fb5115..9eff786 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,7 +16,6 @@ dependencies = [ name = "acceptevm" version = "0.0.3" dependencies = [ - "alloy", "async-std", "bincode", "ethers", @@ -58,18 +57,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -79,534 +66,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "allocator-api2" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" - -[[package]] -name = "alloy" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#35cbf35164f31d2de1b84b2a8a9986e5b9b1560f" -dependencies = [ - "alloy-consensus", - "alloy-contract", - "alloy-core", - "alloy-eips", - "alloy-genesis", - "alloy-network", - "alloy-provider", - "alloy-rpc-client", - "alloy-rpc-types", - "alloy-serde", - "alloy-signer", - "alloy-signer-wallet", - "alloy-transport", - "alloy-transport-http", - "reqwest 0.12.4", -] - -[[package]] -name = "alloy-consensus" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#35cbf35164f31d2de1b84b2a8a9986e5b9b1560f" -dependencies = [ - "alloy-eips", - "alloy-primitives", - "alloy-rlp", - "alloy-serde", - "c-kzg", - "serde", -] - -[[package]] -name = "alloy-contract" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#35cbf35164f31d2de1b84b2a8a9986e5b9b1560f" -dependencies = [ - "alloy-dyn-abi", - "alloy-json-abi", - "alloy-network", - "alloy-primitives", - "alloy-provider", - "alloy-rpc-types", - "alloy-sol-types", - "alloy-transport", - "futures", - "futures-util", - "thiserror", -] - -[[package]] -name = "alloy-core" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e30b83573b348305b9629a094b5331093a030514cd5713433799495cb283fea1" -dependencies = [ - "alloy-dyn-abi", - "alloy-json-abi", - "alloy-primitives", - "alloy-sol-types", -] - -[[package]] -name = "alloy-dyn-abi" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545885d9b0b2c30fd344ae291439b4bfe59e48dd62fbc862f8503d98088967dc" -dependencies = [ - "alloy-json-abi", - "alloy-primitives", - "alloy-sol-type-parser", - "alloy-sol-types", - "const-hex", - "itoa", - "serde", - "serde_json", - "winnow 0.6.8", -] - -[[package]] -name = "alloy-eips" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#35cbf35164f31d2de1b84b2a8a9986e5b9b1560f" -dependencies = [ - "alloy-primitives", - "alloy-rlp", - "alloy-serde", - "c-kzg", - "once_cell", - "serde", - "sha2", -] - -[[package]] -name = "alloy-genesis" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#35cbf35164f31d2de1b84b2a8a9986e5b9b1560f" -dependencies = [ - "alloy-primitives", - "alloy-serde", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-json-abi" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786689872ec4e7d354810ab0dffd48bb40b838c047522eb031cbd47d15634849" -dependencies = [ - "alloy-primitives", - "alloy-sol-type-parser", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-json-rpc" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#35cbf35164f31d2de1b84b2a8a9986e5b9b1560f" -dependencies = [ - "alloy-primitives", - "serde", - "serde_json", - "thiserror", - "tracing", -] - -[[package]] -name = "alloy-network" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#35cbf35164f31d2de1b84b2a8a9986e5b9b1560f" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-json-rpc", - "alloy-primitives", - "alloy-rpc-types", - "alloy-signer", - "alloy-sol-types", - "async-trait", - "futures-utils-wasm", - "thiserror", -] - -[[package]] -name = "alloy-primitives" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525448f6afc1b70dd0f9d0a8145631bf2f5e434678ab23ab18409ca264cae6b3" -dependencies = [ - "alloy-rlp", - "bytes", - "cfg-if", - "const-hex", - "derive_more", - "hex-literal", - "itoa", - "k256", - "keccak-asm", - "proptest", - "rand", - "ruint", - "serde", - "tiny-keccak", -] - -[[package]] -name = "alloy-provider" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#35cbf35164f31d2de1b84b2a8a9986e5b9b1560f" -dependencies = [ - "alloy-eips", - "alloy-json-rpc", - "alloy-network", - "alloy-primitives", - "alloy-rpc-client", - "alloy-rpc-types", - "alloy-rpc-types-trace", - "alloy-transport", - "alloy-transport-http", - "async-stream", - "async-trait", - "auto_impl", - "dashmap", - "futures", - "futures-utils-wasm", - "lru", - "reqwest 0.12.4", - "serde_json", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "alloy-rlp" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" -dependencies = [ - "alloy-rlp-derive", - "arrayvec", - "bytes", -] - -[[package]] -name = "alloy-rlp-derive" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.60", -] - -[[package]] -name = "alloy-rpc-client" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#35cbf35164f31d2de1b84b2a8a9986e5b9b1560f" -dependencies = [ - "alloy-json-rpc", - "alloy-transport", - "alloy-transport-http", - "futures", - "pin-project", - "reqwest 0.12.4", - "serde", - "serde_json", - "tokio", - "tokio-stream", - "tower", - "tracing", - "url", -] - -[[package]] -name = "alloy-rpc-types" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#35cbf35164f31d2de1b84b2a8a9986e5b9b1560f" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-genesis", - "alloy-primitives", - "alloy-rlp", - "alloy-serde", - "alloy-sol-types", - "itertools 0.12.1", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "alloy-rpc-types-trace" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#35cbf35164f31d2de1b84b2a8a9986e5b9b1560f" -dependencies = [ - "alloy-primitives", - "alloy-rpc-types", - "alloy-serde", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-serde" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#35cbf35164f31d2de1b84b2a8a9986e5b9b1560f" -dependencies = [ - "alloy-primitives", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-signer" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#35cbf35164f31d2de1b84b2a8a9986e5b9b1560f" -dependencies = [ - "alloy-primitives", - "async-trait", - "auto_impl", - "elliptic-curve", - "k256", - "thiserror", -] - -[[package]] -name = "alloy-signer-wallet" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#35cbf35164f31d2de1b84b2a8a9986e5b9b1560f" -dependencies = [ - "alloy-consensus", - "alloy-network", - "alloy-primitives", - "alloy-signer", - "async-trait", - "k256", - "rand", - "thiserror", -] - -[[package]] -name = "alloy-sol-macro" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89c80a2cb97e7aa48611cbb63950336f9824a174cdf670527cc6465078a26ea1" -dependencies = [ - "alloy-json-abi", - "alloy-sol-macro-input", - "const-hex", - "heck 0.4.1", - "indexmap", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.60", - "syn-solidity", - "tiny-keccak", -] - -[[package]] -name = "alloy-sol-macro-input" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58894b58ac50979eeac6249661991ac40b9d541830d9a725f7714cc9ef08c23" -dependencies = [ - "alloy-json-abi", - "const-hex", - "dunce", - "heck 0.5.0", - "proc-macro2", - "quote", - "serde_json", - "syn 2.0.60", - "syn-solidity", -] - -[[package]] -name = "alloy-sol-type-parser" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8e71ea68e780cc203919e03f69f59e7afe92d2696fb1dcb6662f61e4031b6" -dependencies = [ - "winnow 0.6.8", -] - -[[package]] -name = "alloy-sol-types" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399287f68d1081ed8b1f4903c49687658b95b142207d7cb4ae2f4813915343ef" -dependencies = [ - "alloy-json-abi", - "alloy-primitives", - "alloy-sol-macro", - "const-hex", - "serde", -] - -[[package]] -name = "alloy-transport" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#35cbf35164f31d2de1b84b2a8a9986e5b9b1560f" -dependencies = [ - "alloy-json-rpc", - "base64 0.22.1", - "futures-util", - "futures-utils-wasm", - "serde", - "serde_json", - "thiserror", - "tokio", - "tower", - "url", - "wasm-bindgen-futures", -] - -[[package]] -name = "alloy-transport-http" -version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#35cbf35164f31d2de1b84b2a8a9986e5b9b1560f" -dependencies = [ - "alloy-json-rpc", - "alloy-transport", - "reqwest 0.12.4", - "serde_json", - "tower", - "tracing", - "url", -] - -[[package]] -name = "ark-ff" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" -dependencies = [ - "ark-ff-asm 0.3.0", - "ark-ff-macros 0.3.0", - "ark-serialize 0.3.0", - "ark-std 0.3.0", - "derivative", - "num-bigint", - "num-traits", - "paste", - "rustc_version 0.3.3", - "zeroize", -] - -[[package]] -name = "ark-ff" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" -dependencies = [ - "ark-ff-asm 0.4.2", - "ark-ff-macros 0.4.2", - "ark-serialize 0.4.2", - "ark-std 0.4.0", - "derivative", - "digest 0.10.7", - "itertools 0.10.5", - "num-bigint", - "num-traits", - "paste", - "rustc_version 0.4.0", - "zeroize", -] - -[[package]] -name = "ark-ff-asm" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-asm" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-macros" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" -dependencies = [ - "num-bigint", - "num-traits", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-macros" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" -dependencies = [ - "num-bigint", - "num-traits", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-serialize" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" -dependencies = [ - "ark-std 0.3.0", - "digest 0.9.0", -] - -[[package]] -name = "ark-serialize" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" -dependencies = [ - "ark-std 0.4.0", - "digest 0.10.7", - "num-bigint", -] - -[[package]] -name = "ark-std" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" -dependencies = [ - "num-traits", - "rand", -] - -[[package]] -name = "ark-std" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" -dependencies = [ - "num-traits", - "rand", -] - [[package]] name = "arrayvec" version = "0.7.4" @@ -758,28 +217,6 @@ dependencies = [ "wasm-bindgen-futures", ] -[[package]] -name = "async-stream" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.60", -] - [[package]] name = "async-task" version = "4.7.1" @@ -805,7 +242,7 @@ checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" dependencies = [ "futures", "pharos", - "rustc_version 0.4.0", + "rustc_version", ] [[package]] @@ -950,19 +387,7 @@ dependencies = [ "async-task", "futures-io", "futures-lite 2.3.0", - "piper", -] - -[[package]] -name = "blst" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94087b935a822949d3291a9989ad2b2051ea141eda0fd4e478a75f6aa3e604b" -dependencies = [ - "cc", - "glob", - "threadpool", - "zeroize", + "piper", ] [[package]] @@ -1023,20 +448,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "c-kzg" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3130f3d8717cc02e668a896af24984d5d5d4e8bf12e278e982e0f1bd88a0f9af" -dependencies = [ - "blst", - "cc", - "glob", - "hex", - "libc", - "serde", -] - [[package]] name = "camino" version = "1.1.7" @@ -1063,7 +474,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver 1.0.22", + "semver", "serde", "serde_json", "thiserror", @@ -1113,7 +524,7 @@ checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3" dependencies = [ "bs58", "coins-core", - "digest 0.10.7", + "digest", "hmac", "k256", "serde", @@ -1146,7 +557,7 @@ dependencies = [ "base64 0.21.7", "bech32", "bs58", - "digest 0.10.7", + "digest", "generic-array", "hex", "ripemd", @@ -1191,12 +602,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "core-foundation" version = "0.9.4" @@ -1293,19 +698,6 @@ dependencies = [ "cipher", ] -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown", - "lock_api", - "once_cell", - "parking_lot_core 0.9.10", -] - [[package]] name = "data-encoding" version = "2.6.0" @@ -1331,39 +723,17 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "derive_more" version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "convert_case", "proc-macro2", "quote", - "rustc_version 0.4.0", "syn 1.0.109", ] -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - [[package]] name = "digest" version = "0.10.7" @@ -1431,7 +801,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", - "digest 0.10.7", + "digest", "elliptic-curve", "rfc6979", "signature", @@ -1452,7 +822,7 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest 0.10.7", + "digest", "ff", "generic-array", "group", @@ -1523,7 +893,7 @@ checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" dependencies = [ "aes", "ctr", - "digest 0.10.7", + "digest", "hex", "hmac", "pbkdf2 0.11.0", @@ -1711,7 +1081,7 @@ dependencies = [ "chrono", "ethers-core", "reqwest 0.11.27", - "semver 1.0.22", + "semver", "serde", "serde_json", "thiserror", @@ -1820,7 +1190,7 @@ dependencies = [ "path-slash", "rayon", "regex", - "semver 1.0.22", + "semver", "serde", "serde_json", "solang-parser", @@ -1906,17 +1276,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" -[[package]] -name = "fastrlp" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" -dependencies = [ - "arrayvec", - "auto_impl", - "bytes", -] - [[package]] name = "ff" version = "0.13.0" @@ -2138,12 +1497,6 @@ dependencies = [ "slab", ] -[[package]] -name = "futures-utils-wasm" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" - [[package]] name = "fxhash" version = "0.2.1" @@ -2253,10 +1606,6 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] [[package]] name = "hashers" @@ -2273,12 +1622,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - [[package]] name = "hermit-abi" version = "0.3.9" @@ -2290,15 +1633,6 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] - -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "hmac" @@ -2306,7 +1640,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] @@ -2579,15 +1913,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.11.0" @@ -2597,15 +1922,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.11" @@ -2667,16 +1983,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "keccak-asm" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb8515fff80ed850aea4a1595f2e519c003e2a00a82fe168ebf5269196caf444" -dependencies = [ - "digest 0.10.7", - "sha3-asm", -] - [[package]] name = "kv-log-macro" version = "1.0.7" @@ -2695,7 +2001,7 @@ dependencies = [ "ascii-canvas", "bit-set", "ena", - "itertools 0.11.0", + "itertools", "lalrpop-util", "petgraph", "regex", @@ -2775,15 +2081,6 @@ dependencies = [ "value-bag", ] -[[package]] -name = "lru" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" -dependencies = [ - "hashbrown", -] - [[package]] name = "md-5" version = "0.10.6" @@ -2791,7 +2088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", - "digest 0.10.7", + "digest", ] [[package]] @@ -3097,12 +2394,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - [[package]] name = "path-slash" version = "0.2.1" @@ -3115,7 +2406,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.7", + "digest", "hmac", "password-hash", "sha2", @@ -3127,7 +2418,7 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "digest 0.10.7", + "digest", "hmac", ] @@ -3146,17 +2437,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "pest" -version = "2.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" -dependencies = [ - "memchr", - "thiserror", - "ucd-trie", -] - [[package]] name = "petgraph" version = "0.6.5" @@ -3174,7 +2454,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" dependencies = [ "futures", - "rustc_version 0.4.0", + "rustc_version", ] [[package]] @@ -3380,30 +2660,6 @@ dependencies = [ "toml_edit 0.20.2", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" version = "1.0.81" @@ -3419,8 +2675,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ - "bit-set", - "bit-vec", "bitflags 2.5.0", "lazy_static", "num-traits", @@ -3428,17 +2682,9 @@ dependencies = [ "rand_chacha", "rand_xorshift", "regex-syntax", - "rusty-fork", - "tempfile", "unarray", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" version = "1.0.36" @@ -3700,7 +2946,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "digest 0.10.7", + "digest", ] [[package]] @@ -3725,36 +2971,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "ruint" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f308135fef9fc398342da5472ce7c484529df23743fb7c734e0f3d472971e62" -dependencies = [ - "alloy-rlp", - "ark-ff 0.3.0", - "ark-ff 0.4.2", - "bytes", - "fastrlp", - "num-bigint", - "num-traits", - "parity-scale-codec", - "primitive-types", - "proptest", - "rand", - "rlp", - "ruint-macro", - "serde", - "valuable", - "zeroize", -] - -[[package]] -name = "ruint-macro" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86854cf50259291520509879a5c294c3c9a4c334e9ff65071c51e42ef1e2343" - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -3767,22 +2983,13 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver 0.11.0", -] - [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.22", + "semver", ] [[package]] @@ -3865,18 +3072,6 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" -[[package]] -name = "rusty-fork" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" -dependencies = [ - "fnv", - "quick-error", - "tempfile", - "wait-timeout", -] - [[package]] name = "ryu" version = "1.0.17" @@ -3999,15 +3194,6 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.22" @@ -4017,15 +3203,6 @@ dependencies = [ "serde", ] -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - [[package]] name = "send_wrapper" version = "0.4.0" @@ -4098,7 +3275,7 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] [[package]] @@ -4109,7 +3286,7 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.7", + "digest", ] [[package]] @@ -4118,27 +3295,17 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest 0.10.7", + "digest", "keccak", ] -[[package]] -name = "sha3-asm" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac61da6b35ad76b195eb4771210f947734321a8d81d7738e1580d953bc7a15e" -dependencies = [ - "cc", - "cfg-if", -] - [[package]] name = "signature" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" dependencies = [ - "digest 0.10.7", + "digest", "rand_core", ] @@ -4217,7 +3384,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c425ce1c59f4b154717592f0bdf4715c3a1d55058883622d3157e1f0908a5b26" dependencies = [ - "itertools 0.11.0", + "itertools", "lalrpop", "lalrpop-util", "phf", @@ -4281,7 +3448,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", "rustversion", @@ -4305,7 +3472,7 @@ dependencies = [ "hex", "once_cell", "reqwest 0.11.27", - "semver 1.0.22", + "semver", "serde", "serde_json", "sha2", @@ -4336,18 +3503,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "syn-solidity" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa0cefd02f532035d83cfec82647c6eb53140b0485220760e669f4bad489e36" -dependencies = [ - "paste", - "proc-macro2", - "quote", - "syn 2.0.60", -] - [[package]] name = "sync_wrapper" version = "0.1.2" @@ -4424,15 +3579,6 @@ dependencies = [ "syn 2.0.60", ] -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - [[package]] name = "time" version = "0.3.36" @@ -4501,21 +3647,9 @@ dependencies = [ "num_cpus", "pin-project-lite", "socket2 0.5.7", - "tokio-macros", "windows-sys 0.48.0", ] -[[package]] -name = "tokio-macros" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.60", -] - [[package]] name = "tokio-native-tls" version = "0.3.1" @@ -4536,18 +3670,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-stream" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - [[package]] name = "tokio-tungstenite" version = "0.20.1" @@ -4606,7 +3728,7 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "toml_datetime", - "winnow 0.5.40", + "winnow", ] [[package]] @@ -4619,7 +3741,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.5.40", + "winnow", ] [[package]] @@ -4724,12 +3846,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "ucd-trie" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" - [[package]] name = "uint" version = "0.9.5" @@ -4823,12 +3939,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - [[package]] name = "value-bag" version = "1.9.0" @@ -4847,15 +3957,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - [[package]] name = "waker-fn" version = "1.2.0" @@ -5148,15 +4249,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winnow" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" -dependencies = [ - "memchr", -] - [[package]] name = "winreg" version = "0.50.0" @@ -5188,7 +4280,7 @@ dependencies = [ "js-sys", "log", "pharos", - "rustc_version 0.4.0", + "rustc_version", "send_wrapper 0.6.0", "thiserror", "wasm-bindgen", @@ -5211,26 +4303,6 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" -[[package]] -name = "zerocopy" -version = "0.7.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "087eca3c1eaf8c47b94d02790dd086cd594b912d2043d4de4bfdd466b3befb7c" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.60", -] - [[package]] name = "zeroize" version = "1.7.0" diff --git a/Cargo.toml b/Cargo.toml index 76face3..8ce3fd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -alloy = { git = "https://github.com/alloy-rs/alloy", version = "0.1.0",features=["rpc-types-eth","eips","signers","signer-wallet","consensus","network","providers","transports","transport-http","contract"] } bincode = "1.3.3" sled = "0.34.7" thiserror = "1.0.59" diff --git a/src/gateway/mod.rs b/src/gateway/mod.rs index b54581f..3dac802 100644 --- a/src/gateway/mod.rs +++ b/src/gateway/mod.rs @@ -36,7 +36,7 @@ pub struct PaymentGatewayConfiguration { pub reflector: Reflector, pub min_confirmations: usize, pub transfer_gas_limit: Option, - + pub use_eip1559: bool, } /// ## Reflector diff --git a/src/lib.rs b/src/lib.rs index 833827e..49d2477 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,8 @@ mod poller; mod transfers; pub mod types; +pub use common::DatabaseError; + #[cfg(test)] mod tests { use std::{fs, path::Path, str::FromStr}; diff --git a/src/transfers/gas_transfers/mod.rs b/src/transfers/gas_transfers/mod.rs index fc14a62..c2062b1 100644 --- a/src/transfers/gas_transfers/mod.rs +++ b/src/transfers/gas_transfers/mod.rs @@ -1,13 +1,17 @@ - -use ethers::{ middleware::SignerMiddleware, providers::Middleware, signers::LocalWallet, types::{TransactionRequest, U256}}; -use std::ops::Mul; use crate::{ gateway::{PaymentGateway, PaymentGatewayConfiguration}, + transfers::get_chain_id, types::Invoice, }; - -use super::{errors::TransferError, get_chain_id, get_gas_price}; - +use ethers::{ + middleware::SignerMiddleware, + providers::Middleware, + signers::{LocalWallet, Signer}, + types::{BlockId, Eip1559TransactionRequest, TransactionRequest, U256}, +}; +use std::ops::Mul; +use ethers::types::BlockNumber::Latest; +use super::{errors::TransferError, get_gas_price}; /// Creates a transaction to transfer gas from a paid invoice to a specified treasury address async fn create_transaction( @@ -15,29 +19,30 @@ async fn create_transaction( invoice: &Invoice, chain_id: U256, gas_price: U256, -) -> TransactionRequest { - +) -> Eip1559TransactionRequest { // Use specified gas limit or fallback let gas_limit = gateway_config.transfer_gas_limit.unwrap_or(21000); // Maximum cost of transaction let max_cost = gas_limit.mul(gas_price.as_u128()); + let balance_of_recipient = gateway_config + .provider + .get_balance(invoice.to, Some(BlockId::Number(Latest))) + .await.unwrap(); // Estimated gas left after transfer - let value = invoice.amount.saturating_sub(U256::from(max_cost)); - - TransactionRequest::default() + let value: U256 = invoice.amount; + println!("Value: {:?}", value); +gateway_config.provider.estimate_eip1559_fees(estimator) + Eip1559TransactionRequest::new() .from(invoice.to) .to(gateway_config.treasury_address) .nonce(0) .chain_id(chain_id.as_u64()) .gas(gas_limit) - .value(value) - .gas_price(gas_price) + .value(balance_of_recipient) } - - /// Transfers gas from a paid invoice to a specified treasury address pub async fn transfer_gas_to_treasury( gateway: PaymentGateway, @@ -47,11 +52,18 @@ pub async fn transfer_gas_to_treasury( let chain_id = get_chain_id(gateway.config.provider.clone()).await?; let gas_price = get_gas_price(gateway.config.provider.clone()).await?; - let transaction = create_transaction(gateway.config.clone(), invoice, chain_id, gas_price).await; - let client = SignerMiddleware::new(gateway.config.provider, signer); + let transaction = + create_transaction(gateway.config.clone(), invoice, chain_id, gas_price).await; + let client = SignerMiddleware::new( + gateway.config.provider, + signer.with_chain_id(chain_id.as_u64()), + ); let pending_tx = client - .send_transaction(transaction, None) + .send_transaction( + transaction, + Some(BlockId::Number(ethers::types::BlockNumber::Latest)), + ) .await .map_err(|e| { log::error!("Could not send transaction: {}", e); @@ -60,7 +72,7 @@ pub async fn transfer_gas_to_treasury( let receipt = pending_tx .confirmations(gateway.config.min_confirmations) - .await + .await .map_err(|e| { log::error!("Error waiting for confirmations: {}", e); TransferError::TransactionNotConfirmed @@ -72,4 +84,4 @@ pub async fn transfer_gas_to_treasury( log::info!("Transaction confirmed: {:?}", receipt.transaction_hash); Ok(format!("{:?}", receipt.transaction_hash)) -} \ No newline at end of file +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 4551628..ffb9c07 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,9 +1,9 @@ use std::ops::{Deref, DerefMut}; -use ethers::types::{Address, U256}; +use ethers::{core::k256::SecretKey, signers::LocalWallet, types::{Address, U256}}; use serde::{Deserialize, Serialize}; use zeroize::ZeroizeOnDrop; - +use ethers::utils::hex; /// Describes the structure of a payment method in /// a gateway #[derive(Clone, Deserialize, Serialize)] @@ -51,3 +51,37 @@ pub struct Invoice { pub hash: Option, } +impl Invoice { + // A to_string method for the Invoice struct that is used for output to csv files + pub fn to_string(&self) -> String { + let wallet = LocalWallet::from_bytes(&self.wallet).expect("Invalid key"); + + // Extract the private key as a scalar + let signer = wallet.signer(); + + + // Convert the scalar into a Secp256k1 secret key + let secret_key = SecretKey::try_from(signer.as_nonzero_scalar()).expect("Failed to convert to secret key"); + + // Encode secret key to bytes + let private_key_bytes = secret_key.to_bytes(); + + // Convert to hex string for display/import. + let private_key_string = hex::encode(private_key_bytes); + + format!( + "{},{},{},{},{},{},{},{}", + self.to, + private_key_string, + self.amount, + self.method.token_address.unwrap_or_default(), + self.message + .iter() + .map(|x| format!("{:02x}", x)) + .collect::(), + self.paid_at_timestamp, + self.expires, + self.hash.as_ref().unwrap_or(&"".to_string()) + ) + } +} From 089cc653fac924d51e4deae6699567d2de971747 Mon Sep 17 00:00:00 2001 From: starkbamse <139136798+starkbamse@users.noreply.github.com> Date: Sun, 19 May 2024 17:38:56 +0200 Subject: [PATCH 2/2] Removed database, eip1559 - Implemented eip1559 transfers - Dynamic gas estimation - Removed sled database in favor of in-memory skipmap --- Cargo.lock | 91 ++++---- Cargo.toml | 4 +- src/common/mod.rs | 35 --- src/db/mod.rs | 24 ++- src/gateway/errors.rs | 19 ++ src/gateway/mod.rs | 271 ++++++++++++++++-------- src/invoice/mod.rs | 46 ++++ src/lib.rs | 58 ++--- src/transfers/errors.rs | 12 -- src/transfers/gas_transfers/mod.rs | 87 -------- src/transfers/mod.rs | 31 --- src/types/mod.rs | 87 -------- src/{ => web3}/erc20/IERC20.json | 0 src/{ => web3}/erc20/mod.rs | 2 +- src/web3/mod.rs | 103 +++++++++ src/{ => web3}/poller/mod.rs | 62 +++--- src/web3/transfers/gas_transfers/mod.rs | 180 ++++++++++++++++ src/web3/transfers/mod.rs | 1 + 18 files changed, 647 insertions(+), 466 deletions(-) delete mode 100644 src/common/mod.rs create mode 100644 src/gateway/errors.rs create mode 100644 src/invoice/mod.rs delete mode 100644 src/transfers/errors.rs delete mode 100644 src/transfers/gas_transfers/mod.rs delete mode 100644 src/transfers/mod.rs delete mode 100644 src/types/mod.rs rename src/{ => web3}/erc20/IERC20.json (100%) rename src/{ => web3}/erc20/mod.rs (97%) create mode 100644 src/web3/mod.rs rename src/{ => web3}/poller/mod.rs (76%) create mode 100644 src/web3/transfers/gas_transfers/mod.rs create mode 100644 src/web3/transfers/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 9eff786..53f2876 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,13 +18,13 @@ version = "0.0.3" dependencies = [ "async-std", "bincode", + "crossbeam-skiplist", "ethers", "log", "reqwest 0.12.4", "serde", "serde_json", "sha2", - "sled", "thiserror", "tokio", "uuid 1.8.0", @@ -655,6 +655,16 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-skiplist" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df29de440c58ca2cc6e587ec3d22347551a32435fbde9d2bff64e78a9ffa151b" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.19" @@ -2335,17 +2345,6 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.2" @@ -2353,21 +2352,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", - "parking_lot_core 0.9.10", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -2378,7 +2363,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall", "smallvec", "windows-targets 0.52.5", ] @@ -2759,15 +2744,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.1" @@ -3299,6 +3275,15 @@ dependencies = [ "keccak", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "2.0.0" @@ -3336,22 +3321,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "sled" -version = "0.34.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" -dependencies = [ - "crc32fast", - "crossbeam-epoch", - "crossbeam-utils", - "fs2", - "fxhash", - "libc", - "log", - "parking_lot 0.11.2", -] - [[package]] name = "smallvec" version = "1.13.2" @@ -3428,7 +3397,7 @@ checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ "new_debug_unreachable", "once_cell", - "parking_lot 0.12.2", + "parking_lot", "phf_shared 0.10.0", "precomputed-hash", ] @@ -3645,11 +3614,25 @@ dependencies = [ "libc", "mio", "num_cpus", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2 0.5.7", + "tokio-macros", "windows-sys 0.48.0", ] +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + [[package]] name = "tokio-native-tls" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index 8ce3fd6..8f19013 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,15 +7,15 @@ edition = "2021" [dependencies] bincode = "1.3.3" -sled = "0.34.7" thiserror = "1.0.59" serde = { version="1.0.197",features=["derive"] } sha2 = "0.10.8" -tokio = "1.37.0" uuid = {version="1.8.0",features=["v4"]} +tokio = {version="1.37.0",features=["full"]} reqwest = "0.12.4" log = "0.4.21" zeroize = {version="1.7.0",features=["zeroize_derive"]} async-std = "1.12.0" ethers = "2.0.14" serde_json = "1.0.117" +crossbeam-skiplist = "0.1.3" diff --git a/src/common/mod.rs b/src/common/mod.rs deleted file mode 100644 index ea42dcf..0000000 --- a/src/common/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -use std::time::{SystemTime, UNIX_EPOCH}; -use thiserror::Error; - -/// Retrieve the current unix time in nanoseconds -pub fn get_unix_time_millis() -> u128 { - let now = SystemTime::now(); - let duration = now.duration_since(UNIX_EPOCH).unwrap_or_default(); - duration.as_millis() -} -/// Retrieve the current unix time in nanoseconds -pub fn get_unix_time_seconds() -> u64 { - let now = SystemTime::now(); - let duration = now.duration_since(UNIX_EPOCH).unwrap_or_default(); - duration.as_secs() -} - -#[derive(Error, Debug)] -pub enum DatabaseError { - #[error("No matches found")] - NotFound, - #[error("Could not get from database")] - Get, - #[error("Could not set to database")] - Set, - #[error("Could not communicate with database")] - Communicate, - #[error("Could not deserialize binary data")] - Deserialize, - #[error("Could not serialize binary data")] - Serialize, - #[error("Could not delete from database")] - NoDelete, - #[error("Database internal error: {0}")] - SledError(#[from] sled::Error), -} diff --git a/src/db/mod.rs b/src/db/mod.rs index 68ee3c7..689cd13 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,5 +1,27 @@ -use crate::{common::DatabaseError, types::Invoice}; use sled::Tree; +use thiserror::Error; +use crate::invoice::Invoice; + +#[derive(Error, Debug)] +pub enum DatabaseError { + #[error("No matches found")] + NotFound, + #[error("Could not get from database")] + Get, + #[error("Could not set to database")] + Set, + #[error("Could not communicate with database")] + Communicate, + #[error("Could not deserialize binary data")] + Deserialize, + #[error("Could not serialize binary data")] + Serialize, + #[error("Could not delete from database")] + NoDelete, + #[error("Database internal error: {0}")] + SledError(#[from] sled::Error), +} + /// Retrieve a value by key from a tree. async fn get_from_tree(db: &Tree, key: &str) -> Result, DatabaseError> { diff --git a/src/gateway/errors.rs b/src/gateway/errors.rs new file mode 100644 index 0000000..9ae64c9 --- /dev/null +++ b/src/gateway/errors.rs @@ -0,0 +1,19 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum GatewayError { + #[error("No matches found")] + NotFound, + #[error("Could not get from database")] + Get, + #[error("Could not set to database")] + Set, + #[error("Could not communicate with database")] + Communicate, + #[error("Could not deserialize binary data")] + Deserialize, + #[error("Could not serialize binary data")] + Serialize, + #[error("Could not delete from database")] + NoDelete, +} diff --git a/src/gateway/mod.rs b/src/gateway/mod.rs index 3dac802..cf05495 100644 --- a/src/gateway/mod.rs +++ b/src/gateway/mod.rs @@ -1,42 +1,159 @@ +pub mod errors; mod hash; -use std::{future::Future, pin::Pin, sync::Arc}; - +use std::{ + future::Future, + pin::Pin, + sync::Arc, + time::{SystemTime, UNIX_EPOCH}, +}; use async_std::channel::Sender; -use ethers::{providers::{Http, Provider}, signers::LocalWallet, types::{Address, U256}}; +use crossbeam_skiplist::SkipMap; use ethers::signers::Signer; -use sled::Tree; + + +pub type Provider = ethers::providers::Provider; +pub type Http = ethers::providers::Http; +pub type Address = ethers::types::Address; +pub type U256 = ethers::types::U256; +pub type Units = ethers::utils::Units; +pub type LocalWallet=ethers::signers::LocalWallet; +pub type SecretKey=ethers::core::k256::SecretKey; +pub use ethers::utils::hex; use crate::{ - common::{get_unix_time_millis, get_unix_time_seconds, DatabaseError}, - db::{get, get_all, get_last, set}, - poller::poll_payments, - types::{self, Invoice, PaymentMethod}, + invoice::{self, Invoice}, + web3::poller::poll_payments, }; -use self::hash::hash_now; +use self::{ + errors::GatewayError, + hash::hash_now, +}; + +/// Retrieve the current unix time in nanoseconds +pub fn get_unix_time_millis() -> u128 { + let now = SystemTime::now(); + let duration = now.duration_since(UNIX_EPOCH).unwrap_or_default(); + duration.as_millis() +} +/// Retrieve the current unix time in nanoseconds +pub fn get_unix_time_seconds() -> u64 { + let now = SystemTime::now(); + let duration = now.duration_since(UNIX_EPOCH).unwrap_or_default(); + duration.as_secs() +} /// ## AcceptEVM /// -/// /// The payment gateway is designed to be ran on the main thread, all of /// the functions are non-blocking asynchronous functions. The underlying polling -/// mechanism is offloaded using `tokio::spawn`. +/// mechanism is offloaded using `tokio::spawn`. All invoices are stored +/// in-memory for now using a SkipMap. Therefore, it is your responsibility to +/// implement persistency for the invoices if you deem that this is required. +/// +/// The payment gateway creates addresses and waits for payments to be made to these addresses. +/// When a deposit is made to the address, the gateway will check the balance and if the balance is +/// greater than or equal to the amount specified in the invoice, the gateway will consider the invoice +/// paid and will transfer the funds to the treasury address. However, due to the uncertainty of the blockchain +/// this transfer could fail. It is therefore important to check if the hash is present in the invoice when +/// receiving the invoice from the receiver. +/// +/// If the hash is present, the invoice was successfully transferred to the treasury. If the hash is not present, +/// the invoice was not transferred to the treasury, and you should handle this case accordingly. The invoice will +/// always contain the wallet that was used to create the invoice. This wallet is a LocalWallet from the ethers crate which +/// you can use to recover the funds. It is therefore important to store this wallet in a safe location for either programmatic +/// or manual recovery. The `to_string()` method of the invoice will convert all invoice data into a readable format +/// that can be stored in a CSV file or any other format that you prefer. The wallet will be transpiled into a private key +/// compatible with wallets like Metamask and TrustWallet. +/// +/// +/// Example: +/// ```rust +/// use acceptevm::gateway::{Provider,PaymentGateway,TransactionType,PaymentGatewayConfiguration, Reflector,Address}; +/// use async_std::channel::unbounded; +/// use acceptevm::gateway::Wei; +/// +/// #[tokio::main] +/// async fn main(){ +/// let (sender, receiver) = unbounded(); +/// let reflector = Reflector::Sender(sender); +/// let transaction_type=TransactionType::Eip1559; +/// let provider = Provider::try_from("https://bsc-dataseed1.binance.org/").expect("Invalid RPC URL"); +/// let gateway = PaymentGateway::new( +/// PaymentGatewayConfiguration{ +/// provider, +/// treasury_address: "0xdac17f958d2ee523a2206206994597c13d831ec7".parse::
().unwrap(), +/// min_confirmations: 10, +/// reflector, +/// invoice_delay_seconds: 10, +/// transaction_type, +/// eip1559_estimation_retry_max: 3, +/// eip1559_estimation_retry_delay_seconds: 10, +/// } +/// ); +/// +/// // Add new invoice and serialize string data with bincode +/// let (invoice_id, invoice) = gateway.new_invoice( +/// Wei::from(100), +/// None, +/// bincode::serialize("Invoice details").expect("Could not serialize invoice details"), +/// 3600 +/// ).await.unwrap(); +/// +/// // Get the invoice from the gateway +/// let invoice = gateway.get_invoice(&invoice_id).await.unwrap(); +/// +/// gateway.poll_payments().await; +/// // Continously receive the paid invoices via the receiver. +/// // You can implement your own logic here. +/// // Example: (note the return type of the receiver) +/// /* while let Ok(invoice:(String,Invoice)) = receiver.recv().await { +/// println!("Received invoice: {:?}", invoice); +/// break; +/// } +/// */ +/// } +/// ``` +/// +/// #[derive(Clone)] pub struct PaymentGateway { pub config: PaymentGatewayConfiguration, - pub tree: Tree, + pub invoices: Arc>, +} + +#[derive(Clone)] +pub enum TransactionType { + Legacy, + Eip1559, } +/// ## PaymentGatewayConfiguration +/// The configuration struct contains the following fields: +/// - `provider`: the provider for the EVM network. This is used to communicate with the EVM network. +/// - `treasury_address`: the address of the treasury for all paid invoices, on this EVM network. +/// - `min_confirmations`: the minimum amount of confirmations required before considering an invoice paid. +/// - `reflector`: The reflector is an enum that allows you to receive the paid invoices. +/// At the moment, the only reflector available is the `Sender` from the async-std channel. +/// This means that you will need to create a channel and pass the sender as the reflector. +/// - `poller_delay_seconds`: how long to wait before checking the next invoice in milliseconds. +/// This is used to prevent potential rate limits from the node. +/// - `transaction_type`: the type of transaction to use. At the moment, the only two options are `Legacy` and `Eip1559`. +/// - `eip1559_estimation_retry_max`: the maximum amount of retries for the EIP1559 estimation. The latest block data +/// is used to estimate the gas prices for the transaction. If the block is empty, the gateway will retry until the +/// maximum amount of retries is reached. Take this into consideration when deploying the gateway on an EVM network. +/// - `eip1559_estimation_retry_delay_seconds`: the delay between each retry in seconds. #[derive(Clone)] pub struct PaymentGatewayConfiguration { pub provider: Provider, pub treasury_address: Address, - pub invoice_delay_millis: u64, + pub poller_delay_seconds: u64, pub reflector: Reflector, pub min_confirmations: usize, - pub transfer_gas_limit: Option, - pub use_eip1559: bool, + pub transaction_type: TransactionType, + pub eip1559_estimation_retry_max: u64, + pub eip1559_estimation_retry_delay_seconds: u64, } /// ## Reflector @@ -58,7 +175,7 @@ pub struct PaymentGatewayConfiguration { #[derive(Clone)] pub enum Reflector { /// A sender from async-std - Sender(Sender), + Sender(Sender<(String,Invoice)>), } // Type alias for the underlying Web3 type. @@ -69,88 +186,71 @@ pub type AsyncCallback = Arc Pin + Send>> + Send + Sync>; impl PaymentGateway { - /// Creates a new payment gateway. - /// - /// - `rpc_url`: the HTTP Rpc url of the EVM network + /// ## Creates a new payment gateway. + /// + /// To minimize the amount of arguments, the configuration is passed as a struct. + /// + /// The configuration struct contains the following fields: + /// - `provider`: the provider for the EVM network. This is used to communicate with the EVM network. /// - `treasury_address`: the address of the treasury for all paid invoices, on this EVM network. - /// - `invoice_delay_millis`: how long to wait before checking the next invoice in milliseconds. - /// This is used to prevent potential rate limits from the node. + /// - `min_confirmations`: the minimum amount of confirmations required before considering an invoice paid. /// - `reflector`: The reflector is an enum that allows you to receive the paid invoices. /// At the moment, the only reflector available is the `Sender` from the async-std channel. /// This means that you will need to create a channel and pass the sender as the reflector. - /// - `sled_path`: The path of the sled database where the pending invoices will - /// be stored. In the event of a crash the invoices are saved and will be - /// checked on reboot. - /// - `name`: A name that describes this gateway. Perhaps the EVM network used? - /// - `transfer_gas_limit`: An optional gas limit used when transferring gas from paid invoices to - /// the treasury. Useful in case your treasury address is a contract address - /// that implements custom functionality for handling incoming gas. + /// - `invoice_delay_seconds`: how long to wait before checking the next invoice in milliseconds. + /// This is used to prevent potential rate limits from the node. + /// - `transaction_type`: the type of transaction to use. At the moment, the only two options are `Legacy` and `Eip1559`. + /// - `eip1559_estimation_retry_max`: the maximum amount of retries for the EIP1559 estimation. The latest block data + /// is used to estimate the gas prices for the transaction. If the block is empty, the gateway will retry until the + /// maximum amount of retries is reached. Take this into consideration when deploying the gateway on an EVM network. + /// - `eip1559_estimation_retry_delay_seconds`: the delay between each retry in seconds. /// /// Example: /// ```rust - /// use acceptevm::gateway::{PaymentGateway, Reflector}; + /// use acceptevm::gateway::{Provider,PaymentGateway,TransactionType,PaymentGatewayConfiguration, Reflector,Address}; /// use async_std::channel::unbounded; - /// let (sender, _receiver) = unbounded(); - /// let reflector = Reflector::Sender(sender); /// - /// PaymentGateway::new( - /// "https://123.com", - /// "0xdac17f958d2ee523a2206206994597c13d831ec7".to_string(), - /// 10, - /// reflector, - /// "./your-wanted-db-path", - /// 10, - /// Some(21000), - /// ); + /// let (sender, receiver) = unbounded(); + /// let reflector = Reflector::Sender(sender); + /// let transaction_type=TransactionType::Eip1559; + /// let provider = Provider::try_from("https://bsc-dataseed1.binance.org/").expect("Invalid RPC URL"); + /// let gateway = PaymentGateway::new( + /// PaymentGatewayConfiguration{ + /// provider, + /// treasury_address: "0xdac17f958d2ee523a2206206994597c13d831ec7".parse::
().unwrap(), + /// min_confirmations: 10, + /// reflector, + /// invoice_delay_seconds: 10, + /// transaction_type, + /// eip1559_estimation_retry_max: 3, + /// eip1559_estimation_retry_delay_seconds: 10, + /// } + /// ); /// ``` - - - pub fn new( - rpc_url: &str, - treasury_address: String, - invoice_delay_millis: u64, - reflector: Reflector, - sled_path: &str, - min_confirmations: usize, - transfer_gas_limit: Option, - ) -> PaymentGateway { - let db = sled::open(sled_path).unwrap(); - let tree = db.open_tree("invoices").unwrap(); - let provider = Provider::try_from(rpc_url).expect("Invalid RPC URL"); - - // TODO: When implementing token transfers allow the user to add their gas wallet here. - + pub fn new(configuration: PaymentGatewayConfiguration) -> PaymentGateway { + let map: SkipMap = SkipMap::new(); PaymentGateway { - config: PaymentGatewayConfiguration { - min_confirmations, - provider, - treasury_address: treasury_address - .parse() - .unwrap_or_else(|_| panic!("Invalid treasury address")), - invoice_delay_millis, - reflector, - transfer_gas_limit, - }, - tree, + config: configuration, + invoices: Arc::new(map), } } - /// Retrieves the last invoice - pub async fn get_last_invoice(&self) -> Result<(String, Invoice), DatabaseError> { - get_last(&self.tree).await - } - /// Retrieves all invoices in the form of a tuple: String,Invoice /// where the first element is the key that was used in the database /// and the second part is the invoice. The key is a SHA256 hash of the /// creation timestamp and the recipient address. - pub async fn get_all_invoices(&self) -> Result, DatabaseError> { - get_all(&self.tree).await + pub async fn get_all_invoices(&self) -> Result, GatewayError> { + let invoices = self.invoices.iter().map(|entry| (entry.key().clone(), entry.value().clone())).collect(); + Ok(invoices) } /// Retrieve an invoice from the payment gateway - pub async fn get_invoice(&self, key: String) -> Result { - get(&self.tree, &key).await + pub async fn get_invoice(&self, key: &str) -> Result { + let invoices = self.invoices.get(key); + match invoices { + Some(invoice) => Ok(invoice.value().clone()), + None => Err(GatewayError::NotFound), + } } /// Spawns an asynchronous task that checks all the pending invoices @@ -171,19 +271,24 @@ impl PaymentGateway { pub async fn new_invoice( &self, amount: Wei, - method: PaymentMethod, + token_address: Option
, message: Vec, expires_in_seconds: u64, - ) -> Result<(String,Invoice), DatabaseError> { + ) -> Result<(String, Invoice), GatewayError> { + // Panic if token address is set + if token_address.is_some() { + panic!("Token address is not supported yet"); + } + // Generate random wallet let signer = LocalWallet::new(&mut ethers::core::rand::thread_rng()); let invoice = Invoice { to: signer.address(), - wallet: types::ZeroizedVec { + wallet: invoice::ZeroizedVec { inner: signer.signer().to_bytes().to_vec(), }, amount, - method, + token_address, message, paid_at_timestamp: 0, expires: get_unix_time_seconds() + expires_in_seconds, @@ -194,7 +299,7 @@ impl PaymentGateway { let seed = format!("{}{}", signer.address(), get_unix_time_millis()); let invoice_id = hash_now(seed); // Save the invoice in db. - set(&self.tree, &invoice_id, &invoice).await?; - Ok((invoice_id,invoice)) + self.invoices.insert(invoice_id.clone(), invoice.clone()); + Ok((invoice_id, invoice)) } } diff --git a/src/invoice/mod.rs b/src/invoice/mod.rs new file mode 100644 index 0000000..24e59f2 --- /dev/null +++ b/src/invoice/mod.rs @@ -0,0 +1,46 @@ +use std::ops::{Deref, DerefMut}; + +use ethers::types::{Address, U256}; +use serde::{Deserialize, Serialize}; +use zeroize::ZeroizeOnDrop; + +/// ## DANGER: Private Key Data is contained in this struct +/// Share it with caution +#[derive(ZeroizeOnDrop, Clone, Deserialize, Serialize,Debug)] +pub struct ZeroizedVec { + pub inner: Vec, +} + +// To automatically dereference into Vec type for restoring wallet +impl Deref for ZeroizedVec { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for ZeroizedVec { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +#[derive(Clone, Deserialize, Serialize,Debug)] +pub struct Invoice { + /// Recipient address + pub to: Address, + /// Contains the keys to restore the wallet + pub wallet: ZeroizedVec, + /// Amount requested + pub amount: U256, + /// Method used for payment + pub token_address: Option
, + /// Arbitrary message attached to the invoice + pub message: Vec, + /// Timestamp at which the invoice was paid + pub paid_at_timestamp: u64, + /// Invoice expiry time + pub expires: u64, + pub hash: Option, +} diff --git a/src/lib.rs b/src/lib.rs index 49d2477..6fd9ff6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,54 +1,44 @@ -mod common; -mod db; -mod erc20; +mod web3; pub mod gateway; -mod poller; -mod transfers; -pub mod types; +pub mod invoice; -pub use common::DatabaseError; #[cfg(test)] mod tests { - use std::{fs, path::Path, str::FromStr}; - + use std::str::FromStr; use async_std::channel::unbounded; - use ethers::types::U256; use crate::{ - common::DatabaseError, - gateway::{PaymentGateway, Reflector}, - types::{Invoice, PaymentMethod}, + gateway::{errors::GatewayError, Address, PaymentGateway, PaymentGatewayConfiguration, Provider, Reflector, TransactionType, U256}, invoice::Invoice }; - fn setup_test_gateway(db_path: &str) -> PaymentGateway { + fn setup_test_gateway() -> PaymentGateway { let (sender, _receiver) = unbounded(); let reflector = Reflector::Sender(sender); + let provider = Provider::try_from("https://123.com").expect("Invalid RPC URL"); + let transaction_type=TransactionType::Eip1559; PaymentGateway::new( - "https://123.com", - "0xdac17f958d2ee523a2206206994597c13d831ec7".to_string(), - 10, - reflector, - db_path, - 10, - Some(21000) + PaymentGatewayConfiguration{ + provider, + treasury_address: "0xdac17f958d2ee523a2206206994597c13d831ec7".parse::
().unwrap(), + min_confirmations: 10, + reflector, + poller_delay_seconds: 1, + transaction_type, +eip1559_estimation_retry_max: 3, + eip1559_estimation_retry_delay_seconds: 10, + + } ) } - fn remove_test_db(db_path: &str) { - if Path::new(db_path).exists() { - fs::remove_dir_all(db_path).expect("Failed to remove test database"); - } - } - async fn insert_test_invoice(gateway: &PaymentGateway) -> Result<(String,Invoice), DatabaseError> { + async fn insert_test_invoice(gateway: &PaymentGateway) -> Result<(String,Invoice), GatewayError> { gateway .new_invoice( U256::from_str("0").unwrap(), - PaymentMethod { - token_address: None, - }, + None, bincode::serialize("test").unwrap(), 3600, ) @@ -57,24 +47,22 @@ mod tests { #[tokio::test] async fn assert_invoice_creation() { - let gateway = setup_test_gateway("./test-assert-invoice-creation"); + let gateway = setup_test_gateway(); insert_test_invoice(&gateway).await.unwrap(); - let database_length = gateway.tree.len(); + let database_length = gateway.invoices.len(); println!("Database length: {}", database_length); assert_eq!(database_length, 1); - remove_test_db("./test-assert-invoice-creation"); } #[tokio::test] async fn assert_valid_address_length() { - let gateway = setup_test_gateway("./test-assert-valid-address-length"); + let gateway = setup_test_gateway(); let invoice = insert_test_invoice(&gateway).await.unwrap(); let address=format!("{:?}", invoice.1.to); let address_length = address.len(); println!("Address: {}", address); println!("Address length: {}", address_length); assert_eq!(address_length, 42); - remove_test_db("./test-assert-valid-address-length"); } } diff --git a/src/transfers/errors.rs b/src/transfers/errors.rs deleted file mode 100644 index 6ce44cf..0000000 --- a/src/transfers/errors.rs +++ /dev/null @@ -1,12 +0,0 @@ -use thiserror::Error; - -#[derive(Error, Debug, PartialEq)] -pub enum TransferError { - #[error("Could not get chain id")] - ChainId, - #[error("Could not transmit transaction")] - SendTransaction, - - #[error("Transaction not confirmed")] - TransactionNotConfirmed, -} diff --git a/src/transfers/gas_transfers/mod.rs b/src/transfers/gas_transfers/mod.rs deleted file mode 100644 index c2062b1..0000000 --- a/src/transfers/gas_transfers/mod.rs +++ /dev/null @@ -1,87 +0,0 @@ -use crate::{ - gateway::{PaymentGateway, PaymentGatewayConfiguration}, - transfers::get_chain_id, - types::Invoice, -}; -use ethers::{ - middleware::SignerMiddleware, - providers::Middleware, - signers::{LocalWallet, Signer}, - types::{BlockId, Eip1559TransactionRequest, TransactionRequest, U256}, -}; -use std::ops::Mul; -use ethers::types::BlockNumber::Latest; -use super::{errors::TransferError, get_gas_price}; - -/// Creates a transaction to transfer gas from a paid invoice to a specified treasury address -async fn create_transaction( - gateway_config: PaymentGatewayConfiguration, - invoice: &Invoice, - chain_id: U256, - gas_price: U256, -) -> Eip1559TransactionRequest { - // Use specified gas limit or fallback - let gas_limit = gateway_config.transfer_gas_limit.unwrap_or(21000); - - // Maximum cost of transaction - let max_cost = gas_limit.mul(gas_price.as_u128()); - let balance_of_recipient = gateway_config - .provider - .get_balance(invoice.to, Some(BlockId::Number(Latest))) - .await.unwrap(); - - // Estimated gas left after transfer - let value: U256 = invoice.amount; - println!("Value: {:?}", value); -gateway_config.provider.estimate_eip1559_fees(estimator) - Eip1559TransactionRequest::new() - .from(invoice.to) - .to(gateway_config.treasury_address) - .nonce(0) - .chain_id(chain_id.as_u64()) - .gas(gas_limit) - .value(balance_of_recipient) -} - -/// Transfers gas from a paid invoice to a specified treasury address -pub async fn transfer_gas_to_treasury( - gateway: PaymentGateway, - invoice: &Invoice, -) -> Result { - let signer = LocalWallet::from_bytes(&invoice.wallet).unwrap(); - let chain_id = get_chain_id(gateway.config.provider.clone()).await?; - let gas_price = get_gas_price(gateway.config.provider.clone()).await?; - - let transaction = - create_transaction(gateway.config.clone(), invoice, chain_id, gas_price).await; - let client = SignerMiddleware::new( - gateway.config.provider, - signer.with_chain_id(chain_id.as_u64()), - ); - - let pending_tx = client - .send_transaction( - transaction, - Some(BlockId::Number(ethers::types::BlockNumber::Latest)), - ) - .await - .map_err(|e| { - log::error!("Could not send transaction: {}", e); - TransferError::SendTransaction - })?; - - let receipt = pending_tx - .confirmations(gateway.config.min_confirmations) - .await - .map_err(|e| { - log::error!("Error waiting for confirmations: {}", e); - TransferError::TransactionNotConfirmed - })? - .ok_or_else(|| { - log::error!("Transaction not confirmed"); - TransferError::TransactionNotConfirmed - })?; - - log::info!("Transaction confirmed: {:?}", receipt.transaction_hash); - Ok(format!("{:?}", receipt.transaction_hash)) -} diff --git a/src/transfers/mod.rs b/src/transfers/mod.rs deleted file mode 100644 index 677e540..0000000 --- a/src/transfers/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -pub mod errors; -pub mod gas_transfers; - - -use ethers::{providers::{Http, Middleware, Provider}, types::U256}; -use self::errors::TransferError; - -// Retrieves the chain id from the provider. -async fn get_chain_id(provider: Provider) -> Result { - match provider.get_chainid().await { - Ok(chain_id) => Ok(chain_id), - Err(error) => { - log::error!("Could not get chain id: {}", error); - Err(TransferError::ChainId) - } - } -} - -/// Retrieves the current gas price from a provider -async fn get_gas_price(provider: Provider) -> Result { - match provider.get_gas_price().await { - Ok(gas_price) => Ok(gas_price), - Err(error) => { - log::error!( - "Could not get gas price (maybe chain uses EIP-1559?): {}", - error - ); - Err(TransferError::ChainId) - } - } -} diff --git a/src/types/mod.rs b/src/types/mod.rs deleted file mode 100644 index ffb9c07..0000000 --- a/src/types/mod.rs +++ /dev/null @@ -1,87 +0,0 @@ -use std::ops::{Deref, DerefMut}; - -use ethers::{core::k256::SecretKey, signers::LocalWallet, types::{Address, U256}}; -use serde::{Deserialize, Serialize}; -use zeroize::ZeroizeOnDrop; -use ethers::utils::hex; -/// Describes the structure of a payment method in -/// a gateway -#[derive(Clone, Deserialize, Serialize)] -pub struct PaymentMethod { - /// The address of the ERC20 token - pub token_address: Option
, -} - -#[derive(ZeroizeOnDrop, Clone, Deserialize, Serialize)] -pub struct ZeroizedVec { - pub inner: Vec, -} - -// To automatically dereference into Vec type for restoring wallet -impl Deref for ZeroizedVec { - type Target = Vec; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl DerefMut for ZeroizedVec { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner - } -} - -#[derive(Clone, Deserialize, Serialize)] -pub struct Invoice { - /// Recipient address - pub to: Address, - /// Recipient instance - pub wallet: ZeroizedVec, - /// Amount requested - pub amount: U256, - /// Method used for payment - pub method: PaymentMethod, - /// Arbitrary message attached to the invoice - pub message: Vec, - /// Timestamp at which the invoice was paid - pub paid_at_timestamp: u64, - /// Invoice expiry time - pub expires: u64, - pub hash: Option, -} - -impl Invoice { - // A to_string method for the Invoice struct that is used for output to csv files - pub fn to_string(&self) -> String { - let wallet = LocalWallet::from_bytes(&self.wallet).expect("Invalid key"); - - // Extract the private key as a scalar - let signer = wallet.signer(); - - - // Convert the scalar into a Secp256k1 secret key - let secret_key = SecretKey::try_from(signer.as_nonzero_scalar()).expect("Failed to convert to secret key"); - - // Encode secret key to bytes - let private_key_bytes = secret_key.to_bytes(); - - // Convert to hex string for display/import. - let private_key_string = hex::encode(private_key_bytes); - - format!( - "{},{},{},{},{},{},{},{}", - self.to, - private_key_string, - self.amount, - self.method.token_address.unwrap_or_default(), - self.message - .iter() - .map(|x| format!("{:02x}", x)) - .collect::(), - self.paid_at_timestamp, - self.expires, - self.hash.as_ref().unwrap_or(&"".to_string()) - ) - } -} diff --git a/src/erc20/IERC20.json b/src/web3/erc20/IERC20.json similarity index 100% rename from src/erc20/IERC20.json rename to src/web3/erc20/IERC20.json diff --git a/src/erc20/mod.rs b/src/web3/erc20/mod.rs similarity index 97% rename from src/erc20/mod.rs rename to src/web3/erc20/mod.rs index 11c6b18..b240429 100644 --- a/src/erc20/mod.rs +++ b/src/web3/erc20/mod.rs @@ -32,7 +32,7 @@ mod tests { use ethers::{providers::Provider, types::{Address, U256}}; - use crate::erc20::ERC20Token; + use crate::web3::erc20::ERC20Token; #[tokio::test] async fn valid_balance() { let provider = Provider::try_from("https://bsc-dataseed1.binance.org/").unwrap(); diff --git a/src/web3/mod.rs b/src/web3/mod.rs new file mode 100644 index 0000000..22a072b --- /dev/null +++ b/src/web3/mod.rs @@ -0,0 +1,103 @@ +pub mod poller; +mod erc20; +mod transfers; + +use ethers::{providers::{Http, Middleware, Provider, ProviderError}, types::{Address, BlockId, BlockNumber, U256}}; +use ethers::types::BlockNumber::Latest; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum TransferError { + #[error("Could not get base fee")] + BaseFee, + #[error("Could not transmit transaction")] + SendTransaction, + #[error("Transaction not confirmed")] + TransactionNotConfirmed, + #[error("Ethers error: {0}")] + EthersError(#[from] ProviderError), +} +#[derive(Error, Debug)] +pub enum FeeEstimationError { + #[error("No transactions in block")] + NoTransactionsInBlock, + #[error("No base fee in block")] + NoBaseFeeInBlock, + #[error("Ethers error: {0}")] + EthersError(#[from] ProviderError), +} + +/// Estimates EIP-1559 transaction fees (max fee per gas and max priority fee per gas) with retries +pub async fn estimate_eip1559_fees_with_retry( + provider: &Provider, + max_retries: u64, + delay_seconds_in_between_retries: u64, +) -> Result<(U256, U256), FeeEstimationError> { + let mut retries = 0; + + loop { + match estimate_eip1559_fees(provider).await { + Ok(fees) => return Ok(fees), + Err(FeeEstimationError::NoTransactionsInBlock) | Err(FeeEstimationError::NoBaseFeeInBlock) => { + if retries >= max_retries { + return Err(FeeEstimationError::NoTransactionsInBlock); + } + retries += 1; + } + Err(e) => return Err(e), + } + // Sleep + tokio::time::sleep(tokio::time::Duration::from_secs(delay_seconds_in_between_retries)).await; + } +} +/// Estimates EIP-1559 transaction fees (max fee per gas and max priority fee per gas) +async fn estimate_eip1559_fees( + provider: &Provider, +) -> Result<(U256, U256), FeeEstimationError> { + let block = provider + .get_block_with_txs(BlockId::Number(BlockNumber::Latest)) + .await? + .ok_or(FeeEstimationError::NoTransactionsInBlock)?; + + let base_fee = block.base_fee_per_gas.ok_or(FeeEstimationError::NoBaseFeeInBlock)?; + + let mut total_max_fee = U256::zero(); + let mut total_priority_fee = U256::zero(); + let count = block.transactions.len() as u64; + + if count == 0 { + return Err(FeeEstimationError::NoTransactionsInBlock); + } + + for tx in block.transactions { + if let Some(max_fee_per_gas) = tx.max_fee_per_gas { + total_max_fee += max_fee_per_gas; + // Calculate priority fee as max_fee - base_fee + total_priority_fee += max_fee_per_gas.saturating_sub(base_fee); + } + } + + let average_max_fee = total_max_fee / U256::from(count); + let average_priority_fee = total_priority_fee / U256::from(count); + + Ok((average_max_fee, average_priority_fee)) +} + + +/// Retrieves the gas token balance of the specified address on the specified web3 instance +pub async fn get_native_balance( + provider: &Provider, + address: &Address, +) -> Result { + Ok(provider.get_balance(*address, Some(BlockId::Number(Latest))).await?) +} + +// Retrieves the chain id from the provider. +pub async fn get_chain_id(provider: Provider) -> Result { + Ok(provider.get_chainid().await?) +} + +/// Retrieves the current gas price from a provider +pub async fn get_gas_price(provider: Provider) -> Result { + Ok(provider.get_gas_price().await?) +} diff --git a/src/poller/mod.rs b/src/web3/poller/mod.rs similarity index 76% rename from src/poller/mod.rs rename to src/web3/poller/mod.rs index 52d6ae6..657714e 100644 --- a/src/poller/mod.rs +++ b/src/web3/poller/mod.rs @@ -1,15 +1,13 @@ -use crate::{ - common::get_unix_time_seconds, - db::{delete, get_all}, - erc20::ERC20Token, - gateway::PaymentGateway, - transfers::{errors::TransferError, gas_transfers::transfer_gas_to_treasury}, - types::Invoice, -}; -use ethers::{contract::ContractError, providers::ProviderError, types::{Address, BlockNumber::Latest}}; + +use ethers::contract::ContractError; +use ethers::providers::{Http, Provider}; use crate::gateway::Reflector::Sender; -use ethers::{providers::{Http, Middleware, Provider}, types::{BlockId, U256}}; -use sled::Tree; +use crate::gateway::{get_unix_time_seconds, PaymentGateway}; +use crate::invoice::Invoice; + +use super::erc20::ERC20Token; +use super::transfers::gas_transfers::transfer_gas_to_treasury; +use super::{get_native_balance, TransferError}; /// Checks if a specific token of a specific amount has been received /// at a certain address. @@ -24,21 +22,11 @@ async fn check_if_token_received( Ok(false) } -/// Retrieves the gas token balance of the specified address on the specified web3 instance -async fn get_native_balance( - provider: &Provider, - address: &Address, -) -> Result { - provider - .get_balance(*address, Some(BlockId::Number(Latest))) - .await -} - /// Used to check if the invoice recipient has received enough money to cover the invoice async fn check_if_native_received( provider: Provider, invoice: &Invoice, -) -> Result>> { +) -> Result { let balance_of_recipient = get_native_balance(&provider, &invoice.to).await?; if balance_of_recipient.ge(&invoice.amount) { return Ok(true); @@ -49,7 +37,7 @@ async fn check_if_native_received( /// A function that branches control flow depending on the invoice shall /// be paid by an ERC20-compatible token or the native gas token on the network async fn check_and_process(provider: Provider, invoice: &Invoice) -> bool { - match &invoice.method.token_address { + match &invoice.token_address { Some(address) => { let token = ERC20Token::new(provider, *address); check_if_token_received(token, invoice).await.unwrap_or_else(|error| { @@ -64,12 +52,6 @@ async fn check_and_process(provider: Provider, invoice: &Invoice) -> bool } } -async fn delete_invoice(tree: &Tree, key: String) { - // Optimistically delete the old invoice. - if let Err(delete_error) = delete(tree, &key).await { - log::error!("Could not remove invoice: {}", delete_error); - } -} async fn transfer_to_treasury( gateway: PaymentGateway, @@ -81,15 +63,17 @@ async fn transfer_to_treasury( /// Periodically checks if invoices are paid in accordance /// to the specified polling interval. pub async fn poll_payments(gateway: PaymentGateway) { + log::info!("Starting polling payments"); loop { - match get_all(&gateway.tree).await { + log::info!("Pending invoices: {:?}", gateway.invoices.len()); + match gateway.get_all_invoices().await { Ok(all) => { // Loop through all invoices for (key, mut invoice) in all { // If the current time is greater than expiry if get_unix_time_seconds() > invoice.expires { // Delete the invoice and continue with the next iteration - delete_invoice(&gateway.tree, key).await; + gateway.invoices.remove(&key); continue; } // Check if the invoice was paid @@ -97,6 +81,7 @@ pub async fn poll_payments(gateway: PaymentGateway) { check_and_process(gateway.config.provider.clone(), &invoice).await; if check_result { + log::info!("Starting transfer to treasury"); // Attempt transfer to treasury match transfer_to_treasury(gateway.clone(), &invoice).await { Ok(receipt) => { @@ -112,20 +97,20 @@ pub async fn poll_payments(gateway: PaymentGateway) { // If the transfer_to_treasury invoice was paid, delete it, stand in queue for the // lock to the callback function. - delete_invoice(&gateway.tree, key).await; + gateway.invoices.remove(&key); invoice.paid_at_timestamp = get_unix_time_seconds(); match gateway.config.reflector { Sender(ref sender) => { // Attempt to send the PriceData through the channel. - if let Err(error) = sender.send(invoice).await { + if let Err(error) = sender.send((key,invoice)).await { log::error!("Failed sending data: {}", error); } } } } // To prevent rate limitations on certain Web3 RPC's we sleep here for the specified amount. - tokio::time::sleep(std::time::Duration::from_millis( - gateway.config.invoice_delay_millis, + tokio::time::sleep(std::time::Duration::from_secs( + gateway.config.poller_delay_seconds, )) .await; } @@ -135,8 +120,8 @@ pub async fn poll_payments(gateway: PaymentGateway) { } } // To prevent busy idling we sleep here too. - tokio::time::sleep(std::time::Duration::from_millis( - gateway.config.invoice_delay_millis, + tokio::time::sleep(std::time::Duration::from_secs( + gateway.config.poller_delay_seconds, )) .await; } @@ -147,7 +132,8 @@ mod tests { use ethers::{providers::Provider, types::{Address, U256}}; - use crate::poller::get_native_balance; + use crate::web3::get_native_balance; + #[tokio::test] async fn valid_balance() { diff --git a/src/web3/transfers/gas_transfers/mod.rs b/src/web3/transfers/gas_transfers/mod.rs new file mode 100644 index 0000000..1652ada --- /dev/null +++ b/src/web3/transfers/gas_transfers/mod.rs @@ -0,0 +1,180 @@ +use ethers::{ + middleware::SignerMiddleware, + providers::{Http, Middleware, Provider}, + signers::{LocalWallet, Signer}, + types::{ + transaction::eip2718::TypedTransaction, BlockId, BlockNumber, Eip1559TransactionRequest, + TransactionRequest, U256, + }, +}; +use std::ops::Mul; + +use crate::{ + gateway::{PaymentGateway, TransactionType}, + invoice::Invoice, + web3::{ + estimate_eip1559_fees_with_retry, get_chain_id, get_gas_price, get_native_balance, + TransferError, + }, +}; + +async fn transmit_transaction( + signer: LocalWallet, + transaction: TypedTransaction, + chain_id: U256, + gateway: PaymentGateway, +) -> Result { + let client = SignerMiddleware::new( + gateway.config.provider, + signer.with_chain_id(chain_id.as_u64()), + ); + + let pending_tx = client + .send_transaction(transaction, Some(BlockId::Number(BlockNumber::Latest))) + .await + .map_err(|e| { + log::error!("Transaction send failed: {}", e); + TransferError::SendTransaction + })?; + + let receipt = pending_tx + .confirmations(gateway.config.min_confirmations) + .await + .map_err(|e| { + log::error!("Error waiting for confirmations: {}", e); + TransferError::TransactionNotConfirmed + })? + .ok_or_else(|| { + log::error!("Transaction not confirmed"); + TransferError::TransactionNotConfirmed + })?; + log::info!("Transaction confirmed: {:?}", receipt.transaction_hash); + Ok(format!("{:?}", receipt.transaction_hash)) +} + +async fn estimate_gas_on_transaction( + provider: &Provider, + transaction: TransactionRequest, +) -> Result { + provider + .estimate_gas( + &transaction.clone().into(), + Some(BlockId::Number(BlockNumber::Latest)), + ) + .await + .map_err(|e| { + log::error!("Gas estimation failed: {}", e); + TransferError::SendTransaction + }) +} + +async fn transfer_gas_to_treasury_legacy( + gateway: PaymentGateway, + invoice: &Invoice, + signer: LocalWallet, + chain_id: U256, + gas_price: U256, + balance: U256, +) -> Result { + let provider = &gateway.config.provider; + + // Use specified gas limit or fallback during estimation only + let mut gas_limit = 21000; + + // Maximum cost of transaction + let mut max_cost = gas_limit.mul(gas_price.as_u128()); + let mut value = balance.saturating_sub(U256::from(max_cost)); + + // Create a transaction request + let mut transaction = TransactionRequest::new() + .from(invoice.to) + .to(gateway.config.treasury_address) + .nonce(0) + .chain_id(chain_id.as_u64()) + .gas_price(gas_price) + .value(value); + + // Estimate gas + let gas_estimate = estimate_gas_on_transaction(provider, transaction.clone()).await?; + + // Update gas limit and max cost + gas_limit = gas_estimate.as_u128(); + max_cost = gas_limit * gas_price.as_u128(); + value = balance.saturating_sub(U256::from(max_cost)); + transaction = transaction.gas(gas_estimate).value(value); + + // Transmit transaction + transmit_transaction(signer, transaction.into(), chain_id, gateway).await +} +async fn transfer_gas_to_treasury_eip1559( + gateway: PaymentGateway, + invoice: &Invoice, + signer: LocalWallet, + chain_id: U256, + balance: U256, +) -> Result { + let provider = &gateway.config.provider; + + let base_fee = provider + .get_block(BlockNumber::Latest) + .await? + .and_then(|b| b.base_fee_per_gas) + .ok_or(TransferError::BaseFee)?; + + match estimate_eip1559_fees_with_retry( + provider, + gateway.config.eip1559_estimation_retry_max, + gateway.config.eip1559_estimation_retry_max, + ) + .await + { + Ok((estimated_max_fee, estimated_priority_fee)) => { + let max_fee_per_gas = + std::cmp::max(estimated_max_fee, base_fee + estimated_priority_fee); + + let mut transaction = Eip1559TransactionRequest::new() + .from(invoice.to) + .to(gateway.config.treasury_address) + .nonce(0) + .chain_id(chain_id.as_u64()) + .max_fee_per_gas(max_fee_per_gas) + .max_priority_fee_per_gas(estimated_priority_fee) + .value(U256::zero()); + + let gas_estimate = + estimate_gas_on_transaction(provider, transaction.clone().into()).await?; + let max_total_fee = max_fee_per_gas.mul(gas_estimate); + + transaction = transaction + .gas(gas_estimate) + .value(balance.saturating_sub(max_total_fee)); + + transmit_transaction(signer, transaction.into(), chain_id, gateway).await + } + Err(e) => { + log::error!("Could not estimate fees: {}", e); + Err(TransferError::SendTransaction) + } + } +} + +/// Transfers gas from a paid invoice to a specified treasury address +pub async fn transfer_gas_to_treasury( + gateway: PaymentGateway, + invoice: &Invoice, +) -> Result { + let signer = LocalWallet::from_bytes(&invoice.wallet).unwrap(); + let chain_id = get_chain_id(gateway.config.provider.clone()).await?; + let gas_price = get_gas_price(gateway.config.provider.clone()).await?; + let balance = get_native_balance(&gateway.config.provider, &invoice.to).await?; + + match gateway.config.transaction_type { + TransactionType::Legacy => { + transfer_gas_to_treasury_legacy(gateway, invoice, signer, chain_id, gas_price, balance) + .await + } + TransactionType::Eip1559 => { + transfer_gas_to_treasury_eip1559(gateway, invoice, signer, chain_id, balance).await + } + } +} diff --git a/src/web3/transfers/mod.rs b/src/web3/transfers/mod.rs new file mode 100644 index 0000000..4b1b553 --- /dev/null +++ b/src/web3/transfers/mod.rs @@ -0,0 +1 @@ +pub mod gas_transfers;