Skip to main content

Deploy a delegator account

A delegator smart contract account must be deployed to the blockchain, just like any other smart contract. It can be deployed either just-in-time (when sending a user operation) or ahead of time.

The DeleGatorClient that you use to interact with the account must be configured correctly with isAccountDeployed on the account.

Counterfactual accounts

A delegator smart contract account is considered "counterfactual" until it is deployed. This means that you know where the account will be deployed in the future, so you can do some things you would normally do with an account (such as send tokens or NFTs) before actually deploying the account.

Prerequisites

Deploy just in time

When sending your first user operation, you can deploy the smart contract account as part of processing the user operation. This happens via the user operation's factory and factoryData fields.

The MetaMask Delegation Toolkit generates these fields automatically when you create a user operation (as long as the DeleGatorClient is created with isAccountDeployed set to false). For example:

const client = createDeleGatorClient({
transport,
chain,
account: {
implementation: Implementation.Hybrid,
deployParams: [owner.address, [], [], []],
deploySalt,
// isAccountDeployed must be false in order for factory and factoryData to be set on the user operation.
isAccountDeployed: false,
signatory: owner,
},
});

const userOperation = await delegatorClient.createAndSignExecuteUserOp({
to,
value,
data,
});

// userOperation.factory and userOperation.factoryData are automatically set.

// todo: submit the userOperation to the bundler, and wait for inclusion.

const deployedClient = client.toDeployedClient();

See Send a user operation to learn how to submit the user operation to a bundler.

note

You can deploy the account ahead of time, using this mechanism, by sending a "no action" user operation:

const action = createAction(zeroAddress);
const deployAccountUserOperation = await delegatorClient.createAndSignExecuteUserOp(action);

Deploy ahead of time

You can deploy a delegator smart contract account directly using a traditional transaction (or a user operation from a different smart contract account). To do so, get the factoryData from the DeleGatorClient and submit a transaction that will execute that factoryData against the SimpleFactory contract.

note

The following example assumes that a Viem Wallet Client and Public Client are correctly configured.

const client = createDeleGatorClient({
transport,
chain,
account: {
implementation: Implementation.Hybrid,
deployParams: [owner.address, [], [], []],
deploySalt,
// isAccountDeployed must be false in order for factoryData to be set on account.
isAccountDeployed: false,
signatory: owner,
},
});

const hash = await walletClient.sendTransaction({
to: client.environment.SimpleFactory
data: client.account.factoryData,
});

await publicClient.waitForTransactionReceipt({
hash
});

const deployedClient = client.toDeployedClient();

Create a DeleGatorClient for a deployed account

Once your delegator smart contract account is deployed, you can create a DeleGatorClient directly, with either the deployParams and deploySalt or the address of the smart contract account.

const deployedClient = createDeleGatorClient({
transport,
chain,
account: {
implementation: Implementation.Hybrid,
deployParams: [owner.address, [], [], []],
deploySalt,
isAccountDeployed: true,
signatory: owner,
},
});
note

Once a counterfactual account is deployed (for example, once the account's first user operation is included in the blockchain), you must recreate the DeleGatorClient as a "deployed" client. You can do this using this same example, or using the counterfactualClient.toDeployedClient() convenience function.

Check if a delegator account is deployed

In some cases, you may not be certain whether a delegator account has been deployed or not. You can check the deployment status and create a DeleGatorClient using the following approach:

const address = getDeleGatorAddress(environment, {
implementation: Implementation.Hybrid,
deployParams: [owner.address, [], [], []],
deploySalt,
});
const isAccountDeployed = !!(await publicClient.getCode({ address }));

const client = createDeleGatorClient({
transport,
chain,
environment,
account: {
implementation: Implementation.Hybrid,
deployParams: [owner.address, [], [], []],
deploySalt,
isAccountDeployed,
},
});