Create a social invite link
This tutorial walks you through creating an invite link to enable users to refer their friends to your dapp with minimal friction.
For example, Alice (the inviter) wants Bob (the invitee) to try out your dapp. She sends him a link that allows him to claim 0.001 ETH from her wallet within a time limit. Bob can start using your dapp right away, without installing a wallet or paying gas fees.
You'll enable this by creating a MetaMask smart account to represent the inviter, using a paymaster to abstract gas fees, and creating an open delegation to represent an invitation. This tutorial uses Pimlico's paymaster, but you can use any paymaster of your choice.
Prerequisites
- Get an Infura API key from the MetaMask Developer dashboard.
- Create a Pimlico API key.
Steps
1. Install the Delegation Toolkit
Install the MetaMask Delegation Toolkit in your project:
- npm
- Yarn
- pnpm
- Bun
npm install @metamask/delegation-toolkit
yarn add @metamask/delegation-toolkit
pnpm add @metamask/delegation-toolkit
bun add @metamask/delegation-toolkit
2. Create a Public Client
Create a Viem Public Client using Viem's createPublicClient
function.
You will configure a smart account and Bundler Client with the Public Client, which you can use to query the signer's account state and interact with the blockchain network.
import { createPublicClient, http } from 'viem';
import { sepolia as chain } from 'viem/chains';
const publicClient = createPublicClient({
chain,
transport: http(),
});
3. Create a Paymaster Client
Create a Viem Paymaster Client using Viem's createPaymasterClient
function.
This client interacts with the paymaster service.
Replace <YOUR-API-KEY>
with your Pimlico API key:
import { createPaymasterClient } from 'viem/account-abstraction';
const paymasterClient = createPaymasterClient({
transport: http('https://api.pimlico.io/v2/11155111/rpc?apikey=<YOUR-API-KEY>'),
});
4. Create a Bundler Client
Create a Viem Bundler Client using Viem's createBundlerClient
function.
Pass the paymasterClient
to the paymaster
property.
You can use the bundler service to estimate gas for user operations and submit transactions to the network.
import { createBundlerClient } from 'viem/account-abstraction';
const bundlerClient = createBundlerClient({
client: publicClient,
transport: http('https://your-bundler-rpc.com'),
paymaster: paymasterClient,
});
5. Create and deploy an inviter account
Create an account to represent the inviter. This account will be creating a delegation, and must be a MetaMask smart account. This example uses a Hybrid smart account, which is a flexible smart account implementation that supports both an externally owned account (EOA) owner and any number of passkey (WebAuthn) signers.
import { Implementation, toMetaMaskSmartAccount } from '@metamask/delegation-toolkit';
import { privateKeyToAccount } from 'viem/accounts';
const account = privateKeyToAccount('0x...');
const smartAccount = await toMetaMaskSmartAccount({
client: publicClient,
implementation: Implementation.Hybrid,
deployParams: [account.address, [], [], []],
deploySalt: '0x',
signatory: { account },
});
Deploy the smart account by sending a user operation:
import { zeroAddress } from 'viem';
// Appropriate fee per gas must be determined for the specific bundler being used.
const maxFeePerGas = 1n;
const maxPriorityFeePerGas = 1n;
const userOperationHash = await bundlerClient.sendUserOperation({
account: smartAccount,
calls: [{ to: zeroAddress }],
maxFeePerGas,
maxPriorityFeePerGas,
});
Fund the deployed smart account with some Sepolia ETH to sponsor gas fees for the invitee.
You can use the MetaMask faucet to get Sepolia ETH.
6. Create and sign an invitation
Create an open root delegation to represent an invitiation. A root delegation is the first delegation in a chain of delegations, and an open root delegation grants permission to any account. In this example, the inviter creates an invitation that can be redeemed by any invitee, allowing the invitee to spend up to 0.001 ETH.
import { createOpenDelegation, getDelegatorEnvironment } from '@metamask/delegation-toolkit';
const delegation = createOpenDelegation({
from: smartAccount.address,
environment: getDelegatorEnvironment(chain.id);
scope: {
type: 'nativeTokenTransferAmount',
// 0.001 ETH in wei format.
maxAmount: 1000000000000000n,
},
});
Sign the delegation to enable the invitee to redeem it in the future:
const signature = await smartAccount.signDelegation({
delegation,
})
const signedDelegation = {
...delegation,
signature,
}
7. Create an invitee account
Create an account to represent the invitee. This account will be redeeming the delegation and can be a smart account or an externally owned account (EOA). This example uses an EOA:
import { privateKeyToAccount } from 'viem/accounts';
import { sepolia as chain } from 'viem/chains';
import { createWalletClient, http } from 'viem';
const delegateAccount = privateKeyToAccount('0x...');
export const delegateWalletClient = createWalletClient({
account: delegateAccount,
chain,
transport: http(),
})
8. Redeem the invitation
The invitee can redeem the invitation by submitting a transaction to the DelegationManager
contract, which validates the delegation and executes delegated actions.
In this case, the invitee can spend up to 0.001 ETH from the inviter when using your dapp.
import { createExecution, getDeleGatorEnvironment } from '@metamask/delegation-toolkit'
import { DelegationManager } from '@metamask/delegation-toolkit/contracts'
import { SINGLE_DEFAULT_MODE } from '@metamask/delegation-toolkit/utils'
import { zeroAddress } from 'viem'
const delegations = [signedDelegation]
const executions = createExecution({ target: zeroAddress })
const redeemDelegationCalldata = DelegationManager.encode.redeemDelegations({
delegations: [delegations],
modes: [SINGLE_DEFAULT_MODE],
executions: [executions]
});
const transactionHash = await delegateWalletClient.sendTransaction({
to: getDeleGatorEnvironment(chain.id).DelegationManager,
data: redeemDelegationCalldata,
chain,
})
Next steps
- Learn more about smart account implementations.
- Learn more about delegation types.
- Learn more about using delegation scopes.