@provablehq/shield-swap-cli
v0.12.1
Published
Command line trader for the Shield Swap AMM DEX on Aleo.
Maintainers
Keywords
Readme
@provablehq/shield-swap-cli
The shield-swap command: trade on Shield Swap from a terminal. Each subcommand
does one job against a live deployment — builds a session, plans the work,
prints what happened.
This is a separate install from @provablehq/shield-swap-sdk, so a project that
only needs the client never pulls the command line in. Install it, then call it by
name:
npm install -g @provablehq/shield-swap-cli
shield-swap pools
shield-swap swap --from USDCx --to ETH --amount 1.5 --executeOr keep it in the project and go through the local bin:
npm install --save-dev @provablehq/shield-swap-cli
npx shield-swap setup --new
npx shield-swap poolsnpx shield-swap runs the binary that is installed. npx @provablehq/shield-swap-cli
names the package instead, which sends npx to the registry: the version can change
between two commands in the same session, and it does not work offline. Prefer the
install.
shield-swap on its own lists the subcommands, and every subcommand takes
--help for its own flags.
Running it from the Veil repo
No build step. tsx honours the root tsconfig's paths, so the SDK resolves
straight to source and every run picks up edits immediately:
pnpm install
pnpm shield-swap pools
pnpm shield-swap swap --from USDCx --to ETH --amount 1.5Use pnpm -s when parsing the output. pnpm run prints a two-line banner on
stdout, which is harmless for a person and fatal for --json:
pnpm -s shield-swap balances --jsonOr skip the wrapper and call it directly, which has no banner:
npx tsx packages/shield-swap-cli/src/index.ts balances --jsonTo exercise the built binary as a user would get it — the shebang, the lazy
command loading, the bin wiring — build the package and run dist:
pnpm --filter @provablehq/shield-swap-cli build
node packages/shield-swap-cli/dist/index.js poolsState lands in ./.shield-swap/<network>/ relative to the working directory, so
running from the repo root keeps a session there. Set SHIELD_SWAP_STATE_DIR to
point somewhere else — worth doing if you want a scratch account separate from
the one you normally trade with.
To drive the SDK directly while sharing the same state file the command line writes, import the session helpers:
import { loadSession, formatAmount } from '@provablehq/shield-swap-cli/session'Two rules that hold everywhere
Nothing spends until you pass --execute. Every write command plans the
transaction against live chain state, prints exactly what it would do, and stops.
The dry run and the real run differ only in whether a transaction follows, so a
first run is always safe and always worth doing.
Mainnet is never implicit. The default network is testnet. Mainnet needs
--network mainnet on every invocation (or SHIELD_SWAP_NETWORK=mainnet in the
environment), because everything downstream is per-network — the DEX API host, the
prover, the record scanner, the token registry, and the blinded identity store —
and the mainnet ones move real value. When a plan is for mainnet, the banner above
it says so.
Amounts you type are human units (--amount 1.5), amounts you read are rendered
with each token's decimals, and raw base units stay inside the SDK where they
belong. --json prints one machine-readable object and silences everything else,
so an agent or a pipeline can drive the same command a person uses.
What each command needs
Every command loads the session from ./.shield-swap/<network>/state.json, which
means all of them need setup to have run once. "Funds" below means a private
balance: private records are what pay for trades and deposits, and a public
balance cannot be traded from. Transaction fees are paid by the delegated prover's
FeeMaster account by default, so a faucet-funded account needs no public credits
(set SHIELD_SWAP_FEE_MASTER=0 when the account should pay its own).
| Command | What it does | Needs |
| --- | --- | --- |
| setup | Sets up all credentials Shield Swap requires, idempotently: key material, DEX authentication, Provable API credentials, invite-code redemption, API token, testnet airdrop. | Nothing (an invite code when access is locked; a key file for a returning account) |
| redeem | Redeems a referral code, or gets/creates the account's shareable code with --generate. | Session; referral code when redeeming |
| pools | Lists pools from the API and joins each with chain state, so the tradeable flag and the depth come from the mappings rather than the index. | Session |
| balances | Private and public holdings per token, reconciled against the registry. | Session, record access |
| positions | Every liquidity position the account holds, with its range, its backing amounts, and the fees earned that a collect would pay. | Session, record access |
| swap | Sells one token for another, single hop or routed, then claims the output. | Funds in the token being sold |
| swap-concurrent | Makes multiple swaps concurrently, one per token sold, planned before any is submitted. | Funds in each token being sold |
| history | Swap history and the status of each swap, claiming what is still waiting; rebuilds a lost identity store from chain history. | Session (claiming needs the prover) |
| mint | Opens a position: aligns a percentage range to the pool's tick spacing and deposits what the range consumes. | Funds on both sides of the pool |
| liquidity | Adds to an open position, or removes liquidity and books it as owed. | A position; funds on both sides to add |
| collect | Sweeps what a position is owed into records, and with --close burns the drained position. | A position with something owed |
| liquidity-e2e | The whole lifecycle in one run — mint, increase, decrease, collect, burn — with the waits each step needs. | Funds on both sides of the pool |
"Record access" means the hosted record scanner, which setup configures with
the Provable API credentials it registers. Reads that touch only mappings
(pools) work without it.
Redeeming or generating a referral code
For an account already saved by setup, preview and redeem a referral code:
shield-swap redeem --code REF123
shield-swap redeem --code REF123 --execute
shield-swap redeem --network mainnet --code REF123 --execute --json--code is required for redemption. Redemption authenticates with the saved account, unlocks
the gated DEX API endpoints, and records access in that network's state file.
It spends no funds and requests no airdrop. The API reports invalid or already-used
codes as errors. Without --execute, the command only previews the account and
code; it does not check whether the code is valid.
If setup stopped because an invite code was missing, it has already saved the
account. Run redeem to unlock access, then re-run setup to finish the remaining
setup steps.
To generate a code to share, use --generate instead of --code:
shield-swap redeem --generate
shield-swap redeem --generate --execute --jsonThe server creates a personal code when issuance is enabled, or returns the
account's existing code. Generation requires --execute and does not redeem a
code or change the account's access grant. --generate and --code MUST NOT be
combined. If no code can be issued, the command reports an error.
A suggested order
shield-swap setup --new— once. It ends by telling you the account is ready, and on testnet it draws funds and waits for the records to land.shield-swap balances— confirm what arrived. Nothing below works until this shows a private balance.shield-swap pools— see what can be traded and how deep it is. Note the pool keys and symbols you care about.shield-swap swap --from … --to … --amount …— no--executefirst, then with it. A swap is two transactions and this does both, so the proceeds arrive in the same run.shield-swap history— after any trading session. It reads the chain rather than local bookkeeping, so an entry appears exactly when a claim would succeed.shield-swap mint --pair … --percent …— become the market instead of trading against it. Read the plan carefully: the range, and how much of each side the range actually consumes.shield-swap positions— watch what the position holds and earns.shield-swap liquidity --position … --increase|--decrease— top it up, or take part of it back out. A decrease books the proceeds; it does not pay them out.shield-swap collect— sweep the earnings, and--closeto burn a position you are finished with.
shield-swap liquidity-e2e walks steps 6 through 9 in one go. It is the fastest
way to prove a funded account works end to end against a live deployment, and the
place to look for how the waiting between dependent transactions has to be done.
Two kinds of lag worth knowing about
Both bite when one transaction is built from the result of the last one, which is most of the liquidity flow.
Mapping writes propagate to reads asynchronously, so a read taken straight after a confirmed transaction can still show the previous state. Every command that needs its own write back polls for it.
The record scanner lags further, and its failure mode is quieter. Each liquidity write spends the position record and issues a new one; a transaction built on the spent record carries a serial number the chain has already consumed, so the node drops it at verification. It never reaches a block, and the only symptom is a confirmation wait against a transaction nothing has heard of. Checking that a record exists is not enough — the spent one satisfies that too — so the commands wait for the record's tag to change.
Keeping state safe
./.shield-swap/<network>/state.json holds the private key and the DEX
credentials, written with mode 0600. Add .shield-swap/ to .gitignore and treat
it like a wallet file. Set SHIELD_SWAP_STATE_DIR to keep it somewhere else.
Nothing is shared between testnet and mainnet — not the key, not the API grant, and above all not the blinded identity store, whose reservations are only meaningful against the chain they were checked on.
swap and swap-concurrent obtain API-backed quotes and pass them directly to
execution. The printed minimum is the submitted minimum, for 1–3-hop routes.
Missing output estimates and expired quotes fail before submission; rerun to
obtain fresh terms. Quote lifetime is 60 seconds from the request start.
