From 6e61e6de708e682e241bac40ff06522ba4ee3ad1 Mon Sep 17 00:00:00 2001 From: Theo Butler Date: Wed, 15 May 2024 21:10:02 -0400 Subject: [PATCH] feat: setup authorized signers (#3) --- Cargo.lock | 88 +++--- src/abi/Escrow.abi.json | 593 ++++++++++++++++++++++++++++++++++------ src/config.rs | 1 + src/main.rs | 102 ++++++- 4 files changed, 657 insertions(+), 127 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4758a42..14e976a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,9 +49,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525448f6afc1b70dd0f9d0a8145631bf2f5e434678ab23ab18409ca264cae6b3" +checksum = "db8aa973e647ec336810a9356af8aea787249c9d00b1525359f3db29a68d231b" dependencies = [ "alloy-rlp", "bytes", @@ -81,9 +81,23 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.7.2" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dbd17d67f3e89478c8a634416358e539e577899666c927bc3d2b1328ee9b6ca" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.63", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89c80a2cb97e7aa48611cbb63950336f9824a174cdf670527cc6465078a26ea1" +checksum = "2c6da95adcf4760bb4b108fefa51d50096c5e5fdd29ee72fed3e86ee414f2e34" dependencies = [ "alloy-sol-macro-input", "const-hex", @@ -99,9 +113,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58894b58ac50979eeac6249661991ac40b9d541830d9a725f7714cc9ef08c23" +checksum = "32c8da04c1343871fb6ce5a489218f9c85323c8340a36e9106b5fc98d4dd59d5" dependencies = [ "const-hex", "dunce", @@ -114,9 +128,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399287f68d1081ed8b1f4903c49687658b95b142207d7cb4ae2f4813915343ef" +checksum = "40a64d2d2395c1ac636b62419a7b17ec39031d6b2367e66e9acbf566e6055e9c" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -482,9 +496,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ "serde", ] @@ -615,9 +629,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.11.3" +version = "1.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba00838774b4ab0233e355d26710fbfc8327a05c017f6dc4873f876d1f79f78" +checksum = "70ff96486ccc291d36a958107caf2c0af8c78c0af7d31ae2f35ce055130de1a6" dependencies = [ "cfg-if", "cpufeatures", @@ -742,9 +756,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" dependencies = [ "darling_core", "darling_macro", @@ -752,9 +766,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" dependencies = [ "fnv", "ident_case", @@ -766,9 +780,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", @@ -3266,9 +3280,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rusty-fork" @@ -3457,18 +3471,18 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", @@ -3488,9 +3502,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -3726,9 +3740,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" @@ -3802,9 +3816,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa0cefd02f532035d83cfec82647c6eb53140b0485220760e669f4bad489e36" +checksum = "b8db114c44cf843a8bacd37a146e37987a0b823a0e8bc4fdc610c9c72ab397a5" dependencies = [ "paste", "proc-macro2", @@ -4087,21 +4101,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.12" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.12", + "toml_edit 0.22.13", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] @@ -4130,9 +4144,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.12" +version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" dependencies = [ "indexmap 2.2.6", "serde", diff --git a/src/abi/Escrow.abi.json b/src/abi/Escrow.abi.json index 2bff501..04d2837 100644 --- a/src/abi/Escrow.abi.json +++ b/src/abi/Escrow.abi.json @@ -1,167 +1,602 @@ [ { + "type": "function", + "name": "MAX_THAWING_PERIOD", "inputs": [], - "name": "EscrowNotThawing", - "type": "error" + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" }, { + "type": "function", + "name": "allocationIDTracker", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract AllocationIDTracker" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "authorizeSigner", "inputs": [ + { "name": "signer", "type": "address", "internalType": "address" }, { - "internalType": "uint256", - "name": "currentTimestamp", - "type": "uint256" + "name": "proofDeadline", + "type": "uint256", + "internalType": "uint256" }, + { "name": "proof", "type": "bytes", "internalType": "bytes" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "authorizedSigners", + "inputs": [ + { "name": "signer", "type": "address", "internalType": "address" } + ], + "outputs": [ + { "name": "sender", "type": "address", "internalType": "address" }, { - "internalType": "uint256", "name": "thawEndTimestamp", - "type": "uint256" + "type": "uint256", + "internalType": "uint256" } ], - "name": "EscrowStillThawing", - "type": "error" + "stateMutability": "view" + }, + { + "type": "function", + "name": "cancelThawSigner", + "inputs": [ + { "name": "signer", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "deposit", + "inputs": [ + { "name": "receiver", "type": "address", "internalType": "address" }, + { "name": "amount", "type": "uint256", "internalType": "uint256" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "depositMany", + "inputs": [ + { + "name": "receivers", + "type": "address[]", + "internalType": "address[]" + }, + { "name": "amounts", "type": "uint256[]", "internalType": "uint256[]" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "escrowAccounts", + "inputs": [ + { "name": "sender", "type": "address", "internalType": "address" }, + { "name": "receiver", "type": "address", "internalType": "address" } + ], + "outputs": [ + { "name": "balance", "type": "uint256", "internalType": "uint256" }, + { + "name": "amountThawing", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "thawEndTimestamp", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" }, { + "type": "function", + "name": "escrowToken", "inputs": [], - "name": "InputsLengthMismatch", - "type": "error" + "outputs": [ + { "name": "", "type": "address", "internalType": "contract IERC20" } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getEscrowAccountFromSignerAddress", + "inputs": [ + { "name": "signer", "type": "address", "internalType": "address" }, + { "name": "receiver", "type": "address", "internalType": "address" } + ], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct Escrow.EscrowAccount", + "components": [ + { "name": "balance", "type": "uint256", "internalType": "uint256" }, + { + "name": "amountThawing", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "thawEndTimestamp", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getEscrowAmount", + "inputs": [ + { "name": "sender", "type": "address", "internalType": "address" }, + { "name": "receiver", "type": "address", "internalType": "address" } + ], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" }, { + "type": "function", + "name": "redeem", "inputs": [ { - "internalType": "uint256", - "name": "available", - "type": "uint256" + "name": "signedRAV", + "type": "tuple", + "internalType": "struct TAPVerifier.SignedRAV", + "components": [ + { + "name": "rav", + "type": "tuple", + "internalType": "struct TAPVerifier.ReceiptAggregateVoucher", + "components": [ + { + "name": "allocationId", + "type": "address", + "internalType": "address" + }, + { + "name": "timestampNs", + "type": "uint64", + "internalType": "uint64" + }, + { + "name": "valueAggregate", + "type": "uint128", + "internalType": "uint128" + } + ] + }, + { "name": "signature", "type": "bytes", "internalType": "bytes" } + ] }, { - "internalType": "uint256", - "name": "required", - "type": "uint256" + "name": "allocationIDProof", + "type": "bytes", + "internalType": "bytes" } ], - "name": "InsufficientEscrow", - "type": "error" + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "revokeAuthorizedSigner", + "inputs": [ + { "name": "signer", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "revokeSignerThawingPeriod", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" }, { + "type": "function", + "name": "staking", "inputs": [], - "name": "InsufficientThawAmount", - "type": "error" + "outputs": [ + { "name": "", "type": "address", "internalType": "contract IStaking" } + ], + "stateMutability": "view" }, { + "type": "function", + "name": "tapVerifier", "inputs": [], - "name": "InvalidRAVSigner", - "type": "error" + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract TAPVerifier" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "thaw", + "inputs": [ + { "name": "receiver", "type": "address", "internalType": "address" }, + { "name": "amount", "type": "uint256", "internalType": "uint256" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "thawSigner", + "inputs": [ + { "name": "signer", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" }, { + "type": "function", + "name": "withdraw", + "inputs": [ + { "name": "receiver", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "withdrawEscrowThawingPeriod", "inputs": [], - "name": "InvalidSignerProof", - "type": "error" + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" }, { + "type": "event", + "name": "AuthorizeSigner", "inputs": [ { - "internalType": "uint256", - "name": "thawingPeriod", - "type": "uint256" + "name": "signer", + "type": "address", + "indexed": true, + "internalType": "address" }, { - "internalType": "uint256", - "name": "maxThawingPeriod", - "type": "uint256" + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" } ], - "name": "RevokeSignerThawingTooLong", - "type": "error" + "anonymous": false }, { + "type": "event", + "name": "CancelThaw", "inputs": [ { - "internalType": "address", - "name": "signer", - "type": "address" + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" }, { - "internalType": "address", - "name": "authorizingSender", - "type": "address" + "name": "receiver", + "type": "address", + "indexed": true, + "internalType": "address" } ], - "name": "SignerAlreadyAuthorized", - "type": "error" + "anonymous": false }, { - "inputs": [], - "name": "SignerNotAuthorized", - "type": "error" + "type": "event", + "name": "CancelThawSigner", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "authorizedSigner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "thawEndTimestamp", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false }, { + "type": "event", + "name": "Deposit", "inputs": [ { - "internalType": "address", - "name": "signer", - "type": "address" + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "receiver", + "type": "address", + "indexed": true, + "internalType": "address" }, { - "internalType": "address", + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Redeem", + "inputs": [ + { "name": "sender", - "type": "address" + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "receiver", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "allocationID", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "expectedAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "actualAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" } ], - "name": "SignerNotAuthorizedBySender", - "type": "error" + "anonymous": false }, { - "inputs": [], - "name": "SignerNotThawing", - "type": "error" + "type": "event", + "name": "RevokeAuthorizedSigner", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "authorizedSigner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false }, { + "type": "event", + "name": "Thaw", "inputs": [ { - "internalType": "uint256", - "name": "currentTimestamp", - "type": "uint256" + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "receiver", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "totalAmountThawing", + "type": "uint256", + "indexed": false, + "internalType": "uint256" }, { - "internalType": "uint256", "name": "thawEndTimestamp", - "type": "uint256" + "type": "uint256", + "indexed": false, + "internalType": "uint256" } ], - "name": "SignerStillThawing", - "type": "error" + "anonymous": false }, { + "type": "event", + "name": "ThawSigner", "inputs": [ { - "internalType": "uint256", - "name": "thawingPeriod", - "type": "uint256" + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" }, { - "internalType": "uint256", - "name": "maxThawingPeriod", - "type": "uint256" + "name": "authorizedSigner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "thawEndTimestamp", + "type": "uint256", + "indexed": false, + "internalType": "uint256" } ], - "name": "WithdrawEscrowThawingTooLong", - "type": "error" + "anonymous": false }, { + "type": "event", + "name": "Withdraw", "inputs": [ { - "internalType": "address[]", - "name": "receivers", - "type": "address[]" + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" }, { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" + "name": "receiver", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" } ], - "name": "depositMany", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" + "anonymous": false + }, + { "type": "error", "name": "EscrowNotThawing", "inputs": [] }, + { + "type": "error", + "name": "EscrowStillThawing", + "inputs": [ + { + "name": "currentTimestamp", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "thawEndTimestamp", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { "type": "error", "name": "InputsLengthMismatch", "inputs": [] }, + { + "type": "error", + "name": "InsufficientEscrow", + "inputs": [ + { "name": "available", "type": "uint256", "internalType": "uint256" }, + { "name": "required", "type": "uint256", "internalType": "uint256" } + ] + }, + { "type": "error", "name": "InsufficientThawAmount", "inputs": [] }, + { "type": "error", "name": "InvalidRAVSigner", "inputs": [] }, + { "type": "error", "name": "InvalidSignerProof", "inputs": [] }, + { + "type": "error", + "name": "RevokeSignerThawingTooLong", + "inputs": [ + { + "name": "thawingPeriod", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "maxThawingPeriod", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "SignerAlreadyAuthorized", + "inputs": [ + { "name": "signer", "type": "address", "internalType": "address" }, + { + "name": "authorizingSender", + "type": "address", + "internalType": "address" + } + ] + }, + { "type": "error", "name": "SignerNotAuthorized", "inputs": [] }, + { + "type": "error", + "name": "SignerNotAuthorizedBySender", + "inputs": [ + { "name": "signer", "type": "address", "internalType": "address" }, + { "name": "sender", "type": "address", "internalType": "address" } + ] + }, + { "type": "error", "name": "SignerNotThawing", "inputs": [] }, + { + "type": "error", + "name": "SignerStillThawing", + "inputs": [ + { + "name": "currentTimestamp", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "thawEndTimestamp", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "WithdrawEscrowThawingTooLong", + "inputs": [ + { + "name": "thawingPeriod", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "maxThawingPeriod", + "type": "uint256", + "internalType": "uint256" + } + ] } ] diff --git a/src/config.rs b/src/config.rs index ab477b7..bb362a2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -18,6 +18,7 @@ pub struct Config { #[serde_as(as = "DisplayFromStr")] pub rpc_url: Hidden, pub secret_key: Hidden, + pub signers: Vec>, pub update_interval_seconds: u32, } diff --git a/src/main.rs b/src/main.rs index 8a62fac..164528f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,14 +7,17 @@ use config::Config; use ethers::middleware::contract::abigen; use ethers::prelude::{Http, Provider, SignerMiddleware}; use ethers::signers::{LocalWallet, Signer as _}; -use ethers::types::U256; +use ethers::types::{Bytes, H256, U256}; +use ethers::utils::keccak256; use serde::Deserialize; use serde_with::serde_as; use std::collections::{HashMap, HashSet}; use std::sync::Arc; +use std::time::{SystemTime, UNIX_EPOCH}; use std::{env, fs, time::Duration}; use thegraph_core::client::Client as SubgraphClient; use thegraph_core::types::alloy_primitives::Address; +use thegraph_core::types::alloy_sol_types::SolValue; use tokio::time::{interval, MissedTickBehavior}; #[global_allocator] @@ -39,10 +42,10 @@ async fn main() -> anyhow::Result<()> { .context("failed to load config")?; tracing::info!("{config:#?}"); - let wallet = + let sender = LocalWallet::from_bytes(config.secret_key.as_slice())?.with_chain_id(config.chain_id); - let sender_address = wallet.address(); - tracing::info!(%sender_address); + let sender_address: Address = sender.address().0.into(); + tracing::info!(sender = %sender_address); let provider = Provider::new(Http::new_with_client( config.rpc_url.clone(), @@ -50,22 +53,68 @@ async fn main() -> anyhow::Result<()> { .timeout(Duration::from_secs(10)) .build()?, )); - let provider = Arc::new(SignerMiddleware::new(provider, wallet)); + let provider = Arc::new(SignerMiddleware::new(provider, sender.clone())); let contract = Escrow::new( ethers::abi::Address::from(config.escrow_contract.0 .0), provider.clone(), ); - let debts = track_receipts(&config.kafka, config.graph_env) - .await - .context("failed to start kafka client")?; - let http_client = reqwest::Client::builder() .timeout(Duration::from_secs(10)) .build()?; let mut network_subgraph = SubgraphClient::new(http_client.clone(), config.network_subgraph); let mut escrow_subgraph = SubgraphClient::new(http_client.clone(), config.escrow_subgraph); - let sender = Address::from(sender_address.0); + + let authorized_signers = authorized_signers(&mut escrow_subgraph, &sender_address) + .await + .context("fetch authorized signers")?; + for signer in config.signers { + let signer = LocalWallet::from_bytes(signer.as_slice())?.with_chain_id(config.chain_id); + let authorized = authorized_signers.contains(&signer.address().0.into()); + tracing::info!(signer = %signer.address(), %authorized); + let deadline_offset_s = 60; + let deadline: U256 = (SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() + + deadline_offset_s) + .into(); + let mut proof_message = [0u8; 84]; + U256::from(config.chain_id).to_big_endian(&mut proof_message[0..32]); + deadline.to_big_endian(&mut proof_message[32..64]); + proof_message[64..].copy_from_slice(&sender.address().0.abi_encode_packed()); + let hash = H256(keccak256(proof_message)); + let signature = signer + .sign_message(hash) + .await + .context("sign authorization proof")?; + let mut proof = [0u8; 65]; + signature.r.to_big_endian(&mut proof[0..32]); + signature.s.to_big_endian(&mut proof[32..64]); + proof[64] = signature.v as u8; + let proof: Bytes = proof.into(); + let tx = contract.authorize_signer(signer.address(), deadline, proof); + match tx.send().await { + Ok(result) => { + result.await.context("authorize tx provider error")?; + } + Err(err) => match err.decode_contract_revert::() { + // We may encounter this condition if the subgraph is behind. + Some(EscrowErrors::SignerAlreadyAuthorized { .. }) => (), + Some(revert) => { + return Err(anyhow!("Revert({revert:?})").context("authorize signer")); + } + None => { + return Err(anyhow!(err).context("authorize signer")); + } + }, + }; + tracing::info!(signer = %signer.address(), "authorized"); + } + + let debts = track_receipts(&config.kafka, config.graph_env) + .await + .context("failed to start kafka client")?; let mut interval = interval(Duration::from_secs(config.update_interval_seconds as u64)); interval.set_missed_tick_behavior(MissedTickBehavior::Skip); @@ -79,7 +128,7 @@ async fn main() -> anyhow::Result<()> { continue; } }; - let escrow_accounts = match escrow_accounts(&mut escrow_subgraph, &sender).await { + let escrow_accounts = match escrow_accounts(&mut escrow_subgraph, &sender_address).await { Ok(escrow_accounts) => escrow_accounts, Err(escrow_accounts_err) => { tracing::error!(%escrow_accounts_err); @@ -161,6 +210,37 @@ fn next_balance(debt: u128) -> u128 { (next_round as u128 * GRT).min(MAX_DEPOSIT) } +async fn authorized_signers( + escrow_subgraph: &mut SubgraphClient, + sender: &Address, +) -> anyhow::Result> { + #[derive(Deserialize)] + struct Data { + sender: Option, + } + #[derive(Deserialize)] + struct Sender { + signers: Vec, + } + #[derive(Deserialize)] + struct Signer { + id: Address, + } + let data = escrow_subgraph + .query::(format!( + r#"{{ sender(id:"{sender:?}") {{ signers {{ id }} }} }}"#, + )) + .await + .map_err(|err| anyhow!(err))?; + let signers = data + .sender + .into_iter() + .flat_map(|s| s.signers) + .map(|s| s.id) + .collect(); + Ok(signers) +} + async fn active_indexers( network_subgraph: &mut SubgraphClient, ) -> anyhow::Result> {