AEP-95: One-Way AKT Sink to Solana


aep: 95 title: “One-Way AKT Sink to Solana” author: Artur Troian (@troian), Joseph Chalabi (@chalabi2) status: Draft type: Standard category: Core created: 2026-10-27 discussions-to: https://github.com/orgs/akash-network/discussions roadmap: major requires: 79

Summary

This AEP defines a one-way sink (bridge) for moving AKT from Akash to Solana, the first step in the Akash chain’s migration (see AEP-79 and its Chain Migration Program document set).

  1. The user signs MsgSink on Akash with an amount and a Solana wallet address.
  2. A new module, x/sink, locks the AKT and writes a fixed 77-byte transfer record into its store.
  3. A relayer submits a proof of the record to the AKT program on Solana. An Akash light client on Solana verifies it against headers signed by Akash validators.
  4. The AKT program mints the same amount of SPL AKT, once per record. If the recipient cannot hold AKT, it marks the record voided instead.
  5. Reporters read the outcome on Solana and submit it to Akash. A confirmed mint closes the record. A confirmed void allows a refund after a challenge window.

The AKT mint is created on Solana with zero supply before the Akash upgrade. Transfers open after the upgrade and completion of the token and portal audits required by Akash governance proposal 346. Overclock Labs (OCL) then sends 1,000,000 AKT through the bridge to a Solana treasury vault to provide initial liquidity. Every AKT on Solana, including this seed deposit, is minted against a proven deposit and backed by AKT locked in x/sink.

During the launch phase, SPL AKT supports transfers and DEX liquidity only, with no staking or governance rights. Inflation and BME remain on Akash, and total supply follows Akash’s schedule.

Motivation

AKT launches on Solana before any protocol functions move there. The immediate goal is market access. Solana has the deepest DEX liquidity and widest wallet distribution for DePIN assets. AKT holders should be able to trade against SOL and USDC there without a wrapped-asset intermediary.

The bridge is deliberately one-way. SPL AKT is the canonical token on Solana, so no return path is needed. Omitting that path removes the largest class of bridge attacks. Locking AKT preserves Akash’s supply accounting and allows refunds through ordinary bank transfers.

This AEP covers the launch phase only. Staking, governance, BME and emissions on Solana belong to a later AEP.

Constraints and scope

The design must meet these requirements.

  1. The AKT mint is created on Solana first. Users initiate transfers from Akash to Solana, with no return path. Transfers remain open for 2 years from token launch, as proposal 346 requires.
  2. AKT moved to Solana is locked on Akash in a new module, x/sink. The marketplace escrow module is not reused.
  3. The user signs one Akash transaction (Keplr or any Cosmos wallet) with an amount and a Solana address. The AKT is locked, the destination is checked on both chains, and the AKT is minted on Solana. If Solana cannot accept the transfer, the AKT goes back to the sender.
  4. During the launch phase the Solana token has no protocol functions: no staking and no governance.
  5. Solana mints nothing on its own. Every SPL AKT is minted against a proven x/sink record. Inflation and BME continue on Akash.
  6. Initial liquidity comes from Akash: once transfers open, OCL sends 1,000,000 AKT through x/sink to the treasury vault.
  7. No intermediary chains in the transfer path (no Cosmos Hub, Axelar or Wormhole Gateway routing).
  8. Anyone can relay. The relayer, reporters and light client can each be replaced through configuration or governance.
  9. Only liquid AKT can be deposited. Bonded and unbonding AKT must finish unbonding first. A separate upgrade will support moving staked AKT in one step, as proposal 346 describes.

Out of scope: emissions, staking or governance on Solana, the future of the Akash chain, and residual balances at any future shutdown.

Terminology

TermMeaning
x/sinkNew Akash module. Accepts deposits, locks AKT, stores transfer records and void orders, counts reporter votes and pays refunds.
Transfer recordThe 77-byte value at transfer/{id} in the sink store. Written once at deposit and never changed.
Void orderThe 9-byte value at void/{id}, written by governance. Proving it on Solana voids that transfer.
AKT programThe Solana program that holds the AKT mint authority. It mints only against proven transfer records.
Light clientA dedicated deployment of the ICS07 Tendermint client from cosmos/ibc-contracts. It stores verified Akash headers on Solana and checks ICS23 proofs against their app hashes.
RelayerOff-chain service that uploads Akash headers to the light client and submits proofs to the AKT program. It has no authority over funds. Anyone can run one.
ReporterIndependent operator that reads transfer outcomes from its own Solana node and votes on Akash. Reporters cannot mint.
Transfer statusA permanent Solana account for each record ID. The first successful mint or void sets it to Minted or Voided.
Seed depositOCL’s 1,000,000 AKT transfer to the treasury vault through MsgSink after transfers open. It provides initial liquidity and follows the same rules as any other transfer.
Token launchThe moment transfers open. Proposal 346 expects it 2 to 3 weeks after that proposal passes, once the token and portal audits are complete.
Pause guardianA key on each chain that can pause operations but cannot resume them.
Upgrade authorityThe key that can replace a Solana program’s code. In production, a time-locked multisig.

Trust model

What each side checks

DirectionChecked on chainTrusted
Akash to Solana (mint, void)An ICS23 proof that the record or void order is in Akash state, against the app hash of a header the light client verified with Akash validator signaturesAkash validators, the initial checkpoint, and the upgrade authorities of the three Solana programs
Solana to Akash (outcomes)Nothing. There is no Solana light client on AkashA reporter threshold above two thirds, with a challenge window and veto before any refund

To accept a header that skips heights, the light client requires signatures representing more than one third of the last trusted validator set’s voting power and more than two thirds of the new set’s voting power. Forging Akash state on Solana therefore requires more than one third of the stake trusted at the last update. That stake remains bonded and slashable during the trusting period. Two valid, conflicting headers freeze the client.

What each party could do if compromised

