Protocol Lifecycle
This section describes the three phases of asset movement through the Yellow Network: deposit from L1, internal transfer between accounts, and withdrawal back to L1. Each phase produces Certificates — the protocol's universal primitive for cluster-attested state change.
Data Structures
Account State
Each account is keyed by AccountID = keccak256(WalletAddress). The responsible cluster is determined by XOR-distance in the DHT.
type Account struct {
AccountID [32]byte // keccak256(WalletAddress)
Address [20]byte // L1 wallet address
Nonce uint64
Balances map[Code]map[AssetID]uint256 // Ledger keyed by chart-of-accounts code
LastUpdate uint64
}
Double-Entry Ledger
Every state mutation follows double-entry accounting — total debits must equal total credits. If they don't, the state is corrupt, detectable by any watcher.
| Code | Account | Normal Balance | Description |
|---|---|---|---|
| 1010 | User Balances | Debit | Available balance per user, per asset |
| 1020 | Escrowed Funds | Debit | Pending withdrawal to L1 |
| 1030 | Pool Reserves | Debit | AMM liquidity pool reserves |
| 1040 | Collateral (YELLOW) | Debit | NodeID stake — mirrors on-chain Registry |
| 2010 | L1 Custody Obligation | Credit | Protocol-level liability per asset |
| 2020 | CreditOp Clearing | Credit | In-flight transfer — zeroed when recipient processes CreditOp |
| 3010 | Accrued Fees | Credit | Unswapped fee revenue per user account |
Certificate
The Certificate is the protocol's universal state-change primitive. Every operation produces a Certificate carrying the responsible cluster's BLS threshold attestation.
type Certificate struct {
Type CertType // Credit | Swap | Escrow | Dispute
Account string // Account whose state is being mutated
Attestation // Embedded BLS threshold proof
Credit *CreditOp // Transfer credit
Swap *SwapOp // Swap execution
Escrow *EscrowOp // Withdrawal escrow
Dispute *DisputeOp // Fraud dispute by C_watch
}
type Attestation struct {
ThresholdSig []byte // Aggregated BLS signature (G1)
Bitmask uint64 // Which validators signed
Validators [][32]byte // BLS public keys (G2) of signers
}
The consensus layer operates on Certificates generically — sign, verify, aggregate — without knowledge of the operation type. New operation types can be added by defining a new CertType and payload struct.
Client Submission
A user MAY connect to any node in the network to submit commands. The entry node is not required to be a member of the user's cluster.
- Authentication — The entry node issues a random 32-byte nonce challenge. The user signs with their ECDSA key.
- Transaction signing — Every state-changing command includes
ECDSA_Sign(PrivateKey, keccak256(TxID, From, To, Asset, Amount, Nonce)). - Routing — The entry node determines the responsible cluster via
FindCluster(keccak256(User.Address), k)and forwards via P2P. - Response — The resulting certificate is routed back through the entry node.
Phase I: Deposit (L1 → YNP)
- User sends asset X to the Yellow Custody contract on L1.
- Custody emits
Deposit(User, Asset, Amount). - The user's cluster
C_Usermonitors L1 events. - The cluster waits for chain-specific L1 block confirmations:
- Ethereum Mainnet: 12 blocks (~3 minutes)
- Chains with instant finality: 1 block
- Configurable per ChainID in the network manifest via governance
- After confirmation,
C_Userproduces a BLS threshold signature (quorumt = floor(2k/3) + 1) to credit the user's off-chain balance:
Account.Balances[1010][X] += Amount
Phase II: Internal Transfer (Lock-and-Certify)
Cross-shard transfers use a Lock-and-Certify pattern with deferred fee settlement. The sender's cluster debits the sender and accrues the fee in the original asset, then emits a CreditOp Certificate — all in a single BLS signing round.
Step 1: Request and Valuation
- User signs and submits
Tx(To, Asset, Amount, MaxFee, Nonce, Signature)to any network node, which routes it toC_Sender. C_Senderverifies the ECDSA signature, then queriesC_Poolfor the fee asset price to determineV_Userand required k.
Step 2: Debit and Certify (1 BLS round)
C_Sender verifies balance, nonce, and that the transfer does not exceed the Security Ceiling:
DR 1010 Sender Balance Amount + Fee
CR 3010 Accrued Fees Fee
CR 2020 CreditOp Clearing Amount
Balances[1010][Asset] -= (Amount + Fee)Sender.Balances[3010][Asset] += Fee— the fee accrues in the original asset; no swap yet.C_Senderproduces a CreditOp Certificate attesting to the debit, fee accrual, and outbound credit.
Step 3: Settlement (Recipient Cluster)
The CreditOp Certificate is routed to C_Recipient:
- Verify —
C_Recipientvalidates the BLS signature against the Registry and checks the Security Ceiling. - Credit —
Balances[1010][Asset] += Amount.
DR 2020 CreditOp Clearing Amount
CR 1010 Recipient Balance Amount
Rollback on Failure
The sender's Balances[2020] carries the in-flight amount. If the recipient cluster does not acknowledge the CreditOp within 5 minutes:
- The sender's cluster reverses the journal entry — debiting 2020 and crediting back to
Balances[1010]. - The accrued fee in
Balances[3010]is also reversed. - The nonce is incremented (preventing replay). The user receives an error and may retry.
- Atomicity: Either both sides settle or neither does.
Phase III: Withdrawal (YNP → L1)
Withdrawals use the Optimistic Escrow mechanism. The signing cluster C_sign processes the withdrawal; the replication set C_watch independently verifies it and MAY override a fraudulent withdrawal via a Dispute Certificate.
Step 1: Escrow (Off-Chain)
C_signverifies balance and that the amount does not exceed the Security Ceiling.C_signmoves funds fromBalances[1010]toBalances[1020](escrowed).C_signgenerates an Escrow Certificate (threshold signature with k signers).C_signbroadcasts the Escrow Certificate to allC_watchnodes.
Step 2: Submission (On-Chain)
A Designated Submitter is chosen deterministically:
Index = uint256(WithdrawalID) % k
where WithdrawalID = keccak256(abi.encode(chainid, recipient, asset, amount, nonce, k)).
Fallback: Round-robin with exponential timeout — primary has 2 minutes, then each subsequent submitter gets 2 minutes, up to 12 rounds (24 minutes total). If no submission after 12 rounds, the escrow expires and funds are auto-released.
The submitter calls Custody.startWithdrawal(recipient, asset, amount, nonce, k, signature).
Step 3: Challenge Period
The Custody contract starts a timer:
- Mainnet: 1 hour (3600 seconds)
- Testnet: 5 minutes (300 seconds)
Every C_watch node monitors L1 for startWithdrawal events matching accounts in their replication scope. Each watcher compares the on-chain parameters against their local state replica:
- Does
Balances[1020][asset]equal the submittedamount? - Does the
recipientmatch the account's L1 address? - Does the
noncematch the expected sequence?
Honest case: All parameters match — no action needed. The cool-down expires normally.
Fraud detection: If any parameter mismatches, the watchers produce a Dispute Certificate:
- Any
C_watchnode that detects fraud broadcasts a dispute proposal. C_watchruns a BLS signing ceremony with r participants and thresholdfloor(2r/3) + 1.- The Dispute Certificate carries the correct parameters, a nonce strictly greater than the original, and
k = r. - Any
C_watchnode submits it viaCustody.disputeWithdrawal().
On-chain verification:
- The disputed withdrawal is still pending
- The Dispute Certificate is fresh (
signedAt + CERT_LIFETIME >= block.timestamp) counterNonce > originalNoncecounterK > originalK- BLS threshold signature is valid
On successful dispute:
- The withdrawal is cancelled; funds remain in Custody
- The contract emits
Disputed(WithdrawalID, DisputeID, counterK, originalSigners) - Original signers are slashed via the Escrow Certificate gate
- 25% of forfeited collateral goes to
C_watchvalidators; 75% to the protocol treasury
Step 4: Finalization
If no dispute is submitted during the cool-down, Custody.finalize() is called. This is a permissionless function — any address may call it. L1 assets are transferred to the user, and C_sign burns the escrowed Balances[1020].