From d70a53b230ad56a8def874f1d688a59dd4be0c3a Mon Sep 17 00:00:00 2001 From: Conner Swenberg Date: Wed, 16 Oct 2024 17:45:42 -0700 Subject: [PATCH 1/4] Add diagrams --- docs/diagrams/approve.md | 23 ++++++++++++++++++ docs/diagrams/approveWithSignature.md | 24 +++++++++++++++++++ docs/diagrams/requestSpendPermission.md | 32 +++++++++++++++++++++++++ docs/diagrams/revoke.md | 21 ++++++++++++++++ docs/diagrams/spend.md | 32 +++++++++++++++++++++++++ 5 files changed, 132 insertions(+) create mode 100644 docs/diagrams/approve.md create mode 100644 docs/diagrams/approveWithSignature.md create mode 100644 docs/diagrams/requestSpendPermission.md create mode 100644 docs/diagrams/revoke.md create mode 100644 docs/diagrams/spend.md diff --git a/docs/diagrams/approve.md b/docs/diagrams/approve.md new file mode 100644 index 0000000..46aaf10 --- /dev/null +++ b/docs/diagrams/approve.md @@ -0,0 +1,23 @@ +# Approve Permissions + +While the default experience is for apps to [request spend permissions](requestSpendPermission.md) and [approve with signatures](./approveWithSignature.md), it can also be valuable to approve permissions via direct calls to `SpendPermissionManager.approve`. For example, paying now to start a subscription and approving a permission to pay the same amount every month. + +```mermaid +sequenceDiagram + autonumber + participant E as Entrypoint + participant A as Account + participant PM as Permission Manager + participant EC as External Contract + + Note over E: Validation phase + E->>A: validateUserOp + A-->>E: validation data + Note over E: Execution phase + E->>A: executeBatch + opt + A->>EC: call + end + A->>PM: approve + Note over A,PM: SpendPermission data +``` diff --git a/docs/diagrams/approveWithSignature.md b/docs/diagrams/approveWithSignature.md new file mode 100644 index 0000000..24f8af0 --- /dev/null +++ b/docs/diagrams/approveWithSignature.md @@ -0,0 +1,24 @@ +# Approve Permission With Signature + +Spenders (apps) approve their permission by calling `SpendPermissionManager.approveWithSignature` using the signature returned from the wallet when [requesting spend permissions](requestSpendPermission.md). + +If the signature is [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492) formatted, `SpendPermissionManager` will automatically detect this and deploy the account on behalf of the app. Afterwards, it will call `isValidSignature` to verify the account signed the permission. + +```mermaid +sequenceDiagram + autonumber + participant S as Spender + participant PM as Permission Manager + participant A as Account + participant F as Factory + + S->>PM: approveWithSignature + Note over PM: validate signature + opt if 6492 initCode + PM->>F: createAccount + F->>A: create2 + end + PM->>A: isValidSignature + A-->>PM: EIP-1271 magic value + Note over PM: revert or store approval +``` diff --git a/docs/diagrams/requestSpendPermission.md b/docs/diagrams/requestSpendPermission.md new file mode 100644 index 0000000..0e9c1d0 --- /dev/null +++ b/docs/diagrams/requestSpendPermission.md @@ -0,0 +1,32 @@ +# Request Spend Permission + +Apps request spend permissions from users by sending an `eth_signTypedData` request containing the permission details. + +Users are guided to sign the permission hash and add the `SpendPermissionManager` contract as an owner if it is not already. + +If a users account is not yet deployed, but has the `SpendPermissionManager` as an initial owner in its `initCode`, the returned signature is formatted according to [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492) with the `CoinbaseSmartWalletFactory` address and this `initCode`. + +This entire process takes place offchain and requires no transactions or network fees. + +```mermaid +sequenceDiagram + autonumber + participant A as App + participant WF as Wallet Frontend + participant U as User + participant WB as Wallet Backend + + A->>WF: eth_signTypedData + WF->>U: approve permission + U-->>WF: signature + WF->>WB: get account status + WB-->>WF: deploy status, initCode, current + pending owners + alt account deployed && manager is owner + else account not deployed && manager in initCode + Note right of WF: wrap signature in ERC-6492 + else manager not in initCode && manager not owner + WF->>U: add manager + U-->>WF: signature + end + WF-->>A: signature +``` diff --git a/docs/diagrams/revoke.md b/docs/diagrams/revoke.md new file mode 100644 index 0000000..5db531c --- /dev/null +++ b/docs/diagrams/revoke.md @@ -0,0 +1,21 @@ +# Revoke Permissions + +Users can revoke permissions at any time by calling `SpendPermissionManager.revoke`, which can also be batched via `CoinbaseSmartWallet.executeBatch`. + +```mermaid +sequenceDiagram + autonumber + participant E as Entrypoint + participant A as Account + participant PM as Permission Manager + + Note over E: Validation phase + E->>A: validateUserOp + A-->>E: validation data + Note over E: Execution phase + E->>A: executeBatch + loop + A->>PM: revoke + Note over A,PM: SpendPermission data + end +``` diff --git a/docs/diagrams/spend.md b/docs/diagrams/spend.md new file mode 100644 index 0000000..9b2b50f --- /dev/null +++ b/docs/diagrams/spend.md @@ -0,0 +1,32 @@ +# Spend Tokens + +Spenders (apps) spend tokens by calling `SpendPermissionManager.spend` with their spend permission values, a recipient, and an amount of tokens to spend. + +Spenders may want to batch this call with an additionally prepended call to [approve their permission via user signature](./approveWithSignature.md) or the convenience function `SpendPermissionManager.spendWithSignature`. After a permission is approved, it is cheaper to avoid re-validating the signature and approval by just calling `SpendPermissionManager.spend` for repeated use. + +When executing a spend, `SpendPermissionManager` calls `CoinbaseSmartWallet.execute` with either a call to transfer native or ERC-20 tokens to the recipient. Only these two kinds of calls are allowed by the `SpendPermissionManager` on a `CoinbaseSmartWallet` for reduced risk. + +```mermaid +sequenceDiagram + autonumber + participant S as Spender + participant PM as Permission Manager + participant A as Account + participant ERC20 + + opt + S->>PM: approveWithSignature + Note over PM: validate signature and store approval + end + S->>PM: spend + Note over PM: validate permission approved
and spend value within allowance + PM->>A: execute + Note over PM,A: transfer tokens + alt token is ERC-7528 address + A->>S: call{value}() + Note over A,S: transfer native token to spender + else else is ERC20 contract + A->>ERC20: transfer(spender, value) + Note over A,ERC20: transfer ERC20 to spender + end +``` From 6cd84d29f4eaad95fd0bd2ede4893b1e57d57663 Mon Sep 17 00:00:00 2001 From: Conner Swenberg Date: Wed, 16 Oct 2024 18:06:20 -0700 Subject: [PATCH 2/4] Update readme --- README.md | 93 +++++++++++++++++++++++++ docs/diagrams/approveWithSignature.md | 2 +- docs/diagrams/requestSpendPermission.md | 3 +- docs/diagrams/spend.md | 4 +- 4 files changed, 97 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 094f351..6c19df3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,96 @@ # Spend Permissions +> :information_source: These contracts are unaudited. Please use at your own risk. + **Spend Permissions enable apps to spend native and ERC-20 tokens on behalf of users.** + +## Design Overview + +### 1. Periphery addition to Coinbase Smart Wallet V1 + +While implementing this feature as a new V2 wallet implementation was tempting, we decided to leverage the modular owner system from [Smart Wallet V1](https://github.com/coinbase/smart-wallet) and avoid a hard upgrade. This helped reduce our launch timeline and also reduced the risk of introducing this unique account authentication paradigm. + +### 2. Only Native and ERC-20 token support + +Spend Permissions only support spending Native (e.g. ETH) and ERC-20 (e.g. USDC) tokens with a recurring allowance refresh. This enables use cases like subscriptins (e.g 10 USDC per month) out of the box and also can support apps that want to limit asking users for spend permissions every session. + +Compared to an approach that enables apps to make arbitrary external calls from user accounts, we consider this implementation safer given the tighter and fully-known scope of account control. + +### 3. Spender-originated calls + +Spend Permissions allow users to delegate token spending to a `spender` address, presumably controlled by the app. When an app wants to spend user tokens, it has `spender` call into the user account through a middleware contract, `SpendPermissionManager`, which validates the spend is within the approved allowance. + +Compared to an approach that uses the ERC-4337 EntryPoint to prompt external calls from user accounts, we consider this implementation safer given the avoided edge case of accounting for when ERC-4337 Paymasters spend user tokens. + +## End-to-end Journey + +### 1. App requests permissions from user (offchain) + +Apps request spend permissions from users by sending an `eth_signTypedData` request containing the permission details. + +```mermaid +sequenceDiagram + autonumber + participant A as App + participant WF as Wallet Frontend + participant U as User + participant WB as Wallet Backend + + A->>WF: eth_signTypedData + WF->>U: approve permission + U-->>WF: signature + WF->>WB: get account status + WB-->>WF: deploy status, initCode, current + pending owners + alt account not deployed && manager in initCode + Note right of WF: wrap signature in ERC-6492 + else manager not in initCode && manager not owner + WF->>U: add manager + U-->>WF: signature + end + WF-->>A: signature +``` + +### 2. App approves and spends (onchain) + +Apps approve their permission by calling `SpendPermissionManager.approveWithSignature` using the signature returned from the wallet when [requesting spend permissions](requestSpendPermission.md). + +```mermaid +sequenceDiagram + autonumber + participant S as Spender + participant PM as Permission Manager + participant A as Account + participant F as Factory + + S->>PM: approveWithSignature + Note over PM: validate signature + opt if 6492 initCode + PM->>F: createAccount + F->>A: create2 + end + PM->>A: isValidSignature + A-->>PM: EIP-1271 magic value + Note over PM: revert or store approval +``` + +### 3. User revokes permission (onchain) + +Users can revoke permissions at any time by calling `SpendPermissionManager.revoke`, which can also be batched via `CoinbaseSmartWallet.executeBatch`. + +```mermaid +sequenceDiagram + autonumber + participant E as Entrypoint + participant A as Account + participant PM as Permission Manager + + Note over E: Validation phase + E->>A: validateUserOp + A-->>E: validation data + Note over E: Execution phase + E->>A: executeBatch + loop + A->>PM: revoke + Note over A,PM: SpendPermission data + end +``` diff --git a/docs/diagrams/approveWithSignature.md b/docs/diagrams/approveWithSignature.md index 24f8af0..30ef582 100644 --- a/docs/diagrams/approveWithSignature.md +++ b/docs/diagrams/approveWithSignature.md @@ -1,6 +1,6 @@ # Approve Permission With Signature -Spenders (apps) approve their permission by calling `SpendPermissionManager.approveWithSignature` using the signature returned from the wallet when [requesting spend permissions](requestSpendPermission.md). +Apps approve their permission by calling `SpendPermissionManager.approveWithSignature` using the signature returned from the wallet when [requesting spend permissions](requestSpendPermission.md). If the signature is [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492) formatted, `SpendPermissionManager` will automatically detect this and deploy the account on behalf of the app. Afterwards, it will call `isValidSignature` to verify the account signed the permission. diff --git a/docs/diagrams/requestSpendPermission.md b/docs/diagrams/requestSpendPermission.md index 0e9c1d0..5ef377b 100644 --- a/docs/diagrams/requestSpendPermission.md +++ b/docs/diagrams/requestSpendPermission.md @@ -21,8 +21,7 @@ sequenceDiagram U-->>WF: signature WF->>WB: get account status WB-->>WF: deploy status, initCode, current + pending owners - alt account deployed && manager is owner - else account not deployed && manager in initCode + alt account not deployed && manager in initCode Note right of WF: wrap signature in ERC-6492 else manager not in initCode && manager not owner WF->>U: add manager diff --git a/docs/diagrams/spend.md b/docs/diagrams/spend.md index 9b2b50f..7147739 100644 --- a/docs/diagrams/spend.md +++ b/docs/diagrams/spend.md @@ -25,8 +25,8 @@ sequenceDiagram alt token is ERC-7528 address A->>S: call{value}() Note over A,S: transfer native token to spender - else else is ERC20 contract + else else is ERC-20 contract A->>ERC20: transfer(spender, value) - Note over A,ERC20: transfer ERC20 to spender + Note over A,ERC20: transfer ERC-20 to spender end ``` From 69376181482f6fa0bfb91ce9dd35f7c3dbf67ae1 Mon Sep 17 00:00:00 2001 From: Conner Swenberg Date: Wed, 16 Oct 2024 19:00:32 -0700 Subject: [PATCH 3/4] Add Spend Permission Accounting docs --- docs/SpendPermissionAccounting.md | 66 +++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 docs/SpendPermissionAccounting.md diff --git a/docs/SpendPermissionAccounting.md b/docs/SpendPermissionAccounting.md new file mode 100644 index 0000000..d45611d --- /dev/null +++ b/docs/SpendPermissionAccounting.md @@ -0,0 +1,66 @@ +# Spend Permission Accounting + +## Onchain Accounting + +Tracking asset expenditure accurately is important for enforcing user-approved limits. Given the fragility and complications with doing this accounting offchain and the severity of inaccuracies, we designed for fully onchain accounting. Onchain accounting enables us to have higher confidence in its reliability and keep our system trust-minimized. +The accounting for each spend permission is stored in a single slot, via a mapping keyed by the hash of its struct values. +A spend permission contains 3 entity values: + +1. `account`: user whose tokens will be spent +1. `spender`: app who is able to spend tokens +1. `token`: ERC-20 contract or ERC-7528 native token + +A spend permission contains 4 accounting values: + +1. `start`: time this permission is valid starting at +1. `end`: time this permission is valid until +1. `period`: duration of a recurring interval that resets the `spender`'s allowance +1. `allowance`: amount of tokens spendable per period + +### Recurring Accounting + +Spend permissions allow an app to request to spend user assets on a recurring basis (e.g. 10 USDC / month). As apps spend user assets, the recurring logic automatically increments and enforces the allowance for the current period. Once enough time passes to enter the next period, the allowance usage is reset to zero and the app can keep spending up to the same allowance. + +This design allows users and apps to have reduced friction in approving asset use, while still giving the user control to manage risk and keep asset allowance small upfront. This design is also intuitive for users and can easily support recurring models like subscriptions, automated trading strategies, and payroll. + +The start time and period period set a deterministic schedule infinitely into the future for when allowances reset to zero for the next period. The end time enforces when the permission can no longer be used and does not have to correlate with a clean period boundary. +Consider this example configuration: + +``` +start = 0 +end = 1000 +period = 100 +allowance = 100 +``` + +This configuration would produce the following period-size ranges each with their own allowance: + +``` +[0, 99], [100, 199], [200, 299], ... +``` + +When a new spend is attempted, the contract first determines what the current period range is. If the current time falls within the period of last stored use, we simply check if this new usage will exceed the allowance. + +``` +t = 0, spend = 25 +period = [0, 99] +allowance = 0 + 25 = 25, +overspend = 25 > 100 = false +t = 10, spend = 25 +period = [0, 99] +allowance = 25 + 25 = 50 +overspend = 50 > 100 = false +``` + +If the current time exceeds the period of last stored use, that means we are in a new period and should reset the allowance to zero and then add our new attempted spend. + +``` +t = 0, spend = 25 +period = [0, 99] +allowance = 0 + 25 = 25, +overspend = 25 > 100 = false +t = 110, spend = 25 +period = [100, 199] +allowance = 0 + 25 = 25 +overspend = 25 > 100 = false +``` From efe6e25430f630d4fde7edbda89832c7d58a4964 Mon Sep 17 00:00:00 2001 From: Conner Swenberg Date: Wed, 16 Oct 2024 19:14:30 -0700 Subject: [PATCH 4/4] Update docs --- README.md | 48 ++++++++++++------- docs/SpendPermissionAccounting.md | 10 ++-- docs/diagrams/approve.md | 2 +- docs/diagrams/approveWithSignature.md | 2 +- ...ndPermission.md => signSpendPermission.md} | 4 +- 5 files changed, 41 insertions(+), 25 deletions(-) rename docs/diagrams/{requestSpendPermission.md => signSpendPermission.md} (86%) diff --git a/README.md b/README.md index 6c19df3..52078a3 100644 --- a/README.md +++ b/README.md @@ -8,25 +8,27 @@ ### 1. Periphery addition to Coinbase Smart Wallet V1 -While implementing this feature as a new V2 wallet implementation was tempting, we decided to leverage the modular owner system from [Smart Wallet V1](https://github.com/coinbase/smart-wallet) and avoid a hard upgrade. This helped reduce our launch timeline and also reduced the risk of introducing this unique account authentication paradigm. +While implementing this feature as a new V2 wallet implementation was tempting, we decided to leverage the modular owner system from [Smart Wallet V1](https://github.com/coinbase/smart-wallet) and avoid a hard upgrade. ### 2. Only Native and ERC-20 token support -Spend Permissions only support spending Native (e.g. ETH) and ERC-20 (e.g. USDC) tokens with a recurring allowance refresh. This enables use cases like subscriptins (e.g 10 USDC per month) out of the box and also can support apps that want to limit asking users for spend permissions every session. +Spend Permissions only supports spending Native (e.g. ETH) and ERC-20 (e.g. USDC) tokens on a recurring period. This enables use cases like subscriptions out of the box (e.g 10 USDC per month) and also can support apps that want to avoid asking users for spend permissions every session. -Compared to an approach that enables apps to make arbitrary external calls from user accounts, we consider this implementation safer given the tighter and fully-known scope of account control. +This approach does **not** enable apps to make arbitrary external calls from user accounts, improving security by having a tighter and fully-known scope of account control. ### 3. Spender-originated calls -Spend Permissions allow users to delegate token spending to a `spender` address, presumably controlled by the app. When an app wants to spend user tokens, it has `spender` call into the user account through a middleware contract, `SpendPermissionManager`, which validates the spend is within the approved allowance. +Spend Permissions allow users to delegate token spending to a `spender` address, presumably controlled by the app. When an app wants to spend user tokens, it calls into `SpendPermissionManager` from this `spender` address. `SpendPermissionManager` will then validate the spend is within the approved permission's allowance and calls into the user's account to transfer tokens. -Compared to an approach that uses the ERC-4337 EntryPoint to prompt external calls from user accounts, we consider this implementation safer given the avoided edge case of accounting for when ERC-4337 Paymasters spend user tokens. +This approach does **not** use the ERC-4337 EntryPoint to prompt external calls from user accounts, improving security by avoiding the possibility of ERC-4337 Paymasters spending users' tokens on gas fees. ## End-to-end Journey -### 1. App requests permissions from user (offchain) +### 1. App requests and user signs permissions (offchain) -Apps request spend permissions from users by sending an `eth_signTypedData` request containing the permission details. +Apps request spend permissions for users to sign by sending an `eth_signTypedData` RPC request containing the permission details. + +Read more details [here](./docs/diagrams/signSpendPermission.md). ```mermaid sequenceDiagram @@ -52,7 +54,11 @@ sequenceDiagram ### 2. App approves and spends (onchain) -Apps approve their permission by calling `SpendPermissionManager.approveWithSignature` using the signature returned from the wallet when [requesting spend permissions](requestSpendPermission.md). +Spenders (apps) spend tokens by calling `SpendPermissionManager.spend` with their spend permission values, a recipient, and an amount of tokens to spend. + +Spenders may want to batch this call with an additionally prepended call to [approve their permission via user signature](./approveWithSignature.md) or the convenience function `SpendPermissionManager.spendWithSignature`. + +Read more details [here](./docs/diagrams/spend.md). ```mermaid sequenceDiagram @@ -60,23 +66,31 @@ sequenceDiagram participant S as Spender participant PM as Permission Manager participant A as Account - participant F as Factory + participant ERC20 - S->>PM: approveWithSignature - Note over PM: validate signature - opt if 6492 initCode - PM->>F: createAccount - F->>A: create2 + opt + S->>PM: approveWithSignature + Note over PM: validate signature and store approval + end + S->>PM: spend + Note over PM: validate permission approved
and spend value within allowance + PM->>A: execute + Note over PM,A: transfer tokens + alt token is ERC-7528 address + A->>S: call{value}() + Note over A,S: transfer native token to spender + else else is ERC-20 contract + A->>ERC20: transfer(spender, value) + Note over A,ERC20: transfer ERC-20 to spender end - PM->>A: isValidSignature - A-->>PM: EIP-1271 magic value - Note over PM: revert or store approval ``` ### 3. User revokes permission (onchain) Users can revoke permissions at any time by calling `SpendPermissionManager.revoke`, which can also be batched via `CoinbaseSmartWallet.executeBatch`. +Read more details [here](./docs/diagrams/revoke.md). + ```mermaid sequenceDiagram autonumber diff --git a/docs/SpendPermissionAccounting.md b/docs/SpendPermissionAccounting.md index d45611d..f689dd7 100644 --- a/docs/SpendPermissionAccounting.md +++ b/docs/SpendPermissionAccounting.md @@ -42,11 +42,12 @@ This configuration would produce the following period-size ranges each with thei When a new spend is attempted, the contract first determines what the current period range is. If the current time falls within the period of last stored use, we simply check if this new usage will exceed the allowance. ``` -t = 0, spend = 25 +new spend=25 @ t=0 period = [0, 99] allowance = 0 + 25 = 25, overspend = 25 > 100 = false -t = 10, spend = 25 + +new spend=25 @ t=50 period = [0, 99] allowance = 25 + 25 = 50 overspend = 50 > 100 = false @@ -55,11 +56,12 @@ overspend = 50 > 100 = false If the current time exceeds the period of last stored use, that means we are in a new period and should reset the allowance to zero and then add our new attempted spend. ``` -t = 0, spend = 25 +new spend=25 @ t=0 period = [0, 99] allowance = 0 + 25 = 25, overspend = 25 > 100 = false -t = 110, spend = 25 + +new spend=25 @ t=150 period = [100, 199] allowance = 0 + 25 = 25 overspend = 25 > 100 = false diff --git a/docs/diagrams/approve.md b/docs/diagrams/approve.md index 46aaf10..b8752cb 100644 --- a/docs/diagrams/approve.md +++ b/docs/diagrams/approve.md @@ -1,6 +1,6 @@ # Approve Permissions -While the default experience is for apps to [request spend permissions](requestSpendPermission.md) and [approve with signatures](./approveWithSignature.md), it can also be valuable to approve permissions via direct calls to `SpendPermissionManager.approve`. For example, paying now to start a subscription and approving a permission to pay the same amount every month. +While the default experience is for apps to request the user [sign spend permissions](signSpendPermission.md) and [approve with signatures](./approveWithSignature.md), it can also be valuable to approve permissions via direct calls to `SpendPermissionManager.approve`. For example, paying now to start a subscription and approving a permission to pay the same amount every month. ```mermaid sequenceDiagram diff --git a/docs/diagrams/approveWithSignature.md b/docs/diagrams/approveWithSignature.md index 30ef582..3c41bd4 100644 --- a/docs/diagrams/approveWithSignature.md +++ b/docs/diagrams/approveWithSignature.md @@ -1,6 +1,6 @@ # Approve Permission With Signature -Apps approve their permission by calling `SpendPermissionManager.approveWithSignature` using the signature returned from the wallet when [requesting spend permissions](requestSpendPermission.md). +Apps approve their permission by calling `SpendPermissionManager.approveWithSignature` using the signature returned from the wallet when [signing spend permissions](signSpendPermission.md). If the signature is [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492) formatted, `SpendPermissionManager` will automatically detect this and deploy the account on behalf of the app. Afterwards, it will call `isValidSignature` to verify the account signed the permission. diff --git a/docs/diagrams/requestSpendPermission.md b/docs/diagrams/signSpendPermission.md similarity index 86% rename from docs/diagrams/requestSpendPermission.md rename to docs/diagrams/signSpendPermission.md index 5ef377b..a80011a 100644 --- a/docs/diagrams/requestSpendPermission.md +++ b/docs/diagrams/signSpendPermission.md @@ -1,8 +1,8 @@ -# Request Spend Permission +# Sign Spend Permission Apps request spend permissions from users by sending an `eth_signTypedData` request containing the permission details. -Users are guided to sign the permission hash and add the `SpendPermissionManager` contract as an owner if it is not already. +Users are guided to sign the permission hash and add the `SpendPermissionManager` contract as an owner if it is not already. Signing to approve enables users to not spend gas on this action, offsetting this cost to the app. If a users account is not yet deployed, but has the `SpendPermissionManager` as an initial owner in its `initCode`, the returned signature is formatted according to [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492) with the `CoinbaseSmartWalletFactory` address and this `initCode`.