PartyWorst caseBound
RelayerDelay mints and voidsAnyone can relay. With open relaying any signer can update headers
Reporter thresholdConfirm a false void, refunding a minted transfer; or confirm a false mint, preventing a voided transfer from being refundedChallenge window with a veto by any one reporter or by governance. Daily refund cap. Any conflicting vote holds the transfer for governance
Upgrade authority (AKT, ICS07 or access manager)Mint without limit, by replacing code or re-pinning to a client it controlsTime-locked multisig appointed by Akash governance. Monitors alert on every admin event. Pause guardians act without the time lock
Akash validators with more than one third of trusted stakeForge Akash state on SolanaPer-transfer and daily mint caps. Guardian pause. Misbehaviour freezes the client
Akash governanceVoid and refund any transfer that has not been minted. Change parametersA refund goes only to the original sender. Governance cannot move locked AKT anywhere else

Specification

Overview

Loading graph...

sequenceDiagram
    autonumber
    actor U as User (Cosmos wallet)
    participant S as x/sink (Akash)
    participant R as Relayer
    participant LC as Light client (Solana)
    participant M as AKT program (Solana)
    participant P as Reporters

    U->>S: MsgSink {dest, amount}
    S->>S: check, lock AKT, write transfer/{id}
    R->>LC: upload signed Akash headers
    R->>M: mint_transfer(record, proof_height) with ICS23 proof
    M->>LC: verify_membership(sink, transfer/{id}, record)
    alt recipient can hold AKT
        M->>M: mint to recipient ATA, status Minted
    else recipient cannot hold AKT
        M->>M: status Voided (InvalidRecipient), nothing minted
    end
    P->>M: read status at finalized commitment
    P->>S: MsgReportOutcome
    alt threshold reports Minted
        S->>S: Minted, AKT stays locked
    else threshold reports Voided
        S->>S: Voided, challenge window starts
        Note over S: any reporter or governance can veto
        S->>U: MsgFinalizeRefund pays the sender after the window
    end

A cap, pause or light-client error leaves no status on Solana, so the relayer retries later. A timeout alone never triggers a refund.

Akash: x/sink module

Store

KeyValueWritten byNotes
transfer/{id}77-byte recordMsgSinkProven on Solana. Never changed or deleted
void/{id}9 bytesMsgRequestVoidProven on Solana
settlement/{id}Settlement (protobuf)Reporters and governanceBookkeeping only, never proven
open/{id} (8-byte big-endian ID)0x01Added at deposit, removed at the first mint or refundIndex behind OpenTransfers

{id} in transfer/ and void/ is the decimal ID with no leading zeros. IDs start at 1, increase by one and are never reused. The store name sink and these two keys are part of the proof format: changing them breaks every Solana proof. Parameters, totals and per-sender usage are stored under separate keys that Solana never reads.

Transfer record. 77 bytes, integers little-endian, no length prefixes.

OffsetLengthField
01Version, 1
18Transfer ID
920Sender address bytes (not bech32)
2932Solana recipient public key
618Amount in uakt, positive
698Akash block height of the deposit

Void order. 9 bytes: version 1, then the transfer ID as a little-endian u64.

Proofs. The proof path has two parts, the store name sink and the key. The proof is a protobuf ibc.core.commitment.v1.MerkleProof (IAVL proof, then multistore proof), fetched with the ABCI query /store/sink/key and prove=true. State committed at height H is proven against the app hash in header H+1.

Deposit: MsgSink

MsgSink { sender, dest_type, dest, amount } returns MsgSinkResponse { id }.

A deposit must meet all of these conditions. Otherwise, the transaction fails without locking funds.

  • params.enabled is true.
  • MsgSink is the only top-level message in the transaction, and sender is an ordinary signing account. Module accounts, authz MsgExec and other nested execution are rejected.
  • dest_type is 1 (Solana public key).
  • dest is canonical base58 of 32 bytes and not all zeros. It must be a valid Ed25519 point (a wallet address) unless it is in vault_allowlist.
  • amount is positive, in uakt, at least min_transfer, and no greater than either solana_daily_mint_cap or max_transfer. Each sender is limited to max_daily_per_account deposits per UTC day and max_pending_per_account open transfers.
  • If max_daily_amount_per_account is set, the sender’s total for the UTC day, including this deposit, stays within it.

On success, the module moves amount from the sender to the sink account, assigns the next ID, writes transfer/{id}, starts a pending settlement, adds the open-index entry and emits sink_deposit. The response includes the transfer ID.

The sink account is a blocked bank address. MsgSend, MsgMultiSend and contract sends to it fail, and it cannot be a distribution withdraw address. The memo is ignored. The user pays only the normal Akash transaction fee.

Settlement

Each transfer has a settlement status and a hold field.

StatusMeaningTerminal
PendingDeposited, no outcome confirmedNo
MintedReporters confirmed the Solana mint. The AKT stays lockedYes
VoidedA Solana void was confirmed. The refund waits for the challenge windowNo
RefundedThe AKT was returned to the senderYes
HoldSet whenCleared by
NoneNormal state
VetoA reporter or governance vetoes a voided transferGovernance
ConflictVotes disagree, or a vote contradicts a confirmed outcomeGovernance

Reporter votes. MsgReportOutcome { reporter, id, reporter_epoch, round, observation }. The observation contains the outcome (minted or voided), the finalized Solana slot and the 64-byte transaction signature.

  • The reporter must be in params.reporters, and the vote must specify the current reporter epoch and the transfer’s current round. Duplicate votes have no effect.
  • When reporter_threshold votes report a mint and there is no hold, the transfer becomes Minted (sink_mint_confirmed).
  • When reporter_threshold votes report a void and there is no hold, the transfer becomes Voided and its challenge window ends challenge_period_seconds later (sink_void_confirmed).
  • Mixed mint and void votes, or a vote against a confirmed outcome, set a conflict hold (sink_conflict).
  • Changing the reporter set or the threshold increases reporter_epoch. Votes from older epochs do not count. On the next report, a nonterminal transfer returns to pending for a new vote.

Refund. MsgFinalizeRefund { executor, id }. Anyone can submit it. A refund is paid only when:

  • refunds_enabled is true,
  • the transfer is Voided with no hold,
  • the void was confirmed in the current reporter epoch,
  • the challenge window has ended,
  • the amount fits within the remaining daily_refund_cap for the UTC day, and
  • it is the oldest waiting refund, ordered by challenge end and then ID. MsgFinalizeRefund rejects all others.

