diff --git a/.changeset/real-chefs-fail.md b/.changeset/real-chefs-fail.md new file mode 100644 index 00000000..64890734 --- /dev/null +++ b/.changeset/real-chefs-fail.md @@ -0,0 +1,5 @@ +--- +"permissionless": patch +--- + +Added flow to handle USDT approval flow on mainnet in prepareUserOperationForErc20Paymaster diff --git a/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts b/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts index e4759146..6026bd67 100644 --- a/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts +++ b/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts @@ -29,6 +29,10 @@ import { getAction, parseAccount } from "viem/utils" import { getTokenQuotes } from "../../../actions/pimlico.js" import { erc20BalanceOverride } from "../../../utils/erc20BalanceOverride.js" +const MAINNET_USDT_ADDRESS = getAddress( + "0xdAC17F958D2ee523a2206206994597C13D831ec7" +) + export const prepareUserOperationForErc20Paymaster = ( pimlicoClient: Client, @@ -127,6 +131,7 @@ export const prepareUserOperationForErc20Paymaster = calls = await account.decodeCalls(parameters.callData) } + // Create basic approval call array with max approval const callsWithDummyApproval = [ { abi: erc20Abi, @@ -137,6 +142,16 @@ export const prepareUserOperationForErc20Paymaster = ...(calls ? calls : []) ] + // For USDT on mainnet, add zero approval at the beginning + if (token === MAINNET_USDT_ADDRESS) { + callsWithDummyApproval.unshift({ + abi: erc20Abi, + functionName: "approve", + args: [paymasterERC20Address, 0n], + to: MAINNET_USDT_ADDRESS + }) + } + //////////////////////////////////////////////////////////////////////////////// // Call prepareUserOperation //////////////////////////////////////////////////////////////////////////////// @@ -243,17 +258,26 @@ export const prepareUserOperationForErc20Paymaster = const hasSufficientApproval = allowance >= maxCostInToken - const finalCalls = hasSufficientApproval - ? (calls ?? []) - : [ - { - abi: erc20Abi, - functionName: "approve", - args: [paymasterERC20Address, maxCostInToken], - to: paymasterContext.token - }, - ...(calls ?? []) - ] + const finalCalls: unknown[] = calls ? [...calls] : [] + + if (!hasSufficientApproval) { + finalCalls.unshift({ + abi: erc20Abi, + functionName: "approve", + args: [paymasterERC20Address, maxCostInToken], + to: paymasterContext.token + }) + } + + // For USDT on mainnet, add zero approval at the beginning + if (token === MAINNET_USDT_ADDRESS) { + finalCalls.unshift({ + abi: erc20Abi, + functionName: "approve", + args: [paymasterERC20Address, 0n], + to: MAINNET_USDT_ADDRESS + }) + } userOperation.callData = await account.encodeCalls( finalCalls.map((call_) => {