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
- Follow the quickstart setup guide.
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.
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:
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:
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:
const newDelegation = createRootDelegation(
delegateClient.account.address,
delegatorClient.account.address,
[]
);
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:
const signedDelegation = await delegatorClient.signDelegation(delegation);
5. Redeem the delegation
The following code starts the redemption flow by creating an ExecutionStruct
using createExecution
to transfer 0 ETH to the delegate. The ExecutionStruct
, delegation, and mode are used to create a redemption that is then passed to createRedeemDelegationsUserOp
, which returns a user operation:
gasFee
defines the maxFeePerGas
and maxPriorityFeePerGas
- this can be determined via the PimlicoGasFeeResolver
- see the example in Hello Gator.
const execution: ExecutionStruct = createExecution(delegateClient.account.address);
const gasFee = {
maxFeePerGas: 100n,
maxPriorityFeePerGas: 100n
};
const redemption = {
permissionContext: [delegation],
executions: [execution],
mode: SINGLE_DEFAULT_MODE
}
const unsponsoredUserOp = await client.createRedeemDelegationUserOp(
[redemption],
gasFee
);
The following code sponsors the user operation's gas fees using Pimlico's verifying paymaster, and signs the user operation:
const sponsorship = await paymaster.getUserOperationSponsorship(
client.environment.EntryPoint,
chain,
unsponsoredUserOp
);
const unsignedUserOp = {
...unsponsoredUserOp,
...sponsorship,
};
const userOp = await client.signUserOp(unsignedUserOp);
The following code sends the user operation to the bundler:
const { result: hash } = await bundler.sendUserOp(
userOp,
client.environment.EntryPoint
);
You can see more redemption examples in Redeem a delegation.
Example
You can view a working example of the Viem client. In addition, you can view the project source code on GitHub.