Skip to content

Latest commit

 

History

History

devnet

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

README.md

Devnet Setup Commands

Prerequisites

cp .env.example .env
# Edit .env with your keys
# Required (no defaults shipped): set POSTGRES_PASSWORD and POSTGRES_REPLICATION_PASSWORD
# (and JWT_SECRET if enabling auth, ADMIN_PRIVATE_KEY for the operator) or services fail to start
# Generate strong values with: openssl rand -hex 32

Two admin keys

Steps 1-3 below are escrow governance and are signed by keypairs/escrow-admin.json, the Instance.admin key. The services run as a different key, the channel admin in ADMIN_PRIVATE_KEY, which holds receipt-mint authority and is what step 2 registers as the operator. Keep them separate: SetNewAdmin only rewrites Instance.admin, so a key that is also the channel admin keeps its receipt-mint authority and its Operator PDA after a handover and can still drain escrow custody.

The commands below read their signers from keypair files. Create them first:

mkdir -p keypairs
solana-keygen new -o ./keypairs/escrow-admin.json -s --no-bip39-passphrase  # signs create_instance / add_operator / allow_mint
solana-keygen new -o ./keypairs/user.json -s --no-bip39-passphrase          # signs deposit / withdraw

Run all commands from project root:

1. Create Instance

Also allocates the instance's withdrawal bitmap (8 KB, about 0.058 SOL of rent), so fund the admin keypair accordingly before running this.

cargo run --bin create_instance -- \
  https://api.devnet.solana.com \
  ./keypairs/escrow-admin.json

2. Add Operator

cargo run --bin add_operator -- \
  https://api.devnet.solana.com \
  ./keypairs/escrow-admin.json \
  <INSTANCE_ID> \
  <OPERATOR_PUBKEY>

3. Allow Mint

cargo run --bin allow_mint -- \
  https://api.devnet.solana.com \
  ./keypairs/escrow-admin.json \
  <INSTANCE_ID> \
  <MINT_ADDRESS> \
  <WITHDRAW_FEE> \
  <MIN_WITHDRAW_AMOUNT> \
  <MIN_DEPOSIT_AMOUNT>

<WITHDRAW_FEE> is in the mint's base units, charged on top of every Solana Private Channels withdrawal and paid to the operator admin; size it to cover one release's SOL cost. <MIN_WITHDRAW_AMOUNT> is the smallest amount one withdrawal may move, also in base units, which limits how many releases a balance can queue at once. 0 is allowed for either, but a fee of 0 leaves the operator paying every release, so use it only where every participant is known; see the zero-fee warning. Run again with new values, to or from 0, to reprice; they apply from the mint's next deposit. Re-running also re-opens both gates and re-pins the mint profile (accepting any change since the last allow), so follow the reprice procedure. <MIN_DEPOSIT_AMOUNT> is the smallest amount a deposit must land in the escrow, in base units, 0 for none; the escrow enforces it from the moment allow_mint lands.

Run allow_mint only once the stack is up and indexer-solana is healthy. With no backfill start_slot set, as in the shipped devnet config, a fresh escrow indexer starts at the chain tip and never indexes an earlier AllowMint, so the mint's first deposit is quarantined (docs/runbooks/deposit_manual_review.md Path F).

4. Deposit (Solana → Solana Private Channels)

cargo run --bin deposit -- \
  https://api.devnet.solana.com \
  ./keypairs/user.json \
  <INSTANCE_ID> \
  <MINT_ADDRESS> \
  <AMOUNT>

5. Withdraw (Solana Private Channels → Solana)

cargo run --bin withdraw -- \
  http://localhost:8899 \
  ./keypairs/user.json \
  <MINT_ADDRESS> \
  <AMOUNT>

Prints the mint's withdraw fee and minimum before sending, and stops if <AMOUNT> is below the minimum. The balance must cover <AMOUNT> plus the fee, and only <AMOUNT> is released on Solana. The operator admin withdraws without either.

The tool reads the mint's withdraw config before sending. With JWT on (JWT_SECRET set) the public gateway refuses that read without a token, so point it at the read node: PRIVATE_CHANNEL_READ_RPC_URL=http://localhost:8901. Sending still goes through the gateway URL.

Seize (permanent delegate, channel only)

Moves a user's channel balance into the compliance ATA, ATA(<COMPLIANCE_OWNER>, <MINT>) under Token-2022, signed by the permanent delegate key. It is the only move the node admits from that key; anything else is refused at ingress.

cargo run --bin seize -- \
  http://localhost:8899 \
  ./keypairs/delegate.json \
  <MINT> \
  <SOURCE_OWNER> \
  <COMPLIANCE_OWNER> \
  [--amount <BASE_UNITS>] [--freeze-authority ./keypairs/admin.json]

One transaction creates the compliance ATA if needed, thaws a frozen source, moves the whole balance (or --amount) with TransferChecked, and freezes the source again. The freeze authority defaults to the delegate key. It simulates with sigVerify first, because the node drops a refused delegate move without an RPC error.

With JWT on (JWT_SECRET set) the public gateway refuses the account reads and the simulation without a token, so point them at the read node: PRIVATE_CHANNEL_READ_RPC_URL=http://localhost:8901. Sending still goes through the gateway URL.

Block Mint (set the deposit / withdrawal gates)

Both flags are absolute, so passing false for one re-opens that gate. The script prints the current gates before sending and warns when a flag re-opens one. Blocking deposits leaves already-escrowed balances withdrawable.

cargo run --bin block_mint -- \
  https://api.devnet.solana.com \
  ./keypairs/escrow-admin.json \
  <INSTANCE_ID> \
  <MINT_ADDRESS> \
  <BLOCK_DEPOSITS> \
  <BLOCK_WITHDRAWALS>

Whitelist (optional)

Set which directions require an allowed wallet. Both flags are absolute, and the script warns when a flag turns a direction off.

cargo run --bin set_whitelist_enabled -- \
  https://api.devnet.solana.com \
  ./keypairs/escrow-admin.json \
  <INSTANCE_ID> \
  <WHITELIST_DEPOSITS> \
  <WHITELIST_WITHDRAWALS>

Add a wallet, or rewrite its flags. Blocking both directions is rejected.

cargo run --bin add_or_update_allowed_wallet -- \
  https://api.devnet.solana.com \
  ./keypairs/escrow-admin.json \
  <INSTANCE_ID> \
  <WALLET_PUBKEY> \
  <BLOCK_DEPOSITS> \
  <BLOCK_WITHDRAWALS>

Remove a wallet; its rent goes back to the admin.

cargo run --bin remove_allowed_wallet -- \
  https://api.devnet.solana.com \
  ./keypairs/escrow-admin.json \
  <INSTANCE_ID> \
  <WALLET_PUBKEY>

See the whitelist notes before turning on withdrawals or removing a wallet.

Monitor

# Watch deposit processing
docker logs -f private-channel-operator-solana

# Watch withdrawal processing
docker logs -f private-channel-operator-private-channel