Skip to content

feat: wallet routes public view calls directly through node for simulations#20245

Merged
AztecBot merged 1 commit intonextfrom
gj/public_static_optimization
Feb 9, 2026
Merged

feat: wallet routes public view calls directly through node for simulations#20245
AztecBot merged 1 commit intonextfrom
gj/public_static_optimization

Conversation

@Thunkar
Copy link
Contributor

@Thunkar Thunkar commented Feb 6, 2026

Optimization suggested by @olehmisar and implemented slightly differently here.

Reading public information from wallets is way faster AND parallelizable if we go directly to the node. This PR assembles a fake Tx object with only PublicCalls, that the node will accept and simulate in batches of 32.

@Thunkar Thunkar self-assigned this Feb 6, 2026
@Thunkar Thunkar added ci-full Run all master checks. ci-merge-queue labels Feb 6, 2026
Comment on lines +311 to +317
* Simulates a transaction, optimizing public static calls by running them directly
* on the node while sending the remaining calls through the standard PXE path.
* Return values from both paths are merged back in original call order.
* @param executionPayload - The execution payload to simulate.
* @param opts - Simulation options (from address, fee settings, etc.).
* @returns The merged simulation result.
*/
Copy link
Contributor

Choose a reason for hiding this comment

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

The "optimizing public static calls by running... etc" seems like an extremely ad-hoc comment that makes sense when reviewing this PR but feels odd to highlight in general. Also it leaks implementation details that will make it probably a bit harder to grok to an occasional dev

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mmm, I think it's important in this case. Furthermore, we should instruct app devs to try and run public static calls first on their payloads (or even on a dedicated payload!) so they can take advantage of the optimization. This is why I debated having the optimization be in the wallet or providing a standalone function, discoverability.

true /* simulatePublic */,
opts?.skipTxValidation,
opts?.skipFeeEnforcement ?? true,
const { publicStaticCalls, otherCalls } = extractOptimizablePublicStaticCalls(executionPayload);
Copy link
Contributor

Choose a reason for hiding this comment

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

might feel like I'm contradicting myself but while I think we shouldn't mention the optimization in the js-doc of this function, the function body could use some comments to explain why we're going into the trouble of writing it like this

Copy link
Contributor

@nchamo nchamo left a comment

Choose a reason for hiding this comment

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

Left some small suggestions

Copy link
Contributor

@mverzilli mverzilli left a comment

Choose a reason for hiding this comment

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

Left a bunch of comments but nothing blocker. Perhaps the main concern would be making sure that we're not leaking functions from utils that are too implementation oriented, which then people will start using, which then we might need to maintain/deprecate/document/etc

@Thunkar Thunkar force-pushed the gj/public_static_optimization branch from e4507d3 to 04c9a63 Compare February 9, 2026 08:45
Copy link
Contributor

@nchamo nchamo left a comment

Choose a reason for hiding this comment

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

Great work, really like how it looks now 🔥

@Thunkar Thunkar removed ci-full Run all master checks. ci-merge-queue labels Feb 9, 2026
Comment on lines +69 to +71
const balanceOf = await makeFunctionCall(FunctionType.PUBLIC, true, 'balanceOf');
const totalSupply = await makeFunctionCall(FunctionType.PUBLIC, true, 'totalSupply');
const transfer = await makeFunctionCall(FunctionType.PRIVATE, false, 'transfer');
Copy link
Contributor

Choose a reason for hiding this comment

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

mega-nit: I had to keep going to makeFunctionCall signature to remind myself what true and false mean here. would suggest to make it either a named param or even split in two auxes: (makeStaticFunctionCall and makeNonStaticFunctionCall)... or perhaps an enum (akin to FunctionType.PRIVATE etc)

const transfer = await makeFunctionCall(FunctionType.PRIVATE, false, 'transfer');
const payload = new ExecutionPayload([balanceOf, totalSupply, transfer], [], []);

