From 217d1a3a0fea0b189b661252989ee3416549cf44 Mon Sep 17 00:00:00 2001 From: Vignesh Date: Tue, 9 Apr 2024 05:42:44 +0530 Subject: [PATCH] added oracle support and multiToken mode --- backend/src/abi/MultiTokenPaymasterAbi.ts | 844 ++++++++++++++++++++++ backend/src/constants/Pimlico.ts | 26 + backend/src/migrations/002.apiKeys.sql | 2 + backend/src/paymaster/index.ts | 60 ++ backend/src/routes/admin.ts | 4 + backend/src/routes/index.ts | 54 +- backend/src/utils/common.ts | 13 +- backend/src/utils/interface.ts | 8 +- 8 files changed, 1003 insertions(+), 8 deletions(-) create mode 100644 backend/src/abi/MultiTokenPaymasterAbi.ts diff --git a/backend/src/abi/MultiTokenPaymasterAbi.ts b/backend/src/abi/MultiTokenPaymasterAbi.ts new file mode 100644 index 0000000..ce61a7c --- /dev/null +++ b/backend/src/abi/MultiTokenPaymasterAbi.ts @@ -0,0 +1,844 @@ +export default [ + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "contract IEntryPoint", + "name": "_entryPoint", + "type": "address" + }, + { + "internalType": "address", + "name": "_verifyingSigner", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "inputs": [], + "name": "CanNotWithdrawToZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "CannotBeUnrealisticValue", + "type": "error" + }, + { + "inputs": [], + "name": "DEXRouterCannotBeZero", + "type": "error" + }, + { + "inputs": [], + "name": "DepositCanNotBeZero", + "type": "error" + }, + { + "inputs": [], + "name": "EntryPointCannotBeZero", + "type": "error" + }, + { + "inputs": [], + "name": "FeeReceiverCannotBeZero", + "type": "error" + }, + { + "inputs": [], + "name": "NativeTokenBalanceZero", + "type": "error" + }, + { + "inputs": [], + "name": "NativeTokensWithdrawalFailed", + "type": "error" + }, + { + "inputs": [], + "name": "OwnerCannotBeZero", + "type": "error" + }, + { + "inputs": [], + "name": "TokensAndAmountsLengthMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "VerifyingSignerCannotBeZero", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "_oldThresholdCost", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "_newThresholdCost", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "_actor", + "type": "address" + } + ], + "name": "EPGasThresholdChange", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_oldfeeReceiver", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_newfeeReceiver", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_actor", + "type": "address" + } + ], + "name": "FeeReceiverChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Received", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "totalCharge", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "oracleAggregator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "priceMarkup", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "userOpHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "exchangeRate", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "enum TestTokenPaymaster.ExchangeRateSource", + "name": "priceSource", + "type": "uint8" + } + ], + "name": "TokenPaymasterOperation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "charge", + "type": "uint256" + } + ], + "name": "TokenPaymentDue", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_oldSigner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_newSigner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_actor", + "type": "address" + } + ], + "name": "VerifyingSignerChanged", + "type": "event" + }, + { + "inputs": [], + "name": "UNACCOUNTED_COST", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "unstakeDelaySec", + "type": "uint32" + } + ], + "name": "addStake", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "deposit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "entryPoint", + "outputs": [ + { + "internalType": "contract IEntryPoint", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeReceiver", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getDeposit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "initCode", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "callGasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "verificationGasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "preVerificationGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPriorityFeePerGas", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "paymasterAndData", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct UserOperation", + "name": "userOp", + "type": "tuple" + }, + { + "internalType": "enum TestTokenPaymaster.ExchangeRateSource", + "name": "priceSource", + "type": "uint8" + }, + { + "internalType": "uint48", + "name": "validUntil", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "validAfter", + "type": "uint48" + }, + { + "internalType": "address", + "name": "feeToken", + "type": "address" + }, + { + "internalType": "address", + "name": "oracleAggregator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "exchangeRate", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "priceMarkup", + "type": "uint32" + } + ], + "name": "getHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "paymasterAndData", + "type": "bytes" + } + ], + "name": "parsePaymasterAndData", + "outputs": [ + { + "internalType": "enum TestTokenPaymaster.ExchangeRateSource", + "name": "priceSource", + "type": "uint8" + }, + { + "internalType": "uint48", + "name": "validUntil", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "validAfter", + "type": "uint48" + }, + { + "internalType": "address", + "name": "feeToken", + "type": "address" + }, + { + "internalType": "address", + "name": "oracleAggregator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "exchangeRate", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "priceMarkup", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum IPaymaster.PostOpMode", + "name": "mode", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "context", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "actualGasCost", + "type": "uint256" + } + ], + "name": "postOp", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newFeeReceiver", + "type": "address" + } + ], + "name": "setFeeReceiver", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_newThresholdCost", + "type": "uint256" + } + ], + "name": "setUnaccountedEPGasThreshold", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newVerifyingSigner", + "type": "address" + } + ], + "name": "setVerifyingSigner", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unlockStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "initCode", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "callGasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "verificationGasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "preVerificationGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPriorityFeePerGas", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "paymasterAndData", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "internalType": "struct UserOperation", + "name": "userOp", + "type": "tuple" + }, + { + "internalType": "bytes32", + "name": "userOpHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "maxCost", + "type": "uint256" + } + ], + "name": "validatePaymasterUserOp", + "outputs": [ + { + "internalType": "bytes", + "name": "context", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "validationData", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "verifyingSigner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "dest", + "type": "address" + } + ], + "name": "withdrawAllNative", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdrawERC20", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "withdrawERC20Full", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20[]", + "name": "token", + "type": "address[]" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "amount", + "type": "uint256[]" + } + ], + "name": "withdrawMultipleERC20", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20[]", + "name": "token", + "type": "address[]" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "withdrawMultipleERC20Full", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "withdrawAddress", + "type": "address" + } + ], + "name": "withdrawStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "withdrawAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdrawTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] as const; \ No newline at end of file diff --git a/backend/src/constants/Pimlico.ts b/backend/src/constants/Pimlico.ts index b69d011..a226ede 100644 --- a/backend/src/constants/Pimlico.ts +++ b/backend/src/constants/Pimlico.ts @@ -178,4 +178,30 @@ export const PAYMASTER_ADDRESS: Record> = { }, } +/** + * Example Structure for adding deployed multi-token paymasters on AWS secrets manager + */ +export const MULTI_TOKEN_PAYMASTERS: Record> = { + "80001": { + "0x453478E2E0c846c069e544405d5877086960BEf2": "0xe85649152D15825F2226B2d9C49c07b1cd2b36C7" + }, + "28122024": { + "0x453478E2E0c846c069e544405d5877086960BEf2": "0xe85649152D15825F2226B2d9C49c07b1cd2b36C7" + } +} + +/** + * Example Structure for adding deployed multi-token oracles on AWS secrets manager + */ +export const MULTI_TOKEN_ORACLES: Record> = { + "80001": { + "0x453478E2E0c846c069e544405d5877086960BEf2": "0x75bDb3803d671ac3051112E8A7745fF88eEd6d94" + }, + "28122024": { + "0x453478E2E0c846c069e544405d5877086960BEf2": "0x75bDb3803d671ac3051112E8A7745fF88eEd6d94" + } +} + + + export const bytecode = "0x610120604081815234620004105760a08262002084803803809162000025828562000415565b833981010312620004105781516001600160a01b0380821693918490036200041057602090818301519080821682036200041057620000668585016200044f565b93608062000077606083016200044f565b9101519682881693848903620004105762000092336200047f565b6080528060a0528560e0526101009782895265030d4000864760c51b60018060c01b036001541617600155600094338587541603620003ce57156200037b57620000dc906200047f565b86519586868163313ce56760e01b9485825260049a8b915afa90811562000371579060ff9187916200034f575b5016604d81116200033c5784918791600a0a60c052888a5180948193878352165afa908115620003325760089160ff91879162000310575b501603620002ba5790858592885194859384928352165afa918215620002af5760089260ff92906200027b575b5016036200022157505051611bbd9182620004c7833960805182818161074c015281816108900152818161096501528181610a2201528181610ad3015281816112c00152818161138a01526114d7015260a05182818161017a0152818161030201528181610beb0152611839015260c051828181610e9f015281816112040152611749015260e051828181610ddb01528181610e3301526116d30152518181816106e501528181610e5c01526116fc0152f35b608492519162461bcd60e51b8352820152603160248201527f50502d4552433230203a206e6174697665206173736574206f7261636c6520646044820152700cac6d2dac2d8e640daeae6e840c4ca407607b1b6064820152fd5b620002a09150843d8611620002a7575b62000297818362000415565b81019062000464565b386200016e565b503d6200028b565b8551903d90823e3d90fd5b865162461bcd60e51b8152808701869052602a60248201527f50502d4552433230203a20746f6b656e206f7261636c6520646563696d616c73604482015269040daeae6e840c4ca40760b31b6064820152608490fd5b6200032b9150883d8a11620002a75762000297818362000415565b3862000141565b88513d87823e3d90fd5b634e487b7160e01b865260118852602486fd5b6200036a9150883d8a11620002a75762000297818362000415565b3862000109565b89513d88823e3d90fd5b875162461bcd60e51b815260048101879052602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b6064878a519062461bcd60e51b825280600483015260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b600080fd5b601f909101601f19168101906001600160401b038211908210176200043957604052565b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036200041057565b9081602091031262000410575160ff81168103620004105790565b600080546001600160a01b039283166001600160a01b03198216811783559216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a356fe6040608081526004908136101561001557600080fd5b600091823560e01c9081630396cb601461133557838263205c287814611267575081633a34c83f146112275781633b97e856146111ce5781633c2154bc14610f7c5781633e04619d14610f36578163673a7e2814610dff5781636c5ec25c14610d90578163715018a614610cf35781638da5cb5b14610ca2578163914e245a14610c4b5781639dbdb97714610c105781639e281a9814610b8e578163a9a2340914610af7578163b0d691fe14610a8857838263bb9fe6bf146109d2578263c23a5cea1461090b57508163c399ec8814610817578163cdcf4b9b146107db57838263d0e30db01461070957508163efb1ad5d1461069a578163f2fde38b14610560578163f465c77e146101a2575063fc0c546a1461013157600080fd5b3461019e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019e576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fd5b9050823461055d576060917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc91838336011261055d5781359167ffffffffffffffff9586841161055957610160848301958536030112610559576102046114c0565b6001549177ffffffffffffffffffffffffffffffffffffffffffffffff831680156104fc576101248601907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec61025a838a611634565b905001967effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdf881661049f579069d3c21bcecceda10000009160e463ffffffff60c098891c16910135619c40026044350102020490602080971461041f575b506102c18761168f565b96835197828a52308552891b602c526f23b872dd000000000000000000000000600c5286866064601c8273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af13d15600188511417161561041457906103637fffffffffffffffffffffffffffffffffffffffff00000000000000000000000092878b5289865261168f565b9087890152881b16828701526034865286860197868910908911176103e6575086815286528351928360a0860152825b8481106103d4575050838301018190526080830152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168101030190f35b85810180830151908401528101610393565b6041907f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b8686637939f4248152fd5b6104299088611634565b60341161049b5760140135811161044057896102b7565b50848060649351927f08c379a000000000000000000000000000000000000000000000000000000000845283015260248201527f50502d4552433230203a20746f6b656e20616d6f756e7420746f6f20686967686044820152fd5b8580fd5b60648460208751917f08c379a0000000000000000000000000000000000000000000000000000000008352820152601e60248201527f50502d4552433230203a20696e76616c69642064617461206c656e67746800006044820152fd5b50602060649251917f08c379a0000000000000000000000000000000000000000000000000000000008352820152601860248201527f50502d4552433230203a207072696365206e6f742073657400000000000000006044820152fd5b8280fd5b80fd5b9050346105595760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261055957610599611419565b906105a261155d565b73ffffffffffffffffffffffffffffffffffffffff809216928315610617575050600054827fffffffffffffffffffffffff0000000000000000000000000000000000000000821617600055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a380f35b90602060849251917f08c379a0000000000000000000000000000000000000000000000000000000008352820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b50503461019e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019e576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b809184827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126107d75773ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691823b156107d257839060248351809581937fb760faf9000000000000000000000000000000000000000000000000000000008352309083015234905af19081156107c957506107b95750f35b6107c29061143c565b61055d5780f35b513d84823e3d90fd5b505050fd5b5050fd5b50503461019e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019e5760209051620f42408152f35b9190503461055957827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610559578051917f70a08231000000000000000000000000000000000000000000000000000000008352309083015260208260248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9182156109015783926108ca575b6020838351908152f35b9091506020813d82116108f9575b816108e56020938361147f565b8101031261055957602092505190386108c0565b3d91506108d8565b81513d85823e3d90fd5b809184346107d75760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126107d757610945611419565b61094d61155d565b73ffffffffffffffffffffffffffffffffffffffff807f000000000000000000000000000000000000000000000000000000000000000016803b1561049b57859283602492865197889586947fc23a5cea00000000000000000000000000000000000000000000000000000000865216908401525af19081156107c957506107b95750f35b809184346107d757827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126107d757610a0b61155d565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691823b156107d257839283918351809581937fbb9fe6bf0000000000000000000000000000000000000000000000000000000083525af19081156107c957506107b95750f35b50503461019e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019e576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b83903461019e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019e578035906003821015610559576024359067ffffffffffffffff90818311610b8a5736602384011215610b8a57820135908111610b86573660248284010111610b8657610b8392610b766114c0565b60246044359301906116b0565b80f35b8380fd5b8480fd5b50503461019e577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261055d57610b83610bc8611419565b610bd061155d565b6024359073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016611b68565b50503461019e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019e5760209051619c408152f35b50503461019e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019e5760209077ffffffffffffffffffffffffffffffffffffffffffffffff600154169051908152f35b50503461019e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019e5773ffffffffffffffffffffffffffffffffffffffff60209254169051908152f35b833461055d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261055d57610d2a61155d565b600073ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50503461019e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019e576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b833461055d57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261055d57610e577f00000000000000000000000000000000000000000000000000000000000000006118f7565b610e807f00000000000000000000000000000000000000000000000000000000000000006118f7565b9077ffffffffffffffffffffffffffffffffffffffffffffffff9182807f00000000000000000000000000000000000000000000000000000000000000001691168382820216918183041490151715610f0a5790610edd916115dc565b167fffffffffffffffff000000000000000000000000000000000000000000000000600154161760015580f35b6024846011877f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b50503461019e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019e5760209063ffffffff60015460c01c169051908152f35b83833461019e57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019e57610fb4611406565b926024359363ffffffff90818616918287036111c957610fd261155d565b81169262124f80841161116c57620f424080851061110f57831161108d57507ffed7660357162e9e060534e05beba94ac6e3bfb17b1f793bd7350aaed0e9e8c4949577ffffffffffffffffffffffffffffffffffffffffffffffff7bffffffff0000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000006001549360e01b169360c01b169116171760015582519182526020820152a180f35b60849060208651917f08c379a00000000000000000000000000000000000000000000000000000000083528201526024808201527f50502d4552433230203a20757064617465207468726573686f6c6420746f6f2060448201527f68696768000000000000000000000000000000000000000000000000000000006064820152fd5b60648260208851917f08c379a0000000000000000000000000000000000000000000000000000000008352820152602060248201527f50502d4552433230203a207072696365206d61726b65757020746f6f206c6f776044820152fd5b60649060208651917f08c379a0000000000000000000000000000000000000000000000000000000008352820152602060248201527f50502d4552433230203a207072696365206d61726b757020746f6f20686967686044820152fd5b600080fd5b50503461019e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019e57602090517f00000000000000000000000000000000000000000000000000000000000000008152f35b50503461019e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019e5760209060015460e01c9051908152f35b809184346107d757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126107d7576112a0611419565b6112a861155d565b73ffffffffffffffffffffffffffffffffffffffff807f000000000000000000000000000000000000000000000000000000000000000016803b1561049b57859283604492865197889586947f205c2878000000000000000000000000000000000000000000000000000000008652169084015260243560248401525af19081156107c957506107b95750f35b91905060207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610559578261136b611406565b61137361155d565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016803b156105595763ffffffff91602491855196879485937f0396cb60000000000000000000000000000000000000000000000000000000008552169083015234905af19081156107c957506113fd575080f35b610b839061143c565b6004359063ffffffff821682036111c957565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036111c957565b67ffffffffffffffff811161145057604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761145057604052565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036114ff57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f53656e646572206e6f7420456e747279506f696e7400000000000000000000006044820152fd5b73ffffffffffffffffffffffffffffffffffffffff60005416330361157e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b9077ffffffffffffffffffffffffffffffffffffffffffffffff80911691821561160557160490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156111c9570180359067ffffffffffffffff82116111c9576020019181360383136111c957565b8115611605570490565b3573ffffffffffffffffffffffffffffffffffffffff811681036111c95790565b92919260038110156118b1576002146118ac5769d3c21bcecceda10000006116f77f00000000000000000000000000000000000000000000000000000000000000006118f7565b6117207f00000000000000000000000000000000000000000000000000000000000000006118f7565b9060015461177077ffffffffffffffffffffffffffffffffffffffffffffffff928380841695817f00000000000000000000000000000000000000000000000000000000000000001602166115dc565b918160e01c921691620f4240808402918561178b8185611685565b82840110938415611894575b50505050611864575b505063ffffffff60015460c01c163a619c4002850102020492806020116111c957813584811161180c575b506034116111c95760206040917f472a42a044527b87df02c0ce8e6c00c0057fac40d6c424c93c24b02322eb14b593835195865282860152013560601c92a2565b816034116111c9578461185e9103602084013560601c73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016611b68565b386117cb565b8192507fffffffffffffffff000000000000000000000000000000000000000000000000161760015538806117a0565b6118a092939450611685565b91031138808581611797565b505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b519069ffffffffffffffffffff821682036111c957565b60a073ffffffffffffffffffffffffffffffffffffffff916004604051809481937ffeaf968c000000000000000000000000000000000000000000000000000000008352165afa8015611b5c576000809281908293611b06575b506000841315611aa8577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd5d00420190428211611a795710611a1b5769ffffffffffffffffffff8091169116106119bd5777ffffffffffffffffffffffffffffffffffffffffffffffff1690565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f50502d4552433230203a205374616c65207072696365000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f50502d4552433230203a20496e636f6d706c65746520726f756e6400000000006044820152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f50502d4552433230203a20436861696e6c696e6b207072696365203c3d2030006044820152fd5b935050905060a0823d8211611b54575b81611b2360a0938361147f565b8101031261055d5750611b35816118e0565b6020820151611b4b6080606085015194016118e0565b91909238611951565b3d9150611b16565b6040513d6000823e3d90fd5b60109260209260145260345260446000938480936fa9059cbb00000000000000000000000082525af13d156001835114171615611ba457603452565b806390b8ec1860209252fdfea164736f6c6343000812000a"; diff --git a/backend/src/migrations/002.apiKeys.sql b/backend/src/migrations/002.apiKeys.sql index ce07966..881f5a7 100644 --- a/backend/src/migrations/002.apiKeys.sql +++ b/backend/src/migrations/002.apiKeys.sql @@ -8,6 +8,8 @@ CREATE TABLE IF NOT EXISTS api_keys ( PRIVATE_KEY varchar NOT NULL, SUPPORTED_NETWORKS varchar DEFAULT NULL, ERC20_PAYMASTERS varchar DEFAULT NULL, + MULTI_TOKEN_PAYMASTERS varchar DEFAULT NULL, + MULTI_TOKEN_ORACLES varchar DEFAULT NULL, TRANSACTION_LIMIT INT NOT NULL, NO_OF_TRANSACTIONS_IN_A_MONTH int, INDEXER_ENDPOINT varchar diff --git a/backend/src/paymaster/index.ts b/backend/src/paymaster/index.ts index 76dac2c..7ca58cd 100644 --- a/backend/src/paymaster/index.ts +++ b/backend/src/paymaster/index.ts @@ -7,6 +7,7 @@ import { PimlicoPaymaster } from './pimlico.js'; import ErrorMessage from '../constants/ErrorMessage.js'; import { PAYMASTER_ADDRESS } from '../constants/Pimlico.js'; import { getEtherscanFee } from '../utils/common.js'; +import MultiTokenPaymasterAbi from '../abi/MultiTokenPaymasterAbi.js'; export class Paymaster { feeMarkUp: BigNumber; @@ -64,6 +65,64 @@ export class Paymaster { } } + async getPaymasterAndDataForMultiTokenPaymaster(userOp: any, validUntil: string, validAfter: string, feeToken: string, oracleAggregator: string, paymasterContract: Contract, signer: Wallet) { + const exchangeRate = 10000000; // This is for setting min tokens required for the txn that gets validated on estimate + const priceMarkup = 1100000; // 10% more of the actual cost. Can be anything between 1e6 to 2e6 + // actual signing... + // priceSource inputs available 0 - for using external exchange price and 1 - for oracle based price + const hash = await paymasterContract.getHash( + userOp, + 1, + validUntil, + validAfter, + feeToken, + oracleAggregator, + exchangeRate, + priceMarkup, + ); + + const sig = await signer.signMessage(arrayify(hash)); + + const paymasterAndData = hexConcat([ + paymasterContract.address, + '0x01', + defaultAbiCoder.encode( + ['uint48', 'uint48', 'address', 'address', 'uint256', 'uint32'], + [validUntil, validAfter, feeToken, oracleAggregator, exchangeRate, priceMarkup] + ), + sig, + ]); + + return paymasterAndData; + } + + async signMultiTokenPaymaster(userOp: any, validUntil: string, validAfter: string, entryPoint: string, paymasterAddress: string, feeToken: string, oracleAggregator: string, bundlerRpc: string, signer: Wallet, log?: FastifyBaseLogger) { + try { + const provider = new providers.JsonRpcProvider(bundlerRpc); + const paymasterContract = new ethers.Contract(paymasterAddress, MultiTokenPaymasterAbi, provider); + userOp.paymasterAndData = await this.getPaymasterAndDataForMultiTokenPaymaster(userOp, validUntil, validAfter, feeToken, oracleAggregator, paymasterContract, signer); + + if (!userOp.signature) userOp.signature = '0x'; + const response = await provider.send('eth_estimateUserOperationGas', [userOp, entryPoint]); + userOp.verificationGasLimit = response.verificationGasLimit; + userOp.preVerificationGas = response.preVerificationGas; + userOp.callGasLimit = response.callGasLimit; + const paymasterAndData = await this.getPaymasterAndDataForMultiTokenPaymaster(userOp, validUntil, validAfter, feeToken, oracleAggregator, paymasterContract, signer); + + const returnValue = { + paymasterAndData, + verificationGasLimit: response.verificationGasLimit, + preVerificationGas: response.preVerificationGas, + callGasLimit: response.callGasLimit, + } + + return returnValue; + } catch (err: any) { + if (log) log.error(err, 'signCombinedPaymaster'); + throw new Error('Failed to process request to bundler. Please contact support team RawErrorMsg:' + err.message) + } + } + async pimlico(userOp: any, bundlerRpc: string, entryPoint: string, PaymasterAddress: string, log?: FastifyBaseLogger) { try { const provider = new providers.JsonRpcProvider(bundlerRpc); @@ -253,6 +312,7 @@ export class Paymaster { const encodedData = paymasterContract.interface.encodeFunctionData('depositFunds', []); const etherscanFeeData = await getEtherscanFee(chainId); + console.log('etherscanFeeData: ', etherscanFeeData); let feeData; if (etherscanFeeData) { feeData = etherscanFeeData; diff --git a/backend/src/routes/admin.ts b/backend/src/routes/admin.ts index 850830f..e37c038 100644 --- a/backend/src/routes/admin.ts +++ b/backend/src/routes/admin.ts @@ -97,6 +97,8 @@ const adminRoutes: FastifyPluginAsync = async (server) => { PRIVATE_KEY, \ SUPPORTED_NETWORKS, \ ERC20_PAYMASTERS, \ + MULTI_TOKEN_PAYMASTERS, \ + MULTI_TOKEN_ORACLES, \ TRANSACTION_LIMIT, \ NO_OF_TRANSACTIONS_IN_A_MONTH, \ INDEXER_ENDPOINT) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [ @@ -105,6 +107,8 @@ const adminRoutes: FastifyPluginAsync = async (server) => { hmac, body.SUPPORTED_NETWORKS, body.ERC20_PAYMASTERS, + body.MULTI_TOKEN_PAYMASTERS ?? null, + body.MULTI_TOKEN_ORACLES ?? null, body.TRANSACTION_LIMIT ?? 0, body.NO_OF_TRANSACTIONS_IN_A_MONTH ?? 10, body.INDEXER_ENDPOINT ?? process.env.DEFAULT_INDEXER_ENDPOINT diff --git a/backend/src/routes/index.ts b/backend/src/routes/index.ts index 2d5507d..705e97a 100644 --- a/backend/src/routes/index.ts +++ b/backend/src/routes/index.ts @@ -56,13 +56,15 @@ const routes: FastifyPluginAsync = async (server) => { const userOp = body.params[0]; const entryPoint = body.params[1]; const context = body.params[2]; - const gasToken = context?.token ? context.token : null; + let gasToken = context?.token ? context.token : null; const mode = context?.mode ? String(context.mode) : null; const chainId = query['chainId'] ?? body.params[3]; const api_key = query['apiKey'] ?? body.params[4]; if (!api_key) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) let customPaymasters = []; + let multiTokenPaymasters = []; + let multiTokenOracles = []; let privateKey = ''; let supportedNetworks; let noOfTxns; @@ -83,6 +85,14 @@ const routes: FastifyPluginAsync = async (server) => { const buffer = Buffer.from(secrets['ERC20_PAYMASTERS'], 'base64'); customPaymasters = JSON.parse(buffer.toString()); } + if (secrets['MULTI_TOKEN_PAYMASTERS']) { + const buffer = Buffer.from(secrets['MULTI_TOKEN_PAYMASTERS'], 'base64'); + multiTokenPaymasters = JSON.parse(buffer.toString()); + } + if (secrets['MULTI_TOKEN_ORACLES']) { + const buffer = Buffer.from(secrets['MULTI_TOKEN_ORACLES'], 'base64'); + multiTokenOracles = JSON.parse(buffer.toString()); + } privateKey = secrets['PRIVATE_KEY']; supportedNetworks = secrets['SUPPORTED_NETWORKS']; noOfTxns = secrets['NO_OF_TRANSACTIONS_IN_A_MONTH'] ?? 10; @@ -98,12 +108,21 @@ const routes: FastifyPluginAsync = async (server) => { const buffer = Buffer.from(record['ERC20_PAYMASTERS'], 'base64'); customPaymasters = JSON.parse(buffer.toString()); } + if (record['MULTI_TOKEN_PAYMASTERS']) { + const buffer = Buffer.from(record['MULTI_TOKEN_PAYMASTERS'], 'base64'); + multiTokenPaymasters = JSON.parse(buffer.toString()); + } + if (record['MULTI_TOKEN_ORACLES']) { + const buffer = Buffer.from(record['MULTI_TOKEN_ORACLES'], 'base64'); + multiTokenOracles = JSON.parse(buffer.toString()); + } privateKey = decode(record['PRIVATE_KEY']); supportedNetworks = record['SUPPORTED_NETWORKS']; noOfTxns = record['NO_OF_TRANSACTIONS_IN_A_MONTH']; txnMode = record['TRANSACTION_LIMIT']; indexerEndpoint = record['INDEXER_ENDPOINT'] ?? process.env.DEFAULT_INDEXER_ENDPOINT; } + if ( !userOp || !entryPoint || @@ -118,13 +137,23 @@ const routes: FastifyPluginAsync = async (server) => { if (server.config.SUPPORTED_NETWORKS == '' && !SupportedNetworks) { return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK }); } + if ( mode.toLowerCase() == 'erc20' && !(PAYMASTER_ADDRESS[chainId] && PAYMASTER_ADDRESS[chainId][gasToken]) && !(customPaymasters[chainId] && customPaymasters[chainId][gasToken]) ) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK_TOKEN }) + + if (gasToken && ethers.utils.isAddress(gasToken)) gasToken = ethers.utils.getAddress(gasToken) + + if (mode.toLowerCase() == 'multitoken' && + !(multiTokenPaymasters[chainId] && multiTokenPaymasters[chainId][gasToken]) && + !(multiTokenOracles[chainId] && multiTokenOracles[chainId][gasToken]) + ) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK_TOKEN }) + const networkConfig = getNetworkConfig(chainId, supportedNetworks ?? ''); if (!networkConfig) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK }); + let result; switch (mode.toLowerCase()) { case 'sponsor': { @@ -160,7 +189,28 @@ const routes: FastifyPluginAsync = async (server) => { result = await paymaster.pimlico(userOp, networkConfig.bundler, entryPoint, paymasterAddress, server.log); break; } - case 'default': { + case 'multitoken': { + const date = new Date(); + const provider = new providers.JsonRpcProvider(networkConfig.bundler); + const signer = new Wallet(privateKey, provider) + const validUntil = context.validUntil ? new Date(context.validUntil) : date; + const validAfter = context.validAfter ? new Date(context.validAfter) : date; + const hex = (Number((validUntil.valueOf() / 1000).toFixed(0)) + 600).toString(16); + const hex1 = (Number((validAfter.valueOf() / 1000).toFixed(0)) - 60).toString(16); + let str = '0x' + let str1 = '0x' + for (let i = 0; i < 14 - hex.length; i++) { + str += '0'; + } + for (let i = 0; i < 14 - hex1.length; i++) { + str1 += '0'; + } + str += hex; + str1 += hex1; + result = await paymaster.signMultiTokenPaymaster(userOp, str, str1, entryPoint, multiTokenPaymasters[chainId][gasToken], gasToken, multiTokenOracles[chainId][gasToken], networkConfig.bundler, signer, server.log); + break; + } + default : { return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_MODE }); } } diff --git a/backend/src/utils/common.ts b/backend/src/utils/common.ts index 0511230..2c82292 100644 --- a/backend/src/utils/common.ts +++ b/backend/src/utils/common.ts @@ -1,5 +1,5 @@ import { FastifyBaseLogger, FastifyRequest } from "fastify"; -import { ethers } from "ethers"; +import { BigNumber, ethers } from "ethers"; import { Database } from "sqlite3"; import SupportedNetworks from "../../config.json" assert { type: "json" }; import { EtherscanResponse, getEtherscanFeeResponse } from "./interface.js"; @@ -44,8 +44,7 @@ export async function getEtherscanFee(chainId: number, log?: FastifyBaseLogger): if (etherscanUrls[chainId]) { const data = await fetch(etherscanUrls[chainId]); const response: EtherscanResponse = await data.json(); - - if (response.result && response.result.FastGasPrice) { + if (response.result && typeof response.result === "object" && response.status === "1") { const maxFeePerGas = ethers.utils.parseUnits(response.result.suggestBaseFee, 'gwei') const fastGasPrice = ethers.utils.parseUnits(response.result.FastGasPrice, 'gwei') return { @@ -54,6 +53,14 @@ export async function getEtherscanFee(chainId: number, log?: FastifyBaseLogger): gasPrice: maxFeePerGas, } } + if (response.result && typeof response.result === "string" && response.jsonrpc) { + const gasPrice = BigNumber.from(response.result) + return { + maxFeePerGas: gasPrice, + maxPriorityFeePerGas: gasPrice, + gasPrice: gasPrice + } + } return null; } return null; diff --git a/backend/src/utils/interface.ts b/backend/src/utils/interface.ts index 4f8c678..274efa4 100644 --- a/backend/src/utils/interface.ts +++ b/backend/src/utils/interface.ts @@ -1,8 +1,10 @@ import { BigNumber } from "ethers"; export interface EtherscanResponse { - status: string; - message: string; + jsonrpc?: string; + id?: string; + status?: string; + message?: string; result?: { LastBlock: string; SafeGasPrice: string; @@ -10,7 +12,7 @@ export interface EtherscanResponse { FastGasPrice: string; suggestBaseFee: string; gasUsedRatio: string; - } + } | string; } export interface getEtherscanFeeResponse {