From d4bc476677dfd76d5f9399510dee0c02ea4da2a0 Mon Sep 17 00:00:00 2001 From: Sherif Abdelmoatty Date: Tue, 29 Apr 2025 14:35:25 +0300 Subject: [PATCH 1/5] set gas values to 0 for estimation --- .../src/packs/safe-4337/utils/userOperations.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/relay-kit/src/packs/safe-4337/utils/userOperations.ts b/packages/relay-kit/src/packs/safe-4337/utils/userOperations.ts index 6873d5e22..a9d349963 100644 --- a/packages/relay-kit/src/packs/safe-4337/utils/userOperations.ts +++ b/packages/relay-kit/src/packs/safe-4337/utils/userOperations.ts @@ -145,9 +145,9 @@ export async function createUserOperation( nonce: nonce.toString(), initCode, callData, - callGasLimit: 1n, - verificationGasLimit: 1n, - preVerificationGas: 1n, + callGasLimit: 0n, + verificationGasLimit: 0n, + preVerificationGas: 0n, maxFeePerGas: 1n, maxPriorityFeePerGas: 1n, paymasterAndData, @@ -160,9 +160,9 @@ export async function createUserOperation( nonce: nonce.toString(), ...unpackInitCode(initCode), callData, - callGasLimit: 1n, - verificationGasLimit: 1n, - preVerificationGas: 1n, + callGasLimit: 0n, + verificationGasLimit: 0n, + preVerificationGas: 0n, maxFeePerGas: 1n, maxPriorityFeePerGas: 1n, paymaster: paymasterAndData, From cd06cb98d360d9a060caf3509e710cbbd68c9865 Mon Sep 17 00:00:00 2001 From: Sherif Abdelmoatty Date: Tue, 29 Apr 2025 14:58:03 +0300 Subject: [PATCH 2/5] add GenericFeeEstimator --- packages/relay-kit/src/index.ts | 2 + .../estimators/generic/GenericFeeEstimator.ts | 137 ++++++++++++++++++ .../safe-4337/estimators/generic/types.ts | 36 +++++ .../src/packs/safe-4337/estimators/index.ts | 2 + .../relay-kit/src/packs/safe-4337/types.ts | 4 +- 5 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 packages/relay-kit/src/packs/safe-4337/estimators/generic/GenericFeeEstimator.ts create mode 100644 packages/relay-kit/src/packs/safe-4337/estimators/generic/types.ts diff --git a/packages/relay-kit/src/index.ts b/packages/relay-kit/src/index.ts index b96853506..87befacd3 100644 --- a/packages/relay-kit/src/index.ts +++ b/packages/relay-kit/src/index.ts @@ -15,6 +15,8 @@ export * from './packs/safe-4337/utils' export * from './RelayKitBasePack' +export { GenericFeeEstimator } from './packs/safe-4337/estimators/generic/GenericFeeEstimator' + declare module 'abitype' { export interface Register { AddressType: string diff --git a/packages/relay-kit/src/packs/safe-4337/estimators/generic/GenericFeeEstimator.ts b/packages/relay-kit/src/packs/safe-4337/estimators/generic/GenericFeeEstimator.ts new file mode 100644 index 000000000..9e315974e --- /dev/null +++ b/packages/relay-kit/src/packs/safe-4337/estimators/generic/GenericFeeEstimator.ts @@ -0,0 +1,137 @@ +import { EstimateGasData } from '@safe-global/types-kit' +import { + EstimateFeeFunctionProps, + IFeeEstimator, + UserOperationStringValues +} from '@safe-global/relay-kit/packs/safe-4337/types' +import { createPublicClient, http } from 'viem' +import { + createBundlerClient, + userOperationToHexValues +} from '@safe-global/relay-kit/packs/safe-4337/utils' +import { RPC_4337_CALLS } from '@safe-global/relay-kit/packs/safe-4337/constants' +import { PaymasterRpcSchema } from './types' + +/** + * GenericFeeEstimator is a class that implements the IFeeEstimator interface. You can implement three optional methods that will be called during the estimation process: + * - preEstimateUserOperationGas: Setup the userOperation before calling the eth_estimateUserOperation gas method. + * - postEstimateUserOperationGas: Adjust the userOperation values returned after calling the eth_estimateUserOperation method. + */ +export class GenericFeeEstimator implements IFeeEstimator { + nodeUrl: string + chainId: string + gasMultiplier: number + constructor(nodeUrl: string, chainId: string, gasMultiplier: number = 1.5) { + this.nodeUrl = nodeUrl + this.chainId = chainId + if (gasMultiplier <= 0) { + throw new Error("gasMultiplier can't be equal or less than 0.") + } + this.gasMultiplier = gasMultiplier + } + + async preEstimateUserOperationGas({ + bundlerUrl, // eslint-disable-line @typescript-eslint/no-unused-vars + userOperation, + entryPoint, + paymasterOptions + }: EstimateFeeFunctionProps): Promise { + bundlerUrl + if (paymasterOptions) { + const paymasterClient = createBundlerClient(paymasterOptions.paymasterUrl) + const context = + 'paymasterTokenAddress' in paymasterOptions + ? { + token: paymasterOptions.paymasterTokenAddress + } + : {} + + const [feeData, paymasterStubData] = await Promise.all([ + this.#getUserOperationGasPrices(this.nodeUrl), + paymasterClient.request({ + method: RPC_4337_CALLS.GET_PAYMASTER_STUB_DATA, + params: [ + userOperationToHexValues(userOperation, entryPoint), + entryPoint, + this.chainId, + context + ] + }) + ]) + return { + ...feeData, + ...paymasterStubData + } + } else { + const feeData = await this.#getUserOperationGasPrices(this.nodeUrl) + return { + ...feeData, + ...{} + } + } + } + + async postEstimateUserOperationGas({ + userOperation, + entryPoint, + paymasterOptions + }: EstimateFeeFunctionProps): Promise { + if (!paymasterOptions) return {} + + const paymasterClient = createBundlerClient(paymasterOptions.paymasterUrl) + if (paymasterOptions.isSponsored) { + const params: [UserOperationStringValues, string, string, { sponsorshipPolicyId: string }?] = + [userOperationToHexValues(userOperation, entryPoint), entryPoint, this.chainId] + + if (paymasterOptions.sponsorshipPolicyId) { + params.push({ + sponsorshipPolicyId: paymasterOptions.sponsorshipPolicyId + }) + } + + const sponsoredData = await paymasterClient.request({ + method: RPC_4337_CALLS.GET_PAYMASTER_DATA, + params + }) + + return sponsoredData + } + + const erc20PaymasterData = await paymasterClient.request({ + method: RPC_4337_CALLS.GET_PAYMASTER_DATA, + params: [ + userOperationToHexValues(userOperation, entryPoint), + entryPoint, + this.chainId, + { token: paymasterOptions.paymasterTokenAddress } + ] + }) + + return erc20PaymasterData + } + + async #getUserOperationGasPrices( + nodeUrl: string + ): Promise> { + const client = createPublicClient({ + transport: http(nodeUrl) + }) + const [block, maxPriorityFeePerGas] = await Promise.all([ + client.getBlock({ blockTag: 'latest' }), + client.estimateMaxPriorityFeePerGas() + ]) + // Base fee from the block (can be undefined for non-EIP1559 blocks) + const baseFeePerGas = block.baseFeePerGas + + if (!baseFeePerGas) { + throw new Error('Base fee not available - probably not an EIP-1559 block.') + } + + // Calculate maxFeePerGas + const maxFeePerGas = baseFeePerGas + maxPriorityFeePerGas + return { + maxFeePerGas: BigInt(Math.ceil(Number(maxFeePerGas) * this.gasMultiplier)), + maxPriorityFeePerGas: BigInt(Math.ceil(Number(maxPriorityFeePerGas) * this.gasMultiplier)) + } + } +} diff --git a/packages/relay-kit/src/packs/safe-4337/estimators/generic/types.ts b/packages/relay-kit/src/packs/safe-4337/estimators/generic/types.ts new file mode 100644 index 000000000..8aea3a624 --- /dev/null +++ b/packages/relay-kit/src/packs/safe-4337/estimators/generic/types.ts @@ -0,0 +1,36 @@ +import { UserOperationStringValues } from '@safe-global/relay-kit/packs/safe-4337/types' +import { RPC_4337_CALLS } from '@safe-global/relay-kit/packs/safe-4337/constants' + +export type GetPaymasterStubDataRpcSchema = [ + { + Method: RPC_4337_CALLS.GET_PAYMASTER_STUB_DATA + Parameters: [UserOperationStringValues, string, string, Record?] + ReturnType: + | { + paymasterAndData: string + } + | { + sponsor?: { name: string; icon?: string } + paymaster?: string + paymasterData?: string + paymasterVerificationGasLimit?: string + paymasterPostOpGasLimit?: string + isFinal?: boolean + } + } +] + +export type PaymasterRpcSchema = [ + { + Method: RPC_4337_CALLS.GET_PAYMASTER_DATA + Parameters: [UserOperationStringValues, string, string, Record?] + ReturnType: + | { + paymasterAndData: string + } + | { + paymaster?: string + paymasterData?: string + } + } +] diff --git a/packages/relay-kit/src/packs/safe-4337/estimators/index.ts b/packages/relay-kit/src/packs/safe-4337/estimators/index.ts index bb11224a0..8ac8302fe 100644 --- a/packages/relay-kit/src/packs/safe-4337/estimators/index.ts +++ b/packages/relay-kit/src/packs/safe-4337/estimators/index.ts @@ -1,3 +1,5 @@ import { PimlicoFeeEstimator } from './pimlico/PimlicoFeeEstimator' +import { GenericFeeEstimator } from './generic/GenericFeeEstimator' export { PimlicoFeeEstimator } +export { GenericFeeEstimator } diff --git a/packages/relay-kit/src/packs/safe-4337/types.ts b/packages/relay-kit/src/packs/safe-4337/types.ts index 77c271ab6..1c60fd547 100644 --- a/packages/relay-kit/src/packs/safe-4337/types.ts +++ b/packages/relay-kit/src/packs/safe-4337/types.ts @@ -181,7 +181,7 @@ export type UserOperationStringValues = Omit< export type Safe4337RpcSchema = [ { Method: RPC_4337_CALLS.GET_PAYMASTER_STUB_DATA - Parameters: [UserOperationStringValues, string, string, { token: string }?] + Parameters: [UserOperationStringValues, string, string, { token?: string }?] ReturnType: | { paymasterAndData: string @@ -195,7 +195,7 @@ export type Safe4337RpcSchema = [ }, { Method: RPC_4337_CALLS.GET_PAYMASTER_DATA - Parameters: [UserOperationStringValues, string, string, { token: string }?] + Parameters: [UserOperationStringValues, string, string, { token?: string }?] ReturnType: | { paymasterAndData: string From 1a0975d915763abf8cf4c318902d9370aa3c8395 Mon Sep 17 00:00:00 2001 From: Sherif Abdelmoatty Date: Tue, 29 Apr 2025 14:58:41 +0300 Subject: [PATCH 3/5] add generic fee estimator examples --- playground/config/run.ts | 4 ++ ...userop-counterfactual-generic-estimator.ts | 53 ++++++++++++++ ...serop-erc20-paymaster-generic-estimator.ts | 69 +++++++++++++++++++ .../relay-kit/userop-generic-estimator.ts | 54 +++++++++++++++ ...p-verifying-paymaster-generic-estimator.ts | 65 +++++++++++++++++ 5 files changed, 245 insertions(+) create mode 100644 playground/relay-kit/userop-counterfactual-generic-estimator.ts create mode 100644 playground/relay-kit/userop-erc20-paymaster-generic-estimator.ts create mode 100644 playground/relay-kit/userop-generic-estimator.ts create mode 100644 playground/relay-kit/userop-verifying-paymaster-generic-estimator.ts diff --git a/playground/config/run.ts b/playground/config/run.ts index 03956d7e5..b817b8108 100644 --- a/playground/config/run.ts +++ b/playground/config/run.ts @@ -22,10 +22,14 @@ const playgroundRelayKitPaths = { 'gelato-sponsored-transaction': 'relay-kit/gelato-sponsored-transaction', 'userop-api-kit-interoperability': 'relay-kit/userop-api-kit-interoperability', userop: 'relay-kit/userop', + 'userop-generic-estimator': 'relay-kit/userop-generic-estimator', 'userop-counterfactual': 'relay-kit/userop-counterfactual', + 'userop-counterfactual-generic-estimator': 'relay-kit/userop-counterfactual-generic-estimator', 'userop-erc20-paymaster': 'relay-kit/userop-erc20-paymaster', + 'userop-erc20-paymaster-generic-estimator': 'relay-kit/userop-erc20-paymaster-generic-estimator', 'userop-erc20-paymaster-counterfactual': 'relay-kit/userop-erc20-paymaster-counterfactual', 'userop-verifying-paymaster': 'relay-kit/userop-verifying-paymaster', + 'userop-verifying-paymaster-generic-estimator': 'relay-kit/userop-verifying-paymaster-generic-estimator', 'userop-verifying-paymaster-counterfactual': 'relay-kit/userop-verifying-paymaster-counterfactual', 'userop-parallel-execution': 'relay-kit/userop-parallel-execution' diff --git a/playground/relay-kit/userop-counterfactual-generic-estimator.ts b/playground/relay-kit/userop-counterfactual-generic-estimator.ts new file mode 100644 index 000000000..06435723f --- /dev/null +++ b/playground/relay-kit/userop-counterfactual-generic-estimator.ts @@ -0,0 +1,53 @@ +import * as dotenv from 'dotenv' +import { Safe4337Pack, GenericFeeEstimator } from '@safe-global/relay-kit' +import { waitForOperationToFinish } from '../utils' +import { privateKeyToAccount } from 'viem/accounts' + +dotenv.config({ path: './playground/relay-kit/.env' }) + +// Load environment variables from ./.env file +// Follow .env-sample as an example to create your own file +const { PRIVATE_KEY, RPC_URL = '', CHAIN_ID = '', BUNDLER_URL = '' } = process.env + +async function main() { + // 1) Initialize pack + const account = privateKeyToAccount(`0x${PRIVATE_KEY}`) + + const safe4337Pack = await Safe4337Pack.init({ + provider: RPC_URL, + signer: PRIVATE_KEY, + bundlerUrl: BUNDLER_URL, + safeModulesVersion: '0.3.0', // Blank or 0.3.0 for Entrypoint v0.7, 0.2.0 for Entrypoint v0.6 + options: { + owners: [account.address], + threshold: 1, + saltNonce: '4337' + '1' + } + }) + + // 2) Create SafeOperation + const safeOperation = await safe4337Pack.createTransaction({ + transactions:[{ + to: '0xfaDDcFd59924F559AC24350C4b9dA44b57E62857', + value: '0x0', + data: '0x' + }], + options: { + feeEstimator: new GenericFeeEstimator(RPC_URL, CHAIN_ID), + } + }) + + // 3) Sign SafeOperation + const signedSafeOperation = await safe4337Pack.signSafeOperation(safeOperation) + + console.log('SafeOperation', signedSafeOperation) + + // 4) Execute SafeOperation + const userOperationHash = await safe4337Pack.executeTransaction({ + executable: signedSafeOperation + }) + + await waitForOperationToFinish(userOperationHash, CHAIN_ID, safe4337Pack) +} + +main() diff --git a/playground/relay-kit/userop-erc20-paymaster-generic-estimator.ts b/playground/relay-kit/userop-erc20-paymaster-generic-estimator.ts new file mode 100644 index 000000000..b27746b9d --- /dev/null +++ b/playground/relay-kit/userop-erc20-paymaster-generic-estimator.ts @@ -0,0 +1,69 @@ +import * as dotenv from 'dotenv' +import { Safe4337Pack, GenericFeeEstimator } from '@safe-global/relay-kit' +import { waitForOperationToFinish, setup4337Playground } from '../utils' + +dotenv.config({ path: './playground/relay-kit/.env' }) + +// Load environment variables from ./.env file +// Follow .env-sample as an example to create your own file +const { + PRIVATE_KEY, + SAFE_ADDRESS = '0x', + RPC_URL = '', + CHAIN_ID = '', + PAYMASTER_URL = '', + BUNDLER_URL = '' +} = process.env + +//Candide paymaster contract address +const paymasterAddress = '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba' + +// Candide test token contract address +const tokenAddress = '0xFa5854FBf9964330d761961F46565AB7326e5a3b' + +async function main() { + // 1) Initialize pack with the paymaster data + const safe4337Pack = await Safe4337Pack.init({ + provider: RPC_URL, + signer: PRIVATE_KEY, + bundlerUrl: BUNDLER_URL, + safeModulesVersion: '0.3.0', // Blank or 0.3.0 for Entrypoint v0.7, 0.2.0 for Entrypoint v0.6 + paymasterOptions: { + paymasterUrl: PAYMASTER_URL, + paymasterTokenAddress: tokenAddress, + paymasterAddress: paymasterAddress + }, + options: { + safeAddress: SAFE_ADDRESS + } + }) + + // 2) Create SafeOperation + const safeOperation = await safe4337Pack.createTransaction({ + transactions:[{ + to: '0xfaDDcFd59924F559AC24350C4b9dA44b57E62857', + value: '0x0', + data: '0x' + }], + options: { + feeEstimator: new GenericFeeEstimator( + RPC_URL, + CHAIN_ID, + ) + } + }) + + // 3) Sign SafeOperation + const signedSafeOperation = await safe4337Pack.signSafeOperation(safeOperation) + + console.log('SafeOperation', signedSafeOperation) + + // 4) Execute SafeOperation + const userOperationHash = await safe4337Pack.executeTransaction({ + executable: signedSafeOperation + }) + + await waitForOperationToFinish(userOperationHash, CHAIN_ID, safe4337Pack) +} + +main() diff --git a/playground/relay-kit/userop-generic-estimator.ts b/playground/relay-kit/userop-generic-estimator.ts new file mode 100644 index 000000000..09cd8028f --- /dev/null +++ b/playground/relay-kit/userop-generic-estimator.ts @@ -0,0 +1,54 @@ +import dotenv from 'dotenv' +import { Safe4337Pack, GenericFeeEstimator } from '@safe-global/relay-kit' +import { waitForOperationToFinish } from '../utils' + +dotenv.config({ path: './playground/relay-kit/.env' }) + +// Load environment variables from ./.env file +// Follow .env-sample as an example to create your own file +const { + PRIVATE_KEY, + SAFE_ADDRESS = '0x', + RPC_URL = '', + CHAIN_ID = '', + BUNDLER_URL = '' +} = process.env + +async function main() { + // 1) Initialize pack + const safe4337Pack = await Safe4337Pack.init({ + provider: RPC_URL, + signer: PRIVATE_KEY, + safeModulesVersion: '0.3.0', // Blank or 0.3.0 for Entrypoint v0.7, 0.2.0 for Entrypoint v0.6 + bundlerUrl: BUNDLER_URL, + options: { + safeAddress: SAFE_ADDRESS + } + }) + + // 2) Create SafeOperation + const safeOperation = await safe4337Pack.createTransaction({ + transactions:[{ + to: '0xfaDDcFd59924F559AC24350C4b9dA44b57E62857', + value: '0x0', + data: '0x' + }], + options: { + feeEstimator: new GenericFeeEstimator(RPC_URL, CHAIN_ID) + } + }) + + // 3) Sign SafeOperation + const signedSafeOperation = await safe4337Pack.signSafeOperation(safeOperation) + + console.log('SafeOperation', signedSafeOperation) + + // 4) Execute SafeOperation + const userOperationHash = await safe4337Pack.executeTransaction({ + executable: signedSafeOperation + }) + + await waitForOperationToFinish(userOperationHash, CHAIN_ID, safe4337Pack) +} + +main() diff --git a/playground/relay-kit/userop-verifying-paymaster-generic-estimator.ts b/playground/relay-kit/userop-verifying-paymaster-generic-estimator.ts new file mode 100644 index 000000000..614f5dd85 --- /dev/null +++ b/playground/relay-kit/userop-verifying-paymaster-generic-estimator.ts @@ -0,0 +1,65 @@ +import * as dotenv from 'dotenv' +import { Safe4337Pack, GenericFeeEstimator } from '@safe-global/relay-kit' +import { waitForOperationToFinish } from '../utils' + +dotenv.config({ path: './playground/relay-kit/.env' }) + +// Load environment variables from ./.env file +// Follow .env-sample as an example to create your own file +const { + PRIVATE_KEY, + SAFE_ADDRESS = '0x', + RPC_URL = '', + CHAIN_ID = '', + BUNDLER_URL = '', + PAYMASTER_URL = '', + POLICY_ID +} = process.env + +async function main() { + // 1) Initialize pack with the paymaster data + const safe4337Pack = await Safe4337Pack.init({ + provider: RPC_URL, + signer: PRIVATE_KEY, + safeModulesVersion: '0.3.0', // Blank or 0.3.0 for Entrypoint v0.7, 0.2.0 for Entrypoint v0.6 + bundlerUrl: BUNDLER_URL, + paymasterOptions: { + isSponsored: true, + paymasterUrl: PAYMASTER_URL, + sponsorshipPolicyId: POLICY_ID + }, + options: { + safeAddress: SAFE_ADDRESS + } + }) + + // 2) Create SafeOperation + const safeOperation = await safe4337Pack.createTransaction({ + transactions:[{ + to: '0xfaDDcFd59924F559AC24350C4b9dA44b57E62857', + value: '0x0', + data: '0x' + }], + options: { + feeEstimator: new GenericFeeEstimator( + RPC_URL, + CHAIN_ID, + 1.1 //fee multiplier, defaults to 1.5 + ) + } + }) + + // 3) Sign SafeOperation + const signedSafeOperation = await safe4337Pack.signSafeOperation(safeOperation) + + console.log('SafeOperation', signedSafeOperation) + + // 4) Execute SafeOperation + const userOperationHash = await safe4337Pack.executeTransaction({ + executable: signedSafeOperation + }) + + await waitForOperationToFinish(userOperationHash, CHAIN_ID, safe4337Pack) +} + +main() From dd08d06b52b5a1dfa61ae0a8fac8f52859081424 Mon Sep 17 00:00:00 2001 From: Sherif Abdelmoatty Date: Wed, 30 Apr 2025 16:02:03 +0300 Subject: [PATCH 4/5] add infinit token approval to example --- .../relay-kit/userop-erc20-paymaster-generic-estimator.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/playground/relay-kit/userop-erc20-paymaster-generic-estimator.ts b/playground/relay-kit/userop-erc20-paymaster-generic-estimator.ts index b27746b9d..f1fbbd748 100644 --- a/playground/relay-kit/userop-erc20-paymaster-generic-estimator.ts +++ b/playground/relay-kit/userop-erc20-paymaster-generic-estimator.ts @@ -31,7 +31,9 @@ async function main() { paymasterOptions: { paymasterUrl: PAYMASTER_URL, paymasterTokenAddress: tokenAddress, - paymasterAddress: paymasterAddress + paymasterAddress: paymasterAddress, + //infinit approval just for testing - don't do that in production + amountToApprove: 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn }, options: { safeAddress: SAFE_ADDRESS From 0d18a476a9bd2a33589ccbffb321e5247e011099 Mon Sep 17 00:00:00 2001 From: Sherif Abdelmoatty Date: Wed, 30 Apr 2025 16:44:40 +0300 Subject: [PATCH 5/5] update .env-sample --- playground/relay-kit/.env-sample | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/playground/relay-kit/.env-sample b/playground/relay-kit/.env-sample index 71c76387e..6c1850d13 100644 --- a/playground/relay-kit/.env-sample +++ b/playground/relay-kit/.env-sample @@ -3,13 +3,13 @@ # The derived address will be the Safe owner when using the counterfactual deployment and you should send the test tokens to this address in case the playground requires them PRIVATE_KEY= # Safe address to use with the playgrounds where Safe must already exist -SAFE_ADDRESS=0x... +SAFE_ADDRESS= # Set the preferred RPC url RPC_URL=https://ethereum-sepolia-rpc.publicnode.com # Set the chain ID where the playgrounds will be used -CHAIN_ID=11155111 +CHAIN_ID=0xaa36a7 #sepolia # You can get the bundler and paymaster URL's from your provider's dashboard -BUNDLER_URL= -PAYMASTER_URL= +BUNDLER_URL=https://sepolia.voltaire.candidewallet.com/rpc +PAYMASTER_URL=https://api.candide.dev/paymaster/v3/sepolia/$API_KEY # Set the sponsor policy in case you are using one to test the sponsor user operations related playgrounds -SPONSORSHIP_POLICY_ID= \ No newline at end of file +POLICY_ID=