const optimizedRv0 = new NestedProcessReturnValues([new Fr(100)]);
Copy link
Contributor

Choose a reason for hiding this comment

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

NestedProcessReturnValues is a perplexing name but maybe it's just I'm not familiar with it 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This can be refactored when we start pulling callstack data from the node

}

/**
* Simulates calls through the standard PXE path (account entrypoint).
Copy link
Contributor

Choose a reason for hiding this comment

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

can we maybe comment a bit more on the alternative/s to this? or what it is useful for?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm refactoring this on a followup PR that creates an @aztec/wallets package, moves test-wallet to e2e, etc

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this is the dual of simulateViaNode, so maybe referring to it would be enough

@AztecBot
Copy link
Collaborator

AztecBot commented Feb 9, 2026

Flakey Tests

🤖 says: This CI run detected 1 tests that failed, but were tolerated due to a .test_patterns.yml entry.

\033FLAKED\033 (8;;http://ci.aztec-labs.com/80dc29f9f8e774ac�80dc29f9f8e774ac8;;�): yarn-project/scripts/run_test.sh p2p/src/client/test/p2p_client.integration_message_propagation.test.ts (18s) (code: 1) group:e2e-p2p-epoch-flakes

throw publicOutput.revertReason;
}

return new TxSimulationResult(privateResult, provingResult.publicInputs, publicOutput, undefined);
Copy link
Contributor

Choose a reason for hiding this comment

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

is privateResult garbage here? wondering if we should allow some falsy type to signal that, or at least comment somewhere

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's essentially garbage, but I think it's better to keep the shape consistent for downstream consumers. We can probably iterate on this when we work on adding more stuff (eg offchain effects) to the returns of simulate and send

…ations

Optimization suggested by @olehmisar and implemented slightly differently here.

Reading public information from wallets is way faster AND parallelizable if we go directly to the node. This PR assembles a fake Tx object with only PublicCalls, that the node will accept and simulate in batches of 32.

Co-authored-by: Charlie <5764343+charlielye@users.noreply.github.com>
Co-authored-by: Gregorio Juliana <gregojquiros@gmail.com>
Co-authored-by: Nicolas Chamo <nicolas@chamo.com.ar>
Co-authored-by: benesjan <13470840+benesjan@users.noreply.github.com>
Co-authored-by: mverzilli <651693+mverzilli@users.noreply.github.com>
Co-authored-by: mverzilli <martin@aztec-labs.com>
Co-authored-by: thunkar <gregojquiros@gmail.com>
@AztecBot AztecBot force-pushed the gj/public_static_optimization branch from 80a0c19 to 8b6a0b4 Compare February 9, 2026 10:29
@AztecBot AztecBot enabled auto-merge February 9, 2026 10:29
@AztecBot AztecBot added this pull request to the merge queue Feb 9, 2026
from: await AztecAddress.random(),
to: await AztecAddress.random(),
amount: BigInt(amount),
it('splits a mixed payload into optimized and entrypoint paths and merges results', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

we could maybe add some more test cases:

  • all optimizable
  • none optimizable
  • some optimizable after some non-optimizable (if I understand correctly in this case we don't want to optimize?)

Merged via the queue into next with commit 3f917d3 Feb 9, 2026
20 checks passed
@AztecBot AztecBot deleted the gj/public_static_optimization branch February 9, 2026 11:05
@benesjan
Copy link
Contributor

benesjan commented Feb 16, 2026

This is now causing me a bit of a problem in this PR as I am working on displaying the debug logs from public function simulations because:

  1. I have to handle it in 2 places: once in simulateTx and once in yarn-project/wallet-sdk/src/base-wallet/utils.ts,
  2. in base-wallet in the simulateBatchViaNode I don't have access to contractStore and hence I am not able to get there the contract name and make the log be pretty.

Was there some fundamental reason why we didn't want pxe to handle this optimization?

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.

5 participants