It pays exactly the recorded amount to the recorded sender, marks the transfer Refunded and emits sink_refunded. The refund comes from that transfer’s locked AKT.

Veto. MsgVetoVoid { signer, id, reason }. A current reporter can veto during the challenge window. Governance can veto any time before payment. The transfer must be Voided. The veto sets a hold (sink_veto).

Governance.

  • MsgRequestVoid { authority, id } writes void/{id} for a transfer that is not Minted or Refunded (sink_void_order). It moves no funds. A refund still requires proof of the void on Solana and confirmation by reporters.
  • MsgResolveTransfer { authority, id, decision, observation, reason } resolves a hold or stalled vote. RESTART returns the transfer to pending. CONFIRM_MINT and CONFIRM_VOID require a matching observation. CONFIRM_VOID starts a new challenge window and leaves any refund subject to the refund switch and cap. A Minted or Refunded transfer cannot change. Every decision starts a new round and clears the votes and the hold (sink_resolved).
  • MsgUpdateParams { authority, params } replaces the whole parameter set. It is the only way to turn deposits or refunds on, or to set the guardian.

Pause guardian. MsgGuardianPause { guardian, deposits, refunds }. The signer must be params.pause_guardian. It turns enabled, refunds_enabled or both off, never on (sink_guardian_pause).

Messages

MessageSignerEffect
MsgSinkDepositorLock AKT and write a transfer record
MsgReportOutcomeReporterVote on a transfer’s Solana outcome
MsgVetoVoidReporter or governanceHold a voided transfer before refund
MsgFinalizeRefundAnyonePay an eligible refund to the original sender
MsgGuardianPausePause guardianTurn off deposits, refunds or both
MsgRequestVoidGovernanceWrite a void order
MsgResolveTransferGovernanceRestart, confirm a mint, or confirm a void
MsgUpdateParamsGovernanceReplace parameters

All eight messages have Amino JSON names, such as sink/MsgSink and sink/MsgGuardianPause, so Ledger signing works through Keplr.

Parameters

ParameterUnitDefaultRule
enabledboolfalseNeeds reporters and min_transfer to be set
solana_daily_mint_capuakt10,000,000 AKTCopy of the Solana daily cap. At least max_transfer
min_transferuakt0 (off)Must be set before deposits are enabled. At most max_transfer
max_transferuakt1,000,000 AKTPositive and at most solana_daily_mint_cap
max_daily_per_accountcount20Deposits one sender can make per UTC day. Positive
max_pending_per_accountcount3Open transfers one sender can have at once. Positive
max_daily_amount_per_accountuakt0 (no limit)AKT one sender can deposit per UTC day. If set, at least min_transfer
vault_allowlist32-byte keysemptyAt most 16. Must match the Solana list
reportersaddressesemptyAt most 32
reporter_thresholdcount0More than two thirds of reporters
challenge_period_secondsseconds259,200At least 259,200 (72 hours). Release builds reject anything shorter
daily_refund_capuakt1,000,000 AKTAt least max_transfer. Cannot be lowered below the largest open transfer
refunds_enabledboolfalse
pause_guardianaddressemptyA 20- or 32-byte account, such as a multisig or group policy

See Decisions for proposed mainnet values.

Queries

QueryRESTReturns
Sink/akash/sink/v1/records/{id}Public status and the transfer
State/akash/sink/v1/stateParameters and totals
Transfer/akash/sink/v1/transfers/{id}Record, void-order flag and settlement
Account/akash/sink/v1/accounts/{sender}The sender’s daily and pending usage
OpenTransfers/akash/sink/v1/open_transfers?start_after=&limit=Transfers that are not Minted or Refunded, in ID order, up to 500 per page

Public statuses are transfer_pending, completed (mint confirmed) and void (void confirmed or refunded). A void order alone does not show as void.

Events

EventWhen
sink_depositA deposit is accepted
sink_void_orderGovernance writes a void order
sink_reportA reporter votes
sink_mint_confirmedMint votes reach the threshold
sink_void_confirmedVoid votes reach the threshold (carries the challenge end)
sink_conflictVotes disagree
sink_vetoA void is vetoed
sink_resolvedGovernance resolves a transfer
sink_refundedA refund is paid
sink_guardian_pauseThe guardian switches something off
sink_preexisting_balanceThe upgrade found a balance at the sink address

Totals and invariant

Totals tracks pending_amount, minted_amount, voided_amount, refunded_amount, pending_count, last_transfer_id, the day’s refunds and reporter_epoch. Each transfer’s amount belongs to exactly one total: pending, minted, voided or refunded. The module maintains this equality:

sink bank balance = pending_amount + minted_amount + voided_amount

refunded_amount is the cumulative amount paid out of the module. Only MsgFinalizeRefund can release locked AKT; there is no general withdrawal mechanism.

Upgrade and genesis

GenesisState { params, totals, transfers, accounts }.

The upgrade that adds the module must:

  1. Add the store with StoreUpgrades{Added: ["sink"]}.
  2. Before migrations, run PrepareInstall. The sink address is deterministic, so anyone can send coins to it before the upgrade. PrepareInstall converts any existing account at that address into the module account, preserves its account number, and moves its balance to the community pool (sink_preexisting_balance).
  3. Run InitGenesis with default parameters and empty totals. No funds move.
  4. Leave deposits and refunds disabled, with no reporters or guardian. Governance configures them after the upgrade.

Exported genesis keeps the records, settlements, counters and per-sender usage. Import checks the bank equality above.

Solana: light client

The bridge uses a dedicated deployment of the ICS07 Tendermint client and its access manager, built from cosmos/ibc-contracts at dc78ade828b766b218fb6bcb5417fdaf8e553021 with four patches:

