Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 69 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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};
Expand Down Expand Up @@ -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.
Expand Down
40 changes: 36 additions & 4 deletions aspens/src/commands/trading/stream_orderbook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,31 @@ pub async fn stream_orderbook<F>(
) -> 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<F, Fut>(
url: String,
options: StreamOrderbookOptions,
mut callback: F,
) -> Result<()>
where
F: FnMut(OrderbookEntry) -> Fut,
Fut: std::future::Future<Output = ()>,
{
// Create a channel to connect to the gRPC server
let channel = create_channel(&url).await?;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
});
Expand Down
Loading