Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions modules/dev-cli/bin/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env node

// Suppress noisy SDK warnings before any imports
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
const originalStderrWrite = process.stderr.write.bind(process.stderr);

const suppressedPatterns = [
/@polkadot\/util.*has multiple versions/,
/Either remove and explicitly install matching versions/,
/The following conflicting packages were found/,
/cjs \d+\.\d+\.\d+\s+node_modules\/@polkadot/,
];

function shouldSuppress(message: string): boolean {
return suppressedPatterns.some(pattern => pattern.test(message));
}

process.stdout.write = function(chunk: any, ...args: any[]): boolean {
const message = chunk.toString();
if (shouldSuppress(message)) {
return true;
}
return originalStdoutWrite(chunk, ...args);
} as any;

process.stderr.write = function(chunk: any, ...args: any[]): boolean {
const message = chunk.toString();
if (shouldSuppress(message)) {
return true;
}
return originalStderrWrite(chunk, ...args);
} as any;

import * as yargs from 'yargs';
import {
balanceCommand,
addressCommand,
sendCommand,
transfersCommand,
walletCommand,
lightningCommand,
} from '../src/commands';

yargs
.scriptName('sdk-dev-cli')
.usage('$0 <command> [options]')
.command(balanceCommand)
.command(addressCommand)
.command(sendCommand)
.command(transfersCommand)
.command(walletCommand)
.command(lightningCommand)
.example('$0 balance', 'Get wallet balance')
.example('$0 address create', 'Create a new address')
.example('$0 send --to <address> --amount <amount> --confirm', 'Send a transaction')
.demandCommand(1, 'You must specify a command')
.help()
.alias('help', 'h')
.version()
.alias('version', 'v')
.wrap(yargs.terminalWidth())
.parse();

5 changes: 5 additions & 0 deletions modules/dev-cli/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env node
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
require("../dist/bin/index.js");

60 changes: 60 additions & 0 deletions modules/dev-cli/config.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"test": {
"tbtc": {
"accessToken": "v2x...",
"walletId": "...",
"walletPassphrase": "...",
"otp": "000000",
"enterpriseId": "..."
},
"gteth": {
"accessToken": "v2x...",
"walletId": "...",
"walletPassphrase": "..."
},
"talgo": {
"accessToken": "v2x...",
"walletId": "...",
"walletPassphrase": "..."
}
},
"staging": {
"tbtcsig": {
"accessToken": "v2x...",
"walletId": "...",
"walletPassphrase": "..."
},
"teos": {
"accessToken": "v2x...",
"walletId": "...",
"walletPassphrase": "..."
},
"tlnbtc": {
"accessToken": "v2x...",
"walletId": "...",
"walletId2": "...",
"walletPassphrase": "..."
}
},
"prod": {
"btc": {
"accessToken": "v2x...",
"walletId": "...",
"walletPassphrase": "..."
},
"eth": {
"accessToken": "v2x...",
"walletId": "...",
"walletPassphrase": "..."
}
},
"custom": {
"tbtc": {
"accessToken": "v2x...",
"walletId": "...",
"customRootUri": "https://...",
"customBitcoinNetwork": "..."
}
}
}

28 changes: 28 additions & 0 deletions modules/dev-cli/env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# BitGo Environment (test, staging, prod, custom)
BITGO_ENV=test

# Coin to test (btc, tbtc, eth, gteth, etc.)
BITGO_COIN=tbtc

# Access Token for the environment
BITGO_ACCESS_TOKEN=v2x...

# Wallet ID to use for operations
BITGO_WALLET_ID=...

# Wallet Passphrase (if needed for signing)
BITGO_WALLET_PASSPHRASE=...

# Optional: For custom environments
# BITGO_CUSTOM_ROOT_URI=https://...
# BITGO_CUSTOM_BITCOIN_NETWORK=...

# Optional: For lightning-specific operations
BITGO_WALLET_ID_2=...

# Optional: OTP code (if needed)
BITGO_OTP=000000

# Optional: Enterprise ID for wallet creation
BITGO_ENTERPRISE_ID=...

