Appearance
State and Trackers
The Zink Bridge is stateful. Every transfer is recorded as entries inside tracker accounts on both chains, with per-entry status fields, authority vote maps, and finalization cursors that make the full protocol lifecycle queryable over standard RPC.
Primary on-chain accounts
Bridge
The Bridge account is the per-chain-pair configuration root, derived with seeds ["bridge", chain_id].
| Field | Type | Purpose |
|---|---|---|
bump | u8 | PDA bump seed |
other_chain_id | ChainId (u16) | Identifies the paired chain |
native_vault_address | Pubkey | Vault for native token operations |
update_nonce | UpdateNonce (u64) | Monotonic nonce for AuthProposal sequencing |
threshold | U0F64 | Fixed-point 0.0–1.0 voting threshold; required weight = ceil(total_weight × threshold) |
total_authority_weight | WeightVal (u64) | Sum of all registered authority weights |
fee_definition | FeeDefinition | flat_fee + other_chain_token_account_rent |
fee_target | Pubkey | Destination for collected fees |
other_chain_name | UnsizedString | Human-readable name of the paired chain |
authorities | Map<Pubkey, BridgeAuthority> | Each authority has a weight (u64) and index (u16) |
other_chain_mints | Map<OtherChainMint, ThisChainMint> | Reverse lookup from other-chain mint to this-chain mint |
token_pairs | Map<ThisChainMint, TokenPair> | Forward lookup keyed by this-chain mint |
TokenPair fields
| Field | Type | Purpose |
|---|---|---|
other_chain_mint | KeyFor<OtherChainMint> | Mint address on the paired chain |
token_vault | KeyFor<TokenAccount> | Vault holding escrowed tokens (for Vault mode) |
pending_entries | u64 | Count of in-flight entries for this pair |
own_mint_type | OwnMintType | Vault (lock/release) or MintBurn (burn/mint) |
import_limit | TokenBridgeLimit | Rate limit for tokens bridged to this chain |
export_limit | TokenBridgeLimit | Rate limit for tokens bridged from this chain |
limit_authority | OptionalPubkey | Key that can adjust limits without a full proposal |
frozen | bool | When true, all operations on this pair are blocked |
freeze_authority | OptionalPubkey | Key that can toggle the frozen flag |
TokenBridgeLimit fields
| Field | Type | Purpose |
|---|---|---|
limit | SubTokensVal | Maximum token volume per period |
period_duration | SecondsVal | Length of each rate-limit window in seconds |
period_start | SecondsVal | Unix timestamp when the current period began |
total_for_period | SubTokensVal | Volume consumed in the current period |
Available capacity is limit − total_for_period. The period resets automatically when period_duration elapses.
Fee model
The total fee charged to a depositor is:
flat_fee + other_chain_token_account_rent + rent_for(DepositEntry + TokenAccountData)At cleanup, a partial refund is returned. The refund depends on whether the destination-chain token account was actually created and whether the source-chain ATA was created during the deposit. If neither was created, the corresponding rent portions are refunded.
DepositTracker
The DepositTracker lives on the source chain, derived with seeds ["deposit", bridge].
| Field | Type | Purpose |
|---|---|---|
bridge | KeyFor<Bridge> | Back-reference to the parent bridge |
other_chain_id | ChainId | Paired chain identifier |
entry_count | EntryIdVal (u64) | Total entries ever appended |
next_finalized_entry | EntryIdVal | Cursor: entries before this index are finalized |
auth_votes | AuthVoteMap | Per-authority vote tracking |
entries | List<DepositEntry> | Append-only list of deposit entries |
DepositEntry fields
| Field | Type | Purpose |
|---|---|---|
refund_to | Pubkey | Address that receives fee/token refunds at cleanup |
destination | Pubkey | Address on the destination chain to receive tokens |
mint | KeyFor<ThisChainMint> | Token mint on this (source) chain |
other_chain_mint | KeyFor<OtherChainMint> | Token mint on the destination chain |
amount | SubTokensVal | Amount deposited |
min_amount | SubTokensVal | Minimum acceptable amount at destination |
cleanup_completed | bool | Set to true after cleanup_deposit runs |
status | DepositStatus | Current lifecycle status |
WithdrawTracker
The WithdrawTracker lives on the destination chain, derived with seeds ["withdraw", bridge].
| Field | Type | Purpose |
|---|---|---|
bridge | KeyFor<Bridge> | Back-reference to the parent bridge |
other_chain_id | ChainId | Paired chain identifier |
entry_count | EntryIdVal | Total entries ever appended |
next_finalized_entry | EntryIdVal | Cursor: entries before this index are finalized |
auth_votes | AuthVoteMap | Per-authority vote tracking |
cleanup_tracker | WithdrawCleanupTracker | Per-authority rent accounting for cleanup |
entries | List<WithdrawEntry> | Append-only list of withdraw entries |
WithdrawEntry fields
| Field | Type | Purpose |
|---|---|---|
source_chain_mint | KeyFor<OtherChainMint> | Mint on the source chain |
destination_chain_mint | KeyFor<ThisChainMint> | Mint on this (destination) chain |
destination | Pubkey | Address to receive withdrawn tokens |
amount | SubTokensVal | Amount to withdraw |
status | WithdrawStatus | Current lifecycle status |
WithdrawCleanupTracker
Tracks per-authority cleanup state within the WithdrawTracker:
| Field | Type | Purpose |
|---|---|---|
authorities | Map<Pubkey, WithdrawCleanupInfo> | Per-authority tracking |
Each WithdrawCleanupInfo contains next_finalized_entry (the authority's cleanup cursor) and owed_rent (accumulated rent owed to the authority for accounts created during ack_deposit).
AuthProposal
AuthProposal is the governance mechanism for bridge configuration changes.
| Field | Type | Purpose |
|---|---|---|
rent_to | Pubkey | Receives rent refund when proposal is closed |
chain_id | ChainId | Which bridge this proposal targets |
proposer | Pubkey | Authority that created the proposal |
update_nonce | UpdateNonce | Must match the bridge's current nonce (prevents stale proposals) |
status | ProposalStatus | Building, Voting, Accepted, or Canceled |
vote_weight | WeightVal | Accumulated vote weight |
vote_result | VoteResult | Current vote tally |
proposal_items | UnsizedList<AuthProposalItem> | List of configuration changes |
AuthProposalItem variants
| Variant | Purpose |
|---|---|
ChangeThreshold | Set a new voting threshold (U0F64) |
AddAuthority | Register a new authority with a given weight |
RemoveAuthority | Remove an existing authority |
ModifyAuthority | Change an authority's weight |
AddTokenPair | Register a new bridgeable token pair |
ModifyTokenPair | Update parameters on an existing pair (cannot change mints) |
RemoveTokenPair | Remove a token pair (requires pending_entries == 0) |
A proposal moves through Building → Voting → Accepted (or Canceled). During Building, the proposer can add items. During Voting, authorities cast weighted votes. Once threshold is met, the proposal is applied atomically to the Bridge account and the update_nonce increments.
Status enums
DepositStatus
| Variant | Fields | Meaning |
|---|---|---|
Initialized | — | Deposit created, awaiting authority acknowledgement |
Voting | proposed: DepositResult, yes: WeightVal, no: WeightVal | Authorities are voting on the result (during ack_withdraw) |
Finalized | result: DepositResult | Threshold reached; result is locked |
WithdrawStatus
| Variant | Fields | Meaning |
|---|---|---|
Voting | yes: WeightVal, no: WeightVal | Authorities are voting (during ack_deposit) |
Finalized | result: DepositResult | Threshold reached; withdraw is executable if result is Confirmed |
DepositResult
| Variant | Fields | Meaning |
|---|---|---|
Confirmed | amount: SubTokensVal, created_token_account: CreatedTokenAccount | Transfer approved; specified amount will be released/minted |
Rejected | reason: DepositRejectionReason | Transfer denied; tokens will be refunded at cleanup |
CreatedTokenAccount is an enum with variants Pending, True, and False, tracking whether a destination token account was created as part of the bridge operation. This affects fee refund calculations at cleanup.
DepositRejectionReason
| Variant | Meaning |
|---|---|
InternalError | An unexpected error occurred during processing |
InsufficientFunds | The vault or mint lacks sufficient balance |
LimitExceeded | The token pair's rate limit would be exceeded |
TokenPairFrozen | The token pair is currently frozen |
TokenPairNotFound | No matching token pair exists on the destination chain |
Why the tracker model matters
The tracker model gives operators and integrators concrete on-chain state to answer operational questions:
| Question | Where to look |
|---|---|
| Has the deposit been created? | DepositTracker.entries[id].status == Initialized |
| Has authority threshold been reached on the destination? | WithdrawTracker.entries[id].status == Finalized |
| Was the deposit confirmed or rejected? | WithdrawTracker.entries[id].status.result |
Is withdraw executable? | Finalized + DepositResult::Confirmed |
| Has the source-side been finalized? | DepositTracker.entries[id].status == Finalized |
| Is cleanup safe to run? | DepositTracker.entries[id].status == Finalized && !cleanup_completed |
Upstream-compatible
These are ordinary Solana accounts queried over standard RPC with getAccountInfo and deserialized with the bridge program's layout. The bridge-specific aspect is the voting lifecycle and status model, not the underlying account mechanics.