Skip to main content
Every wallet that uses Agon registers once as a participant. That registration binds one wallet to one stable participant_id. The mapping does not recycle, so off-chain systems can treat wallet → participant_id as stable for the life of the deployment.

What a participant stores

The participant account is Agon’s long-lived identity record. Each account holds:
FieldMeaning
ownerThe wallet that controls the participant
participant_idThe compact numeric ID used inside signed messages and PDA seeds
token_balancesOne balance record per allowlisted token
inbound_channel_policyWhether other parties may open channels to this participant
bumpPDA bump for canonical address checks
The token_balances vector stores available balance, pending-withdrawal state, and withdrawal destination for each token the participant has used. Up to 16 tokens can coexist per participant. See Account layouts for the exact byte layout.

Why participants are permanent

V4 does not let participants close and recreate their accounts. This preserves three simple invariants:
  1. participant_id stays stable for the life of the deployment.
  2. Off-chain services can index users without worrying about ID reuse.
  3. Replay protection is simpler because there is no reopened identity for an old signed message to target.
If a team wants a new protocol identity, they use a new wallet.

Inbound channel policy

Each participant chooses how other parties may open channels to it:
PolicyMeaning
PermissionlessAnyone may create a channel to this participant
ConsentRequiredThe payee must sign channel creation
DisabledNew inbound channels are rejected
The default is ConsentRequired. The policy can be changed later with update_inbound_channel_policy.

Registration

Registration is deliberately small:
await program.methods
  .initializeParticipant()
  .accounts({
    owner: wallet.publicKey,
    feeRecipient,
  } as any)
  .signers([wallet])
  .rpc();
After registration, derive the canonical participant PDA from the wallet address and fetch the account whenever you need the stable participant_id:
const participantPda = PublicKey.findProgramAddressSync(
  [Buffer.from("participant"), wallet.publicKey.toBytes()],
  program.programId
)[0];

const participant = await program.account.participantAccount.fetch(participantPda);

What participants do not store

Participants are identity and balance containers. They do not store:
  • whom they are paying
  • the latest signed commitments
  • operator business logic
That data lives in ChannelState, in off-chain systems, or both.

See also