How to use Pimlico Lock
Skip to content

How to use Pimlico Lock

This guide will show you how to use Pimlico Lock to request ETH from the FlashFund contracts.

Steps

Define imports and create the clients

import { createSmartAccountClient } from "permissionless"
import { Address, Hex, createPublicClient, http, parseEther, toHex } from "viem"
import { sepolia } from "viem/chains"
import { privateKeyToAccount } from "viem/accounts"
import { entryPoint07Address } from "viem/account-abstraction"
import { createPimlicoClient } from "permissionless/clients/pimlico"
import { toSimpleSmartAccount } from "permissionless/accounts"
 
import "dotenv/config"
import { getPimlicoUrl, MagicSpend } from "./magic-spend"
 
const RPC_URL = "https://11155111.rpc.thirdweb.com"
const ETH: Address = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
const amount = "0.001" // 0.001 ETH
 
const PRIVATE_KEY = process.env.ACCOUNT_PRIVATE_KEY
 
if (PRIVATE_KEY === undefined) {
	throw new Error("ACCOUNT_PRIVATE_KEY env var is required")
}
 
const pimlicoUrl = getPimlicoUrl(sepolia.id)
 
export const publicClient = createPublicClient({
	transport: http(RPC_URL),
	chain: sepolia,
})
 
const pimlicoClient = createPimlicoClient({
	transport: http(pimlicoUrl),
	chain: sepolia,
	entryPoint: {
		address: entryPoint07Address,
		version: "0.7",
	},
})
 
// This is the address of the account that owns the stake
const signer = privateKeyToAccount(PRIVATE_KEY as Hex)
console.log(`Signer address: ${signer.address}`)
 
const simpleAccount = await toSimpleSmartAccount({
	client: publicClient,
	owner: signer,
	entryPoint: {
		address: entryPoint07Address,
		version: "0.7",
	},
})
 
const smartAccountClient = createSmartAccountClient({
	account: simpleAccount,
	chain: sepolia,
	bundlerTransport: http(pimlicoUrl, {
		timeout: 60_000,
	}),
	userOperation: {
		estimateFeesPerGas: async () => {
			return (await pimlicoClient.getUserOperationGasPrice()).fast
		},
	},
	paymaster: pimlicoClient,
})
 
const magicSpend = new MagicSpend()
magicSpend.setChainId(sepolia.id)
 

Add funds in the resource lock

const [stakeAddress, stakeCalldata, stakeValue] = await magicSpend.prepareStake({
	type: "pimlico_lock",
	data: {
		token: ETH,
		amount: toHex(parseEther(amount)),
		unstakeDelaySec: "86400",
	},
})
 
await smartAccountClient.sendUserOperation({
	account: simpleAccount,
	calls: [
		{
			to: stakeAddress,
			data: stakeCalldata,
			value: parseEther(stakeValue),
		},
	],
})
 

Get the stakes

const stakes = await magicSpend.getStakes({
	account: simpleAccount.address,
})
console.log(`Stakes: ${JSON.stringify(stakes, null, 2)}`)

Prepare the allowance

const allowance = await magicSpend.prepareAllowance({
	type: "pimlico_lock",
	data: {
		account: simpleAccount.address,
		token: ETH,
		amount: toHex(parseEther(amount)),
		recipient: simpleAccount.address,
	},
})
 
const signature = await signer.signTypedData({
	domain: {
		name: "Pimlico Lock",
		chainId: sepolia.id,
		verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
		version: "1",
	},
	types: {
		AssetAllowance: [
			{ name: "token", type: "address" },
			{ name: "amount", type: "uint128" },
			{ name: "chainId", type: "uint128" },
		],
		Allowance: [
			{ name: "account", type: "address" },
			{ name: "assets", type: "AssetAllowance[]" },
			{ name: "validUntil", type: "uint48" },
			{ name: "validAfter", type: "uint48" },
			{ name: "salt", type: "uint48" },
			{ name: "version", type: "uint32" },
			{ name: "metadata", type: "bytes" },
		],
	},
	primaryType: "Allowance",
	message: {
		account: allowance.account,
		assets: allowance.assets,
		validUntil: Number(allowance.validUntil),
		validAfter: Number(allowance.validAfter),
		salt: Number(allowance.salt),
		version: Number(allowance.version),
		metadata: allowance.metadata,
	},
})

Request a withdrawal sponsorship

const [contract, calldata] = await magicSpend.sponsorWithdrawal({
	type: "pimlico_lock",
	data: {
		allowance,
		signature,
	},
})

Execute the withdrawal

// Send user operation and withdraw funds
// You can add subsequent calls after the withdrawal, like "buy NFT on OpenSea for ETH"
const userOpHash = await smartAccountClient.sendUserOperation({
	account: simpleAccount,
	calls: [
		{
			to: contract,
			value: parseEther("0"),
			data: calldata,
		},
	],
})
 
console.log(`Userop hash: ${userOpHash}`)
 
const receipt = await pimlicoClient.waitForUserOperationReceipt({
	hash: userOpHash,
})
 
console.log(`Transaction hash: ${receipt.receipt.transactionHash}`)