Skip to content

velocity-exchange/multi-distributor

Repository files navigation

Drift DFX IOU Claims

A standalone claim program and supporting toolset for distributing DFX IOU tokens through Merkle proofs.

This repo is forked from Merkle distributor tooling and keeps the existing distributor/claim model intact: each claim tree stores a Merkle root, each claimant proves their allocation, and tokens are paid from a program-owned vault.

Program ID

Current planned DFX distributor program ID:

distAitdwx9mDm3SaPMtGZRjpXMPUenLhmPwoySV3Hp

The matching program keypair is intentionally not committed. Keep it outside git and copy it to target/deploy/merkle_distributor-keypair.json only when preparing a deploy. If the deployer chooses a different keypair later, run anchor keys sync and update downstream dfx-claim configuration.

Token Mint

The DFX IOU mint itself is created with the helpers under scripts/:

  1. scripts/upload-metadata — TypeScript uploader (Metaplex Umi + Irys) that pushes the logo image and off-chain JSON to Arweave and returns the metadata URI.
  2. scripts/create-vanity-token.sh — grinds a vanity mint address, creates the classic SPL Token, attaches Metaplex Token Metadata via metaboss (immutable), mints the initial supply, verifies the wallet balance, and disables the mint authority.

See scripts/README.md for prerequisites, the full flag reference, the end-to-end chained example, and mainnet caveats (immutable metadata, freeze authority handling, vanity prefix cost).

The resulting mint address is what gets passed as --mint [TOKEN_MINT] to every CLI command below.

Claim Model

  • Create one distributor per asset or market-specific asset bucket.
  • Build leaves at the Drift authority level.
  • For immediate DFX IOU claims, set each leaf with amount_unlocked = full entitlement and amount_locked = 0.
  • Fund each distributor vault with the intended token amount by SPL transfer.
  • Users call new_claim once per eligible distributor to withdraw their unlocked amount.

The vesting fields remain available for compatibility with the original program. For DFX IOU claims, use unlocked-only trees and a far-future clawback timestamp.

Claiming From a Program (CPI)

Some claimants are programs rather than wallets — the program itself owned the bad debt, so a program PDA is the claimant in the merkle tree. Since new_claim requires the claimant to sign, such programs claim via CPI with the PDA signing through seeds.

See cpi-example/ for a complete worked example: an Anchor program with a claim endpoint and admin-gated withdraw, admin scripts that fetch proofs from the API, and a litesvm test covering the full flow. The example is not audited and is intended as a reference — adapt the pattern into your own program rather than deploying it as-is.

CLI

Build sharded Merkle trees, create distributors, fund vaults, and verify setup:

cargo build

# CSV amounts are raw on-chain base units (the prepare-*-csv.py scripts already scale to base units),
# so use --decimals 0 with the default --csv-amount-unit tokens. Passing --decimals 6 here would
# multiply every claim by 10^6. See MERKLE_TREES.md.
target/debug/cli create-merkle-tree --csv-path merkle-tree/devnet/csv/output.csv --merkle-tree-path merkle-tree/devnet/trees --max-nodes-per-tree 12000 --amount 0 --decimals 0 --start-airdrop-version 0

target/debug/cli --mint dfxKL8VLUjLMCnFiJ57ZjrjGDiDMLRX8tHmg8biUV39 --keypair-path ~/.config/solana/id.json --rpc-url https://api.devnet.solana.com new-distributor --start-vesting-ts 1779785439 --end-vesting-ts 1779786439 --merkle-tree-path merkle-tree/devnet/trees --clawback-start-ts 1811321499 --enable-slot 465006999

target/debug/cli --mint dfxKL8VLUjLMCnFiJ57ZjrjGDiDMLRX8tHmg8biUV39 --keypair-path ~/.config/solana/id.json --rpc-url https://api.devnet.solana.com  fund-all --merkle-tree-path merkle-tree/devnet/trees

target/debug/cli --mint dfxKL8VLUjLMCnFiJ57ZjrjGDiDMLRX8tHmg8biUV39 --keypair-path ~/.config/solana/id.json --rpc-url https://api.devnet.solana.com  verify --merkle-tree-path merkle-tree/devnet/trees --clawback-start-ts [CLAWBACK_START_TS] --enable-slot [ENABLE_SLOT] --admin [ADMIN]

