From 6becce2f432fa7d1b971abbfb0a2fd814c38c27d Mon Sep 17 00:00:00 2001 From: "tuddman@users.noreply.github.com" Date: Thu, 4 Jun 2026 10:51:00 +0200 Subject: [PATCH 1/3] release: 0.6.0 --- CHANGELOG.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.lock | 10 ++++---- Cargo.toml | 2 +- 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04fe1d5..ff3a56f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,73 @@ change before 1.0. ## [Unreleased] +## [0.6.0] — 2026-06-04 + +The optimistic-ledger release. Trading moved fully off-chain into the +TEE; the chain now sees only deposits, net settlement, and TEE-voucher +withdrawals. This burned the legacy gasless on-chain lock-signing path +and re-bound the SDK to the live **MidribV3** contract. Minor release +because of the source-breaking removals below. + +### Breaking — optimistic-only migration + +- **Gasless on-chain lock signing removed.** Order entry is now + authenticated by the outer envelope signature only (`order_id` + + `amount_in`); there is no per-order on-chain lock. Removed from the + public surface: + - `aspens::evm`: `build_gasless_cross_chain_order`, + `gasless_lock_signing_hash`, and the `MidribV2` / + `IAllowanceTransfer` / `MidribDataTypes` `sol!` bindings. + - `aspens::solana`: `OpenOrderArgs`, `OpenForSignedPayload`, + `OpenForArgs`, `gasless_lock_signing_message`, `derive_order_pda`, + `derive_used_nonce_pda`, and the `ORDER_SEED` / `USED_NONCE_SEED` + constants. + - `aspens::orders`: `GaslessLockParams`. +- **EVM EIP-712 domain version bumped `"2"` → `"3"` (MidribV3).** + `aspens::evm::MIDRIB_EIP712_VERSION` now reads `"3"`. This must stay + in lock-step with the contract and the arborter. +- **`GaslessAuthorization` renamed to `OrderAuthorization`** + (`{ order_id, amount_in }`); `SendOrderRequest.gasless` → + `authorization`. `build_gasless_authorization` now returns + `OrderAuthorization` and is synchronous (no per-chain dispatch, no + Solana `getSlot` RPC). +- **`evm::rpc` re-bound to MidribV3** (`artifacts/MidribV3.json`) for + `deposit` / `withdraw(voucher, sig)` / `tradeBalance`. + `get_locked_balance` now returns `0` — MidribV3 has no on-chain + locked balance under the optimistic ledger. + +### Added + +- **TEE-voucher withdrawal flow** (Track A §8) for both chains, + replacing the removed permissionless on-chain `withdraw`. The SDK now + authenticates the canonical request bytes, calls the gRPC `Withdraw` + RPC to receive the TEE-signed `WithdrawalVoucher`, and submits it + on-chain. `call_withdraw_from_config_with_wallet` (and the `_evm` + variant) gain a `url` param for the gRPC endpoint; CLI, REPL, and the + quickstart example are threaded through. + - Solana withdraw drives the same voucher flow with a bounded retry + on transient post-drain `InsufficientBalance`. + - A pre-flight balance check runs before requesting the voucher; a + rejected submission can resubmit the same voucher. +- **`aspens-admin` operator-fee commands** (fees phase 4c): + `commands::admin::set_operator_fee(chain_network, recipient, bps)` + and `set_operator_admin(chain_network, new_admin)`, driving the + `SetOperatorFee` / `SetOperatorAdmin` RPCs. JWT-authenticated; the + arborter submits the on-chain call. + +### Changed / Removed + +- **`permit2_address` dropped** (proto field 9 of `ChainConfig`), + following the protos source-of-truth removal — it was the per-chain + Permit2 address for the dead on-chain gasless flow. The + `--permit2-address` set-chain CLI arg is gone. +- Removed dead artifacts `MidribV2.json`, `IAllowanceTransfer.json`, + and `MidribDataTypes.sol`. +- Synced proto bindings from `protos@24dac6b` and `protos@d73d1f3`. +- `docs(readme)`: documented all CLI / REPL / admin commands. +- Dropped stale "gasless / on-chain verifier" wording from the + market-order reject path. + ## [0.5.0] — 2026-05-27 A tech-debt sweep that retired legacy public API and tightened the @@ -204,7 +271,8 @@ Pre-0.4.1 history is recorded in git only. The 0.4.x line introduced Solana support, the Wallet enum, and feature gates (`evm`, `solana`, `client`) for lean-signing consumers. -[Unreleased]: https://github.com/aspensprotocol/sdk/compare/0.5.0...HEAD +[Unreleased]: https://github.com/aspensprotocol/sdk/compare/0.6.0...HEAD +[0.6.0]: https://github.com/aspensprotocol/sdk/compare/0.5.0...0.6.0 [0.5.0]: https://github.com/aspensprotocol/sdk/compare/0.4.3...0.5.0 [0.4.3]: https://github.com/aspensprotocol/sdk/releases/tag/0.4.3 [0.4.2]: https://github.com/aspensprotocol/sdk/releases/tag/0.4.2 diff --git a/Cargo.lock b/Cargo.lock index dd1b88c..6ac22c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1084,7 +1084,7 @@ dependencies = [ [[package]] name = "aspens" -version = "0.5.0" +version = "0.6.0" dependencies = [ "alloy", "alloy-chains", @@ -1122,7 +1122,7 @@ dependencies = [ [[package]] name = "aspens-admin" -version = "0.5.0" +version = "0.6.0" dependencies = [ "alloy", "aspens", @@ -1142,7 +1142,7 @@ dependencies = [ [[package]] name = "aspens-cli" -version = "0.5.0" +version = "0.6.0" dependencies = [ "alloy", "alloy-chains", @@ -1162,7 +1162,7 @@ dependencies = [ [[package]] name = "aspens-cliutil" -version = "0.5.0" +version = "0.6.0" dependencies = [ "aspens", "eyre", @@ -1170,7 +1170,7 @@ dependencies = [ [[package]] name = "aspens-repl" -version = "0.5.0" +version = "0.6.0" dependencies = [ "alloy", "alloy-chains", diff --git a/Cargo.toml b/Cargo.toml index 1072608..9aa8625 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ members = [ ] [workspace.package] -version = "0.5.0" +version = "0.6.0" edition = "2021" license = "MIT" repository = "https://github.com/aspensprotocol/sdk" From b63357d0eb256f6dabf10fa4ad3fbc10b1e3e474 Mon Sep 17 00:00:00 2001 From: "tuddman@users.noreply.github.com" Date: Fri, 5 Jun 2026 15:54:29 +0200 Subject: [PATCH 2/3] fix(trading): async send in stream_orderbook_channel to avoid runtime panic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit stream_orderbook_channel called tx.blocking_send inside a tokio::spawn task, which panics ("Cannot block the current thread from within a runtime") on a multi-thread runtime — crashing any in-runtime async consumer such as the rebalancer calling fetch_top_of_book. Extract an async-callback core (stream_orderbook_with) shared by both the sync-callback stream_orderbook primitive and the channel variant; the channel now uses tx.send(entry).await, awaiting backpressure rather than blocking the runtime thread. No public API changes. --- .../src/commands/trading/stream_orderbook.rs | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/aspens/src/commands/trading/stream_orderbook.rs b/aspens/src/commands/trading/stream_orderbook.rs index ca674a5..5e2e080 100644 --- a/aspens/src/commands/trading/stream_orderbook.rs +++ b/aspens/src/commands/trading/stream_orderbook.rs @@ -73,6 +73,31 @@ pub async fn stream_orderbook( ) -> Result<()> where F: FnMut(OrderbookEntry), +{ + // Adapt the synchronous callback into the async-callback core by + // returning an already-ready future for each entry. + stream_orderbook_with(url, options, |entry| { + callback(entry); + std::future::ready(()) + }) + .await +} + +/// Internal core: stream orderbook entries, invoking an **async** callback +/// per entry. +/// +/// Both the sync-callback [`stream_orderbook`] and the channel-based +/// [`stream_orderbook_channel`] delegate here. The async callback is what +/// lets the channel variant `await` a send rather than block the runtime +/// thread (`tx.blocking_send` panics inside a multi-thread tokio runtime). +async fn stream_orderbook_with( + url: String, + options: StreamOrderbookOptions, + mut callback: F, +) -> Result<()> +where + F: FnMut(OrderbookEntry) -> Fut, + Fut: std::future::Future, { // Create a channel to connect to the gRPC server let channel = create_channel(&url).await?; @@ -101,7 +126,7 @@ where while let Some(entry_result) = stream.next().await { match entry_result { Ok(entry) => { - callback(entry); + callback(entry).await; } Err(e) => { tracing::error!("Stream error: {}", e); @@ -250,9 +275,16 @@ pub async fn stream_orderbook_channel( let (tx, rx) = mpsc::channel(100); let handle = tokio::spawn(async move { - stream_orderbook(url, options, |entry| { - // Try to send, ignore if receiver is dropped - let _ = tx.blocking_send(entry); + stream_orderbook_with(url, options, |entry| { + // Async send: `await`s backpressure instead of blocking the + // runtime thread. `blocking_send` panics inside a multi-thread + // tokio runtime. Clone the sender so the returned future owns + // it (the callback is `FnMut`, so the future can't borrow). + let tx = tx.clone(); + async move { + // Ignore the error if the receiver has been dropped. + let _ = tx.send(entry).await; + } }) .await }); From d8947f5f20cd78676d5352a7204fcab1f400fcc9 Mon Sep 17 00:00:00 2001 From: "tuddman@users.noreply.github.com" Date: Fri, 5 Jun 2026 15:57:27 +0200 Subject: [PATCH 3/3] chore: bump to .6 in README --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f46c23a..d0dc696 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ cargo add aspens Or add it manually to your `Cargo.toml`: ```toml [dependencies] -aspens = "0.4" +aspens = "0.6" ``` Full client (gRPC + trading commands + RPC submission): @@ -138,7 +138,7 @@ cargo add aspens --no-default-features --features evm,solana ``` ```toml [dependencies] -aspens = { version = "0.4", default-features = false, features = ["evm", "solana"] } +aspens = { version = "0.6", default-features = false, features = ["evm", "solana"] } ``` ```rust use aspens::orders::{derive_order_id, GaslessLockParams}; @@ -254,10 +254,10 @@ default-on. Consumers can trim down to just what they need: | `client` | Full runtime: `AspensClient`, trading commands, gRPC (`tonic`/`prost`), async runtime (`tokio`), RPC submission (`solana-client`, `alloy-contract`, `alloy-provider`). | Keep for the CLI/REPL/admin experience or anything that talks to the Aspens stack. Drop it for browser / embedded / offline-signing. | Common configurations: -- **Default** (everything): `aspens = "0.4"` -- **Lean EVM signing**: `aspens = { version = "0.4", default-features = false, features = ["evm"] }` -- **Lean Solana signing**: `aspens = { version = "0.4", default-features = false, features = ["solana"] }` -- **Both chains, no client runtime**: `aspens = { version = "0.4", default-features = false, features = ["evm", "solana"] }` +- **Default** (everything): `aspens = "0.6"` +- **Lean EVM signing**: `aspens = { version = "0.6", default-features = false, features = ["evm"] }` +- **Lean Solana signing**: `aspens = { version = "0.6", default-features = false, features = ["solana"] }` +- **Both chains, no client runtime**: `aspens = { version = "0.6", default-features = false, features = ["evm", "solana"] }` The `aspens-cli`, `aspens-repl`, and `aspens-admin` binaries all depend on the default feature set.