Skip to content

Commit

Permalink
feat: tweak EIP-7702 types
Browse files Browse the repository at this point in the history
  • Loading branch information
jxom committed Oct 15, 2024
1 parent afd1f78 commit 5c9ed2f
Show file tree
Hide file tree
Showing 10 changed files with 88 additions and 29 deletions.
5 changes: 5 additions & 0 deletions .changeset/neat-knives-cheat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"viem": patch
---

Tweaked EIP-7702 types.
3 changes: 1 addition & 2 deletions site/pages/experimental/eip7702/contract-writes.md
Original file line number Diff line number Diff line change
Expand Up @@ -441,15 +441,14 @@ const batchCallInvoker = getContract({
address: walletClient.account.address,
walletClient,
})

const delegate = privateKeyToAccount('0x...') // [!code ++]

const authorization = await walletClient.signAuthorization({
contractAddress,
delegate, // [!code ++]
})


const hash = await batchCallInvoker.write.execute([[{
data: '0x',
to: '0xcb98643b8786950F0461f3B0edf99D88F274574D',
Expand Down
3 changes: 1 addition & 2 deletions site/pages/experimental/eip7702/sending-transactions.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,15 +341,14 @@ We can also utilize an Delegate Account to execute a call on behalf of the autho
import { encodeFunctionData{ parseEther } from 'viem'
import { walletClient } from './config'
import { contractAddress } from './contract'

const delegate = privateKeyToAccount('0x...') // [!code ++]

const authorization = await walletClient.signAuthorization({
contractAddress,
delegate, // [!code ++]
})


const hash = await walletClient.sendTransaction({
account: delegate, // [!code ++]
authorizationList: [authorization],
Expand Down
19 changes: 19 additions & 0 deletions site/pages/experimental/eip7702/signAuthorization.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,25 @@ const authorization = await walletClient.signAuthorization({
})
```

### delegate (optional)

- **Type:** `true | Address | Account`

Whether the EIP-7702 Transaction will be executed by another Account.

If not specified, it will be assumed that the EIP-7702 Transaction will be executed by the Account that signed the Authorization.

```ts twoslash
import { privateKeyToAccount } from 'viem/accounts'
import { walletClient } from './client'

const authorization = await walletClient.signAuthorization({
account: privateKeyToAccount('0x...'),
contractAddress: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
delegate: true, // [!code focus]
})
```

### nonce (optional)

- **Type:** `Address`
Expand Down
18 changes: 17 additions & 1 deletion src/experimental/eip7702/actions/signAuthorization.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ test('behavior: partial authorization: no chainId', async () => {
).toBe(true)
})

test('behavior: delegate', async () => {
test('behavior: delegate is address', async () => {
const authorization = await signAuthorization(client, {
account,
contractAddress: wagmiContractConfig.address,
Expand All @@ -222,6 +222,22 @@ test('behavior: delegate', async () => {
).toBe(true)
})

test('behavior: delegate is truthy', async () => {
const authorization = await signAuthorization(client, {
account,
contractAddress: wagmiContractConfig.address,
delegate: true,
})

expect(authorization.nonce).toBe(663)
expect(
await verifyAuthorization({
address: account.address,
authorization,
}),
).toBe(true)
})

test('behavior: account as delegate', async () => {
const authorization = await signAuthorization(client, {
account,
Expand Down
20 changes: 16 additions & 4 deletions src/experimental/eip7702/actions/signAuthorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,13 @@ export type SignAuthorizationParameters<
account extends Account | undefined = Account | undefined,
> = GetAccountParameter<account> &
PartialBy<Authorization, 'chainId' | 'nonce'> & {
/** Delegate of the EIP-7702 Authorization Transaction. @default `account` */
delegate?: Address | Account | undefined
/**
* Whether the EIP-7702 Transaction will be executed by another Account.
*
* If not specified, it will be assumed that the EIP-7702 Transaction will
* be executed by the Account that signed the Authorization.
*/
delegate?: true | Address | Account | undefined
}

export type SignAuthorizationReturnType = SignAuthorizationReturnType_account
Expand Down Expand Up @@ -108,7 +113,11 @@ export async function signAuthorization<
})
const account = parseAccount(account_)

const delegate = delegate_ ? parseAccount(delegate_) : undefined
const delegate = (() => {
if (typeof delegate_ === 'boolean') return delegate_
if (delegate_) return parseAccount(delegate_)
return undefined
})()

if (!account.experimental_signAuthorization)
throw new AccountTypeNotSupportedError({
Expand Down Expand Up @@ -139,7 +148,10 @@ export async function signAuthorization<
address: account.address,
blockTag: 'pending',
})
if (!delegate || isAddressEqual(account.address, delegate.address))
if (
!delegate ||
(delegate !== true && isAddressEqual(account.address, delegate.address))
)
authorization.nonce += 1
}

Expand Down
10 changes: 8 additions & 2 deletions src/experimental/eip7702/types/rpc.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Address } from 'abitype'
import type { Hex, Signature } from '../../../types/misc.js'
import type { Hex } from '../../../types/misc.js'

export type RpcAuthorization = {
/** Address of the contract to set as code for the Authority. */
Expand All @@ -8,5 +8,11 @@ export type RpcAuthorization = {
chainId: Hex
/** Nonce of the Authority to authorize. */
nonce: Hex
} & Signature
/** ECDSA r value. */
r: Hex
/** ECDSA s value. */
s: Hex
/** y parity. */
yParity: Hex
}
export type RpcAuthorizationList = readonly RpcAuthorization[]
12 changes: 12 additions & 0 deletions src/types/eip1193.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1780,6 +1780,18 @@ export type WalletRpcSchema = [
Parameters?: WalletSendCallsParameters
ReturnType: string
},
/**
* @description Creates, signs, and sends a new transaction to the network
* @link https://eips.ethereum.org/EIPS/eip-1474
* @example
* provider.request({ method: 'wallet_sendTransaction', params: [{ from: '0x...', to: '0x...', value: '0x...' }] })
* // => '0x...'
*/
{
Method: 'wallet_sendTransaction'
Parameters: [transaction: TransactionRequest]
ReturnType: Hash
},
/**
* @description Requests for the wallet to show information about a call batch
* that was sent via `wallet_sendCalls`.
Expand Down
2 changes: 1 addition & 1 deletion src/types/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ export type TransactionRequestEIP7702<
> = TransactionRequestBase<quantity, index, type> &
ExactPartial<FeeValuesEIP1559<quantity>> & {
accessList?: AccessList | undefined
authorizationList: AuthorizationList<index, boolean>
authorizationList?: AuthorizationList<index, boolean> | undefined
}

export type TransactionRequest<quantity = bigint, index = number> = OneOf<
Expand Down
25 changes: 8 additions & 17 deletions src/utils/formatters/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,21 +127,12 @@ export const defineTransaction = /*#__PURE__*/ defineFormatter(
function formatAuthorizationList(
authorizationList: RpcAuthorizationList,
): SignedAuthorizationList {
return authorizationList.map(
(authorization) =>
({
contractAddress: (authorization as any).address,
r: authorization.r,
s: authorization.s,
chainId: Number(authorization.chainId),
nonce: Number(authorization.nonce),
...(typeof authorization.yParity !== 'undefined'
? { yParity: Number(authorization.yParity) }
: {}),
...(typeof authorization.v !== 'undefined' &&
typeof authorization.yParity === 'undefined'
? { v: Number(authorization.v) }
: {}),
}) as any,
) as SignedAuthorizationList
return authorizationList.map((authorization) => ({
contractAddress: (authorization as any).address,
chainId: Number(authorization.chainId),
nonce: Number(authorization.nonce),
r: authorization.r,
s: authorization.s,
yParity: Number(authorization.yParity),
})) as SignedAuthorizationList
}

0 comments on commit 5c9ed2f

Please sign in to comment.