PatchChange
Signature batchesEd25519 signatures are checked with Solana’s Ed25519 precompile and stored as compact receipts, so a commit from a large validator set fits Solana’s limits
Monotonic latest heightAccepting an older header no longer lowers the latest height, as in ibc-go
Consensus pruningprune_consensus_state closes expired consensus states and returns their rent. Requires an explicitly assigned RELAYER_ROLE member
Open relayingGranting RELAYER_ROLE to the ICS07 program’s own ID lets any signer submit headers and misbehaviour
  • One client per deployment, at the ["client"] PDA. Each verified height has a consensus state at ["consensus_state", height] with its app hash and timestamp.
  • Client settings: chain ID akashnet-2, trust level 1/3, trusting period at most 1,209,600 s (14 days, two thirds of the 21-day unbonding), unbonding 1,814,400 s, maximum clock drift measured before launch.
  • The initial checkpoint is chosen in a ceremony: at least three people fetch a post-upgrade header, commit and validator set from at least three independent nodes and compare the block hash, app hash and validator hash.
  • The client expires if no header is verified within the trusting period, and freezes on misbehaviour. A frozen or expired client cannot be recovered. The AKT program re-pins to a new deployment instead.
  • The access manager’s RELAYER_ROLE controls header uploads, updates, misbehaviour and pruning. With open relaying, anyone can do all of these except pruning.
  • A full commit from 100 validators fits in one Solana transaction’s compute limit. One from 129 does not. For sets with more than 100 signatures, the relayer submits only enough signatures for a quorum and marks the rest absent. This supports sets of at least 150 validators.

Solana: AKT program

Anchor 0.32.1, legacy SPL Token.

Accounts

AccountSeedsContents
LaunchConfig["config"]The bridge_enabled switch
AKT mint["mint"]Legacy SPL mint, 6 decimals
Mint authority["mint_authority"]PDA that signs mints and metadata updates. Holds no data
BridgeConfig["bridge"]Pinned light-client program and chain ID, mint policy, minted amount for the current UTC day
PauseGuardian["guardian"]Guardian key
ProofBuffer["proof", submitter, nonce]Up to 4,096 bytes of proof, uploaded in chunks
TransferStatus["transfer", id as u64 LE]Permanent Minted or Voided for one record ID

The mint policy has per_transfer_max, daily_mint_cap and up to 16 vaults, with 0 < per_transfer_max <= daily_mint_cap.

Instructions

InstructionCallerEffect
initializeUpgrade authority, onceCreates the zero-supply mint and configuration, with minting disabled
set_token_metadataUpgrade authorityCreates or updates the Metaplex name, symbol and URI
configure_bridge(chain_id, policy)Upgrade authority, oncePins the light client and chain ID after checking the client is active. Minting stays off
set_bridge_enabled(enabled)Upgrade authorityTurns minting on or off
set_mint_policy(policy)Upgrade authorityReplaces the caps and vault list. Preserves the day’s usage
set_pause_guardian(key or none)Upgrade authoritySets, rotates or clears the guardian
guardian_pausePause guardianTurns minting off. It can never turn it on
repin_light_client(chain_id)Upgrade authority, any timePins another active ICS07 deployment without reading the old client, so a broken client cannot block the change
create_proof_buffer, append_proof, close_proof_bufferAnyone, for their own bufferUpload a proof in chunks and reclaim the rent afterwards
mint_transfer(record, proof_height)AnyoneProves transfer/{id}, then mints or voids
void_transfer(id, proof_height)AnyoneProves void/{id}, then voids

mint_transfer

  1. It must be a top-level instruction, and only ComputeBudget instructions may come before it in the transaction. This keeps the TransferResolved event visible to reporters. Minting must be enabled.
  2. It checks that the pinned light client uses the expected program and PDAs, matches the chain ID, and is not frozen. The latest consensus state must be within the trusting period, with a timestamp no later than the clock plus the allowed drift. proof_height must have a stored consensus state at or below the latest height, and the record’s height must be between 1 and proof_height.
  3. It builds the path ["sink", "transfer/{id}"] and the 77-byte value from the record, and calls verify_membership on the pinned client with the uploaded proof.
  4. It checks the recipient. A nonzero on-curve key owned by the System Program passes, whether or not the account exists. So does a key in vaults. Executables, token-program accounts, the zero key and the mint-authority PDA never pass, even if listed. A recipient whose existing AKT token account has been reassigned to another owner does not pass.
  5. If the recipient fails, it writes Voided / InvalidRecipient. Nothing is minted and no token account is created.
  6. If the recipient passes, it checks amount <= per_transfer_max and that the day’s total stays within daily_mint_cap, creates the recipient’s associated token account if needed, mints amount and writes Minted.
  7. It emits TransferResolved { id, proof_height, outcome }.

If a cap, pause, light-client error or invalid proof causes the transaction to fail, nothing is written and the record can be retried.

void_transfer proves void/{id} equals [1, id] with the same light-client checks and writes Voided / Governance. It works while minting is paused and fails if the record already has a status.

Mint and void create the same TransferStatus account, so each record ID is resolved once: minted or voided, never both. A status cannot be closed or reset, including through re-pinning.

Daily cap. Days are fixed UTC days on the Solana clock, not a rolling 24 hours, so up to twice the cap can mint within 24 hours around midnight. Vault recipients are subject to the same caps. This is the per-epoch mint cap in proposal 346, with the UTC day as the epoch.

Events

EventEmitted byFields
TokenMetadataSetset_token_metadatamint, metadata account, URI
BridgeConfiguredconfigure_bridgelight-client program, chain ID, policy
BridgeEnabledSetset_bridge_enabledenabled, signer
MintPolicySetset_mint_policyold and new policy
PauseGuardianSetset_pause_guardianold and new guardian
GuardianPausedguardian_pauseguardian
LightClientRepinnedrepin_light_clientold and new program and chain ID
TransferResolvedmint_transfer, void_transferrecord ID, proof height, outcome

A multisig calls the admin instructions through CPI, so monitors must read events by invocation frame. Anchor’s TypeScript EventParser skips events logged inside a CPI.

Errors

AKT errors are 7000 to 7024. ICS07 errors start at 6000 and appear on the same instruction when a membership check fails, so the two ranges never overlap.

CodeNameCodeName
7000InvalidProgramData7013InvalidConsensusTimestamp
7001UnauthorizedInitializer7014InvalidProofHeight
7002InvalidAuthority7015InvalidProofLength
7003UnauthorizedGuardian7016InvalidProofOffset
7004Reserved (unused)7017IncompleteProof
7005InvalidMetadataUri7018BridgeDisabled
7006InvalidMetadata7019CpiNotAllowed
7007InvalidChainId7020PrecedingInstruction
7008InvalidPolicy7021InvalidRecord
7009InvalidLightClient7022InvalidClock
7010WrongChain7023TransferLimit
7011ClientFrozen7024DailyLimit
7012ClientExpired

