-
Notifications
You must be signed in to change notification settings - Fork 147
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: eip-7702 support in aa-sdk/core, and sma7702 support in account…
…-kit/smart-contracts (#1287) * feat: sma 7702 * feat: adds 7702 support to alchemy signer (#1269) * feat: adds initial impl of signAuthorization * docs: adds signAuthorization documentation * fix: rename var * feat: 7702 progress * feat: update viem and debug 7702 auth * feat: add deterministically deployed demo nft * fix: don't re-sign delegations * feat: update 7702 middleware to higher order function * chore: merge fixes * fix: remove unnecessary console log * fix: correctly await client actions in infra test * fix: correctly encode yParity in zero case * feat: correctly set validation entity in nonce and separate encode actions * feat: consolidate MAv2Base * feat: add unified ma v2 client (#1309) * feat: add unified ma v2 client * chore: update docs * chore: make account source unique * feat: add ma v2 account to useSmartAccountClient hook (#1314) * feat: add unified ma v2 client * chore: make account source unique * feat: add ma v2 account to use smart contract client hook * chore: remove different type names * chore: rename mav2 to ModularAccountV2 * fix: review fix, add common type * fix: typo * fix: remove await * fix: narrow the type instead of doing non null assertion * chore: rebaseme * fix: client type and middleware inclusion * docs: update docs with twoslash * feat: update to new json format for auth * feat: add defaults for useSmartAccountClient to ma v2 (#1328) * fix: fix MAv2 React Hook client creation for 7702 (#1329) * fix: rename MAv2 type to mode, add 7702 middleware for react hook * fix: don't switch MintCard to MAv2 yet * fix: reconnect 7702 account after page refresh * chore: remove comment * docs: regen docs * fix: fix docs * fix: remove unnecessary optional chaining * feat: update eip 7702 auth format * fix: make mode optional in useSmartAccountClient (#1335) * chore: move optional mode into account params (#1339) * chore: move optional mode into account params * chore: removed unused var * fix: make type really optional, add defaults for return type (#1338) * refactor: assert exhaustive account type handling in getSmartAccountClient --------- Co-authored-by: Linna <[email protected]> Co-authored-by: howy <[email protected]> Co-authored-by: jakehobbs <[email protected]>
- Loading branch information
1 parent
df1d99a
commit 8d5501f
Showing
53 changed files
with
1,392 additions
and
368 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { zeroHash } from "viem"; | ||
import { AccountNotFoundError } from "../../errors/account.js"; | ||
import type { UserOperationStruct } from "../../types.js"; | ||
import type { ClientMiddlewareFn } from "../types"; | ||
import { defaultGasEstimator } from "./gasEstimator.js"; | ||
|
||
/** | ||
* A middleware function to estimate the gas usage of a user operation when using an EIP-7702 delegated account. Has an optional custom gas estimator. | ||
* This function is only compatible with accounts using EntryPoint v0.7.0, and the account must have an implementation address defined in `getImplementationAddress()`. | ||
* | ||
* @example | ||
* ```ts twoslash | ||
* import { | ||
* default7702GasEstimator, | ||
* default7702UserOpSigner, | ||
* createSmartAccountClient, | ||
* type SmartAccountClient, | ||
* } from "@aa-sdk/core"; | ||
* import { | ||
* createModularAccountV2, | ||
* type CreateModularAccountV2ClientParams, | ||
* } from "@account-kit/smart-contracts"; | ||
* | ||
* async function createSMA7702AccountClient( | ||
* config: CreateModularAccountV2ClientParams | ||
* ): Promise<SmartAccountClient> { | ||
* const sma7702Account = await createModularAccountV2({ ...config, mode: "7702" }); | ||
* | ||
* return createSmartAccountClient({ | ||
* account: sma7702Account, | ||
* gasEstimator: default7702GasEstimator(config.gasEstimator), | ||
* signUserOperation: default7702UserOpSigner(config.signUserOperation), | ||
* ...config, | ||
* }); | ||
* } | ||
* ``` | ||
* | ||
* @param {ClientMiddlewareFn} [gasEstimator] Optional custom gas estimator function | ||
* @returns {Function} A function that takes user operation struct and parameters, estimates gas usage, and returns the user operation with gas limits. | ||
*/ | ||
export const default7702GasEstimator: ( | ||
gasEstimator?: ClientMiddlewareFn | ||
) => ClientMiddlewareFn = | ||
(gasEstimator?: ClientMiddlewareFn) => async (struct, params) => { | ||
const gasEstimator_ = gasEstimator ?? defaultGasEstimator(params.client); | ||
|
||
const account = params.account ?? params.client.account; | ||
if (!account) { | ||
throw new AccountNotFoundError(); | ||
} | ||
|
||
const entryPoint = account.getEntryPoint(); | ||
if (entryPoint.version !== "0.7.0") { | ||
throw new Error( | ||
"This middleware is only compatible with EntryPoint v0.7.0" | ||
); | ||
} | ||
|
||
const implementationAddress = await account.getImplementationAddress(); | ||
|
||
// Note: does not omit the delegation from estimation if the account is already 7702 delegated. | ||
|
||
(struct as UserOperationStruct<"0.7.0">).eip7702Auth = { | ||
chainId: "0x0", | ||
nonce: "0x0", | ||
address: implementationAddress, | ||
r: zeroHash, // aka `bytes32(0)` | ||
s: zeroHash, | ||
yParity: "0x0", | ||
}; | ||
|
||
const estimatedUO = await gasEstimator_(struct, params); | ||
|
||
estimatedUO.eip7702Auth = undefined; // Strip out the auth after estimation. | ||
|
||
return estimatedUO; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import { toHex } from "viem"; | ||
import { isSmartAccountWithSigner } from "../../account/smartContractAccount.js"; | ||
import { AccountNotFoundError } from "../../errors/account.js"; | ||
import { ChainNotFoundError } from "../../errors/client.js"; | ||
import type { ClientMiddlewareFn } from "../types"; | ||
import { defaultUserOpSigner } from "./userOpSigner.js"; | ||
|
||
/** | ||
* Provides a default middleware function for signing user operations with a client account when using EIP-7702 delegated accounts. | ||
* If the signer doesn't support `signAuthorization`, then this just runs the provided `signUserOperation` middleware. | ||
* This function is only compatible with accounts using EntryPoint v0.7.0, and the account must have an implementation address defined in `getImplementationAddress()`. | ||
* | ||
* @example | ||
* ```ts twoslash | ||
* import { | ||
* default7702GasEstimator, | ||
* default7702UserOpSigner, | ||
* createSmartAccountClient, | ||
* type SmartAccountClient, | ||
* } from "@aa-sdk/core"; | ||
* import { | ||
* createModularAccountV2, | ||
* type CreateModularAccountV2ClientParams, | ||
* } from "@account-kit/smart-contracts"; | ||
* | ||
* async function createSMA7702AccountClient( | ||
* config: CreateModularAccountV2ClientParams | ||
* ): Promise<SmartAccountClient> { | ||
* const sma7702Account = await createModularAccountV2({ ...config, mode: "7702" }); | ||
* | ||
* return createSmartAccountClient({ | ||
* account: sma7702Account, | ||
* gasEstimator: default7702GasEstimator(config.gasEstimator), | ||
* signUserOperation: default7702UserOpSigner(config.signUserOperation), | ||
* ...config, | ||
* }); | ||
* } | ||
* ``` | ||
* | ||
* @param {ClientMiddlewareFn} [userOpSigner] Optional user operation signer function | ||
* @returns {Function} A middleware function that signs EIP-7702 authorization tuples if necessary, and also uses the provided or default user operation signer to generate the user op signature. | ||
*/ | ||
export const default7702UserOpSigner: ( | ||
userOpSigner?: ClientMiddlewareFn | ||
) => ClientMiddlewareFn = | ||
(userOpSigner?: ClientMiddlewareFn) => async (struct, params) => { | ||
const userOpSigner_ = userOpSigner ?? defaultUserOpSigner; | ||
|
||
const uo = await userOpSigner_(struct, params); | ||
|
||
const account = params.account ?? params.client.account; | ||
const { client } = params; | ||
|
||
if (!account || !isSmartAccountWithSigner(account)) { | ||
throw new AccountNotFoundError(); | ||
} | ||
|
||
const signer = account.getSigner(); | ||
|
||
if (!signer.signAuthorization) { | ||
return uo; | ||
} | ||
|
||
if (!client.chain) { | ||
throw new ChainNotFoundError(); | ||
} | ||
|
||
const code = (await client.getCode({ address: account.address })) ?? "0x"; | ||
|
||
const implAddress = await account.getImplementationAddress(); | ||
|
||
const expectedCode = "0xef0100" + implAddress.slice(2); | ||
|
||
if (code.toLowerCase() === expectedCode.toLowerCase()) { | ||
// If the delegation already matches the expected, then we don't need to sign and include an authorization tuple. | ||
return uo; | ||
} | ||
|
||
const accountNonce = await params.client.getTransactionCount({ | ||
address: account.address, | ||
}); | ||
|
||
const authSignature = await signer.signAuthorization({ | ||
chainId: client.chain.id, | ||
contractAddress: implAddress, | ||
nonce: accountNonce, | ||
}); | ||
|
||
const { r, s } = authSignature; | ||
|
||
const yParity = authSignature.yParity ?? authSignature.v - 27n; | ||
|
||
return { | ||
...uo, | ||
eip7702Auth: { | ||
// deepHexlify doesn't encode number(0) correctly, it returns "0x" | ||
chainId: toHex(client.chain.id), | ||
nonce: toHex(accountNonce), | ||
address: implAddress, | ||
r, | ||
s, | ||
yParity: toHex(yParity), | ||
}, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.