#24 Perform Aegis contracts audit readiness review FIX - #159
Open
onakijames-droid wants to merge 1 commit into
Open
#24 Perform Aegis contracts audit readiness review FIX#159onakijames-droid wants to merge 1 commit into
onakijames-droid wants to merge 1 commit into
Conversation
Contributor
| \nThis PR is currently blocked by merge conflicts.\n\nPlease update the branch with the latest main branch and resolve the conflicts before it can be merged. |
Contributor
| \nGitHub has not finished calculating whether this PR can be merged cleanly.\n\nThe auto-merge automation will skip this PR for now. Re-run the automation later. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CLOSE #24 Real-Time Event Streaming & Monitoring
Repo:
onakijames-droid/aegis-contracts· Issue: Enhance the monitoringsystem with real-time event streaming over WebSocket to Soroban RPC
What the developer is building
Aegis is a Real-World Asset (RWA) tokenization protocol on Stellar/Soroban.
The contracts enforce compliance at the ledger level: tokens may only be minted
to, and transferred between, KYC-whitelisted addresses.
Codebase at the time of the issue (156 lines of Rust, 1 test):
src/lib.rsDataKeystorage enum,initialize()src/compliance.rsis_whitelisted()helpersrc/asset.rsmint_asset,transfer,distribute_yieldsrc/test.rsThe exact defined issue
The issue says "enhance the existing monitoring system." The decisive
finding is that no monitoring system existed — and, more fundamentally:
Verified by inspection and by a baseline run (
cargo test→ 1 passing test, noevent assertions). Two dormant markers confirm this was known but unimplemented:
src/compliance.rs:// TODO: Add events for compliance trackingdocs/contract-spec.md: describesdistribute_yieldas "Triggers a dividendyield event for off-chain indexing" — an event that was never published.
This makes the issue a two-layer problem. Every acceptance criterion is
downstream of data that did not exist: you cannot stream, filter, alert on,
persist, replay, chart, or trigger from events that are never emitted. Building
only the off-chain service would have produced a monitor that provably streams
nothing.
Critical research finding — the WebSocket premise
The issue asks for "WebSocket connections to Soroban RPC." Research confirms:
Implementing WebSocket-only would satisfy the issue's wording while failing
its first acceptance criterion in production. The fix therefore implements a
real WebSocket client and a cursor-driven polling transport behind one
interface, auto-selecting and self-healing between them.
STEP 4 · The fix
Layer 1 — On-chain: make the protocol observable (Rust)
New
src/events.rsdefines the canonical event surface using the modern,non-deprecated
#[contractevent]macro. A stable topic layout was chosen so theRPC itself can do the coarse filtering:
Init("aegis","init")adminWhitelistAdd("aegis","wl_add", user)adminMint("aegis","mint", to)[amount, new_balance, total_supply]Transfer("aegis","transfer", from, to)amountYieldDistributed("aegis","yield")[admin, amount, total_supply]Design decisions:
topic 1 narrows to a single action.
server-side instead of shipping every event to the client.
supply growth without replaying the entire ledger.
env.events()directly; they delegate toevents.rsso the topic layout has exactly one source of truth. The topicshape is a public API — accidental drift silently breaks every downstream
consumer.
Layer 2 — Off-chain: the monitoring service (
monitoring/, Node ≥ 18)Dependencies:
wsonly. A dependency-free ScVal XDR decoder is includedrather than pulling the full
@stellar/stellar-sdkinto a sidecar.STEP 9 · Fix features by acceptance criterion
SorobanEventStream: realwsclient withsubscribeEventsframing, ping heartbeats, exponential backoff + jitter reconnect; cursor-drivengetEventsfallback; auto-upgrade back to WS; de-duplicationtopicMatchwith*/**, custom predicate);EventRouterwith priorities and per-route error isolationmatch,threshold,rate,sequence(address-correlated),absence— plus severity, cooldown, history, console/webhook sinks. 7 protocol-specific default rules incl.instant-drainonce,debounceMs,throttleMs,maxRuns, retries w/ backoff, runtime enable/disable via API; log/webhook/collect actionsSTEP 5–8, 10 · Validation
Correctness of the ScVal decoder — validated against the real SDK
The decoder is the highest-risk component (hand-written XDR parsing). Rather
than trusting hand-made fixtures, ground-truth base64 was generated with the
actual
soroban-sdkv26 serializer and asserted against:symbol · address(strkey) · i128 (+/-/i128::MAX) · u32 · i32 · u64 · i64 · bool · void · string · bytes · vec · mixed tuple · encodeSymbol round-tripResult: 19/19 exact matches, including CRC16-checksummed strkey encoding.
The definitive test — real contract output through the real pipeline
cargo test dump_event_xdr -- --ignored --nocapturecaptures the exact XDRthe Soroban host emits from the deployed contract. That output was fed through
the JS pipeline: 23/23 assertions passed — correct addresses, exact i128
amounts, correct field projection for all 5 events.
This is frozen as
monitoring/tests/onchain-compat.test.js, making it thecontract↔monitor seam: if an event's topics or field order change without a
decoder update, CI fails. It also asserts the simulator emits byte-identical XDR
to the host, so the test double cannot drift.
Bugs found and fixed during validation
Validation surfaced four genuine defects, each fixed and regression-tested:
Buffer.from(s,'base64')skips invalidcharacters, so malformed input decoded into plausible garbage instead of
being reported. Added strict validation + a trailing-byte check so corrupt
data always surfaces as
__undecodable.'error'event withno listener. The monitor could die while successfully degrading to
polling. Added a guaranteed listener and a normalizing
_emitError().BigIntbroke the dashboard —store.recent()rehydrated BigInt amountswhich
JSON.stringifycannot serialize, 500-ing/api/eventsand silentlykilling the WebSocket
helloframe. Fixed at the boundary (JSON-safe bydefault, opt-in
{hydrate:true}) plus a BigInt-aware replacer as a net.events.once()auto-rejects on'error', failing atest for behaviour that was actually correct. Replaced with a targeted
listener. (Test bug, not product bug — worth noting because the naive fix
would have been to suppress the legitimate error.)
Pre-existing issue found (not caused by this change)
make buildwas broken on upstreammainbefore any of my edits:Confirmed by building an unmodified clone → same failure (exit 101). Fixed
by switching the Makefile/README to
wasm32v1-none, somake buildnow works.Final verification
cargo fmt --all --checkcargo clippy --all-targetscargo testcargo build --target wasm32v1-none --releasecd monitoring && npm testmake build/make test/make test-all--simulate)websocket; 10 events streamed → stored → routed → 1 critical alert → 5 triggers → replayed; UI HTTP 200Total: 115 automated tests passing, zero warnings.
Confidence: ~97%
Grounded in end-to-end verification against real host output, not just unit
tests: the decoder is validated against SDK ground truth (19/19), the full
pipeline against genuine contract XDR (23/23), and streaming against a real
wsserver rather than a mock. Backward compatibility is preserved — theoriginal
test_lifecyclepasses untouched and no existing function signaturechanged.
The residual ~3% is the one thing not reachable from this environment: a live
deployment against public testnet RPC. That path is mitigated by the polling
transport being the default and by
getEventsrequest/response shapes beingmatched to the documented API, but it is honest to flag it as unexercised here.
STEP 11 · Files created / modified
Created — on-chain (1)
src/events.rs#[contractevent](138 lines)Modified — on-chain (4)
src/lib.rseventsmodule; emitInitsrc/compliance.rsWhitelistAdd— resolves the// TODO: Add eventsmarkersrc/asset.rsMint,Transfer,YieldDistributed(supply now read for the yield event)src/test.rsdump_event_xdrfixture generatorCreated — monitoring service (18)
monitoring/package.json·.gitignore·config.example.json·README.mdmonitoring/src/index.js·cli.js·service.js·config.js·defaults.jsmonitoring/src/rpc/websocket-client.js·jsonrpc.js·backoff.jsmonitoring/src/events/scval.js·normalize.js·filter.jsmonitoring/src/alerts/index.jsmonitoring/src/store/event-store.jsmonitoring/src/triggers/index.jsmonitoring/src/analytics/index.jsmonitoring/src/dashboard/server.js·ui.jsmonitoring/src/simulator.jsCreated — tests (8, 106 tests)
scval(10) ·filter(14) ·alerts(14) ·store(16) ·stream(14) ·triggers(18) ·integration(12) ·onchain-compat(10)Modified — project (4)
Makefilemonitor*,test-all,dump-eventstargetsREADME.mddocs/contract-spec.mddocs/architecture.md