Skip to content
LogoLogo

accessKey.authorize

Authorizes an access key through a local signing account or a connected wallet. Local accounts submit a transaction; JSON-RPC accounts request a wallet authorization.

Usage

import { Account, Expiry, P256 } from 'viem/tempo'
import { client } from './viem.config'
 
// 1. Define root account
const account = Account.fromSecp256k1('0x...')
 
// 2. Define access key attached to the root account
const accessKey = Account.fromP256(P256.randomPrivateKey(), {
  access: account,
})
 
// 3. Authorize the access key
const { receipt } = await client.accessKey.authorizeSync({
  accessKey,
})
 
console.log('Transaction hash:', receipt.transactionHash)
Transaction hash: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef

Authorize Through a Wallet

For a JSON-RPC account, authorize calls wallet_authorizeAccessKey. Omit accessKey to let the wallet generate and store the key. The wallet returns a signed authorization and its root address; this request does not require a transaction. authorizeSync remains an onchain action.

import { createClient, custom } from 'viem'
import { tempo } from 'viem/chains'
import { Addresses, Expiry, tempoActions } from 'viem/tempo'
 
const client = createClient({
  account: '0x0000000000000000000000000000000000000001',
  chain: tempo,
  transport: custom(window.ethereum),
}).extend(tempoActions())
 
const { keyAuthorization, rootAddress } = await client.accessKey.authorize({
  expiry: Expiry.hours(1),
  limits: [{ token: Addresses.pathUsd, limit: 1_000_000_000n }],
  fundingPolicy: true,
})

fundingPolicy: true asks the wallet to resolve its default funding policy. The wallet must support wallet_authorizeAccessKey; unsupported methods and rejected requests propagate without falling back to transaction submission.

With Expiry and Spending Limits

You can set an expiry and per-token spending limits when authorizing an access key:

import { Account, Expiry, P256 } from 'viem/tempo'
import { client } from './viem.config'
 
const account = Account.fromSecp256k1('0x...')
const accessKey = Account.fromP256(P256.randomPrivateKey(), {
  access: account,
})
 
const { receipt } = await client.accessKey.authorizeSync({
  accessKey,
  expiry: Expiry.hours(1), 
  limits: [ 
    { 
      token: '0x20c0000000000000000000000000000000000001', 
      limit: 1000000n, 
    }, 
  ], 
})

With Periodic Spending Limits

Use the period field on limits to set a recurring spending cap that resets after the given number of seconds:

import { parseUnits } from 'viem'
import { Account, Period, P256 } from 'viem/tempo'
import { client } from './viem.config'
 
const account = Account.fromSecp256k1('0x...')
const accessKey = Account.fromP256(P256.randomPrivateKey(), {
  access: account,
})
 
const { receipt } = await client.accessKey.authorizeSync({
  accessKey,
  limits: [ 
    { 
      token: '0x20c0000000000000000000000000000000000001', 
      limit: parseUnits('1000', 6), 
      period: Period.months(1), // resets every month
    }, 
  ], 
})

With Call Scopes

Use scopes to restrict which contracts and functions the access key can call:

import { parseUnits } from 'viem'
import { Account, P256 } from 'viem/tempo'
import { client } from './viem.config'
 
const account = Account.fromSecp256k1('0x...')
const accessKey = Account.fromP256(P256.randomPrivateKey(), {
  access: account,
})
 
const { receipt } = await client.accessKey.authorizeSync({
  accessKey,
  limits: [
    {
      token: '0x20c0000000000000000000000000000000000001',
      limit: parseUnits('10000', 6),
    },
  ],
  scopes: [ 
    { 
      address: '0x20c0000000000000000000000000000000000001', 
      selector: 'transfer(address,uint256)', // or "0xa9059cbb"
      recipients: ['0xcafebabecafebabecafebabecafebabecafebabe'], // optional
    }, 
  ], 
})

With a Witness

Use witness to bind a 32-byte value into the authorization's signing hash. This lets you bind a single signature to an arbitrary offchain context (e.g. a server-issued challenge), or use it as a revocation handle that can be burned onchain (via accessKey.burnWitness) to invalidate the authorization before it is submitted (TIP-1053):

import { Account, P256 } from 'viem/tempo'
import { client } from './viem.config'
 
const account = Account.fromSecp256k1('0x...')
const accessKey = Account.fromP256(P256.randomPrivateKey(), {
  access: account,
})
 
const { receipt } = await client.accessKey.authorizeSync({
  accessKey,
  witness: '0x...', 
})

Admin Keys

Set admin: true to authorize an unrestricted admin key. Admin keys can manage the account's other access keys and cannot carry expiry, limits, or scopes (they are ignored). Requires the T6 hardfork (TIP-1049):

import { Account, P256 } from 'viem/tempo'
import { client } from './viem.config'
 
const account = Account.fromSecp256k1('0x...')
const accessKey = Account.fromP256(P256.randomPrivateKey(), {
  access: account,
})
 
const { receipt } = await client.accessKey.authorizeSync({
  accessKey,
  admin: true, 
})

Once authorized, an admin key can authorize (and manage) other keys on behalf of the account. Pass the admin key as the account: viem binds the authorization to the parent account and signs it with the admin key directly.

import { Account, P256 } from 'viem/tempo'
import { client } from './viem.config'
 
const account = Account.fromSecp256k1('0x...')
 
// Admin key (previously authorized with `admin: true`)
const adminKey = Account.fromP256(P256.randomPrivateKey(), {
  access: account,
})
 
// New key to authorize on behalf of the account
const childKey = Account.fromP256(P256.randomPrivateKey(), {
  access: account,
})
 
const { receipt } = await client.accessKey.authorizeSync({
  account: adminKey, 
  accessKey: childKey,
})

Authorize Public Keys