Authorities

PowerHolder in production
Upgrade authority of the AKT, ICS07 and access-manager programsOne time-locked multisig. All three are the same security tier: any of them can mint without limit
Access-manager admin (grants RELAYER_ROLE, opens or closes relaying)The same multisig
Solana pause guardianAn on-call key outside the time lock, also held by the monitor for the automatic pause
x/sink pause guardianAn on-call key, multisig or group policy chosen by governance
TreasuryA multisig vault
Metadata update authorityThe mint-authority PDA, changed only through set_token_metadata

The upgrade authorities are retained because repin_light_client and verifier fixes require them.

Transfer caps

The following limits apply to transfer amounts and counts across both chains. Raise caps on Solana first; lower them on Akash first. See “Changing caps” under Launch sequence.

CapChainParameterDefaultRule
Minimum transferAkashmin_transfer0 (off)Must be set before deposits are enabled. At most max_transfer
Per-transfer maximumAkashmax_transfer1,000,000 AKTPositive and at most solana_daily_mint_cap. No higher than the Solana per_transfer_max
Per-transfer maximumSolanaper_transfer_maxSet by the multisigPositive and at most daily_mint_cap
Daily mint capSolanadaily_mint_capSet by the multisigAt least per_transfer_max. Counted per fixed UTC day
Copy of the daily mint capAkashsolana_daily_mint_cap10,000,000 AKTEqual to the Solana daily_mint_cap. At least max_transfer
Deposits per sender per dayAkashmax_daily_per_account20Positive
Open transfers per senderAkashmax_pending_per_account3Positive
AKT per sender per dayAkashmax_daily_amount_per_account0 (no limit)If set, at least min_transfer
Daily refund capAkashdaily_refund_cap1,000,000 AKTAt least max_transfer. Cannot be lowered below the largest open transfer

Token

SettingValueReason
Token programSPL Token (legacy)Compatible with pools, aggregators, exchange deposit flows and older wallets
Decimals6Matches uakt, so one unit on Akash is one unit on Solana
Freeze authorityNone, set at creationNobody can freeze holders’ tokens. Freezable mints are flagged by aggregators and rejected by some pools
Mint authorityThe AKT program’s ["mint_authority"] PDAOnly proven transfers can create tokens
MetadataMetaplex: name “Akash Network”, symbol AKT, HTTPS URI of at most 200 bytes, mutable through set_token_metadataWallets and explorers show it as AKT
Initial supplyNone. OCL’s 1,000,000 AKT seed deposit to the treasury vault is the initial liquidityEvery token on Solana is minted against a proven deposit

The mint address is published on akash.network, the GitHub org, and to Jupiter, CoinGecko, CoinMarketCap, Solscan and SolanaFM before the seed deposit is minted. Assume impostor AKT mints already exist.

SPL Token or Token-2022

Solana has two token programs. SPL Token (“legacy”) has a fixed feature set (mint, transfer, burn, approve, freeze) and keeps name and logo in a separate Metaplex account. Every wallet, DEX, aggregator, custodian, exchange and indexer on Solana supports it.

Token-2022 is a separate program with optional extensions fixed at mint creation: transfer fees, transfer hooks, confidential transfers, a permanent delegate, non-transferable tokens, default-frozen accounts and embedded metadata. Each integrator must add support for it separately.

ConsiderationSPL TokenToken-2022
SupportUniversalMost large venues and wallets. Uneven across exchange deposit flows, custodians, older indexers, hardware wallets and tax tools
Features this design needsAllAll, plus extensions it does not use
Extensions that get a mint excluded from poolsNone possibleTransfer hook, transfer fee, permanent delegate, default-frozen, non-transferable
Can it be changed laterNoNo. Switching means another token swap

Legacy SPL Token supports broad trading access on Solana and provides every feature this design needs. The choice is permanent. RENDER also uses legacy SPL Token. Token-2022 accounts cannot receive AKT from the bridge.

Off-chain services

These services have no authority over funds. Each is configured with endpoints and keys, and each can be run by multiple operators.

ServiceDoesNeeds
RelayerKeeps the light client updated (on demand, and after one fifth of the trusting period when idle), fetches ICS23 proofs, uploads them and calls mint_transfer or void_transfer for waiting records in ID order. Checks Akash before spending SOL to avoid minting a record already voided or refunded on Akash. Prunes expired consensus statesAn Akash node that keeps at least one trusting period of state, a Solana RPC, and a funded payer key. Reads the pinned light client from the ["bridge"] account, so it follows a re-pin
ReporterReads TransferStatus and TransferResolved at finalized commitment from its own Solana node, checks program, amount and recipient, sends MsgReportOutcome, vetoes voids that do not match Solana, and sends MsgFinalizeRefund when one is dueIts own Solana node (no third-party RPC), its own Akash node, its own key in a KMS or HSM (external signer supported)
MonitorSupply reconciliation with an automatic pause on any mismatch, light-client age, pending age, admin events, cap usage, fee balancesRead access to both chains, and the Solana pause guardian key for the automatic pause
PortalConnects the user’s Keplr and Solana wallets and fills in the destination, so the address does not have to be typed by hand. Shows each transfer’s statusPublic Akash and Solana endpoints

Costs: users pay the Akash transaction fee. Relayers pay Solana fees and rent: about 0.0035 SOL per mint, including the recipient’s token account and the status account; refundable proof-buffer rent; and about 0.0015 SOL per verified header until it is pruned. min_transfer should be well above the relayer’s cost.

Failure handling

FailureWhereResult
Bad amount, over a limit, deposits off, malformed or off-curve destinationAkash transactionFails. Nothing is locked
Recipient is a token account, program, unlisted PDA or reassigned ATAmint_transferVoided / InvalidRecipient, then refund
Governance void ordervoid_transferVoided / Governance, then refund
Per-transfer maximum or daily cap reachedmint_transferRetry later
Minting pausedmint_transferRetry later. Governance voids still work
Light client expired or frozenSolanaRetry after a re-pin
No relayer runningOff-chainDelay. Anyone can relay
Reporters below thresholdAkashTransfer stays pending on Akash. Minted tokens are not affected, but the transfer still counts toward the sender’s pending limit
Conflicting votes or a vetoAkashHold until governance resolves
Refunds off or daily refund cap reachedAkashRefund waits

