Skip to content

Conversation

@andysim3d
Copy link
Collaborator

@andysim3d andysim3d commented Apr 25, 2025

Pull Request Checklist


PR-Codex overview

This PR introduces a new PolicyToken type and integrates it into the erc7677Middleware. It enhances the middleware to support ERC-20 paymaster interactions, allowing for token-based fee payments in user operations.

Detailed summary

  • Added PolicyToken type with properties: tokenAddress, approvalMode, maxTokenAmount, erc20Name, and version.
  • Updated ClientMiddlewareArgs to include policyToken.
  • Enhanced erc7677Middleware to calculate token amounts based on gas fees and policyToken.
  • Implemented logic for requesting token conversion rates and creating permit data for ERC-20 tokens.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

@vercel
Copy link

vercel bot commented Apr 25, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
aa-sdk-site ❌ Failed (Inspect) Apr 25, 2025 7:55pm
aa-sdk-ui-demo ❌ Failed (Inspect) Apr 25, 2025 7:55pm

| TContext;
};

const Eth2Wei: bigint = 1_000_000_000_000_000_000n;
Copy link
Contributor

@jakehobbs jakehobbs Apr 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this may be of use to you (if this is used to convert eth to wei): https://viem.sh/docs/utilities/parseEther.html

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also if you do need a constant, TypeScript convention is upper snake-case, e.g. ETH_2_WEI, although I would probably name this something like WEI_PER_ETH which is a big more specific.


let max_token: BigInt | undefined = undefined;
if (policyToken !== undefined) {
let total_gas = BigInt(0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit (style): we normally camel case variable names in ts

? BigInt(uo.verificationGasLimit.toLocaleString())
: BigInt(0);

let paymasterAddress = "0x";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Nit) If this variable is always going to be assigned to later, then don't initialize it with an assignment. Instead do

let paymasterAddress: Hex;

This has two advantages:

  • It gives it the more narrow type Hex instead of string, meaning it would be a type error if we accidentally assign a non-hex value to it later.
  • It makes it a type error if we forget to assign to it in one of the branches below.

let paymasterAddress = "0x";

if (entrypoint.version === "0.7.0") {
const uo_07 = uo as UserOperationStruct<"0.7.0">;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to reiterate: uo is not a UserOperationStruct<"0.7.0">, it is a PromiseOrValue<UserOperationStruct<"0.7.0">>. You should read from userOp instead.

const uo_06 = uo as UserOperationStruct<"0.6.0">;
paymasterAddress = uo_06.paymasterAndData
? uo_06.paymasterAndData.toString().slice(0, 42)
: "0x";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we not need to update total_gas in this branch?

| TContext;
};

const Eth2Wei: bigint = 1_000_000_000_000_000_000n;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also if you do need a constant, TypeScript convention is upper snake-case, e.g. ETH_2_WEI, although I would probably name this something like WEI_PER_ETH which is a big more specific.

},
} as const;

context["erc20Context"] = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Nit) Can write this as context.erc20Context.

)) /
Eth2Wei;

const typed_permit_data = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks unused. (Ignore if you just haven't written the part that uses it yet).

@andysim3d andysim3d force-pushed the andy/erc20-paymaster branch 4 times, most recently from fe4f552 to 918b990 Compare April 30, 2025 14:11
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does erc7677 define how erc20 should be handled or is the logic in this file specific to alchemy?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this isn't to a specific spec (it doesn't look like it since it looks similar to the logic for alchemy_requestGasAndPaymasterAndData), then we shouldn't it in here. Instead we should put it just in gasManager.ts in account-kit/infra

context: { policyId: policyId },
});
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

regarding my comment about the logic in aa-sdk/core. I think that logic should go within the alchemyGasManagerMiddleware.

you can leverage the base middleware:

erc7677Middleware<{ policyId: string | string[] }>({
    context: { policyId: policyId },
  })

to make calls to the RPC and do all of the 7677 stuff, but since you also need to sign permit data based on mode you'll likely have to wrap the middleware here.

likely something like:

export function alchemyGasManagerMiddleware(
  policyId: string | string[]
): Pick<ClientMiddlewareConfig, "dummyPaymasterAndData" | "paymasterAndData"> {
  const baseMiddleware = erc7677Middleware<{ policyId: string | string[] }>({
    context: { policyId: policyId },
  });
  // don't actually do this with ...middlewareParams it's just cuz I'm being too lazy to scroll to what should actually be here
  return {
    dummyPaymasterAndData: (...args) => {
      // if you need custom logic, do it here and at some point use:
      baseMiddleware.dummyPaymasterAndData(...args);
    },
    paymasterAndData: (...args) => {
      // do your custom logic, call baseMiddleware.paymasterAndData(...args), do other custom logic, return result
    }
  }
}

Copy link
Contributor

@moldy530 moldy530 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much of this logic is the same as what's in the alchemyRequestGasAndPaymasterData middleware? Since we have to move the code in aa-sdk/core into account-kit/infra anyways, then we should just share as much of the code as we can

@moldy530
Copy link
Contributor

Also, how are we testing this?

Base automatically changed from andy/erc20-paymaster to main April 30, 2025 16:00
@andysim3d andysim3d closed this May 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants