Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move to newest pimlco version and add erc20 #35

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 184 additions & 0 deletions examples/pimlico/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions examples/pimlico/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"main": "index.js",
"license": "MIT",
"dependencies": {
"permissionless": "^0.0.36",
"viem": "^2.7.13"
"permissionless": "^0.2.0",
"viem": "^2.21.0"
},
"scripts": {
"dev": "node src/mint.js"
Expand Down
25 changes: 15 additions & 10 deletions examples/pimlico/src/account.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@

import { privateKeyToSimpleSmartAccount, signerToSafeSmartAccount, signerToEcdsaKernelSmartAccount } from "permissionless/accounts"
import { toSimpleSmartAccount, toSafeSmartAccount, toEcdsaKernelSmartAccount } from "permissionless/accounts"
import { privateKeyToAccount } from "viem/accounts"
import { http, createPublicClient } from 'viem'
import config from '../../../config.js';
import { baseSepolia } from "viem/chains";

const publicClient = createPublicClient({
transport: http(config.rpc_url),
chain: baseSepolia
});

// To customize the signer, see https://docs.pimlico.io/permissionless/reference/accounts/signerToSimpleSmartAccount
Expand All @@ -16,27 +18,30 @@ export const getAccount = async (type) => {
switch (type) {
case "simple":
// EOA signer (private key) and Simple Account
const simpleAccount = await privateKeyToSimpleSmartAccount(publicClient, {
privateKey: config.private_key,
const simpleAccount = await toSimpleSmartAccount({
client: publicClient,
factoryAddress: "0x9406Cc6185a346906296840746125a0E44976454",
entryPoint: config.entry_point,
entryPoint: {address: config.entry_point, version: "0.6"},
owner: signer,
})
return simpleAccount
case "safe":
// EOA signer (private key) and Safe
const safeAccount = await signerToSafeSmartAccount(publicClient, {
entryPoint: config.entry_point,
signer: signer,
const safeAccount = await toSafeSmartAccount({
client: publicClient,
entryPoint: {address: config.entry_point, version: "0.6"},
safeVersion: "1.4.1",
owners: [signer],
// index: 0n, // optional
// address: "0x...", // optional, only if you are using an already created account
})
return safeAccount
case "kernel":
// EOA signer (private key) and Kernel
const kernelAccount = await signerToEcdsaKernelSmartAccount(publicClient, {
entryPoint: config.entry_point,
signer: signer,
const kernelAccount = await toEcdsaKernelSmartAccount({
entryPoint: {address: config.entry_point, version: "0.6"},
owners: [signer],
client: publicClient,
// index: 0n, // optional
// address: "0x...", // optional, only if you are using an already created account
})
Expand Down
111 changes: 91 additions & 20 deletions examples/pimlico/src/mint.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
import { encodeFunctionData, http } from 'viem'
import { http, createPublicClient, parseAbi } from 'viem'
import { baseSepolia } from 'viem/chains'
import { createSmartAccountClient } from 'permissionless'
import { createPimlicoPaymasterClient } from "permissionless/clients/pimlico";
import { createPimlicoClient } from "permissionless/clients/pimlico";
import { getAccount } from './account.js';
import { abi } from './abi.js';
import config from '../../../config.js';

// Get yours at https://www.coinbase.com/cloud/products/base/rpc
// Get yours at https://www.coinbase.com/developer-platform/products/paymaster
const rpcUrl = config.rpc_url
const contractAddress = config.contract_address;
const usdcAddress = "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
const paymasterAddress = "0xf58a1ae03da9bcf16c2729ce22cebcf96da1046e"
const supportsERC20 = false; // enable to do ERC20 approval checks

// Create the Cloud Paymaster
const cloudPaymaster = createPimlicoPaymasterClient({
export const cdpPaymasterBundler = createPimlicoClient({
chain: baseSepolia,
transport: http(rpcUrl)
})

// Standard RPC client
const publicClient = createPublicClient({
chain: baseSepolia,
transport: http(rpcUrl),
})

// Get the account
const account = await getAccount(config.account_type).catch((error) => {
console.error("\x1b[31m", `❌ ${error.message}`);
Expand All @@ -26,27 +33,91 @@ const account = await getAccount(config.account_type).catch((error) => {
const smartAccountClient = createSmartAccountClient({
account,
chain: baseSepolia,
transport: http(rpcUrl),
// IMPORTANT: Set up the Cloud Paymaster to sponsor your transaction
sponsorUserOperation: cloudPaymaster.sponsorUserOperation,
});
bundlerTransport: http(rpcUrl),
userOperation: {
estimateFeesPerGas: async () => {
// Can set up higher prio fees using viem's chain configurations
const gas = await publicClient.estimateFeesPerGas()
return gas;
},
},
paymaster: {
async getPaymasterStubData(parameters) {
const paymasterAndDataStub = await cdpPaymasterBundler.paymaster.getPaymasterStubData(parameters)
return {
paymasterAndData : paymasterAndDataStub
}
},
async getPaymasterAndData(parameters) {
const gasEstimates = await cdpPaymasterBundler.estimateUserOperationGas({
...parameters
})

// update PVG
gasEstimates.preVerificationGas = gasEstimates.preVerificationGas * 2

// Update the userOp with our gas fields
updatedParams = {...parameters}
updatedParams[0] = {...updatedParams[0],
preVerificationGas: gasEstimates.preVerificationGas,
verificationGasLimit: gasEstimates.verificationGasLimit,
callGasLimit: gasEstimates.callGasLimit
}

// get the final payMasterAndData fields
const paymasterAndData = await cdpPaymasterBundler.paymaster.getPaymasterAndData(updatedParams)

// Encode the calldata
const callData = encodeFunctionData({
abi: abi,
functionName: config.function_name,
args: [smartAccountClient.account.address, 0],
// Pimlico requires return that includes the gas estimate updates
return {
paymasterAndData: paymasterAndData,
preVerificationGas: gasEstimates.preVerificationGas,
verificationGasLimit: gasEstimates.verificationGasLimit,
callGasLimit: gasEstimates.callGasLimit
}
},
},
});

// Prepare nft mint call
const calls = [
{
to: contractAddress,
abi: abi,
functionName: config.function_name,
args: [smartAccountClient.account.address, 0],
},
]

// Optional: Check sender USDC balance for erc20 payments
if (supportsERC20) {
const senderPaymasterUSDCApprovalAmount = await publicClient.readContract({
abi: parseAbi(["function allowance(address owner, address spender) returns (uint256)"]),
address: usdcAddress,
functionName: "allowance",
args: [account.address, paymasterAddress],
})

// If under $5 add new approval (mainnet can be much lower than 5$ but there can be gas spikes on Sepolia)
console.log("Sender has approved paymaster to spend: " + senderPaymasterUSDCApprovalAmount + " USDC ");
if (senderPaymasterUSDCApprovalAmount < 5_000_000n) {
console.log("Including new paymaster USDC approval in calls.")
calls.push({
to: usdcAddress,
abi: parseAbi(["function approve(address,uint)"]),
functionName: "approve",
args: [paymasterAddress, 10_000_000], // approve $10 USDC (~1000tx)
})
}
}

console.log("\x1b[33m%s\x1b[0m", `Minting to ${account.address} (Account type: ${config.account_type})`);
console.log("Waiting for transaction...")

// Send the sponsored transaction!
const txHash = await smartAccountClient.sendTransaction({
account: smartAccountClient.account,
to: contractAddress,
data: callData,
value: BigInt(0),
calls
});

console.log("\x1b[32m", `⛽ Successfully sponsored gas for ${config.function_name} transaction with Coinbase Developer Platform!`);
console.log("\x1b[36m", `🔍 View on Etherscan: https://sepolia.basescan.org/tx/${txHash}`);
console.log("\x1b[36m", `🔍 View on Etherscan: https://sepolia.basescan.org/tx/${txHash}`);
process.exit(0);
Loading