Stuck transfers. A transfer that cannot be minted, for example because its amount is above a lowered Solana cap, is resolved in this order:

  1. Governance passes MsgRequestVoid { id }.
  2. Anyone proves the void order with void_transfer. If the transfer was already minted, this fails and reporters confirm the mint instead.
  3. Reporters confirm the void, the challenge window passes, and MsgFinalizeRefund pays the sender.

The order is enforced by the protocol. Akash refunds only after reporters observe a permanent Voided status on Solana, and after that status exists the record can never be minted.

Guarantees

  • Each record ID is resolved at most once on Solana: one mint or one void.
  • A mint is always exactly the recorded amount to the recorded recipient.
  • Akash refunds only a transfer confirmed as voided, only to its original sender, only once, and only after the challenge window.
  • Locked AKT leaves x/sink only through MsgFinalizeRefund.

Supply accounting

Locking does not change Akash total supply, which stays the single source of truth.

Solana supply + SPL AKT burned on Solana = amounts of all Minted statuses on Solana
AKT locked behind those Minted records = Solana supply + SPL AKT burned on Solana

Mint confirmations reach Akash a few minutes after minting. The monitor therefore compares Akash records directly with Solana statuses without waiting for minted_amount. Any mismatch or violation of the x/sink bank equality triggers an alert and an immediate pause of Solana minting through the pause guardian key, as proposal 346 requires. The monitor also pages the on-call team, which can pause deposits on Akash.

FigureDefinition
Total supplyAkash total supply
Circulating supplyAkash circulating supply, minus the x/sink balance, plus Solana supply
Supply on SolanaSolana mint supply, which equals the locked AKT behind minted records, minus burns

Before the first mint, Solana supply is zero. AKT burned on Solana stays locked on Akash. A public endpoint publishes these figures so CoinGecko, CoinMarketCap and exchanges report the same numbers.

Launch sequence

Solana (first)

  1. Generate fresh keypairs for the three programs and publish the reproducible build hashes.
  2. Deploy with a one-time ceremony key. In one transaction, initialize the access manager, allow the multisig as its CPI caller, grant it admin and renounce the ceremony key’s admin.
  3. Run initialize. Check 6 decimals, no freeze authority, the PDA mint authority and zero supply.
  4. Move all three upgrade authorities to the multisig and retire the ceremony key.
  5. Through the multisig, set the token metadata and the pause guardian. Publish the mint address.

Akash (later)

  1. Pass a software-upgrade proposal that specifies how to handle any balance already at the sink address. The upgrade adds x/sink and leaves deposits off. The upgrade takes no AKT from OCL; the seed follows as an ordinary deposit.
  2. Conduct the checkpoint ceremony at a post-upgrade height. Through the multisig, in one transaction, initialize the ICS07 client and run configure_bridge("akashnet-2", policy) with a per-transfer maximum and daily cap of at least 1,000,000 AKT and the treasury vault in vaults. Start the relayer immediately because the trusting period begins at the checkpoint. Open relaying or grant relayer roles.
  3. Reporters run in preview mode against their own nodes. After several days without issues and completion of the two independent token and portal audits required by proposal 346, pass a governance proposal to set the reporters, threshold, challenge period, vault list (including the treasury vault) and minimum transfer. It sets max_transfer, solana_daily_mint_cap and daily_refund_cap to at least 1,000,000 AKT and sets enabled to true. This opens transfers: the token launch in proposal 346.
  4. Seed: OCL sends MsgSink for 1,000,000 AKT to the treasury vault. The multisig runs set_bridge_enabled(true) and the relayer mints it. The policy for other deposits while these higher caps apply remains open (see Decisions).
  5. After the seed mints, lower the per-transfer and daily caps on both chains to their launch values, as in “Changing caps” below. Fund the counter-asset and seed the pools.
  6. Run canary transfers: a normal transfer, an invalid recipient (void, then refund after the challenge window), and a transfer at the cap. Check supply after each. Then raise the daily cap in steps.

Changing caps. Raise the Solana caps first, then the Akash caps, keeping daily_refund_cap at least max_transfer. To reduce caps, lower them on Akash first. Wait until every transfer above the new Solana caps has minted or been refunded, then lower the Solana caps. x/sink refuses to lower daily_refund_cap below the largest open transfer, so every open transfer stays refundable.

Initial liquidity

AKT OCL sends the 1,000,000 AKT seed through the bridge to the treasury vault once transfers open. Pools are seeded after it is minted, which requires the upgrade, audits and governance vote to be complete.

Phase transition

The launch phase ends when a later AEP moves emissions to Solana.

  • Akash: set inflation to zero and disable BME minting.
  • Solana: emissions need a new mint path in the AKT program, added by a program upgrade through the multisig and time lock after Akash governance approves it. The proof-based mint stays for as long as deposits stay open.
  • Before Akash stops running as a Cosmos chain, governance directs the multisig to transfer its authority to the successor or permanently disable minting if deposits end.

AKT remains locked. A later AEP will determine what happens to the locked AKT, the Akash chain and residual balances.

Rationale

A light client for minting. Akash validators authorize mints, so there is no separate operator set to select, pay and rotate. Forging a mint requires more than a third of the trusted Akash stake. The ICS07 client from cosmos/ibc-contracts runs on Solana and verifies standard ICS23 proofs of any Akash store, so x/sink needs no IBC upgrade.

Reporters for outcomes. No Solana light client exists for Cosmos, so Akash cannot verify Solana state. Reporters can authorize refunds but cannot mint. Refunds are subject to the challenge window, veto and daily refund cap.

Voids. Solana cannot send Akash a verifiable message. A void is a permanent Solana status in the same account used to record a mint. A transfer therefore cannot have both outcomes. Akash refunds only after reporters observe that status. Once it exists, no one can mint the record, so a refund can never race a late mint. There is no refund on a timeout.

