Skip to content

Align executeContract behavior across account types - #55

Merged
iamalwaysuncomfortable merged 1 commit into
feat/aleo-abi-transitionsfrom
fix/execute-contract-parity
May 13, 2026
Merged

Align executeContract behavior across account types#55
iamalwaysuncomfortable merged 1 commit into
feat/aleo-abi-transitionsfrom
fix/execute-contract-parity

Conversation

@iamalwaysuncomfortable

Copy link
Copy Markdown
Member

Stacked on #54. Addresses the three behavioral-predictability issues raised in review of that PR.

Summary

  1. RPC path now matches local path return shape. Today the RPC branch casts the wallet's { transactionId } response to RawExecuteResulttransitions is undefined and outputs is whatever the wallet happened to put in that field. After this PR, the RPC path polls getConfirmedTransaction itself and runs extractTransitions on the confirmed tx. The SDK does not broker decryption on behalf of the wallet: record outputs come back as raw record1... ciphertexts on the RPC path. Plaintext outputs come through verbatim. Local-account semantics are unchanged.

  2. executeTransaction aligned with the Aleo wallet adapter spec. Previously the wallet client method executeTransaction was bound to the heavy executeContract action, while the action-level alias inside writeContract.ts pointed executeTransaction to writeContract — three sites disagreed. Now executeTransaction is consistently aliased to writeContract (light, returns tx id) everywhere: wallet client, top-level package export, action layer. The full-lifecycle action is exposed as executeContract on the wallet client and under its own name as a top-level export.

  3. outputs is the called function's transition only on both paths. The raw layer no longer flattens all transition outputs into one array; transitions[] carries the per-transition data. RawExecuteResult.outputs is the called function's transition's outputs (always the last element of execution.transitions[] per Aleo execution semantics, where inner cross-program transitions are recorded before their callers).

Refactoring side-effects

  • waitForConfirmation and extractTransitions moved from packages/provable/src/index.ts closures to shared utilities under packages/core/src/utils/. Both reachable via @veil/core exports.
  • extractTransitions takes an optional Decryptor parameter: (ciphertext: string) => string | null. The local path passes a view-key decryptor (self-custodied); the RPC path passes none (permission boundary).
  • Position-based top-level identification replaces name matching — Aleo execution semantics guarantee the called function's transition is the last in execution.transitions[].

Files changed

  • packages/core/src/actions/wallet/executeContract.ts — RPC path rewritten (submit → poll → extract)
  • packages/core/src/clients/decorators/wallet.ts — `executeTransaction` → `writeContract`; expose `executeContract`
  • packages/core/src/contract/getContract.ts — proxy calls `executeContract`; outputs derivation simplified
  • packages/core/src/index.ts — exports realigned; export new utilities
  • packages/core/src/types/proving.ts — `outputs` docstring updated
  • packages/core/src/utils/extractTransitions.ts — new shared utility
  • packages/core/src/utils/waitForConfirmation.ts — new shared utility
  • packages/provable/src/index.ts — drop local closures; use shared utilities with a view-key decryptor

Test plan

  • All existing tests pass.
  • New tests in executeContract.test.ts:
    • `RPC: submits via wallet, polls for confirmation, returns RawExecuteResult`
    • `RPC: forwards wallet-adapter param shape (privateFee + imports as string[], no fee/programSource)`
    • `RPC: record ciphertexts pass through as raw strings (no decryption)`
  • New tests in createWalletClient.test.ts:
    • `executeTransaction and writeContract produce identical requests` (behavioral, not just type)
    • `top-level executeTransaction export === writeContract`
  • New test in getContract.test.ts: `outputs is empty when no transition matches the called program/function`.
  • `pnpm vitest run` — 691 passing, 0 failing.
  • `pnpm --filter @veil/core exec tsc --noEmit` — 0 errors.
  • `pnpm --filter @veil/loyalty-dapp exec tsc --noEmit` — clean.

- RPC path now polls for confirmation and walks transitions itself, returning
  the same {transactionId, transitions, outputs} shape as the local path. The
  SDK does not ask the wallet to decrypt — record outputs surface as raw
  record1... ciphertexts on the RPC path.
- executeTransaction aliased to writeContract everywhere (matches the Aleo
  wallet adapter spec); the full-lifecycle action lives as executeContract.
- outputs is the called function's transition only on both paths; inner
  cross-program transitions are surfaced via transitions[].
- Extract waitForConfirmation and extractTransitions to shared core utilities.
  Position-based top-level identification replaces name matching.
@vercel

vercel Bot commented May 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
veil-loyalty-dapp Ready Ready Preview, Comment May 13, 2026 4:11am

Request Review

@marshacb marshacb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

@iamalwaysuncomfortable
iamalwaysuncomfortable merged commit 5c7daff into feat/aleo-abi-transitions May 13, 2026
2 checks passed
marshacb added a commit that referenced this pull request May 13, 2026
* Add per-transition output parsing: structured transitions from execute with program/function metadata

* Handle record_with_dynamic_id outputs in transition parser

* Add dynamicId record output test for per-transition parsing

* Align executeContract behavior across account types (#55)

- RPC path now polls for confirmation and walks transitions itself, returning
  the same {transactionId, transitions, outputs} shape as the local path. The
  SDK does not ask the wallet to decrypt — record outputs surface as raw
  record1... ciphertexts on the RPC path.
- executeTransaction aliased to writeContract everywhere (matches the Aleo
  wallet adapter spec); the full-lifecycle action lives as executeContract.
- outputs is the called function's transition only on both paths; inner
  cross-program transitions are surfaced via transitions[].
- Extract waitForConfirmation and extractTransitions to shared core utilities.
  Position-based top-level identification replaces name matching.

* Align codegen and inference integer types to bigint for all bit widths

---------

Co-authored-by: Mike Turner <mike@provable.com>
iamalwaysuncomfortable added a commit that referenced this pull request May 13, 2026
* init types/tests

* adding storage variable parsing from aleo ABI

* remove extra loop for vec storage var checking

* Add contract runtime layer: record serialization, execution actions, and RecordFieldValue.type (#39)

* Add contract runtime utilities: parseRecordPlaintext, toPlaintext, encodeInputs, simulateContract, executeContract, and RecordFieldValue.type for round-trip serialization

* Add contract runtime layer: record serialization, input encoding, simulateContract, executeTransaction, and RecordFieldValue.type for round-trip support

* Rename toPlaintext to toString, scope ExecuteResult.outputs to top-level function

* Add program and recordName to RecordValue, rename toPlaintext to toString, scope ExecuteResult.outputs to top-level function

* Add ABI-aware helpers (getRecordDef, getInputTypes, ABI overloads), Leo compiler format normalization in parseAbi, and serializeRecord alias

* Implement execute lifecycle: local proving, delegated DPS, and fee handling (#46)

* Implement execute lifecycle: local proving + delegated DPS, fee conversion, confirmation polling, and end-to-end integration tests

* Use TransactionJSON from SDK instead of inline type for transaction output extraction

* Add @veil/codegen: typed contract factories with named params, typed returns, and simulate/execute proxies (#45)

* Add @veil/codegen package: generate TypeScript types, record mappers, and function I/O types from Aleo program ABIs

* Add typed contract factory generation to codegen (createXContract with embedded ABI)

* Add typed contract interface with autocomplete for function and mapping names

* Wire auto-encode inputs and auto-parse outputs into getContract proxies, accept ABI type, add comprehensive proxy tests

* Update codegen to use InputValue/ParsedOutput types matching getContract proxy signatures

* Handle unrecognized values gracefully in parseRecordPlaintextLoose

* Generate named params and typed returns in contract factory (closes Ethereum UX gap for inputs and outputs)

* Fix result.outputs reference bug and add _record support for record re-consumption in codegen

* Fix generated type errors: cast plaintext outputs through unknown, narrow abi to ABI

* Clean up generated code: remove unused imports, replace as any with indexed access types, add client validation, fix unused result in void wrappers

* Use structs for ABI detection, remove references in comments

* Re-implement simulate/execute in loadNetwork architecture, update integration tests for dynamic SDK pattern

* Add loyalty-node example app (#43)

* Add loyalty-node example app using @veil/core + @veil/provable with simulate support

* Simplify loyalty-node app to use ABI-aware utilities (no manual loadAbi, getInputTypes, or recordToString)

* Use generated contract factories with simulate proxies, add codegen dependency and generate script

* Use auto-encode/auto-parse contract proxies with native values and RecordValue inputs

* Use codegen named params and typed returns, remove manual helpers and output casts

* Add read_state demo to showcase mapping reads and complete namespace coverage

* Restructure demos around simulate/execute/read namespaces, add ALEO_PRIVATE_KEY env support

* Fix toCredits precision, use funded SDK account, simplify execute demo to single on-chain step

* Adapt loyalty-node to loadNetwork pattern, fix duplicate Input/Output exports

* Add type-level ABI inference for getContract (#48)

* Add type-level ABI inference for getContract: function/mapping name narrowing and typed namespaces

* Map future/dynamicFuture to FutureValue in codegen instead of void

* Add optional dynamicId to record types for RecordWithDynamicID and ExternalRecordWithDynamicID support

* Add typed error classes for transaction lifecycle with error classification (#49)

* Add typed error classes for transaction lifecycle: broadcast, proving, timeout, finalize, and duplicate errors

* Address review feedback: options bag constructors, split record errors, ConfigurationError, lastError tracking, getStatus helper

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: mia <93600681+miazn@users.noreply.github.com>

* Add per-transition output parsing to execute lifecycle (#54)

* Add per-transition output parsing: structured transitions from execute with program/function metadata

* Handle record_with_dynamic_id outputs in transition parser

* Add dynamicId record output test for per-transition parsing

* Align executeContract behavior across account types (#55)

- RPC path now polls for confirmation and walks transitions itself, returning
  the same {transactionId, transitions, outputs} shape as the local path. The
  SDK does not ask the wallet to decrypt — record outputs surface as raw
  record1... ciphertexts on the RPC path.
- executeTransaction aliased to writeContract everywhere (matches the Aleo
  wallet adapter spec); the full-lifecycle action lives as executeContract.
- outputs is the called function's transition only on both paths; inner
  cross-program transitions are surfaced via transitions[].
- Extract waitForConfirmation and extractTransitions to shared core utilities.
  Position-based top-level identification replaces name matching.

* Align codegen and inference integer types to bigint for all bit widths

---------

Co-authored-by: Mike Turner <mike@provable.com>

* Remove NetworkRecordProvider, use RecordScanner as sole scanning path (#56)

* Return per-transition outputs from simulateContract (#57)

* Return per-transition outputs from simulateContract

- SimulateContractReturnType (and RawSimulateResult) now carries transitions[]
  alongside outputs, matching ExecuteContractReturnType's shape. outputs is the
  called function's transition only; inner cross-program transitions are
  surfaced via transitions[].
- Contract proxy's simulate.fn() now returns { transitions, outputs } with the
  same per-transition parsing as execute.fn() — same-program transitions parsed
  with the local ABI, foreign loose-parsed.
- Provable's simulate uses programManager.buildAuthorization instead of
  programManager.run. The Authorization carries the transition list with
  outputs (same structure a confirmed Transaction has, minus the proof) and
  doesn't run a full circuit, so it's faster. Outputs flow through the shared
  extractTransitions with the local-account view-key decryptor.

* Add cross-program Authorization integration test

Surfaces a real wire-format gap: after `decryptTransition(tvk)`, `outputs(true)`
returns JS-native values (`10`) instead of Aleo-typed strings (`'10u32'`).
`transition.toString()` emits the same wire-format JSON the chain returns from
`/transaction/confirmed/{id}`, so the simulate path now JSON.parses that for
each transition before handing it to extractTransitions — outputs are
consistent with the execute path.

Test vectors (multiply_test.aleo / double_test.aleo) taken from the
@provablehq/sdk JSDoc examples.

---------

Signed-off-by: mia <93600681+miazn@users.noreply.github.com>
Co-authored-by: marshacb <cameron.marshall12@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Mike Turner <mike@provable.com>
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.

2 participants