@provablehq/shield-swap-cli 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
var COMMANDS = {
|
|
13
13
|
setup: {
|
|
14
14
|
summary: "Set up all credentials required for Shield Swap.",
|
|
15
|
-
load: () => import("./setup-
|
|
15
|
+
load: () => import("./setup-OR4NAV56.js")
|
|
16
16
|
},
|
|
17
17
|
pools: {
|
|
18
18
|
summary: "List pools with their on-chain depth and whether they are tradeable.",
|
|
@@ -97,7 +97,7 @@ async function setup(argv) {
|
|
|
97
97
|
console.log(
|
|
98
98
|
`\u2713 Provable API consumer: ${provable.credentials.consumerId}` + (provable.registered ? ` (registered, saved to ${credentialsPath(network)})` : "")
|
|
99
99
|
);
|
|
100
|
-
const status = await client.api.
|
|
100
|
+
const status = await client.api.getReferralStatus();
|
|
101
101
|
if (!status.has_access) {
|
|
102
102
|
if (!inviteCode) {
|
|
103
103
|
console.error(
|
|
@@ -105,24 +105,13 @@ async function setup(argv) {
|
|
|
105
105
|
);
|
|
106
106
|
process.exit(2);
|
|
107
107
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
()
|
|
112
|
-
]) {
|
|
113
|
-
try {
|
|
114
|
-
await attempt();
|
|
115
|
-
redeemed = true;
|
|
116
|
-
break;
|
|
117
|
-
} catch (err) {
|
|
118
|
-
if (err instanceof ApiError && err.status === 400) continue;
|
|
119
|
-
throw err;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
if (!redeemed) {
|
|
108
|
+
try {
|
|
109
|
+
await client.api.redeemReferralCode(inviteCode);
|
|
110
|
+
} catch (err) {
|
|
111
|
+
if (!(err instanceof ApiError) || err.status !== 400) throw err;
|
|
123
112
|
console.error(
|
|
124
113
|
`
|
|
125
|
-
INVALID_INVITE_CODE: the server rejected "${inviteCode}"
|
|
114
|
+
INVALID_INVITE_CODE: the server rejected "${inviteCode}". Ask the user for a valid, unused code and re-run.
|
|
126
115
|
`
|
|
127
116
|
);
|
|
128
117
|
process.exit(2);
|
|
@@ -226,4 +215,4 @@ async function main(argv) {
|
|
|
226
215
|
export {
|
|
227
216
|
main
|
|
228
217
|
};
|
|
229
|
-
//# sourceMappingURL=setup-
|
|
218
|
+
//# sourceMappingURL=setup-OR4NAV56.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/setup.ts"],"sourcesContent":["/**\n * Shield Swap account bootstrap — the startup gauntlet, idempotent.\n *\n * Run it as many times as needed; every step is check-then-act, so a failed\n * or interrupted run resumes where it stopped:\n *\n * 1. Key material — reuse the stored account, import the user's\n * existing key, or (only with --new) generate one\n * 2. DEX authentication — challenge/verify session with the account\n * 3. Provable API — reuse/import credentials, else self-register a\n * consumer for proving + scanning\n * 4. Invite code — check access; redeem a code when one is provided\n * 5. API token — mint a long-lived ss_ token for later sessions\n * 6. Airdrop — request testnet tokens when holdings are empty,\n * then poll until the PRIVATE records land\n *\n * Usage:\n * shield-swap setup --new # brand-new account\n * shield-swap setup --network mainnet --private-key-file <path> # mainnet\n * shield-swap setup --private-key-file <path> # returning user (key in a file)\n * shield-swap setup --invite-code CODE # when access is locked\n * shield-swap setup --api-url <origin> # pin a DEX API deployment\n *\n * A private key is NEVER pasted into a conversation or command history: a\n * returning user either writes it to a file and passes the path, or exports\n * SHIELD_SWAP_PRIVATE_KEY (or SHIELD_SWAP_PRIVATE_KEY_FILE) in their own\n * shell. Other environment fallbacks: ALEO_CONSUMER_ID + ALEO_DPS_API_KEY,\n * SHIELD_SWAP_INVITE_CODE, SHIELD_SWAP_API_URL.\n *\n * Exit codes: 0 ready · 2 needs input from the user (message says what) ·\n * 3 airdrop still pending · 1 anything else.\n *\n * State lands in ./.shield-swap/<network>/state.json (private key +\n * credentials — gitignore it, treat it like a wallet file). Nothing is shared\n * between networks, including the blinded identity store, whose reservations\n * are only meaningful against the chain they were checked on.\n *\n * Mainnet moves real value, so it is never the default: pass\n * `--network mainnet` explicitly. The airdrop step is testnet-only and refuses\n * to run on mainnet rather than pretending a faucet exists.\n */\nimport { readFileSync } from 'node:fs'\nimport { help } from '../color.js'\nimport { ApiError, DEFAULT_API_URL } from '@provablehq/shield-swap-sdk'\nimport { fileCredentialStore } from '@provablehq/veil-aleo-sdk/node'\nimport {\n loadState,\n saveState,\n ensureKeyMaterial,\n credentialsPath,\n resolveNetwork,\n stateDir,\n NeedsConfigDecisionError,\n loadSession,\n formatAmount,\n pollUntil,\n} from '../session.js'\n\nconst USAGE = `shield-swap setup — bootstrap an account and get it funded\n\n --new generate a brand-new account\n --private-key-file <path> import an existing key, read from this file\n --consumer-id <id> Provable API consumer id (else self-registers)\n --api-key <key> Provable API key\n --invite-code <code> redeem an invite code when access is locked\n --api-url <origin> pin a DEX API deployment\n --network <testnet|mainnet> default testnet\n\nEvery step is check-then-act, so re-running resumes where a failed run stopped.\n\nA private key is NEVER pasted into a conversation or command history: write it\nto a file and pass --private-key-file, or export SHIELD_SWAP_PRIVATE_KEY in your\nown shell. Other environment fallbacks: SHIELD_SWAP_PRIVATE_KEY_FILE,\nALEO_CONSUMER_ID, ALEO_DPS_API_KEY, SHIELD_SWAP_INVITE_CODE, SHIELD_SWAP_API_URL.\n\nExit codes: 0 ready · 2 needs input from the user · 3 airdrop still pending ·\n1 anything else.`\n\n/**\n * Reads a `--flag value` pair out of the arguments.\n *\n * `setup` keeps its own reader rather than using the shared `flags()`: every\n * option is an optional string with an environment-variable fallback, and none\n * of them gate a transaction, so the strict unknown-flag rejection that\n * protects the spending commands would only get in the way here.\n *\n * @param argv Arguments after the subcommand name.\n * @param flag The flag to look for, leading dashes included.\n * @returns The argument that follows the flag, or undefined when it is absent.\n */\nfunction argValue(argv: string[], flag: string): string | undefined {\n const i = argv.indexOf(flag)\n return i >= 0 ? argv[i + 1] : undefined\n}\n\n/**\n * Resolves the private key to import, when the caller is restoring an account.\n *\n * The key itself never travels through a conversation or the command line: it\n * is read from a file the user wrote, or from an env var the user exported in\n * their own shell.\n *\n * @param argv Arguments after the subcommand name.\n * @returns The key to import, or undefined when this is a fresh account.\n * @throws If the named key file exists but is empty.\n */\nfunction resolveImportKey(argv: string[]): string | undefined {\n const keyFile = argValue(argv, '--private-key-file') ?? process.env.SHIELD_SWAP_PRIVATE_KEY_FILE\n if (keyFile) {\n const key = readFileSync(keyFile, 'utf8').trim()\n if (!key) throw new Error(`private key file ${keyFile} is empty`)\n return key\n }\n return process.env.SHIELD_SWAP_PRIVATE_KEY\n}\n\n/**\n * Bootstraps the account, one idempotent step at a time.\n *\n * @param argv Arguments after the subcommand name.\n */\nasync function setup(argv: string[]): Promise<void> {\n const inviteCode = argValue(argv, '--invite-code') ?? process.env.SHIELD_SWAP_INVITE_CODE\n const consumerId = argValue(argv, '--consumer-id') ?? process.env.ALEO_CONSUMER_ID\n const apiKey = argValue(argv, '--api-key') ?? process.env.ALEO_DPS_API_KEY\n // Flag-only on purpose: SHIELD_SWAP_API_URL stays an ephemeral per-run\n // override (see loadSession); only an explicit --api-url pins the\n // deployment and resets deployment-scoped state.\n const network = resolveNetwork(argValue(argv, '--network'))\n const apiUrl = argValue(argv, '--api-url')?.replace(/\\/$/, '')\n const credentialStore = fileCredentialStore(credentialsPath(network))\n const allowGenerate = argv.includes('--new')\n const importKey = resolveImportKey(argv)\n\n // ── 1 + 2: key material and Provable API credentials ────────────────\n let state = loadState(network)\n console.log(`network: ${network} · state: ${stateDir(network)}`)\n\n // Pin the DEX API deployment. The access grant, API token, and airdrop\n // job all live in one deployment's database — switching deployments\n // invalidates them, so clear them and let the later steps re-derive.\n // Account key material and Provable API credentials are NOT touched\n // (chain- and prover-scoped, not DEX-scoped).\n if (apiUrl && apiUrl !== (state.apiUrl ?? DEFAULT_API_URL)) {\n state.apiUrl = apiUrl\n state.dexApiToken = undefined\n state.accessRedeemed = undefined\n state.airdropJobId = undefined\n saveState(state)\n console.log(`✓ DEX API pinned to ${state.apiUrl} (deployment-scoped state reset)`)\n }\n try {\n state = await ensureKeyMaterial(state, { importKey, allowGenerate })\n } catch (err) {\n if (err instanceof NeedsConfigDecisionError) {\n console.error(\n '\\nNEEDS_CONFIG_DECISION: no shield-swap account is configured here. Ask the user ' +\n 'whether they already have one before creating anything. NEVER ask them to paste ' +\n 'a private key into the conversation:\\n' +\n ' - existing account → the user saves their key to a file themselves, then re-run\\n' +\n ' with --private-key-file <path> (or they export SHIELD_SWAP_PRIVATE_KEY in\\n' +\n ' their own shell). Add --consumer-id/--api-key if they have Provable API\\n' +\n ' credentials.\\n' +\n ' - brand new → re-run with --new\\n',\n )\n process.exit(2)\n }\n throw err\n }\n console.log(`✓ account: ${state.address}`)\n\n // Supplied credentials win over registering a new consumer, so a returning\n // user keeps theirs. Absent both, the client registers one below. Awaited\n // because ProvableCredentialStore permits async: this store happens to be\n // synchronous, but reading a promise as a value would silently skip the seed\n // and leave the write unobserved.\n if (consumerId && apiKey && !(await credentialStore.load())) {\n await credentialStore.save({ consumerId, apiKey })\n }\n\n // Credentials used to live in the state file. Move them rather than letting\n // the client register a replacement: an API key is issued once and cannot be\n // reissued, so a fresh consumer would abandon the old one.\n const legacy = (state as { provableApi?: { consumerId: string; apiKey: string } }).provableApi\n if (legacy && !(await credentialStore.load())) {\n await credentialStore.save(legacy)\n delete (state as { provableApi?: unknown }).provableApi\n saveState(state)\n console.log(`✓ moved Provable API credentials to ${credentialsPath(network)}`)\n }\n\n // ── 3: wire the client and authenticate with the DEX API ────────────\n // The network must be passed explicitly: loadSession defaults to testnet, so\n // omitting it would authenticate, register, and redeem against testnet while\n // writing the results into the network-scoped state this script resolved.\n const { client, account } = await loadSession({ network })\n console.log('✓ DEX API session established (challenge/verify)')\n\n // Front-loaded on purpose: registration would otherwise happen on the first\n // prove or scan, and a newly issued API key is only reportable here.\n const provable = await client.authenticateProvableApi()\n console.log(\n `✓ Provable API consumer: ${provable.credentials.consumerId}` +\n (provable.registered ? ` (registered, saved to ${credentialsPath(network)})` : ''),\n )\n\n // ── 4: invite-code access gate ───────────────────────────────────────\n // Distributed invite codes are referral codes; /referral/redeem is the\n // single endpoint that unlocks an account.\n const status = await client.api.getReferralStatus()\n if (!status.has_access) {\n if (!inviteCode) {\n console.error(\n '\\nNEEDS_INVITE_CODE: this account has not redeemed an invite code, so ' +\n 'the DEX API is locked. Ask the user for their invite code, then re-run:\\n' +\n ' shield-swap setup --invite-code <code>\\n',\n )\n process.exit(2)\n }\n try {\n await client.api.redeemReferralCode(inviteCode)\n } catch (err) {\n if (!(err instanceof ApiError) || err.status !== 400) throw err\n console.error(\n `\\nINVALID_INVITE_CODE: the server rejected \"${inviteCode}\". ` +\n 'Ask the user for a valid, unused code and re-run.\\n',\n )\n process.exit(2)\n }\n state.accessRedeemed = true\n saveState(state)\n console.log('✓ code redeemed — access unlocked')\n } else {\n state.accessRedeemed = true\n saveState(state)\n console.log('✓ access already granted')\n }\n\n // ── 5: long-lived API token for later sessions ───────────────────────\n if (!state.dexApiToken) {\n const created = await client.api.createApiToken({ name: `ss-agent-${account.address.slice(5, 17)}` })\n state.dexApiToken = created.token\n saveState(state)\n console.log(`✓ minted DEX API token (${created.token_prefix}…, stored in state file)`)\n } else {\n console.log('✓ DEX API token already on file')\n }\n\n // ── 6: airdrop when the account holds nothing ────────────────────────\n // The faucet delivers PRIVATE records, so the check must scan the private\n // side; a fresh account's public balances stay zero even after funding.\n const funded = async () => {\n const balances = await client.getBalances()\n return Object.values(balances).some((b) => b.total > 0n)\n }\n if (await funded()) {\n console.log('✓ account already funded')\n } else if (network === 'mainnet') {\n // There is no mainnet faucet, and quietly skipping would leave the account\n // configured but unable to trade — a failure a user would only discover\n // when a swap could not select a record.\n throw new Error(\n `account ${account.address} holds no tokens on mainnet, and there is no faucet to draw from. ` +\n 'Fund it from an exchange or another wallet, then re-run this script to verify.',\n )\n } else {\n // Request the faucet at most once per account: the job id persists in\n // the state file, so a re-run resumes polling instead of double-drawing.\n if (!state.airdropJobId) {\n const job = await client.api.airdrop(account.address)\n state.airdropJobId = job.job_id\n saveState(state)\n console.log(`… airdrop started (job ${job.job_id})`)\n } else {\n console.log(`… resuming airdrop job ${state.airdropJobId}`)\n }\n\n // Two phases: the faucet job finishing (fast), then the record service\n // indexing the new private records (slower, asynchronous).\n let job: Awaited<ReturnType<typeof client.api.getAirdropStatus>> | null = null\n const jobDone = await pollUntil(async () => {\n job = await client.api.getAirdropStatus(state.airdropJobId!).catch(() => null)\n return job?.status === 'complete'\n }, 24, 5_000)\n if (!jobDone) {\n console.error(\n `\\nAIRDROP_PENDING: faucet job ${state.airdropJobId} has not completed yet ` +\n `(last status: ${job ? (job as { status?: string }).status : 'unknown'}). ` +\n 'Re-run `shield-swap setup` in a few minutes — it resumes this job, it does not double-request.\\n',\n )\n process.exit(3)\n }\n const rejected = (job!.results ?? []).filter((r: { status?: string }) => r.status !== 'accepted')\n if (rejected.length > 0) {\n // The job finished but some transfers failed — surface it and allow a\n // fresh request next run instead of resuming a dead job forever.\n state.airdropJobId = undefined\n saveState(state)\n console.error(\n `\\nAIRDROP_FAILED: faucet job finished with rejected transfers: ` +\n `${rejected.map((r: { symbol?: string; status?: string }) => `${r.symbol}:${r.status}`).join(', ')}. ` +\n 'Re-run `shield-swap setup` to request a fresh airdrop.\\n',\n )\n process.exit(3)\n }\n console.log('… faucet job complete — waiting for the records to become scannable')\n\n const landed = await pollUntil(funded, 36, 10_000)\n if (!landed) {\n console.error(\n '\\nAIRDROP_PENDING: the faucet finished but the records are not scannable ' +\n 'yet (the record service indexes asynchronously). Re-run `shield-swap setup` in a ' +\n 'few minutes — it resumes this job, it does not double-request.\\n',\n )\n process.exit(3)\n }\n console.log('✓ airdrop landed')\n }\n\n // ── report ────────────────────────────────────────────────────────────\n const balances = await client.getBalances()\n console.log(`\\nAccount ${account.address} is ready on ${network}:`)\n for (const entry of Object.values(balances)) {\n if (entry.total > 0n) {\n const priv = formatAmount(entry.private, entry.decimals, entry.symbol)\n const pub = formatAmount(entry.public, entry.decimals, entry.symbol)\n console.log(` ${entry.symbol}: ${priv} private, ${pub} public`)\n }\n }\n console.log(\n '\\nASK_NEXT_ACTION: setup is complete — ask the user what to do next with ONE ' +\n 'user-selectable prompt (the harness\\'s selection UI if it has one, a numbered ' +\n 'list otherwise) offering ALL SEVEN options below, in this order. Free-form ' +\n 'input stays available as the escape hatch (\"Other\"); map whatever the user ' +\n 'types onto the runbooks before improvising against the SDK. Do not pick for ' +\n 'them. Frame the setting first: Shield Swap is a private exchange on Aleo — ' +\n 'what is traded, and by whom, stays hidden on the public chain. ' +\n (network === 'mainnet'\n ? 'This account is on MAINNET: every trade below moves real funds, and there ' +\n 'is no faucet to recover from a mistake. Say so before the user picks.'\n : 'This account is on the test network, so trading uses test tokens.') +\n '\\n\\n' +\n '1. Develop on Shield Swap (developing.md). For a user building their own\\n' +\n ' dApp, trading bot, or server/agent integration rather than trading\\n' +\n ' here. If chosen, FIRST ask what they are building, then follow\\n' +\n ' developing.md — it picks the packages by where their keys live and\\n' +\n ' maps to the docs, examples, and integration caveats.\\n' +\n '2. Follow their own playbook. Ask whether they have instructions of\\n' +\n ' their own — a markdown strategy file, notes, or a memory store such\\n' +\n ' as an Obsidian vault. Their document decides WHAT to do; the runbooks\\n' +\n ' here describe HOW each step works.\\n' +\n '3. Swap tokens (swapping.md). Trade one token for another. It settles\\n' +\n ' in two steps — placing the trade, then collecting what was bought —\\n' +\n ' and both happen in one go. The natural first move for a trader.\\n' +\n '4. Several swaps at once (swapping.md, concurrency recipe). Place a\\n' +\n ' handful of trades in parallel and watch them all land — the busiest\\n' +\n ' way to exercise the exchange. First show the user which trades are\\n' +\n ' possible right now and ask how many (and which) they want; collect\\n' +\n ' each one as it lands.\\n' +\n '5. Open a liquidity position (liquidity.md). Instead of trading, become\\n' +\n ' the market: deposit a pair of tokens so other people can trade against\\n' +\n ' them. The user picks the price range their deposit works in, and while\\n' +\n ' the market price sits inside that range they earn a small cut of every\\n' +\n ' trade that passes through.\\n' +\n '6. Add or remove liquidity (liquidity.md). Top up a position, or take\\n' +\n ' some of it back out — whatever comes out becomes earnings to collect.\\n' +\n '7. Collect earnings (collecting.md). Sweep up everything the account is\\n' +\n ' owed — tokens bought in earlier swaps and the fees its liquidity\\n' +\n ' earned — into the wallet. Good to run after any trading session.\\n',\n )\n}\n\n\n/**\n * Runs the `setup` subcommand.\n *\n * Failures are reported as `SETUP_FAILED: <message>` with exit 1, which\n * startup.md documents as the signal to read the message and retry once\n * before digging — the step-specific exits (`NEEDS_CONFIG_DECISION`,\n * `NEEDS_INVITE_CODE`) leave from inside {@link setup} with their own codes.\n *\n * @param argv Arguments after the subcommand name, as the dispatcher supplies them.\n */\nexport async function main(argv: string[]): Promise<void> {\n // Checked before anything else: setup's first act is to read and migrate the\n // state file, so a --help that fell through would bootstrap the account it\n // was only asked to describe.\n if (argv.includes('--help') || argv.includes('-h')) {\n console.log(help(USAGE))\n return\n }\n try {\n await setup(argv)\n } catch (error) {\n console.error('\\nSETUP_FAILED:', error instanceof Error ? error.message : error)\n process.exit(1)\n }\n}\n"],"mappings":"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","names":["balances","job"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@provablehq/shield-swap-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Command line trader for the Shield Swap AMM DEX on Aleo.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"access": "public"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@provablehq/
|
|
33
|
-
"@provablehq/
|
|
32
|
+
"@provablehq/shield-swap-sdk": "^0.10.0",
|
|
33
|
+
"@provablehq/veil-aleo-sdk": "^0.10.0"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "tsup",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/setup.ts"],"sourcesContent":["/**\n * Shield Swap account bootstrap — the startup gauntlet, idempotent.\n *\n * Run it as many times as needed; every step is check-then-act, so a failed\n * or interrupted run resumes where it stopped:\n *\n * 1. Key material — reuse the stored account, import the user's\n * existing key, or (only with --new) generate one\n * 2. DEX authentication — challenge/verify session with the account\n * 3. Provable API — reuse/import credentials, else self-register a\n * consumer for proving + scanning\n * 4. Invite code — check access; redeem a code when one is provided\n * 5. API token — mint a long-lived ss_ token for later sessions\n * 6. Airdrop — request testnet tokens when holdings are empty,\n * then poll until the PRIVATE records land\n *\n * Usage:\n * shield-swap setup --new # brand-new account\n * shield-swap setup --network mainnet --private-key-file <path> # mainnet\n * shield-swap setup --private-key-file <path> # returning user (key in a file)\n * shield-swap setup --invite-code CODE # when access is locked\n * shield-swap setup --api-url <origin> # pin a DEX API deployment\n *\n * A private key is NEVER pasted into a conversation or command history: a\n * returning user either writes it to a file and passes the path, or exports\n * SHIELD_SWAP_PRIVATE_KEY (or SHIELD_SWAP_PRIVATE_KEY_FILE) in their own\n * shell. Other environment fallbacks: ALEO_CONSUMER_ID + ALEO_DPS_API_KEY,\n * SHIELD_SWAP_INVITE_CODE, SHIELD_SWAP_API_URL.\n *\n * Exit codes: 0 ready · 2 needs input from the user (message says what) ·\n * 3 airdrop still pending · 1 anything else.\n *\n * State lands in ./.shield-swap/<network>/state.json (private key +\n * credentials — gitignore it, treat it like a wallet file). Nothing is shared\n * between networks, including the blinded identity store, whose reservations\n * are only meaningful against the chain they were checked on.\n *\n * Mainnet moves real value, so it is never the default: pass\n * `--network mainnet` explicitly. The airdrop step is testnet-only and refuses\n * to run on mainnet rather than pretending a faucet exists.\n */\nimport { readFileSync } from 'node:fs'\nimport { help } from '../color.js'\nimport { ApiError, DEFAULT_API_URL } from '@provablehq/shield-swap-sdk'\nimport { fileCredentialStore } from '@provablehq/veil-aleo-sdk/node'\nimport {\n loadState,\n saveState,\n ensureKeyMaterial,\n credentialsPath,\n resolveNetwork,\n stateDir,\n NeedsConfigDecisionError,\n loadSession,\n formatAmount,\n pollUntil,\n} from '../session.js'\n\nconst USAGE = `shield-swap setup — bootstrap an account and get it funded\n\n --new generate a brand-new account\n --private-key-file <path> import an existing key, read from this file\n --consumer-id <id> Provable API consumer id (else self-registers)\n --api-key <key> Provable API key\n --invite-code <code> redeem an invite code when access is locked\n --api-url <origin> pin a DEX API deployment\n --network <testnet|mainnet> default testnet\n\nEvery step is check-then-act, so re-running resumes where a failed run stopped.\n\nA private key is NEVER pasted into a conversation or command history: write it\nto a file and pass --private-key-file, or export SHIELD_SWAP_PRIVATE_KEY in your\nown shell. Other environment fallbacks: SHIELD_SWAP_PRIVATE_KEY_FILE,\nALEO_CONSUMER_ID, ALEO_DPS_API_KEY, SHIELD_SWAP_INVITE_CODE, SHIELD_SWAP_API_URL.\n\nExit codes: 0 ready · 2 needs input from the user · 3 airdrop still pending ·\n1 anything else.`\n\n/**\n * Reads a `--flag value` pair out of the arguments.\n *\n * `setup` keeps its own reader rather than using the shared `flags()`: every\n * option is an optional string with an environment-variable fallback, and none\n * of them gate a transaction, so the strict unknown-flag rejection that\n * protects the spending commands would only get in the way here.\n *\n * @param argv Arguments after the subcommand name.\n * @param flag The flag to look for, leading dashes included.\n * @returns The argument that follows the flag, or undefined when it is absent.\n */\nfunction argValue(argv: string[], flag: string): string | undefined {\n const i = argv.indexOf(flag)\n return i >= 0 ? argv[i + 1] : undefined\n}\n\n/**\n * Resolves the private key to import, when the caller is restoring an account.\n *\n * The key itself never travels through a conversation or the command line: it\n * is read from a file the user wrote, or from an env var the user exported in\n * their own shell.\n *\n * @param argv Arguments after the subcommand name.\n * @returns The key to import, or undefined when this is a fresh account.\n * @throws If the named key file exists but is empty.\n */\nfunction resolveImportKey(argv: string[]): string | undefined {\n const keyFile = argValue(argv, '--private-key-file') ?? process.env.SHIELD_SWAP_PRIVATE_KEY_FILE\n if (keyFile) {\n const key = readFileSync(keyFile, 'utf8').trim()\n if (!key) throw new Error(`private key file ${keyFile} is empty`)\n return key\n }\n return process.env.SHIELD_SWAP_PRIVATE_KEY\n}\n\n/**\n * Bootstraps the account, one idempotent step at a time.\n *\n * @param argv Arguments after the subcommand name.\n */\nasync function setup(argv: string[]): Promise<void> {\n const inviteCode = argValue(argv, '--invite-code') ?? process.env.SHIELD_SWAP_INVITE_CODE\n const consumerId = argValue(argv, '--consumer-id') ?? process.env.ALEO_CONSUMER_ID\n const apiKey = argValue(argv, '--api-key') ?? process.env.ALEO_DPS_API_KEY\n // Flag-only on purpose: SHIELD_SWAP_API_URL stays an ephemeral per-run\n // override (see loadSession); only an explicit --api-url pins the\n // deployment and resets deployment-scoped state.\n const network = resolveNetwork(argValue(argv, '--network'))\n const apiUrl = argValue(argv, '--api-url')?.replace(/\\/$/, '')\n const credentialStore = fileCredentialStore(credentialsPath(network))\n const allowGenerate = argv.includes('--new')\n const importKey = resolveImportKey(argv)\n\n // ── 1 + 2: key material and Provable API credentials ────────────────\n let state = loadState(network)\n console.log(`network: ${network} · state: ${stateDir(network)}`)\n\n // Pin the DEX API deployment. The access grant, API token, and airdrop\n // job all live in one deployment's database — switching deployments\n // invalidates them, so clear them and let the later steps re-derive.\n // Account key material and Provable API credentials are NOT touched\n // (chain- and prover-scoped, not DEX-scoped).\n if (apiUrl && apiUrl !== (state.apiUrl ?? DEFAULT_API_URL)) {\n state.apiUrl = apiUrl\n state.dexApiToken = undefined\n state.accessRedeemed = undefined\n state.airdropJobId = undefined\n saveState(state)\n console.log(`✓ DEX API pinned to ${state.apiUrl} (deployment-scoped state reset)`)\n }\n try {\n state = await ensureKeyMaterial(state, { importKey, allowGenerate })\n } catch (err) {\n if (err instanceof NeedsConfigDecisionError) {\n console.error(\n '\\nNEEDS_CONFIG_DECISION: no shield-swap account is configured here. Ask the user ' +\n 'whether they already have one before creating anything. NEVER ask them to paste ' +\n 'a private key into the conversation:\\n' +\n ' - existing account → the user saves their key to a file themselves, then re-run\\n' +\n ' with --private-key-file <path> (or they export SHIELD_SWAP_PRIVATE_KEY in\\n' +\n ' their own shell). Add --consumer-id/--api-key if they have Provable API\\n' +\n ' credentials.\\n' +\n ' - brand new → re-run with --new\\n',\n )\n process.exit(2)\n }\n throw err\n }\n console.log(`✓ account: ${state.address}`)\n\n // Supplied credentials win over registering a new consumer, so a returning\n // user keeps theirs. Absent both, the client registers one below. Awaited\n // because ProvableCredentialStore permits async: this store happens to be\n // synchronous, but reading a promise as a value would silently skip the seed\n // and leave the write unobserved.\n if (consumerId && apiKey && !(await credentialStore.load())) {\n await credentialStore.save({ consumerId, apiKey })\n }\n\n // Credentials used to live in the state file. Move them rather than letting\n // the client register a replacement: an API key is issued once and cannot be\n // reissued, so a fresh consumer would abandon the old one.\n const legacy = (state as { provableApi?: { consumerId: string; apiKey: string } }).provableApi\n if (legacy && !(await credentialStore.load())) {\n await credentialStore.save(legacy)\n delete (state as { provableApi?: unknown }).provableApi\n saveState(state)\n console.log(`✓ moved Provable API credentials to ${credentialsPath(network)}`)\n }\n\n // ── 3: wire the client and authenticate with the DEX API ────────────\n // The network must be passed explicitly: loadSession defaults to testnet, so\n // omitting it would authenticate, register, and redeem against testnet while\n // writing the results into the network-scoped state this script resolved.\n const { client, account } = await loadSession({ network })\n console.log('✓ DEX API session established (challenge/verify)')\n\n // Front-loaded on purpose: registration would otherwise happen on the first\n // prove or scan, and a newly issued API key is only reportable here.\n const provable = await client.authenticateProvableApi()\n console.log(\n `✓ Provable API consumer: ${provable.credentials.consumerId}` +\n (provable.registered ? ` (registered, saved to ${credentialsPath(network)})` : ''),\n )\n\n // ── 4: invite-code access gate ───────────────────────────────────────\n const status = await client.api.getAccessStatus()\n if (!status.has_access) {\n if (!inviteCode) {\n console.error(\n '\\nNEEDS_INVITE_CODE: this account has not redeemed an invite code, so ' +\n 'the DEX API is locked. Ask the user for their invite code, then re-run:\\n' +\n ' shield-swap setup --invite-code <code>\\n',\n )\n process.exit(2)\n }\n // Distributed codes come in two kinds with one purpose: access codes\n // (/access/redeem) and referral codes (/referral/redeem) both unlock\n // the account. Try both before rejecting the code.\n let redeemed = false\n for (const attempt of [\n () => client.api.redeemAccessCode(inviteCode),\n () => client.api.redeemReferralCode(inviteCode),\n ]) {\n try {\n await attempt()\n redeemed = true\n break\n } catch (err) {\n if (err instanceof ApiError && err.status === 400) continue\n throw err\n }\n }\n if (!redeemed) {\n console.error(\n `\\nINVALID_INVITE_CODE: the server rejected \"${inviteCode}\" as both an access ` +\n 'code and a referral code. Ask the user for a valid, unused code and re-run.\\n',\n )\n process.exit(2)\n }\n state.accessRedeemed = true\n saveState(state)\n console.log('✓ code redeemed — access unlocked')\n } else {\n state.accessRedeemed = true\n saveState(state)\n console.log('✓ access already granted')\n }\n\n // ── 5: long-lived API token for later sessions ───────────────────────\n if (!state.dexApiToken) {\n const created = await client.api.createApiToken({ name: `ss-agent-${account.address.slice(5, 17)}` })\n state.dexApiToken = created.token\n saveState(state)\n console.log(`✓ minted DEX API token (${created.token_prefix}…, stored in state file)`)\n } else {\n console.log('✓ DEX API token already on file')\n }\n\n // ── 6: airdrop when the account holds nothing ────────────────────────\n // The faucet delivers PRIVATE records, so the check must scan the private\n // side; a fresh account's public balances stay zero even after funding.\n const funded = async () => {\n const balances = await client.getBalances()\n return Object.values(balances).some((b) => b.total > 0n)\n }\n if (await funded()) {\n console.log('✓ account already funded')\n } else if (network === 'mainnet') {\n // There is no mainnet faucet, and quietly skipping would leave the account\n // configured but unable to trade — a failure a user would only discover\n // when a swap could not select a record.\n throw new Error(\n `account ${account.address} holds no tokens on mainnet, and there is no faucet to draw from. ` +\n 'Fund it from an exchange or another wallet, then re-run this script to verify.',\n )\n } else {\n // Request the faucet at most once per account: the job id persists in\n // the state file, so a re-run resumes polling instead of double-drawing.\n if (!state.airdropJobId) {\n const job = await client.api.airdrop(account.address)\n state.airdropJobId = job.job_id\n saveState(state)\n console.log(`… airdrop started (job ${job.job_id})`)\n } else {\n console.log(`… resuming airdrop job ${state.airdropJobId}`)\n }\n\n // Two phases: the faucet job finishing (fast), then the record service\n // indexing the new private records (slower, asynchronous).\n let job: Awaited<ReturnType<typeof client.api.getAirdropStatus>> | null = null\n const jobDone = await pollUntil(async () => {\n job = await client.api.getAirdropStatus(state.airdropJobId!).catch(() => null)\n return job?.status === 'complete'\n }, 24, 5_000)\n if (!jobDone) {\n console.error(\n `\\nAIRDROP_PENDING: faucet job ${state.airdropJobId} has not completed yet ` +\n `(last status: ${job ? (job as { status?: string }).status : 'unknown'}). ` +\n 'Re-run `shield-swap setup` in a few minutes — it resumes this job, it does not double-request.\\n',\n )\n process.exit(3)\n }\n const rejected = (job!.results ?? []).filter((r: { status?: string }) => r.status !== 'accepted')\n if (rejected.length > 0) {\n // The job finished but some transfers failed — surface it and allow a\n // fresh request next run instead of resuming a dead job forever.\n state.airdropJobId = undefined\n saveState(state)\n console.error(\n `\\nAIRDROP_FAILED: faucet job finished with rejected transfers: ` +\n `${rejected.map((r: { symbol?: string; status?: string }) => `${r.symbol}:${r.status}`).join(', ')}. ` +\n 'Re-run `shield-swap setup` to request a fresh airdrop.\\n',\n )\n process.exit(3)\n }\n console.log('… faucet job complete — waiting for the records to become scannable')\n\n const landed = await pollUntil(funded, 36, 10_000)\n if (!landed) {\n console.error(\n '\\nAIRDROP_PENDING: the faucet finished but the records are not scannable ' +\n 'yet (the record service indexes asynchronously). Re-run `shield-swap setup` in a ' +\n 'few minutes — it resumes this job, it does not double-request.\\n',\n )\n process.exit(3)\n }\n console.log('✓ airdrop landed')\n }\n\n // ── report ────────────────────────────────────────────────────────────\n const balances = await client.getBalances()\n console.log(`\\nAccount ${account.address} is ready on ${network}:`)\n for (const entry of Object.values(balances)) {\n if (entry.total > 0n) {\n const priv = formatAmount(entry.private, entry.decimals, entry.symbol)\n const pub = formatAmount(entry.public, entry.decimals, entry.symbol)\n console.log(` ${entry.symbol}: ${priv} private, ${pub} public`)\n }\n }\n console.log(\n '\\nASK_NEXT_ACTION: setup is complete — ask the user what to do next with ONE ' +\n 'user-selectable prompt (the harness\\'s selection UI if it has one, a numbered ' +\n 'list otherwise) offering ALL SEVEN options below, in this order. Free-form ' +\n 'input stays available as the escape hatch (\"Other\"); map whatever the user ' +\n 'types onto the runbooks before improvising against the SDK. Do not pick for ' +\n 'them. Frame the setting first: Shield Swap is a private exchange on Aleo — ' +\n 'what is traded, and by whom, stays hidden on the public chain. ' +\n (network === 'mainnet'\n ? 'This account is on MAINNET: every trade below moves real funds, and there ' +\n 'is no faucet to recover from a mistake. Say so before the user picks.'\n : 'This account is on the test network, so trading uses test tokens.') +\n '\\n\\n' +\n '1. Develop on Shield Swap (developing.md). For a user building their own\\n' +\n ' dApp, trading bot, or server/agent integration rather than trading\\n' +\n ' here. If chosen, FIRST ask what they are building, then follow\\n' +\n ' developing.md — it picks the packages by where their keys live and\\n' +\n ' maps to the docs, examples, and integration caveats.\\n' +\n '2. Follow their own playbook. Ask whether they have instructions of\\n' +\n ' their own — a markdown strategy file, notes, or a memory store such\\n' +\n ' as an Obsidian vault. Their document decides WHAT to do; the runbooks\\n' +\n ' here describe HOW each step works.\\n' +\n '3. Swap tokens (swapping.md). Trade one token for another. It settles\\n' +\n ' in two steps — placing the trade, then collecting what was bought —\\n' +\n ' and both happen in one go. The natural first move for a trader.\\n' +\n '4. Several swaps at once (swapping.md, concurrency recipe). Place a\\n' +\n ' handful of trades in parallel and watch them all land — the busiest\\n' +\n ' way to exercise the exchange. First show the user which trades are\\n' +\n ' possible right now and ask how many (and which) they want; collect\\n' +\n ' each one as it lands.\\n' +\n '5. Open a liquidity position (liquidity.md). Instead of trading, become\\n' +\n ' the market: deposit a pair of tokens so other people can trade against\\n' +\n ' them. The user picks the price range their deposit works in, and while\\n' +\n ' the market price sits inside that range they earn a small cut of every\\n' +\n ' trade that passes through.\\n' +\n '6. Add or remove liquidity (liquidity.md). Top up a position, or take\\n' +\n ' some of it back out — whatever comes out becomes earnings to collect.\\n' +\n '7. Collect earnings (collecting.md). Sweep up everything the account is\\n' +\n ' owed — tokens bought in earlier swaps and the fees its liquidity\\n' +\n ' earned — into the wallet. Good to run after any trading session.\\n',\n )\n}\n\n\n/**\n * Runs the `setup` subcommand.\n *\n * Failures are reported as `SETUP_FAILED: <message>` with exit 1, which\n * startup.md documents as the signal to read the message and retry once\n * before digging — the step-specific exits (`NEEDS_CONFIG_DECISION`,\n * `NEEDS_INVITE_CODE`) leave from inside {@link setup} with their own codes.\n *\n * @param argv Arguments after the subcommand name, as the dispatcher supplies them.\n */\nexport async function main(argv: string[]): Promise<void> {\n // Checked before anything else: setup's first act is to read and migrate the\n // state file, so a --help that fell through would bootstrap the account it\n // was only asked to describe.\n if (argv.includes('--help') || argv.includes('-h')) {\n console.log(help(USAGE))\n return\n }\n try {\n await setup(argv)\n } catch (error) {\n console.error('\\nSETUP_FAILED:', error instanceof Error ? error.message : error)\n process.exit(1)\n }\n}\n"],"mappings":"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","names":["balances","job"]}
|