25 changes: 25 additions & 0 deletions modules/dev-cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "@bitgo/sdk-dev-cli",
"version": "1.0.0",
"description": "Development CLI tool for quickly testing BitGo SDK changes",
"private": true,
"main": "dist/src/index.js",
"bin": "./dist/bin/index.js",
"scripts": {
"build": "tsc --build --incremental --verbose .",
"clean": "rm -rf dist",
"prepare": "npm run build"
},
"dependencies": {
"bitgo": "workspace:*",
"yargs": "^17.3.1",
"chalk": "4",
"dotenv": "^16.0.3",
"@bitgo/abstract-lightning": "workspace:*"
},
"devDependencies": {
"@types/node": "^22.15.29",
"@types/yargs": "^17.0.19"
}
}

50 changes: 50 additions & 0 deletions modules/dev-cli/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env bash

# Quick setup script for dev-cli

cd "$(dirname "$0")"

echo "BitGo Dev CLI Setup"
echo "==================="
echo ""

# Check if config.json exists
if [ ! -f config.json ]; then
echo "Creating config.json from example..."
cp config.example.json config.json
echo "✓ Created config.json"
echo ""
echo "⚠️ Please edit config.json and fill in your configuration:"
echo " Organize by environment (test, staging, prod) and coin"
echo ""
else
echo "✓ config.json already exists"
fi

# Optionally create .env if user wants it
if [ ! -f .env ]; then
read -p "Do you also want to create .env file? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
cp env.example .env
echo "✓ Created .env file"
echo " Note: config.json takes precedence, .env variables can override"
fi
fi

# Build the module
echo ""
echo "Building dev-cli..."
yarn build

echo ""
echo "✓ Setup complete!"
echo ""
echo "Next steps:"
echo " 1. Edit config.json with your credentials for each env/coin"
echo " 2. Try: BITGO_COIN=tbtc BITGO_ENV=test yarn bitgo-dev balance"
echo " 3. Or: BITGO_COIN=gteth BITGO_ENV=test yarn bitgo-dev balance"
echo ""
echo "See QUICKSTART.md for more examples"
echo ""

29 changes: 29 additions & 0 deletions modules/dev-cli/src/bitgo-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { BitGo } from 'bitgo';
import { Config } from './config';

export async function getBitGoInstance(config: Config): Promise<BitGo> {
const bitgoOptions: any = { env: config.env };

if (config.customRootUri) {
bitgoOptions.customRootUri = config.customRootUri;
}

if (config.customBitcoinNetwork) {
bitgoOptions.customBitcoinNetwork = config.customBitcoinNetwork;
}

const bitgo = new BitGo(bitgoOptions);
await bitgo.authenticateWithAccessToken({ accessToken: config.accessToken });

return bitgo;
}

export async function unlockIfNeeded(bitgo: BitGo, config: Config): Promise<void> {
if (config.otp) {
const unlocked = await bitgo.unlock({ otp: config.otp, duration: 3600 });
if (!unlocked) {
throw new Error('Failed to unlock BitGo session with provided OTP');
}
}
}

75 changes: 75 additions & 0 deletions modules/dev-cli/src/commands/address.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { CommandModule } from 'yargs';
import { getConfig, validateWalletId } from '../config';
import { getBitGoInstance, unlockIfNeeded } from '../bitgo-client';
import { logSuccess, logError, logInfo, logJSON } from '../utils';

