A clean-room, MIT-licensed EVM↔EVM bridge: M-of-N attester settlement over an xERC20 (ERC-7281) token layer, deployed at the same address on every chain.
send() burn xSCI on A ──Sent(opId, Message)──▶ ≥M of N attesters sign (EIP-712, B's domain, B's epoch)
│
deliver(Message, sigs) on B: peer ✓ route ✓ replay ✓ quorum ✓ ──▶ mint xSCI (within THIS bridge's daily limit)
- Settlement = validator set.
SpinBridge.deliververifies ≥thresholdattester signatures on-chain (strictly-ascending unique signers, OZ ECDSA, domain bound to the destination chain + bridge + attester epoch). Delivery is permissionless. - Token = xERC20.
SpinXERC20grants each bridge its own linear-refilling daily mint and burn limit — the issuer, not the bridge, decides the blast radius.SpinXERC20Lockboxwraps a canonical asset (SCIEN·TIFIC) 1:1 on its home chain;SpinBridge.sendCanonicallocks + burns in one transaction. - Same address everywhere. One
londonartifact, CREATE2 through0x4e59…956C; factory, token and bridge land at identical addresses on all 9 rail chains (incl. Zilliqa 32769). Proven on two anvils. - No upgrade path, no arbitrary calls, no standing approvals, no zero-default trust. Attester rotation bumps an epoch that voids outstanding signatures without orphaning undelivered messages.
Status: fully proven locally, never deployed. 48 forge tests (3 fuzz properties × 512 runs), 40 relayer
self-tests, 30-assertion two-chain end-to-end — see contracts/tests/RESULTS.md. Not audited.
contracts/ Foundry (solc 0.8.24, evm london, OZ 5.0.2)
src/SpinBridge.sol the gateway (send / sendCanonical / deliver / governance)
src/SpinBridgeMessage.sol Message struct, EIP-712 typehash, structHash, opId
src/token/SpinXERC20*.sol xERC20 token, factory (CREATE2), lockbox — ported from SPINTRADE, hardened
test/ SpinXERC20.t.sol · SpinBridge.t.sol · SpinBridge.fuzz.t.sol
tests/RESULTS.md pasted evidence from the last full run
script/DeploySpinBridge.s.sol the ONLY signing path: predict → deploy-if-missing → wire → deployments/<chainId>.json
relayer/ attester + relayer (Node, ethers v6 vendored/npm; dry-run default; keys via env only; pure logic + selftest)
sdk/ client SDK — js/ (SpinBridgeClient: quote / buildSend / send / status / waitDelivered) and python/ (spinbridge-sdk,
httpx-only, unsigned; used by SPINTRADE's "bridged route" venue); vectors.json pins selectors/topics for both
scripts/ e2e-anvil.sh + e2e.mjs — two chains, real deploy, real relayer, 30 assertions
deploy/ spinbridge-legs.json (rail status), salts.json, deploy-multichain.mjs (predict/probe only — never signs)
docs/ ARCHITECTURE · SECURITY · DEPLOY · RELAYER · INTEGRATION (SPINBRIDGE ⟂ SPINTRADE ⟂ wARbridge boundary) · TECH_MAP · REFERENCE_SET
.github/workflows/ ci.yml — forge (london + gascheck) · relayer + JS SDK selftests (node 20/22) · Python SDK · two-anvil e2e for both asset families
cd contracts && bash scripts/link-libs.sh && forge test # 48/48
node ../relayer/selftest.mjs # 40/40
bash ../scripts/e2e-anvil.sh # 30/30 on two anvils (needs anvil, forge, node)
node ../deploy/deploy-multichain.mjs --offline # predicted rail addresses| decision | why |
|---|---|
| validator set, not intent/relayer or aggregation | it is the trust model already audited for the SPINTRADE lockbox rail (multisig-timelock oracle), it is buildable clean-room, and Across (BUSL) / Chainflip (own chain) were not viable bases |
| xERC20 token layer | per-bridge daily limits bound the loss from any bridge failure and keep the issuer sovereign; SCIEN·TIFIC is fixed-supply so a lockbox is required anyway |
| epoch in the digest, not the opId | rotation kills stale signatures but never strands a message |
london for the whole rail |
one initcode hash ⇒ one address, and Zilliqa EVM needs it |
| fee in the bridged token, ≤ 34 bps, immutable collector | matches the BANKON fibonacci ladder cap; a caller can never set the fee |
| guardian pause-only | fast stop without a fast restart key |
token decimals set at deploy (12 for xwAR) |
the lockbox mints base units 1:1, so the xERC20 must mirror its canonical's decimals or every wallet misdisplays by 10^6 |
| SPINTRADE/wARbridge stay outside | see docs/INTEGRATION.md: SPINBRIDGE is transport; CEX/AR/custody live elsewhere and call the SDK |
MIT. No BUSL/GPL/LGPL code is included; the reference inventory in docs/REFERENCE_SET.md was read,
not copied. The xERC20 port is in-house SPINTRADE code (MIT).