Locking, not burning. Burning would require minting refunds on Akash and would change Akash total supply with every transfer. Locking keeps Akash supply as the single source of truth, makes a refund a bank transfer, and leaves the fate of the locked AKT to governance.

A dedicated module. The marketplace escrow handles leases. The bridge requires immutable proof records, a void-order key, reporter voting and a refund path, which do not belong in the marketplace module.

A dedicated deposit message. Accepting plain sends to the module would mean handling every way funds can reach its address: IBC transfers, distribution withdrawals, authz and contracts. MsgSink is checked before fees are charged. The sink address remains blocked like other module accounts, and the memo is ignored.

The seed goes through the bridge. Every SPL AKT, including OCL’s 1,000,000 AKT seed, is minted against a proven x/sink record, so all supply is backed by proven deposits and every mint is verified by the light client. This delays liquidity: pools can be seeded only after the upgrade, the light-client checkpoint and the seed mint.

A daily cap, with per-transfer and per-sender limits. The Solana per-transfer and daily caps bound what a forged header or a verifier bug could mint. max_transfer keeps every deposit within one day’s mint cap and one day’s refund cap. Per-sender limits slow floods of small deposits that cost the relayer rent. When set, the daily amount limit caps one sender’s share of the daily cap. Absolute amounts in base units are simple to check on both chains. Fixed UTC days allow up to twice the cap within 24 hours around midnight; the design accepts this trade-off. A transfer waits until a UTC day has room for its whole amount. The relayer submits waiting records in ID order, so after each rollover the oldest is tried first. Anyone minting other records first can still delay it, and governance can void and refund a transfer that keeps waiting.

Permissionless mint, void and relaying. The system does not depend on a particular operator. Anyone with a valid proof can resolve a record, and open relaying allows any signer to keep the light client current. Re-pinning lets the program move to a light client run by others.

Validation on Solana as well as on Akash. Akash checks the address format and requires a wallet key or listed vault. Only Solana can see whether the destination is a token account, a program or a reassigned ATA, so the AKT program voids those and the sender gets a refund instead of losing the tokens.

Legacy SPL over Token-2022. See the Token section under Specification.

Backward Compatibility

Existing modules, messages and state remain unchanged. x/sink is new and is installed in a chain upgrade that adds its store. Any existing account at the sink address is converted to the module account, and its balance goes to the community pool. Balances, delegations, vesting, IBC channels and marketplace escrow are untouched. No IBC upgrade is needed. Wallets need the Amino registration of MsgSink to sign with Ledger. Exchanges and data providers see a new non-circulating category (AKT locked in x/sink) and a new circulating venue (Solana).

Test Cases

SuiteCovers
x/sink unit and keeper testsDeposit checks, record encoding, per-sender limits, parameter bounds between min_transfer, max_transfer, solana_daily_mint_cap and daily_refund_cap, settlement voting, epochs, holds, refunds, guardian, genesis import and export, bank equality
Upgrade tests and old-to-new binary rehearsalStore install, no AKT taken from OCL, dust and existing accounts at the sink address swept to the community pool
AKT program unit testsWire format against a hand-written 77-byte vector, per-transfer and daily caps, policy rules, recipient rules, invocation rules
AKT program integration suiteReal patched ICS07 verifier and ICS23 proofs on a local validator: zero-supply initialize, mint, void, replay, caps, pause, guardian, re-pin, expiry, proof buffers, CPI rejection, interface stability
Real Akash state proofA signed MsgSink in the real Akash app, proven on Solana
Local stackReal CometBFT and Solana validators, signed header updates, the 1,000,000 AKT seed under raised caps and the cap lowering after it, mints, a cap-size mint after a UTC rollover, recipient voids, governance voids, challenge and refund, a cap-size refund, validator changes, 100-validator headers, misbehaviour freezing
Team testnetLong-running Akash testnet chain with the full off-chain stack and the reproducible program build

Reproducible build hashes are published with the final release commit, not in this AEP.

Security Considerations

ThreatEffectMitigation
Forged Akash headerUnbacked mint on SolanaNeeds more than a third of trusted Akash stake. The per-transfer and daily caps bound the loss. Automatic guardian pause on a supply mismatch. Misbehaviour freezes the client
Bad initial checkpointLight client trusts a false validator setCheckpoint ceremony with independent people and nodes
Light client expiresMints and voids stop while deposits continueRelayer refreshes at one fifth of the trusting period. Monitors warn at 25% and page at 50% of the period. x/sink guardian pauses deposits. Re-pin after a new ceremony
Reporter threshold compromise, false voidRefund of a transfer that was minted (double spend). A record refunded without a Solana status could also be minted later by anyoneThreshold above two thirds. Challenge window of at least 72 hours with a single-reporter or governance veto. Daily refund cap. The honest relayer checks Akash first, and the monitor alerts
Reporter threshold compromise, false mintA voided transfer closes as Minted and the sender is never refunded. Supply is unaffectedThreshold. Any honest conflicting vote holds the transfer for governance. Monitor compares outcomes with Solana statuses
Reporter reads a fabricated Solana slotA veto is delayed by a few minutesReporters reject slots beyond their own finalized slot plus a margin and look up signatures directly. Challenge period of at least 72 hours
Wrong destinationTokens minted to an unreachable accountAkash requires a wallet key or listed vault. Solana voids token accounts, programs, unlisted PDAs and reassigned ATAs. The portal fills in the address from a connected wallet. A valid key nobody holds cannot be detected
ReplayDouble mint or double refundOne permanent status account per record ID. IDs are never reused. Akash refunds once and only from Voided. Never re-pin to a reset or forked chain that reuses IDs
Event hidden from reportersTransfer stays pending on AkashMint and void reject CPI and allow only ComputeBudget instructions before them
Rent griefing with tiny depositsRelayer spends SOLmin_transfer, the per-sender limits, and the relayer’s spend ledger and per-transfer budget
Daily cap contentionA transfer needs room for its full amount within a day’s cap, so a transfer near the cap may wait several days. The same applies to refunds under daily_refund_capSet the cap well above normal daily volume, and the per-transfer maximum well below it. The relayer submits in ID order. Monitors alert on pending age and above 80% cap usage. Raise the cap on both chains, Solana first. Governance can void and refund a transfer that keeps waiting
Large validator setHeader updates exceed computeRelayer submits only a quorum of signatures. Recheck before the active set grows past 100
Upgrade halts the chainUnexpected account at the sink addressPrepareInstall adopts any account and moves its balance to the community pool
Governance capture on AkashHostile parameters or void ordersA void refunds only the original sender and only if the transfer was not minted. Locked AKT cannot be sent anywhere else
Impostor mintsUsers buy the wrong tokenCanonical address published before the seed. Jupiter verification. Seeded pools on the canonical mint

