Deployment-scoped signing domain
Every signed message includes a 16-bytemessage_domain.
In V4, message_domain is derived inside the program from:
- a fixed Agon domain tag
- the program ID
- the chain ID
Distinct chain IDs per environment
Mainnet, devnet, testnet, and localnet use distinct chain IDs in V4. Becausemessage_domain depends on chain_id, the derived domain differs across environments by construction.
The practical consequence: a commitment signed for devnet cannot be replayed on mainnet, and vice versa, even if the program ID were otherwise identical.
See Deployment for the specific chain IDs and message domains.
Permanent participants and permanent channels
V4 removed participant recycling and channel close-and-reopen flows. This eliminates a whole class of replay complexity:participant_idvalues do not get reused.- There is no reopened payment channel for an old signed message to target.
- Off-chain services can rely on stable channel identities.
Canonical PDA checks
Settlement paths do not trust arbitrary program-owned accounts just because they deserialize correctly. V4 re-derives and verifies the canonical PDA for every participant and channel account passed in — including accounts that arrive throughremaining_accounts. This matters most in settle_commitment_bundle and settle_clearing_round, where many accounts are passed dynamically.
Concretely, the program checks:
ParticipantAccountPDAs against["participant", owner]ChannelStatePDAs against["channel-v2", payer_id (u32 LE), payee_id (u32 LE), token_id (u16 LE)]
ChannelNotInitialized or ParticipantNotFound.
Monotonic cumulative settlement
Both unilateral commitments and cooperative rounds must move channel state strictly forward. If a signed message tries to settle to a cumulative amount that is less than or equal to the currentsettled_cumulative, the program rejects it with CommitmentAmountMustIncrease. This is both the replay-protection mechanism for the hot path and the rule that makes “keep only the newest commitment” a safe strategy off-chain.
Front-run resistance on settlement submission
Only the payee or an explicitly-namedauthorized_settler may submit a unilateral commitment. This prevents a payer from front-running settlement of its own commitment to manipulate balance ordering. A violation returns UnauthorizedSettler.
Balanced clearing rounds
settle_clearing_round requires that the net flow sums across the included participants balance correctly. Rounds with inconsistent deltas are rejected with NetFlowImbalance. Participants whose final net position is negative must hold enough total balance to cover it; the program uses saturating checks and returns InsufficientBalance or NetPositionOverflow on failure.
Ed25519 verification integrity
Every settlement instruction re-reads the Ed25519 instruction data and verifies:- The Ed25519 program instruction is present and self-contained.
- The signature count matches the expected number of signers.
- Each signed message parses as a valid
agon-cmt-v5oragon-round-v4body. - The Ed25519 instruction is not being called from a CPI that could mask its provenance (
CpiNotAllowed).
Timelocked channel maintenance
V4 places a timelock in front of actions that would otherwise surprise the other side of the relationship:- unlocking channel-specific locked funds
- rotating the channel signing key
- withdrawing protocol balance to an external token account
Self-channel rejection
V4 explicitly rejects self-channels. This prevents a payer from creating a channel to itself and using settlement to bypass the unlock timing model. A violation returnsSelfChannelNotAllowed.
Token-registration safety
Adding a token is an authority-gated instruction. The program rejects:- duplicate token IDs (
TokenIdAlreadyInUse) - duplicate mints (
TokenAlreadyRegistered) - zero token IDs (
InvalidTokenId) - invalid ASCII symbols (
InvalidTokenSymbol) - token decimals above the protocol’s supported range (
InvalidTokenDecimals) - settlement against token accounts whose mint does not match the registered token (
InvalidTokenMint)
Summary
Taken together, these checks mean:- Signed messages are bound to one deployment.
- Account lookups are bound to canonical state.
- Settlement only moves forward.
- Channel maintenance cannot surprise the other side instantly.
- Submission is front-run resistant.
- Cross-token confusion is caught at registration.