export const addressCommand: CommandModule = {
command: 'address <action>',
describe: 'Address operations',
builder: (yargs) => {
return yargs
.positional('action', {
describe: 'Action to perform',
choices: ['create', 'list'],
demandOption: true,
})
.option('chain', {
alias: 'c',
describe: 'Address chain (for UTXO coins)',
type: 'number',
default: 10,
})
.option('limit', {
alias: 'l',
describe: 'Number of addresses to list',
type: 'number',
});
},
handler: async (argv: any) => {
try {
const config = getConfig();
const walletId = validateWalletId(config);
const bitgo = await getBitGoInstance(config);

if (argv.action === 'create') {
logInfo(`Creating new address for wallet ${walletId}...`);

await unlockIfNeeded(bitgo, config);

const wallet = await bitgo.coin(config.coin).wallets().get({ id: walletId });
const addressOptions: any = {};

// For UTXO coins, set chain
if (argv.chain) {
addressOptions.chain = argv.chain;
}

const address = await wallet.createAddress(addressOptions);

console.log('\n' + '─'.repeat(50));
logJSON(address);
console.log('─'.repeat(50) + '\n');

logSuccess('Address created successfully');
} else if (argv.action === 'list') {
logInfo(`Listing addresses for wallet ${walletId}...`);

const wallet = await bitgo.coin(config.coin).wallets().get({ id: walletId });
const addresses = await wallet.addresses();

console.log('\n' + '─'.repeat(50));
if (argv.limit) {
logJSON(addresses.addresses.slice(0, argv.limit));
} else {
logJSON(addresses);
}
console.log('─'.repeat(50) + '\n');

logSuccess('Addresses retrieved successfully');
}
} catch (error) {
logError(`Failed to perform address operation: ${error.message}`);
process.exit(1);
}
},
};
40 changes: 40 additions & 0 deletions modules/dev-cli/src/commands/balance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { CommandModule } from 'yargs';
import { getConfig, validateWalletId } from '../config';
import { getBitGoInstance } from '../bitgo-client';
import { logSuccess, logError, logInfo, logJSON } from '../utils';

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note

Unused import logJSON.

Copilot Autofix

AI 3 months ago

To fix this issue, remove logJSON from the import statement on line 4 in modules/dev-cli/src/commands/balance.ts. You should not change how any other imported symbol is used. Only delete the unused import, leaving logSuccess, logError, and logInfo unchanged in the import statement. This improves code clarity and removes a potential source of confusion or maintenance overhead. No further code or imports need to be added or altered elsewhere in the file.


Suggested changeset 1
modules/dev-cli/src/commands/balance.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/modules/dev-cli/src/commands/balance.ts b/modules/dev-cli/src/commands/balance.ts
--- a/modules/dev-cli/src/commands/balance.ts
+++ b/modules/dev-cli/src/commands/balance.ts
@@ -1,7 +1,7 @@
 import { CommandModule } from 'yargs';
 import { getConfig, validateWalletId } from '../config';
 import { getBitGoInstance } from '../bitgo-client';
-import { logSuccess, logError, logInfo, logJSON } from '../utils';
+import { logSuccess, logError, logInfo } from '../utils';
 
 export const balanceCommand: CommandModule = {
   command: 'balance',
EOF
@@ -1,7 +1,7 @@
import { CommandModule } from 'yargs';
import { getConfig, validateWalletId } from '../config';
import { getBitGoInstance } from '../bitgo-client';
import { logSuccess, logError, logInfo, logJSON } from '../utils';
import { logSuccess, logError, logInfo } from '../utils';

export const balanceCommand: CommandModule = {
command: 'balance',
Copilot is powered by AI and may make mistakes. Always verify output.

export const balanceCommand: CommandModule = {
command: 'balance',
describe: 'Get wallet balance',
handler: async () => {
try {
const config = getConfig();
const walletId = validateWalletId(config);

logInfo(`Getting balance for wallet ${walletId} on ${config.coin}...`);

const bitgo = await getBitGoInstance(config);
const wallet = await bitgo.coin(config.coin).wallets().get({ id: walletId });

console.log('\n' + '─'.repeat(50));
console.log(`Wallet ID: ${wallet.id()}`);

// Handle different coin types
if (wallet.receiveAddress) {
console.log(`Receive Address: ${wallet.receiveAddress()}`);
} else if (wallet.coinSpecific && wallet.coinSpecific()?.rootAddress) {
console.log(`Root Address: ${wallet.coinSpecific().rootAddress}`);
}

console.log(`Balance: ${wallet.balanceString()}`);
console.log(`Confirmed Balance: ${wallet.confirmedBalanceString()}`);
console.log(`Spendable Balance: ${wallet.spendableBalanceString()}`);
console.log('─'.repeat(50) + '\n');

logSuccess('Balance retrieved successfully');
} catch (error) {
logError(`Failed to get balance: ${error.message}`);
process.exit(1);
}
},
};
7 changes: 7 additions & 0 deletions modules/dev-cli/src/commands/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export { balanceCommand } from './balance';
export { addressCommand } from './address';
export { sendCommand } from './send';
export { transfersCommand } from './transfers';
export { walletCommand } from './wallet';
export { lightningCommand } from './lightning';

Loading
Loading