Summary
Building a bare FunctionCall means filling in eight fields by hand, seven of which the contract
artifact already describes:
FunctionCall.from({
name: "transfer", // artifact
to: someAddress, // caller
selector: await FunctionSelector.fromSignature("transfer((Field),u128,Field)"), // artifact
type: FunctionType.PRIVATE, // artifact
hideMsgSender: false, // call-site option
isStatic: false, // artifact
args: [recipient.toField(), new Fr(amount), nonce], // caller, hand-encoded
returnTypes: [], // artifact
});
aztec codegen already knows every one of those: it generates a typed wrapper per method, complete
with a selector and the argument encoding. But the only thing it can produce is a
ContractFunctionInteraction, and only from a wallet-bound instance
(Contract.at(address, wallet).methods.transfer(...)).
Request
Generate a static, wallet-free builder that returns a FunctionCall directly:
TokenContract.callAt(address).transfer(recipient, amount, nonce); // -> FunctionCall
Codegen supplies name, selector, type, isStatic and returnTypes from the artifact, and
encodes the arguments from their typed values — the same encoding the existing wrapper already
performs. The caller supplies only the target address and the arguments, with hideMsgSender
available as an option where it applies.
Why a static builder specifically
ContractFunctionInteraction.getFunctionCall() already returns the right object, so the gap is not
the conversion — it is that reaching it requires a Wallet and a registered contract instance.
Code that assembles an ExecutionPayload frequently has neither: it runs while the payload is
being built, and holds only the callee's AztecAddress.
Today that code hand-writes a signature string and calls FunctionSelector.fromSignature, which
duplicates the ABI — including the encoding rules (AztecAddress → (Field), u128, …). A
parameter change keeps compiling, produces a different selector, and fails at runtime inside a
private circuit with nothing connecting the failure back to the signature that drifted.
Hand-encoding args has the same weakness: [recipient.toField(), new Fr(amount), nonce] is a
manual re-implementation of the typed wrapper's encoding, and nothing checks it against the ABI.
Who benefits
Anyone assembling an ExecutionPayload outside a wallet-bound contract instance: custom
FeePaymentMethod implementations (the sponsored-FPC pattern and anything like it), account
entrypoints, and batching/multicall helpers.
Relationship to #25297
#25297 asks for the smaller piece — exposing the already-generated selectors statically, e.g.
TokenContract.selectors.transfer. That removes the hand-written signature strings on their own.
This issue asks for the full builder, which additionally removes the FunctionCall.from({...})
boilerplate and the hand-encoded args. Either is useful; this one subsumes the other.
Environment
aztec 5.0.1.
Summary
Building a bare
FunctionCallmeans filling in eight fields by hand, seven of which the contractartifact already describes:
aztec codegenalready knows every one of those: it generates a typed wrapper per method, completewith a selector and the argument encoding. But the only thing it can produce is a
ContractFunctionInteraction, and only from a wallet-bound instance(
Contract.at(address, wallet).methods.transfer(...)).Request
Generate a static, wallet-free builder that returns a
FunctionCalldirectly:Codegen supplies
name,selector,type,isStaticandreturnTypesfrom the artifact, andencodes the arguments from their typed values — the same encoding the existing wrapper already
performs. The caller supplies only the target address and the arguments, with
hideMsgSenderavailable as an option where it applies.
Why a static builder specifically
ContractFunctionInteraction.getFunctionCall()already returns the right object, so the gap is notthe conversion — it is that reaching it requires a
Walletand a registered contract instance.Code that assembles an
ExecutionPayloadfrequently has neither: it runs while the payload isbeing built, and holds only the callee's
AztecAddress.Today that code hand-writes a signature string and calls
FunctionSelector.fromSignature, whichduplicates the ABI — including the encoding rules (
AztecAddress→(Field),u128, …). Aparameter change keeps compiling, produces a different selector, and fails at runtime inside a
private circuit with nothing connecting the failure back to the signature that drifted.
Hand-encoding
argshas the same weakness:[recipient.toField(), new Fr(amount), nonce]is amanual re-implementation of the typed wrapper's encoding, and nothing checks it against the ABI.
Who benefits
Anyone assembling an
ExecutionPayloadoutside a wallet-bound contract instance: customFeePaymentMethodimplementations (the sponsored-FPC pattern and anything like it), accountentrypoints, and batching/multicall helpers.
Relationship to #25297
#25297 asks for the smaller piece — exposing the already-generated selectors statically, e.g.
TokenContract.selectors.transfer. That removes the hand-written signature strings on their own.This issue asks for the full builder, which additionally removes the
FunctionCall.from({...})boilerplate and the hand-encoded
args. Either is useful; this one subsumes the other.Environment
aztec 5.0.1.