Instead of passing an AccessKeyAccount, you can authorize a key by its public key or address directly:

import { Account, P256 } from 'viem/tempo'
import { client } from './viem.config'
 
const account = Account.fromSecp256k1('0x...')
 
// Authorize by public key
const { receipt } = await client.accessKey.authorizeSync({ 
  accessKey: { 
    publicKey: '0x...', 
    type: 'p256', 
  }, 
}) 

You can also authorize by address:

import { Account } from 'viem/tempo'
import { client } from './viem.config'
 
const account = Account.fromSecp256k1('0x...')
 
// Authorize by address
const { receipt } = await client.accessKey.authorizeSync({ 
  accessKey: { 
    address: '0x...', 
    type: 'p256', 
  }, 
}) 

Asynchronous Usage

The example above uses a *Sync variant of the action, that will wait for the transaction to be included before returning.

If you are optimizing for performance, you should use the non-sync accessKey.authorize action and wait for inclusion manually:

import { Actions, Account, Expiry, P256 } from 'viem/tempo'
import { client } from './viem.config'
 
const account = Account.fromSecp256k1('0x...')
const accessKey = Account.fromP256(P256.randomPrivateKey(), {
  access: account,
})
 
const { hash } = await client.accessKey.authorize({
  accessKey,
  expiry: Expiry.hours(1),
})
const receipt = await client.waitForTransactionReceipt({ hash: hash! })
 
const { args }
  = Actions.accessKey.authorize.extractEvent(receipt.logs)

Return Type

authorize always returns the same shape:

type ReturnValue = {
  rootAddress: Address
  keyAuthorization: KeyAuthorization.Signed
  hash: Hex | undefined
}

Local accounts submit the signed authorization and return its transaction hash. JSON-RPC accounts return the wallet's signed authorization with hash: undefined. Numeric authorization fields are decoded.

authorizeSync returns the onchain event and receipt:

type ReturnType = {
  /** The account that authorized the key. */
  account: Address
  /** The public key that was authorized. */
  publicKey: Address
  /** The signature type. */
  signatureType: number
  /** The expiry timestamp. */
  expiry: bigint
  /** Transaction receipt */
  receipt: TransactionReceipt
}

Parameters

accessKey

  • Type: { accessKeyAddress: Address; keyType: string } | { address: Address; type: string } | { publicKey: Hex; type: string }

The access key to authorize. Accepts an AccessKeyAccount, or an object with { address, type } or { publicKey, type }. Optional for JSON-RPC accounts, where omission asks the wallet to generate and store a key.

admin (optional)

  • Type: boolean

Whether to authorize the key as an admin key. Admin keys are unrestricted and can manage the account's other access keys; expiry, limits, and scopes are ignored. Requires the T6 hardfork (TIP-1049).

expiry (optional)

  • Type: number

Unix timestamp when the key expires. Required for JSON-RPC accounts.

fundingPolicy (optional)

  • Type: true | bigint | FundingPolicy.Inline

Use an existing policy ID, an inline { admins, rules } policy, or true to select the funding handler's configured policyId. The default is resolved before signing; explicit IDs and inline policies are unchanged.

The policy's administrators can change its rules for all attached access keys. Relay.funding supplies current registered policyRules when filling payments.

See access key funding for a complete example.

keyType (optional)

  • Type: 'secp256k1' | 'p256' | 'webAuthn'

For JSON-RPC accounts, selects the type of wallet-generated key. Defaults to the wallet's preferred type. A supplied accessKey determines its own key type.

limits (optional)

  • Type: { token: Address; limit: bigint; period?: number }[]

Spending limits per token. Optionally include period (in seconds) to make the limit periodic. It resets after each period. Use Period.months(1), Period.seconds(n), etc. from ox/tempo for convenience.

scopes (optional)

  • Type: { address: Address; selector?: Hex | string; recipients?: Address[] }[]

Call scopes restricting which contracts/selectors this key can call. Each scope entry specifies a contract address, an optional 4-byte function selector, and optional recipient addresses (for transfer-like functions). If scopes is set to [] (empty array), the key cannot make any calls.

witness (optional)

  • Type: Hex

Optional 32-byte witness bound into the authorization's signing hash. Can be burned onchain via accessKey.burnWitness to invalidate the authorization before it is submitted (TIP-1053).

account (optional)

  • Type: Account | Address

Account that sends the transaction. For a coordinated multisig transaction, pass the multisig account.

feeToken (optional)

  • Type: Address | bigint

Fee token for the transaction.

Can be an unpaused USD-denominated TIP-20 token address or ID. Use client.fee.validateToken({ token }) to validate a token before submitting a transaction or setting it as a fee preference.

feePayer (optional)

  • Type: Account | true

Fee payer for the transaction.

Can be a Viem Account, or true if a Fee Payer Service will be used.

gas (optional)

  • Type: bigint

Gas limit for the transaction.

maxFeePerGas (optional)

  • Type: bigint

Max fee per gas for the transaction.

maxPriorityFeePerGas (optional)

  • Type: bigint

Max priority fee per gas for the transaction.

nonce (optional)

  • Type: number

Nonce for the transaction.

nonceKey (optional)

  • Type: 'expiring' | bigint

Nonce key for the transaction. Use 'expiring' to use expiring nonces (TIP-1009), which enables concurrent transaction submission without nonce ordering.

owner (optional)

  • Type: Account | Address

Local owner that signs one coordinated multisig approval.

validBefore (optional)

  • Type: number

Unix timestamp before which the transaction must be included.

validAfter (optional)

  • Type: number

Unix timestamp after which the transaction can be included.

throwOnReceiptRevert (optional)

  • Type: boolean
  • Default: true

Whether to throw an error if the transaction receipt indicates a revert. Only applicable to *Sync actions.