Skip to main content

Viem client quickstart

This quickstart walks you through:

  • Configuring the Viem client of the MetaMask Delegation Toolkit.
  • Creating a delegator account.
  • Completing the delegation lifecycle (creating, signing, and redeeming a delegation).

Prerequisites

Steps

1. Run the quickstart

In the root of the project, run the quickstart:

yarn dev

By default, the quickstart runs at https://localhost:3000 (it will try other ports if something is already using 3000). Navigate to the Viem client at https://localhost:3000/viem.

The following steps demonstrate how the quickstart creates a Viem client and completes the delegation lifecycle. You can view the project source code on GitHub.

note

In hello-gator, the delegator account is created immediately when the page loads, while the delegate account is only created when the user selects Create "delegator" account. The delegator account must be manually deployed before the delegation can be redeemed, while the delegate account is deployed using factory data on the user operation.

2. Create the client

In src/app/page.tsx, the following code generates a private key, derives the account, and creates a Viem client with specific deployment parameters:

page.tsx
const SALT = "0x1";

// ...

const privateKey = generatePrivateKey();
const owner = privateKeyToAccount(privateKey);

const viemClient = createDeleGatorClient({
transport: http(),
chain,
account: {
implementation: Implementation.Hybrid,
deployParams: [owner.address, [], [], []],
isAccountDeployed: false,
signatory: owner,
deploySalt: SALT,
},
});

You can also use a WalletClient as the signatory:

page.tsx
const SALT = "0x1";

// ...

const privateKey: Hex = generatePrivateKey();
const account = privateKeyToAccount(privateKey);

const owner = createWalletClient({
transport: http(),
account,
});

const viemClient = createDeleGatorClient({
transport: http(),
chain,
account: {
implementation: Implementation.Hybrid,
deployParams: [owner.account.address, [], [], []],
isAccountDeployed: false,
signatory: owner,
deploySalt: SALT,
},
});

3. Create a delegation

The following code creates a root delegation using the createRootDelegation helper, which returns an instance of DelegationStruct. In this example, no caveats are applied, which gives the delegate full control over the new delegator account:

page.tsx
const newDelegation = createRootDelegation(
delegateClient.account.address,
delegatorClient.account.address,
);
info

A root delegation is a delegation that doesn't derive its authority from another delegation – it is the "root" of a chain a delegations. You can also use the helper createDelegation to redelegate a delegation. This requires a third parameter, authority, which is the hash of the delegation from which the new delegation derives its authority.

4. Sign the delegation

The following code signs the delegation using signDelegation, which returns a new instance of the DelegationStruct with the signature field filled in:

page.tsx
const signedDelegation = await delegatorClient.signDelegation(delegation);

5. Redeem the delegation

The following code starts the redemption flow by creating an ActionStruct using createAction to transfer 0 ETH to the delegate. The ActionStruct and delegation is then passed to createRedeemDelegationUserOp, which returns a user operation:

note

gasFee defines the maxFeePerGas and maxPriorityFeePerGas - this can be determined via the PimlicoGasFeeResolver - see the example in Hello Gator.

page.tsx
const action = createAction(delegateClient.account.address);
const gasFee = {
maxFeePerGas: 100n,
maxPriorityFeePerGas: 100n
};

const unsponsoredUserOp = await client.createRedeemDelegationUserOp(
[delegation],
action,
gasFee
);

The following code sponsors the user operation's gas fees using Pimlico's verifying paymaster, and signs the user operation:

page.tsx
const sponsorship = await paymaster.getUserOperationSponsorship(
client.account.environment.EntryPoint,
chain,
unsponsoredUserOp
);

const unsignedUserOp = {
...unsponsoredUserOp,
...sponsorship,
};

const userOp = await client.signUserOp(unsignedUserOp);

The following code sends the user operation to the bundler:

page.tsx
const { result: hash } = await bundler.sendUserOp(
userOp,
client.account.environment.EntryPoint
);

Example

You can view a working example of the Viem client. In addition, you can view the project source code on GitHub.