A compromised upgrade authority poses the greatest risk because it can mint without limit. A time-locked multisig holds that authority, monitors track its actions, and guardians can pause both chains without waiting for the time lock.

Two independent audits of the token and portal code must be completed before transfers open, as proposal 346 requires. Their reports must be published within 30 days.

Operational considerations

Wallets. MsgSink is a normal Cosmos transaction, so Keplr, Leap and Ledger-backed accounts can sign it. Ledger signing uses its Amino registration. The portal connects the Solana wallet and uses its address as the destination. Manual address entry shows a warning because Solana addresses have no checksum. AKT on Osmosis or other IBC chains must return to Akash first. No SOL is needed to send a transfer.

Exchanges. Exchanges can move AKT from their Akash hot wallets to their Solana deposit addresses. SPL AKT deposits and withdrawals at exchanges close the arbitrage loop between exchanges and Solana DEXes. Exchange coordination begins before launch.

Canonical address. Before the seed deposit is minted, publish the mint address on akash.network and GitHub, obtain Jupiter verification, update the contract address on the existing CoinGecko and CoinMarketCap AKT pages, and claim the Solscan and SolanaFM pages. There is one official portal domain, announced through official Akash channels.

Incentives. None at launch. LP incentives are reviewed at 90 days.

Operators. Run at least one relayer; anyone can run additional relayers. Each reporter operates independently, using its own hosts, Akash and Solana nodes, and key. Alerts go to the on-call operator.

Monitoring. Track light-client age and frozen state, pending-transfer age, and transfers resolved on Solana but still pending on Akash. Monitor every void during its challenge window, reconcile supply, and check the bank equality. Track all authority and configuration changes on both chains, guardian and re-pin actions, relayer and reporter fee balances, reporter vote age, and cap usage above 80%.

Runbooks.

IncidentFirst actionFollow-up
Suspected bad mint or verifier bugSolana guardian pauses minting, automatically on a supply mismatch. x/sink guardian pauses depositsReconcile supply, then upgrade or re-pin
Suspicious void quorumAny honest reporter vetoes. If evidence cannot be checked, the x/sink guardian pauses refundsGovernance resolves with MsgResolveTransfer
Light client close to expiryRestart the relayer, switch RPC, check the payer balanceDetermine why updates stopped
Light client expired or frozenPause depositsNew checkpoint ceremony, then re-pin
Relayer key leakedRevoke its RELAYER_ROLELoss is limited to its SOL balance. It cannot forge headers
Reporter key leakedGovernance replaces the reporter. The new epoch prevents refunds based on older void confirmationsWatch the other reporters’ votes
Multisig member key leakedRotate the memberReview pending multisig transactions

Decisions

Decided

ItemDecision
VerificationICS07 Tendermint light client on Solana, pinned in the AKT program and replaceable with repin_light_client
Deposit formatDedicated MsgSink. The sink is a blocked bank address
Return pathPermanent Solana voids observed by a reporter threshold, with a challenge window, veto and daily refund cap
Initial supplyNone at token creation. Once transfers open, OCL sends 1,000,000 AKT with MsgSink to the treasury vault, under caps raised to at least that amount on both chains and lowered once it mints
Token programLegacy SPL Token, 6 decimals, no freeze authority, PDA mint authority
CapsA per-transfer maximum and a daily mint cap in base units on fixed UTC days, on both chains. On Akash, also per-sender daily and pending limits
PausingA pause-only guardian on each chain
RelayingOpen to any signer through the open relaying patch. Mint and void are permissionless
Fee grantNone. Users pay the normal Akash fee
IncentivesNone at launch

Open

ItemProposalDecided by
Seed windowDuring OCL’s seed transfer, deposits are open to everyone with caps of at least 1,000,000 AKT on both chains. Choose whether the x/sink guardian pauses deposits immediately after the seed until a later proposal reopens them with low caps, or deposits remain open while caps are loweredAkash governance
Amino namessink/MsgSink and sink/MsgGuardianPause differ from the akash/... style. Changing them changes sign bytes, so decide before the releaseAkash maintainers
Reporter setAt least 7 independent operators, threshold 5Akash governance
Challenge periodAt least 259,200 s (72 hours)Akash governance
Light-client trusting periodAt most 1,209,600 s (14 days)Multisig
Maximum clock driftMeasure on mainnet firstMultisig
CapsStart low, except for the seed transfer, and raise after a period without issues. max_transfer at most the Solana per_transfer_max, solana_daily_mint_cap equal to the Solana daily cap, daily_refund_cap at least max_transferAkash governance, multisig
Per-sender limitsThe defaults: 20 deposits a day and 3 open transfers per senderAkash governance
Minimum transferWell above the relayer’s cost of about 0.0035 SOL per mintAkash governance
Vault allowlistReviewed vaults only, the same list on both chainsAkash governance, multisig
Pause guardiansAn on-call key on Solana, also held by the monitor for the automatic pause. An on-call key or group policy for x/sinkOperations, Akash governance
Checkpoint ceremonyThree people, three independent nodes, a post-upgrade heightLaunch team
Program keypairsFresh keypairs for all three programs. Destroy the verifier’s keypair after deployment because open relaying grants the role to its program IDLaunch team
Token metadataName, symbol, logo and a public, versioned HTTPS URIAkash maintainers
AuditsTwo independent audits of the token and portal code, covering the mint logic, before transfers open, as proposal 346 requires. Reports published within 30 daysOCL

References

All content is licensed under Apache 2.0.

Created:

Last Updated:

Status:

Authors:

View next aep

Completion Date: 7/30/2018

Decentralized cloud computing exchange connects those who need computing resources with those that have computing capacity to lease providers. Based on the Akash Network Whitepaper.