Skip to content

fix(paymaster): compile the calldata of the calls like execute(), in getOutsideCall and in Contract - #1736

Merged
PhilippeR26 merged 1 commit into
starknet-io:developfrom
PhilippeR26:fix/paymasterEncode
Oct 21, 2026
Merged

PhilippeR26 merged 1 commit into
starknet-io:developfrom
PhilippeR26:fix/paymasterEncode

Conversation

@PhilippeR26

Copy link
Copy Markdown
Collaborator

Motivation and Resolution

This PR fixes two bugs of the paymaster path. The first one is a regression of 11.4.1.

1. A parameter given as an object broke the paymaster safety check. Since 11.4.1 (#1734), the safety check rebuilds the expected outside execution from the user calls, with getTypedData and getOutsideCall. getOutsideCall kept an array calldata as it was. So a parameter given as an object, like cairo.uint256(100), reached the typed-data hash. The transaction then failed before signing, with Invalid BigNumberish: [object Object]. This hit contract.invoke(…, { paymasterDetails }), contract.withOptions({ paymasterDetails }), and account.executePaymasterTransaction with such a call. account.getOutsideTransaction goes through the same code, and had the same problem.

Resolution: getOutsideCall now follows the rule of execute() (see fromCallsToExecuteCalldata_cairo1). A calldata marked as compiled is kept as it is. Any other calldata is a list or an object of parameters, and is compiled with CallData.compile.

2. Contract sent the raw arguments to the paymaster. In contract.invoke and contract.estimate, the paymaster branch built the call with the raw arguments, not with the calldata compiled with the ABI. For example, with transfer(recipient, 100), the u256 amount was sent as one felt instead of two.

Resolution: both branches now send the call compiled with the ABI, the same call that execute() uses.

RPC version (if applicable)

Not applicable.

Usage related changes

  • contract.invoke and contract.withOptions with paymasterDetails, and account.executePaymasterTransaction, work again when a parameter is an object, like cairo.uint256(100).
  • With paymasterDetails, contract.invoke and contract.estimate now send the calldata compiled with the ABI, like execute().
  • account.getOutsideTransaction accepts the same calldata forms as execute(). In its result, outsideExecution.calls[].calldata is now always written as decimal strings, even when the call gave hex values. The values and the signed hash do not change.

Development related changes

  • New unit test file __tests__/utils/outsideExecution.test.ts, with 4 tests. npm run test:unit runs it too.
  • __tests__/accountPaymaster.test.ts: a new test sends a call with a u256 object through the paymaster safety check.
  • __tests__/pluginPaymaster.test.ts: two new tests check that contract.invoke and contract.estimate with paymasterDetails send the calldata compiled with the ABI.
  • The six new tests failed before the fix, and pass after it.
  • Checks done:
    • npm run ts:check and ESLint pass.
    • The full suite on devnet passes: 3181 tests.
    • __tests__/contractPaymaster.test.ts on Sepolia (Pathfinder) passes 4/4. Its two transfer tests failed with 11.4.1. They now send their transactions through the AVNU paymaster.
  • Note: contractPaymaster.test.ts is the only test of the Contract + paymaster path with a real paymaster. It runs only on Testnet, and the CI runs the Testnet tests only for main. This is why the regression was not caught before the 11.4.1 release.

Checklist:

  • Performed a self-review of the code
  • Rebased to the last commit of the target branch (or merged it into my branch)
  • Documented the changes in code (API docs will be generated automatically)
  • Updated the tests
  • All tests are passing

…getOutsideCall and in Contract

Since 11.4.1, the paymaster safety check rebuilds the expected outside execution from the user calls. getOutsideCall kept an array calldata as it was, so a parameter given as an object, like cairo.uint256(100), reached the hash and failed with "Invalid BigNumberish". contract.invoke, contract.estimate and withOptions with paymasterDetails hit it with any u256 object argument. account.getOutsideTransaction had the same problem.

getOutsideCall now follows the rule of execute(): a calldata marked as compiled is kept, and any other calldata is a list or an object of parameters, which is flattened. For a flat calldata, the values do not change: they are only written in decimal in the OutsideCall.

The paymaster branch of contract.invoke and contract.estimate sent the raw arguments. It now sends the calldata compiled with the abi, like execute(). Before, a u256 given as a number, as in transfer(recipient, 100), was sent as one felt instead of two.

New unit tests cover both cases, and the Sepolia test contractPaymaster.test.ts passes again.
@PhilippeR26 PhilippeR26 mentioned this pull request Oct 21, 2026
6 tasks
@PhilippeR26
PhilippeR26 merged commit c38db7b into starknet-io:develop Oct 21, 2026
9 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 11.4.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant