From 7073f0cb87be1a3d44015423f1509570786db55c Mon Sep 17 00:00:00 2001 From: David J Parrott Date: Sat, 12 Sep 2026 01:58:48 -0400 Subject: [PATCH] feat(opp): add SYNDICATE_LIQ / LIQ_YIELD / DESYNDICATE_LIQ attestation types (simple_swap) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Protocol-only half of the cross-repo `simple_swap` feature: liq-token syndication moves onto OPP so the WIRE depot becomes the ledger of record for syndicated liqSOL (and, at parity, liqEth). - `ATTESTATION_TYPE_SYNDICATE_LIQ = 60963` (outpost -> depot): `SyndicateLIQ { chain_code, user: ChainAddress, amount: TokenAmount, sequence }` — a holder handed `amount` liq tokens to the outpost; from that moment the tokens are outpost property. - `ATTESTATION_TYPE_LIQ_YIELD = 60964` (outpost -> depot): `LIQYield { chain_code, amount: TokenAmount, sequence, epoch }` — the outpost's GLOBAL report of liq yield claimed for the syndicated pool since its previous report. No user address: the depot splits it across its own ledger. This replaces the per-user `StakingReward` reports for syndicated liqSOL. - `ATTESTATION_TYPE_DESYNDICATE_LIQ = 60965` (depot -> outpost): `DesyndicateLIQ { chain_code, user, amount: TokenAmount, request_id }` — depot-originated release; the outpost pays inline at dispatch, log-and-skip on refusal. `user` is the holder's native pubkey in both directions (SVM: 32-byte Ed25519; EVM: 33-byte compressed secp256k1), never a derived address: the depot has one identity for the AuthX-linked holder and the outpost derives the transfer destination. Every `amount` is a `TokenAmount` whose `token_code` names the chain's liq token as the depot registered it (the packed slug, LIQSOL on Solana) and whose `amount` is base units (liqSOL: 9 decimals, 1:1 with the depot frame). `sequence` is one per-outpost strictly-increasing counter shared by SyndicateLIQ and LIQYield, the depot's dedupe key. Depot-side handling and emission are deliberately NOT part of this change: `sysio.msgch::dispatch_attestation` drops the two inbound types through its `default: break`, nothing calls `queueout` with DESYNDICATE_LIQ, and the Solana relay has no `effect_shape` for it yet. Those land with the depot ledger. The host `FC_REFLECT_ENUM` mirror is extended so the plugins can name the new values. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01NoaXitAvpNSNj7114S9338 Change-Id: I3c0d32add50f0f051573a9b4b5a6d9e360fdb57c --- libraries/opp/include/sysio/opp/opp.hpp | 5 +- .../sysio/opp/attestations/attestations.proto | 72 +++++++++++++++++++ .../opp/proto/sysio/opp/types/types.proto | 11 +++ 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/libraries/opp/include/sysio/opp/opp.hpp b/libraries/opp/include/sysio/opp/opp.hpp index 7224652467..10fa613404 100644 --- a/libraries/opp/include/sysio/opp/opp.hpp +++ b/libraries/opp/include/sysio/opp/opp.hpp @@ -106,7 +106,10 @@ FC_REFLECT_ENUM(sysio::opp::types::AttestationType, (ATTESTATION_TYPE_RESERVE_CREATE_CANCEL) (ATTESTATION_TYPE_RESERVE_CREATE_CANCELLED) (ATTESTATION_TYPE_RESERVE_READY) - (ATTESTATION_TYPE_EMISSIONS_BLOCKED)) + (ATTESTATION_TYPE_EMISSIONS_BLOCKED) + (ATTESTATION_TYPE_SYNDICATE_LIQ) + (ATTESTATION_TYPE_LIQ_YIELD) + (ATTESTATION_TYPE_DESYNDICATE_LIQ)) // --------------------------------------------------------------------------- // Nested enums on attestation messages diff --git a/libraries/opp/proto/sysio/opp/attestations/attestations.proto b/libraries/opp/proto/sysio/opp/attestations/attestations.proto index 69ce3d8721..b5e5fb90d1 100644 --- a/libraries/opp/proto/sysio/opp/attestations/attestations.proto +++ b/libraries/opp/proto/sysio/opp/attestations/attestations.proto @@ -526,3 +526,75 @@ message EmissionsBlocked { int64 sysio_balance = 5; uint32 first_blocked_at = 6; // unix seconds } + +// --------------------------------------------------------------------------- +// liq syndication (simple_swap) +// +// A holder syndicates their liq token (liqSOL on Solana) by handing it to the +// outpost; from that moment the tokens are outpost property and the WIRE +// depot is the ledger of record for who syndicated what. The outpost keeps no +// per-user syndication state and never pays a syndicator on its own +// initiative: every de-syndication is depot-originated (DESYNDICATE_LIQ) and +// the outpost applies it inline at dispatch, exactly like a remit. +// +// Every `amount` is a `TokenAmount`: `token_code` is the chain's liq token as +// registered on the depot (a packed slug_name, e.g. LIQSOL), `amount` is in +// that token's base units (liqSOL is 9-decimal, so 1:1 with the depot frame). +// `sequence` is a per-outpost strictly-increasing counter carried so the depot +// can dedupe replays without retaining per-attestation history. +// --------------------------------------------------------------------------- + +// Outpost -> Depot. A user syndicated `amount` liq tokens to the outpost. +message SyndicateLIQ { + // Chain code of the emitting outpost (bound to the proven source chain). + uint64 chain_code = 1; + // The syndicating user's native PUBKEY -- the one identity the depot + // parks the syndication against and later matches an AuthX link on. + // Always the public key, never a derived address, so the same bytes + // round-trip unchanged through DesyndicateLIQ.user. Encoding follows the + // VM family: SVM the 32-byte Ed25519 pubkey (which is the address); EVM + // the 33-byte COMPRESSED secp256k1 point, NOT the 20-byte address -- + // sysio.authex indexes links by pubkey only, and the depot cannot derive + // an address from a compressed key. + sysio.opp.types.ChainAddress user = 2; + // The liq tokens syndicated: `token_code` names the outpost's liq token as + // the depot registered it, `amount` is base units of that token. + sysio.opp.types.TokenAmount amount = 3; + // Per-outpost monotonic sequence shared with LIQYield; dedupe key. + uint64 sequence = 4; +} + +// Outpost -> Depot. Yield the outpost claimed for the syndicated liq pool since +// its previous report. Global to the outpost: no user address — the depot +// splits it across its own syndication ledger. +message LIQYield { + uint64 chain_code = 1; + // The liq tokens claimed as yield for the syndicated pool: `token_code` + // names the outpost's liq token, `amount` is base units of that token. + sysio.opp.types.TokenAmount amount = 2; + // Per-outpost monotonic sequence shared with SyndicateLIQ; dedupe key. + uint64 sequence = 3; + // Outpost-chain epoch at which the report was taken. Informational. + uint64 epoch = 4; +} + +// Depot -> Outpost. Release `amount` liq tokens from the outpost's syndicated +// pool to `user`. The outpost transfers inline at dispatch; refusals are +// logged and skipped (never retried locally), the depot ledger is authoritative. +message DesyndicateLIQ { + // Chain code of the destination outpost. + uint64 chain_code = 1; + // The recipient's native PUBKEY, byte-identical to the SyndicateLIQ.user + // the depot recorded: a de-syndication pays only the AuthX-linked holder of + // the syndicated position, never an arbitrary destination, so the depot + // hands back the same identity it holds. The OUTPOST derives the transfer + // destination: SVM the pubkey is the address; EVM + // Secp256k1.addressFromCompressedPubkey (the derivation OPPInboundLib + // already applies to inbound remits). + sysio.opp.types.ChainAddress user = 2; + // The liq tokens to release: `token_code` must name this outpost's liq + // token (the outpost refuses any other), `amount` is base units of it. + sysio.opp.types.TokenAmount amount = 3; + // Depot-side request id for correlation in outpost logs. + uint64 request_id = 4; +} diff --git a/libraries/opp/proto/sysio/opp/types/types.proto b/libraries/opp/proto/sysio/opp/types/types.proto index da05cce45b..95c57b8f18 100644 --- a/libraries/opp/proto/sysio/opp/types/types.proto +++ b/libraries/opp/proto/sysio/opp/types/types.proto @@ -280,6 +280,17 @@ enum AttestationType { // gate state is depot-local (sysio.epoch::blocklog); no outpost consumes // a cross-chain notification of it. Slot retained; do not reuse. ATTESTATION_TYPE_EMISSIONS_BLOCKED = 60962; // 0xEE22 + + // --------------------------------------------------------------------------- + // liq syndication attestations (simple_swap, 2026-09-12) + // Syndicated liq tokens (liqSOL today, liqEth at parity) become outpost + // property; the depot owns the syndication ledger and originates every + // de-syndication. Per-user yield reporting for the syndicated pool is + // replaced by one global LIQ_YIELD report. + // --------------------------------------------------------------------------- + ATTESTATION_TYPE_SYNDICATE_LIQ = 60963; // 0xEE23 outpost → depot + ATTESTATION_TYPE_LIQ_YIELD = 60964; // 0xEE24 outpost → depot + ATTESTATION_TYPE_DESYNDICATE_LIQ = 60965; // 0xEE25 depot → outpost } // Reason codes recorded by sysio.epoch::advance's emissions readiness gate