From 0f65376eb2aa8037dac5fd3cf6d9576d012762a7 Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Thu, 20 Mar 2025 11:39:37 +0000 Subject: [PATCH 1/7] fix approval flow for USDT on mainnet --- .../prepareUserOperationForErc20Paymaster.ts | 73 ++++++++++++++----- 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts b/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts index e4759146..2d604308 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,15 +131,32 @@ export const prepareUserOperationForErc20Paymaster = calls = await account.decodeCalls(parameters.callData) } - const callsWithDummyApproval = [ - { - abi: erc20Abi, - functionName: "approve", - args: [paymasterERC20Address, maxUint256], // dummy approval to ensure simulation passes - to: paymasterContext.token - }, - ...(calls ? calls : []) - ] + const callsWithDummyApproval = + token === MAINNET_USDT_ADDRESS + ? [ + { + abi: erc20Abi, + functionName: "approve", + args: [paymasterERC20Address, 0n], // USDT on mainnet requires zero approval first + to: paymasterContext.token + }, + { + abi: erc20Abi, + functionName: "approve", + args: [paymasterERC20Address, maxUint256], + to: paymasterContext.token + }, + ...(calls ? calls : []) + ] + : [ + { + abi: erc20Abi, + functionName: "approve", + args: [paymasterERC20Address, maxUint256], // dummy approval to ensure simulation passes + to: paymasterContext.token + }, + ...(calls ? calls : []) + ] //////////////////////////////////////////////////////////////////////////////// // Call prepareUserOperation @@ -245,15 +266,31 @@ export const prepareUserOperationForErc20Paymaster = const finalCalls = hasSufficientApproval ? (calls ?? []) - : [ - { - abi: erc20Abi, - functionName: "approve", - args: [paymasterERC20Address, maxCostInToken], - to: paymasterContext.token - }, - ...(calls ?? []) - ] + : token === MAINNET_USDT_ADDRESS + ? [ + { + abi: erc20Abi, + functionName: "approve", + args: [paymasterERC20Address, 0n], // USDT on mainnet requires zero approval first + to: paymasterContext.token + }, + { + abi: erc20Abi, + functionName: "approve", + args: [paymasterERC20Address, maxCostInToken], + to: paymasterContext.token + }, + ...(calls ?? []) + ] + : [ + { + abi: erc20Abi, + functionName: "approve", + args: [paymasterERC20Address, maxCostInToken], + to: paymasterContext.token + }, + ...(calls ?? []) + ] userOperation.callData = await account.encodeCalls( finalCalls.map((call_) => { From 97f1ba07b3dd6b2dd4b1622aed2e06c80cde0b59 Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Thu, 20 Mar 2025 11:43:45 +0000 Subject: [PATCH 2/7] add changeset --- .changeset/real-chefs-fail.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/real-chefs-fail.md 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 From 6f0dfbe7e39dbe7909f9638d4ec891e9742da51e Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Thu, 20 Mar 2025 11:57:40 +0000 Subject: [PATCH 3/7] simplify --- .../prepareUserOperationForErc20Paymaster.ts | 96 +++++++++---------- 1 file changed, 43 insertions(+), 53 deletions(-) diff --git a/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts b/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts index 2d604308..be3f6f62 100644 --- a/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts +++ b/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts @@ -131,32 +131,26 @@ export const prepareUserOperationForErc20Paymaster = calls = await account.decodeCalls(parameters.callData) } - const callsWithDummyApproval = - token === MAINNET_USDT_ADDRESS - ? [ - { - abi: erc20Abi, - functionName: "approve", - args: [paymasterERC20Address, 0n], // USDT on mainnet requires zero approval first - to: paymasterContext.token - }, - { - abi: erc20Abi, - functionName: "approve", - args: [paymasterERC20Address, maxUint256], - to: paymasterContext.token - }, - ...(calls ? calls : []) - ] - : [ - { - abi: erc20Abi, - functionName: "approve", - args: [paymasterERC20Address, maxUint256], // dummy approval to ensure simulation passes - to: paymasterContext.token - }, - ...(calls ? calls : []) - ] + // Create basic approval call array with max approval + const callsWithDummyApproval = [ + { + abi: erc20Abi, + functionName: "approve", + args: [paymasterERC20Address, maxUint256], // dummy approval to ensure simulation passes + to: paymasterContext.token + }, + ...(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], // USDT on mainnet requires zero approval first + to: paymasterContext.token + }) + } //////////////////////////////////////////////////////////////////////////////// // Call prepareUserOperation @@ -264,33 +258,29 @@ export const prepareUserOperationForErc20Paymaster = const hasSufficientApproval = allowance >= maxCostInToken - const finalCalls = hasSufficientApproval - ? (calls ?? []) - : token === MAINNET_USDT_ADDRESS - ? [ - { - abi: erc20Abi, - functionName: "approve", - args: [paymasterERC20Address, 0n], // USDT on mainnet requires zero approval first - to: paymasterContext.token - }, - { - abi: erc20Abi, - functionName: "approve", - args: [paymasterERC20Address, maxCostInToken], - to: paymasterContext.token - }, - ...(calls ?? []) - ] - : [ - { - abi: erc20Abi, - functionName: "approve", - args: [paymasterERC20Address, maxCostInToken], - to: paymasterContext.token - }, - ...(calls ?? []) - ] + let finalCalls: unknown[] = calls ? [...calls] : [] + + if (!hasSufficientApproval) { + finalCalls.unshift({ + abi: erc20Abi, + functionName: "approve", + args: [ + paymasterERC20Address, + hasSufficientApproval ? maxCostInToken : 0n + ], + 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], // USDT on mainnet requires zero approval first + to: paymasterContext.token + }) + } userOperation.callData = await account.encodeCalls( finalCalls.map((call_) => { From c176bbb0339509bea513246438ee3064590caec4 Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Thu, 20 Mar 2025 11:57:49 +0000 Subject: [PATCH 4/7] simplify --- .../pimlico/utils/prepareUserOperationForErc20Paymaster.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts b/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts index be3f6f62..5037361a 100644 --- a/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts +++ b/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts @@ -258,7 +258,7 @@ export const prepareUserOperationForErc20Paymaster = const hasSufficientApproval = allowance >= maxCostInToken - let finalCalls: unknown[] = calls ? [...calls] : [] + const finalCalls: unknown[] = calls ? [...calls] : [] if (!hasSufficientApproval) { finalCalls.unshift({ From 2cb10ea42cff1e1126d6ee5924a5e6b7ea09d7d5 Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Thu, 20 Mar 2025 11:59:24 +0000 Subject: [PATCH 5/7] fix comments --- .../pimlico/utils/prepareUserOperationForErc20Paymaster.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts b/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts index 5037361a..1a21c928 100644 --- a/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts +++ b/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts @@ -147,7 +147,7 @@ export const prepareUserOperationForErc20Paymaster = callsWithDummyApproval.unshift({ abi: erc20Abi, functionName: "approve", - args: [paymasterERC20Address, 0n], // USDT on mainnet requires zero approval first + args: [paymasterERC20Address, 0n], to: paymasterContext.token }) } @@ -277,7 +277,7 @@ export const prepareUserOperationForErc20Paymaster = finalCalls.unshift({ abi: erc20Abi, functionName: "approve", - args: [paymasterERC20Address, 0n], // USDT on mainnet requires zero approval first + args: [paymasterERC20Address, 0n], to: paymasterContext.token }) } From 7152167b722020d1314afb9b03d9120c7cb23b9e Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Thu, 20 Mar 2025 12:00:32 +0000 Subject: [PATCH 6/7] nit --- .../pimlico/utils/prepareUserOperationForErc20Paymaster.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts b/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts index 1a21c928..1292f4c1 100644 --- a/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts +++ b/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts @@ -148,7 +148,7 @@ export const prepareUserOperationForErc20Paymaster = abi: erc20Abi, functionName: "approve", args: [paymasterERC20Address, 0n], - to: paymasterContext.token + to: MAINNET_USDT_ADDRESS }) } @@ -278,7 +278,7 @@ export const prepareUserOperationForErc20Paymaster = abi: erc20Abi, functionName: "approve", args: [paymasterERC20Address, 0n], - to: paymasterContext.token + to: MAINNET_USDT_ADDRESS }) } From be2d518c2a98295c6732a48f1f32cbf4d1320aba Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Thu, 20 Mar 2025 12:49:00 +0000 Subject: [PATCH 7/7] fix tests --- .../pimlico/utils/prepareUserOperationForErc20Paymaster.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts b/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts index 1292f4c1..6026bd67 100644 --- a/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts +++ b/packages/permissionless/experimental/pimlico/utils/prepareUserOperationForErc20Paymaster.ts @@ -264,10 +264,7 @@ export const prepareUserOperationForErc20Paymaster = finalCalls.unshift({ abi: erc20Abi, functionName: "approve", - args: [ - paymasterERC20Address, - hasSufficientApproval ? maxCostInToken : 0n - ], + args: [paymasterERC20Address, maxCostInToken], to: paymasterContext.token }) }