diff --git a/.changeset/tricky-humans-burn.md b/.changeset/tricky-humans-burn.md new file mode 100644 index 00000000..261279c0 --- /dev/null +++ b/.changeset/tricky-humans-burn.md @@ -0,0 +1,5 @@ +--- +"permissionless": minor +--- + +Permissionless 0.2 released. Migration guide - https://docs.pimlico.io/permissionless/how-to/migration-guide diff --git a/bun.lockb b/bun.lockb index bf430555..4757c3ac 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index ffc72a6f..35ead2f7 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,6 @@ "get-port": "^7.0.0", "tsc-alias": "^1.8.8", "vitest": "^1.2.0", - "viem": "^2.16.2" + "viem": "2.20.0" } } diff --git a/packages/permissionless-test/mock-aa-infra/alto/constants.ts b/packages/permissionless-test/mock-aa-infra/alto/constants.ts index 4e0b07ae..75d32d2d 100644 --- a/packages/permissionless-test/mock-aa-infra/alto/constants.ts +++ b/packages/permissionless-test/mock-aa-infra/alto/constants.ts @@ -135,6 +135,9 @@ export const KERNEL_V07_META_FACTORY_CREATECALL: Hex = export const LIGHT_ACCOUNT_FACTORY_V110_CREATECALL: Hex = "0x4e59b44847b379578588920ca78fbf26c0b4956c5528f3e2f146000008fabf7760a0346100cb576001600160401b0390601f6130cb38819003918201601f1916830191848311848410176100b5578084926020946040528339810103126100cb57516001600160a01b038116908190036100cb576040519161270590818401908111848210176100b55760209284926109c6843981520301906000f080156100a9576080526040516108f590816100d1823960805181818160e00152818161030601526103f70152f35b6040513d6000823e3d90fd5b634e487b7160e01b600052604160045260246000fd5b600080fdfe608080604052600436101561001357600080fd5b600090813560e01c90816311464fbe14610096575080635fbfb9cf1461007c57638cb84e181461004257600080fd5b3461007957602061005b61005536610108565b90610363565b73ffffffffffffffffffffffffffffffffffffffff60405191168152f35b80fd5b503461007957602061005b61009036610108565b90610274565b90503461010457817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101045760209073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b5080fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60409101126101595760043573ffffffffffffffffffffffffffffffffffffffff81168103610159579060243590565b600080fd5b6060810190811067ffffffffffffffff82111761017a57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761017a57604052565b60005b8381106101fd5750506000910152565b81810151838201526020016101ed565b90601f60609373ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0931684526040602085015261026d81518092816040880152602088880191016101ea565b0116010190565b9061027f8183610363565b803b610347575073ffffffffffffffffffffffffffffffffffffffff9182604051917fc4d66de8000000000000000000000000000000000000000000000000000000006020840152166024820152602481526102da8161015e565b6040519061042c8083019183831067ffffffffffffffff84111761017a57839261032c926104948539867f0000000000000000000000000000000000000000000000000000000000000000169061020d565b03906000f5801561033b571690565b6040513d6000823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff1692915050565b600b9060559261042c60209061046f61047b83604096875190610388838701836101a9565b85825282820195610494873961041d61044973ffffffffffffffffffffffffffffffffffffffff92838c51917fc4d66de80000000000000000000000000000000000000000000000000000000088840152166024820152602481526103ec8161015e565b8b51928391878301957f0000000000000000000000000000000000000000000000000000000000000000168661020d565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826101a9565b8951958693610460868601998a92519283916101ea565b840191518093868401906101ea565b010380845201826101a9565b5190208351938401528201523081520160ff8153209056fe60406080815261042c908138038061001681610218565b93843982019181818403126102135780516001600160a01b038116808203610213576020838101516001600160401b0394919391858211610213570186601f820112156102135780519061007161006c83610253565b610218565b918083528583019886828401011161021357888661008f930161026e565b813b156101b9577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b031916841790556000927fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8480a28051158015906101b2575b61010b575b855160e790816103458239f35b855194606086019081118682101761019e578697849283926101889952602788527f416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c87890152660819985a5b195960ca1b8a8901525190845af4913d15610194573d9061017a61006c83610253565b91825281943d92013e610291565b508038808080806100fe565b5060609250610291565b634e487b7160e01b84526041600452602484fd5b50826100f9565b855162461bcd60e51b815260048101859052602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608490fd5b600080fd5b6040519190601f01601f191682016001600160401b0381118382101761023d57604052565b634e487b7160e01b600052604160045260246000fd5b6001600160401b03811161023d57601f01601f191660200190565b60005b8381106102815750506000910152565b8181015183820152602001610271565b919290156102f357508151156102a5575090565b3b156102ae5790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b8251909150156103065750805190602001fd5b6044604051809262461bcd60e51b825260206004830152610336815180928160248601526020868601910161026e565b601f01601f19168101030190fdfe60806040523615605f5773ffffffffffffffffffffffffffffffffffffffff7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54166000808092368280378136915af43d82803e15605b573d90f35b3d90fd5b73ffffffffffffffffffffffffffffffffffffffff7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54166000808092368280378136915af43d82803e15605b573d90f3fea26469706673582212205da2750cd2b0cadfd354d8a1ca4752ed7f22214c8069d852f7dc6b8e9e5ee66964736f6c63430008150033a26469706673582212205367f15fddc0d5cbb3b407c1f8fa018b2549200abc34a5978c9abd75b26a675a64736f6c6343000815003360e03462000160576001600160401b0390601f6200270538819003918201601f1916830191848311848410176200016557808492602094604052833981010312620001605751906001600160a01b03821682036200016057306080527f33e4b41198cc5b8053630ed667ea7c0c4c873f7fc8d9a478b5d7259cec0a4a00918260a05260c05281549060ff8260401c166200014e57808083160362000108575b60405161258990816200017c82396080518181816107b201528181610dbd0152610f99015260a0518161141d015260c0518181816109d701528181610bf501528181610cd4015281816111b001528181611387015281816115ff015281816122af01526124b50152f35b6001600160401b031990911681179091556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a13880806200009e565b60405163f92ee8a960e01b8152600490fd5b600080fd5b634e487b7160e01b600052604160045260246000fdfe6080604052600436101561001b575b361561001957600080fd5b005b60003560e01c806223de291461019a57806301ffc9a7146101955780630a1028c414610190578063150b7a021461018b5780631626ba7e1461018657806318dfb3c7146101815780633659cfe61461017c5780633a871cdd1461017757806347e1da2a146101725780634a58db191461016d5780634d44560d146101685780634f1ef2861461016357806352d1902d1461015e5780638da5cb5b14610159578063a786cac914610154578063b0d691fe1461014f578063b61d27f61461014a578063bc197c8114610145578063c399ec8814610140578063c4d66de81461013b578063d087d28814610136578063f23a6e6114610131578063f2fde38b1461012c5763f698da250361000e5761184d565b6116f2565b611661565b611580565b6113e0565b61130f565b611248565b6111d4565b611165565b61113d565b61106e565b610f53565b610d4d565b610c76565b610bb3565b610ac3565b61096d565b61075e565b610672565b6105bb565b61052a565b610504565b61027b565b6101f0565b73ffffffffffffffffffffffffffffffffffffffff8116036101bd57565b600080fd5b9181601f840112156101bd5782359167ffffffffffffffff83116101bd57602083818601950101116101bd57565b346101bd5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd5761022a60043561019f565b61023560243561019f565b61024060443561019f565b67ffffffffffffffff6084358181116101bd576102619036906004016101c2565b505060a4359081116101bd576100199036906004016101c2565b346101bd5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd576004357fffffffff0000000000000000000000000000000000000000000000000000000081168091036101bd57807f150b7a020000000000000000000000000000000000000000000000000000000060209214908115610341575b8115610317575b506040519015158152f35b7f01ffc9a7000000000000000000000000000000000000000000000000000000009150143861030c565b7f4e2312e00000000000000000000000000000000000000000000000000000000081149150610305565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff81116103ae57604052565b61036b565b6020810190811067ffffffffffffffff8211176103ae57604052565b6060810190811067ffffffffffffffff8211176103ae57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176103ae57604052565b67ffffffffffffffff81116103ae57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926104728261042c565b9161048060405193846103eb565b8294818452818301116101bd578281602093846000960137010152565b9080601f830112156101bd578160206104b893359101610466565b90565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126101bd576004359067ffffffffffffffff82116101bd576104b89160040161049d565b346101bd57602061051c610517366104bb565b611ec8565b818151910120604051908152f35b346101bd5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd5761056460043561019f565b61056f60243561019f565b60643567ffffffffffffffff81116101bd5761058f9036906004016101c2565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b346101bd5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd5760243567ffffffffffffffff81116101bd5761061761060f602092369060040161049d565b600435611f6d565b7fffffffff0000000000000000000000000000000000000000000000000000000060405191168152f35b9181601f840112156101bd5782359167ffffffffffffffff83116101bd576020808501948460051b0101116101bd57565b346101bd5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd5767ffffffffffffffff6004358181116101bd576106c2903690600401610641565b916024359081116101bd576106db903690600401610641565b91906106e561249d565b8284036107345760005b8481106106f857005b8061072e6107096001938887611da0565b356107138161019f565b610728610721848988611e06565b3691610466565b90612514565b016106ef565b60046040517fa24a13a6000000000000000000000000000000000000000000000000000000008152fd5b346101bd5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd576004356107998161019f565b73ffffffffffffffffffffffffffffffffffffffff90817f000000000000000000000000000000000000000000000000000000000000000016916107df833014156118d9565b61080e7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc938285541614611964565b61081661241c565b60405190610823826103b3565b600082527f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff161561085d5750506100199150611a7a565b6020600491604094939451928380927f52d1902d00000000000000000000000000000000000000000000000000000000825286165afa6000918161093d575b5061092a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201527f6f6e206973206e6f7420555550530000000000000000000000000000000000006064820152608490fd5b0390fd5b6100199361093891146119ef565b611b66565b61095f91925060203d8111610966575b61095781836103eb565b81019061188e565b903861089c565b503d61094d565b346101bd577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6060813601126101bd576004359067ffffffffffffffff82116101bd576101609082360301126101bd5760443573ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163303610a6557610a10610a28926024359060040161234d565b9080610a2c575b506040519081529081906020820190565b0390f35b600080808093337ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff150610a5e6118a9565b5038610a17565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f6163636f756e743a206e6f742066726f6d20456e747279506f696e74000000006044820152fd5b346101bd5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd5767ffffffffffffffff6004358181116101bd57610b13903690600401610641565b6024358381116101bd57610b2b903690600401610641565b936044359081116101bd57610b44903690600401610641565b92610b4d61249d565b838114801590610ba9575b6107345760005b818110610b6857005b80610ba3610b79600193858a611da0565b35610b838161019f565b610b8e838b89611da0565b35610b9d610721858b8a611e06565b9161253c565b01610b5f565b5085811415610b58565b6000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610c735773ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001681813b15610c7357602491604051928380927fb760faf900000000000000000000000000000000000000000000000000000000825230600483015234905af18015610c6e57610c62575080f35b610c6b9061039a565b80f35b61189d565b80fd5b346101bd57600060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610c7357600435610cb38161019f565b610cbb61241c565b8173ffffffffffffffffffffffffffffffffffffffff807f00000000000000000000000000000000000000000000000000000000000000001692833b15610d49576044908360405195869485937f205c287800000000000000000000000000000000000000000000000000000000855216600484015260243560248401525af18015610c6e57610c62575080f35b8280fd5b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd57600435610d838161019f565b60243567ffffffffffffffff81116101bd57610da390369060040161049d565b9073ffffffffffffffffffffffffffffffffffffffff91827f00000000000000000000000000000000000000000000000000000000000000001692610dea843014156118d9565b610e197f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc948286541614611964565b610e2161241c565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff1615610e575750506100199150611a7a565b6020600491604094939451928380927f52d1902d00000000000000000000000000000000000000000000000000000000825286165afa60009181610f33575b50610f20576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201527f6f6e206973206e6f7420555550530000000000000000000000000000000000006064820152608490fd5b61001993610f2e91146119ef565b611c45565b610f4c91925060203d81116109665761095781836103eb565b9038610e96565b346101bd5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd5773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163003610fea576040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152602090f35b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c00000000000000006064820152fd5b346101bd5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd57602073ffffffffffffffffffffffffffffffffffffffff7f691ec1a18226d004c07c9f8e5c4a6ff15a7b38db267cf7e3c945aef8be5122005416604051908152f35b919082519283825260005b8481106111295750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b6020818301810151848301820152016110ea565b346101bd57610a28611151610517366104bb565b6040519182916020835260208301906110df565b346101bd5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101bd5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd5760043561120f8161019f565b6044359067ffffffffffffffff82116101bd5761123e6112366100199336906004016101c2565b61072161249d565b906024359061253c565b346101bd5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd5761128260043561019f565b61128d60243561019f565b67ffffffffffffffff6044358181116101bd576112ae903690600401610641565b50506064358181116101bd576112c8903690600401610641565b50506084359081116101bd576112e29036906004016101c2565b50506040517fbc197c81000000000000000000000000000000000000000000000000000000008152602090f35b346101bd5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd576040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa8015610c6e576020916000916113c3575b50604051908152f35b6113da9150823d81116109665761095781836103eb565b386113ba565b346101bd5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd5760043561141b8161019f565b7f00000000000000000000000000000000000000000000000000000000000000009081549067ffffffffffffffff60ff8360401c1615921680159081611578575b600114908161156e575b159081611565575b5061153b5782547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011783556114aa908261150557612227565b6114b057005b80547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a1005b83547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff1668010000000000000000178455612227565b60046040517ff92ee8a9000000000000000000000000000000000000000000000000000000008152fd5b9050153861146e565b303b159150611466565b83915061145c565b346101bd5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd576040517f35567e1a0000000000000000000000000000000000000000000000000000000081523060048201526000602482015260208160448173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa8015610c6e57610a289160009161164357506040519081529081906020820190565b61165b915060203d81116109665761095781836103eb565b38610a17565b346101bd5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd5761169b60043561019f565b6116a660243561019f565b60843567ffffffffffffffff81116101bd576116c69036906004016101c2565b505060206040517ff23a6e61000000000000000000000000000000000000000000000000000000008152f35b346101bd5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd5760043561172d8161019f565b61173561241c565b73ffffffffffffffffffffffffffffffffffffffff908181169182158015611844575b611813577f691ec1a18226d004c07c9f8e5c4a6ff15a7b38db267cf7e3c945aef8be512200541690818314611813576117ec9073ffffffffffffffffffffffffffffffffffffffff7f691ec1a18226d004c07c9f8e5c4a6ff15a7b38db267cf7e3c945aef8be51220091167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b602483604051907fb20f76e30000000000000000000000000000000000000000000000000000000082526004820152fd5b50308314611758565b346101bd5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101bd576020611886611e21565b604051908152f35b908160209103126101bd575190565b6040513d6000823e3d90fd5b3d156118d4573d906118ba8261042c565b916118c860405193846103eb565b82523d6000602084013e565b606090565b156118e057565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f64656c656761746563616c6c00000000000000000000000000000000000000006064820152fd5b1561196b57565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602c60248201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060448201527f6163746976652070726f787900000000000000000000000000000000000000006064820152fd5b156119f657565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f7860448201527f6961626c655555494400000000000000000000000000000000000000000000006064820152fd5b803b15611ae25773ffffffffffffffffffffffffffffffffffffffff7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc91167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e7472616374000000000000000000000000000000000000006064820152fd5b90611b7082611a7a565b73ffffffffffffffffffffffffffffffffffffffff82167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b600080a2805115801590611c3d575b611bbf575050565b611c3a9160008060405193611bd3856103cf565b602785527f416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c60208601527f206661696c6564000000000000000000000000000000000000000000000000006040860152602081519101845af4611c346118a9565b91611ca5565b50565b506000611bb7565b90611c4f82611a7a565b73ffffffffffffffffffffffffffffffffffffffff82167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b600080a2805115801590611c9d57611bbf575050565b506001611bb7565b91929015611d205750815115611cb9575090565b3b15611cc25790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b825190915015611d335750805190602001fd5b610926906040519182917f08c379a00000000000000000000000000000000000000000000000000000000083526020600484015260248301906110df565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9190811015611db05760051b0190565b611d71565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101bd570180359067ffffffffffffffff82116101bd576020019181360383136101bd57565b90821015611db057611e1d9160051b810190611db5565b9091565b60405160208101907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82527fcbe29a6ace531c23849b5cdb1a6b991866eb7dc20deda15202ba6fd921ed2c0060408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815260c0810181811067ffffffffffffffff8211176103ae5760405251902090565b6020815191012060405160208101917f5e3baca2936049843f06038876a12f03627b5edc98025751ecf2ac75626401998352604082015260408152611f0c816103cf565b519020611f17611e21565b90604051917f1901000000000000000000000000000000000000000000000000000000000000602084015260228301526042820152604281526080810181811067ffffffffffffffff8211176103ae5760405290565b60405190602082015260208152604081019080821067ffffffffffffffff8311176103ae57611f9e91604052611ec8565b602081519101209073ffffffffffffffffffffffffffffffffffffffff91827f691ec1a18226d004c07c9f8e5c4a6ff15a7b38db267cf7e3c945aef8be5122005416611fea83836120a3565b600581969296101561207457159485612068575b50508315612056575b505050612032577fffffffff0000000000000000000000000000000000000000000000000000000090565b7f1626ba7e0000000000000000000000000000000000000000000000000000000090565b6120609350612166565b388080612007565b16811493503880611ffe565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9060418151146000146120cd57611e1d916020820151906060604084015193015160001a906120d7565b5050600090600290565b9291907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831161215a5791608094939160ff602094604051948552168484015260408301526060820152600093849182805260015afa15610c6e57815173ffffffffffffffffffffffffffffffffffffffff811615612154579190565b50600190565b50505050600090600390565b600091929082916040516121e3816121b760208201947f1626ba7e00000000000000000000000000000000000000000000000000000000998a875260248401526040604484015260648301906110df565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826103eb565b51915afa906121f06118a9565b82612219575b8261220057505090565b6122159192506020808251830101910161188e565b1490565b9150602082511015916121f6565b73ffffffffffffffffffffffffffffffffffffffff9081811691821561231c576122ad839273ffffffffffffffffffffffffffffffffffffffff7f691ec1a18226d004c07c9f8e5c4a6ff15a7b38db267cf7e3c945aef8be51220091167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b7f0000000000000000000000000000000000000000000000000000000000000000167fec6a23b49d2c363d250c9dda15610e835d428207d15ddb36a6c230e37371ddf1600080a360007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a3565b60246040517fb20f76e300000000000000000000000000000000000000000000000000000000815260006004820152fd5b73ffffffffffffffffffffffffffffffffffffffff91827f691ec1a18226d004c07c9f8e5c4a6ff15a7b38db267cf7e3c945aef8be51220054167f19457468657265756d205369676e6564204d6573736167653a0a33320000000060005281601c526123d06123c9610721603c60002095610140810190611db5565b80946120a3565b600581969296101561207457159485612410575b505083156123fe575b5050506123f957600190565b600090565b6124089350612166565b3880806123ed565b168114935038806123e4565b303314158061245c575b61242c57565b60246040517f4a0bfec1000000000000000000000000000000000000000000000000000000008152336004820152fd5b5073ffffffffffffffffffffffffffffffffffffffff7f691ec1a18226d004c07c9f8e5c4a6ff15a7b38db267cf7e3c945aef8be5122005416331415612426565b73ffffffffffffffffffffffffffffffffffffffff807f00000000000000000000000000000000000000000000000000000000000000001633141590816124e6575b5061242c57565b90507f691ec1a18226d004c07c9f8e5c4a6ff15a7b38db267cf7e3c945aef8be5122005416331415386124df565b600091829182602083519301915af161252b6118a9565b90156125345750565b602081519101fd5b916000928392602083519301915af161252b6118a956fea2646970667358221220c5240b5a614209162da17798c4589910308036b820e321c267b03d8cedb5e48164736f6c634300081500330000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d2789" +export const LIGHT_ACCOUNT_FACTORY_V200_CREATECALL: Hex = + "0x00000000000000000000000000000000000000005f1ffd9d31306e056bcc959b60c060405234620000ae57620032713881900360c0601f8201601f19168101906001600160401b03821190821017620000b3576040928291845260c03912620000ae576200006960c0516200005481620000e5565b60e051906200006382620000e5565b62000103565b604051610d8590816200034f82396080518181816101160152818161050b015281816105ee01526106a9015260a0518181816107cc01528181610a170152610b6b0152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b606081019081106001600160401b03821117620000b357604052565b6001600160a01b03811603620000ae57565b6040513d6000823e3d90fd5b6001600160a01b03908116908115620001bc5762000165918160018060a01b031980600154166001558260005491821617600055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a38216620001d5565b60405161219d8082016001600160401b03811183821017620000b35783620001a2918493620010d485396001600160a01b03909116815260200190565b03906000f08015620001b65760a052608052565b620000f7565b604051631e4fbdf760e01b815260006004820152602490fd5b60405160208082018160006301ffc9a760e01b94858452856024820152602481526200020181620000c9565b51617530938785fa923d6000519085620002e0575b5084620002d5575b50836200026d575b505050806200025b575b15620002395750565b60405163075b76fd60e21b81526001600160a01b039091166004820152602490fd5b506200026781620002ec565b62000230565b829350906000918560405185810192835263ffffffff60e01b6024820152602481526200029a81620000c9565b5192fa60005190913d83620002c9575b505081620002be575b501538808062000226565b9050151538620002b3565b101591503880620002aa565b15159350386200021e565b84111594503862000216565b6000602091604051838101906301ffc9a760e01b825263122a0e9b60e31b6024820152602481526200031e81620000c9565b5191617530fa6000513d8262000341575b50816200033a575090565b9050151590565b602011159150386200032f56fe60806040818152600480361015610021575b505050361561001f57600080fd5b005b600092833560e01c908163290ab98414610b21575080635fbfb9cf14610967578063715018a61461090857806379ba5097146108285780638cb84e181461071e5780638da5cb5b146106cd57806394430fa51461065e57838163bb9fe6bf1461059e57508063c23a5cea146104a4578063d9caed12146102b0578063e30c397814610259578063f2fde38b146101ac5763fbb1c3d403610011578183927ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85781359163ffffffff83168093036101a3576100ff610c5e565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001692833b1561019f5760248592845195869384927f0396cb600000000000000000000000000000000000000000000000000000000084528301528235905af190811561019657506101835750f35b61018c90610bb2565b6101935780f35b80fd5b513d84823e3d90fd5b8480fd5b505050fd5b5050fd5b83346101935760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610193576101e4610b8f565b6101ec610c5e565b73ffffffffffffffffffffffffffffffffffffffff80911690817fffffffffffffffffffffffff000000000000000000000000000000000000000060015416176001558254167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b5050346102ac57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102ac5760209073ffffffffffffffffffffffffffffffffffffffff600154169051908152f35b5080fd5b5090346104a05760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a0576102e9610b8f565b6024359073ffffffffffffffffffffffffffffffffffffffff9081831680930361049b57610315610c5e565b16908115610473578491908061036757508180809247905af1610336610bf5565b501561034157505080f35b517f90b8ec18000000000000000000000000000000000000000000000000000000008152fd5b928092505160208101917fa9059cbb00000000000000000000000000000000000000000000000000000000835260248201526044356044820152604481526080810181811067ffffffffffffffff821117610445578352516103da918691829182875af16103d3610bf5565b9084610caf565b8051908115159182610421575b50506103f35750505080f35b6024935051917f5274afe7000000000000000000000000000000000000000000000000000000008352820152fd5b819250906020918101031261019f576020015180159081150361019f5738806103e7565b6041867f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b5050517f8579befe000000000000000000000000000000000000000000000000000000008152fd5b600080fd5b8280fd5b509190346102ac5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102ac576104de610b8f565b906104e7610c5e565b73ffffffffffffffffffffffffffffffffffffffff809216918215610576579383947f00000000000000000000000000000000000000000000000000000000000000001692833b1561019f576024859283855196879485937fc23a5cea0000000000000000000000000000000000000000000000000000000085528401525af190811561019657506101835750f35b8482517f8579befe000000000000000000000000000000000000000000000000000000008152fd5b808484346101a857827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a8576105d7610c5e565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016803b156101a35783918351809581937fbb9fe6bf0000000000000000000000000000000000000000000000000000000083525af19081156101965750610652575080f35b61065b90610bb2565b80f35b5050346102ac57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102ac576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b5050346102ac57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102ac5773ffffffffffffffffffffffffffffffffffffffff60209254169051908152f35b5050346102ac57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102ac579060209161075a610b8f565b9073ffffffffffffffffffffffffffffffffffffffff918352602435845280832081517fcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f36060527f5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e207683526160098652837f000000000000000000000000000000000000000000000000000000000000000016601e5268603d3d8160223d3973600a52605f6021209083528460605260ff85536035523060601b60015260155260558320926035525191168152f35b5090346104a057827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a0576001549173ffffffffffffffffffffffffffffffffffffffff9133838516036108d85750507fffffffffffffffffffffffff0000000000000000000000000000000000000000809216600155825491339083161783553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b6024925051907f118cdaa70000000000000000000000000000000000000000000000000000000082523390820152fd5b50913461019357807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101935750610941610c5e565b517f4a7f394f000000000000000000000000000000000000000000000000000000008152fd5b5090346104a057807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104a05761099f610b8f565b73ffffffffffffffffffffffffffffffffffffffff928185526024356020528285209385928451937fcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f36060527f5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e20768652616009602052827f000000000000000000000000000000000000000000000000000000000000000016601e5268603d3d8160223d3973600a52605f96602197605f60212060358801523060581b875260ff87538160158801526055872098893b15610b015750505050816001965b85875288606052169515610a95575b6020868651908152f35b853b15610afd577fc4d66de80000000000000000000000000000000000000000000000000000000084521690820152838160248183875af18015610af35760209450610ae4575b808080610a8b565b610aed90610bb2565b38610adc565b82513d86823e3d90fd5b8680fd5b909192985089f58015610b15578290610a7c565b8363301164258952601cfd5b8490346102ac57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102ac5760209073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361049b57565b67ffffffffffffffff8111610bc657604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b3d15610c595767ffffffffffffffff903d828111610bc65760405192601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116840190811184821017610bc65760405282523d6000602084013e565b606090565b73ffffffffffffffffffffffffffffffffffffffff600054163303610c7f57565b60246040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152fd5b90610cee5750805115610cc457805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580610d46575b610cff575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15610cf756fea264697066735822122020672d0c03264e2785eb3a17a40742d95e9887bed833176dd597224a3829b8d664736f6c634300081700336101803462000224576001600160401b0390601f6200219d38819003918201601f191683019291908484118385101762000229578160209284926040968752833981010312620002245751916001600160a01b03831683036200022457306080523060a0524660c052620000726200023f565b92600c845260a06001602086016b131a59da1d1058d8dbdd5b9d60a21b815260206200009d6200023f565b8381520196601960f91b88525190209520948060e052610100958087528551917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260208301528582015246606082015230608082015220926101209384527f33e4b41198cc5b8053630ed667ea7c0c4c873f7fc8d9a478b5d7259cec0a4a006101609381855261014093845281549060ff82851c1662000213578080831603620001ce575b5050505192611f3d9485620002608639608051858181610ac60152610b78015260a05185611689015260c051856116ac015260e0518561171e01525184611744015251836116670152518281816103710152818161052301528181610708015281816108cb01528181610cd001528181610dc601528181610fcd01526119c80152518161042f0152f35b6001600160401b0319909116811790915581519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a138808062000144565b835163f92ee8a960e01b8152600490fd5b600080fd5b634e487b7160e01b600052604160045260246000fd5b60408051919082016001600160401b03811183821017620002295760405256fe6080604081815260049081361015610022575b505050361561002057600080fd5b005b600092833560e01c90816301ffc9a714611307575080630a1028c414611294578063150b7a02146112065780631626ba7e1461117f57806318dfb3c7146110a857806319822f7c14610f5f57806347e1da2a14610e3b5780634a58db1914610d845780634d44560d14610c6a5780634f1ef28614610b1f57806352d1902d14610a9357806384b0196e146109615780638da5cb5b146108ef578063b0d691fe14610880578063b61d27f614610801578063bc197c8114610740578063c399ec881461068f578063c4d66de8146103f3578063d087d288146102f2578063f23a6e61146102615763f2fde38b03610012573461025d5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261025d57610149611506565b916101526119b0565b73ffffffffffffffffffffffffffffffffffffffff8093169283158015610254575b610225577f691ec1a18226d004c07c9f8e5c4a6ff15a7b38db267cf7e3c945aef8be512200918254918216938486146101f65750507fffffffffffffffffffffffff000000000000000000000000000000000000000016831790557f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b908560249251917fb20f76e3000000000000000000000000000000000000000000000000000000008352820152fd5b508260249251917fb20f76e3000000000000000000000000000000000000000000000000000000008352820152fd5b50308414610174565b8280fd5b5082346102ef5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102ef5761029a611506565b506102a3611529565b506084359067ffffffffffffffff82116102ef57506020926102c79136910161154c565b5050517ff23a6e61000000000000000000000000000000000000000000000000000000008152f35b80fd5b508290346103ef57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103ef578051927f35567e1a000000000000000000000000000000000000000000000000000000008452309084015281602484015260208360448173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9182156103e457916103aa575b6020925051908152f35b90506020823d6020116103dc575b816103c560209383611454565b810103126103d75760209151906103a0565b600080fd5b3d91506103b8565b9051903d90823e3d90fd5b5080fd5b50903461025d5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261025d5761042c611506565b917f00000000000000000000000000000000000000000000000000000000000000009182549160ff83821c16159267ffffffffffffffff811680159081610687575b600114908161067d575b159081610674575b5061064d578360017fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000008316178655610618575b5073ffffffffffffffffffffffffffffffffffffffff8095169182156105ea575081907f691ec1a18226d004c07c9f8e5c4a6ff15a7b38db267cf7e3c945aef8be512200827fffffffffffffffffffffffff000000000000000000000000000000000000000082541617905551947f0000000000000000000000000000000000000000000000000000000000000000167fec6a23b49d2c363d250c9dda15610e835d428207d15ddb36a6c230e37371ddf18780a3847f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a3610594578280f35b7fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d291817fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff6020935416905560018152a138808280f35b8660249251917fb20f76e3000000000000000000000000000000000000000000000000000000008352820152fd5b7fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000001668010000000000000001178455386104b3565b50517ff92ee8a9000000000000000000000000000000000000000000000000000000008152fd5b90501538610480565b303b159150610478565b85915061046e565b508290346103ef57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103ef578051927f70a08231000000000000000000000000000000000000000000000000000000008452309084015260208360248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9182156103e457916103aa576020925051908152f35b5082346102ef5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102ef57610779611506565b50610782611529565b5067ffffffffffffffff906044358281116103ef576107a4903690860161157a565b50506064358281116103ef576107bd903690860161157a565b50506084359182116102ef57506020926107d99136910161154c565b5050517fbc197c81000000000000000000000000000000000000000000000000000000008152f35b5050346103ef5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103ef5761083a611506565b6044359167ffffffffffffffff831161087c5761086061086f916108799436910161154c565b6108686119b0565b36916114cf565b9060243590611a98565b80f35b8380fd5b8382346103ef57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103ef576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b8382346103ef57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103ef5760209073ffffffffffffffffffffffffffffffffffffffff7f691ec1a18226d004c07c9f8e5c4a6ff15a7b38db267cf7e3c945aef8be51220054169051908152f35b5082346102ef57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102ef579080519061099e82611438565b600c82526020927f4c696768744163636f756e74000000000000000000000000000000000000000084840152610a4b8251926109d984611438565b600193600181527f320000000000000000000000000000000000000000000000000000000000000087820152610a3e8251967f0f00000000000000000000000000000000000000000000000000000000000000885260e08989015260e08801906115ab565b91868303908701526115ab565b4660608501523060808501528160a085015283810360c0850152846060519182815201946080925b828110610a805785870386f35b8351875295810195928101928401610a73565b5082346102ef57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102ef57307f000000000000000000000000000000000000000000000000000000000000000003610b1357602082517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b639f03a026915052601cfd5b5090817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261025d57610b52611506565b9160243567ffffffffffffffff8111610c6657610b72903690840161154c565b919093307f000000000000000000000000000000000000000000000000000000000000000014610c5a5773ffffffffffffffffffffffffffffffffffffffff90610bba6119b0565b16926352d1902d6001527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90816020600183601d895afa5103610c4e575090828480949388967fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8880a255610c2d578380f35b8190519485378338925af415610c4557818180808380f35b903d90823e3d90fd5b6355299b49600152601dfd5b83639f03a0268752601cfd5b8480fd5b508290346103ef57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103ef5782359073ffffffffffffffffffffffffffffffffffffffff80831680930361087c57610cc56119b0565b8215610d5c579383947f00000000000000000000000000000000000000000000000000000000000000001692833b15610c66576044859283855196879485937f205c287800000000000000000000000000000000000000000000000000000000855284015260243560248401525af1908115610d535750610d435750f35b610d4c906113f5565b6102ef5780f35b513d84823e3d90fd5b8482517f8579befe000000000000000000000000000000000000000000000000000000008152fd5b50827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261025d578273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001692833b156103ef5760248351809581937fb760faf9000000000000000000000000000000000000000000000000000000008352309083015234905af1908115610d535750610e32575080f35b610879906113f5565b503461025d5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261025d5767ffffffffffffffff908235828111610c6657610e8b903690850161157a565b602494919435848111610f5b57610ea5903690840161157a565b919094604435908111610f5757610ebf903690850161157a565b939094610eca6119b0565b848314801590610f4d575b610f27575050865b818110610ee8578780f35b80610f21610f01610efc600194868c6117eb565b61182a565b610f0c83878b6117eb565b35610f1b610868858a8c61189c565b91611a98565b01610edd565b517fa24a13a6000000000000000000000000000000000000000000000000000000008152fd5b5083831415610ed5565b8780fd5b8680fd5b508290346103ef577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc916060833601126102ef5783359267ffffffffffffffff84116103ef576101209084360301126102ef576044359273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016330361104b57602094611004916024359101611ac0565b9280611013575b505051908152f35b81808092337ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff150611043611a68565b50838061100b565b60648560208551917f08c379a0000000000000000000000000000000000000000000000000000000008352820152601c60248201527f6163636f756e743a206e6f742066726f6d20456e747279506f696e74000000006044820152fd5b50903461025d57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261025d5767ffffffffffffffff908035828111610c66576110f8903690830161157a565b909260243590811161117b57611111903690840161157a565b92909461111c6119b0565b838303610f27575050845b818110611132578580f35b611140610efc8284876117eb565b868061115061086885888b61189c565b602093828583519301915af190611165611a68565b9115611175575050600101611127565b81519101fd5b8580fd5b5082346102ef57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102ef576024359067ffffffffffffffff82116102ef57506111fe6020936111f77fffffffff00000000000000000000000000000000000000000000000000000000933690830161154c565b913561177b565b915191168152f35b5082346102ef5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102ef5761123f611506565b50611248611529565b506064359067ffffffffffffffff82116102ef575060209261126c9136910161154c565b5050517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b5082346102ef5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102ef5782359067ffffffffffffffff82116102ef57366023830112156102ef57506112fb602093826024611300943693013591016114cf565b611609565b9051908152f35b8490843461025d5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261025d57357fffffffff00000000000000000000000000000000000000000000000000000000811680910361025d57602092507f150b7a020000000000000000000000000000000000000000000000000000000081149081156113cb575b81156113a1575b5015158152f35b7f01ffc9a7000000000000000000000000000000000000000000000000000000009150148361139a565b7f4e2312e00000000000000000000000000000000000000000000000000000000081149150611393565b67ffffffffffffffff811161140957604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff82111761140957604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761140957604052565b67ffffffffffffffff811161140957601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926114db82611495565b916114e96040519384611454565b8294818452818301116103d7578281602093846000960137010152565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036103d757565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036103d757565b9181601f840112156103d75782359167ffffffffffffffff83116103d757602083818601950101116103d757565b9181601f840112156103d75782359167ffffffffffffffff83116103d7576020808501948460051b0101116103d757565b919082519283825260005b8481106115f55750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b6020818301810151848301820152016115b6565b6020815191012060405160208101917f5e3baca2936049843f06038876a12f03627b5edc98025751ecf2ac75626401998352604082015260408152606081019181831067ffffffffffffffff841117611409578260405281519020917f0000000000000000000000000000000000000000000000000000000000000000917f000000000000000000000000000000000000000000000000000000000000000030147f0000000000000000000000000000000000000000000000000000000000000000461416156116f5575b5050671901000000000000600052601a52603a5260426018206000603a5290565b60a092507f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82527f000000000000000000000000000000000000000000000000000000000000000060808201527f0000000000000000000000000000000000000000000000000000000000000000838201524660c082015260e0309101522038806116d4565b9061179a61179f9392604051906020820152602081526112fb81611438565b6118b7565b6117c7577fffffffff0000000000000000000000000000000000000000000000000000000090565b7f1626ba7e0000000000000000000000000000000000000000000000000000000090565b91908110156117fb5760051b0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b3573ffffffffffffffffffffffffffffffffffffffff811681036103d75790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156103d7570180359067ffffffffffffffff82116103d7576020019181360383136103d757565b908210156117fb576118b39160051b81019061184b565b9091565b90916001908181106119865780156117fb5781843560f81c80611917575081106103d7576119149361190e927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff36930191016114cf565b90611cf2565b90565b146119465760046040517f60cd402d000000000000000000000000000000000000000000000000000000008152fd5b8082116103d75761191493611980927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff36930191016114cf565b90611bfb565b60046040517f60cd402d000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff807f0000000000000000000000000000000000000000000000000000000000000000163314159081611a5d575b81611a2f575b506119ff57565b60246040517f4a0bfec1000000000000000000000000000000000000000000000000000000008152336004820152fd5b90507f691ec1a18226d004c07c9f8e5c4a6ff15a7b38db267cf7e3c945aef8be5122005416331415386119f8565b3330141591506119f2565b3d15611a93573d90611a7982611495565b91611a876040519384611454565b82523d6000602084013e565b606090565b916000928392602083519301915af1611aaf611a68565b9015611ab85750565b602081519101fd5b610100810190611ad0828261184b565b929050600180931061198657611ae6818361184b565b156117fb573560f81c80611b775750611b2c906000947f19457468657265756d205369676e6564204d6573736167653a0a3332000000008652601c52603c85209261184b565b90818411610c6657611b69929161190e91857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff36930191016114cf565b15611b72575090565b905090565b9280949314611baa5760046040517f60cd402d000000000000000000000000000000000000000000000000000000008152fd5b611bb39161184b565b91908284116103d757611bf09261198091857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff36930191016114cf565b156119145750600090565b906000809173ffffffffffffffffffffffffffffffffffffffff7f691ec1a18226d004c07c9f8e5c4a6ff15a7b38db267cf7e3c945aef8be512200541690604051611caf81611c8360208201947f1626ba7e00000000000000000000000000000000000000000000000000000000998a875260248401526040604484015260648301906115ab565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611454565b51915afa90611cbc611a68565b82611ce4575b82611ccc57505090565b9091506020818051810103126103d757602001511490565b915060208251101591611cc2565b611d0891611cff91611d47565b90929192611d83565b73ffffffffffffffffffffffffffffffffffffffff807f691ec1a18226d004c07c9f8e5c4a6ff15a7b38db267cf7e3c945aef8be512200541691161490565b8151919060418303611d7857611d7192506020820151906060604084015193015160001a90611e6a565b9192909190565b505060009160029190565b6004811015611e3b5780611d95575050565b60018103611dc75760046040517ff645eedf000000000000000000000000000000000000000000000000000000008152fd5b60028103611e0057602482604051907ffce698f70000000000000000000000000000000000000000000000000000000082526004820152fd5b600314611e0a5750565b602490604051907fd78bce0c0000000000000000000000000000000000000000000000000000000082526004820152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411611efb57926020929160ff608095604051948552168484015260408301526060820152600092839182805260015afa15611eef57805173ffffffffffffffffffffffffffffffffffffffff811615611ee657918190565b50809160019190565b604051903d90823e3d90fd5b5050506000916003919056fea26469706673582212200896f337e411e9db94675cb703bb4056435327d18f202a547674e38ca452f52464736f6c63430008170033000000000000000000000000ddf32240b4ca3184de7ec8f0d5aba27dec8b7a5c0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da032" + /* ========= TRUST ACCOUNT RELATED ========= */ // Will deploy the Trust Factory 0x729c310186a57833f622630a16d13f710b83272a export const TRUST_FACTORY_V06_CREATECALL: Hex = diff --git a/packages/permissionless-test/mock-aa-infra/alto/index.ts b/packages/permissionless-test/mock-aa-infra/alto/index.ts index 6d7267fa..82685974 100644 --- a/packages/permissionless-test/mock-aa-infra/alto/index.ts +++ b/packages/permissionless-test/mock-aa-infra/alto/index.ts @@ -31,6 +31,7 @@ import { KERNEL_V07_V3_1_ECDSA_VALIDATOR_V3_CREATECALL, KERNEL_V07_V3_1_FACTORY_CREATECALL, LIGHT_ACCOUNT_FACTORY_V110_CREATECALL, + LIGHT_ACCOUNT_FACTORY_V200_CREATECALL, SAFE_7579_LAUNCHPAD_CREATECALL, SAFE_7579_MODULE_CREATECALL, SAFE_7579_REGISTRY_CREATECALL, @@ -234,6 +235,12 @@ export const setupContracts = async (rpc: string) => { gas: 15_000_000n, nonce: nonce++ }), + walletClient.sendTransaction({ + to: DETERMINISTIC_DEPLOYER, + data: LIGHT_ACCOUNT_FACTORY_V200_CREATECALL, + gas: 15_000_000n, + nonce: nonce++ + }), walletClient.sendTransaction({ to: DETERMINISTIC_DEPLOYER, data: TRUST_FACTORY_V06_CREATECALL, @@ -451,6 +458,8 @@ export const setupContracts = async (rpc: string) => { "0xaac5D4240AF87249B3f71BC8E4A2cae074A3E419", // Kernel v0.3.1 Factory "0x00004EC70002a32400f8ae005A26081065620D20", // LightAccountFactory v1.1.0 "0xae8c656ad28F2B59a196AB61815C16A0AE1c3cba", // LightAccount v1.1.0 implementation + "0x0000000000400CdFef5E2714E63d8040b700BC24", // LightAccountFactory v2.0.0 + "0x8E8e658E22B12ada97B402fF0b044D6A325013C7", // LightAccount v2.0.0 implementation "0x81b9E3689390C7e74cF526594A105Dea21a8cdD5", // Trust Secp256k1VerificationFacet "0x729c310186a57833f622630a16d13f710b83272a", // Trust factory "0xFde53272dcd7938d16E031A6989753c321728332", // Trust AccountFacet diff --git a/packages/permissionless-test/mock-aa-infra/mock-paymaster/helpers/schema.ts b/packages/permissionless-test/mock-aa-infra/mock-paymaster/helpers/schema.ts index 5c294bc6..6d728920 100644 --- a/packages/permissionless-test/mock-aa-infra/mock-paymaster/helpers/schema.ts +++ b/packages/permissionless-test/mock-aa-infra/mock-paymaster/helpers/schema.ts @@ -259,6 +259,14 @@ export const pmGetPaymasterStubDataParamsSchema = z return [val[0], val[1], val[2], val[3] ?? null] as const }) +export const pimlicoGetTokenQuotesSchema = z.tuple([ + z.object({ + tokens: z.array(addressSchema) + }), + addressSchema, // entryPoint + hexNumberSchema +]) + export type UserOperationV7 = zodInfer export type UserOperationV6 = zodInfer export type JsonRpcSchema = zodInfer diff --git a/packages/permissionless-test/mock-aa-infra/mock-paymaster/index.ts b/packages/permissionless-test/mock-aa-infra/mock-paymaster/index.ts index 44321b33..158b84b8 100644 --- a/packages/permissionless-test/mock-aa-infra/mock-paymaster/index.ts +++ b/packages/permissionless-test/mock-aa-infra/mock-paymaster/index.ts @@ -1,13 +1,9 @@ import cors from "@fastify/cors" import Fastify from "fastify" import { defineInstance } from "prool" -import { http } from "viem" +import { http, createPublicClient } from "viem" +import { createBundlerClient } from "viem/account-abstraction" import { foundry } from "viem/chains" -import { - ENTRYPOINT_ADDRESS_V06, - ENTRYPOINT_ADDRESS_V07 -} from "../../../permissionless" -import { createPimlicoBundlerClient } from "../../../permissionless/clients/pimlico" import { getAnvilWalletClient } from "./helpers/utils" import { setupVerifyingPaymasterV06, @@ -39,16 +35,14 @@ export const paymaster = defineInstance( anvilRpc ) - const altoBundlerV07 = createPimlicoBundlerClient({ - chain: foundry, - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 + const publicClient = createPublicClient({ + transport: http(anvilRpc), + chain: foundry }) - const altoBundlerV06 = createPimlicoBundlerClient({ + const bundler = createBundlerClient({ chain: foundry, - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V06 + transport: http(altoRpc) }) app.register(cors, { @@ -57,10 +51,10 @@ export const paymaster = defineInstance( }) const rpcHandler = createRpcHandler( - altoBundlerV07, - altoBundlerV06, + bundler, verifyingPaymasterV07, verifyingPaymasterV06, + publicClient, walletClient ) app.post("/", {}, rpcHandler) diff --git a/packages/permissionless-test/mock-aa-infra/mock-paymaster/relay.ts b/packages/permissionless-test/mock-aa-infra/mock-paymaster/relay.ts index 326af8d6..7fa46e95 100644 --- a/packages/permissionless-test/mock-aa-infra/mock-paymaster/relay.ts +++ b/packages/permissionless-test/mock-aa-infra/mock-paymaster/relay.ts @@ -2,8 +2,10 @@ import util from "node:util" import type { FastifyReply, FastifyRequest } from "fastify" import { type Account, + type Address, BaseError, type Chain, + type Client, type GetContractReturnType, type Hex, type PublicClient, @@ -12,22 +14,19 @@ import { type WalletClient, concat, encodeAbiParameters, + getAddress, toHex } from "viem" +import { + type BundlerClient, + type UserOperation, + entryPoint06Address, + entryPoint07Address +} from "viem/account-abstraction" +import { readContract } from "viem/actions" import { fromZodError } from "zod-validation-error" +import { getPackedUserOperation } from "../../../permissionless" import { - ENTRYPOINT_ADDRESS_V07, - type EstimateUserOperationGasReturnType, - getPackedUserOperation -} from "../../../permissionless" -import type { PimlicoBundlerClient } from "../../../permissionless/clients/pimlico" -import type { - ENTRYPOINT_ADDRESS_V06_TYPE, - ENTRYPOINT_ADDRESS_V07_TYPE, - UserOperation -} from "../../../permissionless/types" -import { ENTRYPOINT_ADDRESS_V06 } from "../../../permissionless/utils" -import type { VERIFYING_PAYMASTER_V06_ABI, VERIFYING_PAYMASTER_V07_ABI } from "./helpers/abi" @@ -35,9 +34,9 @@ import { InternalBundlerError, type JsonRpcSchema, RpcError, - UserOperationV7, ValidationErrors, jsonRpcSchema, + pimlicoGetTokenQuotesSchema, pmGetPaymasterData, pmGetPaymasterStubDataParamsSchema, pmSponsorUserOperationParamsSchema @@ -45,12 +44,12 @@ import { import { maxBigInt } from "./helpers/utils" const handleMethodV06 = async ( - userOperation: UserOperation<"v0.6">, - altoBundlerV06: PimlicoBundlerClient, + userOperation: UserOperation<"0.6">, + bundler: BundlerClient, verifyingPaymasterV06: GetContractReturnType< - typeof VERIFYING_PAYMASTER_V06_ABI, - PublicClient + typeof VERIFYING_PAYMASTER_V06_ABI >, + publicClient: Client, walletClient: WalletClient, estimateGas: boolean ) => { @@ -67,33 +66,29 @@ const handleMethodV06 = async ( const preVerificationGas = userOperation.preVerificationGas if (estimateGas) { - let gasEstimates: - | EstimateUserOperationGasReturnType - | undefined = undefined try { - gasEstimates = await altoBundlerV06.estimateUserOperationGas({ - userOperation: op + const gasEstimates = await bundler.estimateUserOperationGas({ + ...op }) + op = { + ...op, + ...gasEstimates + } + + op.callGasLimit = maxBigInt(op.callGasLimit, callGasLimit) + op.preVerificationGas = maxBigInt( + op.preVerificationGas, + preVerificationGas + ) + op.verificationGasLimit = maxBigInt( + op.verificationGasLimit, + verificationGasLimit + ) } catch (e: unknown) { if (!(e instanceof BaseError)) throw new InternalBundlerError() const err = e.walk() as RpcRequestError throw err } - - op = { - ...op, - ...gasEstimates - } - - op.callGasLimit = maxBigInt(op.callGasLimit, callGasLimit) - op.preVerificationGas = maxBigInt( - op.preVerificationGas, - preVerificationGas - ) - op.verificationGasLimit = maxBigInt( - op.verificationGasLimit, - verificationGasLimit - ) } else if ( userOperation.preVerificationGas === 1n || userOperation.verificationGasLimit === 1n || @@ -118,11 +113,14 @@ const handleMethodV06 = async ( ), toHex(0, { size: 65 }) ]) - const hash = await verifyingPaymasterV06.read.getHash([ - op, - validUntil, - validAfter - ]) + + const hash = await readContract(publicClient, { + abi: VERIFYING_PAYMASTER_V06_ABI, + functionName: "getHash", + address: verifyingPaymasterV06.address, + args: [{ ...op, initCode: op.initCode ?? "0x" }, validUntil, validAfter] + }) + const sig = await walletClient.signMessage({ message: { raw: hash } }) @@ -149,12 +147,12 @@ const handleMethodV06 = async ( } const handleMethodV07 = async ( - userOperation: UserOperation<"v0.7">, - altoBundlerV07: PimlicoBundlerClient, + userOperation: UserOperation<"0.7">, + bundler: BundlerClient, verifyingPaymasterV07: GetContractReturnType< - typeof VERIFYING_PAYMASTER_V07_ABI, - PublicClient + typeof VERIFYING_PAYMASTER_V07_ABI >, + publicClient: Client, walletClient: WalletClient, estimateGas: boolean ) => { @@ -170,33 +168,30 @@ const handleMethodV07 = async ( const preVerificationGas = userOperation.preVerificationGas if (estimateGas) { - let gasEstimates: - | EstimateUserOperationGasReturnType - | undefined = undefined try { - gasEstimates = await altoBundlerV07.estimateUserOperationGas({ - userOperation: op + const gasEstimates = await bundler.estimateUserOperationGas({ + ...op }) + + op = { + ...op, + ...gasEstimates + } + + op.callGasLimit = maxBigInt(op.callGasLimit, callGasLimit) + op.preVerificationGas = maxBigInt( + op.preVerificationGas, + preVerificationGas + ) + op.verificationGasLimit = maxBigInt( + op.verificationGasLimit, + verificationGasLimit + ) } catch (e: unknown) { if (!(e instanceof BaseError)) throw new InternalBundlerError() const err = e.walk() as RpcRequestError throw err } - - op = { - ...op, - ...gasEstimates - } - - op.callGasLimit = maxBigInt(op.callGasLimit, callGasLimit) - op.preVerificationGas = maxBigInt( - op.preVerificationGas, - preVerificationGas - ) - op.verificationGasLimit = maxBigInt( - op.verificationGasLimit, - verificationGasLimit - ) } else if ( userOperation.preVerificationGas === 1n || userOperation.verificationGasLimit === 1n || @@ -221,11 +216,14 @@ const handleMethodV07 = async ( toHex(0, { size: 65 }) ]) op.paymaster = verifyingPaymasterV07.address - const hash = await verifyingPaymasterV07.read.getHash([ - getPackedUserOperation(op), - validUntil, - validAfter - ]) + + const hash = await readContract(publicClient, { + abi: VERIFYING_PAYMASTER_V07_ABI, + functionName: "getHash", + address: verifyingPaymasterV07.address, + args: [getPackedUserOperation(op), validUntil, validAfter] + }) + const sig = await walletClient.signMessage({ message: { raw: hash } }) @@ -257,16 +255,14 @@ const handleMethodV07 = async ( } const handleMethod = async ( - altoBundlerV07: PimlicoBundlerClient, - altoBundlerV06: PimlicoBundlerClient, + bundler: BundlerClient, verifyingPaymasterV07: GetContractReturnType< - typeof VERIFYING_PAYMASTER_V07_ABI, - PublicClient + typeof VERIFYING_PAYMASTER_V07_ABI >, verifyingPaymasterV06: GetContractReturnType< - typeof VERIFYING_PAYMASTER_V06_ABI, - PublicClient + typeof VERIFYING_PAYMASTER_V06_ABI >, + publicClient: PublicClient, walletClient: WalletClient, parsedBody: JsonRpcSchema ) => { @@ -284,21 +280,23 @@ const handleMethod = async ( const [userOperation, entryPoint] = params.data - if (entryPoint === ENTRYPOINT_ADDRESS_V07) { + if (entryPoint === entryPoint07Address) { return await handleMethodV07( - userOperation as UserOperation<"v0.7">, - altoBundlerV07, + userOperation, + bundler, verifyingPaymasterV07, + publicClient, walletClient, true ) } - if (entryPoint === ENTRYPOINT_ADDRESS_V06) { + if (entryPoint === entryPoint06Address) { return await handleMethodV06( - userOperation as UserOperation<"v0.6">, - altoBundlerV06, + userOperation, + bundler, verifyingPaymasterV06, + publicClient, walletClient, true ) @@ -329,7 +327,7 @@ const handleMethod = async ( icon: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAgEAkACQAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCADEAMQDAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD+IOv6OP1MKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAHL3pMljsD0FIV2GB6CgLsMD0FAXYYHoKAuwwPQUBdhgegoC7DA9BQF2GB6CgLsa3amhobTKCgAoAKACgAoAKACgAoAKACgAoAKACgAoAcvekyZdB9Ik7fw18M/iR4zsZtU8H/D7xv4r023u3sJ9R8NeFNe12xgvooLe5kspbvS7C6t47uO3u7W4e3eQTJDc28rIEmjZsp1qNN2qVaUHa9p1IRdtr2k07XTXyJlOEHaU4xe9pSSdu+rR0P/ChPjn/ANEX+LH/AIbnxh/8pqj63hf+gmh/4Op//JC9rS/5+0//AAOP+Yv/AAoT46f9EX+LH/hufGH/AMpqPreF/wCgmh/4Op//ACQe1pf8/af/AIGv8w/4UJ8dP+iL/Fj/AMNz4w/+U1H1vC/9BND/AMHU/wD5IPa0v+ftP/wNf5h/woT46f8ARF/ix/4bnxh/8pqPreF/6CaH/g6n/wDJB7Wl/wA/af8A4Gv8w/4UJ8dP+iL/ABY/8Nz4w/8AlNR9bwv/AEE0P/B1P/5IPa0v+ftP/wADX+Yf8KE+On/RF/ix/wCG58Yf/Kaj63hf+gmh/wCDqf8A8kHtaX/P2n/4Gv8AMafgH8dTjHwW+LJ/7pz4w/8AlNVRxOGe2IoP/uNT/wDkhqtR1/e0/wDwOP8AmO/4Z/8Ajp/0Rf4s/wDhufGH/wApqv6xh/8An/R/8G0//kifrNH/AJ+0/wDwOP8A8kH/AAz/APHT/oi/xZ/8N14w/wDlNR9Yw/8Az/o/+Daf/wAkH1mj/wA/af8A4HH/AOSD/hn/AOOn/RF/iz/4brxh/wDKal9Yw/8Az/o/+Daf/wAkH1ml/wA/Kf8A4HH/AOSOc8S/C74k+DbNNR8XfD7xx4V0+SUQR3/iTwnr2h2ckxGRCl1qdhawPKeojVy+O1XGrSm7Qq05vtCcZP7otsuFanN2jOEn2jJN/cmzhtp9q0sXzLzDafaiwcy8xdnvRYObyDZ7/p/9eiwc3kGz3/T/AOvRYObyDZ7/AKf/AF6LBzeQbPf9P/r0WDm8hlIoKACgAoAcvekyZdB9Ik/0pP8AgzjBP/BMv46+37dvxLA+n/DPn7MB/LJNfk/HkIxzjDWWkstpSbu9/rWMWmvkrnx/ETf12mv+oal/6VM/rKxXxVl5/e/8z5+3mvuQYosvP73/AJhbzX3IMUWXn97/AMwt5r7kGKLLz+9/5hbzX3IMUWXn97/zC3mvuQYosvP73/mFvNfcg2juAfqKa02uvm/8xrT+rBtX0H5Ua9397/zANq+g/KjXu/vf+YWXZfcG1fQflRr3f3v/ADCy7L7jj/H/AMPfAnxU8G+Ifh58S/B3hnx94F8V6dPpPiXwf4v0XT/EPhzXdNuBiWy1TR9Ut7mxvIGIV1WeF/LlRJYyksaOulKrVoVI1aNSdOpB3hOE5RlFrqmndf0ioTlTkp05OEou6lF2afk0f463/BWr9l3wT+xh/wAFG/2sf2bPhqblPh78O/iRBP4IsbueS7n0Xwp468K+HfiPoPhxryYme9Xw1pfi+18PxXlyz3V1FpqT3UstzJLI37pkWMq5hleExddp1atN+0cVZOcJzpylbZOXJzNKyu3ZJWR+g5fXnicHQrVHecoe80rXcW4N/Plv89NND8669g7AoAKACgAoAKAIak0CgAoAKAHL3pMmXQfSJP6pv+CHH/BwJ8Ef+CU37KHxD/Z5+JXwE+KnxS1zxn+0P4q+M9p4g8D634U0zSbPSvEPw1+EvgaHRriDXriK7fULe8+HV9fSzRKbZrbUbREJljmx8dxHw1iM7xtHE0cTQoxpYSGHcaqm5OUa1eq5LlTVrVku90zxMzyqrj8RCtCrTpqNGFO01K94uTvov7x+zH/EZz+yf/0Z3+0L/wCFX8Of/k2vA/1Cxv8A0H4T/wABrf8AyJ53+rmI/wCgmh90/wDIP+Izn9k//ozv9oX/AMKv4c//ACbR/qFjf+g/Cf8AgNb/AORD/VzEf9BND7p/5B/xGc/sn/8ARnf7Qv8A4Vfw5/8Ak2j/AFCxv/QfhP8AwGt/8iH+rmI/6CaH3T/yD/iM5/ZP/wCjO/2hf/Cr+HP/AMm0f6hY3/oPwn/gNb/5EP8AVzEf9BND7p/5B/xGc/sn/wDRnf7Qv/hV/Dn/AOTaP9Qsb/0H4T/wGt/8iH+rmI/6CaH3T/yD/iM5/ZP/AOjO/wBoX/wq/hz/APJtH+oWN/6D8J/4DW/+RD/VzEf9BND7p/5B/wARnH7KHb9jv9oT8fF3w3X8gb3JqJcCY6P/ADG4V+kav6oT4dxC/wCYik/SM/8AIT/iM5/ZR/6M5/aF/wDCu+G3/wAnUf6i43/oMof+C6ov9Xa//P8Apf8AgMg/4jOf2Uf+jOf2hf8Awrvht/8AJ1H+ouN/6DKH/gqqH+rtf/n/AEv/AAGR+nf/AASn/wCC/fwU/wCCrfx58cfAX4bfAX4o/CzWfA3wk1X4uXeu+ONd8J6npl9pmk+MfBXg6XSbaDQLia6S+luvG1peJNKBbrb2VwjHzJIgfHzjh3EZPQp161elVjUrKilCM4yTdOdTmfMrWtBrvdo48blVXBU1VnVhNOSjaKknqm7u+nQ/fevnjyj/ACR/+DjYAf8ABaP9uHHfxN8JCfr/AMM8fCGv2rhV2yLA6/Yq/wDqRWPvMn/5F+H/AMM//Tkz8SK+kT7s9MWmAUwCgAoAKAIak0CgAoAKAHKQM0mS0O3D1pWYrM6zwN4D8cfE7xVo3gX4beDfFXxA8a+Irn7FoHhDwV4e1fxT4n1u72NKbbSdB0OzvtU1CdYkkleO0tZXSKOSVgERmGdWpToQlVrVIUqcfinUlGEFfRXlJqK+bRM5Rpxc5yjCK3lKUYxXrKTSXzZ95R/8Eff+CqEsaSp/wT2/a8KSIrrv+BPxAibawBG6OXRUkjYA/MjorKcggGvNee5Mrp5ngrrp7aL19Y3T+TON5ll6dnjMPfyqxf4q6+5jv+HPf/BVL/pHt+11/wCGN8ef/Keo/t/KOuY4O3lWiV/aGAf/ADG4b/wdD8r3D/hz3/wVS/6R7ftdf+GN8ef/ACno/wBYck/6GWE/8HwD6/gf+gzDf+DY/wCYf8Oe/wDgql/0j2/a6/8ADG+PP/lPR/rDkn/Qywn/AIPgH1/A/wDQZhv/AAbH/MP+HPf/AAVS/wCke37XX/hjfHn/AMp6P9Yck/6GWE/8HwD6/gf+gzDf+DY/5h/w57/4Kpf9I9v2uv8Awxvjz/5T0f6w5J/0MsJ/4PgH1/A/9BmG/wDBsf8AMX/hz1/wVR7/APBPf9rkfX4G+PP/AJT0f29k0vhzLB6d8RTX5tB/aOAW+MofKfN+V7B/w56/4Kof9I+f2uP/AAxvj3/5TVazzJ7X/tLBf+FNL9ZC/tLLv+gyj97/AMj49+N3wA+N37NfjY/Db9oD4U+Pfg34/XSbDXm8G/EbwzqnhPxGNF1UzrpuqHStXt7W7+w3xtbkWtyIzFMYJQjEowHbhsXhsbT9thK9LEUlKUHUpSU4c8fijzLS66nTTq0q0eelUjUg9pRd0f0//wDBm/8A8pGv2gv+zK/GZ/L45fAOvkOPP+RXhP8AsYQX/ltif8jxeIP90g/+nyX/AJJI/wBJKvys+NP8kf8A4ON/+U0f7cH/AGMvwj/9Z4+EVftHC/8AyIsB/gq/+pFY+9yf/kXYbzjP/wBO1F+h+JFfSHpC1a2QBTAKACgAoAhqTQKACgAoAKACgD/Qv/4M2v2cfhjD+zH+0j+1hceHNMvfjBq/x/1T4C2Pie8s4LjVPD3gLwd8Nvhf47n0vRLyRWl02DxJrfxHefXUtTGdQXQNEW5aRLKFV/MOPMXW+uYTBKclh/qqxMoKVoyqyrV6alJLdxjTtG+15NbnyHEVaft6VDmfs/ZKpyptJyc5xu+9lGy7Xfc/tD2+7fma/P7I+b+YbR6n8zRyoPn+X+QbR7/nRZef3k8q8/vDaPf86LLz+8OVef3htHv+dFl5/eHKvP7w2j3/ADosvP7w5V5/eKBimlYaVhaBn+X7/wAHaf8Aylmuv+zbvg3/AOlnjSv1vge39iK3/QXXv62p/pY+1yD/AHF/9f6n/tp7j/wZvf8AKRv9oL/syrxn/wCry+AVYcef8ivB/wDYxp/+o2KI4g/3OH/X+P8A6RM/0ka/Kz40/wAkb/g42/5TR/tw/wDYzfCT/wBZ4+ENftHC/wDyIsv/AMFX/wBSax97k/8AyLsL/hqf+n6p+JNfSHpC1a2QBTAKACgAoAhqTQKACgAoAKACgD/Sq/4M5P8AlGX8df8As+z4l/8ArP37MNfk/Hf/ACNsN/2LqX/qVjD4riL/AH2l/wBgy/8ATtU/q91K7Nhp1/fLGJTZ2dzdCItsEht4Xl2b9rbN+zbu2ttznacYPxcVzSiu7S+88JK7S7tL72kfwNzf8HpPxRilljH7APgIiOR0BP7QfiAEhGK5IHwnwCcZx26V+kf6gU3qszqRXZ4WEmvV/WF+R9T/AKtJaPFyb6tU4pfL3mR/8RpnxR/6MA8Bf+JB+IP/AJ09L/iH8P8AoaT/APCOP/zSH+ra/wCgqX/guP8A8kH/ABGmfFH/AKMA8Bf+JB+IP/nT0f8AEP4f9DSf/hHH/wCaQ/1bX/QVL/wXH/5IP+I0z4o/9GAeAv8AxIPxB/8AOno/4h/D/oaT/wDCOP8A80h/q2v+gqX/AILj/wDJB/xGmfFH/owDwF/4kH4g/wDnT0f8Q/h/0NJ/+Ecf/mkP9W1/0FS/8Fx/+SD/AIjTfih3/YB8Bj6ftB+IP/nTVMuAoRt/wpTf/crFf+52H+rcOuLqL0owl+c0L/xGm/E7/owHwH/4kH4g/wDnTVP+ocf+hjP/AMJo/wDy4P8AVun/ANBtX/wRD/5afzTf8FUv+Cimt/8ABUH9qmb9p/Xvhbpfwfvpfh34Q+Hw8H6R4qu/GNosHhKTWJY9TOs3uh+Hpmlvm1eQPbf2cqQCFAsspckfXZHlSyfA/U1Wdde2qVfaSgoP31FcvKpSWnLvfXsezgcHHA0PYxqSq+/KfNKCh8SWllKXbv1P28/4M3v+UjX7QX/ZlXjP/wBXl8Aq8Djz/kV4P/sY0/8A1GxR53EH+50/+v8AH/0iZ/pI1+Vnxp/kjf8ABxt/ymj/AG4f+xm+Ef8A6zx8Ia/aOF/+RFl/+Cr/AOpNY+9yf/kXYX/DU/8AT9U/EmvpD0hatbIApgFABQAUAQ1JoFABQAUAFABQB/pV/wDBnJ/yjK+On/Z9nxM/9Z+/Zhr8n48/5G+G/wCxdR/9ScWfFcRf77R/7BY/+nqx/Vj4h/5AGuf9gjUv/SOavidtTwo/FH/FH/0pH+Fnd/8AH1c/9d5v/RjV/RkPgj6I/U3v935H0p+yx+xn+1D+2z48uvhr+yz8FvGXxl8XadYx6prNp4Zt7O30zw9pcs62sWpeJvE2t3mleGPDVlPct9ntbnXtY06K7uA0Fs0sqOg5MdmOCy2kq2NxEMPTk+WLlduTte0YxTlJ+idupzV8TQw0eatUUE3ZX1bfWyV27eh+oH/ENN/wWfPP/DIQH1+NvwAz9Dj4nkZHfHHpXj/63ZB/0G/+Ua3/AMrOP+2cu/6CEvWE/wD5EX/iGl/4LQf9Ghr/AOHt+AP/AM8+k+L8gX/MY36UK/8A8rE86y5f8v7+kJ//ACIf8Q0v/BaD/o0Nf/D2/AH/AOefT/1vyD/oNf8A4Ir/APysP7ay7/n/AP8AlOf/AMiH/ENJ/wAFnz1/ZDH4fG34Af1+J9TLizIpbYz/AMo1/wD5WJ51l3/P/wD8kn/8iJ/xDR/8Fnv+jQx/4e34Af8Azz6n/WvIv+gz/wAo1v8A5WL+2cu/5/8A/kk//kRf+IaP/gs9/wBGh/8AmbfgB/8APPqlxZkXXG/+Ua//AMrD+2cu/wCf/wD5JP8A+RP6JP8Ag2p/4JGft/fsA/tp/GH4s/tXfAv/AIVh4C8U/sweJvh5oWuf8LC+GPi77b4u1D4q/CLxJaaV9g8E+MfEOp2/m6N4Y1u8+2XNnFYr9i8h7lbie3im+X4tzvLczwOGo4LEe2qQxkas4+zqQtTVCvByvOEU/enFW31v0PJzjMMLisPCFCrzyVTma5ZxsuWSv70Ut2j+2uvgD5k/yR/+Djf/AJTR/twf9jL8I/8A1nn4RV+0cL/8iLAf4Kv/AKkVj73J/wDkX4f0n/6cmfiRX0h6QtWtkAUwCgAoAKAIak0CgAoAKACgAoA/0q/+DOT/AJRlfHT/ALPs+Jn/AKz9+zDX5Px5/wAjfDf9i6j/AOpOLPiuIv8AfaP/AGCx/wDT1Y/qx8Q/8gDXP+wRqX/pHNXxJ4Ufij/ij/6Uj/Czu/8Aj6uf+u83/oxq/oyn8EfRH6m9/u/I/wBRH/g1L+FfgjwT/wAEmfAvjzw/oVjZeLfjH8U/i54n8fa8lvENT12+8MeNtW8AeH4Lq92/aJLDR/D3hiyh0+xaQ2tpc3WqXUEUc+pXsk/4/wAaVqtXOqlOdSTp4ejRp0qd/cgpU1Uk0tlKUptyfxNcqbskl8JntWc8wlBv3adOEYLtdKUvvuv62/pTr5KyPGCjlXmAUcq8wCmlYAoAKACgAPAoA/yI/wDg4K8X+HPHP/BYz9ubXvCurWOt6TB8RfCPheS+0+5iurddd8C/CT4e+B/FenGWFnT7Ro3inw7rOj3ke7dDeWE8MgWSN1X9t4XpuOR4CM4yi/ZSmk1a8alWpUhJd1KEoyT6ppn32Uxccvwye7hKVvJ1JNP5ppo/G0CvfaZ6aSYuBVLZDsgwKYWQYFAWQYFAWQYFAWRBSGFABQAUAOXvSZLHYHoKQrs/0pf+DOMk/wDBMv46f9n2fEz/ANZ//ZiH9K/JuOZXzahfpgKa+7EYr/M+L4gk3jqflh4r/wAq1f1/U/qz8Q/8gDW/+wRqP/pHNXxqV2l30+88RfFH/HD/ANKR/hZ3nF3c/wDXxMPykav6LgnyRT0aSUl2fVH6i5aJ23S/JH+qv/wa85/4c0fs4k/9Df8AHr/1dnjj/P4V+NcY6Z/i49PZ4WXzlh6bZ8DnOuY1319xf+SR/wAj9h/2ufiv4j+A/wCyp+0x8cPB9po9/wCLfg3+z98Zvir4XsfEVveXfh+98RfDz4c+JPF2i2mu2mnX+lahdaNc6lpFtDqdtY6ppl5PZPNFa6hZzulxH4ODowxGLw2Hm2oV69GlJxaUlGpUhCTi2mk0pNptNJ7pnBQp+1rUabvy1KtOEmt1GclFtX6q5/nq/wDEYn/wU7/6I3+w9/4bD44//RH1+lrgLKGrvE5nrr/Hw3/zIfWf6vYP/n7iP/Aqf/yoT/iMT/4Kd/8ARGv2Hv8Aw2Hxx/8Aoj6P9Qsn/wCgnM//AAfhv/mQf+r2D/5+4j/wKn/8qEb/AIPFP+Cnf/RGv2H/APw2PxyH8v2kKuPAeUL/AJiMxfrXo/8AtuGiC4dwb/5e4j/wKn/8rG/8Rin/AAU8/wCiN/sP/wDhsvjn/wDRIU/9Q8o/5/5h/wCD6f8A8pH/AKu4P/n7iPvp/wDysP8AiMU/4Kef9Eb/AGH/APw2Pxz/APokKP8AUPKP+f8AmH/g+n/8oD/V3B/8/cR99L/5WH/EYr/wU8/6I1+w9/4bH45//RI0f6hZP/0EZj/4Ppf/ADOH+ruD/wCfuI++j/8AKjyT41f8HYv/AAVY+L3gTWfA+hv+zx8C5tbs7rT7nxv8Fvhx4wsPHVnaXsLW840bV/iJ8TfiRYaLeCN3Ntq+l6Ra6zp8xW607ULS7igni2ocEZNRqRqSeLxCi7+yr1oypS7c8YU6bkv7rfK1pJNG1LIcDTalL2tWzTSqSjy6d1CEb+jdu6Z/NXqeralreo3+saxf3uravqt7danquq6nd3F/qWp6lfTyXV9qGoX128t1e315cyy3F3d3MstxczySTTSPI7MfroQjTioxSUUlGMUkoxilZJJaJJaJLRJJJaHr8iW1klskv8ijv9v1qxpW6/h/wRfM9qQ9f6X/AAQ8z2oDX+l/wQ8z2oDX+l/wQ8z2oDX+l/wQ8z2oDX+l/wAEjoGFABQAUAOXvSZMug+kSf6Uv/BnH/yjK+On/Z9nxM/9Z/8A2Yf/AK9fknHP/I2w/wD2BUv/AFKxif4JHxeff77H/rxH/wBO1v8AgH9WfiD/AJAOt/8AYJ1H/wBI5q+Pjo0+zPEW8f8AFH/0pH+Fje/8fd37XM//AKNav6Hpzbpxvq+VXfXY/UUrxj6L8kf6rX/Br1/yhn/Zw/7G/wCPf/q7fHNfjvF//I/xn+DC/wDqPTPg87/5GVf0p/8Apqmfpx/wUk/5R3/t5H0/Yx/ai/8AVH+Oa8jK/wDkZYD/ALDMN/6fpnFhL/W8Kl1xNH/04j/FjJya/eo6JI/R99T+in9mf/g2J/4KLftW/AL4TftHfDPxZ+y/Z+APjL4L0jx54StfFnxL8daX4kg0XWofPs49Z07TvhRrFlZ36p/r4LbVL6JG4W4frXy+M4wyzBYqvhKtPFuph6jpzlClFwcla/K3NXS2vbdM8itnWDoVp0ZqrzQk4ytBWTXrJP8AAxv2t/8Ag2k/4KEfsY/s6fFH9p74t+K/2Z774c/CHRLPX/FVp4L+I3jfWPFE1jfa3pWgQro+map8LNCsby4F9q9q7x3GrWSi3WZ1kaREik0wHFuV4/F0MHRhi1VxE1CMp0qagnZv3mqsmlZfyvoOhnWFr1oUacK3NUdk3GCXf/n43t5H89VfUntBQAUAFABQAUAFABQAUAFABQAUAFABQAUAKDii1xNXF3H2pWFZH+lR/wAGcf8AyjK+On/Z9nxM/wDWf/2Y/wDCvyPjj/kbUP8AsCh/6lYo+Jz7/fYf9eF+FWsf1Z+IP+QDrf8A2CdR/wDSOavj1ueIt4/4o/8ApSP8LK8/4/Lv/r5n/wDRr1/QlL+HH/CvyP1KPwx/wr8kf6A3/BBj/gtl/wAExv2NP+CZXwT/AGf/ANpT9puL4a/F3wl4l+Lt94g8Iv8ACH49eLGsLTxN8UvFfiLQ5v7b8C/C7xP4buhfaNqVleBLPWLiS3E/kXaQXMcsKfnHEnD+b47N8TicJg51qFSGHUKiqUYqThRhCWk6kZaSTTut12Pks0yvHYjHVq1GhKdOShaXNBXtTgno5J6NNao+6/23P+DhX/gj78XP2Mv2t/hT8Pf2v4PEPj74mfsyfHr4feCNAHwL/aW0s634v8Z/CrxX4c8NaQNS1r4Nado+nnUtZ1KyshfarqFjptoZ/tF9eWtrHLOnnYDhnPaGNwlapgJxp0sVh6lSXtaD5acK0JTlZVW3aKbsk32Ry4bJ8xp4nDVJ4aShTrU5zfPDSMZxcnpJt2Sb0XQ/zBO9frii7X9T7VKzS8z/AGJv+CJ3/KJr9gH/ALNr+Hv/AKQPX4dxF/yOcx/7Cqn5n55mf/Ixxn/X9nnP/BwL/wAoc/26/wDsmHh7/wBWf4Eq+Gv+R5l7Wlqz/wDTVT8uhWWaY/Df4p/+m5H+QzX7mfogUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAf6VP/BnH/yjK+On/Z9nxL/9UB+zHX5Fxx/yNqH/AGBw/wDUnFHxGff75D/rwv8A09WP6s/EP/IB1v8A7BGo9Bk/8ec3Ycn8K+PW6XdpfN6I8Nbx/wAUf/Skf4gt38DfjWbq6YfB/wCKJBuJiCPAHisggyMQeNJ7iv6BpVaSpxbrUkuVaurC2i6Pm1P06NWi4Raq0mnFf8vI9vW5X/4UZ8bP+iPfFL/w3/iz/wCVNX7eh/z/AKH/AIOp/wDyRXtaX/P2n/4HH/MP+FGfGz/oj3xS/wDDf+LP/lTR7eh/z/of+Dqf/wAkHtaX/P2n/wCBx/zA/A341gZPwe+KWPX/AIV/4s/+VFCxOHWn1ih6e2pdv8QvaUv+flK/+OH+Z/rwf8EYdM1LRf8AglT+wZpWsaffaVqlh+zj4Atb/TdStJ7G/srmKxdZbe7s7qOK4tp42yskU0aSIwIZQa/EeIZRlnGYyi1KLxVS0otNPXo1dM/PMyaeYYtppp13Zp3T801o0eV/8HAv/KHP9uz/ALJh4e/9Wf4Eq+Gf+R5l/wD1+f8A6bqFZZ/yMML/AIp/+m5H+QzX7ofooUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAf6Uv8AwZxTRN/wTP8AjvCHQyx/t0/EiR4wwLrHL8AP2ZxE7LnIR2ilVGIwxjcAnacfkfHC/wCFWh/2BR+9YrF/193c+Hz92xsE+tBf+nax/WfXxp4g3aOwA+gFLbRILJ77+i/UNopXfZfcKy8vuj/kG0UXfZfcFl5fdH/INopqUu1vkFl5fdH/ACFxQ1f/AIYLL+v+AfjX/wAHBtzbWn/BHD9umS5nit0k+G/hW1RppFjV7i9+K/w/s7SBSxAMtzdTw28KD5pJpUjUFmAr3eGYt57l1tf3s2/SNCrJ/gj0MrV8ww1t+af/AKbmf5EJUjtX7kfod/kGD6H8qAuu4YPofyoC67hg+h/KgLruGD6H8qAuu4YPofyoC67hg+h/KgLruGD6H8qAuu4YPofyoC67hg+h/KgLruGD6H8qAuu4lAwoAKACgAoAKAP22/4I1f8ABar4tf8ABJPxv41tbPwRbfGX4A/FifS734i/Cq716fw1qtp4g0a2ns9M8a+A/EX2PV7LRfEMdpOthrVrqOh6lp/ibSbWz0+4Om3dlpmsad87nuQUc5hBup7DE0rqnWUOdOL3hUhzR5o3V01JSi7tXu0/IzLLKePUXz+yrU78tTl5k4veEldNq6umndO71u0/6mYv+DzX9kAxp5v7In7SKSlV8xI9f+GMiK+PmVJG16FnUHhWaKMsMEopOB8a+A8fd2x2Da6Nxrpv5KnK33v1PE/1dxP/AD/oP5VF/wC2kn/EZn+x9/0aL+0p/wCDz4X/APzRUv8AUTH/APQdg/ur/wDyoP8AV3Ff8/8AD/8AlX/5WH/EZn+x9/0aL+0p/wCDz4Xf/NFR/qJj/wDoOwf3V/8A5UH+ruK/5/4f/wAq/wDysP8AiMz/AGPv+jRf2lP/AAefC/8A+aKj/UTH/wDQdg/ur/8AyoP9XcV/z/w//lX/AOVh/wARmX7H3/Rov7Sn/g8+F/8A80VH+omP/wCg3B/dX/8AlQv9XcV/z/w//lT/AOQF/wCIzL9j7/o0b9pP/wAHnwv/APmio/1Ex/8A0G4P7q//AMrH/q7iv+f9D/yp/wDIH4G/8Fnf+Divx/8A8FOfhtZ/s3/Cf4VXfwG/Z0bXNL8SeNrTXvEVv4n+IHxQ1Xw/eJqPhu01y50yz0/RPD3hTRNUjt9Zj8OWQ1u5v9e07StWudeSOwg01PpMg4Wp5RWeLr11icVyuNPli4U6KmnGbjduU5Si+XmkopRcko63PVy3J1g5+2qTVSrZqPKnGME97Xd5SfdpWTtbqfzSV9fHqe41cMVdxcq8wxRcOVeYYouHKvMMUXDlXmGKLhyrzDFFw5V5hii4cq8wxRcOVeYYouHKvMMUXDlXmQUigoAKACgAoAKAJR0H0FHL5kPdhRy+Ygo5fMAo5fMAo5fMAo5fMAo5fMBRUtWLjt8x1EeowqgCgAoAKACgAoAKACgAoAKAK9ABQAUAFABQAUASjoPoKZm936i0wCgAoAKACgAoAUVEuhcdvmLSj1GFUAUAFABQAUAFABQAUAFABQBXoAKACgAoAKACgCUdB9BTM3u/UWmAUAFABQAUAFACiol0Ljt8xaUeowqgCgAoAKACgAoAKACgAoAKAK9ABQAUAFABQAUASjoPoKZm936i0wCgAoAKACgAoAUVEuhcdvmLSj1GFUAUAFABQAUAFABQAUAFABQBXoAKACgAoAKACgCUdB9BTM3u/UWmAUAFABQAUAFACiol0Ljt8xaUeowqgCgAoAKACgAoAKACgAoAKAK9ABQAUAFABQAUASjoPoKZm936i0wCgAoAKACgAoAUVEuhcdvmLSj1GFUAUAFABQAUAFABQAUAFABQAP/Z" } - if (entryPoint === ENTRYPOINT_ADDRESS_V07) { + if (entryPoint === entryPoint07Address) { return { paymaster: verifyingPaymasterV07.address, paymasterData: @@ -341,7 +339,7 @@ const handleMethod = async ( } } - if (entryPoint === ENTRYPOINT_ADDRESS_V06) { + if (entryPoint === entryPoint06Address) { return { paymasterAndData: `${verifyingPaymasterV06.address}00000000000000000000000000000000000000000000000000000101010101010000000000000000000000000000000000000000000000000000000000000000cd91f19f0f19ce862d7bec7b7d9b95457145afc6f639c28fd0360f488937bfa41e6eedcd3a46054fd95fcd0e3ef6b0bc0a615c4d975eef55c8a3517257904d5b1c`, sponsor: sponsorData, @@ -367,21 +365,23 @@ const handleMethod = async ( const [userOperation, entryPoint] = params.data - if (entryPoint === ENTRYPOINT_ADDRESS_V07) { + if (entryPoint === entryPoint07Address) { return await handleMethodV07( - userOperation as UserOperation<"v0.7">, - altoBundlerV07, + userOperation as UserOperation<"0.7">, + bundler, verifyingPaymasterV07, + publicClient, walletClient, false ) } - if (entryPoint === ENTRYPOINT_ADDRESS_V06) { + if (entryPoint === entryPoint06Address) { return await handleMethodV06( - userOperation as UserOperation<"v0.6">, - altoBundlerV06, + userOperation, + bundler, verifyingPaymasterV06, + publicClient, walletClient, false ) @@ -407,23 +407,59 @@ const handleMethod = async ( ] } + if (parsedBody.method === "pimlico_getTokenQuotes") { + const params = pimlicoGetTokenQuotesSchema.safeParse(parsedBody.params) + + if (!params.success) { + throw new RpcError( + fromZodError(params.error).message, + ValidationErrors.InvalidFields + ) + } + + const [context, entryPoint] = params.data + const { tokens } = context + + const quotes = { + [getAddress("0xffffffffffffffffffffffffffffffffffffffff")]: { + exchangeRate: "0x5cc717fbb3450c0000", + postOpGas: "0xc350" + } + } + + let paymaster: Address + if (entryPoint === entryPoint07Address) { + paymaster = verifyingPaymasterV07.address + } else { + paymaster = verifyingPaymasterV06.address + } + + return { + quotes: tokens + .filter((t) => quotes[t]) // Filter out unrecongized tokens + .map((token) => ({ + ...quotes[token], + paymaster, + token + })) + } + } + throw new RpcError( - "Attempted to call an unknown method", + `Attempted to call an unknown method ${parsedBody.method}`, ValidationErrors.InvalidFields ) } export const createRpcHandler = ( - altoBundlerV07: PimlicoBundlerClient, - altoBundlerV06: PimlicoBundlerClient, + bundler: BundlerClient, verifyingPaymasterV07: GetContractReturnType< - typeof VERIFYING_PAYMASTER_V07_ABI, - PublicClient + typeof VERIFYING_PAYMASTER_V07_ABI >, verifyingPaymasterV06: GetContractReturnType< - typeof VERIFYING_PAYMASTER_V06_ABI, - PublicClient + typeof VERIFYING_PAYMASTER_V06_ABI >, + publicClient: PublicClient, walletClient: WalletClient ) => { return async (request: FastifyRequest, _reply: FastifyReply) => { @@ -438,10 +474,10 @@ export const createRpcHandler = ( try { const result = await handleMethod( - altoBundlerV07, - altoBundlerV06, + bundler, verifyingPaymasterV07, verifyingPaymasterV06, + publicClient, walletClient, parsedBody.data ) diff --git a/packages/permissionless-test/src/testWithRpc.ts b/packages/permissionless-test/src/testWithRpc.ts index d05b837e..a730f99c 100644 --- a/packages/permissionless-test/src/testWithRpc.ts +++ b/packages/permissionless-test/src/testWithRpc.ts @@ -1,12 +1,11 @@ import getPort from "get-port" import { alto, anvil } from "prool/instances" +import { + entryPoint06Address, + entryPoint07Address +} from "viem/account-abstraction" import { foundry } from "viem/chains" import { test } from "vitest" -import type { EntryPoint } from "../../permissionless/types/entrypoint" -import { - ENTRYPOINT_ADDRESS_V06, - ENTRYPOINT_ADDRESS_V07 -} from "../../permissionless/utils" import { ENTRY_POINT_SIMULATIONS_ADDRESS, setupContracts @@ -30,7 +29,7 @@ export const getAltoInstance = async ({ }) const instance = alto({ - entrypoints: [ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07], + entrypoints: [entryPoint06Address, entryPoint07Address], rpcUrl: anvilRpc, executorPrivateKeys: [anvilPrivateKey], entrypointSimulationContract: ENTRY_POINT_SIMULATIONS_ADDRESS, diff --git a/packages/permissionless-test/src/types.ts b/packages/permissionless-test/src/types.ts index 3cfd89a6..b6b23a53 100644 --- a/packages/permissionless-test/src/types.ts +++ b/packages/permissionless-test/src/types.ts @@ -1,13 +1,12 @@ import type { Address, Hex, PublicClient } from "viem" -import type { PimlicoPaymasterClient } from "../../permissionless/clients/pimlico" -import type { EntryPoint } from "../../permissionless/types" -export type AAParamType = { - entryPoint: T +export type AAParamType = { + entryPoint: { + version: entryPointVersion + } anvilRpc: string altoRpc: string - paymasterClient?: PimlicoPaymasterClient - privateKey?: Hex + paymasterRpc: string } // param used when testing with a already deployed contract diff --git a/packages/permissionless-test/src/utils.ts b/packages/permissionless-test/src/utils.ts index f43d9a71..bf2fa01d 100644 --- a/packages/permissionless-test/src/utils.ts +++ b/packages/permissionless-test/src/utils.ts @@ -3,71 +3,61 @@ import { type Account, type Address, type Chain, - type Hex, type Transport, type WalletClient, - createClient, createPublicClient, createWalletClient, - getAddress, parseEther } from "viem" +import { + type SmartAccount, + createBundlerClient, + createPaymasterClient, + entryPoint06Address, + entryPoint07Address +} from "viem/account-abstraction" import { generatePrivateKey, mnemonicToAccount, privateKeyToAccount } from "viem/accounts" -import { foundry, sepolia } from "viem/chains" +import { foundry } from "viem/chains" +import { toBiconomySmartAccount } from "../../permissionless/accounts/biconomy/toBiconomySmartAccount" import { - type BundlerClient, - ENTRYPOINT_ADDRESS_V06, - ENTRYPOINT_ADDRESS_V07, - type SmartAccountClient, - createBundlerClient, - createSmartAccountClient -} from "../../permissionless" + type KernelVersion, + toEcdsaKernelSmartAccount +} from "../../permissionless/accounts/kernel/toEcdsaKernelSmartAccount" import { - type SafeSmartAccount, - type SmartAccount, - privateKeyToBiconomySmartAccount, - privateKeyToLightSmartAccount, - privateKeyToSafeSmartAccount, - privateKeyToSimpleSmartAccount, - privateKeyToTrustSmartAccount, - signerToBiconomySmartAccount, - signerToEcdsaKernelSmartAccount, - signerToLightSmartAccount, - signerToSafeSmartAccount, - signerToSimpleSmartAccount, - signerToTrustSmartAccount -} from "../../permissionless/accounts" -import type { KernelEcdsaSmartAccount } from "../../permissionless/accounts" -import type { KernelVersion } from "../../permissionless/accounts/kernel/signerToEcdsaKernelSmartAccount" + type LightAccountVersion, + toLightSmartAccount +} from "../../permissionless/accounts/light/toLightSmartAccount" +import { toSafeSmartAccount } from "../../permissionless/accounts/safe/toSafeSmartAccount" import { - type PimlicoBundlerClient, - type PimlicoPaymasterClient, - createPimlicoBundlerClient, - createPimlicoPaymasterClient -} from "../../permissionless/clients/pimlico" -import { paymasterActionsEip7677 } from "../../permissionless/experimental" -import type { - ENTRYPOINT_ADDRESS_V06_TYPE, - ENTRYPOINT_ADDRESS_V07_TYPE, - EntryPoint -} from "../../permissionless/types" -import type { AAParamType, ExistingSignerParamType } from "./types" + type ToSimpleSmartAccountReturnType, + toSimpleSmartAccount +} from "../../permissionless/accounts/simple/toSimpleSmartAccount" +import { toTrustSmartAccount } from "../../permissionless/accounts/trust/toTrustSmartAccount" +import { createSmartAccountClient } from "../../permissionless/clients/createSmartAccountClient" +import { createPimlicoClient } from "../../permissionless/clients/pimlico" +import type { AAParamType } from "./types" export const PAYMASTER_RPC = "http://localhost:3000" -export const ensureBundlerIsReady = async (altoRpc: string) => { +export const ensureBundlerIsReady = async ({ + altoRpc, + anvilRpc +}: { altoRpc: string; anvilRpc: string }) => { const bundlerClient = getBundlerClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - altoRpc: altoRpc + altoRpc: altoRpc, + anvilRpc, + entryPoint: { + version: "0.6" + } }) while (true) { try { - await bundlerClient.chainId() + await bundlerClient.getChainId() return } catch { await new Promise((resolve) => setTimeout(resolve, 1000)) @@ -107,34 +97,103 @@ export const getAnvilWalletClient = ({ }) } -export const getPimlicoPaymasterClient = ({ - entryPoint, - paymasterRpc -}: { entryPoint: T; paymasterRpc: string }): PimlicoPaymasterClient => { - return createPimlicoPaymasterClient({ - chain: foundry, - transport: http(paymasterRpc), - entryPoint +export const getBundlerClient = ({ + altoRpc, + anvilRpc, + account, + paymasterRpc, + entryPoint +}: { + altoRpc: string + paymasterRpc?: string + anvilRpc: string + account?: account + entryPoint: { + version: "0.6" | "0.7" + } +}) => { + const paymaster = paymasterRpc + ? createPimlicoClient({ + transport: http(paymasterRpc), + entryPoint: { + address: + entryPoint.version === "0.6" + ? entryPoint06Address + : entryPoint07Address, + version: entryPoint.version + } + }) + : undefined + + const pimlicoBundler = createPimlicoClient({ + transport: http(altoRpc), + entryPoint: { + address: + entryPoint.version === "0.6" + ? entryPoint06Address + : entryPoint07Address, + version: entryPoint.version + } + }) + + return createBundlerClient({ + client: getPublicClient(anvilRpc), + account, + paymaster, + transport: http(altoRpc), + userOperation: { + estimateFeesPerGas: async () => { + return (await pimlicoBundler.getUserOperationGasPrice()).fast + } + } }) } -export const getBundlerClient = ({ - entryPoint, - altoRpc -}: { entryPoint: T; altoRpc: string }): BundlerClient => - createBundlerClient({ +export const getSmartAccountClient = < + account extends SmartAccount | undefined +>({ + altoRpc, + anvilRpc, + account, + paymasterRpc +}: { + altoRpc: string + paymasterRpc?: string + anvilRpc: string + account?: account +}) => { + const paymaster = paymasterRpc + ? createPaymasterClient({ + transport: http(paymasterRpc) + }) + : undefined + + return createSmartAccountClient({ + client: getPublicClient(anvilRpc), chain: foundry, - entryPoint, - transport: http(altoRpc) - }) as BundlerClient + account, + paymaster, + bundlerTransport: http(altoRpc) + }) +} -export const getPimlicoBundlerClient = ({ - entryPoint, +export const getPimlicoClient = ({ + entryPointVersion, altoRpc -}: { entryPoint: T; altoRpc: string }): PimlicoBundlerClient => - createPimlicoBundlerClient({ +}: { + entryPointVersion: entryPointVersion + altoRpc: string +}) => + createPimlicoClient({ chain: foundry, - entryPoint, + entryPoint: { + address: (entryPointVersion === "0.6" + ? entryPoint06Address + : entryPoint07Address) as entryPointVersion extends "0.6" + ? typeof entryPoint06Address + : typeof entryPoint07Address, + version: entryPointVersion + }, transport: http(altoRpc) }) @@ -185,321 +244,242 @@ export const fund = async ({ await publicClient.waitForTransactionReceipt({ hash }) } -export const getSimpleAccountClient = async ({ +export const getSimpleAccountClient = async < + entryPointVersion extends "0.6" | "0.7" +>({ entryPoint, - paymasterClient, - anvilRpc, - altoRpc, - privateKey -}: AAParamType): Promise< - SmartAccountClient> + anvilRpc +}: AAParamType): Promise< + ToSimpleSmartAccountReturnType > => { - const publicClient = getPublicClient(anvilRpc) - - const smartAccount = privateKey - ? await privateKeyToSimpleSmartAccount(publicClient, { - entryPoint, - privateKey - }) - : await signerToSimpleSmartAccount(publicClient, { - entryPoint, - signer: privateKeyToAccount(generatePrivateKey()) - }) - - return createSmartAccountClient({ - chain: foundry, - account: smartAccount, - bundlerTransport: http(altoRpc), - // @ts-ignore - middleware: paymasterClient - ? { - sponsorUserOperation: paymasterClient.sponsorUserOperation - } - : undefined + return toSimpleSmartAccount({ + client: getPublicClient(anvilRpc), + entryPoint: { + address: + entryPoint.version === "0.6" + ? entryPoint06Address + : entryPoint07Address, + version: (entryPoint.version === "0.6" + ? "0.6" + : "0.7") as entryPointVersion + }, + owner: privateKeyToAccount(generatePrivateKey()) }) } -export const getLightAccountClient = async ({ +export const getLightAccountClient = async < + entryPointVersion extends "0.6" | "0.7" +>({ entryPoint, - paymasterClient, anvilRpc, - altoRpc, - privateKey -}: AAParamType): Promise< - SmartAccountClient> -> => { - const publicClient = getPublicClient(anvilRpc) - const smartAccount = privateKey - ? await privateKeyToLightSmartAccount(publicClient, { - entryPoint, - lightAccountVersion: "1.1.0", - privateKey - }) - : await signerToLightSmartAccount(publicClient, { - entryPoint, - signer: privateKeyToAccount(generatePrivateKey()), - lightAccountVersion: "1.1.0" - }) - - return createSmartAccountClient({ - chain: foundry, - account: smartAccount, - bundlerTransport: http(altoRpc), - entryPoint: entryPoint, - // eip7677Client: await getEip7677Client({ entryPoint }), - middleware: { - // @ts-ignore - sponsorUserOperation: paymasterClient?.sponsorUserOperation - } + version +}: AAParamType & { + version?: LightAccountVersion +}) => { + return toLightSmartAccount({ + entryPoint: { + address: + entryPoint.version === "0.6" + ? entryPoint06Address + : entryPoint07Address, + version: entryPoint.version === "0.6" ? "0.6" : "0.7" + }, + client: getPublicClient(anvilRpc), + version: version ?? "1.1.0", + owner: privateKeyToAccount(generatePrivateKey()) }) } // Only supports v0.6 for now export const getTrustAccountClient = async < - T extends ENTRYPOINT_ADDRESS_V06_TYPE + entryPointVersion extends "0.6" | "0.7" >({ - entryPoint, - paymasterClient, - altoRpc, - anvilRpc, - privateKey -}: AAParamType): Promise< - SmartAccountClient> -> => { - const publicClient = getPublicClient(anvilRpc) - const smartAccount = privateKey - ? await privateKeyToTrustSmartAccount(publicClient, { - entryPoint, - privateKey - }) - : await signerToTrustSmartAccount(publicClient, { - entryPoint, - signer: privateKeyToAccount(generatePrivateKey()) - }) - - // @ts-ignore - return createSmartAccountClient({ - chain: foundry, - account: smartAccount, - bundlerTransport: http(altoRpc), - middleware: { - // @ts-ignore - sponsorUserOperation: paymasterClient?.sponsorUserOperation + anvilRpc +}: AAParamType) => { + return toTrustSmartAccount({ + client: getPublicClient(anvilRpc), + owner: privateKeyToAccount(generatePrivateKey()), + entryPoint: { + address: entryPoint06Address, + version: "0.6" } }) } // Only supports v0.6 for now -export const getBiconomyClient = async ({ - paymasterClient, - privateKey, - anvilRpc, - altoRpc, - entryPoint = ENTRYPOINT_ADDRESS_V06 -}: AAParamType) => { - const publicClient = getPublicClient(anvilRpc) - const ecdsaSmartAccount = privateKey - ? await privateKeyToBiconomySmartAccount(publicClient, { - entryPoint, - privateKey - }) - : await signerToBiconomySmartAccount(publicClient, { - entryPoint, - signer: privateKeyToAccount(generatePrivateKey()) - }) - - // @ts-ignore - return createSmartAccountClient({ - account: ecdsaSmartAccount, - chain: foundry, - bundlerTransport: http(altoRpc), - middleware: { - // @ts-ignore - sponsorUserOperation: paymasterClient?.sponsorUserOperation +export const getBiconomyClient = async < + entryPointVersion extends "0.6" | "0.7" +>({ + anvilRpc +}: AAParamType) => { + return toBiconomySmartAccount({ + client: getPublicClient(anvilRpc), + owners: [privateKeyToAccount(generatePrivateKey())], + entryPoint: { + address: entryPoint06Address, + version: "0.6" } }) } -export const getKernelEcdsaClient = async ({ +export const getKernelEcdsaClient = async < + entryPointVersion extends "0.6" | "0.7" +>({ entryPoint, - paymasterClient, anvilRpc, - altoRpc, - privateKey = generatePrivateKey(), version -}: AAParamType & { version?: KernelVersion }): Promise< - SmartAccountClient> -> => { +}: AAParamType & { + version?: KernelVersion +}) => { const publicClient = getPublicClient(anvilRpc) if ( (version === "0.3.0-beta" || version === "0.3.1") && - entryPoint === ENTRYPOINT_ADDRESS_V06 + entryPoint.version === "0.6" ) { throw new Error("ERC7579 is not supported for V06") } - const kernelEcdsaAccount = - entryPoint === ENTRYPOINT_ADDRESS_V07 - ? await signerToEcdsaKernelSmartAccount(publicClient, { - entryPoint: entryPoint, - signer: privateKeyToAccount(privateKey), - version - }) - : await signerToEcdsaKernelSmartAccount(publicClient, { - entryPoint, - signer: privateKeyToAccount(privateKey) - }) - // @ts-ignore - return createSmartAccountClient({ - chain: foundry, - account: kernelEcdsaAccount as KernelEcdsaSmartAccount, - bundlerTransport: http(altoRpc), - middleware: { - // @ts-ignore - sponsorUserOperation: paymasterClient?.sponsorUserOperation - } + return toEcdsaKernelSmartAccount({ + client: publicClient, + entryPoint: { + address: + entryPoint.version === "0.6" + ? entryPoint06Address + : entryPoint07Address, + version: entryPoint.version === "0.6" ? "0.6" : "0.7" + }, + owners: [privateKeyToAccount(generatePrivateKey())], + version }) } -export const getSafeClient = async ({ - setupTransactions = [], +export const getSafeClient = async ({ entryPoint, - paymasterClient, anvilRpc, - altoRpc, - privateKey, erc7579 }: { - setupTransactions?: { - to: Address - data: Address - value: bigint - }[] - anvilRpc: string - altoRpc: string - entryPoint: T - paymasterClient?: PimlicoPaymasterClient - privateKey?: Hex erc7579?: boolean -}): Promise>> => { +} & AAParamType) => { const publicClient = getPublicClient(anvilRpc) - const safeSmartAccount = privateKey - ? await privateKeyToSafeSmartAccount(publicClient, { - entryPoint, - privateKey, - safeVersion: "1.4.1", - saltNonce: 420n, - safe4337ModuleAddress: erc7579 - ? "0x3Fdb5BC686e861480ef99A6E3FaAe03c0b9F32e2" - : undefined, - erc7579LaunchpadAddress: erc7579 - ? "0xEBe001b3D534B9B6E2500FB78E67a1A137f561CE" - : undefined - }) - : await signerToSafeSmartAccount(publicClient, { - entryPoint, - signer: privateKeyToAccount(generatePrivateKey()), - safeVersion: "1.4.1", - saltNonce: 420n, - safe4337ModuleAddress: erc7579 - ? "0x3Fdb5BC686e861480ef99A6E3FaAe03c0b9F32e2" - : undefined, - erc7579LaunchpadAddress: erc7579 - ? "0xEBe001b3D534B9B6E2500FB78E67a1A137f561CE" - : undefined - }) - - const pimlicoBundlerClient = getPimlicoBundlerClient({ - entryPoint, - altoRpc - }) - - // @ts-ignore - return createSmartAccountClient({ - chain: foundry, - account: safeSmartAccount, - bundlerTransport: http(altoRpc), - middleware: { - gasPrice: async () => - (await pimlicoBundlerClient.getUserOperationGasPrice()).fast, - // @ts-ignore - sponsorUserOperation: paymasterClient?.sponsorUserOperation - } + return toSafeSmartAccount({ + client: publicClient, + entryPoint: { + address: + entryPoint.version === "0.6" + ? entryPoint06Address + : entryPoint07Address, + version: entryPoint.version === "0.6" ? "0.6" : "0.7" + }, + owners: [privateKeyToAccount(generatePrivateKey())], + version: "1.4.1", + saltNonce: 420n, + safe4337ModuleAddress: erc7579 + ? "0x3Fdb5BC686e861480ef99A6E3FaAe03c0b9F32e2" + : undefined, + erc7579LaunchpadAddress: erc7579 + ? "0xEBe001b3D534B9B6E2500FB78E67a1A137f561CE" + : undefined }) } -export const getEip7677Client = async ({ - entryPoint -}: { entryPoint: TEntryPoint }) => { - const client = createClient({ - chain: foundry, - transport: http(PAYMASTER_RPC) - }).extend(paymasterActionsEip7677(entryPoint)) - - return client -} - export const getCoreSmartAccounts = () => [ { name: "Trust", - getSmartAccountClient: async ( - conf: AAParamType + getSmartAccountClient: async ( + conf: AAParamType ) => { - if (conf.entryPoint !== ENTRYPOINT_ADDRESS_V06) { - throw new Error("Biconomy only works with V06") - } - return getTrustAccountClient( - conf as AAParamType - ) + return getBundlerClient({ + account: await getTrustAccountClient(conf), + ...conf + }) }, supportsEntryPointV06: true, supportsEntryPointV07: false, isEip1271Compliant: true }, { - name: "LightAccount v1.1.0", - getSmartAccountClient: async ( - conf: AAParamType - ) => getLightAccountClient(conf), + name: "LightAccount 1.1.0", + getSmartAccountClient: async ( + conf: AAParamType + ) => + getBundlerClient({ + account: await getLightAccountClient({ + ...conf, + version: "1.1.0" as LightAccountVersion + }), + ...conf + }), supportsEntryPointV06: true, supportsEntryPointV07: false, isEip1271Compliant: true }, + { + name: "LightAccount 2.0.0", + getSmartAccountClient: async ( + conf: AAParamType + ) => + getBundlerClient({ + account: await getLightAccountClient({ + ...conf, + version: "2.0.0" as LightAccountVersion + }), + ...conf + }), + supportsEntryPointV06: false, + supportsEntryPointV07: true, + isEip1271Compliant: true + }, { name: "Simple", - getSmartAccountClient: async ( - conf: AAParamType - ) => getSimpleAccountClient(conf), + getSmartAccountClient: async ( + conf: AAParamType + ) => + getBundlerClient({ + account: await getSimpleAccountClient(conf), + ...conf + }), supportsEntryPointV06: true, supportsEntryPointV07: true, isEip1271Compliant: false }, { name: "Kernel", - getSmartAccountClient: async ( - conf: AAParamType - ) => getKernelEcdsaClient(conf), + getSmartAccountClient: async ( + conf: AAParamType + ) => + getBundlerClient({ + account: await getKernelEcdsaClient(conf), + ...conf + }), supportsEntryPointV06: true, supportsEntryPointV07: true, isEip1271Compliant: true }, { name: "Kernel 7579 0.3.0-beta", - getSmartAccountClient: async ( - conf: AAParamType + getSmartAccountClient: async ( + conf: AAParamType ) => - getKernelEcdsaClient({ - ...conf, - version: "0.3.0-beta" as KernelVersion + getBundlerClient({ + account: await getKernelEcdsaClient({ + ...conf, + version: "0.3.0-beta" as KernelVersion + }), + ...conf }), - getErc7579SmartAccountClient: async ( - conf: AAParamType + getErc7579SmartAccountClient: async < + entryPointVersion extends "0.6" | "0.7" + >( + conf: AAParamType ) => - getKernelEcdsaClient({ - ...conf, - version: "0.3.0-beta" as KernelVersion + getSmartAccountClient({ + account: await getKernelEcdsaClient({ + ...conf, + version: "0.3.0-beta" as KernelVersion + }), + ...conf }), supportsEntryPointV06: false, supportsEntryPointV07: true, @@ -507,19 +487,27 @@ export const getCoreSmartAccounts = () => [ }, { name: "Kernel 7579 0.3.1", - getSmartAccountClient: async ( - conf: AAParamType + getSmartAccountClient: async ( + conf: AAParamType ) => - getKernelEcdsaClient({ - ...conf, - version: "0.3.1" as KernelVersion + getBundlerClient({ + account: await getKernelEcdsaClient({ + ...conf, + version: "0.3.1" as KernelVersion + }), + ...conf }), - getErc7579SmartAccountClient: async ( - conf: AAParamType + getErc7579SmartAccountClient: async < + entryPointVersion extends "0.6" | "0.7" + >( + conf: AAParamType ) => - getKernelEcdsaClient({ - ...conf, - version: "0.3.1" as KernelVersion + getSmartAccountClient({ + account: await getKernelEcdsaClient({ + ...conf, + version: "0.3.1" as KernelVersion + }), + ...conf }), supportsEntryPointV06: false, supportsEntryPointV07: true, @@ -527,110 +515,49 @@ export const getCoreSmartAccounts = () => [ }, { name: "Biconomy", - getSmartAccountClient: async ( - conf: AAParamType - ) => { - if (conf.entryPoint !== ENTRYPOINT_ADDRESS_V06) { - throw new Error("Biconomy only works with V06") - } - return getBiconomyClient( - conf as AAParamType - ) - }, + getSmartAccountClient: async ( + conf: AAParamType + ) => + getBundlerClient({ + account: await getBiconomyClient(conf), + ...conf + }), supportsEntryPointV06: true, supportsEntryPointV07: false, isEip1271Compliant: true }, { name: "Safe", - getSmartAccountClient: async ( - conf: AAParamType - ) => getSafeClient(conf), + getSmartAccountClient: async ( + conf: AAParamType + ) => + getBundlerClient({ + account: await getSafeClient(conf), + ...conf + }), supportsEntryPointV06: true, supportsEntryPointV07: true, isEip1271Compliant: true }, { name: "Safe 7579", - getSmartAccountClient: async ( - conf: AAParamType - ) => getSafeClient({ ...conf, erc7579: true }), - getErc7579SmartAccountClient: async ( - conf: AAParamType - ) => getSafeClient({ ...conf, erc7579: true }), - supportsEntryPointV06: false, - supportsEntryPointV07: true, - isEip1271Compliant: true - }, - - // ---------------------------- Account from private key ------------------------------------------------- - - { - name: "Trust private key", - getSmartAccountClient: async ( - conf: AAParamType - ) => { - if (conf.entryPoint !== ENTRYPOINT_ADDRESS_V06) { - throw new Error("Biconomy only works with V06") - } - return getTrustAccountClient({ - ...(conf as AAParamType), - privateKey: generatePrivateKey() - }) - }, - supportsEntryPointV06: true, - supportsEntryPointV07: false, - isEip1271Compliant: true - }, - { - name: "LightAccount v1.1.0 private key", - getSmartAccountClient: async ( - conf: AAParamType + getSmartAccountClient: async ( + conf: AAParamType ) => - getLightAccountClient({ - ...conf, - privateKey: generatePrivateKey() + getBundlerClient({ + account: await getSafeClient({ ...conf, erc7579: true }), + ...conf }), - supportsEntryPointV06: true, - supportsEntryPointV07: false, - isEip1271Compliant: true - }, - { - name: "Simple private key", - getSmartAccountClient: async ( - conf: AAParamType + getErc7579SmartAccountClient: async < + entryPointVersion extends "0.6" | "0.7" + >( + conf: AAParamType ) => - getSimpleAccountClient({ - ...conf, - privateKey: generatePrivateKey() + getSmartAccountClient({ + account: await getSafeClient({ ...conf, erc7579: true }), + ...conf }), - supportsEntryPointV06: true, - supportsEntryPointV07: true, - isEip1271Compliant: false - }, - { - name: "Biconomy private key", - getSmartAccountClient: async ( - conf: AAParamType - ) => { - if (conf.entryPoint !== ENTRYPOINT_ADDRESS_V06) { - throw new Error("Biconomy only works with V06") - } - return getBiconomyClient({ - ...(conf as AAParamType), - privateKey: generatePrivateKey() - }) - }, - supportsEntryPointV06: true, - supportsEntryPointV07: false, - isEip1271Compliant: true - }, - { - name: "Safe private key private key", - getSmartAccountClient: async ( - conf: AAParamType - ) => getSafeClient({ ...conf, privateKey: generatePrivateKey() }), - supportsEntryPointV06: true, + supportsEntryPointV06: false, supportsEntryPointV07: true, isEip1271Compliant: true } diff --git a/packages/permissionless/accounts/biconomy/abi/BiconomySmartAccountAbi.ts b/packages/permissionless/accounts/biconomy/abi/BiconomySmartAccountAbi.ts index db6ee1ac..ee697284 100644 --- a/packages/permissionless/accounts/biconomy/abi/BiconomySmartAccountAbi.ts +++ b/packages/permissionless/accounts/biconomy/abi/BiconomySmartAccountAbi.ts @@ -1,59 +1,36 @@ -/** - * The exeuctor abi, used to execute transactions on the Biconomy Modular Smart Account - */ -export const BiconomyExecuteAbi = [ +export const FactoryAbi = [ { inputs: [ { internalType: "address", - name: "dest", + name: "moduleSetupContract", type: "address" }, - { - internalType: "uint256", - name: "value", - type: "uint256" - }, { internalType: "bytes", - name: "func", + name: "moduleSetupData", type: "bytes" - } - ], - name: "execute_ncC", - outputs: [], - stateMutability: "nonpayable", - type: "function" - }, - { - inputs: [ - { - internalType: "address[]", - name: "dest", - type: "address[]" }, { - internalType: "uint256[]", - name: "value", - type: "uint256[]" - }, + internalType: "uint256", + name: "index", + type: "uint256" + } + ], + name: "deployCounterFactualAccount", + outputs: [ { - internalType: "bytes[]", - name: "func", - type: "bytes[]" + internalType: "address", + name: "proxy", + type: "address" } ], - name: "executeBatch_y6U", - outputs: [], stateMutability: "nonpayable", type: "function" } ] as const -/** - * The init abi, used to initialise Biconomy Modular Smart Account / setup default ECDSA module - */ -export const BiconomyInitAbi = [ +export const BiconomyAbi = [ { inputs: [ { @@ -101,5 +78,51 @@ export const BiconomyInitAbi = [ ], stateMutability: "nonpayable", type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "dest", + type: "address" + }, + { + internalType: "uint256", + name: "value", + type: "uint256" + }, + { + internalType: "bytes", + name: "func", + type: "bytes" + } + ], + name: "execute_ncC", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "address[]", + name: "dest", + type: "address[]" + }, + { + internalType: "uint256[]", + name: "value", + type: "uint256[]" + }, + { + internalType: "bytes[]", + name: "func", + type: "bytes[]" + } + ], + name: "executeBatch_y6U", + outputs: [], + stateMutability: "nonpayable", + type: "function" } -] +] as const diff --git a/packages/permissionless/accounts/biconomy/privateKeyToBiconomySmartAccount.ts b/packages/permissionless/accounts/biconomy/privateKeyToBiconomySmartAccount.ts deleted file mode 100644 index 39f36efb..00000000 --- a/packages/permissionless/accounts/biconomy/privateKeyToBiconomySmartAccount.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { - Chain, - Client, - Hex, - PublicActions, - PublicRpcSchema, - Transport -} from "viem" -import { privateKeyToAccount } from "viem/accounts" -import type { ENTRYPOINT_ADDRESS_V06_TYPE, Prettify } from "../../types" -import { - type BiconomySmartAccount, - type SignerToBiconomySmartAccountParameters, - signerToBiconomySmartAccount -} from "./signerToBiconomySmartAccount" - -export type PrivateKeyToBiconomySmartAccountParameters< - entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE -> = Prettify< - { - privateKey: Hex - } & Omit, "signer"> -> - -/** - * @description Creates a Biconomy Smart Account from a private key. - * - * @returns A Private Key Biconomy Smart Account using ECDSA as default validation module. - */ -export async function privateKeyToBiconomySmartAccount< - entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined ->( - client: Client< - TTransport, - TChain, - undefined, - PublicRpcSchema, - PublicActions - >, - { - privateKey, - ...rest - }: PrivateKeyToBiconomySmartAccountParameters -): Promise> { - const privateKeyAccount = privateKeyToAccount(privateKey) - return signerToBiconomySmartAccount< - entryPoint, - TTransport, - TChain, - "privateKey" - >(client, { - signer: privateKeyAccount, - ...rest - }) -} diff --git a/packages/permissionless/accounts/biconomy/signerToBiconomySmartAccount.ts b/packages/permissionless/accounts/biconomy/signerToBiconomySmartAccount.ts deleted file mode 100644 index edc52b4e..00000000 --- a/packages/permissionless/accounts/biconomy/signerToBiconomySmartAccount.ts +++ /dev/null @@ -1,454 +0,0 @@ -import type { PublicActions, PublicRpcSchema, TypedData } from "viem" -import { - type Address, - type Chain, - type Client, - type Hex, - type LocalAccount, - type Transport, - type TypedDataDefinition, - concatHex, - encodeAbiParameters, - encodeFunctionData, - encodePacked, - getContractAddress, - hexToBigInt, - keccak256, - parseAbiParameters -} from "viem" -import { getChainId, signMessage, signTypedData } from "viem/actions" -import { getAccountNonce } from "../../actions/public/getAccountNonce" -import type { Prettify } from "../../types" -import type { ENTRYPOINT_ADDRESS_V06_TYPE } from "../../types/entrypoint" -import { getEntryPointVersion } from "../../utils" -import { getUserOperationHash } from "../../utils/getUserOperationHash" -import { isSmartAccountDeployed } from "../../utils/isSmartAccountDeployed" -import { toSmartAccount } from "../toSmartAccount" -import { - SignTransactionNotSupportedBySmartAccount, - type SmartAccount, - type SmartAccountSigner -} from "../types" -import { - BiconomyExecuteAbi, - BiconomyInitAbi -} from "./abi/BiconomySmartAccountAbi" - -export type BiconomySmartAccount< - entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE, - transport extends Transport = Transport, - chain extends Chain | undefined = Chain | undefined -> = SmartAccount - -/** - * The account creation ABI for Biconomy Smart Account (from the biconomy SmartAccountFactory) - */ - -const createAccountAbi = [ - { - inputs: [ - { - internalType: "address", - name: "moduleSetupContract", - type: "address" - }, - { - internalType: "bytes", - name: "moduleSetupData", - type: "bytes" - }, - { - internalType: "uint256", - name: "index", - type: "uint256" - } - ], - name: "deployCounterFactualAccount", - outputs: [ - { - internalType: "address", - name: "proxy", - type: "address" - } - ], - stateMutability: "nonpayable", - type: "function" - } -] as const - -/** - * Default addresses for Biconomy Smart Account - */ -const BICONOMY_ADDRESSES: { - ECDSA_OWNERSHIP_REGISTRY_MODULE: Address - ACCOUNT_V2_0_LOGIC: Address - FACTORY_ADDRESS: Address - DEFAULT_FALLBACK_HANDLER_ADDRESS: Address -} = { - ECDSA_OWNERSHIP_REGISTRY_MODULE: - "0x0000001c5b32F37F5beA87BDD5374eB2aC54eA8e", - ACCOUNT_V2_0_LOGIC: "0x0000002512019Dafb59528B82CB92D3c5D2423aC", - FACTORY_ADDRESS: "0x000000a56Aaca3e9a4C479ea6b6CD0DbcB6634F5", - DEFAULT_FALLBACK_HANDLER_ADDRESS: - "0x0bBa6d96BD616BedC6BFaa341742FD43c60b83C1" -} - -const BICONOMY_PROXY_CREATION_CODE = - "0x6080346100aa57601f61012038819003918201601f19168301916001600160401b038311848410176100af578084926020946040528339810103126100aa57516001600160a01b0381168082036100aa5715610065573055604051605a90816100c68239f35b60405162461bcd60e51b815260206004820152601e60248201527f496e76616c696420696d706c656d656e746174696f6e206164647265737300006044820152606490fd5b600080fd5b634e487b7160e01b600052604160045260246000fdfe608060405230546000808092368280378136915af43d82803e156020573d90f35b3d90fdfea2646970667358221220a03b18dce0be0b4c9afe58a9eb85c35205e2cf087da098bbf1d23945bf89496064736f6c63430008110033" - -/** - * Get the account initialization code for Biconomy smart account with ECDSA as default authorization module - * @param owner - * @param index - * @param factoryAddress - * @param ecdsaValidatorAddress - */ -const getAccountInitCode = async ({ - owner, - index, - ecdsaModuleAddress -}: { - owner: Address - index: bigint - ecdsaModuleAddress: Address -}): Promise => { - if (!owner) throw new Error("Owner account not found") - - // Build the module setup data - const ecdsaOwnershipInitData = encodeFunctionData({ - abi: BiconomyInitAbi, - functionName: "initForSmartAccount", - args: [owner] - }) - - // Build the account init code - return encodeFunctionData({ - abi: createAccountAbi, - functionName: "deployCounterFactualAccount", - args: [ecdsaModuleAddress, ecdsaOwnershipInitData, index] - }) -} - -const getAccountAddress = async ({ - factoryAddress, - accountLogicAddress, - fallbackHandlerAddress, - ecdsaModuleAddress, - owner, - index = BigInt(0) -}: { - factoryAddress: Address - accountLogicAddress: Address - fallbackHandlerAddress: Address - ecdsaModuleAddress: Address - owner: Address - index?: bigint -}): Promise
=> { - // Build the module setup data - const ecdsaOwnershipInitData = encodeFunctionData({ - abi: BiconomyInitAbi, - functionName: "initForSmartAccount", - args: [owner] - }) - - // Build account init code - const initialisationData = encodeFunctionData({ - abi: BiconomyInitAbi, - functionName: "init", - args: [ - fallbackHandlerAddress, - ecdsaModuleAddress, - ecdsaOwnershipInitData - ] - }) - - const deploymentCode = encodePacked( - ["bytes", "uint256"], - [BICONOMY_PROXY_CREATION_CODE, hexToBigInt(accountLogicAddress)] - ) - - const salt = keccak256( - encodePacked( - ["bytes32", "uint256"], - [keccak256(encodePacked(["bytes"], [initialisationData])), index] - ) - ) - - return getContractAddress({ - from: factoryAddress, - salt, - bytecode: deploymentCode, - opcode: "CREATE2" - }) -} - -export type SignerToBiconomySmartAccountParameters< - entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE, - TSource extends string = string, - TAddress extends Address = Address -> = Prettify<{ - nonceKey?: bigint - signer: SmartAccountSigner - entryPoint: entryPoint - address?: Address - index?: bigint - factoryAddress?: Address - accountLogicAddress?: Address - fallbackHandlerAddress?: Address - ecdsaModuleAddress?: Address -}> - -/** - * Build a Biconomy modular smart account from a private key, that use the ECDSA signer behind the scene - * @param client - * @param privateKey - * @param entryPoint - * @param index - * @param factoryAddress - * @param accountLogicAddress - * @param ecdsaModuleAddress - */ -export async function signerToBiconomySmartAccount< - entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSource extends string = string, - TAddress extends Address = Address ->( - client: Client< - TTransport, - TChain, - undefined, - PublicRpcSchema, - PublicActions - >, - { - signer, - address, - entryPoint: entryPointAddress, - index = BigInt(0), - factoryAddress = BICONOMY_ADDRESSES.FACTORY_ADDRESS, - accountLogicAddress = BICONOMY_ADDRESSES.ACCOUNT_V2_0_LOGIC, - fallbackHandlerAddress = BICONOMY_ADDRESSES.DEFAULT_FALLBACK_HANDLER_ADDRESS, - ecdsaModuleAddress = BICONOMY_ADDRESSES.ECDSA_OWNERSHIP_REGISTRY_MODULE, - nonceKey - }: SignerToBiconomySmartAccountParameters -): Promise> { - const entryPointVersion = getEntryPointVersion(entryPointAddress) - - if (entryPointVersion !== "v0.6") { - throw new Error("Only EntryPoint 0.6 is supported") - } - - // Get the private key related account - const viemSigner: LocalAccount = { - ...signer, - signTransaction: (_, __) => { - throw new SignTransactionNotSupportedBySmartAccount() - } - } as LocalAccount - - // Helper to generate the init code for the smart account - const generateInitCode = () => - getAccountInitCode({ - owner: viemSigner.address, - index, - ecdsaModuleAddress - }) - - // Fetch account address and chain id - const [accountAddress, chainId] = await Promise.all([ - address ?? - getAccountAddress({ - owner: viemSigner.address, - ecdsaModuleAddress, - factoryAddress, - accountLogicAddress, - fallbackHandlerAddress, - index - }), - client.chain?.id ?? getChainId(client) - ]) - - if (!accountAddress) throw new Error("Account address not found") - - let smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) - - return toSmartAccount({ - address: accountAddress, - async signMessage({ message }) { - let signature: Hex = await signMessage(client, { - account: viemSigner, - message - }) - const potentiallyIncorrectV = Number.parseInt( - signature.slice(-2), - 16 - ) - if (![27, 28].includes(potentiallyIncorrectV)) { - const correctV = potentiallyIncorrectV + 27 - signature = (signature.slice(0, -2) + - correctV.toString(16)) as Hex - } - return encodeAbiParameters( - [{ type: "bytes" }, { type: "address" }], - [signature, ecdsaModuleAddress] - ) - }, - async signTransaction(_, __) { - throw new SignTransactionNotSupportedBySmartAccount() - }, - async signTypedData< - const TTypedData extends TypedData | Record, - TPrimaryType extends - | keyof TTypedData - | "EIP712Domain" = keyof TTypedData - >(typedData: TypedDataDefinition) { - let signature: Hex = await signTypedData< - TTypedData, - TPrimaryType, - TChain, - undefined - >(client, { - account: viemSigner, - ...typedData - }) - const potentiallyIncorrectV = Number.parseInt( - signature.slice(-2), - 16 - ) - if (![27, 28].includes(potentiallyIncorrectV)) { - const correctV = potentiallyIncorrectV + 27 - signature = (signature.slice(0, -2) + - correctV.toString(16)) as Hex - } - return encodeAbiParameters( - [{ type: "bytes" }, { type: "address" }], - [signature, ecdsaModuleAddress] - ) - }, - client: client, - publicKey: accountAddress, - entryPoint: entryPointAddress, - source: "biconomySmartAccount", - - // Get the nonce of the smart account - async getNonce(key?: bigint) { - return getAccountNonce(client, { - sender: accountAddress, - entryPoint: entryPointAddress, - key: key ?? nonceKey - }) - }, - - // Sign a user operation - async signUserOperation(userOperation) { - const hash = getUserOperationHash({ - userOperation: { - ...userOperation, - signature: "0x" - }, - entryPoint: entryPointAddress, - chainId: chainId - }) - const signature = await signMessage(client, { - account: viemSigner, - message: { raw: hash } - }) - // userOp signature is encoded module signature + module address - const signatureWithModuleAddress = encodeAbiParameters( - parseAbiParameters("bytes, address"), - [signature, ecdsaModuleAddress] - ) - return signatureWithModuleAddress - }, - - async getFactory() { - if (smartAccountDeployed) return undefined - - smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) - - if (smartAccountDeployed) return undefined - - return factoryAddress - }, - - async getFactoryData() { - if (smartAccountDeployed) return undefined - - smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) - - if (smartAccountDeployed) return undefined - return generateInitCode() - }, - - // Encode the init code - async getInitCode() { - if (smartAccountDeployed) return "0x" - - smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) - - if (smartAccountDeployed) return "0x" - - return concatHex([factoryAddress, await generateInitCode()]) - }, - - // Encode the deploy call data - async encodeDeployCallData(_) { - throw new Error("Doesn't support account deployment") - }, - - // Encode a call - async encodeCallData(args) { - if (Array.isArray(args)) { - // Encode a batched call - const argsArray = args as { - to: Address - value: bigint - data: Hex - }[] - - return encodeFunctionData({ - abi: BiconomyExecuteAbi, - functionName: "executeBatch_y6U", - args: [ - argsArray.map((a) => a.to), - argsArray.map((a) => a.value), - argsArray.map((a) => a.data) - ] - }) - } - const { to, value, data } = args as { - to: Address - value: bigint - data: Hex - } - // Encode a simple call - return encodeFunctionData({ - abi: BiconomyExecuteAbi, - functionName: "execute_ncC", - args: [to, value, data] - }) - }, - - // Get simple dummy signature for ECDSA module authorization - async getDummySignature(_userOperation) { - const moduleAddress = - BICONOMY_ADDRESSES.ECDSA_OWNERSHIP_REGISTRY_MODULE - const dynamicPart = moduleAddress.substring(2).padEnd(40, "0") - return `0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000${dynamicPart}000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000` - } - }) -} diff --git a/packages/permissionless/accounts/biconomy/toBiconomySmartAccount.ts b/packages/permissionless/accounts/biconomy/toBiconomySmartAccount.ts new file mode 100644 index 00000000..3ddf7903 --- /dev/null +++ b/packages/permissionless/accounts/biconomy/toBiconomySmartAccount.ts @@ -0,0 +1,274 @@ +import type { + Account, + Assign, + Chain, + EIP1193Provider, + OneOf, + Prettify, + Transport, + WalletClient +} from "viem" +import { + type Address, + type Client, + type Hex, + type LocalAccount, + encodeAbiParameters, + encodeFunctionData, + parseAbiParameters +} from "viem" +import { + type SmartAccount, + type SmartAccountImplementation, + entryPoint06Abi, + entryPoint06Address, + getUserOperationHash, + toSmartAccount +} from "viem/account-abstraction" +import { signMessage } from "viem/actions" +import { getAccountNonce } from "../../actions/public/getAccountNonce" +import { getSenderAddress } from "../../actions/public/getSenderAddress" +import { toOwner } from "../../utils/toOwner" +import { BiconomyAbi, FactoryAbi } from "./abi/BiconomySmartAccountAbi" + +/** + * The account creation ABI for Biconomy Smart Account (from the biconomy SmartAccountFactory) + */ + +/** + * Default addresses for Biconomy Smart Account + */ +const BICONOMY_ADDRESSES: { + ECDSA_OWNERSHIP_REGISTRY_MODULE: Address + FACTORY_ADDRESS: Address +} = { + ECDSA_OWNERSHIP_REGISTRY_MODULE: + "0x0000001c5b32F37F5beA87BDD5374eB2aC54eA8e", + FACTORY_ADDRESS: "0x000000a56Aaca3e9a4C479ea6b6CD0DbcB6634F5" +} + +/** + * Get the account initialization code for Biconomy smart account with ECDSA as default authorization module + * @param owner + * @param index + * @param factoryAddress + * @param ecdsaValidatorAddress + */ +const getAccountInitCode = async ({ + owner, + index, + ecdsaModuleAddress +}: { + owner: Address + index: bigint + ecdsaModuleAddress: Address +}): Promise => { + if (!owner) throw new Error("Owner account not found") + + // Build the module setup data + const ecdsaOwnershipInitData = encodeFunctionData({ + abi: BiconomyAbi, + functionName: "initForSmartAccount", + args: [owner] + }) + + // Build the account init code + return encodeFunctionData({ + abi: FactoryAbi, + functionName: "deployCounterFactualAccount", + args: [ecdsaModuleAddress, ecdsaOwnershipInitData, index] + }) +} + +export type ToBiconomySmartAccountParameters = Prettify<{ + client: Client + owners: [ + OneOf< + | EIP1193Provider + | WalletClient + | LocalAccount + > + ] + address?: Address | undefined + entryPoint: { + address: Address + version: "0.6" + } + nonceKey?: bigint + index?: bigint + factoryAddress?: Address + ecdsaModuleAddress?: Address +}> + +export type BiconomySmartAccountImplementation = Assign< + SmartAccountImplementation, + { sign: NonNullable } +> + +export type ToBiconomySmartAccountReturnType = Prettify< + SmartAccount +> + +/** + * Build a Biconomy modular smart account from a private key, that use the ECDSA signer behind the scene + * @param client + * @param privateKey + * @param entryPoint + * @param index + * @param factoryAddress + * @param ecdsaModuleAddress + */ + +export async function toBiconomySmartAccount( + parameters: ToBiconomySmartAccountParameters +): Promise { + const { owners, client, index = 0n, address } = parameters + + const localOwner = await toOwner({ owner: owners[0] }) + + const entryPoint = { + address: parameters.entryPoint?.address ?? entryPoint06Address, + abi: entryPoint06Abi, + version: parameters.entryPoint?.version ?? "0.6" + } + + const factoryAddress = + parameters.factoryAddress ?? BICONOMY_ADDRESSES.FACTORY_ADDRESS + + let accountAddress: Address | undefined = address + + const ecdsaModuleAddress = + parameters.ecdsaModuleAddress ?? + BICONOMY_ADDRESSES.ECDSA_OWNERSHIP_REGISTRY_MODULE + + const getFactoryArgs = async () => { + return { + factory: factoryAddress, + factoryData: await getAccountInitCode({ + owner: localOwner.address, + index, + ecdsaModuleAddress + }) + } + } + + return toSmartAccount({ + client, + entryPoint, + getFactoryArgs, + async getAddress() { + if (accountAddress) return accountAddress + + const { factory, factoryData } = await getFactoryArgs() + + // Get the sender address based on the init code + accountAddress = await getSenderAddress(client, { + factory, + factoryData, + entryPointAddress: entryPoint.address + }) + + return accountAddress + }, + async getNonce(args) { + const address = await this.getAddress() + return getAccountNonce(client, { + address, + entryPointAddress: entryPoint.address, + key: args?.key ?? parameters?.nonceKey + }) + }, + encodeCalls: async (calls) => { + if (calls.length > 1) { + // Encode a batched call + return encodeFunctionData({ + abi: BiconomyAbi, + functionName: "executeBatch_y6U", + args: [ + calls.map((a) => a.to), + calls.map((a) => a.value ?? 0n), + calls.map((a) => a.data ?? "0x") + ] + }) + } + const { to, value, data } = calls[0] + // Encode a simple call + return encodeFunctionData({ + abi: BiconomyAbi, + functionName: "execute_ncC", + args: [to, value ?? 0n, data ?? "0x"] + }) + }, + // Get simple dummy signature for ECDSA module authorization + async getStubSignature() { + const dynamicPart = ecdsaModuleAddress.substring(2).padEnd(40, "0") + return `0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000${dynamicPart}000000000000000000000000000000000000000000000000000000000000004181d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b00000000000000000000000000000000000000000000000000000000000000` + }, + async sign({ hash }) { + return this.signMessage({ message: hash }) + }, + async signMessage({ message }) { + let signature = await localOwner.signMessage({ + message + }) + + const potentiallyIncorrectV = Number.parseInt( + signature.slice(-2), + 16 + ) + if (![27, 28].includes(potentiallyIncorrectV)) { + const correctV = potentiallyIncorrectV + 27 + signature = (signature.slice(0, -2) + + correctV.toString(16)) as Hex + } + return encodeAbiParameters( + [{ type: "bytes" }, { type: "address" }], + [signature, ecdsaModuleAddress] + ) + }, + async signTypedData(typedData) { + let signature = await localOwner.signTypedData(typedData) + + const potentiallyIncorrectV = Number.parseInt( + signature.slice(-2), + 16 + ) + if (![27, 28].includes(potentiallyIncorrectV)) { + const correctV = potentiallyIncorrectV + 27 + signature = (signature.slice(0, -2) + + correctV.toString(16)) as Hex + } + return encodeAbiParameters( + [{ type: "bytes" }, { type: "address" }], + [signature, ecdsaModuleAddress] + ) + }, + // Sign a user operation + async signUserOperation(parameters) { + const { chainId = client.chain?.id, ...userOperation } = parameters + + if (!chainId) throw new Error("Chain id not found") + + const hash = getUserOperationHash({ + userOperation: { + ...userOperation, + sender: userOperation.sender ?? (await this.getAddress()), + signature: "0x" + }, + entryPointAddress: entryPoint.address, + entryPointVersion: entryPoint.version, + chainId: chainId + }) + const signature = await signMessage(client, { + account: localOwner, + message: { raw: hash } + }) + // userOp signature is encoded module signature + module address + const signatureWithModuleAddress = encodeAbiParameters( + parseAbiParameters("bytes, address"), + [signature, ecdsaModuleAddress] + ) + return signatureWithModuleAddress + } + }) +} diff --git a/packages/permissionless/accounts/index.ts b/packages/permissionless/accounts/index.ts index d28cb06b..e70cd175 100644 --- a/packages/permissionless/accounts/index.ts +++ b/packages/permissionless/accounts/index.ts @@ -1,105 +1,74 @@ import { - type PrivateKeyToSimpleSmartAccountParameters, - privateKeyToSimpleSmartAccount -} from "./simple/privateKeyToSimpleSmartAccount" + type SimpleSmartAccountImplementation, + type ToSimpleSmartAccountParameters, + type ToSimpleSmartAccountReturnType, + toSimpleSmartAccount +} from "./simple/toSimpleSmartAccount" import { - type SignerToSimpleSmartAccountParameters, - type SimpleSmartAccount, - signerToSimpleSmartAccount -} from "./simple/signerToSimpleSmartAccount" + type LightAccountVersion, + type LightSmartAccountImplementation, + type ToLightSmartAccountParameters, + type ToLightSmartAccountReturnType, + toLightSmartAccount +} from "./light/toLightSmartAccount" import { - type PrivateKeyToLightSmartAccountParameters, - privateKeyToLightSmartAccount -} from "./light/privateKeyToLightSmartAccount" + type ToTrustSmartAccountParameters, + type ToTrustSmartAccountReturnType, + type TrustSmartAccountImplementation, + toTrustSmartAccount +} from "./trust/toTrustSmartAccount" import { - type LightSmartAccount, - type SignerToLightSmartAccountParameters, - signerToLightSmartAccount -} from "./light/signerToLightSmartAccount" - -import { - type SignerToTrustSmartAccountParameters, - type TrustSmartAccount, - signerToTrustSmartAccount -} from "./trust/signerToTrustSmartAccount" - -import { - type PrivateKeyToTrustSmartAccountParameters, - privateKeyToTrustSmartAccount -} from "./trust/privateKeyToTrustSmartAccount" - -import { - type PrivateKeyToSafeSmartAccountParameters, - privateKeyToSafeSmartAccount -} from "./safe/privateKeyToSafeSmartAccount" - -import { - type SafeSmartAccount, + type SafeSmartAccountImplementation, type SafeVersion, - type SignerToSafeSmartAccountParameters, - signerToSafeSmartAccount -} from "./safe/signerToSafeSmartAccount" - -import { - type KernelEcdsaSmartAccount, - type SignerToEcdsaKernelSmartAccountParameters, - signerToEcdsaKernelSmartAccount -} from "./kernel/signerToEcdsaKernelSmartAccount" + type ToSafeSmartAccountParameters, + type ToSafeSmartAccountReturnType, + toSafeSmartAccount +} from "./safe/toSafeSmartAccount" import { - type BiconomySmartAccount, - type SignerToBiconomySmartAccountParameters, - signerToBiconomySmartAccount -} from "./biconomy/signerToBiconomySmartAccount" + type EcdsaKernelSmartAccountImplementation, + type KernelVersion, + type ToEcdsaKernelSmartAccountParameters, + type ToEcdsaKernelSmartAccountReturnType, + toEcdsaKernelSmartAccount +} from "./kernel/toEcdsaKernelSmartAccount" import { - type PrivateKeyToBiconomySmartAccountParameters, - privateKeyToBiconomySmartAccount -} from "./biconomy/privateKeyToBiconomySmartAccount" - -import { - SignTransactionNotSupportedBySmartAccount, - type SmartAccount, - type SmartAccountSigner -} from "./types" - -import { toSmartAccount } from "./toSmartAccount" + type BiconomySmartAccountImplementation, + type ToBiconomySmartAccountParameters, + type ToBiconomySmartAccountReturnType, + toBiconomySmartAccount +} from "./biconomy/toBiconomySmartAccount" export { + type ToSimpleSmartAccountParameters, + type SimpleSmartAccountImplementation, + type ToSimpleSmartAccountReturnType, + toSimpleSmartAccount, + type LightAccountVersion, + type ToLightSmartAccountParameters, + type LightSmartAccountImplementation, + type ToLightSmartAccountReturnType, + toLightSmartAccount, + type ToTrustSmartAccountParameters, + type TrustSmartAccountImplementation, + type ToTrustSmartAccountReturnType, + toTrustSmartAccount, + type ToSafeSmartAccountParameters, + type SafeSmartAccountImplementation, + type ToSafeSmartAccountReturnType, + toSafeSmartAccount, + type ToEcdsaKernelSmartAccountParameters, + type EcdsaKernelSmartAccountImplementation, + type ToEcdsaKernelSmartAccountReturnType, type SafeVersion, - type SmartAccountSigner, - type SafeSmartAccount, - signerToSafeSmartAccount, - type SimpleSmartAccount, - signerToSimpleSmartAccount, - type LightSmartAccount, - signerToLightSmartAccount, - type TrustSmartAccount, - signerToTrustSmartAccount, - privateKeyToTrustSmartAccount, - SignTransactionNotSupportedBySmartAccount, - privateKeyToBiconomySmartAccount, - privateKeyToSimpleSmartAccount, - privateKeyToLightSmartAccount, - type SmartAccount, - privateKeyToSafeSmartAccount, - type KernelEcdsaSmartAccount, - signerToEcdsaKernelSmartAccount, - type BiconomySmartAccount, - signerToBiconomySmartAccount, - toSmartAccount, - type SignerToSimpleSmartAccountParameters, - type SignerToLightSmartAccountParameters, - type SignerToSafeSmartAccountParameters, - type PrivateKeyToSimpleSmartAccountParameters, - type PrivateKeyToLightSmartAccountParameters, - type PrivateKeyToSafeSmartAccountParameters, - type SignerToEcdsaKernelSmartAccountParameters, - type SignerToBiconomySmartAccountParameters, - type PrivateKeyToBiconomySmartAccountParameters, - type SignerToTrustSmartAccountParameters, - type PrivateKeyToTrustSmartAccountParameters + type KernelVersion, + toEcdsaKernelSmartAccount, + type ToBiconomySmartAccountReturnType, + type ToBiconomySmartAccountParameters, + type BiconomySmartAccountImplementation, + toBiconomySmartAccount } diff --git a/packages/permissionless/accounts/kernel/abi/KernelV3AccountAbi.ts b/packages/permissionless/accounts/kernel/abi/KernelV3AccountAbi.ts index 31820fbb..5571635c 100644 --- a/packages/permissionless/accounts/kernel/abi/KernelV3AccountAbi.ts +++ b/packages/permissionless/accounts/kernel/abi/KernelV3AccountAbi.ts @@ -106,3 +106,708 @@ export const KernelV3ExecuteAbi = [ stateMutability: "payable" } ] as const + +export const KernelV3AccountAbi = [ + { + type: "constructor", + inputs: [ + { + name: "_entrypoint", + type: "address", + internalType: "contract IEntryPoint" + } + ], + stateMutability: "nonpayable" + }, + { type: "fallback", stateMutability: "payable" }, + { type: "receive", stateMutability: "payable" }, + { + type: "function", + name: "accountId", + inputs: [], + outputs: [ + { + name: "accountImplementationId", + type: "string", + internalType: "string" + } + ], + stateMutability: "pure" + }, + { + type: "function", + name: "currentNonce", + inputs: [], + outputs: [{ name: "", type: "uint32", internalType: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + name: "eip712Domain", + inputs: [], + outputs: [ + { name: "fields", type: "bytes1", internalType: "bytes1" }, + { name: "name", type: "string", internalType: "string" }, + { name: "version", type: "string", internalType: "string" }, + { name: "chainId", type: "uint256", internalType: "uint256" }, + { + name: "verifyingContract", + type: "address", + internalType: "address" + }, + { name: "salt", type: "bytes32", internalType: "bytes32" }, + { name: "extensions", type: "uint256[]", internalType: "uint256[]" } + ], + stateMutability: "view" + }, + { + type: "function", + name: "entrypoint", + inputs: [], + outputs: [ + { name: "", type: "address", internalType: "contract IEntryPoint" } + ], + stateMutability: "view" + }, + { + type: "function", + name: "execute", + inputs: [ + { name: "execMode", type: "bytes32", internalType: "ExecMode" }, + { name: "executionCalldata", type: "bytes", internalType: "bytes" } + ], + outputs: [], + stateMutability: "payable" + }, + { + type: "function", + name: "executeFromExecutor", + inputs: [ + { name: "execMode", type: "bytes32", internalType: "ExecMode" }, + { name: "executionCalldata", type: "bytes", internalType: "bytes" } + ], + outputs: [ + { name: "returnData", type: "bytes[]", internalType: "bytes[]" } + ], + stateMutability: "payable" + }, + { + type: "function", + name: "executeUserOp", + inputs: [ + { + name: "userOp", + type: "tuple", + internalType: "struct PackedUserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address" + }, + { name: "nonce", type: "uint256", internalType: "uint256" }, + { name: "initCode", type: "bytes", internalType: "bytes" }, + { name: "callData", type: "bytes", internalType: "bytes" }, + { + name: "accountGasLimits", + type: "bytes32", + internalType: "bytes32" + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256" + }, + { + name: "gasFees", + type: "bytes32", + internalType: "bytes32" + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes" + }, + { name: "signature", type: "bytes", internalType: "bytes" } + ] + }, + { name: "userOpHash", type: "bytes32", internalType: "bytes32" } + ], + outputs: [], + stateMutability: "payable" + }, + { + type: "function", + name: "executorConfig", + inputs: [ + { + name: "executor", + type: "address", + internalType: "contract IExecutor" + } + ], + outputs: [ + { + name: "", + type: "tuple", + internalType: "struct ExecutorManager.ExecutorConfig", + components: [ + { + name: "hook", + type: "address", + internalType: "contract IHook" + } + ] + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "initialize", + inputs: [ + { + name: "_rootValidator", + type: "bytes21", + internalType: "ValidationId" + }, + { name: "hook", type: "address", internalType: "contract IHook" }, + { name: "validatorData", type: "bytes", internalType: "bytes" }, + { name: "hookData", type: "bytes", internalType: "bytes" } + ], + outputs: [], + stateMutability: "nonpayable" + }, + { + type: "function", + name: "installModule", + inputs: [ + { name: "moduleType", type: "uint256", internalType: "uint256" }, + { name: "module", type: "address", internalType: "address" }, + { name: "initData", type: "bytes", internalType: "bytes" } + ], + outputs: [], + stateMutability: "payable" + }, + { + type: "function", + name: "installValidations", + inputs: [ + { name: "vIds", type: "bytes21[]", internalType: "ValidationId[]" }, + { + name: "configs", + type: "tuple[]", + internalType: "struct ValidationManager.ValidationConfig[]", + components: [ + { name: "nonce", type: "uint32", internalType: "uint32" }, + { + name: "hook", + type: "address", + internalType: "contract IHook" + } + ] + }, + { + name: "validationData", + type: "bytes[]", + internalType: "bytes[]" + }, + { name: "hookData", type: "bytes[]", internalType: "bytes[]" } + ], + outputs: [], + stateMutability: "payable" + }, + { + type: "function", + name: "invalidateNonce", + inputs: [{ name: "nonce", type: "uint32", internalType: "uint32" }], + outputs: [], + stateMutability: "payable" + }, + { + type: "function", + name: "isAllowedSelector", + inputs: [ + { name: "vId", type: "bytes21", internalType: "ValidationId" }, + { name: "selector", type: "bytes4", internalType: "bytes4" } + ], + outputs: [{ name: "", type: "bool", internalType: "bool" }], + stateMutability: "view" + }, + { + type: "function", + name: "isModuleInstalled", + inputs: [ + { name: "moduleType", type: "uint256", internalType: "uint256" }, + { name: "module", type: "address", internalType: "address" }, + { name: "additionalContext", type: "bytes", internalType: "bytes" } + ], + outputs: [{ name: "", type: "bool", internalType: "bool" }], + stateMutability: "view" + }, + { + type: "function", + name: "isValidSignature", + inputs: [ + { name: "hash", type: "bytes32", internalType: "bytes32" }, + { name: "signature", type: "bytes", internalType: "bytes" } + ], + outputs: [{ name: "", type: "bytes4", internalType: "bytes4" }], + stateMutability: "view" + }, + { + type: "function", + name: "permissionConfig", + inputs: [{ name: "pId", type: "bytes4", internalType: "PermissionId" }], + outputs: [ + { + name: "", + type: "tuple", + internalType: "struct ValidationManager.PermissionConfig", + components: [ + { + name: "permissionFlag", + type: "bytes2", + internalType: "PassFlag" + }, + { + name: "signer", + type: "address", + internalType: "contract ISigner" + }, + { + name: "policyData", + type: "bytes22[]", + internalType: "PolicyData[]" + } + ] + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "rootValidator", + inputs: [], + outputs: [{ name: "", type: "bytes21", internalType: "ValidationId" }], + stateMutability: "view" + }, + { + type: "function", + name: "selectorConfig", + inputs: [{ name: "selector", type: "bytes4", internalType: "bytes4" }], + outputs: [ + { + name: "", + type: "tuple", + internalType: "struct SelectorManager.SelectorConfig", + components: [ + { + name: "hook", + type: "address", + internalType: "contract IHook" + }, + { + name: "target", + type: "address", + internalType: "address" + }, + { + name: "callType", + type: "bytes1", + internalType: "CallType" + } + ] + } + ], + stateMutability: "view" + }, + { + type: "function", + name: "supportsExecutionMode", + inputs: [{ name: "mode", type: "bytes32", internalType: "ExecMode" }], + outputs: [{ name: "", type: "bool", internalType: "bool" }], + stateMutability: "pure" + }, + { + type: "function", + name: "supportsModule", + inputs: [ + { name: "moduleTypeId", type: "uint256", internalType: "uint256" } + ], + outputs: [{ name: "", type: "bool", internalType: "bool" }], + stateMutability: "pure" + }, + { + type: "function", + name: "uninstallModule", + inputs: [ + { name: "moduleType", type: "uint256", internalType: "uint256" }, + { name: "module", type: "address", internalType: "address" }, + { name: "deInitData", type: "bytes", internalType: "bytes" } + ], + outputs: [], + stateMutability: "payable" + }, + { + type: "function", + name: "uninstallValidation", + inputs: [ + { name: "vId", type: "bytes21", internalType: "ValidationId" }, + { name: "deinitData", type: "bytes", internalType: "bytes" }, + { name: "hookDeinitData", type: "bytes", internalType: "bytes" } + ], + outputs: [], + stateMutability: "payable" + }, + { + type: "function", + name: "upgradeTo", + inputs: [ + { + name: "_newImplementation", + type: "address", + internalType: "address" + } + ], + outputs: [], + stateMutability: "payable" + }, + { + type: "function", + name: "validNonceFrom", + inputs: [], + outputs: [{ name: "", type: "uint32", internalType: "uint32" }], + stateMutability: "view" + }, + { + type: "function", + name: "validateUserOp", + inputs: [ + { + name: "userOp", + type: "tuple", + internalType: "struct PackedUserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address" + }, + { name: "nonce", type: "uint256", internalType: "uint256" }, + { name: "initCode", type: "bytes", internalType: "bytes" }, + { name: "callData", type: "bytes", internalType: "bytes" }, + { + name: "accountGasLimits", + type: "bytes32", + internalType: "bytes32" + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256" + }, + { + name: "gasFees", + type: "bytes32", + internalType: "bytes32" + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes" + }, + { name: "signature", type: "bytes", internalType: "bytes" } + ] + }, + { name: "userOpHash", type: "bytes32", internalType: "bytes32" }, + { + name: "missingAccountFunds", + type: "uint256", + internalType: "uint256" + } + ], + outputs: [ + { + name: "validationData", + type: "uint256", + internalType: "ValidationData" + } + ], + stateMutability: "payable" + }, + { + type: "function", + name: "validationConfig", + inputs: [ + { name: "vId", type: "bytes21", internalType: "ValidationId" } + ], + outputs: [ + { + name: "", + type: "tuple", + internalType: "struct ValidationManager.ValidationConfig", + components: [ + { name: "nonce", type: "uint32", internalType: "uint32" }, + { + name: "hook", + type: "address", + internalType: "contract IHook" + } + ] + } + ], + stateMutability: "view" + }, + { + type: "event", + name: "ModuleInstalled", + inputs: [ + { + name: "moduleTypeId", + type: "uint256", + indexed: false, + internalType: "uint256" + }, + { + name: "module", + type: "address", + indexed: false, + internalType: "address" + } + ], + anonymous: false + }, + { + type: "event", + name: "ModuleUninstallResult", + inputs: [ + { + name: "module", + type: "address", + indexed: false, + internalType: "address" + }, + { + name: "result", + type: "bool", + indexed: false, + internalType: "bool" + } + ], + anonymous: false + }, + { + type: "event", + name: "ModuleUninstalled", + inputs: [ + { + name: "moduleTypeId", + type: "uint256", + indexed: false, + internalType: "uint256" + }, + { + name: "module", + type: "address", + indexed: false, + internalType: "address" + } + ], + anonymous: false + }, + { + type: "event", + name: "NonceInvalidated", + inputs: [ + { + name: "nonce", + type: "uint32", + indexed: false, + internalType: "uint32" + } + ], + anonymous: false + }, + { + type: "event", + name: "PermissionInstalled", + inputs: [ + { + name: "permission", + type: "bytes4", + indexed: false, + internalType: "PermissionId" + }, + { + name: "nonce", + type: "uint32", + indexed: false, + internalType: "uint32" + } + ], + anonymous: false + }, + { + type: "event", + name: "PermissionUninstalled", + inputs: [ + { + name: "permission", + type: "bytes4", + indexed: false, + internalType: "PermissionId" + } + ], + anonymous: false + }, + { + type: "event", + name: "Received", + inputs: [ + { + name: "sender", + type: "address", + indexed: false, + internalType: "address" + }, + { + name: "amount", + type: "uint256", + indexed: false, + internalType: "uint256" + } + ], + anonymous: false + }, + { + type: "event", + name: "RootValidatorUpdated", + inputs: [ + { + name: "rootValidator", + type: "bytes21", + indexed: false, + internalType: "ValidationId" + } + ], + anonymous: false + }, + { + type: "event", + name: "SelectorSet", + inputs: [ + { + name: "selector", + type: "bytes4", + indexed: false, + internalType: "bytes4" + }, + { + name: "vId", + type: "bytes21", + indexed: false, + internalType: "ValidationId" + }, + { + name: "allowed", + type: "bool", + indexed: false, + internalType: "bool" + } + ], + anonymous: false + }, + { + type: "event", + name: "TryExecuteUnsuccessful", + inputs: [ + { + name: "batchExecutionindex", + type: "uint256", + indexed: false, + internalType: "uint256" + }, + { + name: "result", + type: "bytes", + indexed: false, + internalType: "bytes" + } + ], + anonymous: false + }, + { + type: "event", + name: "Upgraded", + inputs: [ + { + name: "implementation", + type: "address", + indexed: true, + internalType: "address" + } + ], + anonymous: false + }, + { + type: "event", + name: "ValidatorInstalled", + inputs: [ + { + name: "validator", + type: "address", + indexed: false, + internalType: "contract IValidator" + }, + { + name: "nonce", + type: "uint32", + indexed: false, + internalType: "uint32" + } + ], + anonymous: false + }, + { + type: "event", + name: "ValidatorUninstalled", + inputs: [ + { + name: "validator", + type: "address", + indexed: false, + internalType: "contract IValidator" + } + ], + anonymous: false + }, + { type: "error", name: "EnableNotApproved", inputs: [] }, + { type: "error", name: "ExecutionReverted", inputs: [] }, + { type: "error", name: "InvalidCallType", inputs: [] }, + { type: "error", name: "InvalidCaller", inputs: [] }, + { type: "error", name: "InvalidExecutor", inputs: [] }, + { type: "error", name: "InvalidFallback", inputs: [] }, + { type: "error", name: "InvalidMode", inputs: [] }, + { type: "error", name: "InvalidModuleType", inputs: [] }, + { type: "error", name: "InvalidNonce", inputs: [] }, + { type: "error", name: "InvalidSelector", inputs: [] }, + { type: "error", name: "InvalidSignature", inputs: [] }, + { type: "error", name: "InvalidValidationType", inputs: [] }, + { type: "error", name: "InvalidValidator", inputs: [] }, + { type: "error", name: "NonceInvalidationError", inputs: [] }, + { type: "error", name: "NotSupportedCallType", inputs: [] }, + { type: "error", name: "OnlyExecuteUserOp", inputs: [] }, + { type: "error", name: "PermissionDataLengthMismatch", inputs: [] }, + { type: "error", name: "PermissionNotAlllowedForSignature", inputs: [] }, + { type: "error", name: "PermissionNotAlllowedForUserOp", inputs: [] }, + { type: "error", name: "PolicyDataTooLarge", inputs: [] }, + { + type: "error", + name: "PolicyFailed", + inputs: [{ name: "i", type: "uint256", internalType: "uint256" }] + }, + { type: "error", name: "PolicySignatureOrderError", inputs: [] }, + { type: "error", name: "RootValidatorCannotBeRemoved", inputs: [] }, + { type: "error", name: "SignerPrefixNotPresent", inputs: [] } +] as const diff --git a/packages/permissionless/accounts/kernel/constants.ts b/packages/permissionless/accounts/kernel/constants.ts index 90d7825a..def56f48 100644 --- a/packages/permissionless/accounts/kernel/constants.ts +++ b/packages/permissionless/accounts/kernel/constants.ts @@ -1,15 +1,7 @@ export const DUMMY_ECDSA_SIGNATURE = "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c" export const ROOT_MODE_KERNEL_V2 = "0x00000000" -export enum CALL_TYPE { - SINGLE = "0x00", - BATCH = "0x01", - DELEGATE_CALL = "0xFF" -} -export enum EXEC_TYPE { - DEFAULT = "0x00", - TRY_EXEC = "0x01" -} + export const VALIDATOR_TYPE = { ROOT: "0x00", VALIDATOR: "0x01", diff --git a/packages/permissionless/accounts/kernel/signerToEcdsaKernelSmartAccount.ts b/packages/permissionless/accounts/kernel/toEcdsaKernelSmartAccount.ts similarity index 53% rename from packages/permissionless/accounts/kernel/signerToEcdsaKernelSmartAccount.ts rename to packages/permissionless/accounts/kernel/toEcdsaKernelSmartAccount.ts index 3eb63024..62077413 100644 --- a/packages/permissionless/accounts/kernel/signerToEcdsaKernelSmartAccount.ts +++ b/packages/permissionless/accounts/kernel/toEcdsaKernelSmartAccount.ts @@ -1,35 +1,38 @@ -import type { PublicActions, PublicRpcSchema, TypedData } from "viem" +import type { + Account, + Assign, + Chain, + EIP1193Provider, + OneOf, + Transport, + WalletClient +} from "viem" import { type Address, - type Chain, type Client, type Hex, type LocalAccount, - type Transport, type TypedDataDefinition, concatHex, encodeFunctionData, toHex, zeroAddress } from "viem" +import { + type SmartAccount, + type SmartAccountImplementation, + type UserOperation, + entryPoint06Abi, + entryPoint07Abi, + entryPoint07Address, + getUserOperationHash, + toSmartAccount +} from "viem/account-abstraction" import { signMessage as _signMessage, getChainId } from "viem/actions" +import { getAction } from "viem/utils" import { getAccountNonce } from "../../actions/public/getAccountNonce" import { getSenderAddress } from "../../actions/public/getSenderAddress" -import type { - ENTRYPOINT_ADDRESS_V07_TYPE, - EntryPoint, - Prettify -} from "../../types" -import type { ENTRYPOINT_ADDRESS_V06_TYPE } from "../../types/entrypoint" -import { ENTRYPOINT_ADDRESS_V06, getEntryPointVersion } from "../../utils" -import { getUserOperationHash } from "../../utils/getUserOperationHash" -import { isSmartAccountDeployed } from "../../utils/isSmartAccountDeployed" -import { toSmartAccount } from "../toSmartAccount" -import type { SmartAccount } from "../types" -import { - SignTransactionNotSupportedBySmartAccount, - type SmartAccountSigner -} from "../types" +import { toOwner } from "../../utils/toOwner" import { KernelInitAbi } from "./abi/KernelAccountAbi" import { KernelV3InitAbi, KernelV3_1AccountAbi } from "./abi/KernelV3AccountAbi" import { KernelV3MetaFactoryDeployWithFactoryAbi } from "./abi/KernelV3MetaFactoryAbi" @@ -44,12 +47,6 @@ import { isKernelV2 } from "./utils/isKernelV2" import { signMessage } from "./utils/signMessage" import { signTypedData } from "./utils/signTypedData" -export type KernelEcdsaSmartAccount< - entryPoint extends EntryPoint, - transport extends Transport = Transport, - chain extends Chain | undefined = Chain | undefined -> = SmartAccount - /** * The account creation ABI for a kernel smart account (from the KernelFactory) */ @@ -85,8 +82,8 @@ const createAccountAbi = [ } ] as const -export type KernelVersion = - entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE +export type KernelVersion = + entryPointVersion extends "0.6" ? "0.2.1" | "0.2.2" | "0.2.3" | "0.2.4" : "0.3.0-beta" | "0.3.1" @@ -94,7 +91,7 @@ export type KernelVersion = * Default addresses map for different kernel smart account versions */ export const KERNEL_VERSION_TO_ADDRESSES_MAP: { - [key in KernelVersion]: { + [key in KernelVersion<"0.6" | "0.7">]: { ECDSA_VALIDATOR: Address ACCOUNT_LOGIC: Address FACTORY_ADDRESS: Address @@ -139,16 +136,16 @@ export const KERNEL_VERSION_TO_ADDRESSES_MAP: { * Get supported Kernel Smart Account version based on entryPoint * @param entryPoint */ -const getDefaultKernelVersion = ( - entryPoint: TEntryPoint, - version?: KernelVersion -): KernelVersion => { +const getDefaultKernelVersion = ( + entryPointVersion: TEntryPointVersion, + version?: KernelVersion +): KernelVersion => { if (version) { return version } return ( - entryPoint === ENTRYPOINT_ADDRESS_V06 ? "0.2.2" : "0.3.0-beta" - ) as KernelVersion + entryPointVersion === "0.6" ? "0.2.2" : "0.3.0-beta" + ) as KernelVersion } type KERNEL_ADDRESSES = { @@ -173,7 +170,7 @@ const getDefaultAddresses = ({ metaFactoryAddress: _metaFactoryAddress, kernelVersion }: Partial & { - kernelVersion: KernelVersion + kernelVersion: KernelVersion<"0.6" | "0.7"> }): KERNEL_ADDRESSES => { const addresses = KERNEL_VERSION_TO_ADDRESSES_MAP[kernelVersion] const ecdsaValidatorAddress = @@ -203,20 +200,20 @@ export const getEcdsaRootIdentifierForKernelV3 = ( * @param owner * @param ecdsaValidatorAddress */ -const getInitialisationData = ({ +const getInitializationData = ({ + entryPoint: { version: entryPointVersion }, kernelVersion, - entryPoint: entryPointAddress, owner, ecdsaValidatorAddress }: { - kernelVersion: KernelVersion - entryPoint: entryPoint + kernelVersion: KernelVersion + entryPoint: { + version: entryPointVersion + } owner: Address ecdsaValidatorAddress: Address }) => { - const entryPointVersion = getEntryPointVersion(entryPointAddress) - - if (entryPointVersion === "v0.6") { + if (entryPointVersion === "0.6") { return encodeFunctionData({ abi: KernelInitAbi, functionName: "initialize", @@ -259,8 +256,8 @@ const getInitialisationData = ({ * @param accountLogicAddress * @param ecdsaValidatorAddress */ -const getAccountInitCode = async ({ - entryPoint: entryPointAddress, +const getAccountInitCode = async ({ + entryPointVersion, kernelVersion, owner, index, @@ -268,97 +265,85 @@ const getAccountInitCode = async ({ accountLogicAddress, ecdsaValidatorAddress }: { - kernelVersion: KernelVersion - entryPoint: entryPoint + kernelVersion: KernelVersion + entryPointVersion: entryPointVersion owner: Address index: bigint factoryAddress: Address accountLogicAddress: Address ecdsaValidatorAddress: Address }): Promise => { - if (!owner) throw new Error("Owner account not found") - const entryPointVersion = getEntryPointVersion(entryPointAddress) - // Build the account initialization data - const initialisationData = getInitialisationData({ + const initializationData = getInitializationData({ + entryPoint: { version: entryPointVersion }, kernelVersion, - entryPoint: entryPointAddress, ecdsaValidatorAddress, owner }) // Build the account init code - if (entryPointVersion === "v0.6") { + if (entryPointVersion === "0.6") { return encodeFunctionData({ abi: createAccountAbi, functionName: "createAccount", - args: [accountLogicAddress, initialisationData, index] + args: [accountLogicAddress, initializationData, index] }) } return encodeFunctionData({ abi: KernelV3MetaFactoryDeployWithFactoryAbi, functionName: "deployWithFactory", - args: [factoryAddress, initialisationData, toHex(index, { size: 32 })] + args: [factoryAddress, initializationData, toHex(index, { size: 32 })] }) } -/** - * Check the validity of an existing account address, or fetch the pre-deterministic account address for a kernel smart wallet - * @param client - * @param owner - * @param entryPoint - * @param ecdsaValidatorAddress - * @param initCodeProvider - * @param factoryAddress - */ -const getAccountAddress = async < - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined ->({ - client, - entryPoint: entryPointAddress, - factory, - factoryData -}: { - client: Client - entryPoint: entryPoint - factory: Address - factoryData: Hex -}): Promise
=> { - // Find the init code for this account - const entryPointVersion = getEntryPointVersion(entryPointAddress) - if (entryPointVersion === "v0.6") { - return getSenderAddress(client, { - initCode: concatHex([factory, factoryData]), - entryPoint: entryPointAddress as ENTRYPOINT_ADDRESS_V06_TYPE - }) +export type ToEcdsaKernelSmartAccountParameters< + entryPointVersion extends "0.6" | "0.7", + kernelVersion extends KernelVersion +> = { + client: Client + owners: [ + OneOf< + | EIP1193Provider + | WalletClient + | LocalAccount + > + ] + entryPoint?: { + address: Address + version: entryPointVersion } - return getSenderAddress(client, { - factory: factory, - factoryData: factoryData, - entryPoint: entryPointAddress as ENTRYPOINT_ADDRESS_V07_TYPE - }) -} - -export type SignerToEcdsaKernelSmartAccountParameters< - entryPoint extends EntryPoint, - TSource extends string = string, - TAddress extends Address = Address -> = Prettify<{ - signer: SmartAccountSigner - version?: KernelVersion - entryPoint: entryPoint address?: Address + version?: kernelVersion index?: bigint factoryAddress?: Address metaFactoryAddress?: Address accountLogicAddress?: Address ecdsaValidatorAddress?: Address nonceKey?: bigint -}> +} + +export type EcdsaKernelSmartAccountImplementation< + entryPointVersion extends "0.6" | "0.7" = "0.7" +> = Assign< + SmartAccountImplementation< + entryPointVersion extends "0.6" + ? typeof entryPoint06Abi + : typeof entryPoint07Abi, + entryPointVersion + // { + // // entryPoint === ENTRYPOINT_ADDRESS_V06 ? "0.2.2" : "0.3.0-beta" + // abi: entryPointVersion extends "0.6" ? typeof BiconomyAbi + // factory: { abi: typeof FactoryAbi; address: Address } + // } + >, + { sign: NonNullable } +> + +export type ToEcdsaKernelSmartAccountReturnType< + entryPointVersion extends "0.6" | "0.7" = "0.7" +> = SmartAccount> /** * Build a kernel smart account from a private key, that use the ECDSA signer behind the scene * @param client @@ -369,35 +354,39 @@ export type SignerToEcdsaKernelSmartAccountParameters< * @param accountLogicAddress * @param ecdsaValidatorAddress */ -export async function signerToEcdsaKernelSmartAccount< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSource extends string = string, - TAddress extends Address = Address +export async function toEcdsaKernelSmartAccount< + entryPointVersion extends "0.6" | "0.7", + kernelVersion extends KernelVersion >( - client: Client< - TTransport, - TChain, - undefined, - PublicRpcSchema, - PublicActions - >, - { - signer, + parameters: ToEcdsaKernelSmartAccountParameters< + entryPointVersion, + kernelVersion + > +): Promise> { + const { + client, address, + index = 0n, + owners, version, - entryPoint: entryPointAddress, - index = BigInt(0), + ecdsaValidatorAddress: _ecdsaValidatorAddress, factoryAddress: _factoryAddress, metaFactoryAddress: _metaFactoryAddress, - accountLogicAddress: _accountLogicAddress, - ecdsaValidatorAddress: _ecdsaValidatorAddress, - nonceKey - }: SignerToEcdsaKernelSmartAccountParameters -): Promise> { - const entryPointVersion = getEntryPointVersion(entryPointAddress) - const kernelVersion = getDefaultKernelVersion(entryPointAddress, version) + accountLogicAddress: _accountLogicAddress + } = parameters + + const localOwner = await toOwner({ owner: owners[0] }) + + const entryPoint = { + address: parameters.entryPoint?.address ?? entryPoint07Address, + abi: + (parameters.entryPoint?.version ?? "0.7") === "0.6" + ? entryPoint06Abi + : entryPoint07Abi, + version: parameters.entryPoint?.version ?? "0.7" + } as const + + const kernelVersion = getDefaultKernelVersion(entryPoint.version, version) const { accountLogicAddress, @@ -412,57 +401,88 @@ export async function signerToEcdsaKernelSmartAccount< kernelVersion }) - // Get the private key related account - const viemSigner: LocalAccount = { - ...signer, - signTransaction: (_, __) => { - throw new SignTransactionNotSupportedBySmartAccount() - } - } as LocalAccount - // Helper to generate the init code for the smart account const generateInitCode = () => getAccountInitCode({ + entryPointVersion: entryPoint.version, kernelVersion, - entryPoint: entryPointAddress, - owner: viemSigner.address, + owner: localOwner.address, index, factoryAddress, accountLogicAddress, ecdsaValidatorAddress }) - // Fetch account address and chain id - const [accountAddress, chainId] = await Promise.all([ - address ?? - getAccountAddress({ - client, - factory: - entryPointVersion === "v0.6" - ? factoryAddress - : metaFactoryAddress, - factoryData: await generateInitCode(), - entryPoint: entryPointAddress - }), - client.chain?.id ?? getChainId(client) - ]) - - if (!accountAddress) throw new Error("Account address not found") - - let smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) + let accountAddress: Address | undefined = address + + let chainId: number + + const getMemoizedChainId = async () => { + if (chainId) return chainId + chainId = client.chain + ? client.chain.id + : await getAction(client, getChainId, "getChainId")({}) + return chainId + } + + const getFactoryArgs = async () => { + return { + factory: + entryPoint.version === "0.6" + ? factoryAddress + : metaFactoryAddress, + factoryData: await generateInitCode() + } + } return toSmartAccount({ - address: accountAddress, + client, + entryPoint, + getFactoryArgs, + async getAddress() { + if (accountAddress) return accountAddress + + const { factory, factoryData } = await getFactoryArgs() + + // Get the sender address based on the init code + accountAddress = await getSenderAddress(client, { + factory, + factoryData, + entryPointAddress: entryPoint.address + }) + + return accountAddress + }, + async encodeCalls(calls) { + return encodeCallData({ calls, kernelVersion }) + }, + async getNonce(_args) { + return getAccountNonce(client, { + address: await this.getAddress(), + entryPointAddress: entryPoint.address, + key: getNonceKeyWithEncoding( + kernelVersion, + ecdsaValidatorAddress, + /*args?.key ?? */ parameters.nonceKey ?? 0n + ) + }) + }, + async getStubSignature() { + if (isKernelV2(kernelVersion)) { + return concatHex([ROOT_MODE_KERNEL_V2, DUMMY_ECDSA_SIGNATURE]) + } + return DUMMY_ECDSA_SIGNATURE + }, + async sign({ hash }) { + return this.signMessage({ message: hash }) + }, async signMessage({ message }) { - const signature = await signMessage(client, { - account: viemSigner, + const signature = await signMessage({ + owner: localOwner, message, - accountAddress, - accountVersion: kernelVersion, - chainId + accountAddress: await this.getAddress(), + kernelVersion, + chainId: await getMemoizedChainId() }) if (isKernelV2(kernelVersion)) { @@ -474,26 +494,13 @@ export async function signerToEcdsaKernelSmartAccount< signature ]) }, - async signTransaction(_, __) { - throw new SignTransactionNotSupportedBySmartAccount() - }, - async signTypedData< - const TTypedData extends TypedData | Record, - TPrimaryType extends - | keyof TTypedData - | "EIP712Domain" = keyof TTypedData - >(typedData: TypedDataDefinition) { - const signature = await signTypedData< - TTypedData, - TPrimaryType, - TChain, - undefined - >(client, { - account: viemSigner, - ...typedData, - accountAddress, - accountVersion: kernelVersion, - chainId + async signTypedData(typedData) { + const signature = await signTypedData({ + owner: localOwner, + chainId: await getMemoizedChainId(), + ...(typedData as TypedDataDefinition), + accountAddress: await this.getAddress(), + kernelVersion }) if (isKernelV2(kernelVersion)) { @@ -505,111 +512,30 @@ export async function signerToEcdsaKernelSmartAccount< signature ]) }, - client: client, - publicKey: accountAddress, - entryPoint: entryPointAddress, - source: "kernelEcdsaSmartAccount", - - // Get the nonce of the smart account - async getNonce(key?: bigint) { - return getAccountNonce(client, { - sender: accountAddress, - entryPoint: entryPointAddress, - key: - key ?? - nonceKey ?? - getNonceKeyWithEncoding( - kernelVersion, - ecdsaValidatorAddress - // @dev specify the custom nonceKey here when integrating the said feature - /*, nonceKey */ - ) - }) - }, - // Sign a user operation - async signUserOperation(userOperation) { + async signUserOperation(parameters) { + const { chainId = await getMemoizedChainId(), ...userOperation } = + parameters + const hash = getUserOperationHash({ userOperation: { ...userOperation, + sender: userOperation.sender ?? (await this.getAddress()), signature: "0x" - }, - entryPoint: entryPointAddress, + } as UserOperation, + entryPointAddress: entryPoint.address, + entryPointVersion: entryPoint.version, chainId: chainId }) - const signature = await _signMessage(client, { - account: viemSigner, + const signature = await localOwner.signMessage({ message: { raw: hash } }) + // Always use the sudo mode, since we will use external paymaster if (isKernelV2(kernelVersion)) { return concatHex(["0x00000000", signature]) } return signature - }, - - // Encode the init code - async getInitCode() { - if (smartAccountDeployed) return "0x" - - smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) - - if (smartAccountDeployed) return "0x" - - const _factoryAddress = - entryPointVersion === "v0.6" - ? factoryAddress - : metaFactoryAddress - return concatHex([_factoryAddress, await generateInitCode()]) - }, - - async getFactory() { - if (smartAccountDeployed) return undefined - - smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) - - if (smartAccountDeployed) return undefined - - return entryPointVersion === "v0.6" - ? factoryAddress - : metaFactoryAddress - }, - - async getFactoryData() { - if (smartAccountDeployed) return undefined - - smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) - - if (smartAccountDeployed) return undefined - - return generateInitCode() - }, - - // Encode the deploy call data - async encodeDeployCallData(_) { - throw new Error("Simple account doesn't support account deployment") - }, - - // Encode a call - async encodeCallData(_tx) { - return encodeCallData(_tx, kernelVersion) - }, - - // Get simple dummy signature - async getDummySignature(_userOperation) { - if (isKernelV2(kernelVersion)) { - return concatHex([ROOT_MODE_KERNEL_V2, DUMMY_ECDSA_SIGNATURE]) - } - return DUMMY_ECDSA_SIGNATURE } - }) + }) as Promise> } diff --git a/packages/permissionless/accounts/kernel/utils/encodeCallData.ts b/packages/permissionless/accounts/kernel/utils/encodeCallData.ts index deac9b3b..87f3611c 100644 --- a/packages/permissionless/accounts/kernel/utils/encodeCallData.ts +++ b/packages/permissionless/accounts/kernel/utils/encodeCallData.ts @@ -1,44 +1,31 @@ -import { - type Address, - type Hex, - concatHex, - encodeAbiParameters, - encodeFunctionData, - toHex -} from "viem" -import type { EntryPoint } from "../../../types" +import { type Address, type Hex, encodeFunctionData } from "viem" +import { encode7579Calls } from "../../../utils/encode7579Calls" import { KernelExecuteAbi } from "../abi/KernelAccountAbi" -import { KernelV3ExecuteAbi } from "../abi/KernelV3AccountAbi" -import { CALL_TYPE, EXEC_TYPE } from "../constants" -import type { KernelVersion } from "../signerToEcdsaKernelSmartAccount" -import { getExecMode } from "./getExecMode" +import type { KernelVersion } from "../toEcdsaKernelSmartAccount" import { isKernelV2 } from "./isKernelV2" -export const encodeCallData = ( - _tx: - | { - to: Address - value: bigint - data: Hex - } - | { - to: Address - value: bigint - data: Hex - }[], - accountVersion: KernelVersion -) => { - if (isKernelV2(accountVersion)) { - if (Array.isArray(_tx)) { +export const encodeCallData = ({ + kernelVersion, + calls +}: { + calls: readonly { + to: Address + value?: bigint | undefined + data?: Hex | undefined + }[] + kernelVersion: KernelVersion<"0.6" | "0.7"> +}) => { + if (isKernelV2(kernelVersion)) { + if (calls.length > 1) { // Encode a batched call return encodeFunctionData({ abi: KernelExecuteAbi, functionName: "executeBatch", args: [ - _tx.map((tx) => ({ + calls.map((tx) => ({ to: tx.to, - value: tx.value, - data: tx.data + value: tx.value ?? 0n, + data: tx.data ?? "0x" })) ] }) @@ -47,70 +34,17 @@ export const encodeCallData = ( return encodeFunctionData({ abi: KernelExecuteAbi, functionName: "execute", - args: [_tx.to, _tx.value, _tx.data, 0] + args: [calls[0].to, calls[0].value ?? 0n, calls[0].data ?? "0x", 0] }) } - if (Array.isArray(_tx)) { - // Encode a batched call - const calldata = encodeAbiParameters( - [ - { - name: "executionBatch", - type: "tuple[]", - components: [ - { - name: "target", - type: "address" - }, - { - name: "value", - type: "uint256" - }, - { - name: "callData", - type: "bytes" - } - ] - } - ], - [ - _tx.map((arg) => { - return { - target: arg.to, - value: arg.value, - callData: arg.data - } - }) - ] - ) - return encodeFunctionData({ - abi: KernelV3ExecuteAbi, - functionName: "execute", - args: [ - getExecMode({ - callType: CALL_TYPE.BATCH, - execType: EXEC_TYPE.DEFAULT - }), - calldata - ] - }) - } - - const calldata = concatHex([ - _tx.to, - toHex(_tx.value, { size: 32 }), - _tx.data - ]) - return encodeFunctionData({ - abi: KernelV3ExecuteAbi, - functionName: "execute", - args: [ - getExecMode({ - callType: CALL_TYPE.SINGLE, - execType: EXEC_TYPE.DEFAULT - }), - calldata - ] + return encode7579Calls({ + mode: { + type: calls.length > 1 ? "batchcall" : "call", + revertOnError: false, + selector: "0x", + context: "0x" + }, + callData: calls }) } diff --git a/packages/permissionless/accounts/kernel/utils/getExecMode.ts b/packages/permissionless/accounts/kernel/utils/getExecMode.ts deleted file mode 100644 index 91d5ba68..00000000 --- a/packages/permissionless/accounts/kernel/utils/getExecMode.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { type Hex, concatHex, pad } from "viem" -import type { CALL_TYPE, EXEC_TYPE } from "../constants" - -export const getExecMode = ({ - callType, - execType -}: { - callType: CALL_TYPE - execType: EXEC_TYPE -}): Hex => { - return concatHex([ - callType, // 1 byte - execType, // 1 byte - "0x00000000", // 4 bytes - "0x00000000", // 4 bytes - pad("0x00000000", { size: 22 }) - ]) -} diff --git a/packages/permissionless/accounts/kernel/utils/getNonceKey.ts b/packages/permissionless/accounts/kernel/utils/getNonceKey.ts index 92702549..17bb482d 100644 --- a/packages/permissionless/accounts/kernel/utils/getNonceKey.ts +++ b/packages/permissionless/accounts/kernel/utils/getNonceKey.ts @@ -1,21 +1,20 @@ import { type Address, concatHex, maxUint16, pad, toHex } from "viem" -import type { EntryPoint } from "../../../types" import { VALIDATOR_MODE, VALIDATOR_TYPE } from "../constants" -import type { KernelVersion } from "../signerToEcdsaKernelSmartAccount" +import type { KernelVersion } from "../toEcdsaKernelSmartAccount" import { isKernelV2 } from "./isKernelV2" export const getNonceKeyWithEncoding = ( - accountVerion: KernelVersion, + kernelVersion: KernelVersion<"0.6" | "0.7">, validatorAddress: Address, nonceKey = 0n ) => { - if (isKernelV2(accountVerion)) { + if (isKernelV2(kernelVersion)) { return nonceKey } if (nonceKey > maxUint16) throw new Error( - `nonce key must be equal or less than 2 bytes(maxUint16) for Kernel version ${accountVerion}` + `nonce key must be equal or less than 2 bytes(maxUint16) for Kernel version ${kernelVersion}` ) const validatorMode = VALIDATOR_MODE.DEFAULT @@ -29,6 +28,6 @@ export const getNonceKeyWithEncoding = ( ]), { size: 24 } ) // 24 bytes - const encodedNonceKey = BigInt(encoding) - return encodedNonceKey + + return BigInt(encoding) } diff --git a/packages/permissionless/accounts/kernel/utils/isKernelV2.ts b/packages/permissionless/accounts/kernel/utils/isKernelV2.ts index 68476b63..16fc02f9 100644 --- a/packages/permissionless/accounts/kernel/utils/isKernelV2.ts +++ b/packages/permissionless/accounts/kernel/utils/isKernelV2.ts @@ -1,7 +1,6 @@ -import type { EntryPoint } from "../../../types" -import type { KernelVersion } from "../signerToEcdsaKernelSmartAccount" +import type { KernelVersion } from "../toEcdsaKernelSmartAccount" -export const isKernelV2 = (version: KernelVersion): boolean => { +export const isKernelV2 = (version: KernelVersion<"0.6" | "0.7">): boolean => { const regex = /0\.2\.\d+/ return regex.test(version) } diff --git a/packages/permissionless/accounts/kernel/utils/signMessage.ts b/packages/permissionless/accounts/kernel/utils/signMessage.ts index 98860ff5..510a1d99 100644 --- a/packages/permissionless/accounts/kernel/utils/signMessage.ts +++ b/packages/permissionless/accounts/kernel/utils/signMessage.ts @@ -1,49 +1,40 @@ import { - type Account, - type Chain, - type Client, type LocalAccount, - type SignMessageParameters, type SignMessageReturnType, - type Transport, - hashMessage, - publicActions + type SignableMessage, + hashMessage } from "viem" import { signMessage as _signMessage } from "viem/actions" import { isKernelV2 } from "./isKernelV2" import { type WrapMessageHashParams, wrapMessageHash } from "./wrapMessageHash" -export async function signMessage< - TChain extends Chain | undefined, - TAccount extends Account | undefined ->( - client: Client, - { - account: account_ = client.account, - message, - accountAddress, - accountVersion - }: SignMessageParameters & WrapMessageHashParams -): Promise { +export async function signMessage({ + message, + owner, + accountAddress, + kernelVersion: accountVersion, + chainId +}: { + chainId: number + message: SignableMessage + owner: LocalAccount +} & WrapMessageHashParams): Promise { if (isKernelV2(accountVersion)) { - return _signMessage(client, { - account: account_ as LocalAccount, + return owner.signMessage({ message }) } const wrappedMessageHash = wrapMessageHash(hashMessage(message), { - accountVersion, + kernelVersion: accountVersion, accountAddress, - chainId: client.chain - ? client.chain.id - : await client.extend(publicActions).getChainId() + chainId + // chainId: client.chain + // ? client.chain.id + // : await client.extend(publicActions).getChainId() }) - const signature = await _signMessage(client, { - account: account_ as LocalAccount, + return owner.signMessage({ message: { raw: wrappedMessageHash } }) - - return signature } diff --git a/packages/permissionless/accounts/kernel/utils/signTypedData.ts b/packages/permissionless/accounts/kernel/utils/signTypedData.ts index 901bc204..ff7bd81f 100644 --- a/packages/permissionless/accounts/kernel/utils/signTypedData.ts +++ b/packages/permissionless/accounts/kernel/utils/signTypedData.ts @@ -1,43 +1,32 @@ import { - type Account, - type Chain, - type Client, type LocalAccount, - type SignTypedDataParameters, type SignTypedDataReturnType, - type Transport, - type TypedData, + type TypedDataDefinition, getTypesForEIP712Domain, hashTypedData, - publicActions, validateTypedData } from "viem" - -import { - signMessage as _signMessage, - signTypedData as _signTypedData -} from "viem/actions" import { isKernelV2 } from "./isKernelV2" import { type WrapMessageHashParams, wrapMessageHash } from "./wrapMessageHash" -export async function signTypedData< - const typedData extends TypedData | Record, - primaryType extends keyof typedData | "EIP712Domain", - chain extends Chain | undefined, - account extends Account | undefined ->( - client: Client, - parameters: SignTypedDataParameters & - WrapMessageHashParams +export async function signTypedData( + parameters: TypedDataDefinition & + WrapMessageHashParams & { + owner: LocalAccount + } ): Promise { const { - account: account_, + owner, accountAddress, - accountVersion, + kernelVersion: accountVersion, + chainId, ...typedData - } = parameters as unknown as SignTypedDataParameters & WrapMessageHashParams + } = parameters + if (isKernelV2(accountVersion)) { - return _signTypedData(client, { account: account_, ...typedData }) + return owner.signTypedData({ + ...typedData + }) } const { message, primaryType, types: _types, domain } = typedData const types = { @@ -59,17 +48,12 @@ export async function signTypedData< const typedHash = hashTypedData({ message, primaryType, types, domain }) const wrappedMessageHash = wrapMessageHash(typedHash, { - accountVersion, + kernelVersion: accountVersion, accountAddress, - chainId: client.chain - ? client.chain.id - : await client.extend(publicActions).getChainId() + chainId: chainId }) - const signature = await _signMessage(client, { - account: account_ as LocalAccount, + return owner.signMessage({ message: { raw: wrappedMessageHash } }) - - return signature } diff --git a/packages/permissionless/accounts/kernel/utils/wrapMessageHash.ts b/packages/permissionless/accounts/kernel/utils/wrapMessageHash.ts index 47b5ff20..a2e72a5b 100644 --- a/packages/permissionless/accounts/kernel/utils/wrapMessageHash.ts +++ b/packages/permissionless/accounts/kernel/utils/wrapMessageHash.ts @@ -6,23 +6,22 @@ import { stringToHex } from "viem" import { type Address, domainSeparator } from "viem" -import type { EntryPoint } from "../../../types" -import type { KernelVersion } from "../signerToEcdsaKernelSmartAccount" +import type { KernelVersion } from "../toEcdsaKernelSmartAccount" export type WrapMessageHashParams = { - accountVersion: KernelVersion + kernelVersion: KernelVersion<"0.6" | "0.7"> accountAddress: Address - chainId?: number + chainId: number } export const wrapMessageHash = ( messageHash: Hex, - { accountAddress, accountVersion, chainId }: WrapMessageHashParams + { accountAddress, kernelVersion, chainId }: WrapMessageHashParams ) => { const _domainSeparator = domainSeparator({ domain: { name: "Kernel", - version: accountVersion, + version: kernelVersion, chainId, verifyingContract: accountAddress } diff --git a/packages/permissionless/accounts/light/privateKeyToLightSmartAccount.ts b/packages/permissionless/accounts/light/privateKeyToLightSmartAccount.ts deleted file mode 100644 index 7d7d7651..00000000 --- a/packages/permissionless/accounts/light/privateKeyToLightSmartAccount.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { - Chain, - Client, - Hex, - PublicActions, - PublicRpcSchema, - Transport -} from "viem" -import { privateKeyToAccount } from "viem/accounts" -import type { Prettify } from "../../types" -import type { EntryPoint } from "../../types" -import { - type LightSmartAccount, - type SignerToLightSmartAccountParameters, - signerToLightSmartAccount -} from "./signerToLightSmartAccount" - -export type PrivateKeyToLightSmartAccountParameters< - entryPoint extends EntryPoint -> = Prettify< - { - privateKey: Hex - } & Omit, "signer"> -> - -/** - * @description Creates an Light Account from a private key. - * - * @returns A Private Key Light Account. - */ -export async function privateKeyToLightSmartAccount< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined ->( - client: Client< - TTransport, - TChain, - undefined, - PublicRpcSchema, - PublicActions - >, - { privateKey, ...rest }: PrivateKeyToLightSmartAccountParameters -): Promise> { - const privateKeyAccount = privateKeyToAccount(privateKey) - - return signerToLightSmartAccount(client, { - signer: privateKeyAccount, - ...rest - }) -} diff --git a/packages/permissionless/accounts/light/signerToLightSmartAccount.ts b/packages/permissionless/accounts/light/signerToLightSmartAccount.ts deleted file mode 100644 index 8f043ad5..00000000 --- a/packages/permissionless/accounts/light/signerToLightSmartAccount.ts +++ /dev/null @@ -1,418 +0,0 @@ -import { - type Address, - type Chain, - type Client, - type Hex, - type LocalAccount, - type PublicActions, - type PublicRpcSchema, - type Transport, - type TypedData, - type TypedDataDefinition, - concatHex, - encodeFunctionData, - hashMessage, - hashTypedData -} from "viem" -import { getChainId, signMessage } from "viem/actions" -import { getAccountNonce } from "../../actions/public/getAccountNonce" -import { getSenderAddress } from "../../actions/public/getSenderAddress" -import type { - ENTRYPOINT_ADDRESS_V06_TYPE, - ENTRYPOINT_ADDRESS_V07_TYPE, - Prettify -} from "../../types" -import type { EntryPoint } from "../../types/entrypoint" -import { getEntryPointVersion } from "../../utils" -import { getUserOperationHash } from "../../utils/getUserOperationHash" -import { isSmartAccountDeployed } from "../../utils/isSmartAccountDeployed" -import { toSmartAccount } from "../toSmartAccount" -import { - SignTransactionNotSupportedBySmartAccount, - type SmartAccount, - type SmartAccountSigner -} from "../types" - -export type LightSmartAccount< - entryPoint extends EntryPoint, - transport extends Transport = Transport, - chain extends Chain | undefined = Chain | undefined -> = SmartAccount - -const getAccountInitCode = async ( - owner: Address, - index = BigInt(0) -): Promise => { - if (!owner) throw new Error("Owner account not found") - - return encodeFunctionData({ - abi: [ - { - inputs: [ - { - internalType: "address", - name: "owner", - type: "address" - }, - { - internalType: "uint256", - name: "salt", - type: "uint256" - } - ], - name: "createAccount", - outputs: [ - { - internalType: "contract LightAccount", - name: "ret", - type: "address" - } - ], - stateMutability: "nonpayable", - type: "function" - } - ], - functionName: "createAccount", - args: [owner, index] - }) -} - -const getAccountAddress = async < - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined ->({ - client, - factoryAddress, - entryPoint: entryPointAddress, - owner, - index = BigInt(0) -}: { - client: Client - factoryAddress: Address - owner: Address - entryPoint: entryPoint - index?: bigint -}): Promise
=> { - const entryPointVersion = getEntryPointVersion(entryPointAddress) - - const factoryData = await getAccountInitCode(owner, index) - - if (entryPointVersion === "v0.6") { - return getSenderAddress(client, { - initCode: concatHex([factoryAddress, factoryData]), - entryPoint: entryPointAddress as ENTRYPOINT_ADDRESS_V06_TYPE - }) - } - - // Get the sender address based on the init code - return getSenderAddress(client, { - factory: factoryAddress, - factoryData, - entryPoint: entryPointAddress as ENTRYPOINT_ADDRESS_V07_TYPE - }) -} - -export type LightAccountVersion = "1.1.0" - -export type SignerToLightSmartAccountParameters< - entryPoint extends EntryPoint, - TSource extends string = string, - TAddress extends Address = Address -> = Prettify<{ - signer: SmartAccountSigner - lightAccountVersion: LightAccountVersion - entryPoint: entryPoint - factoryAddress?: Address - index?: bigint - address?: Address - nonceKey?: bigint -}> - -async function signWith1271WrapperV1< - TSource extends string = string, - TAddress extends Address = Address ->( - signer: SmartAccountSigner, - chainId: number, - accountAddress: Address, - hashedMessage: Hex -): Promise { - return signer.signTypedData({ - domain: { - chainId: Number(chainId), - name: "LightAccount", - verifyingContract: accountAddress, - version: "1" - }, - types: { - LightAccountMessage: [{ name: "message", type: "bytes" }] - }, - message: { - message: hashedMessage - }, - primaryType: "LightAccountMessage" - }) -} - -const LIGHT_VERSION_TO_ADDRESSES_MAP: { - [key in LightAccountVersion]: { - factoryAddress: Address - } -} = { - "1.1.0": { - factoryAddress: "0x00004EC70002a32400f8ae005A26081065620D20" - } -} - -const getDefaultAddresses = ( - lightAccountVersion: LightAccountVersion, - { - factoryAddress: _factoryAddress - }: { - factoryAddress?: Address - } -) => { - const factoryAddress = - _factoryAddress ?? - LIGHT_VERSION_TO_ADDRESSES_MAP[lightAccountVersion].factoryAddress - - return { - factoryAddress - } -} - -/** - * @description Creates an Light Account from a private key. - * - * @returns A Private Key Light Account. - */ -export async function signerToLightSmartAccount< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSource extends string = string, - TAddress extends Address = Address ->( - client: Client< - TTransport, - TChain, - undefined, - PublicRpcSchema, - PublicActions - >, - { - signer, - address, - lightAccountVersion, - entryPoint: entryPointAddress, - index = BigInt(0), - factoryAddress: _factoryAddress, - nonceKey - }: SignerToLightSmartAccountParameters -): Promise> { - const viemSigner: LocalAccount = { - ...signer, - signTransaction: (_, __) => { - throw new SignTransactionNotSupportedBySmartAccount() - } - } as LocalAccount - - if (lightAccountVersion !== "1.1.0") { - throw new Error( - "Only Light Account version 1.1.0 is supported at the moment" - ) - } - - const { factoryAddress } = getDefaultAddresses(lightAccountVersion, { - factoryAddress: _factoryAddress - }) - - const [accountAddress, chainId] = await Promise.all([ - address ?? - getAccountAddress({ - client, - factoryAddress, - entryPoint: entryPointAddress, - owner: viemSigner.address, - index - }), - client.chain?.id ?? getChainId(client) - ]) - - if (!accountAddress) throw new Error("Account address not found") - - let smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) - - return toSmartAccount({ - address: accountAddress, - signMessage: async ({ message }) => { - return signWith1271WrapperV1( - signer, - chainId, - accountAddress, - hashMessage(message) - ) - }, - signTransaction: (_, __) => { - throw new SignTransactionNotSupportedBySmartAccount() - }, - async signTypedData< - const TTypedData extends TypedData | Record, - TPrimaryType extends - | keyof TTypedData - | "EIP712Domain" = keyof TTypedData - >(typedData: TypedDataDefinition) { - return signWith1271WrapperV1( - signer, - chainId, - accountAddress, - hashTypedData(typedData) - ) - }, - client: client, - publicKey: accountAddress, - entryPoint: entryPointAddress, - source: "LightSmartAccount", - async getNonce(key?: bigint) { - return getAccountNonce(client, { - sender: accountAddress, - entryPoint: entryPointAddress, - key: key ?? nonceKey - }) - }, - async signUserOperation(userOperation) { - return signMessage(client, { - account: viemSigner, - message: { - raw: getUserOperationHash({ - userOperation, - entryPoint: entryPointAddress, - chainId: chainId - }) - } - }) - }, - async getInitCode() { - if (smartAccountDeployed) return "0x" - - smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) - - if (smartAccountDeployed) return "0x" - - return concatHex([ - factoryAddress, - await getAccountInitCode(viemSigner.address, index) - ]) - }, - async getFactory() { - if (smartAccountDeployed) return undefined - smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) - if (smartAccountDeployed) return undefined - return factoryAddress - }, - async getFactoryData() { - if (smartAccountDeployed) return undefined - smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) - if (smartAccountDeployed) return undefined - return getAccountInitCode(viemSigner.address, index) - }, - async encodeDeployCallData(_) { - throw new Error("Light account doesn't support account deployment") - }, - async encodeCallData(args) { - if (Array.isArray(args)) { - const argsArray = args as { - to: Address - value: bigint - data: Hex - }[] - - return encodeFunctionData({ - abi: [ - { - inputs: [ - { - internalType: "address[]", - name: "dest", - type: "address[]" - }, - { - internalType: "uint256[]", - name: "value", - type: "uint256[]" - }, - { - internalType: "bytes[]", - name: "func", - type: "bytes[]" - } - ], - name: "executeBatch", - outputs: [], - stateMutability: "nonpayable", - type: "function" - } - ], - functionName: "executeBatch", - args: [ - argsArray.map((a) => a.to), - argsArray.map((a) => a.value), - argsArray.map((a) => a.data) - ] - }) - } - - const { to, value, data } = args as { - to: Address - value: bigint - data: Hex - } - - return encodeFunctionData({ - abi: [ - { - inputs: [ - { - internalType: "address", - name: "dest", - type: "address" - }, - { - internalType: "uint256", - name: "value", - type: "uint256" - }, - { - internalType: "bytes", - name: "func", - type: "bytes" - } - ], - name: "execute", - outputs: [], - stateMutability: "nonpayable", - type: "function" - } - ], - functionName: "execute", - args: [to, value, data] - }) - }, - async getDummySignature(_userOperation) { - return "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c" - } - }) -} diff --git a/packages/permissionless/accounts/light/toLightSmartAccount.ts b/packages/permissionless/accounts/light/toLightSmartAccount.ts new file mode 100644 index 00000000..3dffd840 --- /dev/null +++ b/packages/permissionless/accounts/light/toLightSmartAccount.ts @@ -0,0 +1,396 @@ +import { + type Account, + type Address, + type Assign, + type Chain, + type Client, + type EIP1193Provider, + type Hex, + type LocalAccount, + type OneOf, + type Transport, + type WalletClient, + concat, + encodeFunctionData, + hashMessage, + hashTypedData +} from "viem" +import { + type SmartAccount, + type SmartAccountImplementation, + type UserOperation, + entryPoint06Abi, + entryPoint07Abi, + entryPoint07Address, + getUserOperationHash, + toSmartAccount +} from "viem/account-abstraction" +import { getChainId, signMessage } from "viem/actions" +import { getAction } from "viem/utils" +import { getAccountNonce } from "../../actions/public/getAccountNonce" +import { getSenderAddress } from "../../actions/public/getSenderAddress" +import { toOwner } from "../../utils/toOwner" + +const getAccountInitCode = async ( + owner: Address, + index = BigInt(0) +): Promise => { + if (!owner) throw new Error("Owner account not found") + + return encodeFunctionData({ + abi: [ + { + inputs: [ + { + internalType: "address", + name: "owner", + type: "address" + }, + { + internalType: "uint256", + name: "salt", + type: "uint256" + } + ], + name: "createAccount", + outputs: [ + { + internalType: "contract LightAccount", + name: "ret", + type: "address" + } + ], + stateMutability: "nonpayable", + type: "function" + } + ], + functionName: "createAccount", + args: [owner, index] + }) +} + +export type LightAccountVersion = + entryPointVersion extends "0.6" ? "1.1.0" : "2.0.0" + +export type ToLightSmartAccountParameters< + entryPointVersion extends "0.6" | "0.7" = "0.7" +> = { + client: Client + entryPoint?: { + address: Address + version: entryPointVersion + } + owner: OneOf< + | EIP1193Provider + | WalletClient + | LocalAccount + > + version: LightAccountVersion + factoryAddress?: Address + index?: bigint + address?: Address + nonceKey?: bigint +} + +async function signWith1271WrapperV1( + signer: LocalAccount, + chainId: number, + accountAddress: Address, + hashedMessage: Hex +): Promise { + return signer.signTypedData({ + domain: { + chainId: Number(chainId), + name: "LightAccount", + verifyingContract: accountAddress, + version: "1" + }, + types: { + LightAccountMessage: [{ name: "message", type: "bytes" }] + }, + message: { + message: hashedMessage + }, + primaryType: "LightAccountMessage" + }) +} + +const LIGHT_VERSION_TO_ADDRESSES_MAP: { + [key in LightAccountVersion<"0.6" | "0.7">]: { + factoryAddress: Address + } +} = { + "1.1.0": { + factoryAddress: "0x00004EC70002a32400f8ae005A26081065620D20" + }, + "2.0.0": { + factoryAddress: "0x0000000000400CdFef5E2714E63d8040b700BC24" + } +} + +const getDefaultAddresses = ( + lightAccountVersion: LightAccountVersion<"0.6" | "0.7">, + { + factoryAddress: _factoryAddress + }: { + factoryAddress?: Address + } +) => { + const factoryAddress = + _factoryAddress ?? + LIGHT_VERSION_TO_ADDRESSES_MAP[lightAccountVersion].factoryAddress + + return { + factoryAddress + } +} + +export type LightSmartAccountImplementation< + entryPointVersion extends "0.6" | "0.7" +> = Assign< + SmartAccountImplementation< + entryPointVersion extends "0.6" + ? typeof entryPoint06Abi + : typeof entryPoint07Abi, + entryPointVersion + >, + { sign: NonNullable } +> + +export type ToLightSmartAccountReturnType< + entryPointVersion extends "0.6" | "0.7" = "0.7" +> = SmartAccount> + +enum SignatureType { + EOA = "0x00" + // CONTRACT = "0x01", + // CONTRACT_WITH_ADDR = "0x02" +} + +/** + * @description Creates an Light Account from a private key. + * + * @returns A Private Key Light Account. + */ +export async function toLightSmartAccount< + entryPointVersion extends "0.6" | "0.7" = "0.7" +>( + parameters: ToLightSmartAccountParameters +): Promise> { + const { + version, + factoryAddress: _factoryAddress, + address, + owner, + client, + index = BigInt(0), + nonceKey + } = parameters + + const localOwner = await toOwner({ owner }) + + const entryPoint = { + address: parameters.entryPoint?.address ?? entryPoint07Address, + abi: + (parameters.entryPoint?.version ?? "0.7") === "0.6" + ? entryPoint06Abi + : entryPoint07Abi, + version: parameters.entryPoint?.version ?? "0.7" + } as const + + const { factoryAddress } = getDefaultAddresses(version, { + factoryAddress: _factoryAddress + }) + + let accountAddress: Address | undefined = address + + let chainId: number + + const getMemoizedChainId = async () => { + if (chainId) return chainId + chainId = client.chain + ? client.chain.id + : await getAction(client, getChainId, "getChainId")({}) + return chainId + } + + const getFactoryArgs = async () => { + return { + factory: factoryAddress, + factoryData: await getAccountInitCode(localOwner.address, index) + } + } + + return toSmartAccount({ + client, + entryPoint, + getFactoryArgs, + async getAddress() { + if (accountAddress) return accountAddress + + const { factory, factoryData } = await getFactoryArgs() + + accountAddress = await getSenderAddress(client, { + factory, + factoryData, + entryPointAddress: entryPoint.address + }) + + return accountAddress + }, + async encodeCalls(calls) { + if (calls.length > 1) { + return encodeFunctionData({ + abi: [ + { + inputs: [ + { + internalType: "address[]", + name: "dest", + type: "address[]" + }, + { + internalType: "uint256[]", + name: "value", + type: "uint256[]" + }, + { + internalType: "bytes[]", + name: "func", + type: "bytes[]" + } + ], + name: "executeBatch", + outputs: [], + stateMutability: "nonpayable", + type: "function" + } + ], + functionName: "executeBatch", + args: [ + calls.map((a) => a.to), + calls.map((a) => a.value ?? 0n), + calls.map((a) => a.data ?? "0x") + ] + }) + } + + return encodeFunctionData({ + abi: [ + { + inputs: [ + { + internalType: "address", + name: "dest", + type: "address" + }, + { + internalType: "uint256", + name: "value", + type: "uint256" + }, + { + internalType: "bytes", + name: "func", + type: "bytes" + } + ], + name: "execute", + outputs: [], + stateMutability: "nonpayable", + type: "function" + } + ], + functionName: "execute", + args: [calls[0].to, calls[0].value ?? 0n, calls[0].data ?? "0x"] + }) + }, + async getNonce(args) { + return getAccountNonce(client, { + address: await this.getAddress(), + entryPointAddress: entryPoint.address, + key: args?.key ?? nonceKey + }) + }, + async getStubSignature() { + const signature = + "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c" + + switch (version) { + case "1.1.0": + return signature + case "2.0.0": + return concat([SignatureType.EOA, signature]) + default: + throw new Error("Unknown Light Account version") + } + }, + async sign({ hash }) { + return this.signMessage({ message: hash }) + }, + async signMessage({ message }) { + const signature = await signWith1271WrapperV1( + localOwner, + await getMemoizedChainId(), + await this.getAddress(), + hashMessage(message) + ) + + switch (version) { + case "1.1.0": + return signature + case "2.0.0": + return concat([SignatureType.EOA, signature]) + default: + throw new Error("Unknown Light Account version") + } + }, + async signTypedData(typedData) { + const signature = await signWith1271WrapperV1( + localOwner, + await getMemoizedChainId(), + await this.getAddress(), + hashTypedData(typedData) + ) + + switch (version) { + case "1.1.0": + return signature + case "2.0.0": + return concat([SignatureType.EOA, signature]) + default: + throw new Error("Unknown Light Account version") + } + }, + async signUserOperation(parameters) { + const { chainId = await getMemoizedChainId(), ...userOperation } = + parameters + + const hash = getUserOperationHash({ + userOperation: { + ...userOperation, + signature: "0x" + } as UserOperation, + entryPointAddress: entryPoint.address, + entryPointVersion: entryPoint.version, + chainId: chainId + }) + + const signature = await signMessage(client, { + account: localOwner, + message: { + raw: hash + } + }) + + switch (version) { + case "1.1.0": + return signature + case "2.0.0": + return concat([SignatureType.EOA, signature]) + default: + throw new Error("Unknown Light Account version") + } + } + }) as Promise> +} diff --git a/packages/permissionless/accounts/safe/privateKeyToSafeSmartAccount.ts b/packages/permissionless/accounts/safe/privateKeyToSafeSmartAccount.ts deleted file mode 100644 index 8404f27e..00000000 --- a/packages/permissionless/accounts/safe/privateKeyToSafeSmartAccount.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { - Chain, - Client, - Hex, - PublicActions, - PublicRpcSchema, - Transport -} from "viem" -import { privateKeyToAccount } from "viem/accounts" -import type { EntryPoint, Prettify } from "../../types" -import { - type SafeSmartAccount, - type SignerToSafeSmartAccountParameters, - signerToSafeSmartAccount -} from "./signerToSafeSmartAccount" - -export type PrivateKeyToSafeSmartAccountParameters< - entryPoint extends EntryPoint -> = Prettify< - { - privateKey: Hex - } & Omit, "signer"> -> - -/** - * @description Creates an Simple Account from a private key. - * - * @returns A Private Key Simple Account. - */ -export async function privateKeyToSafeSmartAccount< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined ->( - client: Client< - TTransport, - TChain, - undefined, - PublicRpcSchema, - PublicActions - >, - { privateKey, ...rest }: PrivateKeyToSafeSmartAccountParameters -): Promise> { - const privateKeyAccount = privateKeyToAccount(privateKey) - - return signerToSafeSmartAccount(client, { - signer: privateKeyAccount, - ...rest - }) -} diff --git a/packages/permissionless/accounts/safe/signerToSafeSmartAccount.ts b/packages/permissionless/accounts/safe/toSafeSmartAccount.ts similarity index 65% rename from packages/permissionless/accounts/safe/signerToSafeSmartAccount.ts rename to packages/permissionless/accounts/safe/toSafeSmartAccount.ts index 4f4832cb..10b07cf7 100644 --- a/packages/permissionless/accounts/safe/signerToSafeSmartAccount.ts +++ b/packages/permissionless/accounts/safe/toSafeSmartAccount.ts @@ -1,15 +1,18 @@ import { + type Account, type Address, + type Assign, type Chain, type Client, + type EIP1193Provider, type Hex, type LocalAccount, - type PublicActions, - type PublicRpcSchema, + type OneOf, type SignableMessage, type Transport, type TypedData, type TypedDataDefinition, + type WalletClient, concat, concatHex, encodeAbiParameters, @@ -26,27 +29,19 @@ import { zeroAddress } from "viem" import { - getChainId, - readContract, - signMessage, - signTypedData -} from "viem/actions" -import { getAccountNonce } from "../../actions/public/getAccountNonce" -import type { EntryPointVersion, GetEntryPointVersion } from "../../types" -import type { EntryPoint, UserOperation } from "../../types" -import { encode7579CallData } from "../../utils/encode7579CallData" -import { - getEntryPointVersion, - isUserOperationVersion06, - isUserOperationVersion07 -} from "../../utils/getEntryPointVersion" -import { isSmartAccountDeployed } from "../../utils/isSmartAccountDeployed" -import { toSmartAccount } from "../toSmartAccount" -import { - SignTransactionNotSupportedBySmartAccount, type SmartAccount, - type SmartAccountSigner -} from "../types" + type SmartAccountImplementation, + type UserOperation, + entryPoint06Abi, + entryPoint07Abi, + entryPoint07Address, + toSmartAccount +} from "viem/account-abstraction" +import { getChainId, readContract, signTypedData } from "viem/actions" +import { getAction } from "viem/utils" +import { getAccountNonce } from "../../actions/public/getAccountNonce" +import { isSmartAccountDeployed, toOwner } from "../../utils" +import { encode7579Calls } from "../../utils/encode7579Calls" export type SafeVersion = "1.4.1" @@ -268,22 +263,6 @@ const createProxyWithNonceAbi = [ } ] as const -const proxyCreationCodeAbi = [ - { - inputs: [], - name: "proxyCreationCode", - outputs: [ - { - internalType: "bytes", - name: "", - type: "bytes" - } - ], - stateMutability: "pure", - type: "function" - } -] as const - const setupSafeAbi = [ { type: "function", @@ -423,7 +402,7 @@ const EIP712_SAFE_OPERATION_TYPE_V07 = { const SAFE_VERSION_TO_ADDRESSES_MAP: { [key in SafeVersion]: { - [key in EntryPointVersion]: { + [key in "0.6" | "0.7"]: { SAFE_MODULE_SETUP_ADDRESS: Address SAFE_4337_MODULE_ADDRESS: Address SAFE_PROXY_FACTORY_ADDRESS: Address @@ -434,7 +413,7 @@ const SAFE_VERSION_TO_ADDRESSES_MAP: { } } = { "1.4.1": { - "v0.6": { + "0.6": { SAFE_MODULE_SETUP_ADDRESS: "0x8EcD4ec46D4D2a6B64fE960B3D64e8B94B2234eb", SAFE_4337_MODULE_ADDRESS: @@ -447,7 +426,7 @@ const SAFE_VERSION_TO_ADDRESSES_MAP: { MULTI_SEND_CALL_ONLY_ADDRESS: "0x9641d764fc13c8B624c04430C7356C1C7C8102e2" }, - "v0.7": { + "0.7": { SAFE_MODULE_SETUP_ADDRESS: "0x2dd68b007B46fBe91B9A7c3EDa5A7a1063cB5b47", SAFE_4337_MODULE_ADDRESS: @@ -542,12 +521,6 @@ const encodeMultiSend = ( }) } -export type SafeSmartAccount< - entryPoint extends EntryPoint, - transport extends Transport = Transport, - chain extends Chain | undefined = Chain | undefined -> = SmartAccount - const get7579LaunchPadInitData = ({ safe4337ModuleAddress, safeSingletonAddress, @@ -614,9 +587,6 @@ const getInitializerCode = async ({ multiSendAddress, safeSingletonAddress, erc7579LaunchpadAddress, - paymentToken, - payment, - paymentReceiver, setupTransactions = [], safeModules = [], validators = [], @@ -624,16 +594,16 @@ const getInitializerCode = async ({ fallbacks = [], hooks = [], attesters = [], - attestersThreshold = 0 + attestersThreshold = 0, + paymentToken = zeroAddress, + payment = BigInt(0), + paymentReceiver = zeroAddress }: { owner: Address safeSingletonAddress: Address safeModuleSetupAddress: Address safe4337ModuleAddress: Address multiSendAddress: Address - paymentToken: Address - payment: bigint - paymentReceiver: Address erc7579LaunchpadAddress?: Address setupTransactions?: { to: Address @@ -650,6 +620,9 @@ const getInitializerCode = async ({ hooks?: { address: Address; context: Address }[] attesters?: Address[] attestersThreshold?: number + paymentToken?: Address + payment?: bigint + paymentReceiver?: Address }) => { if (erc7579LaunchpadAddress) { const initData = get7579LaunchPadInitData({ @@ -768,7 +741,7 @@ const getInitializerCode = async ({ }) } -function getPaymasterAndData(unpackedUserOperation: UserOperation<"v0.7">) { +function getPaymasterAndData(unpackedUserOperation: UserOperation) { return unpackedUserOperation.paymaster ? concat([ unpackedUserOperation.paymaster, @@ -819,9 +792,6 @@ const getAccountInitCode = async ({ safe4337ModuleAddress: Address safeSingletonAddress: Address multiSendAddress: Address - paymentToken: Address - payment: bigint - paymentReceiver: Address erc7579LaunchpadAddress?: Address saltNonce?: bigint setupTransactions?: { @@ -839,6 +809,9 @@ const getAccountInitCode = async ({ hooks?: { address: Address; context: Address }[] attesters?: Address[] attestersThreshold?: number + paymentToken?: Address + payment?: bigint + paymentReceiver?: Address }): Promise => { if (!owner) { throw new Error("Owner account not found") @@ -877,111 +850,9 @@ const getAccountInitCode = async ({ return initCodeCallData } -const getAccountAddress = async < - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined ->({ - client, - owner, - safeModuleSetupAddress, - safe4337ModuleAddress, - safeProxyFactoryAddress, - safeSingletonAddress, - multiSendAddress, - erc7579LaunchpadAddress, - paymentToken, - payment, - paymentReceiver, - setupTransactions = [], - safeModules = [], - saltNonce = BigInt(0), - validators = [], - executors = [], - fallbacks = [], - hooks = [], - attesters = [], - attestersThreshold = 0 -}: { - client: Client - owner: Address - safeModuleSetupAddress: Address - safe4337ModuleAddress: Address - safeProxyFactoryAddress: Address - safeSingletonAddress: Address - multiSendAddress: Address - setupTransactions: { - to: Address - data: Address - value: bigint - }[] - paymentToken: Address - payment: bigint - paymentReceiver: Address - safeModules?: Address[] - saltNonce?: bigint - erc7579LaunchpadAddress?: Address - validators?: { address: Address; context: Address }[] - executors?: { - address: Address - context: Address - }[] - fallbacks?: { address: Address; context: Address }[] - hooks?: { address: Address; context: Address }[] - attesters?: Address[] - attestersThreshold?: number -}): Promise
=> { - const proxyCreationCode = await readContract(client, { - abi: proxyCreationCodeAbi, - address: safeProxyFactoryAddress, - functionName: "proxyCreationCode" - }) - - const initializer = await getInitializerCode({ - owner, - safeModuleSetupAddress, - safe4337ModuleAddress, - multiSendAddress, - setupTransactions, - safeSingletonAddress, - safeModules, - erc7579LaunchpadAddress, - validators, - executors, - fallbacks, - hooks, - attesters, - attestersThreshold, - paymentToken, - payment, - paymentReceiver - }) - - const deploymentCode = encodePacked( - ["bytes", "uint256"], - [ - proxyCreationCode, - hexToBigInt(erc7579LaunchpadAddress ?? safeSingletonAddress) - ] - ) - - const salt = keccak256( - encodePacked( - ["bytes32", "uint256"], - [keccak256(encodePacked(["bytes"], [initializer])), saltNonce] - ) - ) - - return getContractAddress({ - from: safeProxyFactoryAddress, - salt, - bytecode: deploymentCode, - opcode: "CREATE2" - }) -} - const getDefaultAddresses = ( safeVersion: SafeVersion, - entryPointAddress: EntryPoint, + entryPointVersion: "0.6" | "0.7", { addModuleLibAddress: _addModuleLibAddress, safeModuleSetupAddress: _safeModuleSetupAddress, @@ -1000,8 +871,6 @@ const getDefaultAddresses = ( multiSendCallOnlyAddress?: Address } ) => { - const entryPointVersion = getEntryPointVersion(entryPointAddress) - const safeModuleSetupAddress = _safeModuleSetupAddress ?? _addModuleLibAddress ?? @@ -1026,9 +895,8 @@ const getDefaultAddresses = ( const multiSendCallOnlyAddress = _multiSendCallOnlyAddress ?? - SAFE_VERSION_TO_ADDRESSES_MAP[safeVersion][ - getEntryPointVersion(entryPointAddress) - ].MULTI_SEND_CALL_ONLY_ADDRESS + SAFE_VERSION_TO_ADDRESSES_MAP[safeVersion][entryPointVersion] + .MULTI_SEND_CALL_ONLY_ADDRESS return { safeModuleSetupAddress, @@ -1065,15 +933,23 @@ type GetErc7579Params = attestersThreshold?: number } -export type SignerToSafeSmartAccountParameters< - entryPoint extends EntryPoint, - TSource extends string = string, - TAddress extends Address = Address, - TErc7579 extends Address | undefined = Address | undefined +export type ToSafeSmartAccountParameters< + entryPointVersion extends "0.6" | "0.7", + TErc7579 extends Address | undefined > = { - signer: SmartAccountSigner - safeVersion: SafeVersion - entryPoint: entryPoint + client: Client + owners: [ + OneOf< + | EIP1193Provider + | WalletClient + | LocalAccount + > + ] + version: SafeVersion + entryPoint?: { + address: Address + version: entryPointVersion + } safe4337ModuleAddress?: Address erc7569LaunchpadAddress?: Address erc7579LaunchpadAddress?: TErc7579 @@ -1089,68 +965,187 @@ export type SignerToSafeSmartAccountParameters< paymentReceiver?: Address } & GetErc7579Params -function isErc7579Args( - args: SignerToSafeSmartAccountParameters< - EntryPoint, - string, - Address, - Address | undefined - > -): args is SignerToSafeSmartAccountParameters< - EntryPoint, - string, - Address, - Address -> { +function isErc7579Args( + args: ToSafeSmartAccountParameters +): args is ToSafeSmartAccountParameters { return args.erc7579LaunchpadAddress !== undefined } +const proxyCreationCodeAbi = [ + { + inputs: [], + name: "proxyCreationCode", + outputs: [ + { + internalType: "bytes", + name: "", + type: "bytes" + } + ], + stateMutability: "pure", + type: "function" + } +] as const + +const getAccountAddress = async ({ + client, + owner, + safeModuleSetupAddress, + safe4337ModuleAddress, + safeProxyFactoryAddress, + safeSingletonAddress, + multiSendAddress, + erc7579LaunchpadAddress, + paymentToken, + payment, + paymentReceiver, + setupTransactions = [], + safeModules = [], + saltNonce = BigInt(0), + validators = [], + executors = [], + fallbacks = [], + hooks = [], + attesters = [], + attestersThreshold = 0 +}: { + client: Client + owner: Address + safeModuleSetupAddress: Address + safe4337ModuleAddress: Address + safeProxyFactoryAddress: Address + safeSingletonAddress: Address + multiSendAddress: Address + setupTransactions: { + to: Address + data: Address + value: bigint + }[] + paymentToken?: Address + payment?: bigint + paymentReceiver?: Address + safeModules?: Address[] + saltNonce?: bigint + erc7579LaunchpadAddress?: Address + validators?: { address: Address; context: Address }[] + executors?: { + address: Address + context: Address + }[] + fallbacks?: { address: Address; context: Address }[] + hooks?: { address: Address; context: Address }[] + attesters?: Address[] + attestersThreshold?: number +}): Promise
=> { + const proxyCreationCode = await readContract(client, { + abi: proxyCreationCodeAbi, + address: safeProxyFactoryAddress, + functionName: "proxyCreationCode" + }) + + const initializer = await getInitializerCode({ + owner, + safeModuleSetupAddress, + safe4337ModuleAddress, + multiSendAddress, + setupTransactions, + safeSingletonAddress, + safeModules, + erc7579LaunchpadAddress, + validators, + executors, + fallbacks, + hooks, + attesters, + attestersThreshold, + paymentToken, + payment, + paymentReceiver + }) + + const deploymentCode = encodePacked( + ["bytes", "uint256"], + [ + proxyCreationCode, + hexToBigInt(erc7579LaunchpadAddress ?? safeSingletonAddress) + ] + ) + + const salt = keccak256( + encodePacked( + ["bytes32", "uint256"], + [keccak256(encodePacked(["bytes"], [initializer])), saltNonce] + ) + ) + + return getContractAddress({ + from: safeProxyFactoryAddress, + salt, + bytecode: deploymentCode, + opcode: "CREATE2" + }) +} + +export type SafeSmartAccountImplementation< + entryPointVersion extends "0.6" | "0.7" = "0.7" +> = Assign< + SmartAccountImplementation< + entryPointVersion extends "0.6" + ? typeof entryPoint06Abi + : typeof entryPoint07Abi, + entryPointVersion + // { + // // entryPoint === ENTRYPOINT_ADDRESS_V06 ? "0.2.2" : "0.3.0-beta" + // abi: entryPointVersion extends "0.6" ? typeof BiconomyAbi + // factory: { abi: typeof FactoryAbi; address: Address } + // } + >, + { sign: NonNullable } +> + +export type ToSafeSmartAccountReturnType< + entryPointVersion extends "0.6" | "0.7" = "0.7" +> = SmartAccount> + /** * @description Creates an Simple Account from a private key. * * @returns A Private Key Simple Account. */ -export async function signerToSafeSmartAccount< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSource extends string = string, - TAddress extends Address = Address, - TErc7579 extends Address | undefined = undefined +export async function toSafeSmartAccount< + entryPointVersion extends "0.6" | "0.7", + TErc7579 extends Address | undefined >( - client: Client< - TTransport, - TChain, - undefined, - PublicRpcSchema, - PublicActions - >, - args: SignerToSafeSmartAccountParameters< - entryPoint, - TSource, - TAddress, - TErc7579 - > -): Promise> { - const chainId = client.chain?.id ?? (await getChainId(client)) - + parameters: ToSafeSmartAccountParameters +): Promise> { const { - signer, + client, + owners, address, - safeVersion, - entryPoint: entryPointAddress, + version, safe4337ModuleAddress: _safe4337ModuleAddress, safeProxyFactoryAddress: _safeProxyFactoryAddress, safeSingletonAddress: _safeSingletonAddress, - erc7579LaunchpadAddress = args.erc7579LaunchpadAddress, + erc7579LaunchpadAddress, saltNonce = BigInt(0), validUntil = 0, validAfter = 0, nonceKey, - paymentToken = zeroAddress, - payment = BigInt(0), - paymentReceiver = zeroAddress - } = args + paymentToken, + payment, + paymentReceiver + } = parameters + + const localOwner = await toOwner({ owner: owners[0] }) + + const entryPoint = { + address: parameters.entryPoint?.address ?? entryPoint07Address, + abi: + (parameters.entryPoint?.version ?? "0.7") === "0.6" + ? entryPoint06Abi + : entryPoint07Abi, + version: parameters.entryPoint?.version ?? "0.7" + } as const let _safeModuleSetupAddress: Address | undefined = undefined let _multiSendAddress: Address | undefined = undefined @@ -1168,30 +1163,23 @@ export async function signerToSafeSmartAccount< let attesters: Address[] = [] let attestersThreshold = 0 - if (!isErc7579Args(args)) { - _safeModuleSetupAddress = args.safeModuleSetupAddress - _multiSendAddress = args.multiSendAddress - _multiSendCallOnlyAddress = args.multiSendCallOnlyAddress - safeModules = args.safeModules - setupTransactions = args.setupTransactions ?? [] + if (!isErc7579Args(parameters)) { + _safeModuleSetupAddress = parameters.safeModuleSetupAddress + _multiSendAddress = parameters.multiSendAddress + _multiSendCallOnlyAddress = parameters.multiSendCallOnlyAddress + safeModules = parameters.safeModules + setupTransactions = parameters.setupTransactions ?? [] } - if (isErc7579Args(args)) { - validators = args.validators ?? [] - executors = args.executors ?? [] - fallbacks = args.fallbacks ?? [] - hooks = args.hooks ?? [] - attesters = args.attesters ?? [] - attestersThreshold = args.attestersThreshold ?? 0 + if (isErc7579Args(parameters)) { + validators = parameters.validators ?? [] + executors = parameters.executors ?? [] + fallbacks = parameters.fallbacks ?? [] + hooks = parameters.hooks ?? [] + attesters = parameters.attesters ?? [] + attestersThreshold = parameters.attestersThreshold ?? 0 } - const viemSigner: LocalAccount = { - ...signer, - signTransaction: (_, __) => { - throw new SignTransactionNotSupportedBySmartAccount() - } - } as LocalAccount - const { safeModuleSetupAddress, safe4337ModuleAddress, @@ -1199,7 +1187,7 @@ export async function signerToSafeSmartAccount< safeSingletonAddress, multiSendAddress, multiSendCallOnlyAddress - } = getDefaultAddresses(safeVersion, entryPointAddress, { + } = getDefaultAddresses(version, entryPoint.version, { safeModuleSetupAddress: _safeModuleSetupAddress, safe4337ModuleAddress: _safe4337ModuleAddress, safeProxyFactoryAddress: _safeProxyFactoryAddress, @@ -1208,341 +1196,304 @@ export async function signerToSafeSmartAccount< multiSendCallOnlyAddress: _multiSendCallOnlyAddress }) - const accountAddress = - address ?? - (await getAccountAddress({ - client, - owner: viemSigner.address, - safeModuleSetupAddress, - safe4337ModuleAddress, - safeProxyFactoryAddress, - safeSingletonAddress, - multiSendAddress, - erc7579LaunchpadAddress, - saltNonce, - setupTransactions, - safeModules, - validators, - executors, - fallbacks, - hooks, - attesters, - attestersThreshold, - paymentToken, - payment, - paymentReceiver - })) + let accountAddress: Address | undefined = address - if (!accountAddress) throw new Error("Account address not found") + let chainId: number - let safeDeployed = await isSmartAccountDeployed(client, accountAddress) + const getMemoizedChainId = async () => { + if (chainId) return chainId + chainId = client.chain + ? client.chain.id + : await getAction(client, getChainId, "getChainId")({}) + return chainId + } - const safeSmartAccount: SafeSmartAccount = - toSmartAccount({ - address: accountAddress, - client: client, - publicKey: accountAddress, - entryPoint: entryPointAddress, - source: "SafeSmartAccount", - async signMessage({ message }) { - const messageHash = hashTypedData({ - domain: { - chainId: chainId, - verifyingContract: accountAddress - }, - types: { - SafeMessage: [{ name: "message", type: "bytes" }] - }, - primaryType: "SafeMessage", - message: { - message: generateSafeMessageMessage(message) - } - }) + const getFactoryArgs = async () => { + return { + factory: safeProxyFactoryAddress, + factoryData: await getAccountInitCode({ + owner: localOwner.address, + safeModuleSetupAddress, + safe4337ModuleAddress, + safeSingletonAddress, + multiSendAddress, + erc7579LaunchpadAddress, + saltNonce, + setupTransactions, + safeModules, + validators, + executors, + fallbacks, + hooks, + attesters, + attestersThreshold, + paymentToken, + payment, + paymentReceiver + }) + } + } - return adjustVInSignature( - "eth_sign", - await signMessage(client, { - account: viemSigner, - message: { - raw: toBytes(messageHash) - } - }) - ) - }, - async signTransaction(_, __) { - throw new SignTransactionNotSupportedBySmartAccount() - }, - async signTypedData< - const TTypedData extends TypedData | Record, - TPrimaryType extends - | keyof TTypedData - | "EIP712Domain" = keyof TTypedData - >(typedData: TypedDataDefinition) { - return adjustVInSignature( - "eth_signTypedData", - await signTypedData(client, { - account: viemSigner, - domain: { - chainId: chainId, - verifyingContract: accountAddress - }, - types: { - SafeMessage: [{ name: "message", type: "bytes" }] - }, - primaryType: "SafeMessage", - message: { - message: generateSafeMessageMessage< - TTypedData, - TPrimaryType - >(typedData) - } - }) + return toSmartAccount({ + client, + entryPoint, + getFactoryArgs, + async getAddress() { + if (accountAddress) return accountAddress + + // Get the sender address based on the init code + accountAddress = await getAccountAddress({ + client, + owner: localOwner.address, + safeModuleSetupAddress, + safe4337ModuleAddress, + safeProxyFactoryAddress, + safeSingletonAddress, + multiSendAddress, + erc7579LaunchpadAddress, + saltNonce, + setupTransactions, + safeModules, + validators, + executors, + fallbacks, + hooks, + attesters, + attestersThreshold, + paymentToken, + payment, + paymentReceiver + }) + + return accountAddress + }, + async encodeCalls(calls) { + const hasMultipleCalls = calls.length > 1 + + if (erc7579LaunchpadAddress) { + const safeDeployed = await isSmartAccountDeployed( + client, + await this.getAddress() ) - }, - async getNonce(key?: bigint) { - return getAccountNonce(client, { - sender: accountAddress, - entryPoint: entryPointAddress, - key: key ?? nonceKey - }) - }, - async signUserOperation( - userOperation: UserOperation> - ) { - const message = { - safe: accountAddress, - callData: userOperation.callData, - nonce: userOperation.nonce, - initCode: userOperation.initCode ?? "0x", - maxFeePerGas: userOperation.maxFeePerGas, - maxPriorityFeePerGas: userOperation.maxPriorityFeePerGas, - preVerificationGas: userOperation.preVerificationGas, - verificationGasLimit: userOperation.verificationGasLimit, - callGasLimit: userOperation.callGasLimit, - paymasterAndData: userOperation.paymasterAndData ?? "0x", - validAfter: validAfter, - validUntil: validUntil, - entryPoint: entryPointAddress - } - let isDeployed = false + if (!safeDeployed) { + const initData = get7579LaunchPadInitData({ + safe4337ModuleAddress, + safeSingletonAddress, + erc7579LaunchpadAddress, + owner: localOwner.address, + validators, + executors, + fallbacks, + hooks, + attesters, + attestersThreshold + }) - if ( - isUserOperationVersion06(entryPointAddress, userOperation) - ) { - message.paymasterAndData = userOperation.paymasterAndData - isDeployed = userOperation.initCode === "0x" + return encodeFunctionData({ + abi: setupSafeAbi, + functionName: "setupSafe", + args: [ + { + ...initData, + validators: initData.validators.map( + (validator) => ({ + module: validator.address, + initData: validator.context + }) + ), + callData: encode7579Calls({ + mode: { + type: hasMultipleCalls + ? "batchcall" + : "call", + revertOnError: false, + selector: "0x", + context: "0x" + }, + callData: calls + }) + } + ] + }) } - if ( - isUserOperationVersion07(entryPointAddress, userOperation) - ) { - if (userOperation.factory && userOperation.factoryData) { - message.initCode = concatHex([ - userOperation.factory, - userOperation.factoryData - ]) - } - message.paymasterAndData = - getPaymasterAndData(userOperation) - isDeployed = !userOperation.factory - } + return encode7579Calls({ + mode: { + type: hasMultipleCalls ? "batchcall" : "call", + revertOnError: false, + selector: "0x", + context: "0x" + }, + callData: calls + }) + } - let verifyingContract = safe4337ModuleAddress + let to: Address + let value: bigint + let data: Hex + let operationType = 0 + + if (hasMultipleCalls) { + to = multiSendCallOnlyAddress + value = BigInt(0) + + data = encodeMultiSend( + calls.map((tx) => ({ + to: tx.to, + value: tx.value ?? 0n, + data: tx.data ?? "0x", + operation: 0 + })) + ) + operationType = 1 + } else { + to = calls[0].to + data = calls[0].data ?? "0x" + value = calls[0].value ?? 0n + } - if (erc7579LaunchpadAddress && !isDeployed) { - verifyingContract = userOperation.sender + return encodeFunctionData({ + abi: executeUserOpWithErrorStringAbi, + functionName: "executeUserOpWithErrorString", + args: [to, value, data, operationType] + }) + }, + async getNonce(args) { + return getAccountNonce(client, { + address: await this.getAddress(), + entryPointAddress: entryPoint.address, + key: args?.key ?? nonceKey + }) + }, + async getStubSignature() { + return "0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + }, + async sign({ hash }) { + return this.signMessage({ message: hash }) + }, + async signMessage({ message }) { + const messageHash = hashTypedData({ + domain: { + chainId: await getMemoizedChainId(), + verifyingContract: await this.getAddress() + }, + types: { + SafeMessage: [{ name: "message", type: "bytes" }] + }, + primaryType: "SafeMessage", + message: { + message: generateSafeMessageMessage(message) } + }) - const signatures = [ - { - signer: viemSigner.address, - data: await signTypedData(client, { - account: viemSigner, - domain: { - chainId, - verifyingContract - }, - types: - getEntryPointVersion(entryPointAddress) === - "v0.6" - ? EIP712_SAFE_OPERATION_TYPE_V06 - : EIP712_SAFE_OPERATION_TYPE_V07, - primaryType: "SafeOp", - message: message - }) + return adjustVInSignature( + "eth_sign", + await localOwner.signMessage({ + message: { + raw: toBytes(messageHash) } - ] - - signatures.sort((left, right) => - left.signer - .toLowerCase() - .localeCompare(right.signer.toLowerCase()) - ) - - const signatureBytes = concat(signatures.map((sig) => sig.data)) + }) + ) + }, + async signTypedData(typedData) { + return adjustVInSignature( + "eth_signTypedData", + await localOwner.signTypedData({ + domain: { + chainId: await getMemoizedChainId(), + verifyingContract: await this.getAddress() + }, + types: { + SafeMessage: [{ name: "message", type: "bytes" }] + }, + primaryType: "SafeMessage", + message: { + message: generateSafeMessageMessage(typedData) + } + }) + ) + }, + async signUserOperation(parameters) { + const { chainId = await getMemoizedChainId(), ...userOperation } = + parameters + + const message = { + safe: await this.getAddress(), + callData: userOperation.callData, + nonce: userOperation.nonce, + initCode: userOperation.initCode ?? "0x", + maxFeePerGas: userOperation.maxFeePerGas, + maxPriorityFeePerGas: userOperation.maxPriorityFeePerGas, + preVerificationGas: userOperation.preVerificationGas, + verificationGasLimit: userOperation.verificationGasLimit, + callGasLimit: userOperation.callGasLimit, + paymasterAndData: userOperation.paymasterAndData ?? "0x", + validAfter: validAfter, + validUntil: validUntil, + entryPoint: entryPoint.address + } - return encodePacked( - ["uint48", "uint48", "bytes"], - [validAfter, validUntil, signatureBytes] - ) - }, - async getInitCode() { - safeDeployed = - safeDeployed || - (await isSmartAccountDeployed(client, accountAddress)) + let isDeployed = false - if (safeDeployed) return "0x" + if ("initCode" in userOperation) { + message.paymasterAndData = + userOperation.paymasterAndData ?? "0x" + isDeployed = userOperation.initCode === "0x" + } - return concatHex([ - (await this.getFactory()) ?? "0x", - (await this.getFactoryData()) ?? "0x" - ]) - }, - async getFactory() { - safeDeployed = - safeDeployed || - (await isSmartAccountDeployed(client, accountAddress)) + if ("factory" in userOperation) { + if (userOperation.factory && userOperation.factoryData) { + message.initCode = concatHex([ + userOperation.factory, + userOperation.factoryData + ]) + } + message.paymasterAndData = getPaymasterAndData({ + ...userOperation, + sender: userOperation.sender ?? (await this.getAddress()) + }) + isDeployed = !userOperation.factory + } - if (safeDeployed) return undefined + let verifyingContract = safe4337ModuleAddress - return safeProxyFactoryAddress - }, - async getFactoryData() { - safeDeployed = - safeDeployed || - (await isSmartAccountDeployed(client, accountAddress)) - - if (safeDeployed) return undefined - - return await getAccountInitCode({ - owner: viemSigner.address, - safeModuleSetupAddress, - safe4337ModuleAddress, - safeSingletonAddress, - multiSendAddress, - erc7579LaunchpadAddress, - saltNonce, - setupTransactions, - safeModules, - validators, - executors, - fallbacks, - hooks, - attesters, - attestersThreshold, - paymentToken, - payment, - paymentReceiver - }) - }, - async encodeDeployCallData(_) { - throw new Error( - "Safe account doesn't support account deployment" - ) - }, - async encodeCallData(args) { - const isArray = Array.isArray(args) - - if (erc7579LaunchpadAddress) { - // First transaction will be slower because we need to enable 7579 modules - safeDeployed = - safeDeployed || - (await isSmartAccountDeployed(client, accountAddress)) - - if (!safeDeployed) { - const initData = get7579LaunchPadInitData({ - safe4337ModuleAddress, - safeSingletonAddress, - erc7579LaunchpadAddress, - owner: viemSigner.address, - validators, - executors, - fallbacks, - hooks, - attesters, - attestersThreshold - }) - - return encodeFunctionData({ - abi: setupSafeAbi, - functionName: "setupSafe", - args: [ - { - ...initData, - validators: initData.validators.map( - (validator) => ({ - module: validator.address, - initData: validator.context - }) - ), - callData: encode7579CallData({ - mode: { - type: isArray - ? "batchcall" - : "call", - revertOnError: false, - selector: "0x", - context: "0x" - }, - callData: args - }) - } - ] - }) - } + if (erc7579LaunchpadAddress && !isDeployed) { + verifyingContract = + userOperation.sender ?? (await this.getAddress()) + } - return encode7579CallData({ - mode: { - type: isArray ? "batchcall" : "call", - revertOnError: false, - selector: "0x", - context: "0x" + const signatures = [ + { + signer: localOwner.address, + data: await signTypedData(client, { + account: localOwner, + domain: { + chainId, + verifyingContract }, - callData: args + types: + entryPoint.version === "0.6" + ? EIP712_SAFE_OPERATION_TYPE_V06 + : EIP712_SAFE_OPERATION_TYPE_V07, + primaryType: "SafeOp", + message: message }) } + ] - let to: Address - let value: bigint - let data: Hex - let operationType = 0 - - if (isArray) { - const argsArray = args as { - to: Address - value: bigint - data: Hex - }[] - - to = multiSendCallOnlyAddress - value = BigInt(0) - - data = encodeMultiSend( - argsArray.map((tx) => ({ ...tx, operation: 0 })) - ) - operationType = 1 - } else { - const singleTransaction = args as { - to: Address - value: bigint - data: Hex - } - to = singleTransaction.to - data = singleTransaction.data - value = singleTransaction.value - } + signatures.sort((left, right) => + left.signer + .toLowerCase() + .localeCompare(right.signer.toLowerCase()) + ) - return encodeFunctionData({ - abi: executeUserOpWithErrorStringAbi, - functionName: "executeUserOpWithErrorString", - args: [to, value, data, operationType] - }) - }, - async getDummySignature(_userOperation) { - return "0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - } - }) + const signatureBytes = concat(signatures.map((sig) => sig.data)) - return safeSmartAccount + return encodePacked( + ["uint48", "uint48", "bytes"], + [validAfter, validUntil, signatureBytes] + ) + } + }) as Promise> } diff --git a/packages/permissionless/accounts/simple/privateKeyToSimpleSmartAccount.ts b/packages/permissionless/accounts/simple/privateKeyToSimpleSmartAccount.ts deleted file mode 100644 index 927d47bc..00000000 --- a/packages/permissionless/accounts/simple/privateKeyToSimpleSmartAccount.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { - Address, - Chain, - Client, - Hex, - PublicActions, - PublicRpcSchema, - Transport -} from "viem" -import { privateKeyToAccount } from "viem/accounts" -import type { EntryPoint, Prettify } from "../../types" -import { - type SignerToSimpleSmartAccountParameters, - type SimpleSmartAccount, - signerToSimpleSmartAccount -} from "./signerToSimpleSmartAccount" - -export type PrivateKeyToSimpleSmartAccountParameters< - entryPoint extends EntryPoint -> = Prettify< - { - privateKey: Hex - } & Omit, "signer"> -> - -/** - * @description Creates an Simple Account from a private key. - * - * @returns A Private Key Simple Account. - */ -export async function privateKeyToSimpleSmartAccount< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined ->( - client: Client< - TTransport, - TChain, - undefined, - PublicRpcSchema, - PublicActions - >, - { - privateKey, - ...rest - }: PrivateKeyToSimpleSmartAccountParameters -): Promise> { - const privateKeyAccount = privateKeyToAccount(privateKey) - - return signerToSimpleSmartAccount< - entryPoint, - TTransport, - TChain, - "privateKey", - Address - >(client, { - signer: privateKeyAccount, - ...rest - }) -} diff --git a/packages/permissionless/accounts/simple/signerToSimpleSmartAccount.ts b/packages/permissionless/accounts/simple/signerToSimpleSmartAccount.ts deleted file mode 100644 index 7c987bf9..00000000 --- a/packages/permissionless/accounts/simple/signerToSimpleSmartAccount.ts +++ /dev/null @@ -1,374 +0,0 @@ -import { - type Address, - type Chain, - type Client, - type Hex, - type LocalAccount, - type PublicActions, - type PublicRpcSchema, - type Transport, - concatHex, - encodeFunctionData -} from "viem" -import { getChainId, signMessage } from "viem/actions" -import { getAccountNonce } from "../../actions/public/getAccountNonce" -import { getSenderAddress } from "../../actions/public/getSenderAddress" -import type { - ENTRYPOINT_ADDRESS_V06_TYPE, - ENTRYPOINT_ADDRESS_V07_TYPE, - Prettify -} from "../../types" -import type { EntryPoint } from "../../types/entrypoint" -import { getEntryPointVersion } from "../../utils" -import { getUserOperationHash } from "../../utils/getUserOperationHash" -import { isSmartAccountDeployed } from "../../utils/isSmartAccountDeployed" -import { toSmartAccount } from "../toSmartAccount" -import { - SignTransactionNotSupportedBySmartAccount, - type SmartAccount, - type SmartAccountSigner -} from "../types" - -export type SimpleSmartAccount< - entryPoint extends EntryPoint, - transport extends Transport = Transport, - chain extends Chain | undefined = Chain | undefined -> = SmartAccount - -const getAccountInitCode = async ( - owner: Address, - index = BigInt(0) -): Promise => { - if (!owner) throw new Error("Owner account not found") - - return encodeFunctionData({ - abi: [ - { - inputs: [ - { - internalType: "address", - name: "owner", - type: "address" - }, - { - internalType: "uint256", - name: "salt", - type: "uint256" - } - ], - name: "createAccount", - outputs: [ - { - internalType: "contract SimpleAccount", - name: "ret", - type: "address" - } - ], - stateMutability: "nonpayable", - type: "function" - } - ], - functionName: "createAccount", - args: [owner, index] - }) -} - -const getAccountAddress = async < - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined ->({ - client, - factoryAddress, - entryPoint: entryPointAddress, - owner, - index = BigInt(0) -}: { - client: Client - factoryAddress: Address - owner: Address - entryPoint: entryPoint - index?: bigint -}): Promise
=> { - const entryPointVersion = getEntryPointVersion(entryPointAddress) - - const factoryData = await getAccountInitCode(owner, index) - - if (entryPointVersion === "v0.6") { - return getSenderAddress(client, { - initCode: concatHex([factoryAddress, factoryData]), - entryPoint: entryPointAddress as ENTRYPOINT_ADDRESS_V06_TYPE - }) - } - - // Get the sender address based on the init code - return getSenderAddress(client, { - factory: factoryAddress, - factoryData, - entryPoint: entryPointAddress as ENTRYPOINT_ADDRESS_V07_TYPE - }) -} - -export type SignerToSimpleSmartAccountParameters< - entryPoint extends EntryPoint, - TSource extends string = string, - TAddress extends Address = Address -> = Prettify<{ - signer: SmartAccountSigner - factoryAddress?: Address - entryPoint: entryPoint - index?: bigint - address?: Address - nonceKey?: bigint -}> - -const getFactoryAddress = ( - entryPoint: EntryPoint, - factoryAddress?: Address -): Address => { - if (factoryAddress) return factoryAddress - if (getEntryPointVersion(entryPoint) === "v0.6") { - return "0x9406Cc6185a346906296840746125a0E44976454" - } - return "0x91E60e0613810449d098b0b5Ec8b51A0FE8c8985" -} - -/** - * @description Creates an Simple Account from a private key. - * - * @returns A Private Key Simple Account. - */ -export async function signerToSimpleSmartAccount< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSource extends string = string, - TAddress extends Address = Address ->( - client: Client< - TTransport, - TChain, - undefined, - PublicRpcSchema, - PublicActions - >, - { - signer, - factoryAddress: _factoryAddress, - entryPoint: entryPointAddress, - index = BigInt(0), - address, - nonceKey - }: SignerToSimpleSmartAccountParameters -): Promise> { - const viemSigner: LocalAccount = { - ...signer, - signTransaction: (_, __) => { - throw new SignTransactionNotSupportedBySmartAccount() - } - } as LocalAccount - - const factoryAddress = getFactoryAddress(entryPointAddress, _factoryAddress) - - const [accountAddress, chainId] = await Promise.all([ - address ?? - getAccountAddress({ - client, - factoryAddress, - entryPoint: entryPointAddress, - owner: viemSigner.address, - index - }), - client.chain?.id ?? getChainId(client) - ]) - - if (!accountAddress) throw new Error("Account address not found") - - let smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) - - return toSmartAccount({ - address: accountAddress, - signMessage: async (_) => { - throw new Error("Simple account isn't 1271 compliant") - }, - signTransaction: (_, __) => { - throw new SignTransactionNotSupportedBySmartAccount() - }, - signTypedData: async (_) => { - throw new Error("Simple account isn't 1271 compliant") - }, - client: client, - publicKey: accountAddress, - entryPoint: entryPointAddress, - source: "SimpleSmartAccount", - async getNonce(key?: bigint) { - return getAccountNonce(client, { - sender: accountAddress, - entryPoint: entryPointAddress, - key: key ?? nonceKey - }) - }, - async signUserOperation(userOperation) { - return signMessage(client, { - account: viemSigner, - message: { - raw: getUserOperationHash({ - userOperation, - entryPoint: entryPointAddress, - chainId: chainId - }) - } - }) - }, - async getInitCode() { - if (smartAccountDeployed) return "0x" - - smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) - - if (smartAccountDeployed) return "0x" - - return concatHex([ - factoryAddress, - await getAccountInitCode(viemSigner.address, index) - ]) - }, - async getFactory() { - if (smartAccountDeployed) return undefined - smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) - if (smartAccountDeployed) return undefined - return factoryAddress - }, - async getFactoryData() { - if (smartAccountDeployed) return undefined - smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) - if (smartAccountDeployed) return undefined - return getAccountInitCode(viemSigner.address, index) - }, - async encodeDeployCallData(_) { - throw new Error("Simple account doesn't support account deployment") - }, - async encodeCallData(args) { - if (Array.isArray(args)) { - const argsArray = args as { - to: Address - value: bigint - data: Hex - }[] - - if (getEntryPointVersion(entryPointAddress) === "v0.6") { - return encodeFunctionData({ - abi: [ - { - inputs: [ - { - internalType: "address[]", - name: "dest", - type: "address[]" - }, - { - internalType: "bytes[]", - name: "func", - type: "bytes[]" - } - ], - name: "executeBatch", - outputs: [], - stateMutability: "nonpayable", - type: "function" - } - ], - functionName: "executeBatch", - args: [ - argsArray.map((a) => a.to), - argsArray.map((a) => a.data) - ] - }) - } - return encodeFunctionData({ - abi: [ - { - inputs: [ - { - internalType: "address[]", - name: "dest", - type: "address[]" - }, - { - internalType: "uint256[]", - name: "value", - type: "uint256[]" - }, - { - internalType: "bytes[]", - name: "func", - type: "bytes[]" - } - ], - name: "executeBatch", - outputs: [], - stateMutability: "nonpayable", - type: "function" - } - ], - functionName: "executeBatch", - args: [ - argsArray.map((a) => a.to), - argsArray.map((a) => a.value), - argsArray.map((a) => a.data) - ] - }) - } - - const { to, value, data } = args as { - to: Address - value: bigint - data: Hex - } - - return encodeFunctionData({ - abi: [ - { - inputs: [ - { - internalType: "address", - name: "dest", - type: "address" - }, - { - internalType: "uint256", - name: "value", - type: "uint256" - }, - { - internalType: "bytes", - name: "func", - type: "bytes" - } - ], - name: "execute", - outputs: [], - stateMutability: "nonpayable", - type: "function" - } - ], - functionName: "execute", - args: [to, value, data] - }) - }, - async getDummySignature(_userOperation) { - return "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c" - } - }) -} diff --git a/packages/permissionless/accounts/simple/toSimpleSmartAccount.ts b/packages/permissionless/accounts/simple/toSimpleSmartAccount.ts new file mode 100644 index 00000000..d23f3def --- /dev/null +++ b/packages/permissionless/accounts/simple/toSimpleSmartAccount.ts @@ -0,0 +1,330 @@ +import { + type Account, + type Address, + type Assign, + type Chain, + type Client, + type EIP1193Provider, + type Hex, + type LocalAccount, + type OneOf, + type Transport, + type WalletClient, + encodeFunctionData +} from "viem" +import { + type SmartAccount, + type SmartAccountImplementation, + type UserOperation, + entryPoint06Abi, + entryPoint07Abi, + entryPoint07Address, + getUserOperationHash, + toSmartAccount +} from "viem/account-abstraction" +import { getChainId, signMessage } from "viem/actions" +import { getAction } from "viem/utils" +import { getAccountNonce } from "../../actions/public/getAccountNonce" +import { getSenderAddress } from "../../actions/public/getSenderAddress" +import { toOwner } from "../../utils/toOwner" + +const getAccountInitCode = async ( + owner: Address, + index = BigInt(0) +): Promise => { + if (!owner) throw new Error("Owner account not found") + + return encodeFunctionData({ + abi: [ + { + inputs: [ + { + internalType: "address", + name: "owner", + type: "address" + }, + { + internalType: "uint256", + name: "salt", + type: "uint256" + } + ], + name: "createAccount", + outputs: [ + { + internalType: "contract SimpleAccount", + name: "ret", + type: "address" + } + ], + stateMutability: "nonpayable", + type: "function" + } + ], + functionName: "createAccount", + args: [owner, index] + }) +} + +export type ToSimpleSmartAccountParameters< + entryPointVersion extends "0.6" | "0.7" +> = { + client: Client + owner: OneOf< + | EIP1193Provider + | WalletClient + | LocalAccount + > + factoryAddress?: Address + entryPoint?: { + address: Address + version: entryPointVersion + } + index?: bigint + address?: Address + nonceKey?: bigint +} + +const getFactoryAddress = ( + entryPointVersion: "0.6" | "0.7", + factoryAddress?: Address +): Address => { + if (factoryAddress) return factoryAddress + + if (entryPointVersion === "0.6") { + return "0x9406Cc6185a346906296840746125a0E44976454" + } + return "0x91E60e0613810449d098b0b5Ec8b51A0FE8c8985" +} + +export type SimpleSmartAccountImplementation< + entryPointVersion extends "0.6" | "0.7" = "0.7" +> = Assign< + SmartAccountImplementation< + entryPointVersion extends "0.6" + ? typeof entryPoint06Abi + : typeof entryPoint07Abi, + entryPointVersion + // { + // // entryPoint === ENTRYPOINT_ADDRESS_V06 ? "0.2.2" : "0.3.0-beta" + // abi: entryPointVersion extends "0.6" ? typeof BiconomyAbi + // factory: { abi: typeof FactoryAbi; address: Address } + // } + >, + { sign: NonNullable } +> + +export type ToSimpleSmartAccountReturnType< + entryPointVersion extends "0.6" | "0.7" = "0.7" +> = SmartAccount> + +/** + * @description Creates an Simple Account from a private key. + * + * @returns A Private Key Simple Account. + */ +export async function toSimpleSmartAccount< + entryPointVersion extends "0.6" | "0.7" +>( + parameters: ToSimpleSmartAccountParameters +): Promise> { + const { + client, + owner, + factoryAddress: _factoryAddress, + index = BigInt(0), + address, + nonceKey + } = parameters + + const localOwner = await toOwner({ owner }) + + const entryPoint = { + address: parameters.entryPoint?.address ?? entryPoint07Address, + abi: + (parameters.entryPoint?.version ?? "0.7") === "0.6" + ? entryPoint06Abi + : entryPoint07Abi, + version: parameters.entryPoint?.version ?? "0.7" + } as const + + const factoryAddress = getFactoryAddress( + entryPoint.version, + _factoryAddress + ) + + let accountAddress: Address | undefined = address + + let chainId: number + + const getMemoizedChainId = async () => { + if (chainId) return chainId + chainId = client.chain + ? client.chain.id + : await getAction(client, getChainId, "getChainId")({}) + return chainId + } + + const getFactoryArgs = async () => { + return { + factory: factoryAddress, + factoryData: await getAccountInitCode(localOwner.address, index) + } + } + + return toSmartAccount({ + client, + entryPoint, + getFactoryArgs, + async getAddress() { + if (accountAddress) return accountAddress + + const { factory, factoryData } = await getFactoryArgs() + + // Get the sender address based on the init code + accountAddress = await getSenderAddress(client, { + factory, + factoryData, + entryPointAddress: entryPoint.address + }) + + return accountAddress + }, + async encodeCalls(calls) { + if (calls.length > 1) { + if (entryPoint.version === "0.6") { + return encodeFunctionData({ + abi: [ + { + inputs: [ + { + internalType: "address[]", + name: "dest", + type: "address[]" + }, + { + internalType: "bytes[]", + name: "func", + type: "bytes[]" + } + ], + name: "executeBatch", + outputs: [], + stateMutability: "nonpayable", + type: "function" + } + ], + functionName: "executeBatch", + args: [ + calls.map((a) => a.to), + calls.map((a) => a.data ?? "0x") + ] + }) + } + return encodeFunctionData({ + abi: [ + { + inputs: [ + { + internalType: "address[]", + name: "dest", + type: "address[]" + }, + { + internalType: "uint256[]", + name: "value", + type: "uint256[]" + }, + { + internalType: "bytes[]", + name: "func", + type: "bytes[]" + } + ], + name: "executeBatch", + outputs: [], + stateMutability: "nonpayable", + type: "function" + } + ], + functionName: "executeBatch", + args: [ + calls.map((a) => a.to), + calls.map((a) => a.value ?? 0n), + calls.map((a) => a.data ?? "0x") + ] + }) + } + + return encodeFunctionData({ + abi: [ + { + inputs: [ + { + internalType: "address", + name: "dest", + type: "address" + }, + { + internalType: "uint256", + name: "value", + type: "uint256" + }, + { + internalType: "bytes", + name: "func", + type: "bytes" + } + ], + name: "execute", + outputs: [], + stateMutability: "nonpayable", + type: "function" + } + ], + functionName: "execute", + args: [calls[0].to, calls[0].value ?? 0n, calls[0].data ?? "0x"] + }) + }, + async getNonce(args) { + return getAccountNonce(client, { + address: await this.getAddress(), + entryPointAddress: entryPoint.address, + key: args?.key ?? nonceKey + }) + }, + async getStubSignature() { + return "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c" + }, + async sign({ hash }) { + return this.signMessage({ message: hash }) + }, + signMessage: async (_) => { + throw new Error("Simple account isn't 1271 compliant") + }, + signTypedData: async (_) => { + throw new Error("Simple account isn't 1271 compliant") + }, + async signUserOperation(parameters) { + const { chainId = await getMemoizedChainId(), ...userOperation } = + parameters + return signMessage(client, { + account: localOwner, + message: { + raw: getUserOperationHash({ + userOperation: { + ...userOperation, + sender: + userOperation.sender ?? + (await this.getAddress()), + signature: "0x" + } as UserOperation, + entryPointAddress: entryPoint.address, + entryPointVersion: entryPoint.version, + chainId: chainId + }) + } + }) + } + }) as Promise> +} diff --git a/packages/permissionless/accounts/toSmartAccount.ts b/packages/permissionless/accounts/toSmartAccount.ts deleted file mode 100644 index c4637dd5..00000000 --- a/packages/permissionless/accounts/toSmartAccount.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { - type Abi, - type Address, - type Chain, - type Client, - type CustomSource, - type EncodeDeployDataParameters, - type Hex, - type PublicActions, - type PublicRpcSchema, - type SignableMessage, - type Transport, - type TypedDataDefinition, - concat, - encodeAbiParameters -} from "viem" -import { toAccount } from "viem/accounts" -import type { UserOperation } from "../types" -import type { EntryPoint, GetEntryPointVersion } from "../types/entrypoint" -import { isSmartAccountDeployed } from "../utils" -import { - SignTransactionNotSupportedBySmartAccount, - type SmartAccount -} from "./types" - -const MAGIC_BYTES = - "0x6492649264926492649264926492649264926492649264926492649264926492" - -export function toSmartAccount< - TAccountSource extends CustomSource, - TEntryPoint extends EntryPoint, - TSource extends string = string, - transport extends Transport = Transport, - chain extends Chain | undefined = Chain | undefined, - TAbi extends Abi | readonly unknown[] = Abi ->({ - address, - client, - source, - entryPoint, - getNonce, - getInitCode, - getFactory, - getFactoryData, - encodeCallData, - getDummySignature, - encodeDeployCallData, - signUserOperation, - signMessage, - signTypedData -}: TAccountSource & { - source: TSource - client: Client< - transport, - chain, - undefined, - PublicRpcSchema, - PublicActions - > - entryPoint: TEntryPoint - getNonce: (key?: bigint) => Promise - getInitCode: () => Promise - getFactory: () => Promise
- getFactoryData: () => Promise - encodeCallData: ( - args: - | { - to: Address - value: bigint - data: Hex - } - | { - to: Address - value: bigint - data: Hex - }[] - ) => Promise - getDummySignature( - userOperation: UserOperation> - ): Promise - encodeDeployCallData: ({ - abi, - args, - bytecode - }: EncodeDeployDataParameters) => Promise - signUserOperation: ( - userOperation: UserOperation> - ) => Promise -}): SmartAccount { - const account = toAccount({ - address: address, - signMessage: async ({ message }: { message: SignableMessage }) => { - const isDeployed = await isSmartAccountDeployed(client, address) - const signature = await signMessage({ message }) - - if (isDeployed) return signature - - const abiEncodedMessage = encodeAbiParameters( - [ - { - type: "address", - name: "create2Factory" - }, - { - type: "bytes", - name: "factoryCalldata" - }, - { - type: "bytes", - name: "originalERC1271Signature" - } - ], - [ - (await getFactory()) ?? "0x", // "0x should never happen if it's deployed" - (await getFactoryData()) ?? "0x", // "0x should never happen if it's deployed" - signature - ] - ) - - return concat([abiEncodedMessage, MAGIC_BYTES]) - }, - signTypedData: async (typedData) => { - const isDeployed = await isSmartAccountDeployed(client, address) - const signature = await signTypedData( - typedData as TypedDataDefinition - ) - - if (isDeployed) return signature - - const abiEncodedMessage = encodeAbiParameters( - [ - { - type: "address", - name: "create2Factory" - }, - { - type: "bytes", - name: "factoryCalldata" - }, - { - type: "bytes", - name: "originalERC1271Signature" - } - ], - [ - (await getFactory()) ?? "0x", // "0x should never happen if it's deployed" - (await getFactoryData()) ?? "0x", // "0x should never happen if it's deployed" - signature - ] - ) - - return concat([abiEncodedMessage, MAGIC_BYTES]) - }, - async signTransaction(_, __) { - throw new SignTransactionNotSupportedBySmartAccount() - } - }) - - return { - ...account, - source, - client, - type: "local", - entryPoint, - publicKey: address, - getNonce, - getInitCode, - getFactory, - getFactoryData, - encodeCallData, - getDummySignature, - encodeDeployCallData, - signUserOperation - } -} diff --git a/packages/permissionless/accounts/trust/privateKeyToTrustSmartAccount.ts b/packages/permissionless/accounts/trust/privateKeyToTrustSmartAccount.ts deleted file mode 100644 index 1613b028..00000000 --- a/packages/permissionless/accounts/trust/privateKeyToTrustSmartAccount.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { - Chain, - Client, - Hex, - PublicActions, - PublicRpcSchema, - Transport -} from "viem" -import { privateKeyToAccount } from "viem/accounts" -import type { ENTRYPOINT_ADDRESS_V06_TYPE, Prettify } from "../../types" -import { - type SignerToTrustSmartAccountParameters, - type TrustSmartAccount, - signerToTrustSmartAccount -} from "./signerToTrustSmartAccount" - -export type PrivateKeyToTrustSmartAccountParameters< - entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE -> = Prettify< - { - privateKey: Hex - } & Omit, "signer"> -> - -/** - * @description Creates an Trust Account from a private key. - * - * @returns A Private Key Trust Account. - */ -export async function privateKeyToTrustSmartAccount< - entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined ->( - client: Client< - TTransport, - TChain, - undefined, - PublicRpcSchema, - PublicActions - >, - { privateKey, ...rest }: PrivateKeyToTrustSmartAccountParameters -): Promise> { - const privateKeyAccount = privateKeyToAccount(privateKey) - - return signerToTrustSmartAccount(client, { - signer: privateKeyAccount, - ...rest - }) -} diff --git a/packages/permissionless/accounts/trust/signerToTrustSmartAccount.ts b/packages/permissionless/accounts/trust/signerToTrustSmartAccount.ts deleted file mode 100644 index 6cc97f84..00000000 --- a/packages/permissionless/accounts/trust/signerToTrustSmartAccount.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { - type Address, - type Chain, - type Client, - type Hex, - type LocalAccount, - type PublicActions, - type PublicRpcSchema, - type Transport, - type TypedData, - type TypedDataDefinition, - concatHex, - hashMessage, - hashTypedData -} from "viem" -import { getChainId } from "viem/actions" -import { getAccountNonce } from "../../actions/public/getAccountNonce" - -import { toSmartAccount } from "../toSmartAccount" -import { - SignTransactionNotSupportedBySmartAccount, - type SmartAccount, - type SmartAccountSigner -} from "../types" - -import type { ENTRYPOINT_ADDRESS_V06_TYPE } from "../../types" -import { isSmartAccountDeployed } from "../../utils/isSmartAccountDeployed" - -import { encodeCallData } from "./utils/encodeCallData" -import { getAccountAddress } from "./utils/getAccountAddress" -import { getDummySignature } from "./utils/getDummySignature" -import { getFactoryData } from "./utils/getFactoryData" -import { signTransaction } from "./utils/signTransaction" -import { signUserOperation } from "./utils/signUserOperation" - -async function _signTypedData< - TSource extends string = string, - TAddress extends Address = Address ->( - signer: SmartAccountSigner, - chainId: number, - accountAddress: Address, - hashedMessage: Hex -): Promise { - return signer.signTypedData({ - domain: { - chainId: Number(chainId), - name: "Barz", - verifyingContract: accountAddress, - version: "v0.2.0" - }, - types: { - BarzMessage: [{ name: "message", type: "bytes" }] - }, - message: { - message: hashedMessage - }, - primaryType: "BarzMessage" - }) -} - -/** - * Default addresses for Trust Smart Account - */ -export const TRUST_ADDRESSES: { - secp256k1VerificationFacetAddress: Address - factoryAddress: Address -} = { - secp256k1VerificationFacetAddress: - "0x81b9E3689390C7e74cF526594A105Dea21a8cdD5", - factoryAddress: "0x729c310186a57833f622630a16d13f710b83272a" -} - -export type TrustSmartAccount< - entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE, - transport extends Transport = Transport, - chain extends Chain | undefined = Chain | undefined -> = SmartAccount - -export type SignerToTrustSmartAccountParameters< - entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE, - TSource extends string = string, - TAddress extends Address = Address -> = { - signer: SmartAccountSigner - factoryAddress?: Address - entryPoint: entryPoint - index?: bigint - address?: Address - secp256k1VerificationFacetAddress?: Address - nonceKey?: bigint -} - -/** - * @description Creates an Trust Smart Account from a private key. - * - * @returns A Private Key Trust Smart Account. - */ -export async function signerToTrustSmartAccount< - entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSource extends string = string, - TAddress extends Address = Address ->( - client: Client< - TTransport, - TChain, - undefined, - PublicRpcSchema, - PublicActions - >, - { - signer, - factoryAddress = TRUST_ADDRESSES.factoryAddress, - entryPoint: entryPointAddress, - index = 0n, - secp256k1VerificationFacetAddress = TRUST_ADDRESSES.secp256k1VerificationFacetAddress, - address, - nonceKey - }: SignerToTrustSmartAccountParameters -): Promise> { - const viemSigner: LocalAccount = { - ...signer, - signTransaction: (_, __) => { - throw new SignTransactionNotSupportedBySmartAccount() - } - } - - const [accountAddress, chainId] = await Promise.all([ - address ?? - getAccountAddress(client, { - factoryAddress, - secp256k1VerificationFacetAddress, - entryPoint: entryPointAddress, - bytes: viemSigner.publicKey, - index - }), - client.chain?.id ?? getChainId(client) - ]) - - if (!accountAddress) throw new Error("Account address not found") - - let smartAccountDeployed = await isSmartAccountDeployed( - client, - accountAddress - ) - - return toSmartAccount({ - address: accountAddress, - client: client, - publicKey: accountAddress, - entryPoint: entryPointAddress, - source: "TrustSmartAccount", - signMessage: ({ message }) => { - return _signTypedData( - signer, - chainId, - accountAddress, - hashMessage(message) - ) - }, - signTransaction: signTransaction, - signTypedData< - const TTypedData extends TypedData | Record, - TPrimaryType extends - | keyof TTypedData - | "EIP712Domain" = keyof TTypedData - >(typedData: TypedDataDefinition) { - return _signTypedData( - signer, - chainId, - accountAddress, - hashTypedData(typedData) - ) - }, - getNonce: async (key?: bigint) => { - return getAccountNonce(client, { - sender: accountAddress, - entryPoint: entryPointAddress, - key: key ?? nonceKey - }) - }, - signUserOperation: async (userOperation) => { - return signUserOperation(client, { - account: viemSigner, - userOperation, - entryPoint: entryPointAddress, - chainId: chainId - }) - }, - getInitCode: async () => { - smartAccountDeployed = - smartAccountDeployed || - (await isSmartAccountDeployed(client, accountAddress)) - - if (smartAccountDeployed) { - return "0x" - } - - return concatHex([ - factoryAddress, - await getFactoryData({ - bytes: viemSigner.publicKey, - secp256k1VerificationFacetAddress, - index - }) - ]) - }, - async getFactory() { - smartAccountDeployed = - smartAccountDeployed || - (await isSmartAccountDeployed(client, accountAddress)) - - if (smartAccountDeployed) return undefined - - return factoryAddress - }, - async getFactoryData() { - smartAccountDeployed = - smartAccountDeployed || - (await isSmartAccountDeployed(client, accountAddress)) - - if (smartAccountDeployed) return undefined - - return getFactoryData({ - bytes: viemSigner.publicKey, - secp256k1VerificationFacetAddress, - index - }) - }, - async encodeDeployCallData(_) { - throw new Error("Trust account doesn't support account deployment") - }, - async encodeCallData(args) { - return encodeCallData({ args }) - }, - async getDummySignature(userOperation) { - return getDummySignature(userOperation) - } - }) -} diff --git a/packages/permissionless/accounts/trust/toTrustSmartAccount.ts b/packages/permissionless/accounts/trust/toTrustSmartAccount.ts new file mode 100644 index 00000000..ef9f9a42 --- /dev/null +++ b/packages/permissionless/accounts/trust/toTrustSmartAccount.ts @@ -0,0 +1,224 @@ +import { + type Account, + type Address, + type Assign, + type Chain, + type Client, + type EIP1193Provider, + type Hex, + type LocalAccount, + type OneOf, + type Transport, + type WalletClient, + hashMessage, + hashTypedData +} from "viem" +import { getChainId, signMessage } from "viem/actions" +import { getAccountNonce } from "../../actions/public/getAccountNonce" + +import { + type SmartAccount, + type SmartAccountImplementation, + type UserOperation, + entryPoint06Abi, + entryPoint06Address, + getUserOperationHash, + toSmartAccount +} from "viem/account-abstraction" +import { getAction } from "viem/utils" +import { getSenderAddress } from "../../actions/public/getSenderAddress" +import { toOwner } from "../../utils/toOwner" +import { encodeCallData } from "./utils/encodeCallData" +import { getFactoryData } from "./utils/getFactoryData" + +async function _signTypedData( + signer: LocalAccount, + chainId: number, + accountAddress: Address, + hashedMessage: Hex +): Promise { + return signer.signTypedData({ + domain: { + chainId: Number(chainId), + name: "Barz", + verifyingContract: accountAddress, + version: "v0.2.0" + }, + types: { + BarzMessage: [{ name: "message", type: "bytes" }] + }, + message: { + message: hashedMessage + }, + primaryType: "BarzMessage" + }) +} + +/** + * Default addresses for Trust Smart Account + */ +export const TRUST_ADDRESSES: { + secp256k1VerificationFacetAddress: Address + factoryAddress: Address +} = { + secp256k1VerificationFacetAddress: + "0x81b9E3689390C7e74cF526594A105Dea21a8cdD5", + factoryAddress: "0x729c310186a57833f622630a16d13f710b83272a" +} + +export type ToTrustSmartAccountParameters = { + client: Client + owner: OneOf< + | EIP1193Provider + | WalletClient + | LocalAccount + > + factoryAddress?: Address + entryPoint: { + address: Address + version: "0.6" + } + index?: bigint + address?: Address + secp256k1VerificationFacetAddress?: Address + nonceKey?: bigint +} + +export type TrustSmartAccountImplementation = Assign< + SmartAccountImplementation< + typeof entryPoint06Abi, + "0.6" + // { + // // entryPoint === ENTRYPOINT_ADDRESS_V06 ? "0.2.2" : "0.3.0-beta" + // abi: entryPointVersion extends "0.6" ? typeof BiconomyAbi + // factory: { abi: typeof FactoryAbi; address: Address } + // } + >, + { sign: NonNullable } +> + +export type ToTrustSmartAccountReturnType = + SmartAccount + +/** + * @description Creates an Trust Smart Account from a private key. + * + * @returns A Private Key Trust Smart Account. + */ +export async function toTrustSmartAccount( + parameters: ToTrustSmartAccountParameters +): Promise { + const { + owner, + client, + index = 0n, + address, + factoryAddress = TRUST_ADDRESSES.factoryAddress, + secp256k1VerificationFacetAddress = TRUST_ADDRESSES.secp256k1VerificationFacetAddress + } = parameters + + const localOwner = await toOwner({ owner }) + + let accountAddress: Address | undefined = address + + const entryPoint = { + address: parameters.entryPoint?.address ?? entryPoint06Address, + abi: entryPoint06Abi, + version: parameters.entryPoint?.version ?? "0.6" + } as const + + let chainId: number + + const getMemoizedChainId = async () => { + if (chainId) return chainId + chainId = client.chain + ? client.chain.id + : await getAction(client, getChainId, "getChainId")({}) + return chainId + } + + const getFactoryArgs = async () => { + return { + factory: factoryAddress, + factoryData: await getFactoryData({ + bytes: localOwner.address, + secp256k1VerificationFacetAddress, + index + }) + } + } + + return toSmartAccount({ + client, + entryPoint, + getFactoryArgs, + async getAddress() { + if (accountAddress) return accountAddress + + const { factory, factoryData } = await getFactoryArgs() + + // Get the sender address based on the init code + accountAddress = await getSenderAddress(client, { + factory, + factoryData, + entryPointAddress: entryPoint.address + }) + + return accountAddress + }, + async encodeCalls(calls) { + return encodeCallData(calls) + }, + async getNonce(args) { + return getAccountNonce(client, { + address: await this.getAddress(), + entryPointAddress: entryPoint.address, + key: args?.key ?? parameters?.nonceKey + }) + }, + async getStubSignature() { + return "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c" + }, + async sign({ hash }) { + return this.signMessage({ message: hash }) + }, + async signMessage({ message }) { + return _signTypedData( + localOwner, + await getMemoizedChainId(), + await this.getAddress(), + hashMessage(message) + ) + }, + async signTypedData(typedData) { + return _signTypedData( + localOwner, + await getMemoizedChainId(), + await this.getAddress(), + hashTypedData(typedData) + ) + }, + async signUserOperation(parameters) { + const { chainId = await getMemoizedChainId(), ...userOperation } = + parameters + + return signMessage(client, { + account: localOwner, + message: { + raw: getUserOperationHash({ + userOperation: { + ...userOperation, + sender: + userOperation.sender ?? + (await this.getAddress()), + signature: "0x" + } as UserOperation<"0.6">, + entryPointAddress: entryPoint.address, + entryPointVersion: entryPoint.version, + chainId: chainId + }) + } + }) + } + }) as Promise +} diff --git a/packages/permissionless/accounts/trust/utils/encodeCallData.ts b/packages/permissionless/accounts/trust/utils/encodeCallData.ts index 54fa4690..c5ee95c7 100644 --- a/packages/permissionless/accounts/trust/utils/encodeCallData.ts +++ b/packages/permissionless/accounts/trust/utils/encodeCallData.ts @@ -1,27 +1,13 @@ -import { type Address, type Hex, encodeFunctionData } from "viem" - -export const encodeCallData = async ({ - args -}: { - args: - | { - to: `0x${string}` - value: bigint - data: `0x${string}` - } - | { - to: `0x${string}` - value: bigint - data: `0x${string}` - }[] -}) => { - if (Array.isArray(args)) { - const argsArray = args as { - to: Address - value: bigint - data: Hex - }[] +import { encodeFunctionData } from "viem" +export const encodeCallData = async ( + calls: readonly { + to: `0x${string}` + value?: bigint | undefined + data?: `0x${string}` | undefined + }[] +) => { + if (calls.length > 1) { return encodeFunctionData({ abi: [ { @@ -50,19 +36,13 @@ export const encodeCallData = async ({ ], functionName: "executeBatch", args: [ - argsArray.map((a) => a.to), - argsArray.map((a) => a.value), - argsArray.map((a) => a.data) + calls.map((a) => a.to), + calls.map((a) => a.value ?? 0n), + calls.map((a) => a.data ?? "0x") ] }) } - const { to, value, data } = args as { - to: Address - value: bigint - data: Hex - } - return encodeFunctionData({ abi: [ { @@ -90,6 +70,6 @@ export const encodeCallData = async ({ } ], functionName: "execute", - args: [to, value, data] + args: [calls[0].to, calls[0].value ?? 0n, calls[0].data ?? "0x"] }) } diff --git a/packages/permissionless/accounts/trust/utils/getAccountAddress.ts b/packages/permissionless/accounts/trust/utils/getAccountAddress.ts deleted file mode 100644 index f52d45cf..00000000 --- a/packages/permissionless/accounts/trust/utils/getAccountAddress.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { - type Address, - type Chain, - type Client, - type Transport, - concatHex -} from "viem" -import { getSenderAddress } from "../../../actions/public/getSenderAddress" -import type { ENTRYPOINT_ADDRESS_V06_TYPE, EntryPoint } from "../../../types" -import { getFactoryData } from "./getFactoryData" - -export const getAccountAddress = async < - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined ->( - client: Client, - { - factoryAddress, - entryPoint: entryPointAddress, - bytes, - secp256k1VerificationFacetAddress, - index = 0n - }: { - factoryAddress: Address - bytes: `0x${string}` - entryPoint: entryPoint - secp256k1VerificationFacetAddress: Address - index?: bigint - } -): Promise
=> { - const factoryData = await getFactoryData({ - bytes, - index, - secp256k1VerificationFacetAddress - }) - - return getSenderAddress(client, { - initCode: concatHex([factoryAddress, factoryData]), - entryPoint: entryPointAddress as ENTRYPOINT_ADDRESS_V06_TYPE - }) -} diff --git a/packages/permissionless/accounts/trust/utils/getDummySignature.ts b/packages/permissionless/accounts/trust/utils/getDummySignature.ts deleted file mode 100644 index 5ddfd3f2..00000000 --- a/packages/permissionless/accounts/trust/utils/getDummySignature.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { Hex } from "viem" -import type { - EntryPoint, - GetEntryPointVersion, - UserOperation -} from "../../../types" - -export const getDummySignature = async ( - _userOperation: UserOperation> -) => { - return "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c" as Hex -} diff --git a/packages/permissionless/accounts/trust/utils/signMessage.ts b/packages/permissionless/accounts/trust/utils/signMessage.ts deleted file mode 100644 index 76558165..00000000 --- a/packages/permissionless/accounts/trust/utils/signMessage.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Chain, Client, Transport } from "viem" -import type { SignMessageParameters } from "viem" -import { signMessage as viem_signMessage } from "viem/actions" - -export const signMessage = < - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined ->( - client: Client, - { account, message }: SignMessageParameters -) => { - return viem_signMessage(client, { account, message }) -} diff --git a/packages/permissionless/accounts/trust/utils/signTransaction.ts b/packages/permissionless/accounts/trust/utils/signTransaction.ts deleted file mode 100644 index 208d557c..00000000 --- a/packages/permissionless/accounts/trust/utils/signTransaction.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { SerializeTransactionFn, TransactionSerializable } from "viem" -import { SignTransactionNotSupportedBySmartAccount } from "../../types" - -export const signTransaction = < - serializer extends - SerializeTransactionFn = SerializeTransactionFn, - transaction extends Parameters[0] = Parameters[0] ->( - _transaction: transaction, - _args?: { - serializer?: serializer - } -) => { - throw new SignTransactionNotSupportedBySmartAccount() -} diff --git a/packages/permissionless/accounts/trust/utils/signUserOperation.ts b/packages/permissionless/accounts/trust/utils/signUserOperation.ts deleted file mode 100644 index 0ff0393a..00000000 --- a/packages/permissionless/accounts/trust/utils/signUserOperation.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { Account, Address, Chain, Client, Transport } from "viem" -import type { - EntryPoint, - GetEntryPointVersion, - UserOperation -} from "../../../types" -import { getUserOperationHash } from "../../../utils" -import { signMessage } from "./signMessage" - -export const signUserOperation = async < - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined ->( - client: Client, - { - account, - userOperation, - entryPoint: entryPointAddress, - chainId - }: { - account: Account | Address - userOperation: UserOperation> - entryPoint: entryPoint - chainId: number - } -) => { - return signMessage(client, { - account: account, - message: { - raw: getUserOperationHash({ - userOperation, - entryPoint: entryPointAddress, - chainId: chainId - }) - } - }) -} diff --git a/packages/permissionless/accounts/types.ts b/packages/permissionless/accounts/types.ts deleted file mode 100644 index a11d787f..00000000 --- a/packages/permissionless/accounts/types.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { - type Abi, - type Address, - BaseError, - type Client, - type Hex, - type LocalAccount -} from "viem" -import type { - Chain, - EncodeDeployDataParameters, - PublicActions, - PublicRpcSchema, - Transport -} from "viem" -import type { UserOperation } from "../types" -import type { EntryPoint, GetEntryPointVersion } from "../types/entrypoint" - -export class SignTransactionNotSupportedBySmartAccount extends BaseError { - override name = "SignTransactionNotSupportedBySmartAccount" - constructor({ docsPath }: { docsPath?: string } = {}) { - super( - [ - "A smart account cannot sign or send transaction, it can only sign message or userOperation.", - "Please send user operation instead." - ].join("\n"), - { - docsPath, - docsSlug: "account" - } - ) - } -} - -export type SmartAccount< - entryPoint extends EntryPoint, - TSource extends string, - transport extends Transport, - chain extends Chain | undefined, - TAbi extends Abi | readonly unknown[] = Abi -> = LocalAccount & { - client: Client< - transport, - chain, - undefined, - PublicRpcSchema, - PublicActions - > - entryPoint: entryPoint - getNonce: (key?: bigint) => Promise - getInitCode: () => Promise - getFactory: () => Promise
- getFactoryData: () => Promise - encodeCallData: ( - args: - | { - to: Address - value: bigint - data: Hex - } - | { - to: Address - value: bigint - data: Hex - }[] - ) => Promise - getDummySignature( - userOperation: UserOperation> - ): Promise - encodeDeployCallData: ({ - abi, - args, - bytecode - }: EncodeDeployDataParameters) => Promise - signUserOperation: ( - userOperation: UserOperation> - ) => Promise -} - -export type SmartAccountSigner< - TSource extends string = string, - TAddress extends Address = Address -> = Omit, "signTransaction"> diff --git a/packages/permissionless/actions/bundler/chainId.test.ts b/packages/permissionless/actions/bundler/chainId.test.ts deleted file mode 100644 index 8d5ecf5e..00000000 --- a/packages/permissionless/actions/bundler/chainId.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { http } from "viem" -import { foundry } from "viem/chains" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - type BundlerClient, - createBundlerClient -} from "../../clients/createBundlerClient" -import type { ENTRYPOINT_ADDRESS_V06_TYPE } from "../../types/entrypoint" -import { ENTRYPOINT_ADDRESS_V06 } from "../../utils" -import { chainId } from "./chainId" - -describe("chainId", () => { - testWithRpc("chainId", async ({ rpc }) => { - const { altoRpc } = rpc - const entryPoint = ENTRYPOINT_ADDRESS_V06 - - const bundlerClient = createBundlerClient({ - transport: http(altoRpc), - entryPoint - }) - const id = await chainId(bundlerClient) - expect(id).toBe(foundry.id) - }) -}) diff --git a/packages/permissionless/actions/bundler/chainId.ts b/packages/permissionless/actions/bundler/chainId.ts deleted file mode 100644 index c17c0b28..00000000 --- a/packages/permissionless/actions/bundler/chainId.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { Account, Chain, Client, Transport } from "viem" -import type { BundlerClient } from "../../clients/createBundlerClient" -import type { EntryPoint } from "../../types" -import type { BundlerRpcSchema } from "../../types/bundler" - -/** - * Returns the supported chain id by the bundler service - * - * - Docs: https://docs.pimlico.io/permissionless/reference/bundler-actions/chainId - * - * @param client {@link BundlerClient} that you created using viem's createClient and extended it with bundlerActions. - * @returns Supported chain id - * - * - * @example - * import { createClient } from "viem" - * import { chainId } from "permissionless/actions" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http(BUNDLER_URL) - * }) - * - * const bundlerChainId = chainId(bundlerClient) - * // Return 5n for Goerli - * - */ -export const chainId = async < - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends Account | undefined = Account | undefined ->( - client: Client> -) => { - return Number( - await client.request({ - method: "eth_chainId", - params: [] - }) - ) -} diff --git a/packages/permissionless/actions/bundler/estimateUserOperationGas.test.ts b/packages/permissionless/actions/bundler/estimateUserOperationGas.test.ts deleted file mode 100644 index 9329a493..00000000 --- a/packages/permissionless/actions/bundler/estimateUserOperationGas.test.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { http, parseEther } from "viem" -import { generatePrivateKey } from "viem/accounts" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getPimlicoPaymasterClient, - getSimpleAccountClient -} from "../../../permissionless-test/src/utils" -import { createBundlerClient } from "../../clients/createBundlerClient" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" -import { estimateUserOperationGas } from "./estimateUserOperationGas" - -describe("eth_estimateUserOperationGas", () => { - testWithRpc("eth_estimateUserOperationGas_v06", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const bundlerClientV06 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V06 - }) - - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) - }) - - const userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: "0x5af0d9827e0c53e4799bb226655a1de152a425a5", - data: "0x", - value: 0n - }) - } - }) - - const { preVerificationGas, verificationGasLimit, callGasLimit } = - await estimateUserOperationGas( - bundlerClientV06, - { - userOperation, - entryPoint: ENTRYPOINT_ADDRESS_V06 - }, - { - "0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC": { - balance: parseEther("1"), - stateDiff: { - "0x3ea2f1d0abf3fc66cf29eebb70cbd4e7fe762ef8a09bcc06c8edf641230afec0": - "0x00000000000000000000000000000000000000000000000000000000000001a4" - } - } - } - ) - - expect(preVerificationGas).toBeTruthy() - expect(verificationGasLimit).toBeTruthy() - expect(callGasLimit).toBeTruthy() - }) - - testWithRpc("eth_estimateUserOperationGas_v07", async ({ rpc }) => { - const { anvilRpc, altoRpc } = rpc - const bundlerClientV07 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - - const smartAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc - }) - - const userOperation = - await smartAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await smartAccountClient.account.encodeCallData({ - to: "0x5af0d9827e0c53e4799bb226655a1de152a425a5", - data: "0x", - value: 0n - }) - } - }) - - const { - preVerificationGas, - verificationGasLimit, - callGasLimit, - paymasterVerificationGasLimit, - paymasterPostOpGasLimit - } = await estimateUserOperationGas(bundlerClientV07, { - entryPoint: ENTRYPOINT_ADDRESS_V07, - userOperation - }) - - expect(preVerificationGas).toBeTruthy() - expect(verificationGasLimit).toBeTruthy() - expect(callGasLimit).toBeTruthy() - expect(paymasterVerificationGasLimit).toBe(0n) - expect(paymasterPostOpGasLimit).toBe(0n) - }) - - testWithRpc( - "eth_estimateUserOperationGas_V07_with_error", - async ({ rpc }) => { - const { anvilRpc, altoRpc } = rpc - const bundlerClientV07 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - - const smartAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc - }) - - const userOperation = - await smartAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: - await smartAccountClient.account.encodeCallData({ - to: "0x5af0d9827e0c53e4799bb226655a1de152a425a5", - data: "0x", - value: 1000n - }) - } - }) - - await expect(() => - estimateUserOperationGas( - bundlerClientV07, - { - userOperation, - entryPoint: ENTRYPOINT_ADDRESS_V07 - }, - { - [smartAccountClient.account.address]: { - balance: 0n - } - } - ) - ).rejects.toThrowError(/AA21/) - } - ) - - testWithRpc( - "eth_estimateUserOperationGas_V07_withPaymaster", - async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - const bundlerClientV07 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - - const smartAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) - }) - - const userOperation = - await smartAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: - await smartAccountClient.account.encodeCallData({ - to: "0x5af0d9827e0c53e4799bb226655a1de152a425a5", - data: "0x", - value: 0n - }) - } - }) - - const { - preVerificationGas, - verificationGasLimit, - callGasLimit, - paymasterVerificationGasLimit, - paymasterPostOpGasLimit - } = await estimateUserOperationGas(bundlerClientV07, { - userOperation, - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - - expect(preVerificationGas).toBeTruthy() - expect(verificationGasLimit).toBeTruthy() - expect(callGasLimit).toBeTruthy() - expect(paymasterVerificationGasLimit).toBeTruthy() - expect(paymasterPostOpGasLimit).toBeTruthy() - } - ) -}) diff --git a/packages/permissionless/actions/bundler/estimateUserOperationGas.ts b/packages/permissionless/actions/bundler/estimateUserOperationGas.ts deleted file mode 100644 index 00f518be..00000000 --- a/packages/permissionless/actions/bundler/estimateUserOperationGas.ts +++ /dev/null @@ -1,146 +0,0 @@ -import type { Account, BaseError, Chain, Client, Hex, Transport } from "viem" -import type { PartialBy } from "viem/chains" -import type { BundlerClient } from "../../clients/createBundlerClient" -import type { Prettify } from "../../types/" -import type { BundlerRpcSchema, StateOverrides } from "../../types/bundler" -import type { EntryPoint, GetEntryPointVersion } from "../../types/entrypoint" -import type { UserOperation } from "../../types/userOperation" -import { getEntryPointVersion } from "../../utils" -import { deepHexlify } from "../../utils/deepHexlify" -import { - type GetEstimateUserOperationGasErrorReturnType, - getEstimateUserOperationGasError -} from "../../utils/errors/getEstimateUserOperationGasError" - -export type EstimateUserOperationGasParameters = - { - userOperation: GetEntryPointVersion extends "v0.6" - ? PartialBy< - UserOperation<"v0.6">, - "callGasLimit" | "preVerificationGas" | "verificationGasLimit" - > - : PartialBy< - UserOperation<"v0.7">, - | "callGasLimit" - | "preVerificationGas" - | "verificationGasLimit" - | "paymasterVerificationGasLimit" - | "paymasterPostOpGasLimit" - > - entryPoint: entryPoint - } - -export type EstimateUserOperationGasReturnType = - GetEntryPointVersion extends "v0.6" - ? { - preVerificationGas: bigint - verificationGasLimit: bigint - callGasLimit: bigint - } - : { - preVerificationGas: bigint - verificationGasLimit: bigint - callGasLimit: bigint - paymasterVerificationGasLimit?: bigint - paymasterPostOpGasLimit?: bigint - } - -export type EstimateUserOperationErrorType = - GetEstimateUserOperationGasErrorReturnType - -/** - * Estimates preVerificationGas, verificationGasLimit and callGasLimit for user operation - * - * - Docs: https://docs.pimlico.io/permissionless/reference/bundler-actions/estimateUserOperationGas - * - * @param client {@link BundlerClient} that you created using viem's createClient and extended it with bundlerActions. - * @param args {@link EstimateUserOperationGasParameters} - * @returns preVerificationGas, verificationGasLimit and callGasLimit as {@link EstimateUserOperationGasReturnType} - * - * - * @example - * import { createClient } from "viem" - * import { estimateUserOperationGas } from "permissionless/actions" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http(BUNDLER_URL) - * }) - * - * const gasParameters = estimateUserOperationGas(bundlerClient, { - * serOperation: signedUserOperation, - * entryPoint: entryPoint - * }) - * - * // Return {preVerificationGas: 43492n, verificationGasLimit: 59436n, callGasLimit: 9000n} - * - */ -export const estimateUserOperationGas = async < - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends Account | undefined = Account | undefined ->( - client: Client>, - args: Prettify>, - stateOverrides?: StateOverrides -): Promise> => { - const { userOperation, entryPoint } = args - - const userOperationWithBigIntAsHex = deepHexlify(userOperation) - const stateOverridesWithBigIntAsHex = deepHexlify(stateOverrides) - - try { - const response = await client.request({ - method: "eth_estimateUserOperationGas", - params: stateOverrides - ? [ - userOperationWithBigIntAsHex, - entryPoint, - stateOverridesWithBigIntAsHex - ] - : [userOperationWithBigIntAsHex, entryPoint] - }) - - const entryPointVersion = getEntryPointVersion(entryPoint) - - if (entryPointVersion === "v0.6") { - const responseV06 = response as { - preVerificationGas: Hex - verificationGasLimit: Hex - callGasLimit: Hex - } - - return { - preVerificationGas: BigInt(responseV06.preVerificationGas || 0), - verificationGasLimit: BigInt( - responseV06.verificationGasLimit || 0 - ), - callGasLimit: BigInt(responseV06.callGasLimit || 0) - } as EstimateUserOperationGasReturnType - } - - const responseV07 = response as { - preVerificationGas: Hex - verificationGasLimit: Hex - callGasLimit: Hex - paymasterVerificationGasLimit?: Hex - paymasterPostOpGasLimit?: Hex - } - - return { - preVerificationGas: BigInt(responseV07.preVerificationGas || 0), - verificationGasLimit: BigInt(responseV07.verificationGasLimit || 0), - callGasLimit: BigInt(responseV07.callGasLimit || 0), - paymasterVerificationGasLimit: - responseV07.paymasterVerificationGasLimit - ? BigInt(responseV07.paymasterVerificationGasLimit) - : undefined, - paymasterPostOpGasLimit: responseV07.paymasterPostOpGasLimit - ? BigInt(responseV07.paymasterPostOpGasLimit) - : undefined - } as EstimateUserOperationGasReturnType - } catch (err) { - throw getEstimateUserOperationGasError(err as BaseError, args) - } -} diff --git a/packages/permissionless/actions/bundler/getUserOperationByHash.test.ts b/packages/permissionless/actions/bundler/getUserOperationByHash.test.ts deleted file mode 100644 index f9693cd5..00000000 --- a/packages/permissionless/actions/bundler/getUserOperationByHash.test.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { http, isHash, zeroAddress } from "viem" -import { generatePrivateKey } from "viem/accounts" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getPimlicoPaymasterClient, - getSimpleAccountClient -} from "../../../permissionless-test/src/utils" -import { createBundlerClient } from "../../clients/createBundlerClient" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" -import { getUserOperationByHash } from "./getUserOperationByHash" - -describe("getUserOperationByHash", () => { - testWithRpc("getUserOperationByHash_V06", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - const bundlerClientV06 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V06 - }) - - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) - }) - - const userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - } - }) - - userOperation.signature = - await simpleAccountClient.account.signUserOperation(userOperation) - - const opHash = await bundlerClientV06.sendUserOperation({ - userOperation - }) - - expect(isHash(opHash)).toBe(true) - - await bundlerClientV06.waitForUserOperationReceipt({ - hash: opHash, - timeout: 10000 - }) - - const userOperationFromUserOpHash = await getUserOperationByHash( - bundlerClientV06, - { hash: opHash } - ) - - expect(userOperationFromUserOpHash).not.toBeNull() - expect(userOperationFromUserOpHash?.entryPoint).toBe( - ENTRYPOINT_ADDRESS_V06 - ) - - for (const key in userOperationFromUserOpHash?.userOperation) { - const expected = userOperationFromUserOpHash?.userOperation[key] - const actual = userOperation[key] - - if (typeof expected === "string" && typeof actual === "string") { - expect(expected.toLowerCase()).toBe(actual.toLowerCase()) - } else { - expect(expected).toBe(actual) - } - } - }) - - testWithRpc("getUserOperationByHash_V07", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const bundlerClientV07 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) - }) - - const userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - } - }) - - userOperation.signature = - await simpleAccountClient.account.signUserOperation(userOperation) - - const opHash = await bundlerClientV07.sendUserOperation({ - userOperation - }) - - expect(isHash(opHash)).toBe(true) - - await bundlerClientV07.waitForUserOperationReceipt({ - hash: opHash, - timeout: 10000 - }) - - const userOperationFromUserOpHash = await getUserOperationByHash( - bundlerClientV07, - { hash: opHash } - ) - - for (const key in userOperationFromUserOpHash?.userOperation) { - const expected = userOperationFromUserOpHash?.userOperation[key] - const actual = userOperation[key] - - if (typeof expected === "string" && typeof actual === "string") { - expect(expected.toLowerCase()).toBe(actual.toLowerCase()) - } else { - expect(expected).toBe(actual) - } - } - }) -}) diff --git a/packages/permissionless/actions/bundler/getUserOperationByHash.ts b/packages/permissionless/actions/bundler/getUserOperationByHash.ts deleted file mode 100644 index 97c0ee4c..00000000 --- a/packages/permissionless/actions/bundler/getUserOperationByHash.ts +++ /dev/null @@ -1,110 +0,0 @@ -import type { Account, Address, Chain, Client, Hash, Transport } from "viem" -import type { BundlerClient } from "../../clients/createBundlerClient" -import type { Prettify } from "../../types/" -import type { BundlerRpcSchema } from "../../types/bundler" -import type { EntryPoint, GetEntryPointVersion } from "../../types/entrypoint" -import type { UserOperation } from "../../types/userOperation" -import { ENTRYPOINT_ADDRESS_V06 } from "../../utils/getEntryPointVersion" - -export type GetUserOperationByHashParameters = { - hash: Hash -} - -export type GetUserOperationByHashReturnType = { - userOperation: UserOperation> - entryPoint: Address - transactionHash: Hash - blockHash: Hash - blockNumber: bigint -} - -/** - * Returns the user operation from userOpHash - * - * - Docs: https://docs.pimlico.io/permissionless/reference/bundler-actions/getUserOperationByHash - * - * @param client {@link BundlerClient} that you created using viem's createClient and extended it with bundlerActions. - * @param args {@link GetUserOperationByHashParameters} UserOpHash that was returned by {@link sendUserOperation} - * @returns userOperation along with entryPoint, transactionHash, blockHash, blockNumber if found or null - * - * - * @example - * import { createClient } from "viem" - * import { getUserOperationByHash } from "permissionless/actions" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http(BUNDLER_URL) - * }) - * - * getUserOperationByHash(bundlerClient, {hash: userOpHash}) - * - */ -export const getUserOperationByHash = async < - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends Account | undefined = Account | undefined ->( - client: Client>, - { hash }: Prettify -): Promise> | null> => { - const params: [Hash] = [hash] - - const response = await client.request({ - method: "eth_getUserOperationByHash", - params - }) - - if (!response) return null - - const { - userOperation, - entryPoint: entryPointAddress, - transactionHash, - blockHash, - blockNumber - } = response - - return { - userOperation: (entryPointAddress === ENTRYPOINT_ADDRESS_V06 - ? { - ...userOperation, - nonce: BigInt(userOperation.nonce), - callGasLimit: BigInt(userOperation.callGasLimit), - verificationGasLimit: BigInt( - userOperation.verificationGasLimit - ), - preVerificationGas: BigInt(userOperation.preVerificationGas), - maxFeePerGas: BigInt(userOperation.maxFeePerGas), - maxPriorityFeePerGas: BigInt( - userOperation.maxPriorityFeePerGas - ) - } - : { - ...userOperation, - nonce: BigInt(userOperation.nonce), - callGasLimit: BigInt(userOperation.callGasLimit), - verificationGasLimit: BigInt( - userOperation.verificationGasLimit - ), - preVerificationGas: BigInt(userOperation.preVerificationGas), - maxFeePerGas: BigInt(userOperation.maxFeePerGas), - maxPriorityFeePerGas: BigInt( - userOperation.maxPriorityFeePerGas - ), - paymasterVerificationGasLimit: - userOperation.paymasterVerificationGasLimit - ? BigInt(userOperation.paymasterVerificationGasLimit) - : undefined, - paymasterPostOpGasLimit: - userOperation.paymasterVerificationGasLimit - ? BigInt(userOperation.paymasterPostOpGasLimit) - : undefined - }) as UserOperation>, - entryPoint: entryPointAddress, - transactionHash: transactionHash, - blockHash: blockHash, - blockNumber: BigInt(blockNumber) - } -} diff --git a/packages/permissionless/actions/bundler/getUserOperationReceipt.test.ts b/packages/permissionless/actions/bundler/getUserOperationReceipt.test.ts deleted file mode 100644 index e7693263..00000000 --- a/packages/permissionless/actions/bundler/getUserOperationReceipt.test.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { http, isHash, zeroAddress } from "viem" -import { generatePrivateKey } from "viem/accounts" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getPimlicoPaymasterClient, - getSimpleAccountClient -} from "../../../permissionless-test/src/utils" -import { createBundlerClient } from "../../clients/createBundlerClient" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" -import { getUserOperationReceipt } from "./getUserOperationReceipt" - -describe("getUserOperationReceipt", () => { - testWithRpc("getUserOperationReceipt_V06", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const bundlerClientV06 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V06 - }) - - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) - }) - - const userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - } - }) - - userOperation.signature = - await simpleAccountClient.account.signUserOperation(userOperation) - - const opHash = await bundlerClientV06.sendUserOperation({ - userOperation - }) - - expect(isHash(opHash)).toBe(true) - - const userOperationReceipt = - await bundlerClientV06.waitForUserOperationReceipt({ - hash: opHash, - timeout: 100000 - }) - expect(userOperationReceipt).not.toBeNull() - expect(userOperationReceipt?.userOpHash).toBe(opHash) - expect(userOperationReceipt?.receipt.transactionHash).toBeTruthy() - - const receipt = await getUserOperationReceipt(bundlerClientV06, { - hash: opHash - }) - - expect(receipt?.receipt.transactionHash).toBe( - userOperationReceipt?.receipt.transactionHash - ) - }) - - testWithRpc("getUserOperationReceipt_V07", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const bundlerClientV07 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) - }) - - const userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - } - }) - - userOperation.signature = - await simpleAccountClient.account.signUserOperation(userOperation) - - const opHash = await bundlerClientV07.sendUserOperation({ - userOperation - }) - - expect(isHash(opHash)).toBe(true) - - const userOperationReceipt = - await bundlerClientV07.waitForUserOperationReceipt({ - hash: opHash, - timeout: 100000 - }) - expect(userOperationReceipt).not.toBeNull() - expect(userOperationReceipt?.userOpHash).toBe(opHash) - expect(userOperationReceipt?.receipt.transactionHash).toBeTruthy() - - const receipt = await getUserOperationReceipt(bundlerClientV07, { - hash: opHash - }) - - expect(receipt?.receipt.transactionHash).toBe( - userOperationReceipt?.receipt.transactionHash - ) - }) -}) diff --git a/packages/permissionless/actions/bundler/getUserOperationReceipt.ts b/packages/permissionless/actions/bundler/getUserOperationReceipt.ts deleted file mode 100644 index 476d32f9..00000000 --- a/packages/permissionless/actions/bundler/getUserOperationReceipt.ts +++ /dev/null @@ -1,127 +0,0 @@ -import type { - Account, - Address, - Chain, - Client, - Hash, - Hex, - Log, - Transport -} from "viem" -import type { BundlerClient } from "../../clients/createBundlerClient" -import type { Prettify } from "../../types" -import type { BundlerRpcSchema } from "../../types/bundler" -import type { EntryPoint } from "../../types/entrypoint" -import type { TStatus } from "../../types/userOperation" -import { transactionReceiptStatus } from "../../utils/deepHexlify" - -export type GetUserOperationReceiptParameters = { - hash: Hash -} - -export type GetUserOperationReceiptReturnType = { - userOpHash: Hash - entryPoint: Address - sender: Address - nonce: bigint - paymaster?: Address - actualGasUsed: bigint - actualGasCost: bigint - success: boolean - reason?: string - receipt: { - transactionHash: Hex - transactionIndex: bigint - blockHash: Hash - blockNumber: bigint - from: Address - to: Address | null - cumulativeGasUsed: bigint - status: TStatus - gasUsed: bigint - contractAddress: Address | null - logsBloom: Hex - effectiveGasPrice: bigint - } - logs: Log[] -} - -/** - * Returns the user operation receipt from userOpHash - * - * - Docs: https://docs.pimlico.io/permissionless/reference/bundler-actions/getUserOperationReceipt - * - * @param client {@link BundlerClient} that you created using viem's createClient and extended it with bundlerActions. - * @param args {@link GetUserOperationReceiptParameters} UserOpHash that was returned by {@link sendUserOperation} - * @returns user operation receipt {@link GetUserOperationReceiptReturnType} if found or null - * - * - * @example - * import { createClient } from "viem" - * import { getUserOperationReceipt } from "permissionless/actions" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http(BUNDLER_URL) - * }) - * - * getUserOperationReceipt(bundlerClient, {hash: userOpHash}) - * - */ -export const getUserOperationReceipt = async < - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends Account | undefined = Account | undefined ->( - client: Client>, - { hash }: Prettify -): Promise | null> => { - const params: [Hash] = [hash] - - const response = await client.request({ - method: "eth_getUserOperationReceipt", - params - }) - - if (!response) return null - - const userOperationReceipt: GetUserOperationReceiptReturnType = { - userOpHash: response.userOpHash, - entryPoint: response.entryPoint, - sender: response.sender, - nonce: BigInt(response.nonce), - paymaster: response.paymaster, - actualGasUsed: BigInt(response.actualGasUsed), - actualGasCost: BigInt(response.actualGasCost), - success: response.success, - reason: response.reason, - receipt: { - transactionHash: response.receipt.transactionHash, - transactionIndex: BigInt(response.receipt.transactionIndex), - blockHash: response.receipt.blockHash, - blockNumber: BigInt(response.receipt.blockNumber), - from: response.receipt.from, - to: response.receipt.to, - cumulativeGasUsed: BigInt(response.receipt.cumulativeGasUsed), - status: transactionReceiptStatus[response.receipt.status], - gasUsed: BigInt(response.receipt.gasUsed), - contractAddress: response.receipt.contractAddress, - logsBloom: response.receipt.logsBloom, - effectiveGasPrice: BigInt(response.receipt.effectiveGasPrice) - }, - logs: response.logs.map((log) => ({ - data: log.data, - blockNumber: BigInt(log.blockNumber), - blockHash: log.blockHash, - transactionHash: log.transactionHash, - logIndex: Number(log.logIndex), - transactionIndex: Number(log.transactionIndex), - address: log.address, - topics: log.topics, - removed: log.removed - })) - } - - return userOperationReceipt -} diff --git a/packages/permissionless/actions/bundler/sendUserOperation.test.ts b/packages/permissionless/actions/bundler/sendUserOperation.test.ts deleted file mode 100644 index e1085f95..00000000 --- a/packages/permissionless/actions/bundler/sendUserOperation.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { http, isHash, zeroAddress } from "viem" -import { generatePrivateKey } from "viem/accounts" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getPimlicoPaymasterClient, - getSimpleAccountClient -} from "../../../permissionless-test/src/utils" -import { createBundlerClient } from "../../clients/createBundlerClient" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" -import { getUserOperationReceipt } from "./getUserOperationReceipt" - -describe("sendUserOperation", () => { - testWithRpc("sendUserOperation_V06", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const bundlerClientV06 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V06 - }) - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) - }) - - const userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - } - }) - - userOperation.signature = - await simpleAccountClient.account.signUserOperation(userOperation) - - const opHash = await bundlerClientV06.sendUserOperation({ - userOperation - }) - - expect(isHash(opHash)).toBe(true) - - const userOperationReceipt = - await bundlerClientV06.waitForUserOperationReceipt({ - hash: opHash, - timeout: 100000 - }) - expect(userOperationReceipt).not.toBeNull() - expect(userOperationReceipt?.userOpHash).toBe(opHash) - expect(userOperationReceipt?.receipt.transactionHash).toBeTruthy() - - const receipt = await getUserOperationReceipt(bundlerClientV06, { - hash: opHash - }) - - expect(receipt?.receipt.transactionHash).toBe( - userOperationReceipt?.receipt.transactionHash - ) - }) - - testWithRpc("sendUserOperation_V07", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const bundlerClientV07 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) - }) - - const userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - } - }) - - userOperation.signature = - await simpleAccountClient.account.signUserOperation(userOperation) - - const opHash = await bundlerClientV07.sendUserOperation({ - userOperation - }) - - expect(isHash(opHash)).toBe(true) - - const userOperationReceipt = - await bundlerClientV07.waitForUserOperationReceipt({ - hash: opHash, - timeout: 100000 - }) - expect(userOperationReceipt).not.toBeNull() - expect(userOperationReceipt?.userOpHash).toBe(opHash) - expect(userOperationReceipt?.receipt.transactionHash).toBeTruthy() - - const receipt = await getUserOperationReceipt(bundlerClientV07, { - hash: opHash - }) - - expect(receipt?.receipt.transactionHash).toBe( - userOperationReceipt?.receipt.transactionHash - ) - }) -}) diff --git a/packages/permissionless/actions/bundler/sendUserOperation.ts b/packages/permissionless/actions/bundler/sendUserOperation.ts deleted file mode 100644 index 04b60ffe..00000000 --- a/packages/permissionless/actions/bundler/sendUserOperation.ts +++ /dev/null @@ -1,72 +0,0 @@ -import type { Account, BaseError, Chain, Client, Hash, Transport } from "viem" -import type { BundlerClient } from "../../clients/createBundlerClient" -import type { Prettify } from "../../types/" -import type { BundlerRpcSchema } from "../../types/bundler" -import type { EntryPoint, GetEntryPointVersion } from "../../types/entrypoint" -import type { - UserOperation, - UserOperationWithBigIntAsHex -} from "../../types/userOperation" -import { deepHexlify } from "../../utils/deepHexlify" -import { getSendUserOperationError } from "../../utils/errors/getSendUserOperationError" - -export type SendUserOperationParameters = { - userOperation: UserOperation> - entryPoint: entryPoint -} - -/** - * Sends user operation to the bundler - * - * - Docs: https://docs.pimlico.io/permissionless/reference/bundler-actions/sendUserOperation - * - * @param client {@link BundlerClient} that you created using viem's createClient and extended it with bundlerActions. - * @param args {@link SendUserOperationParameters}. - * @returns UserOpHash that you can use to track user operation as {@link Hash}. - * - * @example - * import { createClient } from "viem" - * import { sendUserOperation } from "permissionless/actions" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http(BUNDLER_URL) - * }) - * - * const userOpHash = sendUserOperation(bundlerClient, { - * userOperation: signedUserOperation, - * entryPoint: entryPoint - * }) - * - * // Return '0xe9fad2cd67f9ca1d0b7a6513b2a42066784c8df938518da2b51bb8cc9a89ea34' - */ -export const sendUserOperation = async < - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends Account | undefined = Account | undefined ->( - client: Client>, - args: Prettify> -): Promise => { - const { userOperation, entryPoint } = args - - try { - const userOperationHash = await client.request({ - method: "eth_sendUserOperation", - params: [ - deepHexlify(userOperation) as UserOperationWithBigIntAsHex< - GetEntryPointVersion - >, - entryPoint - ] - }) - - return userOperationHash - } catch (err) { - throw getSendUserOperationError( - err as BaseError, - args as SendUserOperationParameters - ) - } -} diff --git a/packages/permissionless/actions/bundler/supportedEntryPoints.test.ts b/packages/permissionless/actions/bundler/supportedEntryPoints.test.ts deleted file mode 100644 index dca58f4d..00000000 --- a/packages/permissionless/actions/bundler/supportedEntryPoints.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { http } from "viem" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { createBundlerClient } from "../../clients/createBundlerClient" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" -import { supportedEntryPoints } from "./supportedEntryPoints" - -describe("supportedEntryPoints", () => { - testWithRpc("supportedEntryPoints_V06", async ({ rpc }) => { - const bundlerClientV06 = createBundlerClient({ - transport: http(rpc.altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V06 - }) - - const entryPoints = await supportedEntryPoints(bundlerClientV06) - expect(entryPoints).contain(ENTRYPOINT_ADDRESS_V06) - }) - - testWithRpc("supportedEntryPoints_V07", async ({ rpc }) => { - const bundlerClientV07 = createBundlerClient({ - transport: http(rpc.altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - const entryPoints = await supportedEntryPoints(bundlerClientV07) - expect(entryPoints).contain(ENTRYPOINT_ADDRESS_V07) - }) -}) diff --git a/packages/permissionless/actions/bundler/supportedEntryPoints.ts b/packages/permissionless/actions/bundler/supportedEntryPoints.ts deleted file mode 100644 index 208ade1f..00000000 --- a/packages/permissionless/actions/bundler/supportedEntryPoints.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { Account, Chain, Client, Transport } from "viem" -import type { BundlerClient } from "../../clients/createBundlerClient" -import type { BundlerRpcSchema } from "../../types/bundler" -import type { EntryPoint } from "../../types/entrypoint" - -/** - * Returns the supported entrypoints by the bundler service - * - * - Docs: https://docs.pimlico.io/permissionless/reference/bundler-actions/supportedEntryPoints - * - * @param client {@link BundlerClient} that you created using viem's createClient and extended it with bundlerActions. - * @returns Supported entryPoints - * - * - * @example - * import { createClient } from "viem" - * import { supportedEntryPoints } from "permissionless/actions" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http(BUNDLER_URL) - * }) - * - * const entryPointsSupported = supportedEntryPoints(bundlerClient) - * // Return ['0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789'] - * - */ -export const supportedEntryPoints = async < - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends Account | undefined = Account | undefined ->( - client: Client> -): Promise => { - return client.request({ - method: "eth_supportedEntryPoints", - params: [] - }) -} diff --git a/packages/permissionless/actions/bundler/waitForUserOperationReceipt.test.ts b/packages/permissionless/actions/bundler/waitForUserOperationReceipt.test.ts deleted file mode 100644 index df30228b..00000000 --- a/packages/permissionless/actions/bundler/waitForUserOperationReceipt.test.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { http, isHash, zeroAddress } from "viem" -import { generatePrivateKey } from "viem/accounts" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getPimlicoPaymasterClient, - getSimpleAccountClient -} from "../../../permissionless-test/src/utils" -import { createBundlerClient } from "../../clients/createBundlerClient" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" -import { waitForUserOperationReceipt } from "./waitForUserOperationReceipt" - -describe("waitForUserOperationReceipt", () => { - testWithRpc("waitForUserOperationReceipt_V06", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const bundlerClientV06 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V06 - }) - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) - }) - - const userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - } - }) - - userOperation.signature = - await simpleAccountClient.account.signUserOperation(userOperation) - - const opHash = await bundlerClientV06.sendUserOperation({ - userOperation - }) - - expect(isHash(opHash)).toBe(true) - - const userOperationReceipt = await waitForUserOperationReceipt( - bundlerClientV06, - { - hash: opHash, - timeout: 100000 - } - ) - expect(userOperationReceipt).not.toBeNull() - expect(userOperationReceipt?.userOpHash).toBe(opHash) - expect(userOperationReceipt?.receipt.transactionHash).toBeTruthy() - - const receipt = await bundlerClientV06.getUserOperationReceipt({ - hash: opHash - }) - - expect(receipt?.receipt.transactionHash).toBe( - userOperationReceipt?.receipt.transactionHash - ) - }) - - testWithRpc("waitForUserOperationReceipt_V07", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const bundlerClientV07 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) - }) - - const userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - } - }) - - userOperation.signature = - await simpleAccountClient.account.signUserOperation(userOperation) - - const opHash = await bundlerClientV07.sendUserOperation({ - userOperation - }) - - expect(isHash(opHash)).toBe(true) - - const userOperationReceipt = await waitForUserOperationReceipt( - bundlerClientV07, - { - hash: opHash, - timeout: 100000 - } - ) - expect(userOperationReceipt).not.toBeNull() - expect(userOperationReceipt?.userOpHash).toBe(opHash) - expect(userOperationReceipt?.receipt.transactionHash).toBeTruthy() - - const receipt = await bundlerClientV07.getUserOperationReceipt({ - hash: opHash - }) - - expect(receipt?.receipt.transactionHash).toBe( - userOperationReceipt?.receipt.transactionHash - ) - }) -}) diff --git a/packages/permissionless/actions/bundler/waitForUserOperationReceipt.ts b/packages/permissionless/actions/bundler/waitForUserOperationReceipt.ts deleted file mode 100644 index 8ed2a875..00000000 --- a/packages/permissionless/actions/bundler/waitForUserOperationReceipt.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { - type Account, - BaseError, - type Chain, - type Client, - type Hash, - type Transport, - stringify -} from "viem" -import { getAction } from "viem/utils" -import type { Prettify } from "../../types/" -import { observe } from "../../utils/observe" -import { - type GetUserOperationReceiptReturnType, - getUserOperationReceipt -} from "./getUserOperationReceipt" - -export class WaitForUserOperationReceiptTimeoutError extends BaseError { - override name = "WaitForUserOperationReceiptTimeoutError" - constructor({ hash }: { hash: Hash }) { - super( - `Timed out while waiting for user operation with hash "${hash}" to be confirmed.` - ) - } -} - -export type WaitForUserOperationReceiptParameters = { - /** The hash of the transaction. */ - hash: Hash - /** - * Polling frequency (in ms). Defaults to the client's pollingInterval config. - * @default client.pollingInterval - */ - pollingInterval?: number - /** Optional timeout (in milliseconds) to wait before stopping polling. */ - timeout?: number -} - -/** - * Waits for the User Operation to be included on a [Block](https://viem.sh/docs/glossary/terms.html#block) (one confirmation), and then returns the [User Operation Receipt](https://docs.pimlico.io/permissionless/reference/bundler-actions/getUserOperationReceipt). - * - * - Docs: https://docs.pimlico.io/permissionless/reference/bundler-actions/waitForUserOperationReceipt - * - * @param client - Bundler Client to use - * @param parameters - {@link WaitForUserOperationReceiptParameters} - * @returns The transaction receipt. {@link GetUserOperationReceiptReturnType} - * - * @example - * import { createBundlerClient, waitForUserOperationReceipt, http } from 'viem' - * import { mainnet } from 'viem/chains' - * - * const client = createBundlerClient({ - * chain: mainnet, - * transport: http(), - * }) - * const userOperationReceipt = await waitForUserOperationReceipt(client, { - * hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d', - * }) - */ -export const waitForUserOperationReceipt = < - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends Account | undefined = Account | undefined ->( - bundlerClient: Client, - { - hash, - pollingInterval = bundlerClient.pollingInterval, - timeout = bundlerClient.transport.timeout - }: Prettify -): Promise> => { - const observerId = stringify([ - "waitForUserOperationReceipt", - bundlerClient.uid, - hash - ]) - - let userOperationReceipt: GetUserOperationReceiptReturnType - - return new Promise((resolve, reject) => { - const unobserve = observe( - observerId, - { resolve, reject }, - async (emit) => { - let timeoutTimer: ReturnType - - const _removeInterval = setInterval(async () => { - const done = (fn: () => void) => { - clearInterval(_removeInterval) - fn() - unobserve() - if (timeout) clearTimeout(timeoutTimer) - } - try { - const _userOperationReceipt = await getAction( - bundlerClient, - getUserOperationReceipt, - "getUserOperationReceipt" - )({ hash }) - - if (_userOperationReceipt !== null) { - userOperationReceipt = _userOperationReceipt - } - - if (userOperationReceipt) { - done(() => emit.resolve(userOperationReceipt)) - return - } - } catch (err) { - done(() => emit.reject(err)) - return - } - }, pollingInterval) - - if (timeout) { - timeoutTimer = setTimeout(() => { - clearInterval(_removeInterval) - unobserve() - reject( - new WaitForUserOperationReceiptTimeoutError({ - hash - }) - ) - clearTimeout(timeoutTimer) - }, timeout) - } - } - ) - }) -} diff --git a/packages/permissionless/actions/erc7579.ts b/packages/permissionless/actions/erc7579.ts index f131ede2..bc5ddde4 100644 --- a/packages/permissionless/actions/erc7579.ts +++ b/packages/permissionless/actions/erc7579.ts @@ -1,7 +1,8 @@ import type { Chain, Client, Hash, Transport } from "viem" -import type { SmartAccount } from "../accounts" -import type { GetAccountParameter } from "../types" -import type { EntryPoint } from "../types/entrypoint" +import type { + GetSmartAccountParameter, + SmartAccount +} from "viem/account-abstraction" import { accountId } from "./erc7579/accountId" import { type InstallModuleParameters, @@ -34,79 +35,30 @@ import { uninstallModules } from "./erc7579/uninstallModules" -export type Erc7579Actions< - TEntryPoint extends EntryPoint, - TTransport extends Transport, - TChain extends Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined -> = { +export type Erc7579Actions = { accountId: ( - args?: TSmartAccount extends undefined - ? GetAccountParameter< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - > - : undefined + args?: GetSmartAccountParameter ) => Promise installModule: ( - args: InstallModuleParameters< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - > + args: InstallModuleParameters ) => Promise installModules: ( - args: InstallModulesParameters< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - > + args: InstallModulesParameters ) => Promise isModuleInstalled: ( - args: IsModuleInstalledParameters< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - > + args: IsModuleInstalledParameters ) => Promise supportsExecutionMode: ( - args: SupportsExecutionModeParameters< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - > + args: SupportsExecutionModeParameters ) => Promise supportsModule: ( - args: SupportsModuleParameters< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - > + args: SupportsModuleParameters ) => Promise uninstallModule: ( - args: UninstallModuleParameters< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - > + args: UninstallModuleParameters ) => Promise uninstallModules: ( - args: UninstallModulesParameters< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - > + args: UninstallModulesParameters ) => Promise } @@ -132,55 +84,17 @@ export { uninstallModules } -export function erc7579Actions(_args: { - entryPoint: TEntryPoint -}) { - return < - TTransport extends Transport, - TChain extends Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined - >( - client: Client - ): Erc7579Actions => ({ +export function erc7579Actions() { + return ( + client: Client + ): Erc7579Actions => ({ accountId: (args) => accountId(client, args), - installModule: (args) => - installModule( - client, - args - ), - installModules: (args) => - installModules( - client, - args - ), - isModuleInstalled: (args) => - isModuleInstalled( - client, - args - ), - supportsExecutionMode: (args) => - supportsExecutionMode< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - >(client, args), - supportsModule: (args) => - supportsModule( - client, - args - ), - uninstallModule: (args) => - uninstallModule( - client, - args - ), - uninstallModules: (args) => - uninstallModules( - client, - args - ) + installModule: (args) => installModule(client, args), + installModules: (args) => installModules(client, args), + isModuleInstalled: (args) => isModuleInstalled(client, args), + supportsExecutionMode: (args) => supportsExecutionMode(client, args), + supportsModule: (args) => supportsModule(client, args), + uninstallModule: (args) => uninstallModule(client, args), + uninstallModules: (args) => uninstallModules(client, args) }) } diff --git a/packages/permissionless/actions/erc7579/accountId.test.ts b/packages/permissionless/actions/erc7579/accountId.test.ts index c50ea237..176583e1 100644 --- a/packages/permissionless/actions/erc7579/accountId.test.ts +++ b/packages/permissionless/actions/erc7579/accountId.test.ts @@ -1,12 +1,7 @@ import { zeroAddress } from "viem" -import { generatePrivateKey } from "viem/accounts" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getCoreSmartAccounts, - getPimlicoPaymasterClient -} from "../../../permissionless-test/src/utils" -import { ENTRYPOINT_ADDRESS_V07 } from "../../utils" +import { getCoreSmartAccounts } from "../../../permissionless-test/src/utils" import { accountId } from "./accountId" describe.each(getCoreSmartAccounts())( @@ -15,26 +10,18 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(!getErc7579SmartAccountClient)( "accountId", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - if (!getErc7579SmartAccountClient) { throw new Error("getErc7579SmartAccountClient not defined") } const smartClient = await getErc7579SmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) + entryPoint: { + version: "0.7" + }, + ...rpc }) - const accountIdBeforeDeploy = await accountId( - smartClient as any - ) + const accountIdBeforeDeploy = await accountId(smartClient) // deploy account await smartClient.sendTransaction({ @@ -43,7 +30,7 @@ describe.each(getCoreSmartAccounts())( data: "0x" }) - const postDeployAccountId = await accountId(smartClient as any) + const postDeployAccountId = await accountId(smartClient) expect(accountIdBeforeDeploy).toBe(postDeployAccountId) } diff --git a/packages/permissionless/actions/erc7579/accountId.ts b/packages/permissionless/actions/erc7579/accountId.ts index d7e370bd..6b434ebf 100644 --- a/packages/permissionless/actions/erc7579/accountId.ts +++ b/packages/permissionless/actions/erc7579/accountId.ts @@ -1,5 +1,4 @@ import { - type CallParameters, type Chain, type Client, ContractFunctionExecutionError, @@ -7,22 +6,17 @@ import { decodeFunctionResult, encodeFunctionData } from "viem" -import type { SmartAccount } from "../../accounts/types" -import type { GetAccountParameter } from "../../types" -import type { EntryPoint } from "../../types/entrypoint" -import { parseAccount } from "../../utils/" -import { AccountOrClientNotFoundError } from "../../utils/signUserOperationHashWithECDSA" +import type { + GetSmartAccountParameter, + SmartAccount +} from "viem/account-abstraction" +import { call, readContract } from "viem/actions" +import { getAction } from "viem/utils" +import { AccountNotFoundError } from "../../errors" -export async function accountId< - TEntryPoint extends EntryPoint, - TTransport extends Transport, - TChain extends Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined ->( - client: Client, - args?: GetAccountParameter +export async function accountId( + client: Client, + args?: GetSmartAccountParameter ): Promise { let account_ = client.account @@ -31,17 +25,12 @@ export async function accountId< } if (!account_) { - throw new AccountOrClientNotFoundError({ + throw new AccountNotFoundError({ docsPath: "/docs/actions/wallet/sendTransaction" }) } - const account = parseAccount(account_) as SmartAccount< - TEntryPoint, - string, - TTransport, - TChain - > + const account = account_ as SmartAccount const publicClient = account.client @@ -61,17 +50,24 @@ export async function accountId< ] as const try { - return await publicClient.readContract({ + return await getAction( + publicClient, + readContract, + "readContract" + )({ abi, functionName: "accountId", - address: account.address + address: await account.getAddress() }) } catch (error) { if (error instanceof ContractFunctionExecutionError) { - const factory = await account.getFactory() - const factoryData = await account.getFactoryData() + const { factory, factoryData } = await account.getFactoryArgs() - const result = await publicClient.call({ + const result = await getAction( + publicClient, + call, + "call" + )({ factory: factory, factoryData: factoryData, to: account.address, @@ -79,7 +75,7 @@ export async function accountId< abi, functionName: "accountId" }) - } as unknown as CallParameters) + }) if (!result || !result.data) { throw new Error("accountId result is empty") diff --git a/packages/permissionless/actions/erc7579/installModule.test.ts b/packages/permissionless/actions/erc7579/installModule.test.ts index be030d63..39599afe 100644 --- a/packages/permissionless/actions/erc7579/installModule.test.ts +++ b/packages/permissionless/actions/erc7579/installModule.test.ts @@ -1,61 +1,30 @@ -import { - http, - type Chain, - type Transport, - encodeAbiParameters, - encodePacked, - isHash, - zeroAddress -} from "viem" -import { generatePrivateKey } from "viem/accounts" +import { encodeAbiParameters, encodePacked, isHash, zeroAddress } from "viem" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getCoreSmartAccounts, - getPimlicoPaymasterClient -} from "../../../permissionless-test/src/utils" -import type { SmartAccount } from "../../accounts" -import { createBundlerClient } from "../../clients/createBundlerClient" -import type { SmartAccountClient } from "../../clients/createSmartAccountClient" -import type { ENTRYPOINT_ADDRESS_V07_TYPE } from "../../types/entrypoint" -import { ENTRYPOINT_ADDRESS_V07 } from "../../utils" +import { getCoreSmartAccounts } from "../../../permissionless-test/src/utils" import { erc7579Actions } from "../erc7579" import { installModule } from "./installModule" describe.each(getCoreSmartAccounts())( - "installmodule $name", + "installModule $name", ({ getErc7579SmartAccountClient, name }) => { testWithRpc.skipIf(!getErc7579SmartAccountClient)( "installModule", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - if (!getErc7579SmartAccountClient) { throw new Error("getErc7579SmartAccountClient not defined") } - const privateKey = generatePrivateKey() - - const smartClientWithoutExtend: SmartAccountClient< - ENTRYPOINT_ADDRESS_V07_TYPE, - Transport, - Chain, - SmartAccount - > = await getErc7579SmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: privateKey, - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc + const smartClientWithoutExtend = + await getErc7579SmartAccountClient({ + entryPoint: { + version: "0.7" + }, + ...rpc }) - }) const smartClient = smartClientWithoutExtend.extend( - erc7579Actions({ - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) + erc7579Actions() ) const moduleData = encodePacked( @@ -63,8 +32,8 @@ describe.each(getCoreSmartAccounts())( [smartClient.account.address] ) - const opHash = await installModule(smartClient as any, { - account: smartClient.account as any, + const opHash = await installModule(smartClient, { + account: smartClient.account, type: "executor", address: "0xc98B026383885F41d9a995f85FC480E9bb8bB891", context: name.startsWith("Kernel 7579") @@ -81,25 +50,21 @@ describe.each(getCoreSmartAccounts())( : moduleData }) - const bundlerClientV07 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - expect(isHash(opHash)).toBe(true) const userOperationReceipt = - await bundlerClientV07.waitForUserOperationReceipt({ + await smartClient.waitForUserOperationReceipt({ hash: opHash, timeout: 100000 }) + expect(userOperationReceipt).not.toBeNull() expect(userOperationReceipt?.userOpHash).toBe(opHash) expect( userOperationReceipt?.receipt.transactionHash ).toBeTruthy() - const receipt = await bundlerClientV07.getUserOperationReceipt({ + const receipt = await smartClient.getUserOperationReceipt({ hash: opHash }) @@ -119,38 +84,24 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(!getErc7579SmartAccountClient)( "installModule", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - if (!getErc7579SmartAccountClient) { throw new Error("getErc7579SmartAccountClient not defined") } - const privateKey = generatePrivateKey() - - const smartClientWithoutExtend: SmartAccountClient< - ENTRYPOINT_ADDRESS_V07_TYPE, - Transport, - Chain, - SmartAccount - > = await getErc7579SmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: privateKey, - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc + const smartClientWithoutExtend = + await getErc7579SmartAccountClient({ + entryPoint: { + version: "0.7" + }, + ...rpc }) - }) const smartClient = smartClientWithoutExtend.extend( - erc7579Actions({ - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) + erc7579Actions() ) - await smartClient.sendTransactions({ - transactions: [ + const userOpHash = await smartClient.sendUserOperation({ + calls: [ { to: smartClient.account.address, value: 0n, @@ -164,13 +115,17 @@ describe.each(getCoreSmartAccounts())( ] }) + await smartClient.waitForUserOperationReceipt({ + hash: userOpHash + }) + const moduleData = encodePacked( ["address"], [smartClient.account.address] ) - const opHash = await installModule(smartClient as any, { - account: smartClient.account as any, + const opHash = await installModule(smartClient, { + account: smartClient.account, type: "executor", address: "0xc98B026383885F41d9a995f85FC480E9bb8bB891", context: name.startsWith("Kernel 7579") @@ -187,15 +142,10 @@ describe.each(getCoreSmartAccounts())( : moduleData }) - const bundlerClientV07 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - expect(isHash(opHash)).toBe(true) const userOperationReceipt = - await bundlerClientV07.waitForUserOperationReceipt({ + await smartClient.waitForUserOperationReceipt({ hash: opHash, timeout: 100000 }) @@ -205,7 +155,7 @@ describe.each(getCoreSmartAccounts())( userOperationReceipt?.receipt.transactionHash ).toBeTruthy() - const receipt = await bundlerClientV07.getUserOperationReceipt({ + const receipt = await smartClient.getUserOperationReceipt({ hash: opHash }) diff --git a/packages/permissionless/actions/erc7579/installModule.ts b/packages/permissionless/actions/erc7579/installModule.ts index 2e5a1341..d2b95441 100644 --- a/packages/permissionless/actions/erc7579/installModule.ts +++ b/packages/permissionless/actions/erc7579/installModule.ts @@ -1,132 +1,97 @@ import { type Address, - type Chain, type Client, type Hex, - type Transport, encodeFunctionData, getAddress } from "viem" -import { getAction } from "viem/utils" -import type { SmartAccount } from "../../accounts/types" -import type { GetAccountParameter, Prettify } from "../../types/" -import type { EntryPoint } from "../../types/entrypoint" -import { parseAccount } from "../../utils/" -import { AccountOrClientNotFoundError } from "../../utils/signUserOperationHashWithECDSA" -import type { Middleware } from "../smartAccount/prepareUserOperationRequest" import { - type SendUserOperationParameters, + type GetSmartAccountParameter, + type SmartAccount, sendUserOperation -} from "../smartAccount/sendUserOperation" +} from "viem/account-abstraction" +import { getAction, parseAccount } from "viem/utils" +import { AccountNotFoundError } from "../../errors" import { type ModuleType, parseModuleTypeId } from "./supportsModule" export type InstallModuleParameters< - TEntryPoint extends EntryPoint, - TTransport extends Transport, - TChain extends Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined -> = GetAccountParameter & { + TSmartAccount extends SmartAccount | undefined +> = GetSmartAccountParameter & { type: ModuleType address: Address context: Hex maxFeePerGas?: bigint maxPriorityFeePerGas?: bigint nonce?: bigint -} & Middleware +} export async function installModule< - TEntryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined + TSmartAccount extends SmartAccount | undefined >( - client: Client, - parameters: Prettify< - InstallModuleParameters - > + client: Client, + parameters: InstallModuleParameters ): Promise { const { account: account_ = client.account, maxFeePerGas, maxPriorityFeePerGas, nonce, - middleware, address, context } = parameters if (!account_) { - throw new AccountOrClientNotFoundError({ + throw new AccountNotFoundError({ docsPath: "/docs/actions/wallet/sendTransaction" }) } - const account = parseAccount(account_) as SmartAccount< - TEntryPoint, - string, - TTransport, - TChain - > - - const installModuleCallData = await account.encodeCallData({ - to: account.address, - value: BigInt(0), - data: encodeFunctionData({ - abi: [ - { - name: "installModule", - type: "function", - stateMutability: "nonpayable", - inputs: [ - { - type: "uint256", - name: "moduleTypeId" - }, - { - type: "address", - name: "module" - }, - { - type: "bytes", - name: "initData" - } - ], - outputs: [] - } - ], - functionName: "installModule", - args: [ - parseModuleTypeId(parameters.type), - getAddress(address), - context - ] - }) - }) + const account = parseAccount(account_) as SmartAccount return getAction( client, - sendUserOperation, + sendUserOperation, "sendUserOperation" )({ - userOperation: { - sender: account.address, - maxFeePerGas: maxFeePerGas, - maxPriorityFeePerGas: maxPriorityFeePerGas, - callData: installModuleCallData, - nonce: nonce - }, - account: account, - middleware - } as SendUserOperationParameters< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - >) + calls: [ + { + to: account.address, + value: BigInt(0), + data: encodeFunctionData({ + abi: [ + { + name: "installModule", + type: "function", + stateMutability: "nonpayable", + inputs: [ + { + type: "uint256", + name: "moduleTypeId" + }, + { + type: "address", + name: "module" + }, + { + type: "bytes", + name: "initData" + } + ], + outputs: [] + } + ], + functionName: "installModule", + args: [ + parseModuleTypeId(parameters.type), + getAddress(address), + context + ] + }) + } + ], + maxFeePerGas, + maxPriorityFeePerGas, + nonce, + account + }) } diff --git a/packages/permissionless/actions/erc7579/installModules.test.ts b/packages/permissionless/actions/erc7579/installModules.test.ts index eb89cd04..4500bea6 100644 --- a/packages/permissionless/actions/erc7579/installModules.test.ts +++ b/packages/permissionless/actions/erc7579/installModules.test.ts @@ -1,61 +1,30 @@ -import { - http, - type Chain, - type Transport, - encodeAbiParameters, - encodePacked, - isHash, - zeroAddress -} from "viem" -import { generatePrivateKey } from "viem/accounts" +import { encodeAbiParameters, encodePacked, isHash, zeroAddress } from "viem" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getCoreSmartAccounts, - getPimlicoPaymasterClient -} from "../../../permissionless-test/src/utils" -import type { SmartAccount } from "../../accounts" -import { createBundlerClient } from "../../clients/createBundlerClient" -import type { SmartAccountClient } from "../../clients/createSmartAccountClient" -import type { ENTRYPOINT_ADDRESS_V07_TYPE } from "../../types/entrypoint" -import { ENTRYPOINT_ADDRESS_V07 } from "../../utils" +import { getCoreSmartAccounts } from "../../../permissionless-test/src/utils" import { erc7579Actions } from "../erc7579" import { installModules } from "./installModules" describe.each(getCoreSmartAccounts())( - "installmodules $name", + "installModules $name", ({ getErc7579SmartAccountClient, name }) => { testWithRpc.skipIf(!getErc7579SmartAccountClient)( "installModules", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - if (!getErc7579SmartAccountClient) { throw new Error("getErc7579SmartAccountClient not defined") } - const privateKey = generatePrivateKey() - - const smartClientWithoutExtend: SmartAccountClient< - ENTRYPOINT_ADDRESS_V07_TYPE, - Transport, - Chain, - SmartAccount - > = await getErc7579SmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: privateKey, - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc + const smartClientWithoutExtend = + await getErc7579SmartAccountClient({ + entryPoint: { + version: "0.7" + }, + ...rpc }) - }) const smartClient = smartClientWithoutExtend.extend( - erc7579Actions({ - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) + erc7579Actions() ) const moduleData = encodePacked( @@ -63,8 +32,8 @@ describe.each(getCoreSmartAccounts())( [smartClient.account.address] ) - const opHash = await installModules(smartClient as any, { - account: smartClient.account as any, + const opHash = await installModules(smartClient, { + account: smartClient.account, modules: [ { type: "executor", @@ -89,15 +58,10 @@ describe.each(getCoreSmartAccounts())( ] }) - const bundlerClientV07 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - expect(isHash(opHash)).toBe(true) const userOperationReceipt = - await bundlerClientV07.waitForUserOperationReceipt({ + await smartClient.waitForUserOperationReceipt({ hash: opHash, timeout: 100000 }) @@ -107,7 +71,7 @@ describe.each(getCoreSmartAccounts())( userOperationReceipt?.receipt.transactionHash ).toBeTruthy() - const receipt = await bundlerClientV07.getUserOperationReceipt({ + const receipt = await smartClient.getUserOperationReceipt({ hash: opHash }) @@ -125,40 +89,26 @@ describe.each(getCoreSmartAccounts())( } ) testWithRpc.skipIf(!getErc7579SmartAccountClient)( - "installModule", + "installModules", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - if (!getErc7579SmartAccountClient) { throw new Error("getErc7579SmartAccountClient not defined") } - const privateKey = generatePrivateKey() - - const smartClientWithoutExtend: SmartAccountClient< - ENTRYPOINT_ADDRESS_V07_TYPE, - Transport, - Chain, - SmartAccount - > = await getErc7579SmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: privateKey, - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc + const smartClientWithoutExtend = + await getErc7579SmartAccountClient({ + entryPoint: { + version: "0.7" + }, + ...rpc }) - }) const smartClient = smartClientWithoutExtend.extend( - erc7579Actions({ - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) + erc7579Actions() ) - await smartClient.sendTransactions({ - transactions: [ + const userOpHash = await smartClient.sendUserOperation({ + calls: [ { to: smartClient.account.address, value: 0n, @@ -172,13 +122,17 @@ describe.each(getCoreSmartAccounts())( ] }) + await smartClient.waitForUserOperationReceipt({ + hash: userOpHash + }) + const moduleData = encodePacked( ["address"], [smartClient.account.address] ) - const opHash = await installModules(smartClient as any, { - account: smartClient.account as any, + const opHash = await installModules(smartClient, { + account: smartClient.account, modules: [ { type: "executor", @@ -203,15 +157,10 @@ describe.each(getCoreSmartAccounts())( ] }) - const bundlerClientV07 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - expect(isHash(opHash)).toBe(true) const userOperationReceipt = - await bundlerClientV07.waitForUserOperationReceipt({ + await smartClient.waitForUserOperationReceipt({ hash: opHash, timeout: 100000 }) @@ -221,7 +170,7 @@ describe.each(getCoreSmartAccounts())( userOperationReceipt?.receipt.transactionHash ).toBeTruthy() - const receipt = await bundlerClientV07.getUserOperationReceipt({ + const receipt = await smartClient.getUserOperationReceipt({ hash: opHash }) diff --git a/packages/permissionless/actions/erc7579/installModules.ts b/packages/permissionless/actions/erc7579/installModules.ts index 0f943c4c..ff628bd0 100644 --- a/packages/permissionless/actions/erc7579/installModules.ts +++ b/packages/permissionless/actions/erc7579/installModules.ts @@ -7,133 +7,87 @@ import { encodeFunctionData, getAddress } from "viem" -import { getAction } from "viem/utils" -import type { SmartAccount } from "../../accounts/types" -import type { GetAccountParameter, Prettify } from "../../types/" -import type { EntryPoint } from "../../types/entrypoint" -import { parseAccount } from "../../utils/" -import { AccountOrClientNotFoundError } from "../../utils/signUserOperationHashWithECDSA" -import type { Middleware } from "../smartAccount/prepareUserOperationRequest" import { - type SendUserOperationParameters, + type GetSmartAccountParameter, + type SmartAccount, sendUserOperation -} from "../smartAccount/sendUserOperation" +} from "viem/account-abstraction" +import { getAction, parseAccount } from "viem/utils" +import { AccountNotFoundError } from "../../errors" import { type ModuleType, parseModuleTypeId } from "./supportsModule" export type InstallModulesParameters< - TEntryPoint extends EntryPoint, - TTransport extends Transport, - TChain extends Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined -> = GetAccountParameter & - Middleware & { - modules: { - type: ModuleType - address: Address - context: Hex - }[] - maxFeePerGas?: bigint - maxPriorityFeePerGas?: bigint - nonce?: bigint - } + TSmartAccount extends SmartAccount | undefined +> = GetSmartAccountParameter & { + modules: { + type: ModuleType + address: Address + context: Hex + }[] + maxFeePerGas?: bigint + maxPriorityFeePerGas?: bigint + nonce?: bigint +} export async function installModules< - TEntryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined - | undefined + TSmartAccount extends SmartAccount | undefined >( - client: Client, - parameters: Prettify< - InstallModulesParameters - > + client: Client, + parameters: InstallModulesParameters ): Promise { const { account: account_ = client.account, maxFeePerGas, maxPriorityFeePerGas, nonce, - middleware, modules } = parameters if (!account_) { - throw new AccountOrClientNotFoundError({ + throw new AccountNotFoundError({ docsPath: "/docs/actions/wallet/sendTransaction" }) } - const account = parseAccount(account_) as SmartAccount< - TEntryPoint, - string, - TTransport, - TChain - > - - const installModulesCallData = await account.encodeCallData( - await Promise.all( - modules.map(({ type, address, context }) => ({ - to: account.address, - value: BigInt(0), - data: encodeFunctionData({ - abi: [ - { - name: "installModule", - type: "function", - stateMutability: "nonpayable", - inputs: [ - { - type: "uint256", - name: "moduleTypeId" - }, - { - type: "address", - name: "module" - }, - { - type: "bytes", - name: "initData" - } - ], - outputs: [] - } - ], - functionName: "installModule", - args: [ - parseModuleTypeId(type), - getAddress(address), - context - ] - }) - })) - ) - ) - + const account = parseAccount(account_) as SmartAccount return getAction( client, - sendUserOperation, + sendUserOperation, "sendUserOperation" )({ - userOperation: { - sender: account.address, - maxFeePerGas: maxFeePerGas, - maxPriorityFeePerGas: maxPriorityFeePerGas, - callData: installModulesCallData, - nonce: nonce - }, - account: account, - middleware - } as SendUserOperationParameters< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - >) + calls: modules.map(({ type, address, context }) => ({ + to: account.address, + value: BigInt(0), + data: encodeFunctionData({ + abi: [ + { + name: "installModule", + type: "function", + stateMutability: "nonpayable", + inputs: [ + { + type: "uint256", + name: "moduleTypeId" + }, + { + type: "address", + name: "module" + }, + { + type: "bytes", + name: "initData" + } + ], + outputs: [] + } + ], + functionName: "installModule", + args: [parseModuleTypeId(type), getAddress(address), context] + }) + })), + maxFeePerGas, + maxPriorityFeePerGas, + nonce, + account: account + }) } diff --git a/packages/permissionless/actions/erc7579/isModuleInstalled.test.ts b/packages/permissionless/actions/erc7579/isModuleInstalled.test.ts index 9298b2d1..e2b2648f 100644 --- a/packages/permissionless/actions/erc7579/isModuleInstalled.test.ts +++ b/packages/permissionless/actions/erc7579/isModuleInstalled.test.ts @@ -1,23 +1,7 @@ -import { - http, - type Chain, - type Transport, - encodeAbiParameters, - encodePacked, - zeroAddress -} from "viem" -import { generatePrivateKey, privateKeyToAccount } from "viem/accounts" +import { encodeAbiParameters, encodePacked, zeroAddress } from "viem" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getCoreSmartAccounts, - getPimlicoPaymasterClient -} from "../../../permissionless-test/src/utils" -import type { SmartAccount } from "../../accounts" -import { createBundlerClient } from "../../clients/createBundlerClient" -import type { SmartAccountClient } from "../../clients/createSmartAccountClient" -import type { ENTRYPOINT_ADDRESS_V07_TYPE } from "../../types/entrypoint" -import { ENTRYPOINT_ADDRESS_V07 } from "../../utils" +import { getCoreSmartAccounts } from "../../../permissionless-test/src/utils" import { erc7579Actions } from "../erc7579" import { isModuleInstalled } from "./isModuleInstalled" @@ -27,34 +11,20 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(!getErc7579SmartAccountClient)( "isModuleInstalled", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - if (!getErc7579SmartAccountClient) { throw new Error("getErc7579SmartAccountClient not defined") } - const privateKey = generatePrivateKey() - - const smartClientWithoutExtend: SmartAccountClient< - ENTRYPOINT_ADDRESS_V07_TYPE, - Transport, - Chain, - SmartAccount - > = await getErc7579SmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: privateKey, - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc + const smartClientWithoutExtend = + await getErc7579SmartAccountClient({ + entryPoint: { + version: "0.7" + }, + ...rpc }) - }) const smartClient = smartClientWithoutExtend.extend( - erc7579Actions({ - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) + erc7579Actions() ) const moduleData = encodePacked( @@ -80,21 +50,15 @@ describe.each(getCoreSmartAccounts())( : moduleData }) - const bundlerClientV07 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 + await smartClient.waitForUserOperationReceipt({ + hash: opHash, + timeout: 100000 }) - const receipt = - await bundlerClientV07.waitForUserOperationReceipt({ - hash: opHash, - timeout: 100000 - }) - const isModuleInstalledResult = await isModuleInstalled( - smartClient as any, + smartClient, { - account: smartClient.account as any, + account: smartClient.account, type: "executor", address: "0xc98B026383885F41d9a995f85FC480E9bb8bB891", context: "0x" @@ -102,13 +66,6 @@ describe.each(getCoreSmartAccounts())( ) expect(isModuleInstalledResult).toBe(true) - - const nextTransaction = await smartClient.sendTransaction({ - to: zeroAddress, - value: 0n - }) - - expect(nextTransaction).toBeTruthy() } ) } diff --git a/packages/permissionless/actions/erc7579/isModuleInstalled.ts b/packages/permissionless/actions/erc7579/isModuleInstalled.ts index 6ab614b1..6454fa0a 100644 --- a/packages/permissionless/actions/erc7579/isModuleInstalled.ts +++ b/packages/permissionless/actions/erc7579/isModuleInstalled.ts @@ -1,6 +1,5 @@ import { type Address, - type CallParameters, type Chain, type Client, ContractFunctionExecutionError, @@ -10,60 +9,38 @@ import { encodeFunctionData, getAddress } from "viem" -import type { SmartAccount } from "../../accounts/types" -import type { GetAccountParameter } from "../../types/" -import type { EntryPoint } from "../../types/entrypoint" -import { parseAccount } from "../../utils/" -import { AccountOrClientNotFoundError } from "../../utils/signUserOperationHashWithECDSA" +import type { + GetSmartAccountParameter, + SmartAccount +} from "viem/account-abstraction" +import { call, readContract } from "viem/actions" +import { getAction, parseAccount } from "viem/utils" +import { AccountNotFoundError } from "../../errors" import { type ModuleType, parseModuleTypeId } from "./supportsModule" export type IsModuleInstalledParameters< - TEntryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined -> = GetAccountParameter & { + TSmartAccount extends SmartAccount | undefined +> = GetSmartAccountParameter & { type: ModuleType address: Address context: Hex } export async function isModuleInstalled< - TEntryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined + TSmartAccount extends SmartAccount | undefined >( - client: Client, - parameters: IsModuleInstalledParameters< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - > + client: Client, + parameters: IsModuleInstalledParameters ): Promise { const { account: account_ = client.account, address, context } = parameters if (!account_) { - throw new AccountOrClientNotFoundError({ + throw new AccountNotFoundError({ docsPath: "/docs/actions/wallet/sendTransaction" }) } - const account = parseAccount(account_) as SmartAccount< - TEntryPoint, - string, - TTransport, - TChain - > + const account = parseAccount(account_) as SmartAccount const publicClient = account.client @@ -95,7 +72,11 @@ export async function isModuleInstalled< ] as const try { - return await publicClient.readContract({ + return await getAction( + publicClient, + readContract, + "readContract" + )({ abi, functionName: "isModuleInstalled", args: [ @@ -107,10 +88,13 @@ export async function isModuleInstalled< }) } catch (error) { if (error instanceof ContractFunctionExecutionError) { - const factory = await account.getFactory() - const factoryData = await account.getFactoryData() + const { factory, factoryData } = await account.getFactoryArgs() - const result = await publicClient.call({ + const result = await getAction( + publicClient, + call, + "call" + )({ factory: factory, factoryData: factoryData, to: account.address, @@ -123,7 +107,7 @@ export async function isModuleInstalled< context ] }) - } as unknown as CallParameters) + }) if (!result || !result.data) { throw new Error("accountId result is empty") diff --git a/packages/permissionless/actions/erc7579/supportsExecutionMode.test.ts b/packages/permissionless/actions/erc7579/supportsExecutionMode.test.ts index e2c77bd5..c89e3a4e 100644 --- a/packages/permissionless/actions/erc7579/supportsExecutionMode.test.ts +++ b/packages/permissionless/actions/erc7579/supportsExecutionMode.test.ts @@ -1,12 +1,7 @@ import { zeroAddress } from "viem" -import { generatePrivateKey } from "viem/accounts" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getCoreSmartAccounts, - getPimlicoPaymasterClient -} from "../../../permissionless-test/src/utils" -import { ENTRYPOINT_ADDRESS_V07 } from "../../utils" +import { getCoreSmartAccounts } from "../../../permissionless-test/src/utils" import { supportsExecutionMode } from "./supportsExecutionMode" describe.each(getCoreSmartAccounts())( @@ -15,26 +10,20 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(!getErc7579SmartAccountClient)( "supportsExecutionMode", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - if (!getErc7579SmartAccountClient) { throw new Error("getErc7579SmartAccountClient not defined") } const smartClient = await getErc7579SmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) + entryPoint: { + version: "0.7" + }, + ...rpc }) const supportsExecutionModeBatchCallBeforeDeploy = - await supportsExecutionMode(smartClient as any, { - account: smartClient.account as any, + await supportsExecutionMode(smartClient, { + account: smartClient.account, type: "batchcall", revertOnError: false, selector: "0x0", @@ -51,8 +40,8 @@ describe.each(getCoreSmartAccounts())( }) const supportsExecutionModeBatchCallBeforeDeployPostDeploy = - await supportsExecutionMode(smartClient as any, { - account: smartClient.account as any, + await supportsExecutionMode(smartClient, { + account: smartClient.account, type: "batchcall", revertOnError: false, selector: "0x0", @@ -71,26 +60,20 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(!getErc7579SmartAccountClient)( "supportsExecutionMode", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - if (!getErc7579SmartAccountClient) { throw new Error("getErc7579SmartAccountClient not defined") } const smartClient = await getErc7579SmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) + entryPoint: { + version: "0.7" + }, + ...rpc }) const supportsExecutionModeBatchCallBeforeDeploy = - await supportsExecutionMode(smartClient as any, { - account: smartClient.account as any, + await supportsExecutionMode(smartClient, { + account: smartClient.account, type: "delegatecall" }) @@ -104,8 +87,8 @@ describe.each(getCoreSmartAccounts())( }) const supportsExecutionModeBatchCallBeforeDeployPostDeploy = - await supportsExecutionMode(smartClient as any, { - account: smartClient.account as any, + await supportsExecutionMode(smartClient, { + account: smartClient.account, type: "batchcall", revertOnError: false, selector: "0x0", diff --git a/packages/permissionless/actions/erc7579/supportsExecutionMode.ts b/packages/permissionless/actions/erc7579/supportsExecutionMode.ts index da0d27a3..e08720e1 100644 --- a/packages/permissionless/actions/erc7579/supportsExecutionMode.ts +++ b/packages/permissionless/actions/erc7579/supportsExecutionMode.ts @@ -1,5 +1,4 @@ import { - type CallParameters, type Chain, type Client, ContractFunctionExecutionError, @@ -11,11 +10,14 @@ import { toBytes, toHex } from "viem" -import type { SmartAccount } from "../../accounts/types" -import type { GetAccountParameter, Prettify } from "../../types/" -import type { EntryPoint } from "../../types/entrypoint" -import { parseAccount } from "../../utils/" -import { AccountOrClientNotFoundError } from "../../utils/signUserOperationHashWithECDSA" +import type { + GetSmartAccountParameter, + SmartAccount +} from "viem/account-abstraction" +import { call, readContract } from "viem/actions" +import { getAction } from "viem/utils" +import { parseAccount } from "viem/utils" +import { AccountNotFoundError } from "../../errors" export type CallType = "call" | "delegatecall" | "batchcall" @@ -27,17 +29,9 @@ export type ExecutionMode = { } export type SupportsExecutionModeParameters< - TEntryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined, + TSmartAccount extends SmartAccount | undefined, callType extends CallType = CallType -> = GetAccountParameter & - ExecutionMode +> = GetSmartAccountParameter & ExecutionMode function parseCallType(callType: CallType) { switch (callType) { @@ -69,24 +63,11 @@ export function encodeExecutionMode({ } export async function supportsExecutionMode< - TEntryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined + TSmartAccount extends SmartAccount | undefined, + callType extends CallType = CallType >( - client: Client, - args: Prettify< - SupportsExecutionModeParameters< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - > - > + client: Client, + args: SupportsExecutionModeParameters ): Promise { const { account: account_ = client.account, @@ -97,17 +78,12 @@ export async function supportsExecutionMode< } = args if (!account_) { - throw new AccountOrClientNotFoundError({ + throw new AccountNotFoundError({ docsPath: "/docs/actions/wallet/sendTransaction" }) } - const account = parseAccount(account_) as SmartAccount< - TEntryPoint, - string, - TTransport, - TChain - > + const account = parseAccount(account_) as SmartAccount const publicClient = account.client @@ -138,7 +114,11 @@ export async function supportsExecutionMode< ] as const try { - return await publicClient.readContract({ + return await getAction( + publicClient, + readContract, + "readContract" + )({ abi, functionName: "supportsExecutionMode", args: [encodedMode], @@ -146,10 +126,13 @@ export async function supportsExecutionMode< }) } catch (error) { if (error instanceof ContractFunctionExecutionError) { - const factory = await account.getFactory() - const factoryData = await account.getFactoryData() + const { factory, factoryData } = await account.getFactoryArgs() - const result = await publicClient.call({ + const result = await getAction( + publicClient, + call, + "call" + )({ factory: factory, factoryData: factoryData, to: account.address, @@ -158,7 +141,7 @@ export async function supportsExecutionMode< functionName: "supportsExecutionMode", args: [encodedMode] }) - } as unknown as CallParameters) + }) if (!result || !result.data) { throw new Error("accountId result is empty") diff --git a/packages/permissionless/actions/erc7579/supportsModule.test.ts b/packages/permissionless/actions/erc7579/supportsModule.test.ts index a246954d..7309ed1a 100644 --- a/packages/permissionless/actions/erc7579/supportsModule.test.ts +++ b/packages/permissionless/actions/erc7579/supportsModule.test.ts @@ -1,11 +1,6 @@ -import { generatePrivateKey } from "viem/accounts" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getCoreSmartAccounts, - getPimlicoPaymasterClient -} from "../../../permissionless-test/src/utils" -import { ENTRYPOINT_ADDRESS_V07 } from "../../utils" +import { getCoreSmartAccounts } from "../../../permissionless-test/src/utils" import { supportsModule } from "./supportsModule" describe.each(getCoreSmartAccounts())( @@ -14,27 +9,21 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(!getErc7579SmartAccountClient)( "supportsModule", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - if (!getErc7579SmartAccountClient) { throw new Error("getErc7579SmartAccountClient not defined") } const smartClient = await getErc7579SmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) + entryPoint: { + version: "0.7" + }, + ...rpc }) const supportsValidationModule = await supportsModule( - smartClient as any, + smartClient, { - account: smartClient.account as any, + account: smartClient.account, type: "validator" } ) diff --git a/packages/permissionless/actions/erc7579/supportsModule.ts b/packages/permissionless/actions/erc7579/supportsModule.ts index 3396d5f5..b2a8951f 100644 --- a/packages/permissionless/actions/erc7579/supportsModule.ts +++ b/packages/permissionless/actions/erc7579/supportsModule.ts @@ -1,5 +1,4 @@ import { - type CallParameters, type Chain, type Client, ContractFunctionExecutionError, @@ -7,24 +6,20 @@ import { decodeFunctionResult, encodeFunctionData } from "viem" -import type { SmartAccount } from "../../accounts/types" -import type { GetAccountParameter, Prettify } from "../../types/" -import type { EntryPoint } from "../../types/entrypoint" -import { parseAccount } from "../../utils/" -import { AccountOrClientNotFoundError } from "../../utils/signUserOperationHashWithECDSA" +import type { + GetSmartAccountParameter, + SmartAccount +} from "viem/account-abstraction" +import { call, readContract } from "viem/actions" +import { getAction } from "viem/utils" +import { parseAccount } from "viem/utils" +import { AccountNotFoundError } from "../../errors" export type ModuleType = "validator" | "executor" | "fallback" | "hook" export type SupportsModuleParameters< - TEntryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined -> = GetAccountParameter & { + TSmartAccount extends SmartAccount | undefined +> = GetSmartAccountParameter & { type: ModuleType } @@ -44,34 +39,20 @@ export function parseModuleTypeId(type: ModuleType): bigint { } export async function supportsModule< - TEntryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined + TSmartAccount extends SmartAccount | undefined >( - client: Client, - args: Prettify< - SupportsModuleParameters - > + client: Client, + args: SupportsModuleParameters ): Promise { const { account: account_ = client.account } = args if (!account_) { - throw new AccountOrClientNotFoundError({ + throw new AccountNotFoundError({ docsPath: "/docs/actions/wallet/sendTransaction" }) } - const account = parseAccount(account_) as SmartAccount< - TEntryPoint, - string, - TTransport, - TChain - > + const account = parseAccount(account_) as SmartAccount const publicClient = account.client @@ -95,7 +76,11 @@ export async function supportsModule< ] as const try { - return await publicClient.readContract({ + return await getAction( + publicClient, + readContract, + "readContract" + )({ abi, functionName: "supportsModule", args: [parseModuleTypeId(args.type)], @@ -103,10 +88,13 @@ export async function supportsModule< }) } catch (error) { if (error instanceof ContractFunctionExecutionError) { - const factory = await account.getFactory() - const factoryData = await account.getFactoryData() + const { factory, factoryData } = await account.getFactoryArgs() - const result = await publicClient.call({ + const result = await getAction( + publicClient, + call, + "call" + )({ factory: factory, factoryData: factoryData, to: account.address, @@ -115,7 +103,7 @@ export async function supportsModule< functionName: "supportsModule", args: [parseModuleTypeId(args.type)] }) - } as unknown as CallParameters) + }) if (!result || !result.data) { throw new Error("accountId result is empty") diff --git a/packages/permissionless/actions/erc7579/uninstallModule.test.ts b/packages/permissionless/actions/erc7579/uninstallModule.test.ts index 085c85bc..1da2316f 100644 --- a/packages/permissionless/actions/erc7579/uninstallModule.test.ts +++ b/packages/permissionless/actions/erc7579/uninstallModule.test.ts @@ -1,25 +1,7 @@ -import { - http, - type Chain, - type Transport, - encodeAbiParameters, - encodePacked, - isHash, - zeroAddress -} from "viem" -import { generatePrivateKey, privateKeyToAccount } from "viem/accounts" +import { encodeAbiParameters, encodePacked, isHash, zeroAddress } from "viem" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getCoreSmartAccounts, - getPimlicoPaymasterClient, - getPublicClient -} from "../../../permissionless-test/src/utils" -import type { SmartAccount } from "../../accounts" -import { createBundlerClient } from "../../clients/createBundlerClient" -import type { SmartAccountClient } from "../../clients/createSmartAccountClient" -import type { ENTRYPOINT_ADDRESS_V07_TYPE } from "../../types" -import { ENTRYPOINT_ADDRESS_V07 } from "../../utils" +import { getCoreSmartAccounts } from "../../../permissionless-test/src/utils" import { erc7579Actions } from "../erc7579" import { uninstallModule } from "./uninstallModule" @@ -29,36 +11,20 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(!getErc7579SmartAccountClient)( "uninstallModule", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - if (!getErc7579SmartAccountClient) { throw new Error("getErc7579SmartAccountClient not defined") } - const privateKey = generatePrivateKey() - - const eoaAccount = privateKeyToAccount(privateKey) - - const smartClientWithoutExtend: SmartAccountClient< - ENTRYPOINT_ADDRESS_V07_TYPE, - Transport, - Chain, - SmartAccount - > = await getErc7579SmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: privateKey, - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc + const smartClientWithoutExtend = + await getErc7579SmartAccountClient({ + entryPoint: { + version: "0.7" + }, + ...rpc }) - }) const smartClient = smartClientWithoutExtend.extend( - erc7579Actions({ - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) + erc7579Actions() ) const moduleData = encodePacked( @@ -66,13 +32,6 @@ describe.each(getCoreSmartAccounts())( [smartClient.account.address] ) - const bundlerClientV07 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - - const publicClient = getPublicClient(anvilRpc) - const opHash = await smartClient.installModule({ type: "executor", address: "0xc98B026383885F41d9a995f85FC480E9bb8bB891", @@ -90,20 +49,15 @@ describe.each(getCoreSmartAccounts())( : moduleData }) - const userOperationReceipt = - await bundlerClientV07.waitForUserOperationReceipt({ - hash: opHash, - timeout: 100000 - }) - - await publicClient.waitForTransactionReceipt({ - hash: userOperationReceipt.receipt.transactionHash + await smartClient.waitForUserOperationReceipt({ + hash: opHash, + timeout: 100000 }) const uninstallModuleUserOpHash = await uninstallModule( - smartClient as any, + smartClient, { - account: smartClient.account as any, + account: smartClient.account, type: "executor", address: "0xc98B026383885F41d9a995f85FC480E9bb8bB891", context: name.startsWith("Kernel 7579") @@ -127,7 +81,7 @@ describe.each(getCoreSmartAccounts())( expect(isHash(uninstallModuleUserOpHash)).toBe(true) const userOperationReceiptUninstallModule = - await bundlerClientV07.waitForUserOperationReceipt({ + await smartClient.waitForUserOperationReceipt({ hash: uninstallModuleUserOpHash, timeout: 100000 }) @@ -140,7 +94,7 @@ describe.each(getCoreSmartAccounts())( ).toBeTruthy() const receiptUninstallModule = - await bundlerClientV07.getUserOperationReceipt({ + await smartClient.getUserOperationReceipt({ hash: uninstallModuleUserOpHash }) diff --git a/packages/permissionless/actions/erc7579/uninstallModule.ts b/packages/permissionless/actions/erc7579/uninstallModule.ts index 617eb7dd..d4032607 100644 --- a/packages/permissionless/actions/erc7579/uninstallModule.ts +++ b/packages/permissionless/actions/erc7579/uninstallModule.ts @@ -7,133 +7,94 @@ import { encodeFunctionData, getAddress } from "viem" -import { getAction } from "viem/utils" -import type { SmartAccount } from "../../accounts/types" -import type { GetAccountParameter, Prettify } from "../../types/" -import type { EntryPoint } from "../../types/entrypoint" -import { parseAccount } from "../../utils/" -import { AccountOrClientNotFoundError } from "../../utils/signUserOperationHashWithECDSA" -import type { Middleware } from "../smartAccount/prepareUserOperationRequest" import { - type SendUserOperationParameters, + type GetSmartAccountParameter, + type SmartAccount, sendUserOperation -} from "../smartAccount/sendUserOperation" +} from "viem/account-abstraction" +import { getAction } from "viem/utils" +import { parseAccount } from "viem/utils" +import { AccountNotFoundError } from "../../errors" import { type ModuleType, parseModuleTypeId } from "./supportsModule" export type UninstallModuleParameters< - TEntryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined -> = GetAccountParameter & { + TSmartAccount extends SmartAccount | undefined +> = GetSmartAccountParameter & { type: ModuleType address: Address context: Hex maxFeePerGas?: bigint maxPriorityFeePerGas?: bigint nonce?: bigint -} & Middleware +} export async function uninstallModule< - TEntryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined + TSmartAccount extends SmartAccount | undefined >( - client: Client, - parameters: Prettify< - UninstallModuleParameters< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - > - > + client: Client, + parameters: UninstallModuleParameters ): Promise { const { account: account_ = client.account, maxFeePerGas, maxPriorityFeePerGas, nonce, - middleware, address, context } = parameters if (!account_) { - throw new AccountOrClientNotFoundError({ + throw new AccountNotFoundError({ docsPath: "/docs/actions/wallet/sendTransaction" }) } - const account = parseAccount(account_) as SmartAccount< - TEntryPoint, - string, - TTransport, - TChain - > - - const uninstallModuleCallData = await account.encodeCallData({ - to: account.address, - value: BigInt(0), - data: encodeFunctionData({ - abi: [ - { - name: "uninstallModule", - type: "function", - stateMutability: "nonpayable", - inputs: [ - { - type: "uint256", - name: "moduleTypeId" - }, - { - type: "address", - name: "module" - }, - { - type: "bytes", - name: "deInitData" - } - ], - outputs: [] - } - ], - functionName: "uninstallModule", - args: [ - parseModuleTypeId(parameters.type), - getAddress(address), - context - ] - }) - }) + const account = parseAccount(account_) as SmartAccount return getAction( client, - sendUserOperation, + sendUserOperation, "sendUserOperation" )({ - userOperation: { - sender: account.address, - maxFeePerGas: maxFeePerGas, - maxPriorityFeePerGas: maxPriorityFeePerGas, - callData: uninstallModuleCallData, - nonce: nonce - }, - account: account, - middleware - } as SendUserOperationParameters< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - >) + calls: [ + { + to: account.address, + value: BigInt(0), + data: encodeFunctionData({ + abi: [ + { + name: "uninstallModule", + type: "function", + stateMutability: "nonpayable", + inputs: [ + { + type: "uint256", + name: "moduleTypeId" + }, + { + type: "address", + name: "module" + }, + { + type: "bytes", + name: "deInitData" + } + ], + outputs: [] + } + ], + functionName: "uninstallModule", + args: [ + parseModuleTypeId(parameters.type), + getAddress(address), + context + ] + }) + } + ], + maxFeePerGas, + maxPriorityFeePerGas, + nonce, + account: account + }) } diff --git a/packages/permissionless/actions/erc7579/uninstallModules.test.ts b/packages/permissionless/actions/erc7579/uninstallModules.test.ts index 290be0b4..9da4f4e3 100644 --- a/packages/permissionless/actions/erc7579/uninstallModules.test.ts +++ b/packages/permissionless/actions/erc7579/uninstallModules.test.ts @@ -1,25 +1,7 @@ -import { - http, - type Chain, - type Transport, - encodeAbiParameters, - encodePacked, - isHash, - zeroAddress -} from "viem" -import { generatePrivateKey, privateKeyToAccount } from "viem/accounts" +import { encodeAbiParameters, encodePacked, isHash, zeroAddress } from "viem" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getCoreSmartAccounts, - getPimlicoPaymasterClient, - getPublicClient -} from "../../../permissionless-test/src/utils" -import type { SmartAccount } from "../../accounts" -import { createBundlerClient } from "../../clients/createBundlerClient" -import type { SmartAccountClient } from "../../clients/createSmartAccountClient" -import type { ENTRYPOINT_ADDRESS_V07_TYPE } from "../../types" -import { ENTRYPOINT_ADDRESS_V07 } from "../../utils" +import { getCoreSmartAccounts } from "../../../permissionless-test/src/utils" import { erc7579Actions } from "../erc7579" import { uninstallModules } from "./uninstallModules" @@ -29,36 +11,20 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(!getErc7579SmartAccountClient)( "uninstallModules", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - if (!getErc7579SmartAccountClient) { throw new Error("getErc7579SmartAccountClient not defined") } - const privateKey = generatePrivateKey() - - const eoaAccount = privateKeyToAccount(privateKey) - - const smartClientWithoutExtend: SmartAccountClient< - ENTRYPOINT_ADDRESS_V07_TYPE, - Transport, - Chain, - SmartAccount - > = await getErc7579SmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: privateKey, - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc + const smartClientWithoutExtend = + await getErc7579SmartAccountClient({ + entryPoint: { + version: "0.7" + }, + ...rpc }) - }) const smartClient = smartClientWithoutExtend.extend( - erc7579Actions({ - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) + erc7579Actions() ) const moduleData = encodePacked( @@ -66,13 +32,6 @@ describe.each(getCoreSmartAccounts())( [smartClient.account.address] ) - const bundlerClientV07 = createBundlerClient({ - transport: http(altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - - const publicClient = getPublicClient(anvilRpc) - const opHash = await smartClient.installModule({ type: "executor", address: "0xc98B026383885F41d9a995f85FC480E9bb8bB891", @@ -90,20 +49,15 @@ describe.each(getCoreSmartAccounts())( : moduleData }) - const userOperationReceipt = - await bundlerClientV07.waitForUserOperationReceipt({ - hash: opHash, - timeout: 100000 - }) - - await publicClient.waitForTransactionReceipt({ - hash: userOperationReceipt.receipt.transactionHash + await smartClient.waitForUserOperationReceipt({ + hash: opHash, + timeout: 100000 }) const uninstallModulesUserOpHash = await uninstallModules( - smartClient as any, + smartClient, { - account: smartClient.account as any, + account: smartClient.account, modules: [ { type: "executor", @@ -135,7 +89,7 @@ describe.each(getCoreSmartAccounts())( expect(isHash(uninstallModulesUserOpHash)).toBe(true) const userOperationReceiptUninstallModules = - await bundlerClientV07.waitForUserOperationReceipt({ + await smartClient.waitForUserOperationReceipt({ hash: uninstallModulesUserOpHash, timeout: 100000 }) @@ -149,7 +103,7 @@ describe.each(getCoreSmartAccounts())( ).toBeTruthy() const receiptUninstallModules = - await bundlerClientV07.getUserOperationReceipt({ + await smartClient.getUserOperationReceipt({ hash: uninstallModulesUserOpHash }) diff --git a/packages/permissionless/actions/erc7579/uninstallModules.ts b/packages/permissionless/actions/erc7579/uninstallModules.ts index e9a12853..5e25736a 100644 --- a/packages/permissionless/actions/erc7579/uninstallModules.ts +++ b/packages/permissionless/actions/erc7579/uninstallModules.ts @@ -7,29 +7,19 @@ import { encodeFunctionData, getAddress } from "viem" -import { getAction } from "viem/utils" -import type { SmartAccount } from "../../accounts/types" -import type { GetAccountParameter, Prettify } from "../../types/" -import type { EntryPoint } from "../../types/entrypoint" -import { parseAccount } from "../../utils/" -import { AccountOrClientNotFoundError } from "../../utils/signUserOperationHashWithECDSA" -import type { Middleware } from "../smartAccount/prepareUserOperationRequest" import { - type SendUserOperationParameters, + type GetSmartAccountParameter, + type SmartAccount, sendUserOperation -} from "../smartAccount/sendUserOperation" +} from "viem/account-abstraction" +import { getAction } from "viem/utils" +import { parseAccount } from "viem/utils" +import { AccountNotFoundError } from "../../errors" import { type ModuleType, parseModuleTypeId } from "./supportsModule" export type UninstallModulesParameters< - TEntryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined -> = GetAccountParameter & { + TSmartAccount extends SmartAccount | undefined +> = GetSmartAccountParameter & { modules: [ { type: ModuleType @@ -40,107 +30,68 @@ export type UninstallModulesParameters< maxFeePerGas?: bigint maxPriorityFeePerGas?: bigint nonce?: bigint -} & Middleware +} export async function uninstallModules< - TEntryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined + TSmartAccount extends SmartAccount | undefined >( - client: Client, - parameters: Prettify< - UninstallModulesParameters< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - > - > + client: Client, + parameters: UninstallModulesParameters ): Promise { const { account: account_ = client.account, maxFeePerGas, maxPriorityFeePerGas, nonce, - middleware, modules } = parameters if (!account_) { - throw new AccountOrClientNotFoundError({ + throw new AccountNotFoundError({ docsPath: "/docs/actions/wallet/sendTransaction" }) } - const account = parseAccount(account_) as SmartAccount< - TEntryPoint, - string, - TTransport, - TChain - > - - const uninstallModulesCallData = await account.encodeCallData( - await Promise.all( - modules.map(({ type, address, context }) => ({ - to: account.address, - value: BigInt(0), - data: encodeFunctionData({ - abi: [ - { - name: "uninstallModule", - type: "function", - stateMutability: "nonpayable", - inputs: [ - { - type: "uint256", - name: "moduleTypeId" - }, - { - type: "address", - name: "module" - }, - { - type: "bytes", - name: "deInitData" - } - ], - outputs: [] - } - ], - functionName: "uninstallModule", - args: [ - parseModuleTypeId(type), - getAddress(address), - context - ] - }) - })) - ) - ) + const account = parseAccount(account_) as SmartAccount return getAction( client, - sendUserOperation, + sendUserOperation, "sendUserOperation" )({ - userOperation: { - sender: account.address, - maxFeePerGas: maxFeePerGas, - maxPriorityFeePerGas: maxPriorityFeePerGas, - callData: uninstallModulesCallData, - nonce: nonce - }, - account: account, - middleware - } as SendUserOperationParameters< - TEntryPoint, - TTransport, - TChain, - TSmartAccount - >) + calls: modules.map(({ type, address, context }) => ({ + to: account.address, + value: BigInt(0), + data: encodeFunctionData({ + abi: [ + { + name: "uninstallModule", + type: "function", + stateMutability: "nonpayable", + inputs: [ + { + type: "uint256", + name: "moduleTypeId" + }, + { + type: "address", + name: "module" + }, + { + type: "bytes", + name: "deInitData" + } + ], + outputs: [] + } + ], + functionName: "uninstallModule", + args: [parseModuleTypeId(type), getAddress(address), context] + }) + })), + maxFeePerGas, + maxPriorityFeePerGas, + nonce, + account + }) } diff --git a/packages/permissionless/actions/index.test.ts b/packages/permissionless/actions/index.test.ts deleted file mode 100644 index 4e992fd3..00000000 --- a/packages/permissionless/actions/index.test.ts +++ /dev/null @@ -1,329 +0,0 @@ -import type { - Account, - Address, - Chain, - Client, - Hash, - Hex, - Log, - Transport -} from "viem" -import type { PartialBy } from "viem/chains" -import { describe, expectTypeOf, test } from "vitest" -import type { BundlerRpcSchema } from "../types/bundler" -import type { - ENTRYPOINT_ADDRESS_V06_TYPE, - ENTRYPOINT_ADDRESS_V07_TYPE, - EntryPoint -} from "../types/entrypoint" -import type { TStatus, UserOperation } from "../types/userOperation" -import { - chainId, - estimateUserOperationGas, - getAccountNonce, - getSenderAddress, - getUserOperationByHash, - getUserOperationReceipt, - sendUserOperation, - supportedEntryPoints, - waitForUserOperationReceipt -} from "./index" - -describe("index", () => { - test("sendUserOperation", () => { - expectTypeOf(sendUserOperation).toBeFunction() - expectTypeOf(sendUserOperation) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - expectTypeOf(sendUserOperation).parameter(1).toMatchTypeOf<{ - userOperation: UserOperation<"v0.6" | "v0.7"> - entryPoint: EntryPoint - }>() - expectTypeOf(sendUserOperation).returns.toMatchTypeOf>() - }) - - test("estimateUserOperationGas", () => { - expectTypeOf(estimateUserOperationGas).toBeFunction() - expectTypeOf(estimateUserOperationGas) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - - expectTypeOf(estimateUserOperationGas).parameter(1).toMatchTypeOf<{ - userOperation: - | PartialBy< - UserOperation<"v0.6">, - | "callGasLimit" - | "preVerificationGas" - | "verificationGasLimit" - > - | PartialBy< - UserOperation<"v0.7">, - | "callGasLimit" - | "preVerificationGas" - | "verificationGasLimit" - | "paymasterVerificationGasLimit" - | "paymasterPostOpGasLimit" - > - entryPoint: EntryPoint - }>() - expectTypeOf(estimateUserOperationGas).parameter(2).toMatchTypeOf< - | { - [x: string]: { - balance?: bigint | undefined - nonce?: bigint | number | undefined - code?: Hex | undefined - state?: { - [x: Hex]: Hex - } - stateDiff?: { - [x: Hex]: Hex - } - } - } - | undefined - >() - expectTypeOf( - estimateUserOperationGas - ).returns.toMatchTypeOf< - Promise<{ - preVerificationGas: bigint - verificationGasLimit: bigint - callGasLimit: bigint - }> - >() - expectTypeOf( - estimateUserOperationGas - ).returns.toMatchTypeOf< - Promise<{ - preVerificationGas: bigint - verificationGasLimit: bigint - callGasLimit: bigint - paymasterVerificationGasLimit?: bigint - paymasterPostOpGasLimit?: bigint - }> - >() - }) - - test("supportedEntryPoints", () => { - expectTypeOf(supportedEntryPoints) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - - expectTypeOf(supportedEntryPoints).returns.toMatchTypeOf< - Promise - >() - }) - - test("chainId", () => { - expectTypeOf(chainId) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - - expectTypeOf(chainId).returns.toMatchTypeOf>() - }) - - test("getUserOperationByHash", () => { - expectTypeOf(getUserOperationByHash) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - expectTypeOf(getUserOperationByHash).parameter(1).toMatchTypeOf<{ - hash: Hash - }>() - expectTypeOf( - getUserOperationByHash - ).returns.toMatchTypeOf< - Promise<{ - userOperation: UserOperation<"v0.6"> - entryPoint: Address - transactionHash: Hash - blockHash: Hash - blockNumber: bigint - } | null> - >() - expectTypeOf( - getUserOperationByHash - ).returns.toMatchTypeOf< - Promise<{ - userOperation: UserOperation<"v0.7"> - entryPoint: Address - transactionHash: Hash - blockHash: Hash - blockNumber: bigint - } | null> - >() - }) - - test("getUserOperationReceipt", () => { - expectTypeOf(getUserOperationReceipt) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - expectTypeOf(getUserOperationReceipt).parameter(1).toMatchTypeOf<{ - hash: Hash - }>() - expectTypeOf(getUserOperationReceipt).returns.toMatchTypeOf< - Promise<{ - userOpHash: Hash - entryPoint: Address - sender: Address - nonce: bigint - paymaster?: Address - actualGasUsed: bigint - actualGasCost: bigint - success: boolean - reason?: string - receipt: { - transactionHash: Hex - transactionIndex: bigint - blockHash: Hash - blockNumber: bigint - from: Address - to: Address | null - cumulativeGasUsed: bigint - status: TStatus - gasUsed: bigint - contractAddress: Address | null - logsBloom: Hex - effectiveGasPrice: bigint - } - logs: Log[] - } | null> - >() - }) - - test("getSenderAddress", () => { - expectTypeOf(getSenderAddress) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - expectTypeOf(getSenderAddress) - .parameter(1) - .toMatchTypeOf<{ - initCode: Hex - entryPoint: ENTRYPOINT_ADDRESS_V06_TYPE - factory?: never - factoryData?: never - }>() - expectTypeOf(getSenderAddress) - .parameter(1) - .toMatchTypeOf<{ - entryPoint: ENTRYPOINT_ADDRESS_V07_TYPE - factory: Address - factoryData: Hex - initCode?: never - }>() - expectTypeOf(getSenderAddress).returns.toMatchTypeOf>() - }) - - test("getAccountNonce", () => { - expectTypeOf(getAccountNonce) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - expectTypeOf(getAccountNonce).parameter(1).toMatchTypeOf<{ - sender: Address - entryPoint: EntryPoint - key?: bigint - }>() - expectTypeOf(getAccountNonce).returns.toMatchTypeOf>() - }) - - test("waitForUserOperationReceipt", () => { - expectTypeOf(waitForUserOperationReceipt) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - expectTypeOf(waitForUserOperationReceipt).parameter(1).toMatchTypeOf<{ - hash: Hash - pollingInterval?: number - timeout?: number - }>() - expectTypeOf(waitForUserOperationReceipt).returns.toMatchTypeOf< - Promise<{ - userOpHash: Hash - entryPoint: Address - sender: Address - nonce: bigint - paymaster?: Address - actualGasUsed: bigint - actualGasCost: bigint - success: boolean - reason?: string - receipt: { - transactionHash: Hex - transactionIndex: bigint - blockHash: Hash - blockNumber: bigint - from: Address - to: Address | null - cumulativeGasUsed: bigint - status: TStatus - gasUsed: bigint - contractAddress: Address | null - logsBloom: Hex - effectiveGasPrice: bigint - } - logs: Log[] - }> - >() - }) -}) diff --git a/packages/permissionless/actions/index.ts b/packages/permissionless/actions/index.ts index 3b20e772..0d2236be 100644 --- a/packages/permissionless/actions/index.ts +++ b/packages/permissionless/actions/index.ts @@ -1,60 +1,12 @@ -import type { - EstimateUserOperationErrorType, - EstimateUserOperationGasParameters, - EstimateUserOperationGasReturnType -} from "./bundler/estimateUserOperationGas" -import type { GetUserOperationByHashParameters } from "./bundler/getUserOperationByHash" -import type { GetUserOperationByHashReturnType } from "./bundler/getUserOperationByHash" -import type { - GetUserOperationReceiptParameters, - GetUserOperationReceiptReturnType -} from "./bundler/getUserOperationReceipt" -import type { SendUserOperationParameters } from "./bundler/sendUserOperation" - import type { GetSenderAddressParams } from "./public/getSenderAddress" import { InvalidEntryPointError, getSenderAddress } from "./public/getSenderAddress" -import { chainId } from "./bundler/chainId" -import { estimateUserOperationGas } from "./bundler/estimateUserOperationGas" -import { getUserOperationByHash } from "./bundler/getUserOperationByHash" -import { getUserOperationReceipt } from "./bundler/getUserOperationReceipt" -import { sendUserOperation } from "./bundler/sendUserOperation" -import { supportedEntryPoints } from "./bundler/supportedEntryPoints" -import { waitForUserOperationReceipt } from "./bundler/waitForUserOperationReceipt" -import { - type WaitForUserOperationReceiptParameters, - WaitForUserOperationReceiptTimeoutError -} from "./bundler/waitForUserOperationReceipt" import type { GetAccountNonceParams } from "./public/getAccountNonce" import { getAccountNonce } from "./public/getAccountNonce" -export type { - SendUserOperationParameters, - EstimateUserOperationGasParameters, - EstimateUserOperationGasReturnType, - GetUserOperationByHashParameters, - GetUserOperationByHashReturnType, - GetUserOperationReceiptParameters, - GetUserOperationReceiptReturnType, - GetSenderAddressParams, - GetAccountNonceParams, - WaitForUserOperationReceiptParameters, - EstimateUserOperationErrorType -} +export type { GetSenderAddressParams, GetAccountNonceParams } -export { - sendUserOperation, - estimateUserOperationGas, - supportedEntryPoints, - chainId, - getUserOperationByHash, - getUserOperationReceipt, - getSenderAddress, - getAccountNonce, - InvalidEntryPointError, - waitForUserOperationReceipt, - WaitForUserOperationReceiptTimeoutError -} +export { getSenderAddress, getAccountNonce, InvalidEntryPointError } diff --git a/packages/permissionless/actions/pimlico.test.ts b/packages/permissionless/actions/pimlico.test.ts deleted file mode 100644 index f6d088cf..00000000 --- a/packages/permissionless/actions/pimlico.test.ts +++ /dev/null @@ -1,222 +0,0 @@ -import type { - Account, - Address, - Chain, - Client, - Hash, - Hex, - Transport -} from "viem" -import { describe, expectTypeOf, test } from "vitest" -import type { EntryPoint } from "../types/entrypoint" -import type { PimlicoBundlerRpcSchema } from "../types/pimlico" -import type { UserOperation } from "../types/userOperation" -import { - type PimlicoBundlerActions, - getUserOperationGasPrice, - getUserOperationStatus, - pimlicoBundlerActions, - sendCompressedUserOperation, - sponsorUserOperation, - validateSponsorshipPolicies -} from "./pimlico" - -describe("pimlico", () => { - test("getUserOperationGasPrice", () => { - expectTypeOf(getUserOperationGasPrice).toBeFunction() - expectTypeOf(getUserOperationGasPrice) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - PimlicoBundlerRpcSchema - > - >() - expectTypeOf(getUserOperationGasPrice).returns.toMatchTypeOf | null>() - }) - - test("getUserOperationStatus", () => { - expectTypeOf(getUserOperationStatus).toBeFunction() - expectTypeOf(getUserOperationStatus) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - PimlicoBundlerRpcSchema - > - >() - expectTypeOf(getUserOperationStatus).parameter(1).toMatchTypeOf<{ - hash: string - }>() - expectTypeOf(getUserOperationStatus).returns.toMatchTypeOf< - Promise<{ - status: - | "not_found" - | "not_submitted" - | "submitted" - | "rejected" - | "reverted" - | "included" - | "failed" - transactionHash: Hash | null - }> - >() - }) - - test("sendCompressedUserOperation", () => { - expectTypeOf(sendCompressedUserOperation).toBeFunction() - expectTypeOf(sendCompressedUserOperation) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - PimlicoBundlerRpcSchema - > - >() - expectTypeOf(sendCompressedUserOperation).parameter(1).toMatchTypeOf<{ - compressedUserOperation: Hex - inflatorAddress: Address - entryPoint: Address - }>() - expectTypeOf(sendCompressedUserOperation).returns.toMatchTypeOf< - Promise - >() - }) - - test("sponsorUserOperation", () => { - expectTypeOf(sponsorUserOperation).toBeFunction() - expectTypeOf(sponsorUserOperation) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - PimlicoBundlerRpcSchema - > - >() - expectTypeOf(sponsorUserOperation).parameter(1).toMatchTypeOf<{ - userOperation: UserOperation<"v0.6" | "v0.7"> - entryPoint: EntryPoint - sponsorshipPolicyId?: string - }>() - expectTypeOf(sponsorUserOperation).returns.toMatchTypeOf< - Promise< - | { - callGasLimit: bigint - verificationGasLimit: bigint - preVerificationGas: bigint - paymasterAndData: Hex - } - | { - callGasLimit: bigint - verificationGasLimit: bigint - preVerificationGas: bigint - paymaster: Address - paymasterVerificationGasLimit: bigint - paymasterPostOpGasLimit: bigint - paymasterData: Hex - } - > - >() - }) - - test("sponsorUserOperation", () => { - expectTypeOf(sponsorUserOperation).toBeFunction() - expectTypeOf(sponsorUserOperation) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - PimlicoBundlerRpcSchema - > - >() - expectTypeOf(sponsorUserOperation).parameter(1).toMatchTypeOf<{ - userOperation: UserOperation<"v0.6" | "v0.7"> - entryPoint: EntryPoint - sponsorshipPolicyId?: string - }>() - expectTypeOf(sponsorUserOperation).returns.toMatchTypeOf< - Promise< - | { - callGasLimit: bigint - verificationGasLimit: bigint - preVerificationGas: bigint - paymasterAndData: Hex - } - | { - callGasLimit: bigint - verificationGasLimit: bigint - preVerificationGas: bigint - paymaster: Address - paymasterVerificationGasLimit: bigint - paymasterPostOpGasLimit: bigint - paymasterData: Hex - } - > - >() - }) - - test("validateSponsorshipPolicies", () => { - expectTypeOf(validateSponsorshipPolicies).toBeFunction() - expectTypeOf(validateSponsorshipPolicies) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - PimlicoBundlerRpcSchema - > - >() - expectTypeOf(validateSponsorshipPolicies).parameter(1).toMatchTypeOf<{ - userOperation: UserOperation<"v0.6" | "v0.7"> - entryPoint: EntryPoint - sponsorshipPolicyIds: string[] - }>() - expectTypeOf(validateSponsorshipPolicies).returns.toMatchTypeOf< - Promise< - { - sponsorshipPolicyId: string - data: { - name: string | null - author: string | null - icon: string | null - description: string | null - } - }[] - > - >() - }) - - test("pimlicoBundlerActions", () => { - expectTypeOf(pimlicoBundlerActions).toBeFunction() - expectTypeOf(pimlicoBundlerActions) - .parameter(0) - .toMatchTypeOf() - expectTypeOf(pimlicoBundlerActions).returns.toMatchTypeOf< - (client: Client) => PimlicoBundlerActions - >() - }) -}) diff --git a/packages/permissionless/actions/pimlico.ts b/packages/permissionless/actions/pimlico.ts index 77e500b9..908aba0d 100644 --- a/packages/permissionless/actions/pimlico.ts +++ b/packages/permissionless/actions/pimlico.ts @@ -1,3 +1,8 @@ +import { + type GetTokenQuotesParameters, + type GetTokenQuotesReturnType, + getTokenQuotes +} from "./pimlico/getTokenQuotes" import { type GetUserOperationGasPriceReturnType, getUserOperationGasPrice @@ -17,14 +22,8 @@ import { sponsorUserOperation } from "./pimlico/sponsorUserOperation" -import type { - PimlicoBundlerActions, - PimlicoPaymasterClientActions -} from "../clients/decorators/pimlico" -import { - pimlicoBundlerActions, - pimlicoPaymasterActions -} from "../clients/decorators/pimlico" +import type { PimlicoActions } from "../clients/decorators/pimlico" +import { pimlicoActions } from "../clients/decorators/pimlico" import { type ValidateSponsorshipPolicies, @@ -36,21 +35,22 @@ export type { GetUserOperationGasPriceReturnType, GetUserOperationStatusParameters, GetUserOperationStatusReturnType, - PimlicoBundlerActions, - PimlicoPaymasterClientActions, + PimlicoActions, PimlicoSponsorUserOperationParameters, SendCompressedUserOperationParameters, SponsorUserOperationReturnType, ValidateSponsorshipPolicies, - ValidateSponsorshipPoliciesParameters + ValidateSponsorshipPoliciesParameters, + GetTokenQuotesParameters, + GetTokenQuotesReturnType } export { getUserOperationGasPrice, getUserOperationStatus, - pimlicoBundlerActions, - pimlicoPaymasterActions, + pimlicoActions, sendCompressedUserOperation, sponsorUserOperation, - validateSponsorshipPolicies + validateSponsorshipPolicies, + getTokenQuotes } diff --git a/packages/permissionless/actions/pimlico/getTokenQuotes.test.ts b/packages/permissionless/actions/pimlico/getTokenQuotes.test.ts new file mode 100644 index 00000000..db6f26ca --- /dev/null +++ b/packages/permissionless/actions/pimlico/getTokenQuotes.test.ts @@ -0,0 +1,36 @@ +import { getAddress, isAddress } from "viem" +import { entryPoint07Address } from "viem/account-abstraction" +import { foundry } from "viem/chains" +import { describe, expect } from "vitest" +import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" +import { getPimlicoClient } from "../../../permissionless-test/src/utils" +import { getTokenQuotes } from "./getTokenQuotes" + +describe("getTokenQuotes", () => { + testWithRpc("getTokenQuotes", async ({ rpc }) => { + const pimlicoBundlerClient = getPimlicoClient({ + entryPointVersion: "0.7", + altoRpc: rpc.paymasterRpc + }) + + const token = getAddress("0xffffffffffffffffffffffffffffffffffffffff") + + const quotes = await getTokenQuotes(pimlicoBundlerClient, { + tokens: [token], + entryPointAddress: entryPoint07Address, + chain: foundry + }) + + expect(quotes).toBeTruthy() + expect(Array.isArray(quotes)).toBe(true) + expect(quotes[0].token).toBeTruthy() + expect(isAddress(quotes[0].token)) + expect(quotes[0].token).toEqual(token) + expect(quotes[0].paymaster).toBeTruthy() + expect(isAddress(quotes[0].paymaster)) + expect(quotes[0].exchangeRate).toBeTruthy() + expect(quotes[0].exchangeRate).toBeGreaterThan(0n) + expect(quotes[0].postOpGas).toBeTruthy() + expect(quotes[0].postOpGas).toBeGreaterThan(0n) + }) +}) diff --git a/packages/permissionless/actions/pimlico/getTokenQuotes.ts b/packages/permissionless/actions/pimlico/getTokenQuotes.ts new file mode 100644 index 00000000..59d47b78 --- /dev/null +++ b/packages/permissionless/actions/pimlico/getTokenQuotes.ts @@ -0,0 +1,67 @@ +import { + type Account, + type Address, + type Chain, + ChainNotFoundError, + type Client, + type GetChainParameter, + type Transport, + hexToBigInt, + numberToHex +} from "viem" +import type { PimlicoRpcSchema } from "../../types/pimlico" + +export type GetTokenQuotesParameters< + TChain extends Chain | undefined, + TChainOverride extends Chain | undefined = Chain | undefined +> = { + tokens: Address[] + entryPointAddress: Address +} & GetChainParameter + +export type GetTokenQuotesReturnType = { + paymaster: Address + token: Address + postOpGas: bigint + exchangeRate: bigint +}[] + +/** + * Returns all related fields to calculate the potential cost of a userOperation in ERC-20 tokens. + * + * - Docs: https://docs.pimlico.io/permissionless/reference/pimlico-bundler-actions/getTokenQuotes + * + * @param client that you created using viem's createClient whose transport url is pointing to the Pimlico's bundler. + * @returns slow, standard & fast values for maxFeePerGas & maxPriorityFeePerGas + * @returns quotes, see {@link GetTokenQuotesReturnType} + * + */ +export const getTokenQuotes = async < + TChain extends Chain | undefined, + TTransport extends Transport = Transport, + TChainOverride extends Chain | undefined = Chain | undefined +>( + client: Client, + args: GetTokenQuotesParameters +): Promise => { + const chainId = args.chain?.id ?? client.chain?.id + + if (!chainId) { + throw new ChainNotFoundError() + } + + const res = await client.request({ + method: "pimlico_getTokenQuotes", + params: [ + { tokens: args.tokens }, + args.entryPointAddress, + numberToHex(chainId) + ] + }) + + return res.quotes.map((quote) => ({ + ...quote, + postOpGas: hexToBigInt(quote.postOpGas), + exchangeRate: hexToBigInt(quote.exchangeRate) + })) +} diff --git a/packages/permissionless/actions/pimlico/getUserOperationGasPrice.test.ts b/packages/permissionless/actions/pimlico/getUserOperationGasPrice.test.ts index 492c353b..196a3f35 100644 --- a/packages/permissionless/actions/pimlico/getUserOperationGasPrice.test.ts +++ b/packages/permissionless/actions/pimlico/getUserOperationGasPrice.test.ts @@ -1,13 +1,12 @@ import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { getPimlicoBundlerClient } from "../../../permissionless-test/src/utils" -import { ENTRYPOINT_ADDRESS_V06 } from "../../utils" +import { getPimlicoClient } from "../../../permissionless-test/src/utils" import { getUserOperationGasPrice } from "./getUserOperationGasPrice" describe("getUserOperationGasPrice", () => { testWithRpc("getUserOperationGasPrice", async ({ rpc }) => { - const pimlicoBundlerClient = getPimlicoBundlerClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, + const pimlicoBundlerClient = getPimlicoClient({ + entryPointVersion: "0.6", altoRpc: rpc.altoRpc }) diff --git a/packages/permissionless/actions/pimlico/getUserOperationGasPrice.ts b/packages/permissionless/actions/pimlico/getUserOperationGasPrice.ts index ca81bb95..1b2e7592 100644 --- a/packages/permissionless/actions/pimlico/getUserOperationGasPrice.ts +++ b/packages/permissionless/actions/pimlico/getUserOperationGasPrice.ts @@ -1,6 +1,5 @@ import type { Account, Chain, Client, Transport } from "viem" -import type { Prettify } from "../../types/" -import type { PimlicoBundlerRpcSchema } from "../../types/pimlico" +import type { PimlicoRpcSchema } from "../../types/pimlico" export type GetUserOperationGasPriceReturnType = { slow: { @@ -38,13 +37,14 @@ export type GetUserOperationGasPriceReturnType = { * await getUserOperationGasPrice(bundlerClient) * */ -export const getUserOperationGasPrice = async < - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends Account | undefined = Account | undefined ->( - client: Client -): Promise> => { +export const getUserOperationGasPrice = async ( + client: Client< + Transport, + Chain | undefined, + Account | undefined, + PimlicoRpcSchema + > +): Promise => { const gasPrice = await client.request({ method: "pimlico_getUserOperationGasPrice", params: [] diff --git a/packages/permissionless/actions/pimlico/getUserOperationStatus.test.ts b/packages/permissionless/actions/pimlico/getUserOperationStatus.test.ts index 60c4521d..5aa2b807 100644 --- a/packages/permissionless/actions/pimlico/getUserOperationStatus.test.ts +++ b/packages/permissionless/actions/pimlico/getUserOperationStatus.test.ts @@ -1,58 +1,50 @@ import { isHash, zeroAddress } from "viem" -import { generatePrivateKey } from "viem/accounts" +import { entryPoint06Address } from "viem/account-abstraction" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" import { - getPimlicoBundlerClient, - getPimlicoPaymasterClient, + getBundlerClient, + getPimlicoClient, getSimpleAccountClient } from "../../../permissionless-test/src/utils" -import { bundlerActions } from "../../clients/decorators/bundler" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" import { getUserOperationStatus } from "./getUserOperationStatus" describe("getUserOperationStatus", () => { testWithRpc("getUserOperationStatus_V06", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc + const { altoRpc } = rpc - const bundlerClientV06 = getPimlicoBundlerClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, + const bundlerClient = getPimlicoClient({ + entryPointVersion: "0.6", altoRpc: altoRpc - }).extend(bundlerActions(ENTRYPOINT_ADDRESS_V06)) - - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) }) - const userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) + const simpleAccountClient = getBundlerClient({ + account: await getSimpleAccountClient({ + ...rpc, + entryPoint: { + version: "0.6" } - }) - - userOperation.signature = - await simpleAccountClient.account.signUserOperation(userOperation) + }), + entryPoint: { + version: "0.6" + }, + ...rpc + }) - const opHash = await bundlerClientV06.sendUserOperation({ - userOperation + const opHash = await simpleAccountClient.sendUserOperation({ + calls: [ + { + to: zeroAddress, + data: "0x", + value: 0n + } + ] }) expect(isHash(opHash)).toBe(true) const userOperationReceipt = - await bundlerClientV06.waitForUserOperationReceipt({ + await bundlerClient.waitForUserOperationReceipt({ hash: opHash, timeout: 100000 }) @@ -60,7 +52,7 @@ describe("getUserOperationStatus", () => { expect(userOperationReceipt?.userOpHash).toBe(opHash) expect(userOperationReceipt?.receipt.transactionHash).toBeTruthy() - const receipt = await bundlerClientV06.getUserOperationReceipt({ + const receipt = await bundlerClient.getUserOperationReceipt({ hash: opHash }) @@ -68,7 +60,7 @@ describe("getUserOperationStatus", () => { userOperationReceipt?.receipt.transactionHash ) const userOperationStatus = await getUserOperationStatus( - bundlerClientV06, + bundlerClient, { hash: opHash } @@ -82,46 +74,40 @@ describe("getUserOperationStatus", () => { }) testWithRpc("getUserOperationStatus_V07", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc + const { altoRpc } = rpc - const bundlerClientV07 = getPimlicoBundlerClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, + const bundlerClient = getPimlicoClient({ + entryPointVersion: "0.7", altoRpc: altoRpc - }).extend(bundlerActions(ENTRYPOINT_ADDRESS_V07)) - - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) }) - const userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) + const simpleAccountClient = getBundlerClient({ + account: await getSimpleAccountClient({ + ...rpc, + entryPoint: { + version: "0.7" } - }) - - userOperation.signature = - await simpleAccountClient.account.signUserOperation(userOperation) + }), + entryPoint: { + version: "0.7" + }, + ...rpc + }) - const opHash = await bundlerClientV07.sendUserOperation({ - userOperation + const opHash = await simpleAccountClient.sendUserOperation({ + calls: [ + { + to: zeroAddress, + data: "0x", + value: 0n + } + ] }) expect(isHash(opHash)).toBe(true) const userOperationReceipt = - await bundlerClientV07.waitForUserOperationReceipt({ + await bundlerClient.waitForUserOperationReceipt({ hash: opHash, timeout: 100000 }) @@ -129,7 +115,7 @@ describe("getUserOperationStatus", () => { expect(userOperationReceipt?.userOpHash).toBe(opHash) expect(userOperationReceipt?.receipt.transactionHash).toBeTruthy() - const receipt = await bundlerClientV07.getUserOperationReceipt({ + const receipt = await bundlerClient.getUserOperationReceipt({ hash: opHash }) @@ -137,7 +123,7 @@ describe("getUserOperationStatus", () => { userOperationReceipt?.receipt.transactionHash ) const userOperationStatus = await getUserOperationStatus( - bundlerClientV07, + bundlerClient, { hash: opHash } diff --git a/packages/permissionless/actions/pimlico/getUserOperationStatus.ts b/packages/permissionless/actions/pimlico/getUserOperationStatus.ts index d5edc66c..93b1fcc2 100644 --- a/packages/permissionless/actions/pimlico/getUserOperationStatus.ts +++ b/packages/permissionless/actions/pimlico/getUserOperationStatus.ts @@ -1,8 +1,6 @@ import type { Account, Chain, Client, Hash, Transport } from "viem" -import type { PimlicoBundlerClient } from "../../clients/pimlico" -import type { Prettify } from "../../types/" import type { - PimlicoBundlerRpcSchema, + PimlicoRpcSchema, PimlicoUserOperationStatus } from "../../types/pimlico" @@ -17,7 +15,7 @@ export type GetUserOperationStatusReturnType = PimlicoUserOperationStatus * * - Docs: https://docs.pimlico.io/permissionless/reference/pimlico-bundler-actions/getUserOperationStatus * - * @param client {@link PimlicoBundlerClient} that you created using viem's createClient whose transport url is pointing to the Pimlico's bundler. + * @param client {@link PimlicoClient} that you created using viem's createClient whose transport url is pointing to the Pimlico's bundler. * @param hash {@link Hash} UserOpHash that you must have received from sendUserOperation. * @returns status & transaction hash if included {@link GetUserOperationStatusReturnType} * @@ -35,14 +33,15 @@ export type GetUserOperationStatusReturnType = PimlicoUserOperationStatus * await getUserOperationStatus(bundlerClient, { hash: userOpHash }) * */ -export const getUserOperationStatus = async < - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends Account | undefined = Account | undefined ->( - client: Client, - { hash }: Prettify -): Promise> => { +export const getUserOperationStatus = async ( + client: Client< + Transport, + Chain | undefined, + Account | undefined, + PimlicoRpcSchema + >, + { hash }: GetUserOperationStatusParameters +): Promise => { return client.request({ method: "pimlico_getUserOperationStatus", params: [hash] diff --git a/packages/permissionless/actions/pimlico/sendCompressedUserOperation.ts b/packages/permissionless/actions/pimlico/sendCompressedUserOperation.ts index ed47b16e..232d7728 100644 --- a/packages/permissionless/actions/pimlico/sendCompressedUserOperation.ts +++ b/packages/permissionless/actions/pimlico/sendCompressedUserOperation.ts @@ -7,13 +7,12 @@ import type { Hex, Transport } from "viem" -import type { Prettify } from "../../types/" -import type { PimlicoBundlerRpcSchema } from "../../types/pimlico" +import type { PimlicoRpcSchema } from "../../types/pimlico" export type SendCompressedUserOperationParameters = { compressedUserOperation: Hex inflatorAddress: Address - entryPoint: Address + entryPointAddress: Address } /** @@ -41,22 +40,19 @@ export type SendCompressedUserOperationParameters = { * }) * // Return '0xe9fad2cd67f9ca1d0b7a6513b2a42066784c8df938518da2b51bb8cc9a89ea34' */ -export const sendCompressedUserOperation = async < - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends Account | undefined = Account | undefined ->( - client: Client, - args: Prettify +export const sendCompressedUserOperation = async ( + client: Client< + Transport, + Chain | undefined, + Account | undefined, + PimlicoRpcSchema + >, + args: SendCompressedUserOperationParameters ): Promise => { - const { compressedUserOperation, inflatorAddress, entryPoint } = args + const { compressedUserOperation, inflatorAddress, entryPointAddress } = args return client.request({ method: "pimlico_sendCompressedUserOperation", - params: [ - compressedUserOperation as Hex, - inflatorAddress as Address, - entryPoint as Address - ] + params: [compressedUserOperation, inflatorAddress, entryPointAddress] }) } diff --git a/packages/permissionless/actions/pimlico/sponsorUserOperation.test.ts b/packages/permissionless/actions/pimlico/sponsorUserOperation.test.ts index 1a1d4d2d..aed29fe3 100644 --- a/packages/permissionless/actions/pimlico/sponsorUserOperation.test.ts +++ b/packages/permissionless/actions/pimlico/sponsorUserOperation.test.ts @@ -1,56 +1,49 @@ -import { http, isHash, zeroAddress } from "viem" -import { generatePrivateKey } from "viem/accounts" +import { isHash, zeroAddress } from "viem" +import { entryPoint06Address } from "viem/account-abstraction" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" import { - getPimlicoPaymasterClient, + getBundlerClient, + getPimlicoClient, getSimpleAccountClient } from "../../../permissionless-test/src/utils" -import { createBundlerClient } from "../../clients/createBundlerClient" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" describe("sponsorUserOperation", () => { testWithRpc("sponsorUserOperation_V06", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc + const { altoRpc } = rpc - const bundlerClientV06 = createBundlerClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - transport: http(altoRpc) + const bundlerClient = getPimlicoClient({ + entryPointVersion: "0.6", + altoRpc: altoRpc }) - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) + const simpleAccountClient = getBundlerClient({ + account: await getSimpleAccountClient({ + ...rpc, + entryPoint: { + version: "0.6" + } + }), + entryPoint: { + version: "0.6" + }, + ...rpc }) - const userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) + const opHash = await simpleAccountClient.sendUserOperation({ + calls: [ + { + to: zeroAddress, + data: "0x", + value: 0n } - }) - - userOperation.signature = - await simpleAccountClient.account.signUserOperation(userOperation) - - const opHash = await bundlerClientV06.sendUserOperation({ - userOperation + ] }) expect(isHash(opHash)).toBe(true) const userOperationReceipt = - await bundlerClientV06.waitForUserOperationReceipt({ + await bundlerClient.waitForUserOperationReceipt({ hash: opHash, timeout: 100000 }) @@ -58,7 +51,7 @@ describe("sponsorUserOperation", () => { expect(userOperationReceipt?.userOpHash).toBe(opHash) expect(userOperationReceipt?.receipt.transactionHash).toBeTruthy() - const receipt = await bundlerClientV06.getUserOperationReceipt({ + const receipt = await bundlerClient.getUserOperationReceipt({ hash: opHash }) @@ -68,46 +61,40 @@ describe("sponsorUserOperation", () => { }) testWithRpc("sponsorUserOperation_V07", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc + const { altoRpc } = rpc - const bundlerClientV07 = createBundlerClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - transport: http(altoRpc) + const bundlerClient = getPimlicoClient({ + entryPointVersion: "0.7", + altoRpc: altoRpc }) - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) + const simpleAccountClient = getBundlerClient({ + account: await getSimpleAccountClient({ + ...rpc, + entryPoint: { + version: "0.7" + } + }), + entryPoint: { + version: "0.7" + }, + ...rpc }) - const userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) + const opHash = await simpleAccountClient.sendUserOperation({ + calls: [ + { + to: zeroAddress, + data: "0x", + value: 0n } - }) - - userOperation.signature = - await simpleAccountClient.account.signUserOperation(userOperation) - - const opHash = await bundlerClientV07.sendUserOperation({ - userOperation + ] }) expect(isHash(opHash)).toBe(true) const userOperationReceipt = - await bundlerClientV07.waitForUserOperationReceipt({ + await bundlerClient.waitForUserOperationReceipt({ hash: opHash, timeout: 100000 }) @@ -115,7 +102,7 @@ describe("sponsorUserOperation", () => { expect(userOperationReceipt?.userOpHash).toBe(opHash) expect(userOperationReceipt?.receipt.transactionHash).toBeTruthy() - const receipt = await bundlerClientV07.getUserOperationReceipt({ + const receipt = await bundlerClient.getUserOperationReceipt({ hash: opHash }) diff --git a/packages/permissionless/actions/pimlico/sponsorUserOperation.ts b/packages/permissionless/actions/pimlico/sponsorUserOperation.ts index a8786d9e..fdfac621 100644 --- a/packages/permissionless/actions/pimlico/sponsorUserOperation.ts +++ b/packages/permissionless/actions/pimlico/sponsorUserOperation.ts @@ -1,121 +1,99 @@ -import type { Account, Address, Chain, Client, Hex, Transport } from "viem" -import type { PartialBy } from "viem/types/utils" -import type { Prettify } from "../../types/" import type { - ENTRYPOINT_ADDRESS_V06_TYPE, - EntryPoint, - GetEntryPointVersion -} from "../../types/entrypoint" -import type { PimlicoPaymasterRpcSchema } from "../../types/pimlico" -import type { - UserOperation, - UserOperationWithBigIntAsHex -} from "../../types/userOperation" + Account, + Address, + Chain, + Client, + Hex, + OneOf, + PartialBy, + Transport +} from "viem" +import type { UserOperation } from "viem/account-abstraction" +import type { PimlicoRpcSchema } from "../../types/pimlico" import { deepHexlify } from "../../utils/deepHexlify" -import { ENTRYPOINT_ADDRESS_V06 } from "../../utils/getEntryPointVersion" export type PimlicoSponsorUserOperationParameters< - entryPoint extends EntryPoint + entryPointVersion extends "0.6" | "0.7" > = { - userOperation: entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE - ? PartialBy< - UserOperation<"v0.6">, - "callGasLimit" | "preVerificationGas" | "verificationGasLimit" - > - : PartialBy< - UserOperation<"v0.7">, - | "callGasLimit" - | "preVerificationGas" - | "verificationGasLimit" - | "paymasterVerificationGasLimit" - | "paymasterPostOpGasLimit" - > - entryPoint: entryPoint + userOperation: OneOf< + | (entryPointVersion extends "0.6" + ? PartialBy< + UserOperation<"0.6">, + | "callGasLimit" + | "preVerificationGas" + | "verificationGasLimit" + > + : never) + | (entryPointVersion extends "0.7" + ? PartialBy< + UserOperation<"0.7">, + | "callGasLimit" + | "preVerificationGas" + | "verificationGasLimit" + | "paymasterVerificationGasLimit" + | "paymasterPostOpGasLimit" + > + : never) + > + entryPoint: { + address: Address + version: entryPointVersion + } sponsorshipPolicyId?: string } -export type SponsorUserOperationReturnType = - entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE - ? { - callGasLimit: bigint - verificationGasLimit: bigint - preVerificationGas: bigint - paymasterAndData: Hex - } - : { - callGasLimit: bigint - verificationGasLimit: bigint - preVerificationGas: bigint - paymaster: Address - paymasterVerificationGasLimit: bigint - paymasterPostOpGasLimit: bigint - paymasterData: Hex - } +export type SponsorUserOperationReturnType< + entryPointVersion extends "0.6" | "0.7" = "0.7" +> = OneOf< + | (entryPointVersion extends "0.6" + ? { + callGasLimit: bigint + verificationGasLimit: bigint + preVerificationGas: bigint + paymasterAndData: Hex + } + : never) + | (entryPointVersion extends "0.7" + ? { + callGasLimit: bigint + verificationGasLimit: bigint + preVerificationGas: bigint + paymaster: Address + paymasterVerificationGasLimit: bigint + paymasterPostOpGasLimit: bigint + paymasterData: Hex + } + : never) +> /** - * Returns paymasterAndData & updated gas parameters required to sponsor a userOperation. - * - * - Docs: https://docs.pimlico.io/permissionless/reference/pimlico-paymaster-actions/sponsorUserOperation - * - * @param client {@link PimlicoBundlerClient} that you created using viem's createClient whose transport url is pointing to the Pimlico's bundler. - * @param args {@link PimlicoSponsorUserOperationParameters} UserOperation you want to sponsor & entryPoint. - * @returns paymasterAndData & updated gas parameters, see {@link SponsorUserOperationReturnType} - * - * - * @example - * import { createClient } from "viem" - * import { sponsorUserOperation } from "permissionless/actions/pimlico" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http("https://api.pimlico.io/v2/goerli/rpc?apikey=YOUR_API_KEY_HERE") - * }) - * - * await sponsorUserOperation(bundlerClient, { - * userOperation: userOperationWithDummySignature, - * entryPoint: entryPoint - * }}) - * + * @deprecated Use `getPaymasterData` instead */ export const sponsorUserOperation = async < - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends Account | undefined = Account | undefined + entryPointVersion extends "0.6" | "0.7" = "0.6" | "0.7" >( client: Client< - TTransport, - TChain, - TAccount, - PimlicoPaymasterRpcSchema + Transport, + Chain | undefined, + Account | undefined, + PimlicoRpcSchema >, - args: Prettify> -): Promise>> => { + args: PimlicoSponsorUserOperationParameters +): Promise> => { const response = await client.request({ method: "pm_sponsorUserOperation", params: args.sponsorshipPolicyId ? [ - deepHexlify( - args.userOperation - ) as UserOperationWithBigIntAsHex< - GetEntryPointVersion - >, - args.entryPoint, + deepHexlify(args.userOperation), + args.entryPoint.address, { sponsorshipPolicyId: args.sponsorshipPolicyId } ] - : [ - deepHexlify( - args.userOperation - ) as UserOperationWithBigIntAsHex< - GetEntryPointVersion - >, - args.entryPoint - ] + : [deepHexlify(args.userOperation), args.entryPoint.address] }) - if (args.entryPoint === ENTRYPOINT_ADDRESS_V06) { + if (args.entryPoint.version === "0.6") { const responseV06 = response as { paymasterAndData: Hex preVerificationGas: Hex @@ -131,7 +109,7 @@ export const sponsorUserOperation = async < preVerificationGas: BigInt(responseV06.preVerificationGas), verificationGasLimit: BigInt(responseV06.verificationGasLimit), callGasLimit: BigInt(responseV06.callGasLimit) - } as SponsorUserOperationReturnType + } as SponsorUserOperationReturnType } const responseV07 = response as { @@ -155,5 +133,5 @@ export const sponsorUserOperation = async < ), paymasterPostOpGasLimit: BigInt(responseV07.paymasterPostOpGasLimit), paymasterData: responseV07.paymasterData - } as SponsorUserOperationReturnType + } as SponsorUserOperationReturnType } diff --git a/packages/permissionless/actions/pimlico/validateSponsorshipPolicies.test.ts b/packages/permissionless/actions/pimlico/validateSponsorshipPolicies.test.ts index 9f836468..ad5561de 100644 --- a/packages/permissionless/actions/pimlico/validateSponsorshipPolicies.test.ts +++ b/packages/permissionless/actions/pimlico/validateSponsorshipPolicies.test.ts @@ -1,48 +1,49 @@ -import { generatePrivateKey } from "viem/accounts" +import { entryPoint06Address } from "viem/account-abstraction" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" import { - getPimlicoPaymasterClient, + getBundlerClient, + getPimlicoClient, getSimpleAccountClient } from "../../../permissionless-test/src/utils" -import { ENTRYPOINT_ADDRESS_V06 } from "../../utils" import { validateSponsorshipPolicies } from "./validateSponsorshipPolicies" describe("validateSponsorshipPolicies", () => { testWithRpc("Validating sponsorship policies", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc + const { paymasterRpc } = rpc - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) + const simpleAccountClient = getBundlerClient({ + account: await getSimpleAccountClient({ + ...rpc, + entryPoint: { + version: "0.6" + } + }), + entryPoint: { + version: "0.6" + }, + ...rpc }) - const userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: "0x5af0d9827e0c53e4799bb226655a1de152a425a5", - data: "0x", - value: 0n - }) + const userOperation = await simpleAccountClient.prepareUserOperation({ + calls: [ + { + to: "0x5af0d9827e0c53e4799bb226655a1de152a425a5", + data: "0x", + value: 0n } - }) + ] + }) - const pimlicoPaymasterClient = getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc + const pimlicoPaymasterClient = getPimlicoClient({ + entryPointVersion: "0.6", + altoRpc: paymasterRpc }) const policies = await validateSponsorshipPolicies( pimlicoPaymasterClient, { - entryPoint: ENTRYPOINT_ADDRESS_V06, + entryPointAddress: entryPoint06Address, userOperation: userOperation, sponsorshipPolicyIds: ["sp_crazy_kangaroo"] } diff --git a/packages/permissionless/actions/pimlico/validateSponsorshipPolicies.ts b/packages/permissionless/actions/pimlico/validateSponsorshipPolicies.ts index 2eb511c0..e5c2bef3 100644 --- a/packages/permissionless/actions/pimlico/validateSponsorshipPolicies.ts +++ b/packages/permissionless/actions/pimlico/validateSponsorshipPolicies.ts @@ -1,18 +1,11 @@ -import type { Account, Chain, Client, Transport } from "viem" -import type { Prettify } from "../../types/" -import type { EntryPoint, GetEntryPointVersion } from "../../types/entrypoint" -import type { PimlicoPaymasterRpcSchema } from "../../types/pimlico" -import type { - UserOperation, - UserOperationWithBigIntAsHex -} from "../../types/userOperation" +import type { Account, Address, Chain, Client, Transport } from "viem" +import type { UserOperation } from "viem/account-abstraction" +import type { PimlicoRpcSchema } from "../../types/pimlico" import { deepHexlify } from "../../utils/deepHexlify" -export type ValidateSponsorshipPoliciesParameters< - entryPoint extends EntryPoint -> = { - userOperation: UserOperation> - entryPoint: entryPoint +export type ValidateSponsorshipPoliciesParameters = { + userOperation: UserOperation + entryPointAddress: Address sponsorshipPolicyIds: string[] } @@ -61,27 +54,20 @@ export type ValidateSponsorshipPolicies = { * } * ] */ -export const validateSponsorshipPolicies = async < - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends Account | undefined = Account | undefined ->( +export const validateSponsorshipPolicies = async ( client: Client< - TTransport, - TChain, - TAccount, - PimlicoPaymasterRpcSchema + Transport, + Chain | undefined, + Account | undefined, + PimlicoRpcSchema >, - args: Prettify> -): Promise[]> => { + args: ValidateSponsorshipPoliciesParameters +): Promise => { return await client.request({ method: "pm_validateSponsorshipPolicies", params: [ - deepHexlify(args.userOperation) as UserOperationWithBigIntAsHex< - GetEntryPointVersion - >, - args.entryPoint, + deepHexlify(args.userOperation), + args.entryPointAddress, args.sponsorshipPolicyIds ] }) diff --git a/packages/permissionless/actions/public/getAccountNonce.test.ts b/packages/permissionless/actions/public/getAccountNonce.test.ts index 2fe9099e..ae0ed69d 100644 --- a/packages/permissionless/actions/public/getAccountNonce.test.ts +++ b/packages/permissionless/actions/public/getAccountNonce.test.ts @@ -1,50 +1,68 @@ import { http, createPublicClient } from "viem" +import { + entryPoint06Address, + entryPoint07Address +} from "viem/account-abstraction" import { generatePrivateKey } from "viem/accounts" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { getSimpleAccountClient } from "../../../permissionless-test/src/utils" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" +import { + getBundlerClient, + getSimpleAccountClient +} from "../../../permissionless-test/src/utils" import { getAccountNonce } from "./getAccountNonce" describe("getAccountNonce", () => { testWithRpc("getAccountNonce_V06", async ({ rpc }) => { - const { anvilRpc, altoRpc } = rpc + const { anvilRpc } = rpc const client = createPublicClient({ transport: http(anvilRpc) }) - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc + const simpleAccountClient = getBundlerClient({ + account: await getSimpleAccountClient({ + ...rpc, + entryPoint: { + version: "0.6" + } + }), + entryPoint: { + version: "0.6" + }, + ...rpc }) const nonce = await getAccountNonce(client, { - entryPoint: ENTRYPOINT_ADDRESS_V06, - sender: simpleAccountClient.account.address + entryPointAddress: entryPoint06Address, + address: simpleAccountClient.account.address }) expect(nonce).toBe(0n) }) testWithRpc("getAccountNonce_V07", async ({ rpc }) => { - const { anvilRpc, altoRpc } = rpc + const { anvilRpc } = rpc const client = createPublicClient({ transport: http(anvilRpc) }) - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc + const simpleAccountClient = getBundlerClient({ + account: await getSimpleAccountClient({ + ...rpc, + entryPoint: { + version: "0.7" + } + }), + entryPoint: { + version: "0.7" + }, + ...rpc }) const nonce = await getAccountNonce(client, { - entryPoint: ENTRYPOINT_ADDRESS_V07, - sender: simpleAccountClient.account.address + entryPointAddress: entryPoint07Address, + address: simpleAccountClient.account.address }) expect(nonce).toBe(0n) diff --git a/packages/permissionless/actions/public/getAccountNonce.ts b/packages/permissionless/actions/public/getAccountNonce.ts index 218f4299..002a430f 100644 --- a/packages/permissionless/actions/public/getAccountNonce.ts +++ b/packages/permissionless/actions/public/getAccountNonce.ts @@ -1,12 +1,10 @@ -import type { Address, Chain, Client, Transport } from "viem" +import type { Address, Client } from "viem" import { readContract } from "viem/actions" import { getAction } from "viem/utils" -import type { Prettify } from "../../types/" -import type { EntryPoint } from "../../types/entrypoint" export type GetAccountNonceParams = { - sender: Address - entryPoint: EntryPoint + address: Address + entryPointAddress: Address key?: bigint } @@ -36,21 +34,18 @@ export type GetAccountNonceParams = { * * // Return 0n */ -export const getAccountNonce = async < - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined ->( - client: Client, - args: Prettify +export const getAccountNonce = async ( + client: Client, + args: GetAccountNonceParams ): Promise => { - const { sender, entryPoint, key = BigInt(0) } = args + const { address, entryPointAddress, key = BigInt(0) } = args return await getAction( client, readContract, "readContract" )({ - address: entryPoint, + address: entryPointAddress, abi: [ { inputs: [ @@ -75,6 +70,6 @@ export const getAccountNonce = async < } ], functionName: "getNonce", - args: [sender, key] + args: [address, key] }) } diff --git a/packages/permissionless/actions/public/getSenderAddress.test.ts b/packages/permissionless/actions/public/getSenderAddress.test.ts index 8f9b494e..762aa082 100644 --- a/packages/permissionless/actions/public/getSenderAddress.test.ts +++ b/packages/permissionless/actions/public/getSenderAddress.test.ts @@ -1,10 +1,14 @@ -import { http, createPublicClient } from "viem" -import { generatePrivateKey } from "viem/accounts" +import { http, concatHex, createPublicClient } from "viem" +import { + entryPoint06Address, + entryPoint07Address +} from "viem/account-abstraction" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { getSimpleAccountClient } from "../../../permissionless-test/src/utils" -import type { ENTRYPOINT_ADDRESS_V06_TYPE } from "../../types" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" +import { + getBundlerClient, + getSimpleAccountClient +} from "../../../permissionless-test/src/utils" import { getSenderAddress } from "./getSenderAddress" describe("getSenderAddress", () => { @@ -15,16 +19,29 @@ describe("getSenderAddress", () => { transport: http(anvilRpc) }) - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc + const simpleAccountClient = getBundlerClient({ + account: await getSimpleAccountClient({ + ...rpc, + entryPoint: { + version: "0.6" + } + }), + entryPoint: { + version: "0.6" + }, + ...rpc }) + const { factory, factoryData } = + await simpleAccountClient.account.getFactoryArgs() + + if (!factory || !factoryData) { + throw Error("Init code not found") + } + const address = await getSenderAddress(client, { - entryPoint: ENTRYPOINT_ADDRESS_V06, - initCode: await simpleAccountClient.account.getInitCode() + entryPointAddress: entryPoint06Address, + initCode: concatHex([factory, factoryData]) }) expect(address).toBe(simpleAccountClient.account.address) @@ -36,18 +53,30 @@ describe("getSenderAddress", () => { transport: http(anvilRpc) }) - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc + const simpleAccountClient = getBundlerClient({ + account: await getSimpleAccountClient({ + ...rpc, + entryPoint: { + version: "0.6" + } + }), + entryPoint: { + version: "0.6" + }, + ...rpc }) + const { factory, factoryData } = + await simpleAccountClient.account.getFactoryArgs() + + if (!factory || !factoryData) { + throw Error("Init code not found") + } + await expect(async () => getSenderAddress(client, { - entryPoint: - "0x0000000000000000000000000000000000000000" as ENTRYPOINT_ADDRESS_V06_TYPE, - initCode: await simpleAccountClient.account.getInitCode() + entryPointAddress: "0x0000000000000000000000000000000000000000", + initCode: concatHex([factory, factoryData]) }) ).rejects.toThrowError(/not a valid entry point/) }) @@ -58,22 +87,28 @@ describe("getSenderAddress", () => { transport: http(anvilRpc) }) - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc + const simpleAccountClient = getBundlerClient({ + account: await getSimpleAccountClient({ + ...rpc, + entryPoint: { + version: "0.7" + } + }), + entryPoint: { + version: "0.7" + }, + ...rpc }) - const factory = await simpleAccountClient.account.getFactory() - const factoryData = await simpleAccountClient.account.getFactoryData() + const { factory, factoryData } = + await simpleAccountClient.account.getFactoryArgs() if (!factory || !factoryData) { throw new Error("Factory or factoryData not found") } const address = await getSenderAddress(client, { - entryPoint: ENTRYPOINT_ADDRESS_V07, + entryPointAddress: entryPoint07Address, factory, factoryData }) diff --git a/packages/permissionless/actions/public/getSenderAddress.ts b/packages/permissionless/actions/public/getSenderAddress.ts index 4cca5a30..7d9fd8d7 100644 --- a/packages/permissionless/actions/public/getSenderAddress.ts +++ b/packages/permissionless/actions/public/getSenderAddress.ts @@ -1,14 +1,14 @@ import { type Address, BaseError, - type Chain, type Client, type ContractFunctionExecutionErrorType, ContractFunctionRevertedError, type Hex, InvalidInputRpcError, + type OneOf, + type Prettify, RpcRequestError, - type Transport, UnknownRpcError, concat, decodeErrorResult @@ -16,37 +16,32 @@ import { import { simulateContract } from "viem/actions" import { getAction } from "viem/utils" -import type { Prettify } from "../../types/" -import type { - ENTRYPOINT_ADDRESS_V06_TYPE, - EntryPoint -} from "../../types/entrypoint" -export type GetSenderAddressParams = - entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE - ? { - initCode: Hex - entryPoint: entryPoint - factory?: never - factoryData?: never - } - : { - entryPoint: entryPoint - factory: Address - factoryData: Hex - initCode?: never - } +export type GetSenderAddressParams = OneOf< + | { + initCode: Hex + entryPointAddress: Address + factory?: never + factoryData?: never + } + | { + entryPointAddress: Address + factory: Address + factoryData: Hex + initCode?: never + } +> export class InvalidEntryPointError extends BaseError { override name = "InvalidEntryPointError" constructor({ cause, - entryPoint - }: { cause?: BaseError; entryPoint?: Address } = {}) { + entryPointAddress + }: { cause?: BaseError; entryPointAddress?: Address } = {}) { super( `The entry point address (\`entryPoint\`${ - entryPoint ? ` = ${entryPoint}` : "" + entryPointAddress ? ` = ${entryPointAddress}` : "" }) is not a valid entry point. getSenderAddress did not revert with a SenderAddressResult error.`, { cause @@ -80,15 +75,11 @@ export class InvalidEntryPointError extends BaseError { * * // Return '0x7a88a206ba40b37a8c07a2b5688cf8b287318b63' */ -export const getSenderAddress = async < - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined ->( - client: Client, - args: Prettify> +export const getSenderAddress = async ( + client: Client, + args: Prettify ): Promise
=> { - const { initCode, entryPoint, factory, factoryData } = args + const { initCode, entryPointAddress, factory, factoryData } = args if (!initCode && !factory && !factoryData) { throw new Error( @@ -102,7 +93,7 @@ export const getSenderAddress = async < simulateContract, "simulateContract" )({ - address: entryPoint, + address: entryPointAddress, abi: [ { inputs: [ @@ -224,12 +215,12 @@ export const getSenderAddress = async < if (revertError instanceof UnknownRpcError) { const parsedBody = JSON.parse( + // biome-ignore lint/suspicious/noExplicitAny: (revertError as unknown as any).cause.body ) const revertData = parsedBody.error.data const hexStringRegex = /0x[a-fA-F0-9]+/ - // biome-ignore lint/suspicious/noExplicitAny: const match = revertData.match(hexStringRegex) if (!match) { @@ -263,5 +254,5 @@ export const getSenderAddress = async < throw e } - throw new InvalidEntryPointError({ entryPoint }) + throw new InvalidEntryPointError({ entryPointAddress }) } diff --git a/packages/permissionless/actions/smartAccount.test.ts b/packages/permissionless/actions/smartAccount.test.ts deleted file mode 100644 index c2a63c7e..00000000 --- a/packages/permissionless/actions/smartAccount.test.ts +++ /dev/null @@ -1,198 +0,0 @@ -import type { - Abi, - Account, - Address, - Chain, - Client, - Hash, - Hex, - SignTypedDataParameters, - Transport, - TypedData -} from "viem" -import type { SignMessageParameters } from "viem" -import { describe, expectTypeOf, test } from "vitest" -import type { SmartAccount } from "../accounts" -import type { BundlerRpcSchema } from "../types/bundler" -import type { - ENTRYPOINT_ADDRESS_V06_TYPE, - EntryPoint -} from "../types/entrypoint" -import { - type DeployContractParametersWithPaymaster, - type PrepareUserOperationRequestParameters, - type PrepareUserOperationRequestReturnType, - type SendTransactionWithPaymasterParameters, - type SendTransactionsWithPaymasterParameters, - type SendUserOperationParameters, - type WriteContractWithPaymasterParameters, - deployContract, - prepareUserOperationRequest, - sendTransaction, - sendTransactions, - sendUserOperation, - signMessage, - signTypedData, - writeContract -} from "./smartAccount" - -describe("index", () => { - test("sendUserOperation", () => { - expectTypeOf(deployContract).toBeFunction() - expectTypeOf(deployContract) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - expectTypeOf(deployContract) - .parameter(1) - .toMatchTypeOf>() - expectTypeOf(deployContract).returns.toMatchTypeOf>() - }) - test("prepareUserOperationRequest", () => { - expectTypeOf(prepareUserOperationRequest).toBeFunction() - expectTypeOf(prepareUserOperationRequest) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - expectTypeOf(prepareUserOperationRequest) - .parameter(1) - .toMatchTypeOf>() - expectTypeOf(prepareUserOperationRequest).returns.toMatchTypeOf< - Promise> - >() - }) - test("sendTransaction", () => { - expectTypeOf(sendTransaction).toBeFunction() - expectTypeOf(sendTransaction) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - expectTypeOf(sendTransaction) - .parameter(1) - .toMatchTypeOf>() - expectTypeOf(sendTransaction).returns.toMatchTypeOf>() - }) - test("sendTransactions", () => { - expectTypeOf(sendTransactions).toBeFunction() - expectTypeOf(sendTransactions) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - expectTypeOf(sendTransactions) - .parameter(1) - .toMatchTypeOf< - SendTransactionsWithPaymasterParameters - >() - expectTypeOf(sendTransactions).returns.toMatchTypeOf>() - }) - test("writeContract", () => { - expectTypeOf(writeContract).toBeFunction() - expectTypeOf(writeContract) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - expectTypeOf( - writeContract< - ENTRYPOINT_ADDRESS_V06_TYPE, - Chain, - SmartAccount, - Abi - > - ) - .parameter(1) - .toMatchTypeOf< - WriteContractWithPaymasterParameters< - ENTRYPOINT_ADDRESS_V06_TYPE, - Chain, - SmartAccount, - Abi - > - >() - expectTypeOf(writeContract).returns.toMatchTypeOf>() - }) - test("sendUserOperation", () => { - expectTypeOf(sendUserOperation).toBeFunction() - expectTypeOf(sendUserOperation) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - expectTypeOf(sendUserOperation) - .parameter(1) - .toMatchTypeOf>() - expectTypeOf(sendUserOperation).returns.toMatchTypeOf>() - }) - test("signMessage", () => { - expectTypeOf(signMessage).toBeFunction() - expectTypeOf(signMessage) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - expectTypeOf(signMessage) - .parameter(1) - .toMatchTypeOf>>() - expectTypeOf(signMessage).returns.toMatchTypeOf>() - }) - test("signTypedData", () => { - expectTypeOf(signTypedData).toBeFunction() - expectTypeOf(signTypedData) - .parameter(0) - .toMatchTypeOf< - Client< - Transport, - Chain | undefined, - Account | undefined, - BundlerRpcSchema - > - >() - expectTypeOf( - signTypedData - ) - .parameter(1) - .toMatchTypeOf< - SignTypedDataParameters - >() - expectTypeOf(signTypedData).returns.toMatchTypeOf>() - }) -}) diff --git a/packages/permissionless/actions/smartAccount.ts b/packages/permissionless/actions/smartAccount.ts index 856c15d5..f09e4245 100644 --- a/packages/permissionless/actions/smartAccount.ts +++ b/packages/permissionless/actions/smartAccount.ts @@ -1,56 +1,9 @@ -import { - type DeployContractParametersWithPaymaster, - deployContract -} from "./smartAccount/deployContract" - -import { - type Middleware, - type PrepareUserOperationRequestParameters, - type PrepareUserOperationRequestReturnType, - type SponsorUserOperationReturnType, - prepareUserOperationRequest -} from "./smartAccount/prepareUserOperationRequest" - -import { - type SendTransactionWithPaymasterParameters, - sendTransaction -} from "./smartAccount/sendTransaction" - -import { - type SendUserOperationParameters, - sendUserOperation -} from "./smartAccount/sendUserOperation" +import { sendTransaction } from "./smartAccount/sendTransaction" import { signMessage } from "./smartAccount/signMessage" import { signTypedData } from "./smartAccount/signTypedData" -import { - type SendTransactionsWithPaymasterParameters, - sendTransactions -} from "./smartAccount/sendTransactions" - -import { - type WriteContractWithPaymasterParameters, - writeContract -} from "./smartAccount/writeContract" +import { writeContract } from "./smartAccount/writeContract" -export { - deployContract, - type DeployContractParametersWithPaymaster, - prepareUserOperationRequest, - type PrepareUserOperationRequestParameters, - type PrepareUserOperationRequestReturnType, - type SponsorUserOperationReturnType, - sendTransaction, - sendUserOperation, - type SendUserOperationParameters, - signMessage, - signTypedData, - type SendTransactionWithPaymasterParameters, - type Middleware, - sendTransactions, - type SendTransactionsWithPaymasterParameters, - type WriteContractWithPaymasterParameters, - writeContract -} +export { sendTransaction, signMessage, signTypedData, writeContract } diff --git a/packages/permissionless/actions/smartAccount/deployContract.test.ts b/packages/permissionless/actions/smartAccount/deployContract.test.ts deleted file mode 100644 index c74b96c1..00000000 --- a/packages/permissionless/actions/smartAccount/deployContract.test.ts +++ /dev/null @@ -1,118 +0,0 @@ -import type { Chain, Client, Transport } from "viem" -import { generatePrivateKey } from "viem/accounts" -import { foundry } from "viem/chains" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getCoreSmartAccounts, - getPimlicoPaymasterClient -} from "../../../permissionless-test/src/utils" -import type { SmartAccount } from "../../accounts" -import type { SmartAccountClient } from "../../clients/createSmartAccountClient" -import type { - ENTRYPOINT_ADDRESS_V06_TYPE, - ENTRYPOINT_ADDRESS_V07_TYPE, - EntryPoint -} from "../../types/entrypoint" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" -import { deployContract } from "./deployContract" - -describe.each(getCoreSmartAccounts())( - "deployContract $name", - ({ - getSmartAccountClient, - supportsEntryPointV06, - supportsEntryPointV07 - }) => { - testWithRpc.skipIf(!supportsEntryPointV06)( - "deployContract_V06", - async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) - }) - - await expect(async () => - deployContract( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - account: smartClient.account, - chain: foundry, - abi: [ - { - inputs: [], - stateMutability: "payable", - type: "constructor" - } - ], - bytecode: - "0x608060405260358060116000396000f3006080604052600080fd00a165627a7a72305820f86ff341f0dff29df244305f8aa88abaf10e3a0719fa6ea1dcdd01b8b7d750970029" - } - ) - ).rejects.toThrowError( - /^.*doesn't support account deployment.*$/i - ) - } - ) - - testWithRpc.skipIf(!supportsEntryPointV07)( - "deployContract_V07", - async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const smartClient = (await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) - })) as SmartAccountClient< - ENTRYPOINT_ADDRESS_V07_TYPE, - Transport, - Chain, - SmartAccount - > - - await expect(async () => - deployContract( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - account: smartClient.account, - chain: foundry, - abi: [ - { - inputs: [], - stateMutability: "payable", - type: "constructor" - } - ], - bytecode: - "0x608060405260358060116000396000f3006080604052600080fd00a165627a7a72305820f86ff341f0dff29df244305f8aa88abaf10e3a0719fa6ea1dcdd01b8b7d750970029" - } - ) - ).rejects.toThrowError( - /^.*doesn't support account deployment.*$/i - ) - } - ) - } -) diff --git a/packages/permissionless/actions/smartAccount/deployContract.ts b/packages/permissionless/actions/smartAccount/deployContract.ts deleted file mode 100644 index bbb1ee2a..00000000 --- a/packages/permissionless/actions/smartAccount/deployContract.ts +++ /dev/null @@ -1,129 +0,0 @@ -import type { - Abi, - Chain, - Client, - DeployContractParameters, - EncodeDeployDataParameters, - Hash, - Transport -} from "viem" -import { getAction } from "viem/utils" -import type { SmartAccount } from "../../accounts/types" -import type { Prettify } from "../../types/" -import type { EntryPoint } from "../../types/entrypoint" -import { parseAccount } from "../../utils/" -import { AccountOrClientNotFoundError } from "../../utils/signUserOperationHashWithECDSA" -import { waitForUserOperationReceipt } from "../bundler/waitForUserOperationReceipt" -import type { Middleware } from "./prepareUserOperationRequest" -import { - type SendUserOperationParameters, - sendUserOperation -} from "./sendUserOperation" - -export type DeployContractParametersWithPaymaster< - entryPoint extends EntryPoint, - TAbi extends Abi | readonly unknown[] = Abi | readonly unknown[], - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined, - TChainOverride extends Chain | undefined = Chain | undefined -> = DeployContractParameters & - Middleware - -/** - * Deploys a contract to the network, given bytecode and constructor arguments. - * This function also allows you to sponsor this transaction if sender is a smartAccount - * - * - Docs: https://viem.sh/docs/contract/deployContract.html - * - Examples: https://stackblitz.com/github/wagmi-dev/viem/tree/main/examples/contracts/deploying-contracts - * - * @param client - Client to use - * @param parameters - {@link DeployContractParameters} - * @returns The [Transaction](https://viem.sh/docs/glossary/terms.html#transaction) hash. - * - * @example - * import { createWalletClient, http } from 'viem' - * import { privateKeyToAccount } from 'viem/accounts' - * import { mainnet } from 'viem/chains' - * import { deployContract } from 'viem/contract' - * - * const client = createWalletClient({ - * account: privateKeyToAccount('0x…'), - * chain: mainnet, - * transport: http(), - * }) - * const hash = await deployContract(client, { - * abi: [], - * account: '0x…, - * bytecode: '0x608060405260405161083e38038061083e833981016040819052610...', - * }) - */ -export async function deployContract< - entryPoint extends EntryPoint, - TTransport extends Transport, - TChain extends Chain | undefined, - TAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined ->( - client: Client, - args: Prettify> -): Promise { - const { - abi, - args: constructorArgs, - bytecode, - middleware, - ...request - } = args - - const { account: account_ = client.account } = request - - if (!account_) { - throw new AccountOrClientNotFoundError({ - docsPath: "/docs/actions/wallet/sendTransaction" - }) - } - - const account = parseAccount(account_) as SmartAccount< - entryPoint, - string, - TTransport, - TChain - > - - const userOpHash = await getAction( - client, - sendUserOperation, - "sendUserOperation" - )({ - userOperation: { - sender: account.address, - maxFeePerGas: request.maxFeePerGas, - maxPriorityFeePerGas: request.maxPriorityFeePerGas, - callData: await account.encodeDeployCallData({ - abi, - bytecode, - args: constructorArgs - } as EncodeDeployDataParameters) - }, - account: account, - middleware - } as SendUserOperationParameters) - - const userOperationReceipt = await getAction( - client, - waitForUserOperationReceipt, - "waitForUserOperationReceipt" - )({ - hash: userOpHash - }) - - return userOperationReceipt?.receipt.transactionHash -} diff --git a/packages/permissionless/actions/smartAccount/prepareUserOperationRequest.test.ts b/packages/permissionless/actions/smartAccount/prepareUserOperationRequest.test.ts deleted file mode 100644 index 3bf09aec..00000000 --- a/packages/permissionless/actions/smartAccount/prepareUserOperationRequest.test.ts +++ /dev/null @@ -1,520 +0,0 @@ -import { type Chain, type Client, type Transport, zeroAddress } from "viem" -import { generatePrivateKey } from "viem/accounts" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getCoreSmartAccounts, - getPimlicoBundlerClient, - getPimlicoPaymasterClient -} from "../../../permissionless-test/src/utils" -import type { SmartAccount } from "../../accounts" -import type { EntryPoint } from "../../types/entrypoint" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" -import { pimlicoPaymasterActions } from "../pimlico" -import { prepareUserOperationRequest } from "./prepareUserOperationRequest" - -describe.each(getCoreSmartAccounts())( - "prepareUserOperationRequest $name", - ({ - getSmartAccountClient, - supportsEntryPointV06, - supportsEntryPointV07 - }) => { - testWithRpc.skipIf(!supportsEntryPointV06)( - "prepareUserOperationRequest_v06", - async ({ rpc }) => { - const { anvilRpc, altoRpc } = rpc - - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc - }) - - const userOperation = await prepareUserOperationRequest( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - userOperation: { - callData: await smartClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - } - } - ) - expect(userOperation).toBeTruthy() - expect(userOperation.sender).toBe(smartClient.account.address) - expect(userOperation.nonce).toBe( - await smartClient.account.getNonce() - ) - expect(userOperation.initCode).toBe( - await smartClient.account.getInitCode() - ) - expect(userOperation.callData).toBe( - await smartClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - ) - expect(userOperation.callGasLimit).toBeTruthy() - expect(userOperation.verificationGasLimit).toBeTruthy() - expect(userOperation.maxFeePerGas).toBeTruthy() - expect(userOperation.maxPriorityFeePerGas).toBeTruthy() - expect(userOperation.paymasterAndData).toBe("0x") - expect(userOperation.signature).toBe( - // @ts-ignore: since tests return all smart account client, some of them don't support V06. - // The TS error is because in that case, getDummySignature would not accept the userOperation of type UserOperation - await smartClient.account.getDummySignature(userOperation) - ) - - expect(userOperation.factory).toBe(undefined) - expect(userOperation.factoryData).toBe(undefined) - expect(userOperation.paymaster).toBe(undefined) - expect(userOperation.paymasterVerificationGasLimit).toBe( - undefined - ) - expect(userOperation.paymasterPostOpGasLimit).toBe(undefined) - expect(userOperation.paymasterData).toBe(undefined) - } - ) - - testWithRpc.skipIf(!supportsEntryPointV06)( - "prepareUserOperationRequest_v06", - async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc - }) - - const pimlicoBundlerClient = getPimlicoBundlerClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - altoRpc: altoRpc - }) - - const pimlicoPaymasterActions = getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc: paymasterRpc - }) - - const userOperation = await prepareUserOperationRequest( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - userOperation: { - callData: await smartClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - }, - middleware: { - gasPrice: async () => - ( - await pimlicoBundlerClient.getUserOperationGasPrice() - ).fast, - sponsorUserOperation: - pimlicoPaymasterActions.sponsorUserOperation - } - } - ) - - expect(userOperation).toBeTruthy() - expect(userOperation.sender).toBe(smartClient.account.address) - expect(userOperation.nonce).toBe( - await smartClient.account.getNonce() - ) - expect(userOperation.initCode).toBe( - await smartClient.account.getInitCode() - ) - expect(userOperation.callData).toBe( - await smartClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - ) - expect(userOperation.callGasLimit).toBeTruthy() - expect(userOperation.verificationGasLimit).toBeTruthy() - expect(userOperation.maxFeePerGas).toBeTruthy() - expect(userOperation.maxPriorityFeePerGas).toBeTruthy() - expect(userOperation.paymasterAndData).toBeTruthy() - expect(userOperation.signature).toBe( - // @ts-ignore: since tests return all smart account client, some of them don't support V06. - // The TS error is because in that case, getDummySignature would not accept the userOperation of type UserOperation - await smartClient.account.getDummySignature(userOperation) - ) - - expect(userOperation.factory).toBe(undefined) - expect(userOperation.factoryData).toBe(undefined) - expect(userOperation.paymaster).toBe(undefined) - expect(userOperation.paymasterVerificationGasLimit).toBe( - undefined - ) - expect(userOperation.paymasterPostOpGasLimit).toBe(undefined) - expect(userOperation.paymasterData).toBe(undefined) - } - ) - - testWithRpc.skipIf(!supportsEntryPointV06)( - "prepareUserOperationRequest_v06", - async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc - }) - - const pimlicoBundlerClient = getPimlicoBundlerClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - altoRpc: altoRpc - }) - const pimlicoPaymasterActions = getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc: paymasterRpc - }) - - const userOperation = await prepareUserOperationRequest( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - userOperation: { - callData: await smartClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - }, - middleware: async (args: { - userOperation: T - entryPoint: EntryPoint - }) => { - const gasPrice = ( - await pimlicoBundlerClient.getUserOperationGasPrice() - ).fast - - return { - ...args.userOperation, - maxFeePerGas: gasPrice.maxFeePerGas, - maxPriorityFeePerGas: - gasPrice.maxPriorityFeePerGas, - ...(await pimlicoPaymasterActions.sponsorUserOperation( - { - userOperation: { - ...args.userOperation, - maxFeePerGas: gasPrice.maxFeePerGas, - maxPriorityFeePerGas: - gasPrice.maxPriorityFeePerGas - } - } - )) - } as T - } - } - ) - - expect(userOperation).toBeTruthy() - expect(userOperation.sender).toBe(smartClient.account.address) - expect(userOperation.nonce).toBe( - await smartClient.account.getNonce() - ) - expect(userOperation.initCode).toBe( - await smartClient.account.getInitCode() - ) - expect(userOperation.callData).toBe( - await smartClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - ) - expect(userOperation.callGasLimit).toBeTruthy() - expect(userOperation.verificationGasLimit).toBeTruthy() - expect(userOperation.maxFeePerGas).toBeTruthy() - expect(userOperation.maxPriorityFeePerGas).toBeTruthy() - expect(userOperation.paymasterAndData).toBeTruthy() - expect(userOperation.signature).toBe( - // @ts-ignore: since tests return all smart account client, some of them don't support V06. - // The TS error is because in that case, getDummySignature would not accept the userOperation of type UserOperation - await smartClient.account.getDummySignature(userOperation) - ) - - expect(userOperation.factory).toBe(undefined) - expect(userOperation.factoryData).toBe(undefined) - expect(userOperation.paymaster).toBe(undefined) - expect(userOperation.paymasterVerificationGasLimit).toBe( - undefined - ) - expect(userOperation.paymasterPostOpGasLimit).toBe(undefined) - expect(userOperation.paymasterData).toBe(undefined) - } - ) - testWithRpc.skipIf(!supportsEntryPointV07)( - "prepareUserOperationRequest_v07", - async ({ rpc }) => { - const { anvilRpc, altoRpc } = rpc - - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc - }) - - const userOperation = await prepareUserOperationRequest( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - userOperation: { - callData: await smartClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - } - } - ) - - expect(userOperation).toBeTruthy() - expect(userOperation.sender).toBe(smartClient.account.address) - expect(userOperation.nonce).toBe( - await smartClient.account.getNonce() - ) - expect(userOperation.factory).toBe( - await smartClient.account.getFactory() - ) - expect(userOperation.factoryData).toBe( - await smartClient.account.getFactoryData() - ) - expect(userOperation.callData).toBe( - await smartClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - ) - expect(userOperation.callGasLimit).toBeTruthy() - expect(userOperation.verificationGasLimit).toBeTruthy() - expect(userOperation.maxFeePerGas).toBeTruthy() - expect(userOperation.maxPriorityFeePerGas).toBeTruthy() - expect(userOperation.paymaster).toBe(undefined) - expect(userOperation.paymasterVerificationGasLimit).toBe( - undefined - ) - expect(userOperation.signature).toBe( - // @ts-ignore: since tests return all smart account client, some of them don't support V07. - // The TS error is because in that case, getDummySignature would not accept the userOperation of type UserOperation - await smartClient.account.getDummySignature(userOperation) - ) - expect(userOperation.paymasterPostOpGasLimit).toBe(0n) - expect(userOperation.paymasterData).toBe(undefined) - expect(userOperation.paymasterAndData).toBe(undefined) - expect(userOperation.initCode).toBe(undefined) - } - ) - - testWithRpc.skipIf(!supportsEntryPointV07)( - "prepareUserOperationRequest_v07", - async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc - }) - - const pimlicoBundlerClient = getPimlicoBundlerClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - altoRpc: altoRpc - }) - - const pimlicoPaymasterActions = getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc: paymasterRpc - }) - - const userOperation = await prepareUserOperationRequest( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - userOperation: { - callData: await smartClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - }, - middleware: { - gasPrice: async () => - ( - await pimlicoBundlerClient.getUserOperationGasPrice() - ).fast, - sponsorUserOperation: - pimlicoPaymasterActions.sponsorUserOperation - } - } - ) - - expect(userOperation).toBeTruthy() - expect(userOperation.sender).toBe(smartClient.account.address) - expect(userOperation.nonce).toBe( - await smartClient.account.getNonce() - ) - expect(userOperation.factory).toBe( - await smartClient.account.getFactory() - ) - expect(userOperation.factoryData).toBe( - await smartClient.account.getFactoryData() - ) - expect(userOperation.callData).toBe( - await smartClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - ) - expect(userOperation.callGasLimit).toBeTruthy() - expect(userOperation.verificationGasLimit).toBeTruthy() - expect(userOperation.maxFeePerGas).toBeTruthy() - expect(userOperation.maxPriorityFeePerGas).toBeTruthy() - expect(userOperation.paymaster).toBeTruthy() - expect(userOperation.paymasterVerificationGasLimit).toBeTruthy() - expect(userOperation.signature).toBe( - // @ts-ignore: since tests return all smart account client, some of them don't support V07. - // The TS error is because in that case, getDummySignature would not accept the userOperation of type UserOperation - await smartClient.account.getDummySignature(userOperation) - ) - expect(userOperation.paymasterPostOpGasLimit).toBe(1n) - expect(userOperation.paymasterData).toBeTruthy() - expect(userOperation.paymasterAndData).toBe(undefined) - expect(userOperation.initCode).toBe(undefined) - } - ) - - testWithRpc.skipIf(!supportsEntryPointV07)( - "prepareUserOperationRequest_v07", - async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc - }) - - const pimlicoBundlerClient = getPimlicoBundlerClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - altoRpc: altoRpc - }) - const pimlicoPaymasterActions = getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc: paymasterRpc - }) - - const userOperation = await prepareUserOperationRequest( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - userOperation: { - callData: await smartClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - }, - middleware: async (args: { - userOperation: T - entryPoint: EntryPoint - }) => { - const gasPrice = ( - await pimlicoBundlerClient.getUserOperationGasPrice() - ).fast - - return { - ...args.userOperation, - maxFeePerGas: gasPrice.maxFeePerGas, - maxPriorityFeePerGas: - gasPrice.maxPriorityFeePerGas, - ...(await pimlicoPaymasterActions.sponsorUserOperation( - { - userOperation: { - ...args.userOperation, - maxFeePerGas: gasPrice.maxFeePerGas, - maxPriorityFeePerGas: - gasPrice.maxPriorityFeePerGas - } - } - )) - } as T - } - } - ) - - expect(userOperation).toBeTruthy() - expect(userOperation.sender).toBe(smartClient.account.address) - expect(userOperation.nonce).toBe( - await smartClient.account.getNonce() - ) - expect(userOperation.factory).toBe( - await smartClient.account.getFactory() - ) - expect(userOperation.factoryData).toBe( - await smartClient.account.getFactoryData() - ) - expect(userOperation.callData).toBe( - await smartClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - ) - expect(userOperation.callGasLimit).toBeTruthy() - expect(userOperation.verificationGasLimit).toBeTruthy() - expect(userOperation.maxFeePerGas).toBeTruthy() - expect(userOperation.maxPriorityFeePerGas).toBeTruthy() - expect(userOperation.paymaster).toBeTruthy() - expect(userOperation.paymasterVerificationGasLimit).toBeTruthy() - expect(userOperation.signature).toBe( - // @ts-ignore: since tests return all smart account client, some of them don't support V07. - // The TS error is because in that case, getDummySignature would not accept the userOperation of type UserOperation - await smartClient.account.getDummySignature(userOperation) - ) - expect(userOperation.paymasterPostOpGasLimit).toBe(1n) - expect(userOperation.paymasterData).toBeTruthy() - expect(userOperation.paymasterAndData).toBe(undefined) - expect(userOperation.initCode).toBe(undefined) - } - ) - } -) diff --git a/packages/permissionless/actions/smartAccount/prepareUserOperationRequest.ts b/packages/permissionless/actions/smartAccount/prepareUserOperationRequest.ts deleted file mode 100644 index 444a4150..00000000 --- a/packages/permissionless/actions/smartAccount/prepareUserOperationRequest.ts +++ /dev/null @@ -1,472 +0,0 @@ -import type { Chain, Client, Transport } from "viem" -import { estimateFeesPerGas } from "viem/actions" -import { getAction } from "viem/utils" -import type { SmartAccount } from "../../accounts/types" -import type { PartialPick } from "../../types" -import type { - GetAccountParameter, - PartialBy, - Prettify, - UserOperation -} from "../../types/" -import type { StateOverrides } from "../../types/bundler" -import type { - ENTRYPOINT_ADDRESS_V06_TYPE, - ENTRYPOINT_ADDRESS_V07_TYPE, - EntryPoint, - GetEntryPointVersion -} from "../../types/entrypoint" -import { AccountOrClientNotFoundError, parseAccount } from "../../utils/" -import { getEntryPointVersion } from "../../utils/getEntryPointVersion" -import { estimateUserOperationGas } from "../bundler/estimateUserOperationGas" - -export type SponsorUserOperationReturnType = - entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE - ? Prettify< - Pick< - UserOperation<"v0.6">, - | "callGasLimit" - | "verificationGasLimit" - | "preVerificationGas" - | "paymasterAndData" - > & - PartialPick< - UserOperation<"v0.6">, - "maxFeePerGas" | "maxPriorityFeePerGas" - > - > - : Prettify< - Pick< - UserOperation<"v0.7">, - | "callGasLimit" - | "verificationGasLimit" - | "preVerificationGas" - | "paymaster" - | "paymasterVerificationGasLimit" - | "paymasterPostOpGasLimit" - | "paymasterData" - > & - PartialPick< - UserOperation<"v0.7">, - "maxFeePerGas" | "maxPriorityFeePerGas" - > - > - -export type Middleware = { - middleware?: - | ((args: { - userOperation: UserOperation> - entryPoint: entryPoint - }) => Promise>>) - | { - gasPrice?: () => Promise<{ - maxFeePerGas: bigint - maxPriorityFeePerGas: bigint - }> - sponsorUserOperation?: (args: { - userOperation: UserOperation> - entryPoint: entryPoint - }) => Promise> - } -} - -export type PrepareUserOperationRequestParameters< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined -> = { - userOperation: entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE - ? PartialBy< - UserOperation<"v0.6">, - | "sender" - | "nonce" - | "initCode" - | "callGasLimit" - | "verificationGasLimit" - | "preVerificationGas" - | "maxFeePerGas" - | "maxPriorityFeePerGas" - | "paymasterAndData" - | "signature" - > - : PartialBy< - UserOperation<"v0.7">, - | "sender" - | "nonce" - | "factory" - | "factoryData" - | "callGasLimit" - | "verificationGasLimit" - | "preVerificationGas" - | "maxFeePerGas" - | "maxPriorityFeePerGas" - | "paymaster" - | "paymasterVerificationGasLimit" - | "paymasterPostOpGasLimit" - | "paymasterData" - | "signature" - > -} & GetAccountParameter & - Middleware - -export type PrepareUserOperationRequestReturnType< - entryPoint extends EntryPoint -> = UserOperation> - -async function prepareUserOperationRequestForEntryPointV06< - entryPoint extends EntryPoint = ENTRYPOINT_ADDRESS_V06_TYPE, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined ->( - client: Client, - args: Prettify< - PrepareUserOperationRequestParameters< - entryPoint, - TTransport, - TChain, - TAccount - > - >, - stateOverrides?: StateOverrides -): Promise>> { - const { - account: account_ = client.account, - userOperation: partialUserOperation, - middleware - } = args - if (!account_) throw new AccountOrClientNotFoundError() - - const account = parseAccount(account_) as SmartAccount< - ENTRYPOINT_ADDRESS_V06_TYPE, - string, - Transport, - TChain - > - - const [sender, nonce, initCode, callData] = await Promise.all([ - partialUserOperation.sender || account.address, - partialUserOperation.nonce || account.getNonce(), - partialUserOperation.initCode || account.getInitCode(), - partialUserOperation.callData - ]) - - const userOperation: UserOperation<"v0.6"> = { - sender, - nonce, - initCode, - callData, - paymasterAndData: "0x", - signature: partialUserOperation.signature || "0x", - maxFeePerGas: partialUserOperation.maxFeePerGas || BigInt(0), - maxPriorityFeePerGas: - partialUserOperation.maxPriorityFeePerGas || BigInt(0), - callGasLimit: partialUserOperation.callGasLimit || BigInt(0), - verificationGasLimit: - partialUserOperation.verificationGasLimit || BigInt(0), - preVerificationGas: partialUserOperation.preVerificationGas || BigInt(0) - } - - if (userOperation.signature === "0x") { - userOperation.signature = await account.getDummySignature(userOperation) - } - - if (typeof middleware === "function") { - return middleware({ - userOperation, - entryPoint: account.entryPoint - } as { - userOperation: UserOperation> - entryPoint: entryPoint - }) as Promise> - } - - if (middleware && typeof middleware !== "function" && middleware.gasPrice) { - const gasPrice = await middleware.gasPrice() - userOperation.maxFeePerGas = gasPrice.maxFeePerGas - userOperation.maxPriorityFeePerGas = gasPrice.maxPriorityFeePerGas - } - - if (!userOperation.maxFeePerGas || !userOperation.maxPriorityFeePerGas) { - const estimateGas = await estimateFeesPerGas(account.client) - userOperation.maxFeePerGas = - userOperation.maxFeePerGas || estimateGas.maxFeePerGas - userOperation.maxPriorityFeePerGas = - userOperation.maxPriorityFeePerGas || - estimateGas.maxPriorityFeePerGas - } - - if ( - middleware && - typeof middleware !== "function" && - middleware.sponsorUserOperation - ) { - const sponsorUserOperationData = (await middleware.sponsorUserOperation( - { - userOperation, - entryPoint: account.entryPoint - } as { - userOperation: UserOperation> - entryPoint: entryPoint - } - )) as SponsorUserOperationReturnType - - userOperation.callGasLimit = sponsorUserOperationData.callGasLimit - userOperation.verificationGasLimit = - sponsorUserOperationData.verificationGasLimit - userOperation.preVerificationGas = - sponsorUserOperationData.preVerificationGas - userOperation.paymasterAndData = - sponsorUserOperationData.paymasterAndData - userOperation.maxFeePerGas = - sponsorUserOperationData.maxFeePerGas || userOperation.maxFeePerGas - userOperation.maxPriorityFeePerGas = - sponsorUserOperationData.maxPriorityFeePerGas || - userOperation.maxPriorityFeePerGas - return userOperation as PrepareUserOperationRequestReturnType - } - - if ( - !userOperation.callGasLimit || - !userOperation.verificationGasLimit || - !userOperation.preVerificationGas - ) { - const gasParameters = await getAction( - client, - estimateUserOperationGas, - "estimateUserOperationGas" - )( - { - userOperation, - entryPoint: account.entryPoint - } as { - userOperation: UserOperation> - entryPoint: entryPoint - }, - // @ts-ignore getAction takes only two params but when compiled this will work - stateOverrides - ) - - userOperation.callGasLimit |= gasParameters.callGasLimit - userOperation.verificationGasLimit = - userOperation.verificationGasLimit || - gasParameters.verificationGasLimit - userOperation.preVerificationGas = - userOperation.preVerificationGas || gasParameters.preVerificationGas - } - - return userOperation as PrepareUserOperationRequestReturnType -} - -async function prepareUserOperationRequestEntryPointV07< - entryPoint extends EntryPoint = ENTRYPOINT_ADDRESS_V07_TYPE, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined ->( - client: Client, - args: Prettify< - PrepareUserOperationRequestParameters< - entryPoint, - TTransport, - TChain, - TAccount - > - >, - stateOverrides?: StateOverrides -): Promise>> { - const { - account: account_ = client.account, - userOperation: partialUserOperation, - middleware - } = args - if (!account_) throw new AccountOrClientNotFoundError() - - const account = parseAccount(account_) as SmartAccount< - ENTRYPOINT_ADDRESS_V07_TYPE, - string, - Transport, - TChain - > - - const [sender, nonce, factory, factoryData, callData] = await Promise.all([ - partialUserOperation.sender || account.address, - partialUserOperation.nonce || account.getNonce(), - partialUserOperation.factory || account.getFactory(), - partialUserOperation.factoryData || account.getFactoryData(), - partialUserOperation.callData - ]) - - const userOperation: UserOperation<"v0.7"> = { - sender, - nonce, - factory: factory, - factoryData: factoryData, - callData, - callGasLimit: partialUserOperation.callGasLimit || BigInt(0), - verificationGasLimit: - partialUserOperation.verificationGasLimit || BigInt(0), - preVerificationGas: - partialUserOperation.preVerificationGas || BigInt(0), - maxFeePerGas: partialUserOperation.maxFeePerGas || BigInt(0), - maxPriorityFeePerGas: - partialUserOperation.maxPriorityFeePerGas || BigInt(0), - signature: partialUserOperation.signature || "0x" - } - - if (userOperation.signature === "0x") { - userOperation.signature = await account.getDummySignature(userOperation) - } - - if (typeof middleware === "function") { - return middleware({ - userOperation, - entryPoint: account.entryPoint - } as { - userOperation: UserOperation> - entryPoint: entryPoint - }) as Promise> - } - - if (middleware && typeof middleware !== "function" && middleware.gasPrice) { - const gasPrice = await middleware.gasPrice() - userOperation.maxFeePerGas = gasPrice.maxFeePerGas - userOperation.maxPriorityFeePerGas = gasPrice.maxPriorityFeePerGas - } - - if (!userOperation.maxFeePerGas || !userOperation.maxPriorityFeePerGas) { - const estimateGas = await estimateFeesPerGas(account.client) - userOperation.maxFeePerGas = - userOperation.maxFeePerGas || estimateGas.maxFeePerGas - userOperation.maxPriorityFeePerGas = - userOperation.maxPriorityFeePerGas || - estimateGas.maxPriorityFeePerGas - } - - if ( - middleware && - typeof middleware !== "function" && - middleware.sponsorUserOperation - ) { - const sponsorUserOperationData = (await middleware.sponsorUserOperation( - { - userOperation, - entryPoint: account.entryPoint - } as { - userOperation: UserOperation> - entryPoint: entryPoint - } - )) as SponsorUserOperationReturnType - - userOperation.callGasLimit = sponsorUserOperationData.callGasLimit - userOperation.verificationGasLimit = - sponsorUserOperationData.verificationGasLimit - userOperation.preVerificationGas = - sponsorUserOperationData.preVerificationGas - userOperation.paymaster = sponsorUserOperationData.paymaster - userOperation.paymasterVerificationGasLimit = - sponsorUserOperationData.paymasterVerificationGasLimit - userOperation.paymasterPostOpGasLimit = - sponsorUserOperationData.paymasterPostOpGasLimit - userOperation.paymasterData = sponsorUserOperationData.paymasterData - userOperation.maxFeePerGas = - sponsorUserOperationData.maxFeePerGas || userOperation.maxFeePerGas - userOperation.maxPriorityFeePerGas = - sponsorUserOperationData.maxPriorityFeePerGas || - userOperation.maxPriorityFeePerGas - - return userOperation as PrepareUserOperationRequestReturnType - } - - if ( - !userOperation.callGasLimit || - !userOperation.verificationGasLimit || - !userOperation.preVerificationGas - ) { - const gasParameters = await getAction( - client, - estimateUserOperationGas, - "estimateUserOperationGas" - )( - { - userOperation, - entryPoint: account.entryPoint - }, - // @ts-ignore getAction takes only two params but when compiled this will work - stateOverrides - ) - - userOperation.callGasLimit |= gasParameters.callGasLimit - userOperation.verificationGasLimit = - userOperation.verificationGasLimit || - gasParameters.verificationGasLimit - userOperation.preVerificationGas = - userOperation.preVerificationGas || gasParameters.preVerificationGas - userOperation.paymasterPostOpGasLimit = - userOperation.paymasterPostOpGasLimit || - gasParameters.paymasterPostOpGasLimit - } - - return userOperation as PrepareUserOperationRequestReturnType -} - -export async function prepareUserOperationRequest< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined ->( - client: Client, - args: Prettify< - PrepareUserOperationRequestParameters< - entryPoint, - TTransport, - TChain, - TAccount - > - >, - stateOverrides?: StateOverrides -): Promise>> { - const { account: account_ = client.account } = args - if (!account_) throw new AccountOrClientNotFoundError() - - const account = parseAccount(account_) as SmartAccount< - entryPoint, - string, - TTransport, - TChain - > - - const entryPointVersion = getEntryPointVersion(account.entryPoint) - - if (entryPointVersion === "v0.6") { - return prepareUserOperationRequestForEntryPointV06( - client, - args, - stateOverrides - ) as Promise> - } - - return prepareUserOperationRequestEntryPointV07( - client, - args, - stateOverrides - ) as Promise> -} diff --git a/packages/permissionless/actions/smartAccount/sendTransaction.test.ts b/packages/permissionless/actions/smartAccount/sendTransaction.test.ts index 35f5c1fa..84f8e085 100644 --- a/packages/permissionless/actions/smartAccount/sendTransaction.test.ts +++ b/packages/permissionless/actions/smartAccount/sendTransaction.test.ts @@ -1,15 +1,11 @@ -import { type Chain, type Client, type Transport, zeroAddress } from "viem" -import { generatePrivateKey } from "viem/accounts" +import { zeroAddress } from "viem" +import { foundry } from "viem/chains" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" import { getCoreSmartAccounts, - getPimlicoPaymasterClient, getPublicClient } from "../../../permissionless-test/src/utils" -import type { SmartAccount } from "../../accounts" -import type { EntryPoint } from "../../types/entrypoint" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" import { sendTransaction } from "./sendTransaction" describe.each(getCoreSmartAccounts())( @@ -22,31 +18,20 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(!supportsEntryPointV06)( "sendTransaction_v06", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc + const { anvilRpc } = rpc const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) + entryPoint: { + version: "0.6" + }, + ...rpc }) - const transactionHash = await sendTransaction( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - to: zeroAddress, - data: "0x", - value: 0n - } - ) + const transactionHash = await sendTransaction(smartClient, { + to: zeroAddress, + data: "0x", + value: 0n + }) expect(transactionHash).toBeTruthy() @@ -59,37 +44,44 @@ describe.each(getCoreSmartAccounts())( expect(receipt).toBeTruthy() expect(receipt.transactionHash).toBe(transactionHash) expect(receipt.status).toBe("success") + + // -- second transaction after deployment + + const transactionHash2 = await sendTransaction(smartClient, { + to: zeroAddress, + data: "0x", + value: 0n + }) + + expect(transactionHash2).toBeTruthy() + + const receipt2 = await publicClient.getTransactionReceipt({ + hash: transactionHash2 + }) + + expect(receipt2).toBeTruthy() + expect(receipt2.transactionHash).toBe(transactionHash2) + expect(receipt2.status).toBe("success") } ) testWithRpc.skipIf(!supportsEntryPointV07)( "sendTransaction_v07", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc + const { anvilRpc } = rpc const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) + entryPoint: { + version: "0.7" + }, + ...rpc }) - const transactionHash = await sendTransaction( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - to: zeroAddress, - data: "0x", - value: 0n - } - ) + const transactionHash = await sendTransaction(smartClient, { + to: zeroAddress, + data: "0x", + value: 0n + }) expect(transactionHash).toBeTruthy() @@ -102,6 +94,24 @@ describe.each(getCoreSmartAccounts())( expect(receipt).toBeTruthy() expect(receipt.transactionHash).toBe(transactionHash) expect(receipt.status).toBe("success") + + const transactionHash2 = await sendTransaction(smartClient, { + to: zeroAddress, + data: "0x", + value: 0n + }) + + // -- second transaction after deployment + + expect(transactionHash2).toBeTruthy() + + const receipt2 = await publicClient.getTransactionReceipt({ + hash: transactionHash2 + }) + + expect(receipt2).toBeTruthy() + expect(receipt2.transactionHash).toBe(transactionHash2) + expect(receipt2.status).toBe("success") } ) } diff --git a/packages/permissionless/actions/smartAccount/sendTransaction.ts b/packages/permissionless/actions/smartAccount/sendTransaction.ts index 3f64cb4c..1aad613d 100644 --- a/packages/permissionless/actions/smartAccount/sendTransaction.ts +++ b/packages/permissionless/actions/smartAccount/sendTransaction.ts @@ -5,27 +5,14 @@ import type { SendTransactionParameters, Transport } from "viem" -import { getAction } from "viem/utils" -import type { SmartAccount } from "../../accounts/types" -import type { Prettify } from "../../types/" -import type { EntryPoint } from "../../types/entrypoint" -import { AccountOrClientNotFoundError, parseAccount } from "../../utils/" -import { waitForUserOperationReceipt } from "../bundler/waitForUserOperationReceipt" -import type { Middleware } from "./prepareUserOperationRequest" -import { sendUserOperation } from "./sendUserOperation" - -export type SendTransactionWithPaymasterParameters< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined, - TChainOverride extends Chain | undefined = Chain | undefined -> = SendTransactionParameters & - Middleware +import { + type SendUserOperationParameters, + type SmartAccount, + sendUserOperation, + waitForUserOperationReceipt +} from "viem/account-abstraction" +import { getAction, parseAccount } from "viem/utils" +import { AccountNotFoundError } from "../../errors" /** * Creates, signs, and sends a new transaction to the network. @@ -74,81 +61,64 @@ export type SendTransactionWithPaymasterParameters< * }) */ export async function sendTransaction< - TTransport extends Transport, - TChain extends Chain | undefined, - TAccount extends - | SmartAccount - | undefined, - entryPoint extends EntryPoint, - TChainOverride extends Chain | undefined = Chain | undefined + account extends SmartAccount | undefined, + chain extends Chain | undefined, + accountOverride extends SmartAccount | undefined = undefined, + chainOverride extends Chain | undefined = Chain | undefined, + calls extends readonly unknown[] = readonly unknown[] >( - client: Client, - args: Prettify< - SendTransactionWithPaymasterParameters< - entryPoint, - TTransport, - TChain, - TAccount, - TChainOverride - > - > + client: Client, + args: + | SendTransactionParameters + | SendUserOperationParameters ): Promise { - const { - account: account_ = client.account, - data, - maxFeePerGas, - maxPriorityFeePerGas, - to, - value, - nonce, - middleware - } = args - - if (!account_) { - throw new AccountOrClientNotFoundError({ - docsPath: "/docs/actions/wallet/sendTransaction" - }) - } + let userOpHash: Hash - const account = parseAccount(account_) as SmartAccount< - entryPoint, - string, - TTransport, - TChain - > + if ("to" in args) { + const { + account: account_ = client.account, + data, + maxFeePerGas, + maxPriorityFeePerGas, + to, + value, + nonce + } = args - if (!to) throw new Error("Missing to address") + if (!account_) { + throw new AccountNotFoundError({ + docsPath: "/docs/actions/wallet/sendTransaction" + }) + } - if (account.type !== "local") { - throw new Error("RPC account type not supported") - } + const account = parseAccount(account_) as SmartAccount - const callData = await account.encodeCallData({ - to, - value: value || BigInt(0), - data: data || "0x" - }) + if (!to) throw new Error("Missing to address") - const userOpHash = await getAction( - client, - sendUserOperation< - entryPoint, - TTransport, - TChain, - SmartAccount - >, - "sendUserOperation" - )({ - userOperation: { - sender: account.address, - maxFeePerGas: maxFeePerGas, - maxPriorityFeePerGas: maxPriorityFeePerGas, - callData: callData, + userOpHash = await getAction( + client, + sendUserOperation, + "sendUserOperation" + )({ + calls: [ + { + to, + value: value || BigInt(0), + data: data || "0x" + } + ], + account, + maxFeePerGas, + maxPriorityFeePerGas, nonce: nonce ? BigInt(nonce) : undefined - }, - account, - middleware - }) + }) + } else { + userOpHash = await getAction( + client, + sendUserOperation, + "sendUserOperation" + )({ ...args } as SendUserOperationParameters) + } const userOperationReceipt = await getAction( client, diff --git a/packages/permissionless/actions/smartAccount/sendTransactions.test.ts b/packages/permissionless/actions/smartAccount/sendTransactions.test.ts deleted file mode 100644 index ef70b4b2..00000000 --- a/packages/permissionless/actions/smartAccount/sendTransactions.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { type Chain, type Client, type Transport, zeroAddress } from "viem" -import { generatePrivateKey } from "viem/accounts" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getCoreSmartAccounts, - getPimlicoPaymasterClient, - getPublicClient -} from "../../../permissionless-test/src/utils" -import type { SmartAccount } from "../../accounts" -import type { EntryPoint } from "../../types/entrypoint" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" -import { sendTransactions } from "./sendTransactions" - -describe.each(getCoreSmartAccounts())( - "sendTransactions $name", - ({ - getSmartAccountClient, - supportsEntryPointV06, - supportsEntryPointV07 - }) => { - testWithRpc.skipIf(!supportsEntryPointV06)( - "sendTransactions_v06", - async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) - }) - - const transactionHash = await sendTransactions( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - transactions: [ - { - to: zeroAddress, - data: "0x", - value: 0n - }, - { - to: zeroAddress, - data: "0x", - value: 0n - } - ] - } - ) - - expect(transactionHash).toBeTruthy() - - const publicClient = getPublicClient(anvilRpc) - - const receipt = await publicClient.getTransactionReceipt({ - hash: transactionHash - }) - - expect(receipt).toBeTruthy() - expect(receipt.transactionHash).toBe(transactionHash) - expect(receipt.status).toBe("success") - } - ) - - testWithRpc.skipIf(!supportsEntryPointV07)( - "sendTransactions_v07", - async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) - }) - - const transactionHash = await sendTransactions( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - transactions: [ - { - to: zeroAddress, - data: "0x", - value: 0n - }, - { - to: zeroAddress, - data: "0x", - value: 0n - } - ] - } - ) - - expect(transactionHash).toBeTruthy() - - const publicClient = getPublicClient(anvilRpc) - - const receipt = await publicClient.getTransactionReceipt({ - hash: transactionHash - }) - - expect(receipt).toBeTruthy() - expect(receipt.transactionHash).toBe(transactionHash) - expect(receipt.status).toBe("success") - } - ) - } -) diff --git a/packages/permissionless/actions/smartAccount/sendTransactions.ts b/packages/permissionless/actions/smartAccount/sendTransactions.ts deleted file mode 100644 index ef03a44d..00000000 --- a/packages/permissionless/actions/smartAccount/sendTransactions.ts +++ /dev/null @@ -1,169 +0,0 @@ -import type { - Address, - Chain, - Client, - Hash, - Hex, - SendTransactionParameters, - Transport -} from "viem" -import { getAction } from "viem/utils" -import type { SmartAccount } from "../../accounts/types" -import type { GetAccountParameter, Prettify } from "../../types/" -import type { EntryPoint } from "../../types/entrypoint" -import { AccountOrClientNotFoundError, parseAccount } from "../../utils/" -import { waitForUserOperationReceipt } from "../bundler/waitForUserOperationReceipt" -import type { Middleware } from "./prepareUserOperationRequest" -import { sendUserOperation } from "./sendUserOperation" - -export type SendTransactionsWithPaymasterParameters< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined - | undefined -> = { - transactions: { to: Address; value: bigint; data: Hex }[] -} & GetAccountParameter & - Middleware & { - maxFeePerGas?: bigint - maxPriorityFeePerGas?: bigint - nonce?: bigint - } - -/** - * Creates, signs, and sends a new transactions to the network. - * This function also allows you to sponsor this transaction if sender is a smartAccount - * - * @param client - Client to use - * @param parameters - {@link SendTransactionParameters} - * @returns The [Transaction](https://viem.sh/docs/glossary/terms.html#transaction) hash. - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * import { sendTransaction } from 'viem/wallet' - * - * const client = createWalletClient({ - * chain: mainnet, - * transport: custom(window.ethereum), - * }) - * const hash = await sendTransaction(client, [{ - * account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', - * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', - * value: 1000000000000000000n, - * }, { - * to: '0x61897970c51812dc3a010c7d01b50e0d17dc1234', - * value: 10000000000000000n, - * }]) - * - * @example - * // Account Hoisting - * import { createWalletClient, http } from 'viem' - * import { privateKeyToAccount } from 'viem/accounts' - * import { mainnet } from 'viem/chains' - * import { sendTransaction } from 'viem/wallet' - * - * const client = createWalletClient({ - * account: privateKeyToAccount('0x…'), - * chain: mainnet, - * transport: http(), - * }) - * const hash = await sendTransactions(client, [{ - * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', - * value: 1000000000000000000n, - * }, { - * to: '0x61897970c51812dc3a010c7d01b50e0d17dc1234', - * value: 10000000000000000n, - * }]) - */ -export async function sendTransactions< - TTransport extends Transport, - TChain extends Chain | undefined, - TAccount extends - | SmartAccount - | undefined, - entryPoint extends EntryPoint ->( - client: Client, - args: Prettify< - SendTransactionsWithPaymasterParameters< - entryPoint, - TTransport, - TChain, - TAccount - > - > -): Promise { - const { - account: account_ = client.account, - transactions, - middleware, - maxFeePerGas, - maxPriorityFeePerGas, - nonce - } = args - - if (!account_) { - throw new AccountOrClientNotFoundError({ - docsPath: "/docs/actions/wallet/sendTransaction" - }) - } - - const account = parseAccount(account_) as SmartAccount< - entryPoint, - string, - TTransport, - TChain - > - - if (account.type !== "local") { - throw new Error("RPC account type not supported") - } - - const callData = await account.encodeCallData( - transactions.map(({ to, value, data }) => { - if (!to) throw new Error("Missing to address") - return { - to, - value: value || BigInt(0), - data: data || "0x" - } - }) - ) - - const userOpHash = await getAction( - client, - sendUserOperation< - entryPoint, - TTransport, - TChain, - SmartAccount - >, - "sendUserOperation" - )({ - userOperation: { - sender: account.address, - maxFeePerGas: maxFeePerGas, - maxPriorityFeePerGas: maxPriorityFeePerGas, - callData: callData, - nonce: nonce - }, - account: account, - middleware - }) - - const userOperationReceipt = await getAction( - client, - waitForUserOperationReceipt, - "waitForUserOperationReceipt" - )({ - hash: userOpHash - }) - - return userOperationReceipt?.receipt.transactionHash -} diff --git a/packages/permissionless/actions/smartAccount/sendUserOperation.test.ts b/packages/permissionless/actions/smartAccount/sendUserOperation.test.ts deleted file mode 100644 index a5b28858..00000000 --- a/packages/permissionless/actions/smartAccount/sendUserOperation.test.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { type Chain, type Client, type Transport, zeroAddress } from "viem" -import { generatePrivateKey } from "viem/accounts" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getBundlerClient, - getCoreSmartAccounts, - getPimlicoPaymasterClient, - getPublicClient -} from "../../../permissionless-test/src/utils" -import type { SmartAccount } from "../../accounts" -import type { EntryPoint } from "../../types/entrypoint" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" -import { sendUserOperation } from "./sendUserOperation" - -describe.each(getCoreSmartAccounts())( - "sendUserOperation $name", - ({ - getSmartAccountClient, - supportsEntryPointV06, - supportsEntryPointV07 - }) => { - testWithRpc.skipIf(!supportsEntryPointV06)( - "sendUserOperation_v06", - async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) - }) - - const userOperationHash = await sendUserOperation( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - userOperation: { - callData: await smartClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - } - } - ) - - expect(userOperationHash).toBeTruthy() - - const bundlerClient = getBundlerClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - altoRpc - }) - - const receipt = await bundlerClient.waitForUserOperationReceipt( - { - hash: userOperationHash - } - ) - - expect(receipt).toBeTruthy() - expect(receipt.userOpHash).toBe(userOperationHash) - expect(receipt.entryPoint.toLowerCase()).toBe( - ENTRYPOINT_ADDRESS_V06.toLowerCase() - ) - expect(receipt.receipt.status).toBe("success") - } - ) - - testWithRpc.skipIf(!supportsEntryPointV07)( - "sendUserOperation_v07", - async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) - }) - - const userOperationHash = await sendUserOperation( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - userOperation: { - callData: await smartClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - } - } - ) - - expect(userOperationHash).toBeTruthy() - - const bundlerClient = getBundlerClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - altoRpc - }) - - const receipt = await bundlerClient.waitForUserOperationReceipt( - { - hash: userOperationHash - } - ) - - expect(receipt).toBeTruthy() - expect(receipt.userOpHash).toBe(userOperationHash) - expect(receipt.entryPoint.toLowerCase()).toBe( - ENTRYPOINT_ADDRESS_V07.toLowerCase() - ) - expect(receipt.receipt.status).toBe("success") - } - ) - } -) diff --git a/packages/permissionless/actions/smartAccount/sendUserOperation.ts b/packages/permissionless/actions/smartAccount/sendUserOperation.ts deleted file mode 100644 index b78d19ac..00000000 --- a/packages/permissionless/actions/smartAccount/sendUserOperation.ts +++ /dev/null @@ -1,113 +0,0 @@ -import type { Chain, Client, Hash, Transport } from "viem" -import { getAction } from "viem/utils" -import type { SmartAccount } from "../../accounts/types" -import type { - ENTRYPOINT_ADDRESS_V06_TYPE, - EntryPoint, - GetEntryPointVersion -} from "../../types/entrypoint" -import type { - GetAccountParameter, - PartialBy, - Prettify, - UserOperation -} from "../../types/index" -import { AccountOrClientNotFoundError, parseAccount } from "../../utils/" -import { sendUserOperation as sendUserOperationBundler } from "../bundler/sendUserOperation" -import { - type Middleware, - type PrepareUserOperationRequestParameters, - prepareUserOperationRequest -} from "./prepareUserOperationRequest" - -export type SendUserOperationParameters< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined -> = { - userOperation: entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE - ? PartialBy< - UserOperation<"v0.6">, - | "sender" - | "nonce" - | "initCode" - | "callGasLimit" - | "verificationGasLimit" - | "preVerificationGas" - | "maxFeePerGas" - | "maxPriorityFeePerGas" - | "paymasterAndData" - | "signature" - > - : PartialBy< - UserOperation<"v0.7">, - | "sender" - | "nonce" - | "factory" - | "factoryData" - | "callGasLimit" - | "verificationGasLimit" - | "preVerificationGas" - | "maxFeePerGas" - | "maxPriorityFeePerGas" - | "paymaster" - | "paymasterVerificationGasLimit" - | "paymasterPostOpGasLimit" - | "paymasterData" - | "signature" - > -} & GetAccountParameter & - Middleware - -export async function sendUserOperation< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined ->( - client: Client, - args: Prettify< - SendUserOperationParameters - > -): Promise { - const { account: account_ = client.account } = args - if (!account_) throw new AccountOrClientNotFoundError() - - const account = parseAccount(account_) as SmartAccount< - entryPoint, - string, - TTransport, - TChain - > - - const userOperation = await getAction( - client, - prepareUserOperationRequest, - "prepareUserOperationRequest" - )({ ...args, account } as PrepareUserOperationRequestParameters< - entryPoint, - TTransport, - TChain, - TAccount - >) - - userOperation.signature = await account.signUserOperation( - userOperation as UserOperation> - ) - - return sendUserOperationBundler(client, { - userOperation: userOperation as UserOperation< - GetEntryPointVersion - >, - entryPoint: account.entryPoint - }) -} diff --git a/packages/permissionless/actions/smartAccount/signMessage.test.ts b/packages/permissionless/actions/smartAccount/signMessage.test.ts index 61e9685d..5b6f4b6a 100644 --- a/packages/permissionless/actions/smartAccount/signMessage.test.ts +++ b/packages/permissionless/actions/smartAccount/signMessage.test.ts @@ -1,15 +1,10 @@ -import type { Chain, Client, Transport } from "viem" -import { generatePrivateKey } from "viem/accounts" +import { zeroAddress } from "viem" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" import { getCoreSmartAccounts, - getPimlicoPaymasterClient, getPublicClient } from "../../../permissionless-test/src/utils" -import type { SmartAccount } from "../../accounts" -import type { EntryPoint } from "../../types/entrypoint" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" import { signMessage } from "./signMessage" describe.each(getCoreSmartAccounts())( @@ -24,31 +19,17 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(isEip1271Compliant || !supportsEntryPointV06)( "not isEip1271Compliant_v06", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) + entryPoint: { + version: "0.6" + }, + ...rpc }) await expect(async () => - signMessage( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - message: - "slowly and steadily burning the private keys" - } - ) + signMessage(smartClient, { + message: "slowly and steadily burning the private keys" + }) ).rejects.toThrow() } ) @@ -56,29 +37,18 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(!isEip1271Compliant || !supportsEntryPointV06)( "isEip1271Compliant_v06", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc + const { anvilRpc } = rpc const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) + entryPoint: { + version: "0.6" + }, + ...rpc }) - const signature = await signMessage( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - message: "slowly and steadily burning the private keys" - } - ) + const signature = await signMessage(smartClient, { + message: "slowly and steadily burning the private keys" + }) const publicClient = getPublicClient(anvilRpc) @@ -95,31 +65,17 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(isEip1271Compliant || !supportsEntryPointV07)( "not isEip1271Compliant_v07", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) + entryPoint: { + version: "0.7" + }, + ...rpc }) await expect(async () => - signMessage( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - message: - "slowly and steadily burning the private keys" - } - ) + signMessage(smartClient, { + message: "slowly and steadily burning the private keys" + }) ).rejects.toThrow() } ) @@ -127,33 +83,22 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(!isEip1271Compliant || !supportsEntryPointV07)( "isEip1271Compliant_v07", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc + const { anvilRpc } = rpc const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) + entryPoint: { + version: "0.7" + }, + ...rpc }) - const signature = await signMessage( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - { - message: "slowly and steadily burning the private keys" - } - ) + const signature = await signMessage(smartClient, { + message: "slowly and steadily burning the private keys" + }) const publicClient = getPublicClient(anvilRpc) - if (name === "Safe 7579") { + if (name === "Safe 7579" || name === "LightAccount 2.0.0") { // Due to 7579 launchpad, we can't verify the signature as of now. // Awaiting for the fix return diff --git a/packages/permissionless/actions/smartAccount/signMessage.ts b/packages/permissionless/actions/smartAccount/signMessage.ts index 8c0deebc..2d816446 100644 --- a/packages/permissionless/actions/smartAccount/signMessage.ts +++ b/packages/permissionless/actions/smartAccount/signMessage.ts @@ -5,9 +5,9 @@ import type { SignMessageReturnType, Transport } from "viem" -import type { SmartAccount } from "../../accounts/types" -import type { EntryPoint } from "../../types/entrypoint" -import { AccountOrClientNotFoundError, parseAccount } from "../../utils/" +import type { SmartAccount } from "viem/account-abstraction" +import { parseAccount } from "viem/utils" +import { AccountNotFoundError } from "../../errors" /** * Calculates an Ethereum-specific signature in [EIP-191 format](https://eips.ethereum.org/EIPS/eip-191): `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))`. @@ -55,29 +55,19 @@ import { AccountOrClientNotFoundError, parseAccount } from "../../utils/" * message: 'hello world', * }) */ -export async function signMessage< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined ->( - client: Client, +export async function signMessage( + client: Client, { account: account_ = client.account, message }: SignMessageParameters ): Promise { if (!account_) - throw new AccountOrClientNotFoundError({ + throw new AccountNotFoundError({ docsPath: "/docs/actions/wallet/signMessage" }) - const account = parseAccount(account_) - if (account.type === "local") return account.signMessage({ message }) + const account = parseAccount(account_) as SmartAccount - throw new Error("Sign message is not supported by this account") + return account.signMessage({ message }) } diff --git a/packages/permissionless/actions/smartAccount/signTypedData.test.ts b/packages/permissionless/actions/smartAccount/signTypedData.test.ts index 7c3bc1f5..41766c5f 100644 --- a/packages/permissionless/actions/smartAccount/signTypedData.test.ts +++ b/packages/permissionless/actions/smartAccount/signTypedData.test.ts @@ -1,15 +1,10 @@ -import { type Chain, type Client, type Transport, getAddress } from "viem" -import { generatePrivateKey } from "viem/accounts" +import { getAddress } from "viem" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" import { getCoreSmartAccounts, - getPimlicoPaymasterClient, getPublicClient } from "../../../permissionless-test/src/utils" -import type { SmartAccount } from "../../accounts" -import type { EntryPoint } from "../../types/entrypoint" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" import { signTypedData } from "./signTypedData" const typedData = { @@ -58,28 +53,15 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(isEip1271Compliant || !supportsEntryPointV06)( "not isEip1271Compliant_v06", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) + entryPoint: { + version: "0.6" + }, + ...rpc }) await expect(async () => - signTypedData( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - typedData - ) + signTypedData(smartClient, typedData) ).rejects.toThrow() } ) @@ -87,27 +69,16 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(!isEip1271Compliant || !supportsEntryPointV06)( "isEip1271Compliant_v06", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc + const { anvilRpc } = rpc const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) + entryPoint: { + version: "0.6" + }, + ...rpc }) - const signature = await signTypedData( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - typedData - ) + const signature = await signTypedData(smartClient, typedData) const publicClient = getPublicClient(anvilRpc) @@ -124,28 +95,15 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(isEip1271Compliant || !supportsEntryPointV07)( "not isEip1271Compliant_v07", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) + entryPoint: { + version: "0.7" + }, + ...rpc }) await expect(async () => - signTypedData( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - typedData - ) + signTypedData(smartClient, typedData) ).rejects.toThrow() } ) @@ -153,31 +111,20 @@ describe.each(getCoreSmartAccounts())( testWithRpc.skipIf(!isEip1271Compliant || !supportsEntryPointV07)( "isEip1271Compliant_v07", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc + const { anvilRpc } = rpc const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) + entryPoint: { + version: "0.7" + }, + ...rpc }) - const signature = await signTypedData( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - typedData - ) + const signature = await signTypedData(smartClient, typedData) const publicClient = getPublicClient(anvilRpc) - if (name === "Safe 7579") { + if (name === "Safe 7579" || name === "LightAccount 2.0.0") { // Due to 7579 launchpad, we can't verify the signature as of now. // Awaiting for the fix return diff --git a/packages/permissionless/actions/smartAccount/signTypedData.ts b/packages/permissionless/actions/smartAccount/signTypedData.ts index 438f3b39..398f7654 100644 --- a/packages/permissionless/actions/smartAccount/signTypedData.ts +++ b/packages/permissionless/actions/smartAccount/signTypedData.ts @@ -10,9 +10,9 @@ import { getTypesForEIP712Domain, validateTypedData } from "viem" -import type { SmartAccount } from "../../accounts/types" -import type { EntryPoint } from "../../types/entrypoint" -import { AccountOrClientNotFoundError, parseAccount } from "../../utils/" +import type { SmartAccount } from "viem/account-abstraction" +import { parseAccount } from "viem/utils" +import { AccountNotFoundError } from "../../errors" /** * Signs typed data and calculates an Ethereum-specific signature in [https://eips.ethereum.org/EIPS/eip-712](https://eips.ethereum.org/EIPS/eip-712): `sign(keccak256("\x19\x01" ‖ domainSeparator ‖ hashStruct(message)))` @@ -113,18 +113,11 @@ import { AccountOrClientNotFoundError, parseAccount } from "../../utils/" * }) */ export async function signTypedData< - entryPoint extends EntryPoint, const TTypedData extends TypedData | { [key: string]: unknown }, TPrimaryType extends string, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined + TAccount extends SmartAccount | undefined = SmartAccount | undefined >( - client: Client, + client: Client, { account: account_ = client.account, domain, @@ -134,12 +127,12 @@ export async function signTypedData< }: SignTypedDataParameters ): Promise { if (!account_) { - throw new AccountOrClientNotFoundError({ + throw new AccountNotFoundError({ docsPath: "/docs/actions/wallet/signMessage" }) } - const account = parseAccount(account_) + const account = parseAccount(account_) as SmartAccount const types = { EIP712Domain: getTypesForEIP712Domain({ domain } as { @@ -147,6 +140,7 @@ export async function signTypedData< }), ...(types_ as TTypedData) } + validateTypedData({ domain, message, @@ -154,14 +148,10 @@ export async function signTypedData< types } as TypedDataDefinition) - if (account.type === "local") { - return account.signTypedData({ - domain, - primaryType, - types, - message - } as TypedDataDefinition) - } - - throw new Error("Sign type message is not supported by this account") + return account.signTypedData({ + domain, + primaryType, + types, + message + } as TypedDataDefinition) } diff --git a/packages/permissionless/actions/smartAccount/writeContract.test.ts b/packages/permissionless/actions/smartAccount/writeContract.test.ts deleted file mode 100644 index 7c3bc1f5..00000000 --- a/packages/permissionless/actions/smartAccount/writeContract.test.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { type Chain, type Client, type Transport, getAddress } from "viem" -import { generatePrivateKey } from "viem/accounts" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { - getCoreSmartAccounts, - getPimlicoPaymasterClient, - getPublicClient -} from "../../../permissionless-test/src/utils" -import type { SmartAccount } from "../../accounts" -import type { EntryPoint } from "../../types/entrypoint" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" -import { signTypedData } from "./signTypedData" - -const typedData = { - domain: { - name: "Ether Mail", - version: "1", - chainId: 1, - verifyingContract: getAddress( - "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - ) - }, - types: { - Person: [ - { name: "name", type: "string" }, - { name: "wallet", type: "address" } - ], - Mail: [ - { name: "from", type: "Person" }, - { name: "to", type: "Person" }, - { name: "contents", type: "string" } - ] - }, - primaryType: "Mail" as const, - message: { - from: { - name: "Cow", - wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - to: { - name: "Bob", - wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - contents: "Hello, Bob!" - } -} - -describe.each(getCoreSmartAccounts())( - "signTypedData $name", - ({ - getSmartAccountClient, - isEip1271Compliant, - supportsEntryPointV06, - supportsEntryPointV07, - name - }) => { - testWithRpc.skipIf(isEip1271Compliant || !supportsEntryPointV06)( - "not isEip1271Compliant_v06", - async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) - }) - - await expect(async () => - signTypedData( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - typedData - ) - ).rejects.toThrow() - } - ) - - testWithRpc.skipIf(!isEip1271Compliant || !supportsEntryPointV06)( - "isEip1271Compliant_v06", - async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }) - }) - - const signature = await signTypedData( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - typedData - ) - - const publicClient = getPublicClient(anvilRpc) - - const isVerified = await publicClient.verifyTypedData({ - ...typedData, - address: smartClient.account.address, - signature - }) - - expect(isVerified).toBeTruthy() - } - ) - - testWithRpc.skipIf(isEip1271Compliant || !supportsEntryPointV07)( - "not isEip1271Compliant_v07", - async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) - }) - - await expect(async () => - signTypedData( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - typedData - ) - ).rejects.toThrow() - } - ) - - testWithRpc.skipIf(!isEip1271Compliant || !supportsEntryPointV07)( - "isEip1271Compliant_v07", - async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const smartClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc, - paymasterClient: getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }) - }) - - const signature = await signTypedData( - smartClient as Client< - Transport, - Chain, - SmartAccount - >, - typedData - ) - - const publicClient = getPublicClient(anvilRpc) - - if (name === "Safe 7579") { - // Due to 7579 launchpad, we can't verify the signature as of now. - // Awaiting for the fix - return - } - - const isVerified = await publicClient.verifyTypedData({ - ...typedData, - address: smartClient.account.address, - signature - }) - - expect(isVerified).toBeTruthy() - } - ) - } -) diff --git a/packages/permissionless/actions/smartAccount/writeContract.ts b/packages/permissionless/actions/smartAccount/writeContract.ts index 67a231aa..ca6149bb 100644 --- a/packages/permissionless/actions/smartAccount/writeContract.ts +++ b/packages/permissionless/actions/smartAccount/writeContract.ts @@ -6,107 +6,18 @@ import { type ContractFunctionName, type EncodeFunctionDataParameters, type Hash, + type SendTransactionParameters, type Transport, type WriteContractParameters, encodeFunctionData } from "viem" +import type { SmartAccount } from "viem/account-abstraction" import { getAction } from "viem/utils" -import type { SmartAccount } from "../../accounts/types" -import type { EntryPoint } from "../../types/entrypoint" -import type { Middleware } from "./prepareUserOperationRequest" -import { - type SendTransactionWithPaymasterParameters, - sendTransaction -} from "./sendTransaction" - -/** - * Executes a write function on a contract. - * This function also allows you to sponsor this transaction if sender is a smartAccount - * - * - Docs: https://viem.sh/docs/contract/writeContract.html - * - Examples: https://stackblitz.com/github/wagmi-dev/viem/tree/main/examples/contracts/writing-to-contracts - * - * A "write" function on a Solidity contract modifies the state of the blockchain. These types of functions require gas to be executed, and hence a [Transaction](https://viem.sh/docs/glossary/terms.html) is needed to be broadcast in order to change the state. - * - * Internally, uses a [Wallet Client](https://viem.sh/docs/clients/wallet.html) to call the [`sendTransaction` action](https://viem.sh/docs/actions/wallet/sendTransaction.html) with [ABI-encoded `data`](https://viem.sh/docs/contract/encodeFunctionData.html). - * - * __Warning: The `write` internally sends a transaction – it does not validate if the contract write will succeed (the contract may throw an error). It is highly recommended to [simulate the contract write with `contract.simulate`](https://viem.sh/docs/contract/writeContract.html#usage) before you execute it.__ - * - * @param client - Client to use - * @param parameters - {@link WriteContractParameters} - * @returns A [Transaction Hash](https://viem.sh/docs/glossary/terms.html#hash). - * - * @example - * import { createWalletClient, custom, parseAbi } from 'viem' - * import { mainnet } from 'viem/chains' - * import { writeContract } from 'viem/contract' - * - * const client = createWalletClient({ - * chain: mainnet, - * transport: custom(window.ethereum), - * }) - * const hash = await writeContract(client, { - * address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', - * abi: parseAbi(['function mint(uint32 tokenId) nonpayable']), - * functionName: 'mint', - * args: [69420], - * }) - * - * @example - * // With Validation - * import { createWalletClient, http, parseAbi } from 'viem' - * import { mainnet } from 'viem/chains' - * import { simulateContract, writeContract } from 'viem/contract' - * - * const client = createWalletClient({ - * chain: mainnet, - * transport: http(), - * }) - * const { request } = await simulateContract(client, { - * address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', - * abi: parseAbi(['function mint(uint32 tokenId) nonpayable']), - * functionName: 'mint', - * args: [69420], - * } - * const hash = await writeContract(client, request) - */ -export type WriteContractWithPaymasterParameters< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined, - TAbi extends Abi | readonly unknown[] = Abi | readonly unknown[], - TFunctionName extends ContractFunctionName< - TAbi, - "nonpayable" | "payable" - > = ContractFunctionName, - TArgs extends ContractFunctionArgs< - TAbi, - "nonpayable" | "payable", - TFunctionName - > = ContractFunctionArgs, - TChainOverride extends Chain | undefined = undefined -> = WriteContractParameters< - TAbi, - TFunctionName, - TArgs, - TChain, - TAccount, - TChainOverride -> & - Middleware +import { sendTransaction } from "./sendTransaction" export async function writeContract< - entryPoint extends EntryPoint, - TTransport extends Transport, TChain extends Chain | undefined, - TAccount extends - | SmartAccount - | undefined, + TAccount extends SmartAccount | undefined, const TAbi extends Abi | readonly unknown[], TFunctionName extends ContractFunctionName< TAbi, @@ -127,14 +38,12 @@ export async function writeContract< dataSuffix, functionName, ...request - }: WriteContractWithPaymasterParameters< - entryPoint, - TTransport, - TChain, - TAccount, + }: WriteContractParameters< TAbi, TFunctionName, TArgs, + TChain, + TAccount, TChainOverride > ): Promise { @@ -143,26 +52,19 @@ export async function writeContract< args, functionName } as EncodeFunctionDataParameters) + const hash = await getAction( client, - sendTransaction< - TTransport, - TChain, - TAccount, - entryPoint, - TChainOverride - >, + sendTransaction, "sendTransaction" )({ data: `${data}${dataSuffix ? dataSuffix.replace("0x", "") : ""}`, to: address, ...request - } as unknown as SendTransactionWithPaymasterParameters< - entryPoint, - TTransport, - TChain, + } as unknown as SendTransactionParameters< + Chain | undefined, TAccount, - TChainOverride + undefined >) return hash } diff --git a/packages/permissionless/actions/stackup.ts b/packages/permissionless/actions/stackup.ts deleted file mode 100644 index e3d866b8..00000000 --- a/packages/permissionless/actions/stackup.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { StackupPaymasterClientActions } from "../clients/decorators/stackup" -import { stackupPaymasterActions } from "../clients/decorators/stackup" -import { type AccountsParameters, accounts } from "./stackup/accounts" -import { - type SponsorUserOperationParameters, - type SponsorUserOperationReturnType, - sponsorUserOperation -} from "./stackup/sponsorUserOperation" - -export type { - SponsorUserOperationParameters, - SponsorUserOperationReturnType, - AccountsParameters, - StackupPaymasterClientActions -} - -export { sponsorUserOperation, accounts, stackupPaymasterActions } diff --git a/packages/permissionless/actions/stackup/accounts.ts b/packages/permissionless/actions/stackup/accounts.ts deleted file mode 100644 index b1d11a61..00000000 --- a/packages/permissionless/actions/stackup/accounts.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { Address } from "viem" -import type { StackupPaymasterClient } from "../../clients/stackup" -import type { EntryPoint } from "../../types" - -export type AccountsParameters = { - entryPoint: entryPoint -} - -/** - * Returns all the Paymaster addresses associated with an EntryPoint that’s owned by this service. - * - * https://docs.stackup.sh/docs/paymaster-api-rpc-methods#pm_accounts - * - * @param args {@link AccountsParameters} entryPoint for which you want to get list of supported paymasters. - * @returns paymaster addresses - * - * @example - * import { createClient } from "viem" - * import { accounts } from "permissionless/actions/stackup" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http("https://api.stackup.sh/v2/paymaster/YOUR_API_KEY_HERE") - * }) - * - * await accounts(bundlerClient, { - * entryPoint: entryPoint - * }}) - * - */ -export const accounts = async ( - client: StackupPaymasterClient, - { entryPoint: entryPointAddress }: AccountsParameters -): Promise => { - const response = await client.request({ - method: "pm_accounts", - params: [entryPointAddress] - }) - - return response -} diff --git a/packages/permissionless/actions/stackup/sponsorUserOperation.ts b/packages/permissionless/actions/stackup/sponsorUserOperation.ts deleted file mode 100644 index 3b376c91..00000000 --- a/packages/permissionless/actions/stackup/sponsorUserOperation.ts +++ /dev/null @@ -1,126 +0,0 @@ -import type { Address, Hex } from "viem" -import type { PartialBy } from "viem/types/utils" -import type { StackupPaymasterClient } from "../../clients/stackup" -import type { - ENTRYPOINT_ADDRESS_V06_TYPE, - EntryPoint -} from "../../types/entrypoint" -import type { StackupPaymasterContext } from "../../types/stackup" -import type { UserOperation } from "../../types/userOperation" -import { deepHexlify } from "../../utils/deepHexlify" -import { ENTRYPOINT_ADDRESS_V06 } from "../../utils/getEntryPointVersion" - -export type SponsorUserOperationParameters = { - userOperation: entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE - ? PartialBy< - UserOperation<"v0.6">, - "callGasLimit" | "preVerificationGas" | "verificationGasLimit" - > - : PartialBy< - UserOperation<"v0.7">, - | "callGasLimit" - | "preVerificationGas" - | "verificationGasLimit" - | "paymasterVerificationGasLimit" - | "paymasterPostOpGasLimit" - > - entryPoint: entryPoint - context: StackupPaymasterContext -} - -export type SponsorUserOperationReturnType = - entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE - ? Pick< - UserOperation<"v0.6">, - | "callGasLimit" - | "verificationGasLimit" - | "preVerificationGas" - | "paymasterAndData" - > - : Pick< - UserOperation<"v0.7">, - | "callGasLimit" - | "verificationGasLimit" - | "preVerificationGas" - | "paymaster" - | "paymasterVerificationGasLimit" - | "paymasterPostOpGasLimit" - | "paymasterData" - > - -/** - * Returns paymasterAndData & updated gas parameters required to sponsor a userOperation. - * - * - Docs: https://docs.pimlico.io/permissionless/reference/stackup-paymaster-actions/sponsorUserOperation - * - * @param client {@link PimlicoBundlerClient} that you created using viem's createClient whose transport url is pointing to the Pimlico's bundler. - * @param args {@link sponsorUserOperationParameters} UserOperation you want to sponsor & entryPoint. - * @returns paymasterAndData & updated gas parameters, see {@link SponsorUserOperationReturnType} - * - * - * @example - * import { createClient } from "viem" - * import { sponsorUserOperation } from "permissionless/actions/stackup" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http("https://api.stackup.sh/v2/paymaster/YOUR_API_KEY_HERE") - * }) - * - * await sponsorUserOperation(bundlerClient, { - * userOperation: userOperationWithDummySignature, - * entryPoint: entryPoint - * }}) - * - */ -export const sponsorUserOperation = async ( - client: StackupPaymasterClient, - args: SponsorUserOperationParameters -): Promise> => { - const response = await client.request({ - method: "pm_sponsorUserOperation", - params: [deepHexlify(args.userOperation), args.entryPoint, args.context] - }) - - if (args.entryPoint === ENTRYPOINT_ADDRESS_V06) { - const responseV06 = response as { - paymasterAndData: Hex - preVerificationGas: Hex - verificationGasLimit: Hex - callGasLimit: Hex - paymaster?: never - paymasterVerificationGasLimit?: never - paymasterPostOpGasLimit?: never - paymasterData?: never - } - return { - paymasterAndData: responseV06.paymasterAndData, - preVerificationGas: BigInt(responseV06.preVerificationGas), - verificationGasLimit: BigInt(responseV06.verificationGasLimit), - callGasLimit: BigInt(responseV06.callGasLimit) - } as SponsorUserOperationReturnType - } - - const responseV07 = response as { - preVerificationGas: Hex - verificationGasLimit: Hex - callGasLimit: Hex - paymaster: Address - paymasterVerificationGasLimit: Hex - paymasterPostOpGasLimit: Hex - paymasterData: Hex - paymasterAndData?: never - } - - return { - callGasLimit: BigInt(responseV07.callGasLimit), - verificationGasLimit: BigInt(responseV07.verificationGasLimit), - preVerificationGas: BigInt(responseV07.preVerificationGas), - paymaster: responseV07.paymaster, - paymasterVerificationGasLimit: BigInt( - responseV07.paymasterVerificationGasLimit - ), - paymasterPostOpGasLimit: BigInt(responseV07.paymasterPostOpGasLimit), - paymasterData: responseV07.paymasterData - } as SponsorUserOperationReturnType -} diff --git a/packages/permissionless/clients/createBundlerClient.test.ts b/packages/permissionless/clients/createBundlerClient.test.ts deleted file mode 100644 index 42cf80ea..00000000 --- a/packages/permissionless/clients/createBundlerClient.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { http } from "viem" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../permissionless-test/src/testWithRpc" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../utils" -import { createBundlerClient } from "./createBundlerClient" - -describe("createBundlerClient", () => { - testWithRpc("createBundlerClient_V06", async ({ rpc }) => { - const bundlerClientV06 = createBundlerClient({ - transport: http(rpc.altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V06 - }) - - const entryPoints = await bundlerClientV06.supportedEntryPoints() - expect(entryPoints).contain(ENTRYPOINT_ADDRESS_V06) - }) - - testWithRpc("createBundlerClient_V07", async ({ rpc }) => { - const bundlerClientV07 = createBundlerClient({ - transport: http(rpc.altoRpc), - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - const entryPoints = await bundlerClientV07.supportedEntryPoints() - expect(entryPoints).contain(ENTRYPOINT_ADDRESS_V07) - }) -}) diff --git a/packages/permissionless/clients/createBundlerClient.ts b/packages/permissionless/clients/createBundlerClient.ts deleted file mode 100644 index 4ea177dd..00000000 --- a/packages/permissionless/clients/createBundlerClient.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { - Account, - Chain, - Client, - PublicClientConfig, - Transport -} from "viem" -import { createClient } from "viem" -import type { BundlerRpcSchema } from "../types/bundler" -import type { EntryPoint } from "../types/entrypoint" -import { type BundlerActions, bundlerActions } from "./decorators/bundler" - -export type BundlerClient< - entryPoint extends EntryPoint, - TChain extends Chain | undefined = Chain | undefined -> = Client< - Transport, - TChain, - Account | undefined, - BundlerRpcSchema, - BundlerActions -> -/** - * Creates a EIP-4337 compliant Bundler Client with a given [Transport](https://viem.sh/docs/clients/intro.html) configured for a [Chain](https://viem.sh/docs/clients/chains.html). - * - * - Docs: https://docs.pimlico.io/permissionless/reference/clients/bundlerClient - * - * A Bundler Client is an interface to "erc 4337" [JSON-RPC API](https://eips.ethereum.org/EIPS/eip-4337#rpc-methods-eth-namespace) methods such as sending user operation, estimating gas for a user operation, get user operation receipt, etc through Bundler Actions. - * - * @param config - {@link PublicClientConfig} - * @returns A Bundler Client. {@link BundlerClient} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * - * const bundlerClient = createBundlerClient({ - * chain: mainnet, - * transport: http(BUNDLER_URL), - * }) - */ -export const createBundlerClient = < - entryPoint extends EntryPoint, - transport extends Transport = Transport, - chain extends Chain | undefined = undefined ->( - parameters: PublicClientConfig & { - entryPoint: entryPoint - } -): BundlerClient => { - const { key = "public", name = "Bundler Client" } = parameters - const client = createClient({ - ...parameters, - key, - name, - type: "bundlerClient" - }) - return client.extend(bundlerActions(parameters.entryPoint)) -} diff --git a/packages/permissionless/clients/createSmartAccountClient.test.ts b/packages/permissionless/clients/createSmartAccountClient.test.ts deleted file mode 100644 index 194b93f9..00000000 --- a/packages/permissionless/clients/createSmartAccountClient.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { http } from "viem" -import { generatePrivateKey, privateKeyToAccount } from "viem/accounts" -import { foundry } from "viem/chains" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../permissionless-test/src/testWithRpc" -import { getPublicClient } from "../../permissionless-test/src/utils" -import { signerToSimpleSmartAccount } from "../accounts" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../utils" -import { createSmartAccountClient } from "./createSmartAccountClient" - -describe("createSmartAccountClient", () => { - testWithRpc("createSmartAccountClient_V06", async ({ rpc }) => { - const publicClient = getPublicClient(rpc.anvilRpc) - - const simpleSmartAccount = await signerToSimpleSmartAccount( - publicClient, - { - entryPoint: ENTRYPOINT_ADDRESS_V06, - signer: privateKeyToAccount(generatePrivateKey()) - } - ) - - const smartAccountClient = createSmartAccountClient({ - chain: foundry, - account: simpleSmartAccount, - bundlerTransport: http(rpc.altoRpc) - }) - - expect(smartAccountClient.account.address).toBe( - simpleSmartAccount.address - ) - }) - - testWithRpc("createSmartAccountClient_V07", async ({ rpc }) => { - const publicClient = getPublicClient(rpc.anvilRpc) - - const simpleSmartAccount = await signerToSimpleSmartAccount( - publicClient, - { - entryPoint: ENTRYPOINT_ADDRESS_V07, - signer: privateKeyToAccount(generatePrivateKey()) - } - ) - - const smartAccountClient = createSmartAccountClient({ - chain: foundry, - account: simpleSmartAccount, - bundlerTransport: http(rpc.altoRpc) - }) - - expect(smartAccountClient.account.address).toBe( - simpleSmartAccount.address - ) - }) -}) diff --git a/packages/permissionless/clients/createSmartAccountClient.ts b/packages/permissionless/clients/createSmartAccountClient.ts index 433c2135..616ccaf7 100644 --- a/packages/permissionless/clients/createSmartAccountClient.ts +++ b/packages/permissionless/clients/createSmartAccountClient.ts @@ -1,16 +1,21 @@ import type { + BundlerRpcSchema, Chain, Client, ClientConfig, - Transport, - WalletClientConfig + EstimateFeesPerGasReturnType, + Prettify, + RpcSchema, + Transport } from "viem" -import { createClient } from "viem" -import type { SmartAccount } from "../accounts/types" -import type { Middleware } from "../actions/smartAccount/prepareUserOperationRequest" -import type { Prettify } from "../types/" -import type { BundlerRpcSchema } from "../types/bundler" -import type { EntryPoint } from "../types/entrypoint" +import { + type BundlerActions, + type BundlerClientConfig, + type PaymasterActions, + type SmartAccount, + type UserOperationRequest, + createBundlerClient +} from "viem/account-abstraction" import { type SmartAccountActions, smartAccountActions @@ -22,100 +27,123 @@ import { * - Fix typing, 'accounts' is required to signMessage, signTypedData, signTransaction, but not needed here, since account is embedded in the client */ export type SmartAccountClient< - entryPoint extends EntryPoint, transport extends Transport = Transport, chain extends Chain | undefined = Chain | undefined, - account extends - | SmartAccount - | undefined = - | SmartAccount - | undefined + account extends SmartAccount | undefined = SmartAccount | undefined, + client extends Client | undefined = Client | undefined, + rpcSchema extends RpcSchema | undefined = undefined > = Prettify< Client< transport, - chain, + chain extends Chain + ? chain + : client extends Client + ? chain + : undefined, account, - BundlerRpcSchema, - SmartAccountActions + rpcSchema extends RpcSchema + ? [...BundlerRpcSchema, ...rpcSchema] + : BundlerRpcSchema, + BundlerActions & SmartAccountActions > -> +> & { + client: client + paymaster: BundlerClientConfig["paymaster"] | undefined + paymasterContext: BundlerClientConfig["paymasterContext"] | undefined + userOperation: BundlerClientConfig["userOperation"] | undefined +} export type SmartAccountClientConfig< - entryPoint extends EntryPoint, transport extends Transport = Transport, chain extends Chain | undefined = Chain | undefined, - account extends - | SmartAccount - | undefined = - | SmartAccount - | undefined - | undefined + account extends SmartAccount | undefined = SmartAccount | undefined, + client extends Client | undefined = Client | undefined, + rpcSchema extends RpcSchema | undefined = undefined > = Prettify< Pick< - ClientConfig, - "cacheTime" | "chain" | "key" | "name" | "pollingInterval" - > & - Middleware & { - account: account - bundlerTransport: Transport - } & { - entryPoint?: entryPoint - } -> - -/** - * Creates a EIP-4337 compliant Bundler Client with a given [Transport](https://viem.sh/docs/clients/intro.html) configured for a [Chain](https://viem.sh/docs/clients/chains.html). - * - * - Docs: https://docs.pimlico.io/permissionless/reference/clients/smartAccountClient - * - * A Bundler Client is an interface to "erc 4337" [JSON-RPC API](https://eips.ethereum.org/EIPS/eip-4337#rpc-methods-eth-namespace) methods such as sending user operation, estimating gas for a user operation, get user operation receipt, etc through Bundler Actions. - * - * @param parameters - {@link WalletClientConfig} - * @returns A Bundler Client. {@link SmartAccountClient} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * - * const smartAccountClient = createSmartAccountClient({ - * chain: mainnet, - * transport: http(BUNDLER_URL), - * }) - */ + ClientConfig, + | "account" + | "cacheTime" + | "chain" + | "key" + | "name" + | "pollingInterval" + | "rpcSchema" + > +> & { + bundlerTransport: transport + /** Client that points to an Execution RPC URL. */ + client?: client | Client | undefined + /** Paymaster configuration. */ + paymaster?: + | true + | { + /** Retrieves paymaster-related User Operation properties to be used for sending the User Operation. */ + getPaymasterData?: + | PaymasterActions["getPaymasterData"] + | undefined + /** Retrieves paymaster-related User Operation properties to be used for gas estimation. */ + getPaymasterStubData?: + | PaymasterActions["getPaymasterStubData"] + | undefined + } + | undefined + /** Paymaster context to pass to `getPaymasterData` and `getPaymasterStubData` calls. */ + paymasterContext?: unknown + /** User Operation configuration. */ + userOperation?: + | { + /** Prepares fee properties for the User Operation request. */ + estimateFeesPerGas?: + | ((parameters: { + account: account | SmartAccount + bundlerClient: Client + userOperation: UserOperationRequest + }) => Promise>) + | undefined + } + | undefined +} export function createSmartAccountClient< - TTransport extends Transport, - TChain extends Chain | undefined, - TEntryPoint extends EntryPoint, - TSmartAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined + transport extends Transport, + chain extends Chain | undefined = undefined, + account extends SmartAccount | undefined = undefined, + client extends Client | undefined = undefined, + rpcSchema extends RpcSchema | undefined = undefined >( parameters: SmartAccountClientConfig< - TEntryPoint, - TTransport, - TChain, - TSmartAccount + transport, + chain, + account, + client, + rpcSchema > -): SmartAccountClient { +): SmartAccountClient + +export function createSmartAccountClient( + parameters: SmartAccountClientConfig +): SmartAccountClient { const { - key = "Account", - name = "Smart Account Client", - bundlerTransport + client: client_, + key = "bundler", + name = "Bundler Client", + paymaster, + paymasterContext, + bundlerTransport, + userOperation } = parameters - const client = createClient({ + + const client = createBundlerClient({ ...parameters, + chain: parameters.chain ?? client_?.chain, key, name, transport: bundlerTransport, - type: "smartAccountClient" + paymaster, + paymasterContext, + userOperation }) - return client.extend( - smartAccountActions({ - middleware: parameters.middleware - }) - ) as SmartAccountClient + return client.extend(smartAccountActions()) as unknown as SmartAccountClient } diff --git a/packages/permissionless/clients/decorators/bundler.ts b/packages/permissionless/clients/decorators/bundler.ts deleted file mode 100644 index bd91cef8..00000000 --- a/packages/permissionless/clients/decorators/bundler.ts +++ /dev/null @@ -1,253 +0,0 @@ -import type { Client, Hash } from "viem" -import { chainId } from "../../actions/bundler/chainId" -import { - type EstimateUserOperationGasParameters, - type EstimateUserOperationGasReturnType, - estimateUserOperationGas -} from "../../actions/bundler/estimateUserOperationGas" -import { - type GetUserOperationByHashParameters, - type GetUserOperationByHashReturnType, - getUserOperationByHash -} from "../../actions/bundler/getUserOperationByHash" -import { - type GetUserOperationReceiptParameters, - type GetUserOperationReceiptReturnType, - getUserOperationReceipt -} from "../../actions/bundler/getUserOperationReceipt" -import { - type SendUserOperationParameters, - sendUserOperation -} from "../../actions/bundler/sendUserOperation" -import { supportedEntryPoints } from "../../actions/bundler/supportedEntryPoints" -import { - type WaitForUserOperationReceiptParameters, - waitForUserOperationReceipt -} from "../../actions/bundler/waitForUserOperationReceipt" -import type { Prettify } from "../../types/" -import type { StateOverrides } from "../../types/bundler" -import type { EntryPoint } from "../../types/entrypoint" -import type { BundlerClient } from "../createBundlerClient" - -export type BundlerActions = { - /** - * - * Sends user operation to the bundler - * - * - Docs: https://docs.pimlico.io/permissionless/reference/bundler-actions/sendUserOperation - * - * @param args {@link SendUserOperationParameters}. - * @returns UserOpHash that you can use to track user operation as {@link Hash}. - * - * @example - * import { createClient } from "viem" - * import { bundlerActions } from "permissionless" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http("https://api.pimlico.io/v2/goerli/rpc?apikey=YOUR_API_KEY_HERE") - * }).extend(bundlerActions) - * - * const userOpHash = await bundlerClient.sendUserOperation({ - * userOperation: signedUserOperation, - * entryPoint: entryPoint - * }) - * - * // Return '0xe9fad2cd67f9ca1d0b7a6513b2a42066784c8df938518da2b51bb8cc9a89ea34' - */ - sendUserOperation: ( - args: Prettify< - Omit, "entryPoint"> - > - ) => Promise - /** - * - * Estimates preVerificationGas, verificationGasLimit and callGasLimit for user operation - * - * - Docs: https://docs.pimlico.io/permissionless/reference/bundler-actions/estimateUserOperationGas - * - * @param args {@link EstimateUserOperationGasParameters} - * @returns preVerificationGas, verificationGasLimit and callGasLimit as {@link EstimateUserOperationGasReturnType} - * - * @example - * import { createClient } from "viem" - * import { bundlerActions } from "permissionless" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http(BUNDLER_URL) - * }).extend(bundlerActions) - * - * const gasParameters = await bundlerClient.estimateUserOperationGas({ - * userOperation: signedUserOperation, - * entryPoint: entryPoint - * }) - * - * // Return {preVerificationGas: 43492n, verificationGasLimit: 59436n, callGasLimit: 9000n} - */ - estimateUserOperationGas: ( - args: Prettify< - Omit, "entryPoint"> - >, - stateOverrides?: StateOverrides - ) => Promise>> - /** - * - * Returns the supported entrypoints by the bundler service - * - * - Docs: https://docs.pimlico.io/permissionless/reference/bundler-actions/supportedEntryPoints - * - * @returns Supported entryPoints - * - * @example - * import { createClient } from "viem" - * import { bundlerActions } from "permissionless" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http(BUNDLER_URL) - * }).extend(bundlerActions) - * - * const supportedEntryPoints = await bundlerClient.supportedEntryPoints() - * - * // Return ['0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789'] - */ - supportedEntryPoints: () => Promise - /** - * - * Returns the supported chain id by the bundler service - * - * - Docs: https://docs.pimlico.io/permissionless/reference/bundler-actions/chainId - * - * @returns Supported chain id - * - * @example - * import { createClient } from "viem" - * import { bundlerActions } from "permissionless" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http(BUNDLER_URL) - * }).extend(bundlerActions) - * - * const chainId = await bundlerClient.chainId() - * // Return 5n for Goerli - */ - chainId: () => Promise - /** - * - * Returns the user operation from userOpHash - * - * - Docs: https://docs.pimlico.io/permissionless/reference/bundler-actions/getUserOperationByHash - * - * @param args {@link GetUserOperationByHash} UserOpHash that was returned by {@link sendUserOperation} - * @returns userOperation along with entryPoint, transactionHash, blockHash, blockNumber if found or null - * - * @example - * import { createClient } from "viem" - * import { bundlerActions } from "permissionless" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http(BUNDLER_URL) - * }).extend(bundlerActions) - * - * await bundlerClient.getUserOperationByHash(userOpHash) - * - */ - getUserOperationByHash: ( - args: Prettify - ) => Promise> | null> - /** - * - * Returns the user operation receipt from userOpHash - * - * - Docs: https://docs.pimlico.io/permissionless/reference/bundler-actions/getUserOperationReceipt - * - * @param args {@link GetUserOperationReceiptParameters} UserOpHash that was returned by {@link sendUserOperation} - * @returns user operation receipt {@link GetUserOperationReceiptReturnType} if found or null - * - * @example - * import { createClient } from "viem" - * import { bundlerActions } from "permissionless" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http(BUNDLER_URL) - * }).extend(bundlerActions) - * - * await bundlerClient.getUserOperationReceipt({hash: userOpHash}) - * - */ - getUserOperationReceipt: ( - args: Prettify - ) => Promise | null> - - /** - * Waits for the User Operation to be included on a [Block](https://viem.sh/docs/glossary/terms.html#block) (one confirmation), and then returns the [User Operation Receipt](https://docs.pimlico.io/permissionless/reference/bundler-actions/getUserOperationReceipt). - * - * - Docs: https://docs.pimlico.io/permissionless/reference/bundler-actions/waitForUserOperationReceipt - * - * @param client - Bundler Client to use - * @param parameters - {@link WaitForUserOperationReceiptParameters} - * @returns The transaction receipt. {@link GetUserOperationReceiptReturnType} - * - * @example - * import { createBundlerClient, waitForUserOperationReceipt, http } from 'viem' - * import { mainnet } from 'viem/chains' - * - * const bundlerClient = createBundlerClient({ - * chain: mainnet, - * transport: http(), - * }) - * const userOperationReceipt = await bundlerClient.waitForUserOperationReceipt({ - * hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d', - * }) - */ - waitForUserOperationReceipt: ( - args: Prettify - ) => Promise> -} - -const bundlerActions = - (entryPointAddress: entryPoint) => - (client: Client): BundlerActions => ({ - sendUserOperation: async ( - args: Omit, "entryPoint"> - ): Promise => - sendUserOperation(client as BundlerClient, { - ...args, - entryPoint: entryPointAddress - }), - estimateUserOperationGas: ( - args: Omit< - EstimateUserOperationGasParameters, - "entryPoint" - >, - stateOverrides?: StateOverrides - ) => - estimateUserOperationGas( - client as BundlerClient, - { ...args, entryPoint: entryPointAddress }, - stateOverrides - ), - supportedEntryPoints: (): Promise => - supportedEntryPoints(client as BundlerClient), - chainId: () => chainId(client as BundlerClient), - getUserOperationByHash: (args: GetUserOperationByHashParameters) => - getUserOperationByHash( - client as BundlerClient, - args - ), - getUserOperationReceipt: (args: GetUserOperationReceiptParameters) => - getUserOperationReceipt(client as BundlerClient, args), - waitForUserOperationReceipt: ( - args: WaitForUserOperationReceiptParameters - ) => - waitForUserOperationReceipt( - client as BundlerClient, - args - ) - }) - -export { bundlerActions } diff --git a/packages/permissionless/clients/decorators/pimlico.ts b/packages/permissionless/clients/decorators/pimlico.ts index 69fc4904..499a0827 100644 --- a/packages/permissionless/clients/decorators/pimlico.ts +++ b/packages/permissionless/clients/decorators/pimlico.ts @@ -1,8 +1,11 @@ -import type { Client, Hash } from "viem" +import type { Address, Chain, Client, Hash, Prettify, Transport } from "viem" import { + type GetTokenQuotesParameters, + type GetTokenQuotesReturnType, type SendCompressedUserOperationParameters, type ValidateSponsorshipPolicies, type ValidateSponsorshipPoliciesParameters, + getTokenQuotes, sendCompressedUserOperation, validateSponsorshipPolicies } from "../../actions/pimlico" @@ -20,11 +23,11 @@ import { type SponsorUserOperationReturnType, sponsorUserOperation } from "../../actions/pimlico/sponsorUserOperation" -import type { Prettify } from "../../types/" -import type { EntryPoint } from "../../types/entrypoint" -import type { PimlicoBundlerClient, PimlicoPaymasterClient } from "../pimlico" -export type PimlicoBundlerActions = { +export type PimlicoActions< + TChain extends Chain | undefined, + entryPointVersion extends "0.6" | "0.7" = "0.6" | "0.7" +> = { /** * Returns the live gas prices that you can use to send a user operation. * @@ -95,147 +98,70 @@ export type PimlicoBundlerActions = { */ sendCompressedUserOperation: ( args: Prettify< - Omit + Omit > ) => Promise -} - -export const pimlicoBundlerActions = - (entryPointAddress: entryPoint) => - (client: Client): PimlicoBundlerActions => ({ - getUserOperationGasPrice: async () => - getUserOperationGasPrice( - client as PimlicoBundlerClient - ), - getUserOperationStatus: async ( - args: GetUserOperationStatusParameters - ) => - getUserOperationStatus( - client as PimlicoBundlerClient, - args - ), - sendCompressedUserOperation: async ( - args: Omit - ) => - sendCompressedUserOperation( - client as PimlicoBundlerClient, - { - ...args, - entryPoint: entryPointAddress - } - ) - }) - -export type PimlicoPaymasterClientActions = { /** - * Returns paymasterAndData & updated gas parameters required to sponsor a userOperation. - * - * https://docs.pimlico.io/permissionless/reference/pimlico-paymaster-actions/sponsorUserOperation - * - * @param args {@link PimlicoSponsorUserOperationParameters} UserOperation you want to sponsor & entryPoint. - * @returns paymasterAndData & updated gas parameters, see {@link SponsorUserOperationReturnType} - * - * @example - * import { createClient } from "viem" - * import { sponsorUserOperation } from "permissionless/actions/pimlico" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http("https://api.pimlico.io/v2/goerli/rpc?apikey=YOUR_API_KEY_HERE") - * }).extend(pimlicoPaymasterActions) - * - * await bundlerClient.sponsorUserOperation(bundlerClient, { - * userOperation: userOperationWithDummySignature, - * entryPoint: entryPoint - * }}) - * + * @deprecated Use `getPaymasterData` instead */ sponsorUserOperation: ( args: Omit< - PimlicoSponsorUserOperationParameters, + PimlicoSponsorUserOperationParameters, "entryPoint" > - ) => Promise>> - + ) => Promise>> validateSponsorshipPolicies: ( + args: Prettify< + Omit + > + ) => Promise[]> + getTokenQuotes: < + TChainOverride extends Chain | undefined = Chain | undefined + >( args: Prettify< Omit< - ValidateSponsorshipPoliciesParameters, - "entryPoint" + GetTokenQuotesParameters, + "entryPointAddress" > > - ) => Promise[]> + ) => Promise> } -/** - * Returns valid sponsorship policies for a userOperation from the list of ids passed - * - Docs: https://docs.pimlico.io/permissionless/reference/pimlico-paymaster-actions/ValidateSponsorshipPolicies - * - * @param args {@link ValidateSponsorshipPoliciesParameters} UserOperation you want to sponsor & entryPoint. - * @returns valid sponsorship policies, see {@link ValidateSponsorshipPolicies} - * - * @example - * import { createClient } from "viem" - * import { validateSponsorshipPolicies } from "permissionless/actions/pimlico" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http("https://api.pimlico.io/v2/goerli/rpc?apikey=YOUR_API_KEY_HERE") - * }).extend(pimlicoPaymasterActions) - - * - * await bundlerClient.validateSponsorshipPolicies({ - * userOperation: userOperationWithDummySignature, - * entryPoint: entryPoint, - * sponsorshipPolicyIds: ["sp_shiny_puma"] - * }) - * Returns - * [ - * { - * sponsorshipPolicyId: "sp_shiny_puma", - * data: { - * name: "Shiny Puma", - * author: "Pimlico", - * icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4...", - * description: "This policy is for testing purposes only" - * } - * } - * ] - */ -export const pimlicoPaymasterActions = - (entryPointAddress: entryPoint) => - (client: Client): PimlicoPaymasterClientActions => ({ - sponsorUserOperation: async ( - args: Omit< - PimlicoSponsorUserOperationParameters, - "entryPoint" - > - ) => - sponsorUserOperation( - client as PimlicoPaymasterClient, - { - ...args, - entryPoint: entryPointAddress - } - ), - validateSponsorshipPolicies: async ( - args: Omit< - ValidateSponsorshipPoliciesParameters, - "entryPoint" - > - ) => - validateSponsorshipPolicies( - client as PimlicoPaymasterClient, - { ...args, entryPoint: entryPointAddress } - ) +export const pimlicoActions = + ({ + entryPoint + }: { + entryPoint: { address: Address; version: entryPointVersion } + }) => + < + TTransport extends Transport, + TChain extends Chain | undefined = Chain | undefined + >( + client: Client + ): PimlicoActions => ({ + getUserOperationGasPrice: async () => getUserOperationGasPrice(client), + getUserOperationStatus: async ( + args: GetUserOperationStatusParameters + ) => getUserOperationStatus(client, args), + sendCompressedUserOperation: async (args) => + sendCompressedUserOperation(client, { + ...args, + entryPointAddress: entryPoint.address + }), + sponsorUserOperation: async (args) => + sponsorUserOperation(client, { + ...args, + entryPoint + }), + validateSponsorshipPolicies: async (args) => + validateSponsorshipPolicies(client, { + ...args, + entryPointAddress: entryPoint.address + }), + getTokenQuotes: async (args) => + getTokenQuotes(client, { + ...args, + chain: args.chain, + entryPointAddress: entryPoint.address + }) }) - -/** - * TODO: Add support for pimlicoActions after we support all the actions of v2 of the Pimlico API. - */ -// export const pimlicoActions = (client: Client) => { -// return { -// ...pimlicoBundlerActions(client), -// ...pimlicoPaymasterActions(client) -// } -// } diff --git a/packages/permissionless/clients/decorators/smartAccount.test.ts b/packages/permissionless/clients/decorators/smartAccount.test.ts deleted file mode 100644 index d9f222ef..00000000 --- a/packages/permissionless/clients/decorators/smartAccount.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { http } from "viem" -import { generatePrivateKey, privateKeyToAccount } from "viem/accounts" -import { foundry } from "viem/chains" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { getPublicClient } from "../../../permissionless-test/src/utils" -import { signerToSimpleSmartAccount } from "../../accounts" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" -import { createSmartAccountClient } from "../createSmartAccountClient" - -describe("createSmartAccountClient", () => { - testWithRpc("createSmartAccountClient_V06", async ({ rpc }) => { - const publicClient = getPublicClient(rpc.anvilRpc) - - const simpleSmartAccount = await signerToSimpleSmartAccount( - publicClient, - { - entryPoint: ENTRYPOINT_ADDRESS_V06, - signer: privateKeyToAccount(generatePrivateKey()) - } - ) - - const smartAccountClient = createSmartAccountClient({ - chain: foundry, - account: simpleSmartAccount, - bundlerTransport: http(rpc.altoRpc) - }) - - expect(smartAccountClient.account.address).toBe( - simpleSmartAccount.address - ) - }) - - testWithRpc("createSmartAccountClient_V07", async ({ rpc }) => { - const publicClient = getPublicClient(rpc.anvilRpc) - - const simpleSmartAccount = await signerToSimpleSmartAccount( - publicClient, - { - entryPoint: ENTRYPOINT_ADDRESS_V07, - signer: privateKeyToAccount(generatePrivateKey()) - } - ) - - const smartAccountClient = createSmartAccountClient({ - chain: foundry, - account: simpleSmartAccount, - bundlerTransport: http(rpc.altoRpc) - }) - - expect(smartAccountClient.account.address).toBe( - simpleSmartAccount.address - ) - }) -}) diff --git a/packages/permissionless/clients/decorators/smartAccount.ts b/packages/permissionless/clients/decorators/smartAccount.ts index 39c472c2..96708a37 100644 --- a/packages/permissionless/clients/decorators/smartAccount.ts +++ b/packages/permissionless/clients/decorators/smartAccount.ts @@ -4,54 +4,21 @@ import type { Client, ContractFunctionArgs, ContractFunctionName, - DeployContractParameters, Hash, SendTransactionParameters, Transport, TypedData, WriteContractParameters } from "viem" -import type { SmartAccount } from "../../accounts/types" -import { - type SendTransactionsWithPaymasterParameters, - sendTransactions -} from "../../actions/smartAccount" -import { - type DeployContractParametersWithPaymaster, - deployContract -} from "../../actions/smartAccount/deployContract" -import { - type Middleware, - type PrepareUserOperationRequestReturnType, - prepareUserOperationRequest -} from "../../actions/smartAccount/prepareUserOperationRequest" -import { - type SendTransactionWithPaymasterParameters, - sendTransaction -} from "../../actions/smartAccount/sendTransaction" -import { - type SendUserOperationParameters, - sendUserOperation -} from "../../actions/smartAccount/sendUserOperation" +import type { SmartAccount } from "viem/account-abstraction" +import { sendTransaction } from "../../actions/smartAccount/sendTransaction" import { signMessage } from "../../actions/smartAccount/signMessage" import { signTypedData } from "../../actions/smartAccount/signTypedData" -import { - type WriteContractWithPaymasterParameters, - writeContract -} from "../../actions/smartAccount/writeContract" -import type { Prettify } from "../../types/" -import type { StateOverrides } from "../../types/bundler" -import type { EntryPoint } from "../../types/entrypoint" +import { writeContract } from "../../actions/smartAccount/writeContract" export type SmartAccountActions< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, TChain extends Chain | undefined = Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined = - | SmartAccount - | undefined + TSmartAccount extends SmartAccount | undefined = SmartAccount | undefined > = { /** * Creates, signs, and sends a new transaction to the network. @@ -96,8 +63,20 @@ export type SmartAccountActions< * value: 1000000000000000000n, * }) */ - sendTransaction: ( - args: SendTransactionParameters + sendTransaction: < + TChainOverride extends Chain | undefined = undefined, + accountOverride extends SmartAccount | undefined = undefined, + calls extends readonly unknown[] = readonly unknown[] + >( + args: Parameters< + typeof sendTransaction< + TSmartAccount, + TChain, + accountOverride, + TChainOverride, + calls + > + >[1] ) => Promise /** * Calculates an Ethereum-specific signature in [EIP-191 format](https://eips.ethereum.org/EIPS/eip-191): `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))`. @@ -143,12 +122,8 @@ export type SmartAccountActions< * }) */ signMessage: ( - args: Parameters< - typeof signMessage - >[1] - ) => ReturnType< - typeof signMessage - > + args: Parameters>[1] + ) => ReturnType> /** * Signs typed data and calculates an Ethereum-specific signature in [EIP-191 format](https://eips.ethereum.org/EIPS/eip-191): `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))`. * @@ -250,64 +225,11 @@ export type SmartAccountActions< TPrimaryType extends string >( args: Parameters< - typeof signTypedData< - entryPoint, - TTypedData, - TPrimaryType, - TTransport, - TChain, - TSmartAccount - > + typeof signTypedData >[1] ) => ReturnType< - typeof signTypedData< - entryPoint, - TTypedData, - TPrimaryType, - TTransport, - TChain, - TSmartAccount - > + typeof signTypedData > - /** - * Deploys a contract to the network, given bytecode and constructor arguments. - * This function also allows you to sponsor this transaction if sender is a smartAccount - * - * - Docs: https://viem.sh/docs/contract/deployContract.html - * - Examples: https://stackblitz.com/github/wagmi-dev/viem/tree/main/examples/contracts/deploying-contracts - * - * @param args - {@link DeployContractParameters} - * @returns The [Transaction](https://viem.sh/docs/glossary/terms.html#transaction) hash. {@link DeployContractReturnType} - * - * @example - * import { createWalletClient, http } from 'viem' - * import { privateKeyToAccount } from 'viem/accounts' - * import { mainnet } from 'viem/chains' - * - * const client = createWalletClient({ - * account: privateKeyToAccount('0x…'), - * chain: mainnet, - * transport: http(), - * }) - * const hash = await client.deployContract({ - * abi: [], - * account: '0x…, - * bytecode: '0x608060405260405161083e38038061083e833981016040819052610...', - * }) - */ - deployContract: < - const TAbi extends Abi | readonly unknown[], - TChainOverride extends Chain | undefined = undefined - >( - args: Prettify< - DeployContractParameters< - TAbi, - TChain, - TSmartAccount, - TChainOverride - > - > - ) => Promise /** * Executes a write function on a contract. * This function also allows you to sponsor this transaction if sender is a smartAccount @@ -379,8 +301,6 @@ export type SmartAccountActions< > ) => ReturnType< typeof writeContract< - entryPoint, - TTransport, TChain, TSmartAccount, TAbi, @@ -389,219 +309,20 @@ export type SmartAccountActions< TChainOverride > > - prepareUserOperationRequest: ( - args: Prettify< - Parameters< - typeof prepareUserOperationRequest< - entryPoint, - TTransport, - TChain, - TSmartAccount - > - >[1] - >, - stateOverrides?: StateOverrides - ) => Promise>> - sendUserOperation: ( - args: Prettify< - Parameters< - typeof sendUserOperation< - entryPoint, - TTransport, - TChain, - TSmartAccount - > - >[1] - > - ) => Promise - /** - * Creates, signs, and sends a new transaction to the network. - * This function also allows you to sponsor this transaction if sender is a smartAccount - * - * - Docs: https://viem.sh/docs/actions/wallet/sendTransaction.html - * - Examples: https://stackblitz.com/github/wagmi-dev/viem/tree/main/examples/transactions/sending-transactions - * - JSON-RPC Methods: - * - JSON-RPC Accounts: [`eth_sendTransaction`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sendtransaction) - * - Local Accounts: [`eth_sendRawTransaction`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sendrawtransaction) - * - * @param args - {@link SendTransactionParameters} - * @returns The [Transaction](https://viem.sh/docs/glossary/terms.html#transaction) hash. {@link SendTransactionReturnType} - * - * @example - * import { createWalletClient, custom } from 'viem' - * import { mainnet } from 'viem/chains' - * - * const client = createWalletClient({ - * chain: mainnet, - * transport: custom(window.ethereum), - * }) - * const hash = await client.sendTransaction([{ - * account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', - * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', - * value: 1000000000000000000n - * }, { - * to: '0x61897970c51812dc3a010c7d01b50e0d17dc1234', - * value: 10000000000000000n - * }) - * - * @example - * // Account Hoisting - * import { createWalletClient, http } from 'viem' - * import { privateKeyToAccount } from 'viem/accounts' - * import { mainnet } from 'viem/chains' - * - * const client = createWalletClient({ - * account: privateKeyToAccount('0x…'), - * chain: mainnet, - * transport: http(), - * }) - * const hash = await client.sendTransaction([{ - * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', - * value: 1000000000000000000n - * }, { - * to: '0x61897970c51812dc3a010c7d01b50e0d17dc1234', - * value: 10000000000000000n - * }]) - */ - sendTransactions: ( - args: Prettify< - SendTransactionsWithPaymasterParameters< - entryPoint, - TTransport, - TChain, - TSmartAccount - > - > - ) => ReturnType< - typeof sendTransactions - > } -export function smartAccountActions({ - middleware -}: Middleware) { +export function smartAccountActions() { return < - TTransport extends Transport, TChain extends Chain | undefined = Chain | undefined, - TSmartAccount extends - | SmartAccount - | undefined = - | SmartAccount + TSmartAccount extends SmartAccount | undefined = + | SmartAccount | undefined >( - client: Client - ): SmartAccountActions => ({ - prepareUserOperationRequest: (args, stateOverrides) => - prepareUserOperationRequest( - client, - { - ...args, - middleware - }, - stateOverrides - ), - deployContract: (args) => - deployContract(client, { - ...args, - middleware - } as DeployContractParametersWithPaymaster), - sendTransaction: (args) => - sendTransaction( - client, - { - ...args, - middleware - } as SendTransactionWithPaymasterParameters< - entryPoint, - TTransport, - TChain, - TSmartAccount - > - ), - sendTransactions: (args) => - sendTransactions( - client, - { - ...args, - middleware - } - ), - sendUserOperation: (args) => - sendUserOperation( - client, - { - ...args, - middleware - } as SendUserOperationParameters< - entryPoint, - TTransport, - TChain, - TSmartAccount - > - ), - signMessage: (args) => - signMessage( - client, - args - ), - signTypedData: < - const TTypedData extends TypedData | { [key: string]: unknown }, - TPrimaryType extends string - >( - args: Parameters< - typeof signTypedData< - entryPoint, - TTypedData, - TPrimaryType, - TTransport, - TChain, - TSmartAccount - > - >[1] - ) => - signTypedData< - entryPoint, - TTypedData, - TPrimaryType, - TTransport, - TChain, - TSmartAccount - >(client, args), - writeContract: < - const TAbi extends Abi | readonly unknown[], - TFunctionName extends ContractFunctionName< - TAbi, - "nonpayable" | "payable" - > = ContractFunctionName, - TArgs extends ContractFunctionArgs< - TAbi, - "nonpayable" | "payable", - TFunctionName - > = ContractFunctionArgs< - TAbi, - "nonpayable" | "payable", - TFunctionName - >, - TChainOverride extends Chain | undefined = undefined - >( - args: WriteContractParameters< - TAbi, - TFunctionName, - TArgs, - TChain, - TSmartAccount, - TChainOverride - > - ) => - writeContract(client, { - ...args, - middleware - } as WriteContractWithPaymasterParameters< - entryPoint, - TTransport, - TChain, - TSmartAccount, - TAbi - >) + client: Client + ): SmartAccountActions => ({ + sendTransaction: (args) => sendTransaction(client, args as any), + signMessage: (args) => signMessage(client, args), + signTypedData: (args) => signTypedData(client, args), + writeContract: (args) => writeContract(client, args) }) } diff --git a/packages/permissionless/clients/decorators/stackup.ts b/packages/permissionless/clients/decorators/stackup.ts deleted file mode 100644 index 00f7063d..00000000 --- a/packages/permissionless/clients/decorators/stackup.ts +++ /dev/null @@ -1,77 +0,0 @@ -import type { Address, Client } from "viem" -import { - type AccountsParameters, - accounts -} from "../../actions/stackup/accounts" -import { - type SponsorUserOperationParameters, - type SponsorUserOperationReturnType, - sponsorUserOperation -} from "../../actions/stackup/sponsorUserOperation" -import type { EntryPoint } from "../../types/entrypoint" -import type { StackupPaymasterClient } from "../stackup" - -export type StackupPaymasterClientActions = { - /** - * Returns paymasterAndData & updated gas parameters required to sponsor a userOperation. - * - * https://docs.stackup.sh/docs/paymaster-api-rpc-methods#pm_sponsoruseroperation - * - * @param args {@link SponsorUserOperationParameters} UserOperation you want to sponsor & entryPoint. - * @returns paymasterAndData & updated gas parameters, see {@link SponsorUserOperationReturnType} - * - * @example - * import { createClient } from "viem" - * import { stackupPaymasterActions } from "permissionless/actions/stackup" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http("https://api.stackup.sh/v1/paymaster/YOUR_API_KEY_HERE") - * }).extend(stackupPaymasterActions) - * - * await bundlerClient.sponsorUserOperation(bundlerClient, { - * userOperation: userOperationWithDummySignature, - * entryPoint: entryPoint - * }}) - * - */ - sponsorUserOperation: ( - args: Omit, "entrypoint"> - ) => Promise> - - /** - * Returns all the Paymaster addresses associated with an EntryPoint that’s owned by this service. - * - * https://docs.stackup.sh/docs/paymaster-api-rpc-methods#pm_accounts - * - * @param args {@link AccountsParameters} entryPoint for which you want to get list of supported paymasters. - * @returns paymaster addresses - * - * @example - * import { createClient } from "viem" - * import { stackupPaymasterActions } from "permissionless/actions/stackup" - * - * const bundlerClient = createClient({ - * chain: goerli, - * transport: http("https://api.stackup.sh/v1/paymaster/YOUR_API_KEY_HERE") - * }).extend(stackupPaymasterActions) - * - * await bundlerClient.accounts(bundlerClient, { - * entryPoint: entryPoint - * }}) - * - */ - accounts: (args: AccountsParameters) => Promise -} - -export const stackupPaymasterActions = - (entryPointAddress: entryPoint) => - (client: Client): StackupPaymasterClientActions => ({ - sponsorUserOperation: async (args) => - sponsorUserOperation(client as StackupPaymasterClient, { - ...args, - entryPoint: entryPointAddress - }), - accounts: async (args) => - accounts(client as StackupPaymasterClient, args) - }) diff --git a/packages/permissionless/clients/index.ts b/packages/permissionless/clients/index.ts new file mode 100644 index 00000000..9684a3c4 --- /dev/null +++ b/packages/permissionless/clients/index.ts @@ -0,0 +1,2 @@ +export * from "./createSmartAccountClient" +export * from "./decorators/smartAccount" diff --git a/packages/permissionless/clients/pimlico.ts b/packages/permissionless/clients/pimlico.ts index 1eda6396..4cd141b9 100644 --- a/packages/permissionless/clients/pimlico.ts +++ b/packages/permissionless/clients/pimlico.ts @@ -1,114 +1,123 @@ import type { - Account, + Address, + BundlerRpcSchema, Chain, Client, - PublicClientConfig, + ClientConfig, + Prettify, + RpcSchema, Transport } from "viem" import { createClient } from "viem" -import type { EntryPoint } from "../types/entrypoint" -import type { - PimlicoBundlerRpcSchema, - PimlicoPaymasterRpcSchema -} from "../types/pimlico" -import { type BundlerActions, bundlerActions } from "./decorators/bundler" import { - type PimlicoBundlerActions, - type PimlicoPaymasterClientActions, - pimlicoBundlerActions, - pimlicoPaymasterActions -} from "./decorators/pimlico" - -export type PimlicoBundlerClient = Client< - Transport, - Chain | undefined, - Account | undefined, - PimlicoBundlerRpcSchema, - PimlicoBundlerActions & BundlerActions -> + type BundlerActions, + type PaymasterActions, + type SmartAccount, + bundlerActions, + entryPoint07Address, + paymasterActions +} from "viem/account-abstraction" +import type { PimlicoRpcSchema } from "../types/pimlico" +import { type PimlicoActions, pimlicoActions } from "./decorators/pimlico" -export type PimlicoPaymasterClient = Client< - Transport, - Chain | undefined, - Account | undefined, - PimlicoPaymasterRpcSchema, - PimlicoPaymasterClientActions +export type PimlicoClient< + entryPointVersion extends "0.6" | "0.7" = "0.7" | "0.6", + transport extends Transport = Transport, + chain extends Chain | undefined = Chain | undefined, + account extends SmartAccount | undefined = SmartAccount | undefined, + client extends Client | undefined = Client | undefined, + rpcSchema extends RpcSchema | undefined = undefined +> = Prettify< + Client< + transport, + chain extends Chain + ? chain + : // biome-ignore lint/suspicious/noExplicitAny: We need any to infer the chain type + client extends Client + ? chain + : undefined, + account, + rpcSchema extends RpcSchema + ? [...BundlerRpcSchema, ...PimlicoRpcSchema, ...rpcSchema] + : [...BundlerRpcSchema, ...PimlicoRpcSchema], + BundlerActions & + PaymasterActions & + PimlicoActions + > > -/** - * Creates a pimlico specific Bundler Client with a given [Transport](https://viem.sh/docs/clients/intro.html) configured for a [Chain](https://viem.sh/docs/clients/chains.html). - * - * - Docs: https://docs.pimlico.io/permissionless/reference/clients/pimlicoBundlerClient - * - * A Pimlico Client is an interface to "pimlico endpoints" [JSON-RPC API](https://docs.pimlico.io/reference/bundler/endpoints) methods such as getting current blockchain gas prices, getting user operation status, etc through [Pimlico Bundler Actions](TODO://Add bundler action documentation link). - * - * @param config - {@link PublicClientConfig} - * @returns A Pimlico Bundler Client. {@link PimlicoBundlerClient} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * - * const pimlicoBundlerClient = createPimlicoBundlerClient({ - * chain: mainnet, - * transport: http("https://api.pimlico.io/v2/goerli/rpc?apikey=YOUR_API_KEY_HERE"), - * }) - */ -export const createPimlicoBundlerClient = < - entryPoint extends EntryPoint, +export type PimlicoClientConfig< + entryPointVersion extends "0.6" | "0.7" = "0.7" | "0.6", transport extends Transport = Transport, - chain extends Chain | undefined = undefined ->( - parameters: PublicClientConfig & { - entryPoint: entryPoint + chain extends Chain | undefined = Chain | undefined, + account extends SmartAccount | undefined = SmartAccount | undefined, + rpcSchema extends RpcSchema | undefined = undefined +> = Prettify< + Pick< + ClientConfig, + | "account" + | "cacheTime" + | "chain" + | "key" + | "name" + | "pollingInterval" + | "rpcSchema" + | "transport" + > +> & { + entryPoint?: { + address: Address + version: entryPointVersion } -): PimlicoBundlerClient => { - const { key = "public", name = "Pimlico Bundler Client" } = parameters - const client = createClient({ - ...parameters, - key, - name, - type: "pimlicoBundlerClient" - }) - return client - .extend(bundlerActions(parameters.entryPoint)) - .extend(pimlicoBundlerActions(parameters.entryPoint)) } -/** - * Creates a pimlico specific Paymaster Client with a given [Transport](https://viem.sh/docs/clients/intro.html) configured for a [Chain](https://viem.sh/docs/clients/chains.html). - * - * - Docs: https://docs.pimlico.io/permissionless/reference/clients/pimlicoPaymasterClient - * - * A Pimlico Paymaster Client is an interface to "pimlico paymaster endpoints" [JSON-RPC API](https://docs.pimlico.io/reference/verifying-paymaster/endpoints) methods such as sponsoring user operation, etc through Pimlico Paymaster Actions. - * - * @param config - {@link PublicClientConfig} - * @returns A Pimlico Paymaster Client. {@link PimlicoPaymasterClient} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * - * const pimlicoPaymasterClient = createPimlicoPaymasterClient({ - * chain: mainnet, - * transport: http("https://api.pimlico.io/v2/goerli/rpc?apikey=YOUR_API_KEY_HERE"), - * }) - */ -export const createPimlicoPaymasterClient = < - entryPoint extends EntryPoint, +export function createPimlicoClient< + entryPointVersion extends "0.6" | "0.7" = "0.7", transport extends Transport = Transport, - chain extends Chain | undefined = undefined + chain extends Chain | undefined = undefined, + account extends SmartAccount | undefined = SmartAccount | undefined, + client extends Client | undefined = undefined, + rpcSchema extends RpcSchema | undefined = undefined >( - parameters: PublicClientConfig & { - entryPoint: entryPoint - } -): PimlicoPaymasterClient => { - const { key = "public", name = "Pimlico Paymaster Client" } = parameters - const client = createClient({ + parameters: PimlicoClientConfig< + entryPointVersion, + transport, + chain, + account, + rpcSchema + > +): PimlicoClient< + entryPointVersion, + transport, + chain, + account, + client, + rpcSchema +> + +export function createPimlicoClient( + parameters: PimlicoClientConfig +): PimlicoClient { + const { + key = "public", + name = "Pimlico Bundler Client", + entryPoint + } = parameters + + return createClient({ ...parameters, key, name, - type: "pimlicoPaymasterClient" + type: "pimlicoClient" }) - return client.extend(pimlicoPaymasterActions(parameters.entryPoint)) + .extend(bundlerActions) + .extend(paymasterActions) + .extend( + pimlicoActions({ + entryPoint: { + address: entryPoint?.address ?? entryPoint07Address, + version: entryPoint?.version ?? "0.7" + } + }) + ) } diff --git a/packages/permissionless/clients/stackup.ts b/packages/permissionless/clients/stackup.ts deleted file mode 100644 index 1c3aacd7..00000000 --- a/packages/permissionless/clients/stackup.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { - type Account, - type Chain, - type Client, - type PublicClientConfig, - type Transport, - createClient -} from "viem" -import type { EntryPoint } from "../types/entrypoint" -import type { StackupPaymasterRpcSchema } from "../types/stackup" -import { type BundlerActions, bundlerActions } from "./decorators/bundler" -import { - type StackupPaymasterClientActions, - stackupPaymasterActions -} from "./decorators/stackup" - -export type StackupPaymasterClient = Client< - Transport, - Chain | undefined, - Account | undefined, - StackupPaymasterRpcSchema, - StackupPaymasterClientActions & BundlerActions -> - -/** - * Creates a Stackup specific Paymaster Client with a given [Transport](https://viem.sh/docs/clients/intro.html) configured for a [Chain](https://viem.sh/docs/clients/chains.html). - * - * - Docs: https://docs.pimlico.io/permissionless/reference/clients/stackupPaymasterClient - * - * A Stackup Paymaster Client is an interface to "stackup paymaster endpoints" [JSON-RPC API](https://docs.stackup.sh/docs/paymaster-api-rpc-methods) methods such as sponsoring user operation, etc through Stackup Paymaster Actions. - * - * @param config - {@link PublicClientConfig} - * @returns A Stackup Paymaster Client. {@link StackupPaymasterClient} - * - * @example - * import { createPublicClient, http } from 'viem' - * import { mainnet } from 'viem/chains' - * - * const stackupPaymasterClient = createStackupPaymasterClient({ - * chain: mainnet, - * transport: http("https://api.stackup.sh/v2/paymaster/YOUR_API_KEY_HERE"), - * }) - */ -export const createStackupPaymasterClient = < - entryPoint extends EntryPoint, - transport extends Transport = Transport, - chain extends Chain | undefined = undefined ->( - parameters: PublicClientConfig & { - entryPoint: entryPoint - } -): StackupPaymasterClient => { - const { key = "public", name = "Stackup Paymaster Client" } = parameters - const client = createClient({ - ...parameters, - key, - name, - type: "stackupPaymasterClient" - }) - return client - .extend(bundlerActions(parameters.entryPoint)) - .extend(stackupPaymasterActions(parameters.entryPoint)) -} diff --git a/packages/permissionless/errors/account.ts b/packages/permissionless/errors/account.ts deleted file mode 100644 index 629f7d45..00000000 --- a/packages/permissionless/errors/account.ts +++ /dev/null @@ -1,345 +0,0 @@ -import { type Address, BaseError } from "viem" - -export type SenderAlreadyDeployedErrorType = SenderAlreadyDeployedError & { - name: "SenderAlreadyDeployedError" -} -export class SenderAlreadyDeployedError extends BaseError { - static message = /aa10/ - override name = "SenderAlreadyDeployedError" as const - constructor({ - cause, - sender, - docsPath - }: { cause?: BaseError; sender?: Address; docsPath?: string } = {}) { - super( - [ - `Smart account ${sender} is already deployed.`, - "", - "Possible solutions:", - `• Remove the initCode from the user operation and set it to "0x"`, - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type InitCodeRevertedErrorType = InitCodeRevertedError & { - name: "InitCodeRevertedError" -} -export class InitCodeRevertedError extends BaseError { - static message = /aa13/ - override name = "InitCodeRevertedError" as const - constructor({ - cause, - docsPath - }: { cause?: BaseError; docsPath?: string } = {}) { - super( - [ - "EntryPoint failed to create the smart account with the initCode provided.", - "", - "Possible reasons:", - "• The initCode ran out of gas", - "• The initCode reverted during the account deployment process", - "", - "Possible solutions:", - "• Verify that the factory address in the initCode is correct (the factory address is the first 20 bytes of the initCode).", - "• Verify that the initCode is correct.", - "• Check whether the verificationGasLimit is sufficient for the initCode to complete without running out of gas.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type SenderAddressMismatchErrorType = SenderAddressMismatchError & { - name: "SenderAddressMismatchError" -} -export class SenderAddressMismatchError extends BaseError { - static message = /aa14/ - override name = "SenderAddressMismatchError" as const - constructor({ - cause, - sender, - docsPath - }: { - cause?: BaseError - sender: Address - docsPath?: string - }) { - super( - [ - "The initCode returned a different smart account address than expected.", - `Expected: ${sender}`, - "", - "Possible reasons:", - "• Account deployed with the initCode provided does not match match the sender address provided", - "", - "Possible solutions:", - "• Verify that the sender address was generated deterministically from the initCode. (consider leveraging functions like getSenderAddress)", - "• Verify that the factory address in the initCode is correct (the factory address is the first 20 bytes of the initCode)", - "• Verify that the initCode is correct.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type InitCodeDidNotDeploySenderErrorType = - InitCodeDidNotDeploySenderError & { - name: "InitCodeDidNotDeploySenderError" - } -export class InitCodeDidNotDeploySenderError extends BaseError { - static message = /aa15/ - override name = "InitCodeDidNotDeploySenderError" as const - constructor({ - cause, - sender, - docsPath - }: { - cause?: BaseError - sender: Address - docsPath?: string - }) { - super( - [ - `The initCode did not deploy the sender at the address ${sender}.`, - "", - "Possible reasons:", - "• The initCode factory is not creating an account.", - "• The initCode factory is creating an account, but is not implemented correctly as it is not deploying at the sender address", - "", - "Possible solutions:", - "• Verify that the factory address in the initCode is correct (the factory address is the first 20 bytes of the initCode).", - "• Verify that the initCode factory is implemented correctly. The factory must deploy the smart account at the sender address.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type SenderNotDeployedErrorType = SenderNotDeployedError & { - name: "SenderNotDeployedError" -} -export class SenderNotDeployedError extends BaseError { - static message = /aa20/ - override name = "SenderNotDeployedError" as const - constructor({ - cause, - sender, - docsPath - }: { - cause?: BaseError - sender: Address - docsPath?: string - }) { - super( - [ - `Smart account ${sender} is not deployed.`, - "", - "Possible reasons:", - "• An initCode was not specified, but the sender address (i.e. the smart account) is not deployed.", - "", - "Possible solutions:", - "• If this is the first transaction by this account, make sure the initCode is included in the user operation.", - "• If the smart account is already supposed to be deployed, verify that you have selected the correct sender address for the user operation.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type SmartAccountInsufficientFundsErrorType = - SmartAccountInsufficientFundsError & { - name: "SmartAccountInsufficientFundsError" - } -export class SmartAccountInsufficientFundsError extends BaseError { - static message = /aa21/ - override name = "SmartAccountInsufficientFundsError" as const - constructor({ - cause, - sender, - docsPath - }: { - cause?: BaseError - sender: Address - docsPath?: string - }) { - super( - [ - `You are not using a paymaster, and the ${sender} address did not have enough native tokens to cover the gas costs associated with the user operation.`, - "", - "Possible solutions:", - "• If you are not using a paymaster, verify that the sender address has enough native tokens to cover the required prefund. Consider leveraging functions like getRequiredPrefund.", - "• If you are looking to use a paymaster to cover the gas fees, verify that the paymasterAndData field is set.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type SmartAccountSignatureValidityPeriodErrorType = - SmartAccountSignatureValidityPeriodError & { - name: "SmartAccountSignatureValidityPeriodError" - } -export class SmartAccountSignatureValidityPeriodError extends BaseError { - static message = /aa22/ - override name = "SmartAccountSignatureValidityPeriodError" as const - constructor({ - cause, - docsPath - }: { - cause?: BaseError - docsPath?: string - }) { - super( - [ - "The signature used in the user operation is not valid, because it is outside of the time range it specified.", - "", - "Possible reasons:", - "• This error occurs when the block.timestamp falls after the validUntil timestamp, or before the validAfter timestamp.", - "", - "Possible solutions:", - "• If you are looking to use time-based signatures, verify that the validAfter and validUntil fields are set correctly and that the user operation is sent within the specified range.", - "• If you are not looking to use time-based signatures, verify that the validAfter and validUntil fields are set to 0.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type SmartAccountValidationRevertedErrorType = - SmartAccountValidationRevertedError & { - name: "SmartAccountValidationRevertedError" - } -export class SmartAccountValidationRevertedError extends BaseError { - static message = /aa23/ - override name = "SmartAccountValidationRevertedError" as const - constructor({ - cause, - sender, - docsPath - }: { - cause?: BaseError - sender: Address - docsPath?: string - }) { - super( - [ - `The smart account ${sender} reverted or ran out of gas during the validation of the user operation.`, - "", - "Possible solutions:", - "• Verify that the verificationGasLimit is high enough to cover the validateUserOp function's gas costs.", - "• Make sure validateUserOp returns uint(1) for invalid signatures, and MUST NOT REVERT when the signature is invalid", - "• If you are not using a paymaster, verify that the sender address has enough native tokens to cover the required pre fund. Consider leveraging functions like getRequiredPrefund.", - "• Verify that the validateUserOp function is implemented with the correct logic, and that the user operation is supposed to be valid.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type InvalidSmartAccountSignatureErrorType = - InvalidSmartAccountSignatureError & { - name: "InvalidSmartAccountSignatureError" - } -export class InvalidSmartAccountSignatureError extends BaseError { - static message = /aa24/ - override name = "InvalidSmartAccountSignatureError" as const - constructor({ - cause, - sender, - docsPath - }: { - cause?: BaseError - sender: Address - docsPath?: string - }) { - super( - [ - `The smart account ${sender} signature is invalid.`, - "", - "Possible solutions:", - "• Verify that the user operation was correctly signed, and that the signature was correctly encoded in the signature field of the user operation.", - "• Most smart account implementations sign over the userOpHash. Make sure that the userOpHash is correctly computed. Consider leveraging functions like getUserOperationHash.", - "• Make sure you have selected the correct chainId and entryPointAddress when computing the userOpHash.", - "• Make sure the smart account signature verification function is correctly implemented.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type InvalidSmartAccountNonceErrorType = - InvalidSmartAccountNonceError & { - name: "InvalidSmartAccountNonceError" - } -export class InvalidSmartAccountNonceError extends BaseError { - static message = /aa25/ - override name = "InvalidSmartAccountNonceError" as const - constructor({ - cause, - sender, - nonce, - docsPath - }: { - cause?: BaseError - sender: Address - docsPath?: string - nonce: bigint - }) { - const nonceKey = nonce >> BigInt(64) // first 192 bits of nonce - const nonceSequence = nonce & 0xffffffffffffffffn // last 64 bits of nonce - - super( - [ - `The smart account ${sender} nonce is invalid.`, - `Nonce sent: ${nonce} (key: ${nonceKey}, sequence: ${nonceSequence})`, - "", - "Possible solutions:", - "• Verify that you are using the correct nonce for the user operation. The nonce should be the current nonce of the smart account for the selected key. Consider leveraging functions like getAccountNonce.", - "• Verify that the nonce is formatted correctly.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} diff --git a/packages/permissionless/errors/bundler.ts b/packages/permissionless/errors/bundler.ts deleted file mode 100644 index aac2e7c7..00000000 --- a/packages/permissionless/errors/bundler.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { BaseError } from "viem" - -export type InvalidBeneficiaryAddressErrorType = - InvalidBeneficiaryAddressError & { - name: "InvalidBeneficiaryAddressError" - } -export class InvalidBeneficiaryAddressError extends BaseError { - static message = /aa9[01]/ - override name = "InvalidBeneficiaryAddressError" - constructor({ - cause, - docsPath - }: { - cause?: BaseError - docsPath?: string - }) { - super( - [ - "The bundler did not set a beneficiary address when bundling the user operation.", - "", - "Possible solutions:", - "• If you encounter this error when running self-hosted bundler, make sure you have configured the bundler correctly.", - "• If you are using a bundler provider, reach out to them.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type InvalidAggregatorErrorType = InvalidAggregatorError & { - name: "InvalidAggregatorError" -} -export class InvalidAggregatorError extends BaseError { - static message = /aa96/ - override name = "InvalidAggregatorError" - constructor({ - cause, - docsPath - }: { - cause?: BaseError - docsPath?: string - }) { - super( - [ - "The bundler tried to bundle the user operation with an invalid aggregator.", - "", - "Possible solutions:", - "• If you are using your own bundler, configure it to use a valid aggregator.", - "• If you are using a bundler provider, reach out to them.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} diff --git a/packages/permissionless/errors/estimateUserOperationGas.ts b/packages/permissionless/errors/estimateUserOperationGas.ts deleted file mode 100644 index 4564692b..00000000 --- a/packages/permissionless/errors/estimateUserOperationGas.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { BaseError } from "viem" -import type { EstimateUserOperationGasParameters } from "../actions/bundler/estimateUserOperationGas" -import type { EntryPoint } from "../types/entrypoint" -import { prettyPrint } from "./utils" - -export type EstimateUserOperationGasErrorType = - EstimateUserOperationGasError & { - name: "EstimateUserOperationGasError" - } -export class EstimateUserOperationGasError< - entryPoint extends EntryPoint -> extends BaseError { - override cause: BaseError - - override name = "EstimateUserOperationGasError" - - constructor( - cause: BaseError, - { - userOperation, - entryPoint, - docsPath - }: EstimateUserOperationGasParameters & { - docsPath?: string - } - ) { - const prettyArgs = prettyPrint({ - sender: userOperation.sender, - nonce: userOperation.nonce, - initCode: userOperation.initCode, - callData: userOperation.callData, - callGasLimit: userOperation.callGasLimit, - verificationGasLimit: userOperation.verificationGasLimit, - preVerificationGas: userOperation.preVerificationGas, - maxFeePerGas: userOperation.maxFeePerGas, - maxPriorityFeePerGas: userOperation.maxPriorityFeePerGas, - paymasterAndData: userOperation.paymasterAndData, - signature: userOperation.signature, - factory: userOperation.factory, - factoryData: userOperation.factoryData, - paymaster: userOperation.paymaster, - paymasterVerificationGasLimit: - userOperation.paymasterVerificationGasLimit, - paymasterPostOpGasLimit: userOperation.paymasterPostOpGasLimit, - paymasterData: userOperation.paymasterData, - entryPoint - }) - - super(cause.shortMessage, { - cause, - docsPath, - metaMessages: [ - ...(cause.metaMessages ? [...cause.metaMessages, " "] : []), - "Estimate Gas Arguments:", - prettyArgs - ].filter(Boolean) as string[] - }) - this.cause = cause - } -} diff --git a/packages/permissionless/errors/gas.ts b/packages/permissionless/errors/gas.ts deleted file mode 100644 index 26dec105..00000000 --- a/packages/permissionless/errors/gas.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { BaseError } from "viem" - -export type VerificationGasLimitTooLowErrorType = - VerificationGasLimitTooLowError & { - name: "VerificationGasLimitTooLowError" - } -export class VerificationGasLimitTooLowError extends BaseError { - static message = /aa4[01]/ - override name = "VerificationGasLimitTooLowError" - constructor({ - cause, - verificationGasLimit, - docsPath - }: { - cause?: BaseError - verificationGasLimit?: bigint - docsPath?: string - }) { - super( - [ - `The smart account and paymaster verification exceeded the verificationGasLimit ${verificationGasLimit} set for the user operation.`, - "", - "Possible solutions:", - "• Verify that the verificationGasLimit set for the user operation is high enough to cover the gas used during smart account and paymaster verification.", - "• If you are using the eth_estimateUserOperationGas or pm_sponsorUserOperation method from bundler provider to set user operation gas limits and the EntryPoint throws this error during submission, reach out to them.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type ActualGasCostTooHighErrorType = ActualGasCostTooHighError & { - name: "ActualGasCostTooHighError" -} -export class ActualGasCostTooHighError extends BaseError { - static message = /aa51/ - override name = "ActualGasCostTooHighError" - constructor({ - cause, - docsPath - }: { - cause?: BaseError - docsPath?: string - }) { - super( - [ - "The actual gas cost of the user operation ended up being higher than the funds paid by the smart account or the paymaster.", - "", - "Possible solutions:", - "• If you encounter this error, try increasing the verificationGasLimit set for the user operation.", - "• If you are using the eth_estimateUserOperationGas or pm_sponsorUserOperation method from bundler provider to set user operation gas limits and the EntryPoint throws this error during submission, reach out to them.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type GasValuesOverflowErrorType = GasValuesOverflowError & { - name: "GasValuesOverflowError" -} -export class GasValuesOverflowError extends BaseError { - static message = /aa94/ - override name = "GasValuesOverflowError" - constructor({ - cause, - docsPath - }: { - cause?: BaseError - docsPath?: string - }) { - super( - [ - "The gas limit values of the user operation overflowed, they must fit in uint160.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type BundlerOutOfGasErrorType = BundlerOutOfGasError & { - name: "BundlerOutOfGasError" -} -export class BundlerOutOfGasError extends BaseError { - static message = /aa95/ - override name = "BundlerOutOfGasError" - constructor({ - cause, - docsPath - }: { - cause?: BaseError - docsPath?: string - }) { - super( - [ - "The bundler tried to bundle the user operation with the gas limit set too low.", - "", - "Possible solutions:", - "• If you are using your own bundler, configure it send gas limits properly.", - "• If you are using a bundler provider, reach out to them.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} diff --git a/packages/permissionless/errors/index.ts b/packages/permissionless/errors/index.ts index f629f35b..e3dd1020 100644 --- a/packages/permissionless/errors/index.ts +++ b/packages/permissionless/errors/index.ts @@ -1,118 +1,17 @@ -import { - InitCodeDidNotDeploySenderError, - type InitCodeDidNotDeploySenderErrorType, - InitCodeRevertedError, - type InitCodeRevertedErrorType, - InvalidSmartAccountNonceError, - type InvalidSmartAccountNonceErrorType, - InvalidSmartAccountSignatureError, - type InvalidSmartAccountSignatureErrorType, - SenderAddressMismatchError, - type SenderAddressMismatchErrorType, - SenderAlreadyDeployedError, - type SenderAlreadyDeployedErrorType, - SenderNotDeployedError, - type SenderNotDeployedErrorType, - SmartAccountInsufficientFundsError, - type SmartAccountInsufficientFundsErrorType, - SmartAccountSignatureValidityPeriodError, - type SmartAccountSignatureValidityPeriodErrorType, - SmartAccountValidationRevertedError, - type SmartAccountValidationRevertedErrorType -} from "./account" -import { - EstimateUserOperationGasError, - type EstimateUserOperationGasErrorType -} from "./estimateUserOperationGas" -import { - SendUserOperationError, - type SendUserOperationErrorType -} from "./sendUserOperation" +import { BaseError } from "viem" -import { - InvalidPaymasterAndDataError, - type InvalidPaymasterAndDataErrorType, - PaymasterDataRejectedError, - type PaymasterDataRejectedErrorType, - PaymasterDepositTooLowError, - type PaymasterDepositTooLowErrorType, - PaymasterNotDeployedError, - type PaymasterNotDeployedErrorType, - PaymasterPostOpRevertedError, - type PaymasterPostOpRevertedErrorType, - PaymasterValidationRevertedError, - type PaymasterValidationRevertedErrorType, - PaymasterValidityPeriodError, - type PaymasterValidityPeriodErrorType -} from "./paymaster" - -import { - InvalidAggregatorError, - type InvalidAggregatorErrorType, - InvalidBeneficiaryAddressError, - type InvalidBeneficiaryAddressErrorType -} from "./bundler" - -import { - ActualGasCostTooHighError, - type ActualGasCostTooHighErrorType, - BundlerOutOfGasError, - type BundlerOutOfGasErrorType, - GasValuesOverflowError, - type GasValuesOverflowErrorType, - VerificationGasLimitTooLowError, - type VerificationGasLimitTooLowErrorType -} from "./gas" - -export { - type InitCodeDidNotDeploySenderErrorType, - type InitCodeRevertedErrorType, - type InvalidSmartAccountNonceErrorType, - type InvalidSmartAccountSignatureErrorType, - type SenderAddressMismatchErrorType, - type SenderAlreadyDeployedErrorType, - type SenderNotDeployedErrorType, - type SmartAccountInsufficientFundsErrorType, - type SmartAccountSignatureValidityPeriodErrorType, - type SmartAccountValidationRevertedErrorType, - type InvalidPaymasterAndDataErrorType, - type PaymasterDataRejectedErrorType, - type PaymasterDepositTooLowErrorType, - type PaymasterNotDeployedErrorType, - type PaymasterPostOpRevertedErrorType, - type PaymasterValidationRevertedErrorType, - type PaymasterValidityPeriodErrorType, - type InvalidAggregatorErrorType, - type InvalidBeneficiaryAddressErrorType, - type ActualGasCostTooHighErrorType, - type BundlerOutOfGasErrorType, - type GasValuesOverflowErrorType, - type VerificationGasLimitTooLowErrorType, - SenderAlreadyDeployedError, - EstimateUserOperationGasError, - InitCodeRevertedError, - SenderAddressMismatchError, - InitCodeDidNotDeploySenderError, - SenderNotDeployedError, - SmartAccountInsufficientFundsError, - SmartAccountSignatureValidityPeriodError, - SmartAccountValidationRevertedError, - InvalidSmartAccountNonceError, - PaymasterNotDeployedError, - PaymasterDepositTooLowError, - InvalidSmartAccountSignatureError, - InvalidBeneficiaryAddressError, - InvalidAggregatorError, - InvalidPaymasterAndDataError, - PaymasterDataRejectedError, - PaymasterValidityPeriodError, - PaymasterValidationRevertedError, - VerificationGasLimitTooLowError, - ActualGasCostTooHighError, - GasValuesOverflowError, - BundlerOutOfGasError, - PaymasterPostOpRevertedError, - SendUserOperationError, - type EstimateUserOperationGasErrorType, - type SendUserOperationErrorType +export class AccountNotFoundError extends BaseError { + constructor({ docsPath }: { docsPath?: string | undefined } = {}) { + super( + [ + "Could not find an Account to execute with this Action.", + "Please provide an Account with the `account` argument on the Action, or by supplying an `account` to the Client." + ].join("\n"), + { + docsPath, + docsSlug: "account", + name: "AccountNotFoundError" + } + ) + } } diff --git a/packages/permissionless/errors/paymaster.ts b/packages/permissionless/errors/paymaster.ts deleted file mode 100644 index 7000f238..00000000 --- a/packages/permissionless/errors/paymaster.ts +++ /dev/null @@ -1,257 +0,0 @@ -import { BaseError, type Hex } from "viem" -import { getAddressFromInitCodeOrPaymasterAndData } from "../utils/" - -export type PaymasterNotDeployedErrorType = PaymasterNotDeployedError & { - name: "PaymasterNotDeployedError" -} -export class PaymasterNotDeployedError extends BaseError { - static message = /aa30/ - override name = "PaymasterNotDeployedError" - constructor({ - cause, - paymasterAndData, - docsPath - }: { - cause?: BaseError - paymasterAndData?: Hex - docsPath?: string - } = {}) { - const paymaster = paymasterAndData - ? getAddressFromInitCodeOrPaymasterAndData(paymasterAndData) - : "0x" - - super( - [ - `Paymaster ${paymaster} is not deployed.`, - "", - "Possible solutions:", - "• Verify that the paymasterAndData field is correct, and that the first 20 bytes are the address of the paymaster contract you intend to use.", - "• Verify that the paymaster contract is deployed on the network you are using.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type PaymasterDepositTooLowErrorType = PaymasterDepositTooLowError & { - name: "PaymasterDepositTooLowError" -} -export class PaymasterDepositTooLowError extends BaseError { - static message = /aa31/ - override name = "PaymasterDepositTooLowError" - constructor({ - cause, - paymasterAndData, - docsPath - }: { - cause?: BaseError - paymasterAndData?: Hex - docsPath?: string - } = {}) { - const paymaster = paymasterAndData - ? getAddressFromInitCodeOrPaymasterAndData(paymasterAndData) - : "0x" - - super( - [ - `Paymaster ${paymaster} contract does not have enough funds deposited into the EntryPoint contract to cover the required funds for the user operation.`, - "", - "Possible solutions:", - "• If you are using your own paymaster contract, deposit more funds into the EntryPoint contract through the deposit() function of the paymaster contract.", - "• Verify that the paymasterAndData field is correct, and that the first 20 bytes are the address of the paymaster contract you intend to useVerify that the paymasterAndData field is correct, and that the first 20 bytes are the address of the paymaster contract you intend to use.", - "• If you are using a paymaster service, reach out to them.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type PaymasterValidityPeriodErrorType = PaymasterValidityPeriodError & { - name: "PaymasterValidityPeriodError" -} -export class PaymasterValidityPeriodError extends BaseError { - static message = /aa32/ - override name = "PaymasterValidityPeriodError" - constructor({ - cause, - paymasterAndData, - docsPath - }: { - cause?: BaseError - paymasterAndData?: Hex - docsPath?: string - }) { - const paymaster = paymasterAndData - ? getAddressFromInitCodeOrPaymasterAndData(paymasterAndData) - : "0x" - - super( - [ - `Paymaster ${paymaster}'s data used in the paymasterAndData field of the user operation is not valid, because it is outside of the time range it specified.`, - "", - "Possible reasons:", - "• This error occurs when the block.timestamp falls after the validUntil timestamp, or before the validAfter timestamp.", - "", - "Possible solutions:", - "• If you are using your own paymaster contract and using time-based signatures, verify that the validAfter and validUntil fields are set correctly and that the user operation is sent within the specified range.", - "• If you are using your own paymaster contract and not looking to use time-based signatures, verify that the validAfter and validUntil fields are set to 0.", - "• If you are using a service, contact your service provider for their paymaster's validity.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type PaymasterValidationRevertedErrorType = - PaymasterValidationRevertedError & { - name: "PaymasterValidationRevertedError" - } -export class PaymasterValidationRevertedError extends BaseError { - static message = /aa33/ - override name = "PaymasterValidationRevertedError" - constructor({ - cause, - paymasterAndData, - docsPath - }: { - cause?: BaseError - paymasterAndData?: Hex - docsPath?: string - }) { - const paymaster = paymasterAndData - ? getAddressFromInitCodeOrPaymasterAndData(paymasterAndData) - : "0x" - - super( - [ - `The validatePaymasterUserOp function of the paymaster ${paymaster} either reverted or ran out of gas.`, - "", - "Possible solutions:", - "• Verify that the verificationGasLimit is high enough to cover the validatePaymasterUserOp function's gas costs.", - "• If you are using your own paymaster contract, verify that the validatePaymasterUserOp function is implemented with the correct logic, and that the user operation is supposed to be valid.", - "• If you are using a paymaster service, and the user operation is well formed with a high enough verificationGasLimit, reach out to them.", - "• If you are not looking to use a paymaster to cover the gas fees, verify that the paymasterAndData field is not set.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type PaymasterDataRejectedErrorType = PaymasterDataRejectedError & { - name: "PaymasterDataRejectedError" -} -export class PaymasterDataRejectedError extends BaseError { - static message = /aa34/ - override name = "PaymasterDataRejectedError" - constructor({ - cause, - paymasterAndData, - docsPath - }: { - cause?: BaseError - paymasterAndData?: Hex - docsPath?: string - }) { - const paymaster = paymasterAndData - ? getAddressFromInitCodeOrPaymasterAndData(paymasterAndData) - : "0x" - - super( - [ - `The validatePaymasterUserOp function of the paymaster ${paymaster} rejected paymasterAndData.`, - "", - "Possible solutions:", - "• If you are using your own paymaster contract, verify that the user operation was correctly signed according to your implementation, and that the paymaster signature was correctly encoded in the paymasterAndData field of the user operation.", - "• If you are using a paymaster service, make sure you do not modify any of the fields of the user operation after the paymaster signs over it (except the signature field).", - "• If you are using a paymaster service and you have not modified any of the fields except the signature but you are still getting this error, reach out to them.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type PaymasterPostOpRevertedErrorType = PaymasterPostOpRevertedError & { - name: "PaymasterPostOpRevertedError" -} -export class PaymasterPostOpRevertedError extends BaseError { - static message = /aa50/ - override name = "PaymasterPostOpRevertedError" - constructor({ - cause, - paymasterAndData, - docsPath - }: { - cause?: BaseError - paymasterAndData?: Hex - docsPath?: string - }) { - const paymaster = paymasterAndData - ? getAddressFromInitCodeOrPaymasterAndData(paymasterAndData) - : "0x" - - super( - [ - `The postOp function of the paymaster ${paymaster} reverted.`, - "", - "Possible solutions:", - "• If you are using your own paymaster contract, verify that that you have correctly implemented the postOp function (if you are using one). If you do not intent to make use of the postOp function, make sure you do not set the context parameter in the paymaster's validatePaymasterUserOp function.", - "• If you are using a paymaster service and you see this error, reach out to them.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} - -export type InvalidPaymasterAndDataErrorType = InvalidPaymasterAndDataError & { - name: "InvalidPaymasterAndDataError" -} -export class InvalidPaymasterAndDataError extends BaseError { - static message = /aa93/ - override name = "InvalidPaymasterAndDataError" - constructor({ - cause, - docsPath - }: { - cause?: BaseError - docsPath?: string - }) { - super( - [ - "The paymasterAndData field of the user operation is invalid.", - "", - "Possible solutions:", - "• Make sure you have either not set a value for the paymasterAndData, or that it is at least 20 bytes long.", - "• If you are using a paymaster service, reach out to them.", - "", - docsPath ? `Docs: ${docsPath}` : "" - ].join("\n"), - { - cause - } - ) - } -} diff --git a/packages/permissionless/errors/sendUserOperation.ts b/packages/permissionless/errors/sendUserOperation.ts deleted file mode 100644 index 9d0fcc02..00000000 --- a/packages/permissionless/errors/sendUserOperation.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { BaseError } from "viem" -import type { SendUserOperationParameters } from "../actions/bundler/sendUserOperation" -import type { EntryPoint } from "../types/entrypoint" -import { prettyPrint } from "./utils" - -export type SendUserOperationErrorType = - SendUserOperationError & { - name: "SendUserOperationError" - } -export class SendUserOperationError< - entryPoint extends EntryPoint -> extends BaseError { - override cause: BaseError - - override name = "SendUserOperationError" - - constructor( - cause: BaseError, - { - userOperation, - entryPoint, - docsPath - }: SendUserOperationParameters & { - docsPath?: string - } - ) { - const prettyArgs = prettyPrint({ - ...userOperation, - entryPoint - }) - - super(cause.shortMessage, { - cause, - docsPath, - metaMessages: [ - ...(cause.metaMessages ? [...cause.metaMessages, " "] : []), - "sendUserOperation Arguments:", - prettyArgs - ].filter(Boolean) as string[] - }) - this.cause = cause - } -} diff --git a/packages/permissionless/errors/utils.ts b/packages/permissionless/errors/utils.ts deleted file mode 100644 index a8b7c589..00000000 --- a/packages/permissionless/errors/utils.ts +++ /dev/null @@ -1,19 +0,0 @@ -export type ErrorType = Error & { name: name } - -export function prettyPrint( - args: Record -) { - const entries = Object.entries(args) - .map(([key, value]) => { - if (value === undefined || value === false) return null - return [key, value] - }) - .filter(Boolean) as [string, string][] - const maxLength = entries.reduce( - (acc, [key]) => Math.max(acc, key.length), - 0 - ) - return entries - .map(([key, value]) => ` ${`${key}:`.padEnd(maxLength + 1)} ${value}`) - .join("\n") -} diff --git a/packages/permissionless/experimental/eip7677/actions/getPaymasterData.test.ts b/packages/permissionless/experimental/eip7677/actions/getPaymasterData.test.ts deleted file mode 100644 index 6c7185d6..00000000 --- a/packages/permissionless/experimental/eip7677/actions/getPaymasterData.test.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { zeroAddress } from "viem" -import { generatePrivateKey } from "viem/accounts" -import { foundry } from "viem/chains" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../../../permissionless-test/src/testWithRpc" -import { - getBundlerClient, - getPimlicoPaymasterClient, - getSimpleAccountClient -} from "../../../../permissionless-test/src/utils" -import type { UserOperation } from "../../../types/userOperation" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../../utils" -import { paymasterActionsEip7677 } from "../clients/decorators/paymasterActionsEip7677" - -describe("EIP-7677 getPaymasterData", () => { - testWithRpc("getPaymasterData_V06", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc - }) - - const pimlicoPaymasterClient = getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }).extend(paymasterActionsEip7677(ENTRYPOINT_ADDRESS_V06)) - - let userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - } - }) - - const paymasterData = await pimlicoPaymasterClient.getPaymasterData({ - userOperation, - chain: foundry - }) - expect(paymasterData).not.toBeNull() - expect(paymasterData?.paymasterAndData).not.toBeNull() - - // test that smart account can send op with the returned values - const bundlerClient = getBundlerClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - altoRpc - }) - - userOperation = { - ...userOperation, - ...paymasterData - } - userOperation.signature = - await simpleAccountClient.account.signUserOperation(userOperation) - - const hash = await bundlerClient.sendUserOperation({ - userOperation - }) - - const receipt = await bundlerClient.waitForUserOperationReceipt({ - hash - }) - expect(receipt.success).toBeTruthy() - }) - - testWithRpc("getPaymasterData_V07", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc - }) - - const pimlicoPaymasterClient = getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }).extend(paymasterActionsEip7677(ENTRYPOINT_ADDRESS_V07)) - - let userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - } - }) - - const paymasterGasValues = { - paymasterPostOpGasLimit: 150_000n, - paymasterVerificationGasLimit: 650_000n - } - - const paymasterData = await pimlicoPaymasterClient.getPaymasterData({ - userOperation: { - ...userOperation, - ...paymasterGasValues - }, - chain: foundry - }) - expect(paymasterData).not.toBeNull() - expect(paymasterData?.paymaster).not.toBeNull() - expect(paymasterData?.paymasterData).not.toBeNull() - - // test that smart account can send op with the returned values - const bundlerClient = getBundlerClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - altoRpc - }) - - userOperation = { - ...userOperation, - ...paymasterGasValues, - ...paymasterData - } as UserOperation<"v0.7"> - userOperation.signature = - await simpleAccountClient.account.signUserOperation(userOperation) - - const hash = await bundlerClient.sendUserOperation({ - userOperation - }) - - const receipt = await bundlerClient.waitForUserOperationReceipt({ - hash - }) - expect(receipt.success).toBeTruthy() - }) -}) diff --git a/packages/permissionless/experimental/eip7677/actions/getPaymasterData.ts b/packages/permissionless/experimental/eip7677/actions/getPaymasterData.ts deleted file mode 100644 index c656a105..00000000 --- a/packages/permissionless/experimental/eip7677/actions/getPaymasterData.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { - type Address, - type Chain, - ChainNotFoundError, - type Client, - type GetChainParameter, - type Hex, - type Transport, - toHex -} from "viem" -import type { - ENTRYPOINT_ADDRESS_V06_TYPE, - ENTRYPOINT_ADDRESS_V07_TYPE, - EntryPoint, - GetEntryPointVersion -} from "../../../types/entrypoint" -import type { UserOperationWithBigIntAsHex } from "../../../types/userOperation" -import { deepHexlify, getEntryPointVersion } from "../../../utils" -import type { - Eip7677RpcSchema, - GetRpcPaymasterDataReturnType -} from "../types/paymaster" - -export type GetPaymasterDataParameters< - TEntryPoint extends EntryPoint, - TChain extends Chain | undefined = Chain | undefined, - TChainOverride extends Chain | undefined = Chain | undefined -> = { - userOperation: GetEntryPointVersion extends "v0.6" - ? { - sender: Address - nonce: bigint - initCode: Hex - callData: Hex - callGasLimit: bigint - verificationGasLimit: bigint - preVerificationGas: bigint - maxFeePerGas: bigint - maxPriorityFeePerGas: bigint - paymasterAndData?: Hex - signature?: Hex - factory?: never - factoryData?: never - paymaster?: never - paymasterVerificationGasLimit?: never - paymasterPostOpGasLimit?: never - paymasterData?: never - } - : { - sender: Address - nonce: bigint - factory?: Address - factoryData?: Hex - callData: Hex - callGasLimit: bigint - verificationGasLimit: bigint - preVerificationGas: bigint - maxFeePerGas: bigint - maxPriorityFeePerGas: bigint - paymaster?: Address - paymasterData?: Hex - signature?: Hex - paymasterAndData?: never - paymasterVerificationGasLimit: bigint - paymasterPostOpGasLimit: bigint - } - entryPoint: TEntryPoint - context?: Record -} & GetChainParameter - -export type GetPaymasterDataReturnType = - GetEntryPointVersion extends "v0.6" - ? { - paymasterAndData: Hex - } - : { - paymaster: Hex - paymasterData: Hex - } - -export async function getPaymasterData< - TEntryPoint extends EntryPoint, - TChain extends Chain | undefined, - TTransport extends Transport = Transport, - TChainOverride extends Chain | undefined = Chain | undefined ->( - client: Client< - TTransport, - TChain, - undefined, - Eip7677RpcSchema - >, - { - userOperation, - entryPoint, - context, - chain - }: GetPaymasterDataParameters -): Promise> { - const chainId = chain?.id ?? client.chain?.id - - if (!chainId) { - throw new ChainNotFoundError() - } - - const params: - | [ - UserOperationWithBigIntAsHex>, - TEntryPoint, - Hex, - Record - ] - | [ - UserOperationWithBigIntAsHex>, - TEntryPoint, - Hex - ] = context - ? [ - deepHexlify(userOperation) as UserOperationWithBigIntAsHex< - GetEntryPointVersion - >, - entryPoint, - toHex(chainId), - context - ] - : [ - deepHexlify(userOperation) as UserOperationWithBigIntAsHex< - GetEntryPointVersion - >, - entryPoint, - toHex(chainId) - ] - - const response = await client.request({ - method: "pm_getPaymasterData", - params - }) - - const entryPointVersion = getEntryPointVersion(entryPoint) - - if (entryPointVersion === "v0.6") { - const responseV06 = - response as GetRpcPaymasterDataReturnType - - return { - paymasterAndData: responseV06.paymasterAndData - } as GetPaymasterDataReturnType - } - - const responseV07 = - response as GetRpcPaymasterDataReturnType - - return { - paymaster: responseV07.paymaster, - paymasterData: responseV07.paymasterData - } as GetPaymasterDataReturnType -} diff --git a/packages/permissionless/experimental/eip7677/actions/getPaymasterStubData.test.ts b/packages/permissionless/experimental/eip7677/actions/getPaymasterStubData.test.ts deleted file mode 100644 index 991fbc75..00000000 --- a/packages/permissionless/experimental/eip7677/actions/getPaymasterStubData.test.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { zeroAddress } from "viem" -import { generatePrivateKey } from "viem/accounts" -import { foundry } from "viem/chains" -import { describe, expect } from "vitest" -import { testWithRpc } from "../../../../permissionless-test/src/testWithRpc" -import { - getPimlicoPaymasterClient, - getSimpleAccountClient -} from "../../../../permissionless-test/src/utils" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../../utils" -import { paymasterActionsEip7677 } from "../clients/decorators/paymasterActionsEip7677" - -describe("EIP-7677 getPaymasterStubData", () => { - testWithRpc("getPaymasterStubData_V06", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc - }) - - const pimlicoPaymasterClient = getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06, - paymasterRpc - }).extend(paymasterActionsEip7677(ENTRYPOINT_ADDRESS_V06)) - - const userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - } - }) - - const stubData = await pimlicoPaymasterClient.getPaymasterStubData({ - userOperation, - chain: foundry - }) - expect(stubData).not.toBeNull() - expect(stubData?.paymasterAndData).not.toBeNull() - expect(stubData?.isFinal).toBe(false) - expect(stubData?.sponsor?.icon).toBeTruthy() - expect(stubData?.sponsor?.name).toBe("Pimlico") - }) - - testWithRpc("getPaymasterStubData_V07", async ({ rpc }) => { - const { anvilRpc, altoRpc, paymasterRpc } = rpc - - const simpleAccountClient = await getSimpleAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - privateKey: generatePrivateKey(), - altoRpc: altoRpc, - anvilRpc: anvilRpc - }) - - const pimlicoPaymasterClient = getPimlicoPaymasterClient({ - entryPoint: ENTRYPOINT_ADDRESS_V07, - paymasterRpc - }).extend(paymasterActionsEip7677(ENTRYPOINT_ADDRESS_V07)) - - const userOperation = - await simpleAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await simpleAccountClient.account.encodeCallData({ - to: zeroAddress, - data: "0x", - value: 0n - }) - } - }) - - const stubData = await pimlicoPaymasterClient.getPaymasterStubData({ - userOperation, - chain: foundry - }) - expect(stubData).not.toBeNull() - expect(stubData?.paymaster).not.toBeNull() - expect(stubData?.paymasterData).not.toBeNull() - expect(stubData?.paymasterPostOpGasLimit).not.toBeNull() - expect(stubData?.paymasterVerificationGasLimit).not.toBeNull() - expect(stubData?.isFinal).toBe(false) - expect(stubData?.sponsor?.icon).toBeTruthy() - expect(stubData?.sponsor?.name).toBe("Pimlico") - }) -}) diff --git a/packages/permissionless/experimental/eip7677/actions/getPaymasterStubData.ts b/packages/permissionless/experimental/eip7677/actions/getPaymasterStubData.ts deleted file mode 100644 index 0d0ba502..00000000 --- a/packages/permissionless/experimental/eip7677/actions/getPaymasterStubData.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { - type Address, - type Chain, - ChainNotFoundError, - type Client, - type GetChainParameter, - type Hex, - type Transport, - toHex -} from "viem" -import type { - ENTRYPOINT_ADDRESS_V06_TYPE, - ENTRYPOINT_ADDRESS_V07_TYPE, - EntryPoint, - GetEntryPointVersion -} from "../../../types/entrypoint" -import type { UserOperationWithBigIntAsHex } from "../../../types/userOperation" -import { deepHexlify, getEntryPointVersion } from "../../../utils" -import type { - Eip7677RpcSchema, - GetRpcPaymasterStubDataReturnType -} from "../types/paymaster" - -export type GetPaymasterStubDataParameters< - TEntryPoint extends EntryPoint, - TChain extends Chain | undefined, - TChainOverride extends Chain | undefined = Chain | undefined -> = { - userOperation: GetEntryPointVersion extends "v0.6" - ? { - sender: Address - nonce: bigint - initCode: Hex - callData: Hex - callGasLimit: bigint - verificationGasLimit: bigint - preVerificationGas: bigint - maxFeePerGas: bigint - maxPriorityFeePerGas: bigint - paymasterAndData?: Hex - signature?: Hex - factory?: never - factoryData?: never - paymaster?: never - paymasterVerificationGasLimit?: never - paymasterPostOpGasLimit?: never - paymasterData?: never - } - : { - sender: Address - nonce: bigint - factory?: Address - factoryData?: Hex - callData: Hex - callGasLimit: bigint - verificationGasLimit: bigint - preVerificationGas: bigint - maxFeePerGas: bigint - maxPriorityFeePerGas: bigint - paymaster?: Address - paymasterData?: Hex - signature?: Hex - paymasterAndData?: never - paymasterVerificationGasLimit?: bigint - paymasterPostOpGasLimit?: bigint - } - entryPoint: TEntryPoint - context?: Record -} & GetChainParameter - -export type GetPaymasterStubDataReturnType = - GetEntryPointVersion extends "v0.6" - ? { - paymasterAndData: Hex - sponsor?: { name: string; icon?: string } - isFinal?: boolean - } - : { - paymaster: Hex - paymasterData: Hex - paymasterVerificationGasLimit?: bigint - paymasterPostOpGasLimit?: bigint - sponsor?: { name: string; icon?: string } - isFinal?: boolean - } - -export async function getPaymasterStubData< - TEntryPoint extends EntryPoint, - TChain extends Chain | undefined, - TTransport extends Transport = Transport, - TChainOverride extends Chain | undefined = Chain | undefined ->( - client: Client< - TTransport, - TChain, - undefined, - Eip7677RpcSchema - >, - { - userOperation, - entryPoint, - context, - chain - }: GetPaymasterStubDataParameters -): Promise> { - const chainId = chain?.id ?? client.chain?.id - - if (!chainId) { - throw new ChainNotFoundError() - } - - const params: - | [ - UserOperationWithBigIntAsHex>, - TEntryPoint, - Hex, - Record - ] - | [ - UserOperationWithBigIntAsHex>, - TEntryPoint, - Hex - ] = context - ? [ - deepHexlify(userOperation) as UserOperationWithBigIntAsHex< - GetEntryPointVersion - >, - entryPoint, - toHex(chainId), - context - ] - : [ - deepHexlify(userOperation) as UserOperationWithBigIntAsHex< - GetEntryPointVersion - >, - entryPoint, - toHex(chainId) - ] - - const response = await client.request({ - method: "pm_getPaymasterStubData", - params - }) - - const entryPointVersion = getEntryPointVersion(entryPoint) - - if (entryPointVersion === "v0.6") { - const responseV06 = - response as GetRpcPaymasterStubDataReturnType - - return { - paymasterAndData: responseV06.paymasterAndData, - sponsor: responseV06.sponsor, - isFinal: responseV06.isFinal - } as GetPaymasterStubDataReturnType - } - - const responseV07 = - response as GetRpcPaymasterStubDataReturnType - - return { - paymaster: responseV07.paymaster, - paymasterData: responseV07.paymasterData, - paymasterVerificationGasLimit: responseV07.paymasterVerificationGasLimit - ? BigInt(responseV07.paymasterVerificationGasLimit) - : undefined, - paymasterPostOpGasLimit: responseV07.paymasterPostOpGasLimit - ? BigInt(responseV07.paymasterPostOpGasLimit) - : undefined, - sponsor: responseV07.sponsor, - isFinal: responseV07.isFinal - } as GetPaymasterStubDataReturnType -} diff --git a/packages/permissionless/experimental/eip7677/clients/decorators/paymasterActionsEip7677.ts b/packages/permissionless/experimental/eip7677/clients/decorators/paymasterActionsEip7677.ts deleted file mode 100644 index 452d0658..00000000 --- a/packages/permissionless/experimental/eip7677/clients/decorators/paymasterActionsEip7677.ts +++ /dev/null @@ -1,77 +0,0 @@ -import type { Chain, Client, Transport } from "viem" -import type { EntryPoint } from "../../../../types/entrypoint" -import { - type GetPaymasterDataParameters, - type GetPaymasterDataReturnType, - getPaymasterData -} from "../../actions/getPaymasterData" -import { - type GetPaymasterStubDataParameters, - type GetPaymasterStubDataReturnType, - getPaymasterStubData -} from "../../actions/getPaymasterStubData" -import type { Eip7677RpcSchema } from "../../types/paymaster" - -export type PaymasterActionsEip7677< - TEntryPoint extends EntryPoint, - TChain extends Chain | undefined = Chain | undefined -> = { - getPaymasterData: < - TChainOverride extends Chain | undefined = Chain | undefined - >( - args: Omit< - GetPaymasterDataParameters, - "entryPoint" - > - ) => Promise> - getPaymasterStubData: < - TChainOverride extends Chain | undefined = Chain | undefined - >( - args: Omit< - GetPaymasterStubDataParameters, - "entryPoint" - > - ) => Promise> -} - -const paymasterActionsEip7677 = - (entryPoint: TEntryPoint) => - < - TTransport extends Transport, - TChain extends Chain | undefined = Chain | undefined - >( - client: Client - ): PaymasterActionsEip7677 => ({ - getPaymasterData: (args) => - getPaymasterData( - client as Client< - TTransport, - TChain, - undefined, - Eip7677RpcSchema - >, - { - userOperation: args.userOperation, - context: args.context, - chain: args.chain, - entryPoint - } - ), - getPaymasterStubData: async (args) => - getPaymasterStubData( - client as Client< - TTransport, - TChain, - undefined, - Eip7677RpcSchema - >, - { - userOperation: args.userOperation, - context: args.context, - chain: args.chain, - entryPoint - } - ) - }) - -export { paymasterActionsEip7677 } diff --git a/packages/permissionless/experimental/eip7677/index.ts b/packages/permissionless/experimental/eip7677/index.ts deleted file mode 100644 index 60669669..00000000 --- a/packages/permissionless/experimental/eip7677/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { - type GetPaymasterDataParameters, - type GetPaymasterDataReturnType, - getPaymasterData -} from "./actions/getPaymasterData" -import { - type GetPaymasterStubDataParameters, - type GetPaymasterStubDataReturnType, - getPaymasterStubData -} from "./actions/getPaymasterStubData" -import { - type PaymasterActionsEip7677, - paymasterActionsEip7677 -} from "./clients/decorators/paymasterActionsEip7677" -import type { Eip7677RpcSchema } from "./types/paymaster" - -export { - type GetPaymasterStubDataParameters, - type GetPaymasterStubDataReturnType, - getPaymasterStubData, - type GetPaymasterDataReturnType, - type GetPaymasterDataParameters, - getPaymasterData, - type PaymasterActionsEip7677, - paymasterActionsEip7677, - type Eip7677RpcSchema -} diff --git a/packages/permissionless/experimental/eip7677/types/paymaster.ts b/packages/permissionless/experimental/eip7677/types/paymaster.ts deleted file mode 100644 index bf41777c..00000000 --- a/packages/permissionless/experimental/eip7677/types/paymaster.ts +++ /dev/null @@ -1,63 +0,0 @@ -import type { Hex } from "viem" -import type { - EntryPoint, - GetEntryPointVersion -} from "../../../types/entrypoint" -import type { UserOperationWithBigIntAsHex } from "../../../types/userOperation" - -export type GetRpcPaymasterStubDataParameters = [ - userOperation: UserOperationWithBigIntAsHex< - GetEntryPointVersion - >, - entryPoint: entryPoint, - chainId: Hex, - context?: Record -] - -export type GetRpcPaymasterStubDataReturnType = - GetEntryPointVersion extends "v0.6" - ? { - paymasterAndData: Hex - sponsor?: { name: string; icon?: string } - isFinal?: boolean - } - : { - paymaster: Hex - paymasterData: Hex - paymasterVerificationGasLimit?: Hex | null - paymasterPostOpGasLimit?: Hex | null - sponsor?: { name: string; icon?: string } - isFinal?: boolean - } - -export type GetRpcPaymasterDataParameters = [ - userOperation: UserOperationWithBigIntAsHex< - GetEntryPointVersion - >, - entryPoint: entryPoint, - chainId: Hex, - context?: Record -] - -export type GetRpcPaymasterDataReturnType = - GetEntryPointVersion extends "v0.6" - ? { - paymasterAndData: Hex - } - : { - paymaster: Hex - paymasterData: Hex - } - -export type Eip7677RpcSchema = [ - { - Method: "pm_getPaymasterStubData" - Parameters: GetRpcPaymasterStubDataParameters - ReturnType: GetRpcPaymasterStubDataReturnType - }, - { - Method: "pm_getPaymasterData" - Paremeters: GetRpcPaymasterDataParameters - ReturnType: GetRpcPaymasterDataReturnType - } -] diff --git a/packages/permissionless/experimental/index.ts b/packages/permissionless/experimental/index.ts deleted file mode 100644 index 4952309b..00000000 --- a/packages/permissionless/experimental/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./eip7677" diff --git a/packages/permissionless/index.ts b/packages/permissionless/index.ts index 00b387fe..ed21b5a7 100644 --- a/packages/permissionless/index.ts +++ b/packages/permissionless/index.ts @@ -1,88 +1,3 @@ -import type { - EstimateUserOperationGasParameters, - EstimateUserOperationGasReturnType -} from "./actions/bundler/estimateUserOperationGas" -import type { GetUserOperationByHashParameters } from "./actions/bundler/getUserOperationByHash" -import type { GetUserOperationByHashReturnType } from "./actions/bundler/getUserOperationByHash" -import type { - GetUserOperationReceiptParameters, - GetUserOperationReceiptReturnType -} from "./actions/bundler/getUserOperationReceipt" -import type { SendUserOperationParameters } from "./actions/bundler/sendUserOperation" - -import type { GetSenderAddressParams } from "./actions/public/getSenderAddress" -import { getSenderAddress } from "./actions/public/getSenderAddress" - -import { chainId } from "./actions/bundler/chainId" -import { estimateUserOperationGas } from "./actions/bundler/estimateUserOperationGas" -import { getUserOperationByHash } from "./actions/bundler/getUserOperationByHash" -import { getUserOperationReceipt } from "./actions/bundler/getUserOperationReceipt" -import { sendUserOperation } from "./actions/bundler/sendUserOperation" -import { supportedEntryPoints } from "./actions/bundler/supportedEntryPoints" -import { waitForUserOperationReceipt } from "./actions/bundler/waitForUserOperationReceipt" -import { - type WaitForUserOperationReceiptParameters, - WaitForUserOperationReceiptTimeoutError -} from "./actions/bundler/waitForUserOperationReceipt" -import type { GetAccountNonceParams } from "./actions/public/getAccountNonce" -import { getAccountNonce } from "./actions/public/getAccountNonce" -import { - type BundlerClient, - createBundlerClient -} from "./clients/createBundlerClient" -import { createSmartAccountClient } from "./clients/createSmartAccountClient" -import type { - SmartAccountClient, - SmartAccountClientConfig -} from "./clients/createSmartAccountClient" -import type { BundlerActions } from "./clients/decorators/bundler" -import { bundlerActions } from "./clients/decorators/bundler" -import { - type SmartAccountActions, - smartAccountActions -} from "./clients/decorators/smartAccount" - -export type { - SendUserOperationParameters, - EstimateUserOperationGasParameters, - EstimateUserOperationGasReturnType, - GetUserOperationByHashParameters, - GetUserOperationByHashReturnType, - GetUserOperationReceiptParameters, - GetUserOperationReceiptReturnType, - GetSenderAddressParams, - GetAccountNonceParams, - BundlerClient, - BundlerActions, - WaitForUserOperationReceiptParameters, - SmartAccountClient, - SmartAccountClientConfig, - SmartAccountActions -} - -export { - sendUserOperation, - estimateUserOperationGas, - supportedEntryPoints, - chainId, - getUserOperationByHash, - getUserOperationReceipt, - getSenderAddress, - getAccountNonce, - waitForUserOperationReceipt, - createBundlerClient, - bundlerActions, - WaitForUserOperationReceiptTimeoutError, - createSmartAccountClient, - smartAccountActions -} -import type { UserOperation } from "./types/userOperation" - -export type { UserOperation } - -import type { GetUserOperationHashParams } from "./utils/getUserOperationHash" -import { getUserOperationHash } from "./utils/getUserOperationHash" - -export { getUserOperationHash, type GetUserOperationHashParams } export * from "./utils/" export * from "./errors/" +export * from "./clients/" diff --git a/packages/permissionless/package.json b/packages/permissionless/package.json index dd3a5a51..97f57b99 100644 --- a/packages/permissionless/package.json +++ b/packages/permissionless/package.json @@ -39,11 +39,6 @@ "import": "./_esm/actions/pimlico.js", "default": "./_cjs/actions/pimlico.js" }, - "./actions/stackup": { - "types": "./_types/actions/stackup.d.ts", - "import": "./_esm/actions/stackup.js", - "default": "./_cjs/actions/stackup.js" - }, "./actions/smartAccount": { "types": "./_types/actions/smartAccount.d.ts", "import": "./_esm/actions/smartAccount.js", @@ -59,11 +54,6 @@ "import": "./_esm/clients/pimlico.js", "default": "./_cjs/clients/pimlico.js" }, - "./clients/stackup": { - "types": "./_types/clients/stackup.d.ts", - "import": "./_esm/clients/stackup.js", - "default": "./_cjs/clients/stackup.js" - }, "./utils": { "types": "./_types/utils/index.d.ts", "import": "./_esm/utils/index.js", @@ -74,11 +64,6 @@ "import": "./_esm/errors/index.js", "default": "./_cjs/errors/index.js" }, - "./types": { - "types": "./_types/types/index.d.ts", - "import": "./_esm/types/index.js", - "default": "./_cjs/types/index.js" - }, "./experimental": { "types": "./_types/experimental/index.d.ts", "import": "./_esm/experimental/index.js", @@ -86,6 +71,6 @@ } }, "peerDependencies": { - "viem": ">=2.14.1 <2.18.0" + "viem": "^2.20.0" } } diff --git a/packages/permissionless/types/bundler.ts b/packages/permissionless/types/bundler.ts deleted file mode 100644 index 70355664..00000000 --- a/packages/permissionless/types/bundler.ts +++ /dev/null @@ -1,131 +0,0 @@ -import type { Address, Hash, Hex } from "viem" -import type { PartialBy } from "viem/types/utils" -import type { EntryPoint, GetEntryPointVersion } from "./entrypoint" -import type { UserOperationWithBigIntAsHex } from "./userOperation" - -export type BundlerRpcSchema = [ - { - Method: "eth_sendUserOperation" - Parameters: [ - userOperation: UserOperationWithBigIntAsHex< - GetEntryPointVersion - >, - entryPoint: entryPoint - ] - ReturnType: Hash - }, - { - Method: "eth_estimateUserOperationGas" - Parameters: [ - userOperation: GetEntryPointVersion extends "v0.6" - ? PartialBy< - UserOperationWithBigIntAsHex<"v0.6">, - | "callGasLimit" - | "preVerificationGas" - | "verificationGasLimit" - > - : PartialBy< - UserOperationWithBigIntAsHex<"v0.7">, - | "callGasLimit" - | "preVerificationGas" - | "verificationGasLimit" - | "paymasterVerificationGasLimit" - | "paymasterPostOpGasLimit" - >, - entryPoint: entryPoint, - stateOverrides?: StateOverrides - ] - ReturnType: GetEntryPointVersion extends "v0.6" - ? { - preVerificationGas: Hex - verificationGasLimit: Hex - callGasLimit: Hex - } - : { - preVerificationGas: Hex - verificationGasLimit: Hex - callGasLimit?: Hex | null - paymasterVerificationGasLimit?: Hex | null - paymasterPostOpGasLimit?: Hex | null - } - }, - { - Method: "eth_supportedEntryPoints" - Parameters: [] - ReturnType: Address[] - }, - { - Method: "eth_chainId" - Parameters: [] - ReturnType: Hex - }, - { - Method: "eth_getUserOperationByHash" - Parameters: [hash: Hash] - ReturnType: { - userOperation: UserOperationWithBigIntAsHex< - GetEntryPointVersion - > - entryPoint: entryPoint - transactionHash: Hash - blockHash: Hash - blockNumber: Hex - } - }, - { - Method: "eth_getUserOperationReceipt" - Parameters: [hash: Hash] - ReturnType: UserOperationReceiptWithBigIntAsHex - } -] - -type UserOperationReceiptWithBigIntAsHex = { - userOpHash: Hash - entryPoint: Address - sender: Address - nonce: Hex - paymaster?: Address - actualGasUsed: Hex - actualGasCost: Hex - success: boolean - reason?: string - receipt: { - transactionHash: Hex - transactionIndex: Hex - blockHash: Hash - blockNumber: Hex - from: Address - to: Address | null - cumulativeGasUsed: Hex - status: "0x0" | "0x1" - gasUsed: Hex - contractAddress: Address | null - logsBloom: Hex - effectiveGasPrice: Hex - } - logs: { - data: Hex - blockNumber: Hex - blockHash: Hash - transactionHash: Hash - logIndex: Hex - transactionIndex: Hex - address: Address - topics: [Hex, ...Hex[]] | [] - removed: boolean - }[] -} - -export type StateOverrides = { - [x: string]: { - balance?: bigint | undefined - nonce?: bigint | number | undefined - code?: Hex | undefined - state?: { - [x: Hex]: Hex - } - stateDiff?: { - [x: Hex]: Hex - } - } -} diff --git a/packages/permissionless/types/entrypoint.ts b/packages/permissionless/types/entrypoint.ts deleted file mode 100644 index 32753e5e..00000000 --- a/packages/permissionless/types/entrypoint.ts +++ /dev/null @@ -1,13 +0,0 @@ -export type EntryPointVersion = "v0.6" | "v0.7" - -export type ENTRYPOINT_ADDRESS_V06_TYPE = - "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" -export type ENTRYPOINT_ADDRESS_V07_TYPE = - "0x0000000071727De22E5E9d8BAf0edAc6f37da032" - -export type GetEntryPointVersion = - entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE ? "v0.6" : "v0.7" - -export type EntryPoint = - | ENTRYPOINT_ADDRESS_V06_TYPE - | ENTRYPOINT_ADDRESS_V07_TYPE diff --git a/packages/permissionless/types/index.ts b/packages/permissionless/types/index.ts deleted file mode 100644 index 05462f29..00000000 --- a/packages/permissionless/types/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { Account, Chain, Client, Transport } from "viem" -import type { SmartAccount } from "../accounts/types" -import type { EntryPoint } from "./entrypoint" -import type { UserOperation } from "./userOperation" -export type { UserOperation } -export type { - EntryPointVersion, - ENTRYPOINT_ADDRESS_V06_TYPE, - ENTRYPOINT_ADDRESS_V07_TYPE, - GetEntryPointVersion, - EntryPoint -} from "./entrypoint" - -export type { PackedUserOperation } from "./userOperation" - -export type IsUndefined = [undefined] extends [T] ? true : false - -export type GetAccountParameterWithClient< - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends Account | undefined = Account | undefined -> = IsUndefined extends true - ? { account: Account; client?: Client } - : { client: Client; account?: Account } - -export type GetAccountParameter< - entryPoint extends EntryPoint, - TTransport extends Transport, - TChain extends Chain | undefined, - TAccount extends - | SmartAccount - | undefined -> = IsUndefined extends true - ? { account: SmartAccount } - : { account?: SmartAccount } - -export type Prettify = { - [K in keyof T]: T[K] -} & {} - -export type PartialBy = Omit & Partial> - -export type PartialPick = Partial> - -// biome-ignore lint/suspicious/noExplicitAny: generic type -export type UnionOmit = T extends any - ? Omit - : never diff --git a/packages/permissionless/types/pimlico.ts b/packages/permissionless/types/pimlico.ts index 839044ed..c4ad3094 100644 --- a/packages/permissionless/types/pimlico.ts +++ b/packages/permissionless/types/pimlico.ts @@ -1,7 +1,5 @@ -import type { Address, Hash, Hex } from "viem" -import type { PartialBy } from "viem/types/utils" -import type { EntryPoint, GetEntryPointVersion } from "./entrypoint" -import type { UserOperationWithBigIntAsHex } from "./userOperation" +import type { Address, Hash, Hex, OneOf, PartialBy } from "viem" +import type { UserOperation } from "viem/account-abstraction" type PimlicoUserOperationGasPriceWithBigIntAsHex = { slow: { @@ -30,7 +28,18 @@ export type PimlicoUserOperationStatus = { transactionHash: Hash | null } -export type PimlicoBundlerRpcSchema = [ +type GetTokenQuotesWithBigIntAsHex = { + quotes: { + paymaster: Address + token: Address + postOpGas: Hex + exchangeRate: Hex + }[] +} + +export type PimlicoRpcSchema< + entryPointVersion extends "0.6" | "0.7" = "0.6" | "0.7" +> = [ { Method: "pimlico_getUserOperationGasPrice" Parameters: [] @@ -49,34 +58,36 @@ export type PimlicoBundlerRpcSchema = [ entryPoint: Address ] ReturnType: Hash - } -] - -export type PimlicoPaymasterRpcSchema = [ + }, { Method: "pm_sponsorUserOperation" Parameters: [ - userOperation: GetEntryPointVersion extends "v0.6" - ? PartialBy< - UserOperationWithBigIntAsHex<"v0.6">, - | "callGasLimit" - | "preVerificationGas" - | "verificationGasLimit" - > - : PartialBy< - UserOperationWithBigIntAsHex<"v0.7">, - | "callGasLimit" - | "preVerificationGas" - | "verificationGasLimit" - | "paymasterVerificationGasLimit" - | "paymasterPostOpGasLimit" - >, - entryPoint: entryPoint, + userOperation: OneOf< + | (entryPointVersion extends "0.6" + ? PartialBy< + UserOperation<"0.6", Hex>, + | "callGasLimit" + | "preVerificationGas" + | "verificationGasLimit" + > + : never) + | (entryPointVersion extends "0.7" + ? PartialBy< + UserOperation<"0.7", Hex>, + | "callGasLimit" + | "preVerificationGas" + | "verificationGasLimit" + | "paymasterVerificationGasLimit" + | "paymasterPostOpGasLimit" + > + : never) + >, + entryPoint: Address, metadata?: { sponsorshipPolicyId?: string } ] - ReturnType: GetEntryPointVersion extends "v0.6" + ReturnType: entryPointVersion extends "0.6" ? { paymasterAndData: Hex preVerificationGas: Hex @@ -101,10 +112,8 @@ export type PimlicoPaymasterRpcSchema = [ { Method: "pm_validateSponsorshipPolicies" Parameters: [ - userOperation: UserOperationWithBigIntAsHex< - GetEntryPointVersion - >, - entryPoint: entryPoint, + userOperation: UserOperation, + entryPoint: Address, sponsorshipPolicyIds: string[] ] ReturnType: { @@ -116,5 +125,10 @@ export type PimlicoPaymasterRpcSchema = [ description: string | null } }[] + }, + { + Method: "pimlico_getTokenQuotes" + Parameters: [{ tokens: Address[] }, entryPoint: Address, chainId: Hex] + ReturnType: GetTokenQuotesWithBigIntAsHex } ] diff --git a/packages/permissionless/types/stackup.ts b/packages/permissionless/types/stackup.ts deleted file mode 100644 index 197a95f6..00000000 --- a/packages/permissionless/types/stackup.ts +++ /dev/null @@ -1,63 +0,0 @@ -import type { Address, Hex } from "viem" -import type { PartialBy } from "viem/types/utils" -import type { ENTRYPOINT_ADDRESS_V06_TYPE, EntryPoint } from "./entrypoint" -import type { UserOperationWithBigIntAsHex } from "./userOperation" - -interface StackupPaymasterContextType { - type: "erc20token" | "payg" -} - -export type StackupPaymasterContext = - | (StackupPaymasterContextType & { type: "erc20token"; token: string }) - | (StackupPaymasterContextType & { type: "payg" }) - -export type StackupPaymasterRpcSchema = [ - { - Method: "pm_sponsorUserOperation" - Parameters: [ - userOperation: entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE - ? PartialBy< - UserOperationWithBigIntAsHex<"v0.6">, - | "callGasLimit" - | "preVerificationGas" - | "verificationGasLimit" - > - : PartialBy< - UserOperationWithBigIntAsHex<"v0.7">, - | "callGasLimit" - | "preVerificationGas" - | "verificationGasLimit" - | "paymasterVerificationGasLimit" - | "paymasterPostOpGasLimit" - >, - entryPoint: entryPoint, - context: StackupPaymasterContext - ] - ReturnType: entryPoint extends ENTRYPOINT_ADDRESS_V06_TYPE - ? { - paymasterAndData: Hex - preVerificationGas: Hex - verificationGasLimit: Hex - callGasLimit: Hex - paymaster?: never - paymasterVerificationGasLimit?: never - paymasterPostOpGasLimit?: never - paymasterData?: never - } - : { - preVerificationGas: Hex - verificationGasLimit: Hex - callGasLimit: Hex - paymaster: Address - paymasterVerificationGasLimit: Hex - paymasterPostOpGasLimit: Hex - paymasterData: Hex - paymasterAndData?: never - } - }, - { - Method: "pm_accounts" - Parameters: [entryPoint: Address] - ReturnType: Address[] - } -] diff --git a/packages/permissionless/types/userOperation.ts b/packages/permissionless/types/userOperation.ts deleted file mode 100644 index abd644df..00000000 --- a/packages/permissionless/types/userOperation.ts +++ /dev/null @@ -1,102 +0,0 @@ -import type { Address } from "viem" -import type { Hex } from "viem" -import type { EntryPointVersion } from "./entrypoint" - -export type TStatus = "success" | "reverted" - -export type UserOperationWithBigIntAsHex< - entryPointVersion extends EntryPointVersion -> = entryPointVersion extends "v0.6" - ? { - sender: Address - nonce: Hex - initCode: Hex - callData: Hex - callGasLimit: Hex - verificationGasLimit: Hex - preVerificationGas: Hex - maxFeePerGas: Hex - maxPriorityFeePerGas: Hex - paymasterAndData: Hex - signature: Hex - factory?: never - factoryData?: never - paymaster?: never - paymasterVerificationGasLimit?: never - paymasterPostOpGasLimit?: never - paymasterData?: never - } - : { - sender: Address - nonce: Hex - factory: Address - factoryData: Hex - callData: Hex - callGasLimit: Hex - verificationGasLimit: Hex - preVerificationGas: Hex - maxFeePerGas: Hex - maxPriorityFeePerGas: Hex - paymaster: Address - paymasterVerificationGasLimit: Hex - paymasterPostOpGasLimit: Hex - paymasterData: Hex - signature: Hex - initCode?: never - paymasterAndData?: never - } - -export type UserOperation = - entryPointVersion extends "v0.6" - ? { - sender: Address - nonce: bigint - initCode: Hex - callData: Hex - callGasLimit: bigint - verificationGasLimit: bigint - preVerificationGas: bigint - maxFeePerGas: bigint - maxPriorityFeePerGas: bigint - paymasterAndData: Hex - signature: Hex - factory?: never - factoryData?: never - paymaster?: never - paymasterVerificationGasLimit?: never - paymasterPostOpGasLimit?: never - paymasterData?: never - } - : { - sender: Address - nonce: bigint - factory?: Address - factoryData?: Hex - callData: Hex - callGasLimit: bigint - verificationGasLimit: bigint - preVerificationGas: bigint - maxFeePerGas: bigint - maxPriorityFeePerGas: bigint - paymaster?: Address - paymasterVerificationGasLimit?: bigint - paymasterPostOpGasLimit?: bigint - paymasterData?: Hex - signature: Hex - initCode?: never - paymasterAndData?: never - } - -export type Hex32 = `0x${string & { length: 64 }}` - -export type PackedUserOperation = { - sender: Address - nonce: bigint - initCode: Hex - callData: Hex - accountGasLimits: Hex32 - preVerificationGas: bigint - gasFees: Hex32 - paymasterAndData: Hex - signature: Hex -} diff --git a/packages/permissionless/utils/encode7579CallData.test.ts b/packages/permissionless/utils/encode7579Calls.test.ts similarity index 87% rename from packages/permissionless/utils/encode7579CallData.test.ts rename to packages/permissionless/utils/encode7579Calls.test.ts index 58b1fbf1..f47271ba 100644 --- a/packages/permissionless/utils/encode7579CallData.test.ts +++ b/packages/permissionless/utils/encode7579Calls.test.ts @@ -1,17 +1,19 @@ import { describe, expect, test } from "vitest" -import { encode7579CallData } from "./encode7579CallData" +import { encode7579Calls } from "./encode7579Calls" -describe("encode7579CallData", () => { +describe("encode7579Calls", () => { test("single call", async () => { - const callData = encode7579CallData({ + const callData = encode7579Calls({ mode: { type: "call" }, - callData: { - to: "0x1234567890123456789012345678901234567890", - value: BigInt(12345), - data: "0x1234567890123456789012345678901234567890" - } + callData: [ + { + to: "0x1234567890123456789012345678901234567890", + value: BigInt(12345), + data: "0x1234567890123456789012345678901234567890" + } + ] }) expect(callData).toBe( @@ -20,7 +22,7 @@ describe("encode7579CallData", () => { }) test("batch call", async () => { - const callData = encode7579CallData({ + const callData = encode7579Calls({ mode: { type: "batchcall" }, @@ -45,11 +47,10 @@ describe("encode7579CallData", () => { test("should throw error for batch call with different mode", async () => { expect(() => { - encode7579CallData({ + encode7579Calls({ mode: { type: "call" }, - // @ts-expect-error callData: [ { to: "0x1234567890123456789012345678901234567890", diff --git a/packages/permissionless/utils/encode7579CallData.ts b/packages/permissionless/utils/encode7579Calls.ts similarity index 80% rename from packages/permissionless/utils/encode7579CallData.ts rename to packages/permissionless/utils/encode7579Calls.ts index 8390bad2..4d6f4fa4 100644 --- a/packages/permissionless/utils/encode7579CallData.ts +++ b/packages/permissionless/utils/encode7579Calls.ts @@ -14,24 +14,18 @@ import { export type EncodeCallDataParams = { mode: ExecutionMode - callData: callType extends "batchcall" - ? { - to: Address - value: bigint - data: Hex - }[] - : { - to: Address - value: bigint - data: Hex - } + callData: readonly { + to: Address + value?: bigint | undefined + data?: Hex | undefined + }[] } -export function encode7579CallData({ +export function encode7579Calls({ mode, callData }: EncodeCallDataParams): Hex { - if (Array.isArray(callData) && mode?.type !== "batchcall") { + if (callData.length > 1 && mode?.type !== "batchcall") { throw new Error( `mode ${JSON.stringify(mode)} does not supported for batchcall calldata` ) @@ -58,7 +52,7 @@ export function encode7579CallData({ } ] as const - if (Array.isArray(callData)) { + if (callData.length > 1) { return encodeFunctionData({ abi: executeAbi, functionName: "execute", @@ -89,8 +83,8 @@ export function encode7579CallData({ callData.map((arg) => { return { target: arg.to, - value: arg.value, - callData: arg.data + value: arg.value ?? 0n, + callData: arg.data ?? "0x" } }) ] @@ -105,9 +99,9 @@ export function encode7579CallData({ args: [ encodeExecutionMode(mode), concatHex([ - callData.to, - toHex(callData.value, { size: 32 }), - callData.data + callData[0].to, + toHex(callData[0].value ?? 0n, { size: 32 }), + callData[0].data ?? "0x" ]) ] }) diff --git a/packages/permissionless/utils/errors/getBundlerError.ts b/packages/permissionless/utils/errors/getBundlerError.ts deleted file mode 100644 index eeadf0fa..00000000 --- a/packages/permissionless/utils/errors/getBundlerError.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { - type Address, - BaseError, - ExecutionRevertedError, - type ExecutionRevertedErrorType, - UnknownNodeError, - type UnknownNodeErrorType -} from "viem" -import { SenderAlreadyDeployedError } from "../../errors" -import { - InitCodeDidNotDeploySenderError, - type InitCodeDidNotDeploySenderErrorType, - InitCodeRevertedError, - type InitCodeRevertedErrorType, - InvalidSmartAccountNonceError, - type InvalidSmartAccountNonceErrorType, - SenderAddressMismatchError, - type SenderAddressMismatchErrorType, - type SenderAlreadyDeployedErrorType, - SenderNotDeployedError, - type SenderNotDeployedErrorType, - SmartAccountInsufficientFundsError, - type SmartAccountInsufficientFundsErrorType, - SmartAccountSignatureValidityPeriodError, - type SmartAccountSignatureValidityPeriodErrorType, - SmartAccountValidationRevertedError, - type SmartAccountValidationRevertedErrorType -} from "../../errors/account" -import { - PaymasterDataRejectedError, - type PaymasterDataRejectedErrorType, - PaymasterDepositTooLowError, - type PaymasterDepositTooLowErrorType, - PaymasterNotDeployedError, - type PaymasterNotDeployedErrorType, - PaymasterValidationRevertedError, - type PaymasterValidationRevertedErrorType, - PaymasterValidityPeriodError, - type PaymasterValidityPeriodErrorType -} from "../../errors/paymaster" -import type { - EntryPoint, - GetEntryPointVersion, - UserOperation -} from "../../types" - -export type GetBundlerErrorParameters = { - userOperation: Partial>> - entryPoint: Address -} - -export type GetBundlerErrorReturnType = - | ExecutionRevertedErrorType - | UnknownNodeErrorType - | SenderAlreadyDeployedErrorType - | InitCodeRevertedErrorType - | SenderAddressMismatchErrorType - | InitCodeDidNotDeploySenderErrorType - | SenderNotDeployedErrorType - | SmartAccountInsufficientFundsErrorType - | SmartAccountSignatureValidityPeriodErrorType - | SmartAccountValidationRevertedErrorType - | InvalidSmartAccountNonceErrorType - | PaymasterNotDeployedErrorType - | PaymasterDepositTooLowErrorType - | PaymasterValidityPeriodErrorType - | PaymasterValidationRevertedErrorType - | PaymasterDataRejectedErrorType - -export function getBundlerError( - err: BaseError, - args: GetBundlerErrorParameters -) { - const message = (err.details || "").toLowerCase() - - const executionRevertedError = - err instanceof BaseError - ? err.walk( - (e) => - (e as { code: number }).code === - ExecutionRevertedError.code - ) - : err - - if (executionRevertedError instanceof BaseError) { - return new ExecutionRevertedError({ - cause: err, - message: executionRevertedError.details - }) as ExecutionRevertedErrorType - } - - // TODO: Add validation Errors - if (args.userOperation.sender === undefined) - return new UnknownNodeError({ cause: err }) - if (args.userOperation.nonce === undefined) - return new UnknownNodeError({ cause: err }) - - if (SenderAlreadyDeployedError.message.test(message)) { - return new SenderAlreadyDeployedError({ - cause: err, - sender: args.userOperation.sender, - docsPath: - "https://docs.pimlico.io/bundler/reference/entrypoint-errors/aa10" - }) as SenderAlreadyDeployedErrorType - } - - if (InitCodeRevertedError.message.test(message)) { - return new InitCodeRevertedError({ - cause: err, - docsPath: - "https://docs.pimlico.io/bundler/reference/entrypoint-errors/aa13" - }) as InitCodeRevertedErrorType - } - - if (SenderAddressMismatchError.message.test(message)) { - return new SenderAddressMismatchError({ - cause: err, - sender: args.userOperation.sender, - docsPath: - "https://docs.pimlico.io/bundler/reference/entrypoint-errors/aa14" - }) as SenderAddressMismatchErrorType - } - - if (InitCodeDidNotDeploySenderError.message.test(message)) { - return new InitCodeDidNotDeploySenderError({ - cause: err, - sender: args.userOperation.sender, - docsPath: - "https://docs.pimlico.io/bundler/reference/entrypoint-errors/aa15" - }) as InitCodeDidNotDeploySenderErrorType - } - - if (SenderNotDeployedError.message.test(message)) { - return new SenderNotDeployedError({ - cause: err, - sender: args.userOperation.sender, - docsPath: - "https://docs.pimlico.io/bundler/reference/entrypoint-errors/aa20" - }) as SenderNotDeployedErrorType - } - - if (SmartAccountInsufficientFundsError.message.test(message)) { - return new SmartAccountInsufficientFundsError({ - cause: err, - sender: args.userOperation.sender, - docsPath: - "https://docs.pimlico.io/bundler/reference/entrypoint-errors/aa21" - }) as SmartAccountInsufficientFundsErrorType - } - - if (SmartAccountSignatureValidityPeriodError.message.test(message)) { - return new SmartAccountSignatureValidityPeriodError({ - cause: err, - docsPath: - "https://docs.pimlico.io/bundler/reference/entrypoint-errors/aa22" - }) as SmartAccountSignatureValidityPeriodErrorType - } - - if (SmartAccountValidationRevertedError.message.test(message)) { - return new SmartAccountValidationRevertedError({ - cause: err, - sender: args.userOperation.sender, - docsPath: - "https://docs.pimlico.io/bundler/reference/entrypoint-errors/aa23" - }) as SmartAccountValidationRevertedErrorType - } - - if (InvalidSmartAccountNonceError.message.test(message)) { - return new InvalidSmartAccountNonceError({ - cause: err, - sender: args.userOperation.sender, - nonce: args.userOperation.nonce, - docsPath: - "https://docs.pimlico.io/bundler/reference/entrypoint-errors/aa25" - }) as InvalidSmartAccountNonceErrorType - } - - if (PaymasterNotDeployedError.message.test(message)) { - return new PaymasterNotDeployedError({ - cause: err, - paymasterAndData: args.userOperation.paymasterAndData, - docsPath: - "https://docs.pimlico.io/bundler/reference/entrypoint-errors/aa30" - }) as PaymasterNotDeployedErrorType - } - - if (PaymasterDepositTooLowError.message.test(message)) { - return new PaymasterDepositTooLowError({ - cause: err, - paymasterAndData: args.userOperation.paymasterAndData, - docsPath: - "https://docs.pimlico.io/bundler/reference/entrypoint-errors/aa31" - }) as PaymasterDepositTooLowErrorType - } - - if (PaymasterValidityPeriodError.message.test(message)) { - return new PaymasterValidityPeriodError({ - cause: err, - paymasterAndData: args.userOperation.paymasterAndData, - docsPath: - "https://docs.pimlico.io/bundler/reference/entrypoint-errors/aa32" - }) as PaymasterValidityPeriodErrorType - } - - if (PaymasterValidationRevertedError.message.test(message)) { - return new PaymasterValidationRevertedError({ - cause: err, - paymasterAndData: args.userOperation.paymasterAndData, - docsPath: - "https://docs.pimlico.io/bundler/reference/entrypoint-errors/aa33" - }) as PaymasterValidationRevertedErrorType - } - - if (PaymasterDataRejectedError.message.test(message)) { - return new PaymasterDataRejectedError({ - cause: err, - paymasterAndData: args.userOperation.paymasterAndData, - docsPath: - "https://docs.pimlico.io/bundler/reference/entrypoint-errors/aa34" - }) as PaymasterDataRejectedErrorType - } - - return new UnknownNodeError({ cause: err }) as UnknownNodeErrorType -} diff --git a/packages/permissionless/utils/errors/getEstimateUserOperationGasError.ts b/packages/permissionless/utils/errors/getEstimateUserOperationGasError.ts deleted file mode 100644 index d3e7ad57..00000000 --- a/packages/permissionless/utils/errors/getEstimateUserOperationGasError.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { type BaseError, UnknownNodeError } from "viem" -import type { EstimateUserOperationGasParameters } from "../../actions/bundler/estimateUserOperationGas" -import { - EstimateUserOperationGasError, - type EstimateUserOperationGasErrorType -} from "../../errors/estimateUserOperationGas" -import type { ErrorType } from "../../errors/utils" -import type { EntryPoint } from "../../types/entrypoint" -import { - type GetBundlerErrorParameters, - type GetBundlerErrorReturnType, - getBundlerError -} from "./getBundlerError" - -export type GetEstimateUserOperationGasErrorReturnType< - entryPoint extends EntryPoint, - cause = ErrorType -> = Omit, "cause"> & { - cause: cause | GetBundlerErrorReturnType -} - -export function getEstimateUserOperationGasError< - err extends ErrorType, - entryPoint extends EntryPoint ->(error: err, args: EstimateUserOperationGasParameters) { - const cause = (() => { - const cause = getBundlerError( - // biome-ignore lint/complexity/noBannedTypes: - error as {} as BaseError, - args as GetBundlerErrorParameters - ) - // biome-ignore lint/complexity/noBannedTypes: - if (cause instanceof UnknownNodeError) return error as {} as BaseError - return cause - })() - - throw new EstimateUserOperationGasError(cause, { - ...args - }) as GetEstimateUserOperationGasErrorReturnType -} diff --git a/packages/permissionless/utils/errors/getSendUserOperationError.ts b/packages/permissionless/utils/errors/getSendUserOperationError.ts deleted file mode 100644 index 47edcb4e..00000000 --- a/packages/permissionless/utils/errors/getSendUserOperationError.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { type BaseError, UnknownNodeError } from "viem" -import type { SendUserOperationParameters } from "../../actions/bundler/sendUserOperation" -import { SendUserOperationError } from "../../errors" -import type { EntryPoint } from "../../types/entrypoint" -import { - type GetBundlerErrorParameters, - getBundlerError -} from "./getBundlerError" - -export function getSendUserOperationError( - err: BaseError, - args: SendUserOperationParameters -) { - const cause = (() => { - const cause = getBundlerError( - err as BaseError, - args as GetBundlerErrorParameters - ) - if (cause instanceof UnknownNodeError) return err as BaseError - return cause - })() - - throw new SendUserOperationError(cause, { - ...args - }) -} diff --git a/packages/permissionless/utils/getEntryPointVersion.test.ts b/packages/permissionless/utils/getEntryPointVersion.test.ts deleted file mode 100644 index 0a642c5e..00000000 --- a/packages/permissionless/utils/getEntryPointVersion.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { describe, expect, test } from "vitest" -import { - ENTRYPOINT_ADDRESS_V06, - ENTRYPOINT_ADDRESS_V07, - getEntryPointVersion, - isUserOperationVersion06, - isUserOperationVersion07 -} from "./getEntryPointVersion" - -describe("getEntryPointVersion", () => { - describe("getEntryPointVersion", () => { - test('should return "v0.6" for ENTRYPOINT_ADDRESS_V06', () => { - expect(getEntryPointVersion(ENTRYPOINT_ADDRESS_V06)).toBe("v0.6") - }) - - test('should return "v0.7" for ENTRYPOINT_ADDRESS_V07', () => { - expect(getEntryPointVersion(ENTRYPOINT_ADDRESS_V07)).toBe("v0.7") - }) - }) - - describe("isUserOperationVersion06", () => { - test('should return true for ENTRYPOINT_ADDRESS_V06 and UserOperation<"v0.6">', () => { - const userOperation = {} as any // mock UserOperation<"v0.6"> - expect( - isUserOperationVersion06(ENTRYPOINT_ADDRESS_V06, userOperation) - ).toBe(true) - }) - - test('should return false for ENTRYPOINT_ADDRESS_V07 and UserOperation<"v0.6">', () => { - const userOperation = {} as any // mock UserOperation<"v0.6"> - expect( - isUserOperationVersion06(ENTRYPOINT_ADDRESS_V07, userOperation) - ).toBe(false) - }) - }) - - describe("isUserOperationVersion07", () => { - test('should return false for ENTRYPOINT_ADDRESS_V06 and UserOperation<"v0.6">', () => { - const userOperation = {} as any // mock UserOperation<"v0.6"> - expect( - isUserOperationVersion07(ENTRYPOINT_ADDRESS_V06, userOperation) - ).toBe(false) - }) - - test('should return true for ENTRYPOINT_ADDRESS_V07 and UserOperation<"v0.7">', () => { - const userOperation = {} as any // mock UserOperation<"v0.7"> - expect( - isUserOperationVersion07(ENTRYPOINT_ADDRESS_V07, userOperation) - ).toBe(true) - }) - }) -}) diff --git a/packages/permissionless/utils/getEntryPointVersion.ts b/packages/permissionless/utils/getEntryPointVersion.ts deleted file mode 100644 index c6a1e3f0..00000000 --- a/packages/permissionless/utils/getEntryPointVersion.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { - ENTRYPOINT_ADDRESS_V06_TYPE, - ENTRYPOINT_ADDRESS_V07_TYPE, - UserOperation -} from "../types" -import type { EntryPoint, GetEntryPointVersion } from "../types/entrypoint" - -export const ENTRYPOINT_ADDRESS_V06: ENTRYPOINT_ADDRESS_V06_TYPE = - "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" -export const ENTRYPOINT_ADDRESS_V07: ENTRYPOINT_ADDRESS_V07_TYPE = - "0x0000000071727De22E5E9d8BAf0edAc6f37da032" - -export const getEntryPointVersion = ( - entryPoint: EntryPoint -): GetEntryPointVersion => - entryPoint === ENTRYPOINT_ADDRESS_V06 ? "v0.6" : "v0.7" - -export function isUserOperationVersion06( - entryPoint: EntryPoint, - _operation: UserOperation<"v0.6"> | UserOperation<"v0.7"> -): _operation is UserOperation<"v0.6"> { - return getEntryPointVersion(entryPoint) === "v0.6" -} - -// Type predicate to check if the UserOperation is V07. -export function isUserOperationVersion07( - entryPoint: EntryPoint, - _operation: UserOperation<"v0.6"> | UserOperation<"v0.7"> -): _operation is UserOperation<"v0.7"> { - return getEntryPointVersion(entryPoint) === "v0.7" -} diff --git a/packages/permissionless/utils/getPackedUserOperation.ts b/packages/permissionless/utils/getPackedUserOperation.ts index 241cebde..9fea8556 100644 --- a/packages/permissionless/utils/getPackedUserOperation.ts +++ b/packages/permissionless/utils/getPackedUserOperation.ts @@ -1,8 +1,10 @@ import { type Hex, concat, getAddress, pad, slice, toHex } from "viem" -import type { UserOperation } from "../types/userOperation" -import type { Hex32, PackedUserOperation } from "../types/userOperation" +import type { + PackedUserOperation, + UserOperation +} from "viem/account-abstraction" -export function getInitCode(unpackedUserOperation: UserOperation<"v0.7">) { +export function getInitCode(unpackedUserOperation: UserOperation<"0.7">) { return unpackedUserOperation.factory ? concat([ unpackedUserOperation.factory, @@ -25,14 +27,14 @@ export function unPackInitCode(initCode: Hex) { } export function getAccountGasLimits( - unpackedUserOperation: UserOperation<"v0.7"> + unpackedUserOperation: UserOperation<"0.7"> ) { return concat([ pad(toHex(unpackedUserOperation.verificationGasLimit), { size: 16 }), pad(toHex(unpackedUserOperation.callGasLimit), { size: 16 }) - ]) as Hex32 + ]) } export function unpackAccountGasLimits(accountGasLimits: Hex) { @@ -42,13 +44,13 @@ export function unpackAccountGasLimits(accountGasLimits: Hex) { } } -export function getGasLimits(unpackedUserOperation: UserOperation<"v0.7">) { +export function getGasLimits(unpackedUserOperation: UserOperation<"0.7">) { return concat([ pad(toHex(unpackedUserOperation.maxPriorityFeePerGas), { size: 16 }), pad(toHex(unpackedUserOperation.maxFeePerGas), { size: 16 }) - ]) as Hex32 + ]) } export function unpackGasLimits(gasLimits: Hex) { @@ -59,7 +61,7 @@ export function unpackGasLimits(gasLimits: Hex) { } export function getPaymasterAndData( - unpackedUserOperation: UserOperation<"v0.7"> + unpackedUserOperation: UserOperation<"0.7"> ) { return unpackedUserOperation.paymaster ? concat([ @@ -104,7 +106,7 @@ export function unpackPaymasterAndData(paymasterAndData: Hex) { } export const getPackedUserOperation = ( - userOperation: UserOperation<"v0.7"> + userOperation: UserOperation<"0.7"> ): PackedUserOperation => { return { sender: userOperation.sender, diff --git a/packages/permissionless/utils/getRequiredPrefund.test.ts b/packages/permissionless/utils/getRequiredPrefund.test.ts index d89d872f..a076f7d1 100644 --- a/packages/permissionless/utils/getRequiredPrefund.test.ts +++ b/packages/permissionless/utils/getRequiredPrefund.test.ts @@ -1,9 +1,5 @@ +import type { UserOperation } from "viem/account-abstraction" import { describe, expect, test } from "vitest" -import type { UserOperation } from "../types/userOperation" -import { - ENTRYPOINT_ADDRESS_V06, - ENTRYPOINT_ADDRESS_V07 -} from "./getEntryPointVersion" import { getRequiredPrefund } from "./getRequiredPrefund" describe("getRequiredPrefund", () => { @@ -17,8 +13,8 @@ describe("getRequiredPrefund", () => { paymasterAndData: "0x" } const result = getRequiredPrefund({ - userOperation: userOperation as UserOperation<"v0.6">, - entryPoint: ENTRYPOINT_ADDRESS_V06 + userOperation: userOperation as UserOperation<"0.6">, + entryPointVersion: "0.6" }) const expectedGas = BigInt(1000) + BigInt(2000) * BigInt(1) + BigInt(500) @@ -35,8 +31,8 @@ describe("getRequiredPrefund", () => { paymasterAndData: "0x1234" } const result = getRequiredPrefund({ - userOperation: userOperation as UserOperation<"v0.6">, - entryPoint: ENTRYPOINT_ADDRESS_V06 + userOperation: userOperation as UserOperation<"0.6">, + entryPointVersion: "0.6" }) const multiplier = BigInt(3) const expectedGas = @@ -44,26 +40,49 @@ describe("getRequiredPrefund", () => { const expectedResult = expectedGas * BigInt(10) expect(result).toBe(expectedResult) }) + }) + describe("v0.7 UserOperation", () => { + test("should calculate the required prefund without paymater gasLimits", () => { + const userOperation = { + callGasLimit: BigInt(1000), + verificationGasLimit: BigInt(2000), + preVerificationGas: BigInt(500), + paymasterVerificationGasLimit: undefined, + paymasterPostOpGasLimit: undefined, + paymaster: undefined, + paymasterData: undefined, + maxFeePerGas: BigInt(10) + } + const result = getRequiredPrefund({ + userOperation: userOperation as UserOperation<"0.7">, + entryPointVersion: "0.7" + }) + const expectedGas = + BigInt(1000) + BigInt(2000) * BigInt(1) + BigInt(500) + const expectedResult = expectedGas * BigInt(10) + expect(result).toBe(expectedResult) + }) - test("should calculate the required prefund with paymaster", () => { + test("should calculate the required prefund with paymaster gasLimits", () => { const userOperation = { callGasLimit: BigInt(1000), verificationGasLimit: BigInt(2000), preVerificationGas: BigInt(500), - maxFeePerGas: BigInt(10), - paymaster: "0xPaymasterAddress", - paymasterPostOpGasLimit: BigInt(100), - paymasterVerificationGasLimit: BigInt(200) + paymasterVerificationGasLimit: BigInt(20), + paymasterPostOpGasLimit: BigInt(30), + paymaster: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + maxFeePerGas: BigInt(10) } const result = getRequiredPrefund({ - userOperation: userOperation as UserOperation<"v0.7">, - entryPoint: ENTRYPOINT_ADDRESS_V07 + userOperation: userOperation as UserOperation<"0.7">, + entryPointVersion: "0.7" }) - const multiplier = BigInt(3) - const verificationGasLimit = - BigInt(2000) + BigInt(100) + BigInt(200) const expectedGas = - BigInt(1000) + verificationGasLimit * multiplier + BigInt(500) + BigInt(1000) + + BigInt(2000) + + BigInt(500) + + BigInt(20) + + BigInt(30) const expectedResult = expectedGas * BigInt(10) expect(result).toBe(expectedResult) }) diff --git a/packages/permissionless/utils/getRequiredPrefund.ts b/packages/permissionless/utils/getRequiredPrefund.ts index e60ed0f1..b90162a4 100644 --- a/packages/permissionless/utils/getRequiredPrefund.ts +++ b/packages/permissionless/utils/getRequiredPrefund.ts @@ -1,9 +1,10 @@ -import type { EntryPoint, GetEntryPointVersion, UserOperation } from "../types" -import { ENTRYPOINT_ADDRESS_V06 } from "./getEntryPointVersion" +import type { UserOperation } from "viem/account-abstraction" -export type GetRequiredPrefundReturnType = { - userOperation: UserOperation> - entryPoint: entryPoint +export type GetRequiredPrefundReturnType< + entryPointVersion extends "0.6" | "0.7" +> = { + userOperation: UserOperation + entryPointVersion: entryPointVersion } /** @@ -20,14 +21,14 @@ export type GetRequiredPrefundReturnType = { * userOperation * }) */ -export const getRequiredPrefund = ({ +export const getRequiredPrefund = ({ userOperation, - entryPoint: entryPointAddress -}: GetRequiredPrefundReturnType): bigint => { - if (entryPointAddress === ENTRYPOINT_ADDRESS_V06) { - const userOperationVersion0_6 = userOperation as UserOperation<"v0.6"> + entryPointVersion +}: GetRequiredPrefundReturnType): bigint => { + if (entryPointVersion === "0.6") { + const userOperationVersion0_6 = userOperation as UserOperation<"0.6"> const multiplier = - userOperationVersion0_6.paymasterAndData.length > 2 + (userOperationVersion0_6.paymasterAndData?.length ?? 0) > 2 ? BigInt(3) : BigInt(1) const requiredGas = @@ -40,18 +41,14 @@ export const getRequiredPrefund = ({ ) } - const userOperationV07 = userOperation as UserOperation<"v0.7"> - const multiplier = userOperationV07.paymaster ? BigInt(3) : BigInt(1) - - const verificationGasLimit = - userOperationV07.verificationGasLimit + - (userOperationV07.paymasterPostOpGasLimit || BigInt(0)) + - (userOperationV07.paymasterVerificationGasLimit || BigInt(0)) + const userOperationV07 = userOperation as UserOperation<"0.7"> const requiredGas = + userOperationV07.verificationGasLimit + userOperationV07.callGasLimit + - verificationGasLimit * multiplier + + (userOperationV07.paymasterVerificationGasLimit || 0n) + + (userOperationV07.paymasterPostOpGasLimit || 0n) + userOperationV07.preVerificationGas - return BigInt(requiredGas) * BigInt(userOperationV07.maxFeePerGas) + return requiredGas * userOperationV07.maxFeePerGas } diff --git a/packages/permissionless/utils/getUserOperationHash.ts b/packages/permissionless/utils/getUserOperationHash.ts deleted file mode 100644 index de3ea6e2..00000000 --- a/packages/permissionless/utils/getUserOperationHash.ts +++ /dev/null @@ -1,158 +0,0 @@ -import type { Address, Hash, Hex } from "viem" -import { concat, encodeAbiParameters, keccak256, pad, toHex } from "viem" -import type { EntryPoint, GetEntryPointVersion } from "../types" -import type { UserOperation } from "../types/userOperation" -import { isUserOperationVersion06 } from "./getEntryPointVersion" - -function packUserOp({ - userOperation, - entryPoint: entryPointAddress -}: { - userOperation: UserOperation> - entryPoint: entryPoint -}): Hex { - if (isUserOperationVersion06(entryPointAddress, userOperation)) { - const hashedInitCode = keccak256(userOperation.initCode) - const hashedCallData = keccak256(userOperation.callData) - const hashedPaymasterAndData = keccak256(userOperation.paymasterAndData) - - return encodeAbiParameters( - [ - { type: "address" }, - { type: "uint256" }, - { type: "bytes32" }, - { type: "bytes32" }, - { type: "uint256" }, - { type: "uint256" }, - { type: "uint256" }, - { type: "uint256" }, - { type: "uint256" }, - { type: "bytes32" } - ], - [ - userOperation.sender as Address, - userOperation.nonce, - hashedInitCode, - hashedCallData, - userOperation.callGasLimit, - userOperation.verificationGasLimit, - userOperation.preVerificationGas, - userOperation.maxFeePerGas, - userOperation.maxPriorityFeePerGas, - hashedPaymasterAndData - ] - ) - } - - const hashedInitCode = keccak256( - userOperation.factory && userOperation.factoryData - ? concat([userOperation.factory, userOperation.factoryData]) - : "0x" - ) - const hashedCallData = keccak256(userOperation.callData) - const hashedPaymasterAndData = keccak256( - userOperation.paymaster - ? concat([ - userOperation.paymaster, - pad( - toHex( - userOperation.paymasterVerificationGasLimit || - BigInt(0) - ), - { - size: 16 - } - ), - pad( - toHex(userOperation.paymasterPostOpGasLimit || BigInt(0)), - { - size: 16 - } - ), - userOperation.paymasterData || "0x" - ]) - : "0x" - ) - - return encodeAbiParameters( - [ - { type: "address" }, - { type: "uint256" }, - { type: "bytes32" }, - { type: "bytes32" }, - { type: "bytes32" }, - { type: "uint256" }, - { type: "bytes32" }, - { type: "bytes32" } - ], - [ - userOperation.sender as Address, - userOperation.nonce, - hashedInitCode, - hashedCallData, - concat([ - pad(toHex(userOperation.verificationGasLimit), { - size: 16 - }), - pad(toHex(userOperation.callGasLimit), { size: 16 }) - ]), - userOperation.preVerificationGas, - concat([ - pad(toHex(userOperation.maxPriorityFeePerGas), { - size: 16 - }), - pad(toHex(userOperation.maxFeePerGas), { size: 16 }) - ]), - hashedPaymasterAndData - ] - ) -} - -export type GetUserOperationHashParams = { - userOperation: UserOperation> - entryPoint: entryPoint - chainId: number -} - -/** - * - * Returns user operation hash that is a unique identifier of the user operation. - * - * - Docs: https://docs.pimlico.io/permissionless/reference/utils/getUserOperationHash - * - * @param args: userOperation, entryPoint, chainId as {@link GetUserOperationHashParams} - * @returns userOperationHash as {@link Hash} - * - * @example - * import { getUserOperationHash } from "permissionless/utils" - * - * const userOperationHash = getUserOperationHash({ - * userOperation, - * entryPoint, - * chainId - * }) - * - * // Returns "0xe9fad2cd67f9ca1d0b7a6513b2a42066784c8df938518da2b51bb8cc9a89ea34" - * - */ -export const getUserOperationHash = ({ - userOperation, - entryPoint: entryPointAddress, - chainId -}: GetUserOperationHashParams): Hash => { - const encoded = encodeAbiParameters( - [{ type: "bytes32" }, { type: "address" }, { type: "uint256" }], - [ - keccak256( - packUserOp({ - userOperation, - entryPoint: entryPointAddress - }) - ), - entryPointAddress, - BigInt(chainId) - ] - ) as `0x${string}` - - return keccak256(encoded) -} diff --git a/packages/permissionless/utils/index.ts b/packages/permissionless/utils/index.ts index 55b42674..c65af699 100644 --- a/packages/permissionless/utils/index.ts +++ b/packages/permissionless/utils/index.ts @@ -1,56 +1,26 @@ -import type { Account, Address } from "viem" import { deepHexlify, transactionReceiptStatus } from "./deepHexlify" import { getAddressFromInitCodeOrPaymasterAndData } from "./getAddressFromInitCodeOrPaymasterAndData" import { type GetRequiredPrefundReturnType, getRequiredPrefund } from "./getRequiredPrefund" -import { - type GetUserOperationHashParams, - getUserOperationHash -} from "./getUserOperationHash" import { isSmartAccountDeployed } from "./isSmartAccountDeployed" -import { providerToSmartAccountSigner } from "./providerToSmartAccountSigner" -import { - AccountOrClientNotFoundError, - type SignUserOperationHashWithECDSAParams, - signUserOperationHashWithECDSA -} from "./signUserOperationHashWithECDSA" -import { walletClientToSmartAccountSigner } from "./walletClientToSmartAccountSigner" +import { toOwner } from "./toOwner" -export function parseAccount(account: Address | Account): Account { - if (typeof account === "string") - return { address: account, type: "json-rpc" } - return account -} import { decodeNonce } from "./decodeNonce" import { encodeNonce } from "./encodeNonce" -import { - ENTRYPOINT_ADDRESS_V06, - ENTRYPOINT_ADDRESS_V07, - getEntryPointVersion -} from "./getEntryPointVersion" import { getPackedUserOperation } from "./getPackedUserOperation" export { transactionReceiptStatus, deepHexlify, - getUserOperationHash, getRequiredPrefund, - walletClientToSmartAccountSigner, + toOwner, type GetRequiredPrefundReturnType, - type GetUserOperationHashParams, - signUserOperationHashWithECDSA, - type SignUserOperationHashWithECDSAParams, - AccountOrClientNotFoundError, isSmartAccountDeployed, - providerToSmartAccountSigner, getAddressFromInitCodeOrPaymasterAndData, getPackedUserOperation, - getEntryPointVersion, encodeNonce, - decodeNonce, - ENTRYPOINT_ADDRESS_V06, - ENTRYPOINT_ADDRESS_V07 + decodeNonce } diff --git a/packages/permissionless/utils/isSmartAccountDeployed.ts b/packages/permissionless/utils/isSmartAccountDeployed.ts index b39a4c6c..f8d5ab8b 100644 --- a/packages/permissionless/utils/isSmartAccountDeployed.ts +++ b/packages/permissionless/utils/isSmartAccountDeployed.ts @@ -1,16 +1,13 @@ import type { Address, Client } from "viem" -import { getBytecode } from "viem/actions" +import { getCode } from "viem/actions" export const isSmartAccountDeployed = async ( client: Client, address: Address ): Promise => { - const contractCode = await getBytecode(client, { + const contractCode = await getCode(client, { address: address }) - if ((contractCode?.length ?? 0) > 2) { - return true - } - return false + return Boolean(contractCode) } diff --git a/packages/permissionless/utils/observe.ts b/packages/permissionless/utils/observe.ts deleted file mode 100644 index b4b719a8..00000000 --- a/packages/permissionless/utils/observe.ts +++ /dev/null @@ -1,74 +0,0 @@ -import type { MaybePromise } from "viem/types/utils" - -// biome-ignore lint/suspicious/noExplicitAny: it's a recursive function, so it's hard to type -type Callback = ((...args: any[]) => any) | undefined -type Callbacks = Record - -export const listenersCache = /*#__PURE__*/ new Map< - string, - { id: number; fns: Callbacks }[] ->() -export const cleanupCache = /*#__PURE__*/ new Map void>() - -type EmitFunction = ( - emit: TCallbacks - // biome-ignore lint/suspicious/noConfusingVoidType: -) => MaybePromise void)> - -let callbackCount = 0 - -/** - * @description Sets up an observer for a given function. If another function - * is set up under the same observer id, the function will only be called once - * for both instances of the observer. - */ -export function observe( - observerId: string, - callbacks: TCallbacks, - fn: EmitFunction -) { - const callbackId = ++callbackCount - - const getListeners = () => listenersCache.get(observerId) || [] - - const unsubscribe = () => { - const listeners = getListeners() - listenersCache.set( - observerId, - // biome-ignore lint/suspicious/noExplicitAny: it's a recursive function, so it's hard to type - listeners.filter((cb: any) => cb.id !== callbackId) - ) - } - - const unwatch = () => { - const cleanup = cleanupCache.get(observerId) - if (getListeners().length === 1 && cleanup) cleanup() - unsubscribe() - } - - const listeners = getListeners() - listenersCache.set(observerId, [ - ...listeners, - { id: callbackId, fns: callbacks } - ]) - - if (listeners && listeners.length > 0) return unwatch - - const emit: TCallbacks = {} as TCallbacks - for (const key in callbacks) { - emit[key] = (( - ...args: Parameters> - ) => { - const listeners = getListeners() - if (listeners.length === 0) return - for (const listener of listeners) { - listener.fns[key]?.(...args) - } - }) as TCallbacks[Extract] - } - - const cleanup = fn(emit) - if (typeof cleanup === "function") cleanupCache.set(observerId, cleanup) - - return unwatch -} diff --git a/packages/permissionless/utils/providerToSmartAccountSigner.ts b/packages/permissionless/utils/providerToSmartAccountSigner.ts deleted file mode 100644 index 21584b28..00000000 --- a/packages/permissionless/utils/providerToSmartAccountSigner.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - type EIP1193Provider, - type Hex, - createWalletClient, - custom -} from "viem" -import { walletClientToSmartAccountSigner } from "./walletClientToSmartAccountSigner" - -export const providerToSmartAccountSigner = async ( - provider: EIP1193Provider, - params?: { - signerAddress: Hex - } -) => { - let account: Hex - if (!params) { - try { - ;[account] = await provider.request({ - method: "eth_requestAccounts" - }) - } catch { - ;[account] = await provider.request({ - method: "eth_accounts" - }) - } - } else { - account = params.signerAddress - } - const walletClient = createWalletClient({ - account: account as Hex, - transport: custom(provider) - }) - return walletClientToSmartAccountSigner(walletClient) -} diff --git a/packages/permissionless/utils/signUserOperationHashWithECDSA.ts b/packages/permissionless/utils/signUserOperationHashWithECDSA.ts deleted file mode 100644 index e7ab68cb..00000000 --- a/packages/permissionless/utils/signUserOperationHashWithECDSA.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { - type Account, - BaseError, - type Chain, - type Client, - type Hash, - type Hex, - type Transport -} from "viem" -import type { - EntryPoint, - GetAccountParameterWithClient, - GetEntryPointVersion -} from "../types/" -import type { UserOperation } from "../types/userOperation" -import { parseAccount } from "./" -import { getUserOperationHash } from "./getUserOperationHash" - -export type SignUserOperationHashWithECDSAParams< - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends Account | undefined = Account | undefined -> = GetAccountParameterWithClient & - ( - | { - hash: Hash - userOperation?: undefined - entryPoint?: undefined - chainId?: undefined - } - | { - hash?: undefined - userOperation: UserOperation> - entryPoint: entryPoint - chainId: number - } - ) - -export class AccountOrClientNotFoundError extends BaseError { - override name = "AccountOrClientNotFoundError" - constructor({ docsPath }: { docsPath?: string } = {}) { - super( - [ - "Could not find an Account to execute with this Action.", - "Please provide an Account with the `account` argument on the Action, or by supplying an `account` to the WalletClient." - ].join("\n"), - { - docsPath, - docsSlug: "account" - } - ) - } -} - -/** - * - * Returns signature for user operation. It signs over user operation hash. - * If you have a custom way of signing user operation hash, you can use this function to sign it with ECDSA. - * - * - Docs: https://docs.pimlico.io/permissionless/reference/utils/signUserOperationHashWithECDSA - * - * @param signer: owner as {@link Client} - * @param params: account & (userOperation, entryPoint, chainId) | hash to sign - * @returns signature as {@link Hash} - * - * @example - * import { signUserOperationHashWithECDSA } from "permissionless/utils" - * - * const userOperationSignature = signUserOperationHashWithECDSA(owner, { - * userOperation, - * entryPoint, - * chainId - * }) - * - * // Returns "0x7d9ae17d5e617e4bf3221dfcb69d64d824959e5ae2ef7078c6ddc3a4fe26c8301ab39277c61160dca68ca90071eb449d9fb2fbbc78b3614d9d7282c860270e291c" - * - */ -export const signUserOperationHashWithECDSA = async < - entryPoint extends EntryPoint, - TTransport extends Transport = Transport, - TChain extends Chain | undefined = Chain | undefined, - TAccount extends Account | undefined = Account | undefined ->({ - client, - account: account_ = client?.account, - hash, - userOperation, - chainId, - entryPoint: entryPointAddress -}: SignUserOperationHashWithECDSAParams< - entryPoint, - TTransport, - TChain, - TAccount ->): Promise => { - if (!account_) - throw new AccountOrClientNotFoundError({ - docsPath: - "/permissionless/reference/utils/signUserOperationHashWithECDSA" - }) - - let userOperationHash: Hash - - if (hash) { - userOperationHash = hash - } else { - userOperationHash = getUserOperationHash({ - userOperation, - chainId, - entryPoint: entryPointAddress - }) - } - - const account = parseAccount(account_) - - if (account.type === "local") - return account.signMessage({ - message: { - raw: userOperationHash - } - }) - - if (!client) - throw new AccountOrClientNotFoundError({ - docsPath: - "/permissionless/reference/utils/signUserOperationHashWithECDSA" - }) - - return client.request({ - method: "personal_sign", - params: [userOperationHash, account.address] - }) -} diff --git a/packages/permissionless/utils/toOwner.ts b/packages/permissionless/utils/toOwner.ts new file mode 100644 index 00000000..7cab8742 --- /dev/null +++ b/packages/permissionless/utils/toOwner.ts @@ -0,0 +1,91 @@ +import { + type Account, + type Address, + type Chain, + type EIP1193Provider, + type EIP1193RequestFn, + type EIP1474Methods, + type LocalAccount, + type OneOf, + type Transport, + type WalletClient, + createWalletClient, + custom +} from "viem" +import { toAccount } from "viem/accounts" + +import { signTypedData } from "viem/actions" +import { getAction } from "viem/utils" + +export async function toOwner({ + owner, + address +}: { + owner: OneOf< + | EIP1193Provider + | WalletClient + | LocalAccount + > + address?: Address +}): Promise { + if ("type" in owner && owner.type === "local") { + return owner as LocalAccount + } + + let walletClient: + | WalletClient + | undefined = undefined + + if ("request" in owner) { + if (!address) { + try { + ;[address] = await ( + owner.request as EIP1193RequestFn + )({ + method: "eth_requestAccounts" + }) + } catch { + ;[address] = await ( + owner.request as EIP1193RequestFn + )({ + method: "eth_accounts" + }) + } + } + if (!address) { + // For TS to be happy + throw new Error("address is required") + } + walletClient = createWalletClient({ + account: address, + transport: custom(owner as EIP1193Provider) + }) + } + + if (!walletClient) { + walletClient = owner as WalletClient< + Transport, + Chain | undefined, + Account + > + } + + return toAccount({ + address: walletClient.account.address, + async signMessage({ message }) { + return walletClient.signMessage({ message }) + }, + async signTypedData(typedData) { + return getAction( + walletClient, + signTypedData, + "signTypedData" + )(typedData as any) + }, + async signTransaction(_) { + throw new Error( + "Smart account signer doesn't need to sign transactions" + ) + } + }) +} diff --git a/packages/permissionless/utils/walletClientToSmartAccountSigner.ts b/packages/permissionless/utils/walletClientToSmartAccountSigner.ts deleted file mode 100644 index 6edbdc25..00000000 --- a/packages/permissionless/utils/walletClientToSmartAccountSigner.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { - Account, - Address, - Chain, - Hex, - SignableMessage, - Transport, - TypedData, - TypedDataDefinition, - WalletClient -} from "viem" - -import { signTypedData } from "viem/actions" -import type { SmartAccountSigner } from "../accounts/types" - -export function walletClientToSmartAccountSigner< - TChain extends Chain | undefined = Chain | undefined ->( - walletClient: WalletClient -): SmartAccountSigner<"custom", Address> { - return { - address: walletClient.account.address, - type: "local", - source: "custom", - publicKey: walletClient.account.address, - signMessage: async ({ - message - }: { message: SignableMessage }): Promise => { - return walletClient.signMessage({ message }) - }, - async signTypedData< - const TTypedData extends TypedData | Record, - TPrimaryType extends - | keyof TTypedData - | "EIP712Domain" = keyof TTypedData - >(typedData: TypedDataDefinition) { - return signTypedData( - walletClient, - { - account: walletClient.account, - ...typedData - } - ) - } - } -}