Skip to main content

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.

CodeAccountNormal BalanceDescription
1010User BalancesDebitAvailable balance per user, per asset
1020Escrowed FundsDebitPending withdrawal to L1
1030Pool ReservesDebitAMM liquidity pool reserves
1040Collateral (YELLOW)DebitNodeID stake — mirrors on-chain Registry
2010L1 Custody ObligationCreditProtocol-level liability per asset
2020CreditOp ClearingCreditIn-flight transfer — zeroed when recipient processes CreditOp
3010Accrued FeesCreditUnswapped 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.

  1. Authentication — The entry node issues a random 32-byte nonce challenge. The user signs with their ECDSA key.
  2. Transaction signing — Every state-changing command includes ECDSA_Sign(PrivateKey, keccak256(TxID, From, To, Asset, Amount, Nonce)).
  3. Routing — The entry node determines the responsible cluster via FindCluster(keccak256(User.Address), k) and forwards via P2P.
  4. Response — The resulting certificate is routed back through the entry node.

Phase I: Deposit (L1 → YNP)

  1. User sends asset X to the Yellow Custody contract on L1.
  2. Custody emits Deposit(User, Asset, Amount).
  3. The user's cluster C_User monitors L1 events.
  4. 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
  5. After confirmation, C_User produces a BLS threshold signature (quorum t = 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 to C_Sender.
  • C_Sender verifies the ECDSA signature, then queries C_Pool for the fee asset price to determine V_User and 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_Sender produces 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:

  1. VerifyC_Recipient validates the BLS signature against the Registry and checks the Security Ceiling.
  2. CreditBalances[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)

  1. C_sign verifies balance and that the amount does not exceed the Security Ceiling.
  2. C_sign moves funds from Balances[1010] to Balances[1020] (escrowed).
  3. C_sign generates an Escrow Certificate (threshold signature with k signers).
  4. C_sign broadcasts the Escrow Certificate to all C_watch nodes.

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:

  1. Does Balances[1020][asset] equal the submitted amount?
  2. Does the recipient match the account's L1 address?
  3. Does the nonce match 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:

  1. Any C_watch node that detects fraud broadcasts a dispute proposal.
  2. C_watch runs a BLS signing ceremony with r participants and threshold floor(2r/3) + 1.
  3. The Dispute Certificate carries the correct parameters, a nonce strictly greater than the original, and k = r.
  4. Any C_watch node submits it via Custody.disputeWithdrawal().

On-chain verification:

  • The disputed withdrawal is still pending
  • The Dispute Certificate is fresh (signedAt + CERT_LIFETIME >= block.timestamp)
  • counterNonce > originalNonce
  • counterK > 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_watch validators; 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].