Skip to content

Instantly share code, notes, and snippets.

@himanshugarg06
Last active July 2, 2024 10:42
Show Gist options
  • Save himanshugarg06/3ac3b77125d09052e501268cc5b26cb2 to your computer and use it in GitHub Desktop.
Save himanshugarg06/3ac3b77125d09052e501268cc5b26cb2 to your computer and use it in GitHub Desktop.
Safe smart account integration with Biconomy Bundler and paymaster SDK
import { ENTRYPOINT_ADDRESS_V06, UserOperation } from "permissionless";
import {
SafeSmartAccount,
signerToSafeSmartAccount,
} from "permissionless/accounts";
import {
Chain,
createClient,
createPublicClient,
encodeFunctionData,
Hex,
http,
parseAbi,
PrivateKeyAccount,
PublicClient,
Transport,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { IPaymaster, PaymasterMode, createBundler, createPaymaster } from "@biconomy/account";
const DUMMY_GAS_LIMIT = 1000000n;
// Define constants from environment variables
const RPC_URL = "https://rpc.ankr.com/polygon";
const PAYMASTER_URL = "PAYMASTER_URL";
const BUNDLER_URL = "BUNDLER_URL";
const PRIVATE_KEY = "PRIVATE_KEY";
const nftAddress = "0x1758f42Af7026fBbB559Dc60EcE0De3ef81f665e";
// Create and initialize public, paymaster, and bundler clients
const createClients = async () => {
const publicClient = createPublicClient({
transport: http(RPC_URL),
});
const paymaster: IPaymaster = await createPaymaster({
paymasterUrl: PAYMASTER_URL, // <-- Read about at https://docs.biconomy.io/dashboard/paymaster
strictMode: true
});
const bundlerClient = await createBundler({
bundlerUrl: BUNDLER_URL
});
return { publicClient, paymaster, bundlerClient };
};
// Retrieve the Safe smart account associated with the given signer
const getSafeAccount = async (
publicClient: PublicClient,
signer: PrivateKeyAccount,
) => {
return await signerToSafeSmartAccount(publicClient, {
entryPoint: ENTRYPOINT_ADDRESS_V06,
signer: signer,
safeVersion: "1.4.1",
});
};
// Check if the account's bytecode is deployed and get the initialization code if needed
const getInitCodeIfNeeded = async (
publicClient: PublicClient,
safeAccount: SafeSmartAccount<
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
Transport,
Chain | undefined
>,
) => {
const byteCode = await publicClient.getBytecode({
address: safeAccount.address,
});
return byteCode === undefined
? await safeAccount.getInitCode()
: ("0x" as `0x${string}`);
};
// Create the call data for minting the NFT
const getMintCallData = async (
safeAccount: SafeSmartAccount<
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
Transport,
Chain | undefined
>,
) => {
const parsedAbi = parseAbi(["function safeMint(address _to)"]);
const nftData = encodeFunctionData({
abi: parsedAbi,
functionName: "safeMint",
args: [safeAccount.address],
});
return await safeAccount.encodeCallData({
to: nftAddress,
value: 0n,
data: nftData,
});
};
// Main function to mint the NFT
export const mint = async () => {
try {
// Create necessary clients
const { publicClient, paymaster, bundlerClient } =
await createClients();
const signer = privateKeyToAccount(PRIVATE_KEY);
const safeAccount = await getSafeAccount(publicClient, signer);
console.log(safeAccount.address)
// Get the init code if needed
const initCode = await getInitCodeIfNeeded(publicClient, safeAccount);
const callData = await getMintCallData(safeAccount);
const nonce = await safeAccount.getNonce();
const gasFeeValues = await bundlerClient.getGasFeeValues()
const userOperation: any = {
sender: safeAccount.address,
nonce: Number(nonce).toString(),
initCode,
callData,
maxFeePerGas: gasFeeValues.maxFeePerGas,
maxPriorityFeePerGas: gasFeeValues.maxPriorityFeePerGas,
preVerificationGas: DUMMY_GAS_LIMIT,
callGasLimit: DUMMY_GAS_LIMIT,
verificationGasLimit: DUMMY_GAS_LIMIT,
}
//@ts-ignore
const paymasterData = await paymaster.getPaymasterAndData(userOperation, {mode: PaymasterMode.SPONSORED})
userOperation.paymasterAndData = paymasterData.paymasterAndData;
userOperation.preVerificationGas = paymasterData.preVerificationGas;
userOperation.verificationGasLimit = paymasterData.verificationGasLimit;
userOperation.callGasLimit = paymasterData.callGasLimit;
userOperation.signature = "0x";
const dummySignature = await safeAccount.getDummySignature(userOperation);
userOperation.signature = dummySignature;
console.log(userOperation)
const signature = await safeAccount.signUserOperation(userOperation)
userOperation.signature = signature;
try{
const res = await bundlerClient.sendUserOp(userOperation)
const response = await res.wait();
console.log(response)
} catch(error) {
console.log("error occured", error)
}
} catch (error) {
console.error(`Error in minting NFT: ${error}`);
}
};
// Execute the mint function
mint();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment