Repository navigation
This release adds private record inventory to the core SDK and Shield Swap: a view of an account's spendable records, a planner for the joins and splits that reach a chosen layout, and a shared reservation journal that keeps concurrent writers from spending the same record. It is a port of Veil PR #172 without the CLI.
python -m pip install aleo-sdk==0.8.0
python -m pip install aleo-contract-abi-generator==0.8.0
python -m pip install "shield-swap-sdk[async,mcp]==0.8.0"
python -m pip install "aleo-bridge-sdk[mcp]==0.8.0"Core SDK
Record inventory
One transaction spends whole records, so a large payment needs one record that covers it and several payments submitted together need one record each. The new aleo.inventory module reads an asset's spendable records, plans the joins and splits that reach a target layout, and executes that plan one confirmed transaction at a time, over credits.aleo and ARC-20/ARC-22 tokens. The actions keep Veil's names in snake_case: get_record_inventory, plan_record_inventory, rebalance_record_inventory, reconcile_record_inventory, join_records, split_record, and auto_join.
RecordInventoryandAsyncRecordInventorybehave identically; one engine drives both. Delegated proving is the default, andproving="local"proves in process.- The planner follows Veil's and passes Veil's planner tests. It differs in one case: when halving a record would leave a lot below the minimum, the split cuts whole lots instead, so a reachable target is never rejected (#83). Every plan Veil accepts is unchanged.
- The join-helper registry covers the credits and stablecoin static helpers and the
main_/test_aj_arc20_2_15.aleodynamic routers. Each helper is checked against its deployed signature before use. - The journal is a
RecordInventoryStore, a three-method protocol (list,acquire,update), so it can live in any backend. Three ship with the SDK.MemoryRecordInventoryStorelasts for one process.JsonFileRecordInventoryStore(path)keeps one JSON file under a file lock and can be read in an editor.SqliteRecordInventoryStore(path)uses the standard library'ssqlite3with the same table layout and row JSON as Veil's store, so the two SDKs can share a journal; scope strings match Veil's. The two file-backed stores release a reservation left by an SDK process on the same machine that exited before proving, and keep entries a Veil wallet wrote.
Managed writes on the facade
Enable record management on a client and every write reserves its record inputs before proving, so writers that share a journal never spend the same record. Build the client with Aleo(provider, record_management=RecordManagementConfig(enabled=True, store=...)); aleo.inventory is the same journal. The journal follows the two halves of a write: build_transaction reserves the inputs and saves the proved transaction, submit_transaction records the broadcast, transact is the two in sequence, and delegate does both through the prover (it asks the prover not to broadcast and submits from the SDK). TransactionResult.discard() releases a built transaction that was never sent. Under a private fee, the fee record is chosen once the exact fee is known, held in the same journal entry as the execution inputs, checked against the proved transaction, and kept after a rejected execution. Clients that do not enable record management behave as before.
The rules that protect funds: a record another writer holds raises RecordReservationConflictError before any proof is built. A broadcast the node refuses outright on its first attempt releases the reservation. A timeout, a lost response, a 5xx, or a refusal that follows one of those keeps it, because the transaction may be in the node; reconcile_record_inventory() settles such a spend against the chain. discard() does nothing once a broadcast has started, and submit_transaction refuses a result that was discarded. Overlapping submissions of one result are counted, so a duplicate refused while the first request is pending releases nothing.
Other
AleoNetworkClient.submit_transactionand its async twin takeretry=(defaultTrue). The managed broadcast makes single attempts so each outcome reaches the journal.- Nineteen new top-level exports:
RecordInventory,RecordManagementConfig, the three stores, the plan and record types,InventoryError, andRecordReservationConflictError. Both stub files carry them.
Shield Swap SDK
dex.inventoryis the facade's inventory.plan_inventory(token, target)andrebalance_inventory(...)target the token's underlying record program, the records a trader holds.- Record selection skips reserved nonces. With management enabled and automatic selection, a swap that loses its record to another writer moves to the next eligible record from the same scan, smallest first, before any proof is built. An explicit
token_recordis never replaced, and execution errors are never retried. examples/concurrent-swaps/rebalances to six equal USDCx records, submits six swaps at once, claims each output, and shows the recovery steps after an interruption, on the SQLite store.import aleo_shield_swapraises anImportErrornaming the pip upgrade when the installedaleo-sdkpredatesaleo.inventory. Thealeo-sdkfloor is>=0.8.0.- The agent guide gained
plan_inventoryandrebalance_inventory; its size budget is now 36k characters.
Verified on testnet with the e2e account (tests/integration/test_record_inventory_live.py): rebalance to six equal USDCx and ETH records, an explicit round trip followed by a seven-record stablecoin helper join, six concurrent swaps in each direction where all six first chose the same record and 15 reservation conflicts were resolved by reselection, every output claimed, and the journal reopened with 28 confirmed transactions and no input spent twice. The suite passed five times on 2026-10-23, the last run at the merged head.
Aleo Bridge SDK
Version bump. The aleo-sdk floor stays at >=0.7.0.
ABI generator
Version bump. sdk-abi/ stays on snarkvm 4.11.0 behind Leo's pin.
CI
sccache on the macOS and Windows wheel builds uses the current GitHub Actions cache API (#81).
The release contains record inventory #82, which includes the 0.8.0 version bump (#84), and #81.
Full diff: v0.7.0...v0.8.0