wallet_prepareCalls
Prepares a call bundle. The calls are simulated and a quote for executing the bundle by the server is provided. The quote lives for a certain amount of time, after which it expires.
PreCalls
PreCalls are calls that are executed prior to pre-verification of the signature of the bundle, and before any payment is made.
Only certain types of calls are allowed in precalls:
- Authorizing a key (
Delegation.authorize
) - Revoking a key (
Delegation.revoke
) - Setting call permissions on a key (
Delegation.setCanExecute
) - Setting spend limits on a key (
Delegation.setSpendLimit
) - Removing spend limits on keys (
Delegation.removeSpendLimit
) - Upgrading the delegation (
Delegation.upgradeProxyDelegation
)
PreCalls have their own signatures, and they must be signed with a key that is already attached to the account. The from
and key
fields can be omitted when building a precall (precall: true
).
More details about preCalls can be found in the Orchestrator documentation
Fees
Execution of bundles are paid for by the fee payer, which defaults to the EOA. This can be overriden using feePayer
.
Fees are paid in feeToken
, which is specified in the capabilities of wallet_prepareCalls
. The fee token must be supported on the target chain. The list of supported tokens on each network can be found in the response of wallet_getCapabilities
.
Keys
There exists three different key roles:
- Admin keys are capable of adding and modifying other keys, and capable of spending an unlimited amount of tokens and calling any contract and selector.
- Normal keys can only call contracts as defined by the permissions set on it, and spend the amount of tokens afforded to it by permissions.
- Session keys are like normal keys, except they also have an expiry.
Setting permissions on an admin key is not allowed and will return an error.
For complete details on keys, including their signature encoding, public key encoding, and key hashes, refer to the Key section.
Selectors
Selectors for call permissions can either be a 4-byte selector, e.g. 0x12345678
, or a Solidity-style selector, like transfer(address,uint256)
.
cast sig "transfer(address,uint256)"
# 0xa9059cbb
Request
The request consists of:
- A set of calls (
calls
) - The sender of the intent (
from
) - Optional supported capabilities, such as key additions (
capabilities.authorizeKeys
), key revocations (capabilities.revokeKeys
), and precalls (capabilities.preCalls
).
The fee token the user wishes to pay for execution of the intent is specified in capabilities.meta.feeToken
. It is also possible to specify a nonce here. If no nonce is specified, the latest on-chain nonce for the EOA will be used.
The key the user wishes to sign the intent with is specified in key
.
type Request = {
method: 'wallet_prepareCalls',
params: [{
calls: {
to: Address,
value: Hex,
bytes: Hex,
}[],
chainId: Hex,
// The address of the account sending the bundle.
// It can be omitted in the case of a precall, see "PreCalls".
from?: Address,
capabilities: {
authorizeKeys: {
// See "Keys"
key: {
expiry?: number,
type: 'p256' | 'webauthnp256' | 'secp256k1',
role: 'admin' | 'normal' | 'session',
publicKey: Hex,
},
permissions: ({
type: 'call',
// See "Selectors"
selector: string,
to: Address,
} | {
type: 'spend',
limit: number,
period: 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year',
// defaults to the native token (address zero)
token?: Address,
})[],
}[],
meta: {
// The account that pays fees for this bundle.
// Defaults to the account the bundle is for.
//
// See "Fees".
feePayer?: Address,
feeToken: Address,
nonce?: Hex,
},
// Set of keys to revoke.
revokeKeys: {
hash: Hash,
id?: Address,
}[],
// See "PreCalls"
preCalls: {
eoa: Address,
executionData: Hex,
nonce: Hex,
signature: Hex,
}[],
preCall?: boolean,
},
// The key that will be used to sign the bundle. See "Keys".
//
// It can be omitted in the case of a precall, see "PreCalls".
key?: {
type: 'p256' | 'webauthnp256' | 'secp256k1',
publicKey: Hex,
// Whether the bundle digest will be prehashed by the key.
prehash: boolean,
},
}],
}
Response
The response is primarily a context
object which can either be: a quote, which is the price at which the RPC server will execute the intent for, with a deadline; or a built precall, in case wallet_prepareCalls
was called to build a precall.
The user should sign digest
with a key associated with their account. The signed digest and the context
is then passed to wallet_sendPreparedCalls
, if the user wants the RPC server to execute the intent.
The payment fields in context.quote.op
that are relevant to figure out what the user will pay are:
prePaymentAmount
/prePaymentMaxAmount
: fees the user will pay before the intent is executedtotalPaymentAmount
/totalPaymentMaxAmount
: the maximum fees the user will pay
If totalPaymentAmount - prePaymentAmount
is non-0, the remainder of the fees will be paid after the intent is executed.
type Response = {
// The context returned depending on whether
// a precall or normal bundle was prepared.
//
// In the case of a precall, the precall itself
// is returned, otherwise a quote signed by the
// relay is returned.
context: {
quote: {
chainId: Hex,
intent: {
eoa: Address,
executionData: Hex,
nonce: Hex,
payer: Address,
paymentToken: Address,
prePaymentMaxAmount: Hex,
totalPaymentMaxAmount: Hex,
combinedGas: Hex,
encodedPreCalls: Hex[],
prePaymentAmount: Hex,
totalPaymentAmount: Hex,
paymentRecipient: Address,
signature: Hex,
paymentSignature: Hex,
supportedAccountImplementation: Address,
},
txGas: Hex,
nativeFeeEstimate: {
maxFeePerGas: number,
maxPriorityFeePerGas: number,
},
// UNIX timestamp the quote expires at.
ttl: number,
authorizationAddress?: Address,
entrypoint: Address,
// The RPC servers signature over the quote.
signature: {
y_parity: boolean,
r: Hex,
s: Hex,
},
// The hash of the quote.
hash: Hash,
},
} | {
preCall: {
eoa: Address,
executionData: Hex,
nonce: Hex,
signature: Hex,
},
},
// the digest of the bundle that the user needs to sign
digest: Hash,
// EIP-712 typed data of the precall or bundle.
typedData: TypedData,
// capabilities assigned to the account
capabilities: {
authorizeKeys: {
// key hash
hash: Hash,
// See "Keys"
key: {
expiry?: number,
type: 'p256' | 'webauthnp256' | 'secp256k1',
role: 'admin' | 'normal' | 'session',
publicKey: Hex,
},
permissions: ({
type: 'call',
// See "Selectors"
selector: string,
to: Address,
} | {
type: 'spend',
limit: number,
period: 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year',
// defaults to the native token (address zero)
token?: Address,
})[],
}[],
// Key revocations.
revokeKeys: {
hash: Hash,
id?: Address
}[],
// Simulated asset diffs, where the first element of the tuple is the recipient or sender.
assetDiff: [
Address,
{
// Omitted if this is the native token.
address?: Address,
decimals?: number,
direction: 'incoming' | 'outgoing',
name?: string,
symbol?: string,
type?: 'erc20' | 'erc721',
uri?: string,
// For ERC721, the asset ID. For ERC20 the value moved.
value: number,
}[]
][],
},
// The key that will be used to sign the bundle. See "Keys".
//
// It can be omitted in the case of a precall, see "PreCalls".
key?: {
type: 'p256' | 'webauthnp256' | 'secp256k1',
publicKey: Hex,
// Whether the bundle digest will be prehashed by the key.
prehash: boolean,
},
}