See MERKLE_TREES.md for CSV format, amount units, and distributor-version guidance.

Deploy Scripts

The raw CLI commands above work for a one-off mint. For batch deploys — especially the Insurance Fund (IF), which spans ~63 spot markets each with its own mint — use the config-driven wrappers under scripts/deploy-merkle-trees/. They drive the same cli binary but read shared settings (RPC, program id, keypair, vesting/clawback timestamps, sharding size) from one JSON config so the CLI-call sequence never drifts between markets.

  • deploy-if.sh — IF: iterate markets[] and, per market, generate trees (create-merkle-tree) then create the on-chain distributor (new-distributor).
  • deploy-dfx.sh — the single DFX IOU mint, same two steps.
  • fund-if.sh / fund-dfx.sh — run fund-all against the generated trees to top each distributor vault up to its remaining (unclaimed) entitlement. Funding is idempotent, including after claiming has started, and skips clawed-back distributors.
  • deploy-common.sh — sourced by all four; holds preflight checks, the cli path resolver, jq config readers, and the shared deploy_market() / fund_market() functions.

Copy an example config, fill it in (the real files are gitignored), and run:

cp scripts/deploy-merkle-trees/if-markets.example.json scripts/if-markets.json
cp scripts/deploy-merkle-trees/dfx-config.example.json scripts/dfx-config.json

# generate trees + create distributors
./scripts/deploy-merkle-trees/deploy-if.sh  --config scripts/if-markets.json  --csv-dir ./if-csv  --trees-dir ./if-trees
./scripts/deploy-merkle-trees/deploy-dfx.sh --config scripts/dfx-config.json --csv-dir ./dfx-csv --trees-dir ./dfx-trees

# then fund the vaults
./scripts/deploy-merkle-trees/fund-if.sh  --config scripts/if-markets.json  --trees-dir ./if-trees
./scripts/deploy-merkle-trees/fund-dfx.sh --config scripts/dfx-config.json --trees-dir ./dfx-trees

Pass --dry-run to print the exact, copy-pasteable CLI commands without touching the chain. Trees are always generated in base-unit mode (--csv-amount-unit tokens --decimals 0), so each CSV integer is the exact on-chain claim amount. The verify step and CSV generation stay manual — see scripts/deploy-merkle-trees/deploy-merkle-trees.md for the full flag reference, config keys, and caveats, and DEPLOY.md for the end-to-end runbook.

Development With Devcontainer

This repo includes a devcontainer with pinned Rust, Solana, and Anchor versions for program development.

Build the container:

cd .devcontainer
docker build -t dfx-distributor-dev .

Start the devcontainer service:

docker compose up -d

Open a shell inside it:

docker compose exec dfx-distributor /bin/bash

Alternatively, use your IDE's devcontainer integration. In VS Code or Cursor:

  1. Press Cmd+Shift+P on macOS, or Ctrl+Shift+P elsewhere.
  2. Run Dev Containers: Reopen in Container.
  3. Wait for the container to build.
  4. Use the IDE terminal inside the devcontainer.

Common commands:

# build program without requiring the deploy keypair
anchor build

# build program with keypair check
mkdir -p target/deploy
cp /path/to/merkle_distributor-keypair.json target/deploy/merkle_distributor-keypair.json
anchor build

# update checked-in IDL after build
cp target/idl/merkle_distributor.json programs/merkle-distributor/idl/merkle_distributor.json

# run Rust tests
cargo test -p merkle-distributor --lib
cargo test -p jito-merkle-tree

API

The Axum server under api serves Merkle proof and claim-status data for users.

cargo build
target/debug/drift-dfx-distributor-api --merkle-tree-path merkle-tree/trees \
  --program-id distAitdwx9mDm3SaPMtGZRjpXMPUenLhmPwoySV3Hp \
  --mint dfxKL8VLUjLMCnFiJ57ZjrjGDiDMLRX8tHmg8biUV39 \
  --rpc-url https://api.devnet.solana.com  \
  --ws-url wss://api.devnet.solana.com

About

DFX IOU Merkle distributor program and tooling

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors