Skip to content

Commit 4f2db93

Browse files
committed
feat(cli): add derive-missing command for existing wallets
- Add 'derive-missing' command to coinpay-wallet CLI for bots to derive addresses for newly supported chains on existing wallets - Update WebWalletDocs with derive-missing command documentation - Add dedicated 'Upgrading Existing Wallets' section with SDK + CLI examples - Update environment variable docs to note COINPAY_MNEMONIC requirement This allows bots with existing wallets to easily add support for new coins when they're added to the platform without creating a new wallet.
1 parent 38c1704 commit 4f2db93

File tree

2 files changed

+121
-1
lines changed

2 files changed

+121
-1
lines changed

scripts/coinpay-wallet.mjs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
* pnpm coinpay-wallet send <wallet-id> --from <addr> --to <addr> --chain <chain> --amount <amount>
1313
* pnpm coinpay-wallet address <wallet-id> [--chain <chain>]
1414
* pnpm coinpay-wallet history <wallet-id> [--chain <chain>] [--limit <n>]
15+
* pnpm coinpay-wallet derive-missing <wallet-id> [--chains BTC,ETH,...]
1516
*
1617
* Configuration:
1718
* COINPAY_API_URL - API base URL (default: http://localhost:8080)
1819
* COINPAY_AUTH_TOKEN - JWT auth token (for read-only operations)
20+
* COINPAY_MNEMONIC - Mnemonic phrase (required for send and derive-missing)
1921
*
2022
* Or use a config file at ~/.coinpayrc.json:
2123
* { "apiUrl": "https://coinpayportal.com", "authToken": "..." }
@@ -333,6 +335,69 @@ async function cmdSync(positional, flags, config) {
333335
console.log(` Synced: ${JSON.stringify(result)}\n`);
334336
}
335337

338+
async function cmdDeriveMissing(positional, flags, config) {
339+
const walletId = positional[0];
340+
if (!walletId) {
341+
error('Usage: coinpay-wallet derive-missing <wallet-id> [--chains BTC,ETH,...]');
342+
}
343+
344+
// Require mnemonic for deriving addresses
345+
const mnemonic = process.env.COINPAY_MNEMONIC;
346+
if (!mnemonic) {
347+
error('COINPAY_MNEMONIC is required to derive new addresses.');
348+
}
349+
350+
const { Wallet } = await import('../src/lib/wallet-sdk/index.ts');
351+
352+
// Parse target chains if provided
353+
const targetChains = flags.chains
354+
? flags.chains.split(',').map((c) => c.trim().toUpperCase())
355+
: undefined; // undefined means use default supported chains
356+
357+
console.log(`\nChecking for missing chains on wallet ${walletId}...\n`);
358+
359+
// First, create wallet from seed to get the wallet instance
360+
const wallet = await Wallet.fromSeed(mnemonic, {
361+
baseUrl: config.apiUrl,
362+
chains: ['BTC', 'BCH', 'ETH', 'POL', 'SOL'], // minimal chains for auth
363+
});
364+
365+
if (wallet.walletId !== walletId) {
366+
error(`Mnemonic wallet ID (${wallet.walletId}) doesn't match requested ID (${walletId})`);
367+
}
368+
369+
// Check what's missing
370+
const missingChains = await wallet.getMissingChains(targetChains);
371+
372+
if (missingChains.length === 0) {
373+
console.log(' ✓ No missing chains! All addresses are already derived.\n');
374+
return;
375+
}
376+
377+
console.log(` Missing chains: ${missingChains.join(', ')}\n`);
378+
console.log(' Deriving addresses...\n');
379+
380+
// Derive the missing chains
381+
const results = await wallet.deriveMissingChains(targetChains);
382+
383+
if (results.length === 0) {
384+
console.log(' No new addresses derived.\n');
385+
return;
386+
}
387+
388+
console.log(' New addresses derived:\n');
389+
printTable(
390+
results.map((r) => ({
391+
Chain: r.chain,
392+
Address: r.address.length > 30
393+
? r.address.slice(0, 15) + '...' + r.address.slice(-10)
394+
: r.address,
395+
Index: r.derivationIndex,
396+
}))
397+
);
398+
console.log();
399+
}
400+
336401
// ── Help ──
337402

338403
function showHelp() {
@@ -369,6 +434,9 @@ Commands:
369434
sync <wallet-id> Sync on-chain transaction history
370435
--chain <chain> Sync specific chain only
371436
437+
derive-missing <wallet-id> Derive addresses for newly supported chains
438+
--chains <list> Target chains (default: BTC,BCH,ETH,POL,SOL,USDC_*)
439+
372440
Environment Variables:
373441
COINPAY_API_URL API base URL (default: http://localhost:8080)
374442
COINPAY_AUTH_TOKEN JWT auth token for read-only operations
@@ -413,6 +481,9 @@ async function main() {
413481
case 'sync':
414482
await cmdSync(positional, flags, config);
415483
break;
484+
case 'derive-missing':
485+
await cmdDeriveMissing(positional, flags, config);
486+
break;
416487
default:
417488
error(`Unknown command: ${command}\nRun 'coinpay-wallet help' for usage.`);
418489
}

src/components/docs/WebWalletDocs.tsx

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ pnpm coinpay-wallet create --chains BTC,ETH,SOL`}
7878
{ cmd: 'send', args: '<wallet-id> --from <addr> --to <addr> --chain ETH --amount 0.5', desc: 'Send a transaction' },
7979
{ cmd: 'history', args: '<wallet-id> --chain BTC --limit 20', desc: 'View transaction history' },
8080
{ cmd: 'sync', args: '<wallet-id> --chain SOL', desc: 'Sync on-chain deposits into history' },
81+
{ cmd: 'derive-missing', args: '<wallet-id> [--chains BTC,ETH,...]', desc: 'Derive addresses for newly supported chains' },
8182
].map((c) => (
8283
<div key={c.cmd} className="flex items-start gap-4 p-3 rounded-lg bg-slate-800/50">
8384
<code className="text-purple-400 font-mono text-sm whitespace-nowrap shrink-0">coinpay-wallet {c.cmd}</code>
@@ -94,7 +95,7 @@ pnpm coinpay-wallet create --chains BTC,ETH,SOL`}
9495
<div className="space-y-1 text-sm text-gray-300">
9596
<p><code className="text-purple-400">COINPAY_API_URL</code> — API base URL (default: <code>http://localhost:8080</code>)</p>
9697
<p><code className="text-purple-400">COINPAY_AUTH_TOKEN</code> — JWT token for read-only operations</p>
97-
<p><code className="text-purple-400">COINPAY_MNEMONIC</code> — Mnemonic phrase (required for <code>send</code>)</p>
98+
<p><code className="text-purple-400">COINPAY_MNEMONIC</code> — Mnemonic phrase (required for <code>send</code> and <code>derive-missing</code>)</p>
9899
</div>
99100
<p className="text-gray-500 text-xs mt-2">Or use a config file: <code>~/.coinpayrc.json</code></p>
100101
</div>
@@ -161,6 +162,54 @@ const newAddr = await wallet.deriveAddress('BTC');`}
161162
</CodeBlock>
162163
</div>
163164

165+
{/* Upgrading Wallets - Derive Missing Chains */}
166+
<h3 className="text-xl font-semibold text-white mb-4">Upgrading Existing Wallets</h3>
167+
<div className="mb-8 p-4 bg-amber-500/10 border border-amber-500/20 rounded-lg">
168+
<div className="flex items-center gap-2 mb-2">
169+
<span className="text-amber-400 font-semibold">🔄 New Chains Added?</span>
170+
</div>
171+
<p className="text-amber-300 text-sm mb-3">
172+
When CoinPayPortal adds support for new coins, existing wallets won&apos;t have addresses for them.
173+
Use <code className="text-amber-200">deriveMissingChains()</code> to automatically derive addresses for any missing chains.
174+
</p>
175+
<CodeBlock title="Node.js SDK — Derive Missing Chains" language="javascript">
176+
{`import { Wallet } from '@coinpayportal/wallet-sdk';
177+
178+
// Load your existing wallet from seed
179+
const wallet = await Wallet.fromSeed(process.env.WALLET_MNEMONIC, {
180+
baseUrl: 'https://coinpayportal.com',
181+
chains: ['BTC', 'ETH'], // minimal chains for auth
182+
});
183+
184+
// Check which chains are missing
185+
const missing = await wallet.getMissingChains();
186+
console.log('Missing chains:', missing);
187+
// ['BCH', 'POL', 'SOL', 'USDC_ETH', 'USDC_POL', 'USDC_SOL']
188+
189+
// Derive all missing chains at once
190+
const newAddresses = await wallet.deriveMissingChains();
191+
console.log('New addresses:', newAddresses);
192+
// [{ chain: 'BCH', address: 'bitcoincash:q...' }, ...]`}
193+
</CodeBlock>
194+
<div className="mt-4">
195+
<h4 className="text-white font-semibold text-sm mb-2">CLI Command (for bots)</h4>
196+
<CodeBlock title="Terminal" language="bash">
197+
{`# Check and derive missing chains
198+
export COINPAY_MNEMONIC="your twelve word seed phrase here"
199+
pnpm coinpay-wallet derive-missing <wallet-id>
200+
201+
# Output:
202+
# Missing chains: BCH, POL, SOL, USDC_ETH, USDC_POL, USDC_SOL
203+
# Deriving addresses...
204+
# New addresses derived:
205+
# Chain Address Index
206+
# BCH bitcoincash:q... 0
207+
# POL 0x... 0
208+
# ...`}
209+
</CodeBlock>
210+
</div>
211+
</div>
212+
164213
{/* Wallet Creation */}
165214
<ApiEndpoint method="POST" path="/api/web-wallet/create" description="Register a new wallet. Client generates seed phrase and HD keys locally, then sends only public keys. Include initial_addresses for all chains to have addresses ready immediately — no separate derive calls needed.">
166215
<div className="mb-4 p-4 bg-green-500/10 border border-green-500/20 rounded-lg">

0 commit comments

Comments
 (0)