Skip to content
This repository was archived by the owner on Sep 25, 2026. It is now read-only.

Commit e0bafdd

Browse files
committed
enhancement: more changes to account commands
1 parent 221a220 commit e0bafdd

7 files changed

Lines changed: 128 additions & 17 deletions

File tree

‎src/commands/account.ts‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* eslint-disable no-console */
2+
import {Command} from 'commander'
3+
import {lookupAccount} from './chain/interact'
4+
import {getDefaultChain} from './chain/utils'
5+
6+
/**
7+
* Create the account command
8+
*/
9+
export function createAccountCommand(): Command {
10+
const accountCommand = new Command('account')
11+
accountCommand.description('Lookup account data on the blockchain')
12+
13+
accountCommand
14+
.argument('<accountName>', 'Account name to lookup')
15+
.option('-c, --chain <chainName>', 'Chain to query (default: local or configured default)')
16+
.option('--json', 'Output as JSON')
17+
.action(async (accountName, options) => {
18+
try {
19+
const chainName = options.chain || (await getDefaultChain())
20+
await lookupAccount(chainName, accountName, options)
21+
} catch (error: any) {
22+
console.error(`Error: ${error.message}`)
23+
process.exit(1)
24+
}
25+
})
26+
27+
return accountCommand
28+
}

‎src/commands/chain/index.ts‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {Command} from 'commander'
33
import {showChainLogs, showChainStatus, startLocalChain, stopLocalChain} from './local'
44
import {checkLeapInstallation} from './install'
55
import {addInteractCommands, addInteractSubcommands} from './interact'
6+
import {setDefaultChain} from './utils'
67

78
/**
89
* Create the chain command with subcommands
@@ -84,6 +85,20 @@ export function createChainCommand(): Command {
8485
}
8586
})
8687

88+
// Set default chain
89+
chain
90+
.command('set <chainName>')
91+
.description('Set the default chain for account lookups')
92+
.action(async (chainName) => {
93+
try {
94+
await setDefaultChain(chainName)
95+
console.log(`Default chain set to: ${chainName}`)
96+
} catch (error: any) {
97+
console.error(`Error: ${error.message}`)
98+
process.exit(1)
99+
}
100+
})
101+
87102
// Check installation
88103
chain
89104
.command('check')

‎src/commands/chain/utils.ts‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,3 +280,34 @@ export function createApiClientForPort(port: number): APIClient {
280280
const provider = new FetchProvider(url, {fetch})
281281
return new APIClient({provider})
282282
}
283+
284+
/**
285+
* Get the config file path for storing default chain preference
286+
*/
287+
export function getConfigFilePath(): string {
288+
return path.join(getDefaultConfigDir(), 'default-chain.json')
289+
}
290+
291+
/**
292+
* Get the default chain name (defaults to 'local')
293+
*/
294+
export async function getDefaultChain(): Promise<string> {
295+
const configFile = getConfigFilePath()
296+
try {
297+
const content = await fs.promises.readFile(configFile, 'utf-8')
298+
const config = JSON.parse(content)
299+
return config.chain || 'local'
300+
} catch {
301+
return 'local'
302+
}
303+
}
304+
305+
/**
306+
* Set the default chain name
307+
*/
308+
export async function setDefaultChain(chainName: string): Promise<void> {
309+
const configFile = getConfigFilePath()
310+
await ensureDir(path.dirname(configFile))
311+
const config = {chain: chainName}
312+
await fs.promises.writeFile(configFile, JSON.stringify(config, null, 2))
313+
}

‎src/commands/compile.ts‎

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import {Command} from 'commander'
33
import {execSync} from 'child_process'
44
import {existsSync, mkdirSync, readdirSync, readFileSync, statSync} from 'fs'
5-
import {basename, dirname, extname, join, relative, resolve} from 'path'
5+
import {basename, dirname, extname, isAbsolute, join, normalize, relative, resolve} from 'path'
66
import {checkLeapInstallation} from './chain/install'
77

88
/**
@@ -19,7 +19,10 @@ export async function compileContract(file: string | undefined, outputDir: strin
1919
return
2020
}
2121

22-
const absoluteOutputDir = resolve(outputDir)
22+
// Resolve output directory to absolute path, handling both relative and absolute paths
23+
const absoluteOutputDir = isAbsolute(outputDir)
24+
? normalize(outputDir)
25+
: normalize(resolve(outputDir))
2326
ensureOutputDirectory(absoluteOutputDir)
2427

2528
// Ensure cdt-cpp is installed
@@ -92,11 +95,11 @@ export async function getFilesToCompile(
9295
}
9396

9497
/**
95-
* Ensure output directory exists
98+
* Ensure output directory exists, create it if it doesn't
9699
*/
97100
function ensureOutputDirectory(dir: string): void {
98101
if (!existsSync(dir)) {
99-
throw new Error(`Output directory does not exist: ${dir}`)
102+
mkdirSync(dir, {recursive: true})
100103
}
101104
}
102105

@@ -314,7 +317,15 @@ async function compileSingleFile(
314317
}
315318
} else {
316319
// Use path relative to current directory (preserve structure)
317-
relativePath = relative(currentDir, filePath)
320+
// But if the relative path goes outside currentDir (starts with ..),
321+
// just use the filename to avoid path issues
322+
const relPath = relative(currentDir, filePath)
323+
if (relPath.startsWith('..')) {
324+
// If path goes outside current directory, just use filename
325+
relativePath = basename(filePath, '.cpp')
326+
} else {
327+
relativePath = relPath
328+
}
318329
}
319330

320331
const relativeDir = dirname(relativePath)
@@ -326,7 +337,8 @@ async function compileSingleFile(
326337
wasmOutput = join(outputDir, `${fileName}.wasm`)
327338
} else {
328339
// File is in a subdirectory, preserve the structure (but without src/ prefix if stripped)
329-
const outputSubDir = join(outputDir, relativeDir)
340+
// Normalize the path to prevent issues with relative paths containing '..'
341+
const outputSubDir = normalize(join(outputDir, relativeDir))
330342
// Ensure the output subdirectory exists
331343
if (!existsSync(outputSubDir)) {
332344
mkdirSync(outputSubDir, {recursive: true})

‎src/commands/wallet/account.ts‎

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,22 @@ export async function createAccount(options: AccountCreateOptions): Promise<void
149149
if (response.status === 201) {
150150
log('Account created successfully!', 'info')
151151
log(`Account Name: ${accountName}`, 'info')
152+
log(`Public Key: ${publicKey}`, 'info')
153+
154+
// Import the private key into the wallet
152155
if (privateKey) {
153-
log(`Private Key: ${privateKey.toString()}`, 'info')
156+
try {
157+
addKeyToWallet(privateKey, String(accountName))
158+
log(`✅ Private key imported into wallet as: ${accountName}`, 'info')
159+
} catch (error) {
160+
log(
161+
`⚠️ Could not import key into wallet (may already exist): ${
162+
(error as Error).message
163+
}`,
164+
'info'
165+
)
166+
}
154167
}
155-
log(`Public Key: ${publicKey}`, 'info')
156168
} else {
157169
const responseData = await response.json()
158170
log(
@@ -319,25 +331,24 @@ async function createAccountOnLocalChain(
319331

320332
log('Account created successfully!', 'info')
321333
log(`Account Name: ${accountName}`, 'info')
322-
if (privateKey) {
323-
log(`Private Key: ${privateKey.toString()}`, 'info')
324-
}
325334
log(`Public Key: ${publicKey}`, 'info')
326335
log(`Transaction ID: ${result.resolved?.transaction.id}`, 'info')
327336

328-
// Store the key in wallet with account name (only if we have a private key)
337+
// Import the private key into the wallet (only if we have a private key)
329338
if (privateKey) {
330339
try {
331340
addKeyToWallet(privateKey, accountName)
332-
log(`Key stored in wallet as: ${accountName}`, 'info')
341+
log(`✅ Private key imported into wallet as: ${accountName}`, 'info')
333342
} catch (error) {
334343
log(
335-
`Could not store key in wallet (may already exist): ${(error as Error).message}`,
344+
`⚠️ Could not import key into wallet (may already exist): ${
345+
(error as Error).message
346+
}`,
336347
'info'
337348
)
338349
}
339350
} else {
340-
log('Note: No private key available to store in wallet (public key was provided)', 'info')
351+
log('Note: No private key available to import (public key was provided)', 'info')
341352
}
342353
}
343354

‎src/index.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {createChainCommand} from './commands/chain/index'
77
import {createCompileCommand} from './commands/compile'
88
import {createDevCommand} from './commands/dev'
99
import {createWalletCommand} from './commands/wallet/index'
10+
import {createAccountCommand} from './commands/account'
1011

1112
const program = new Command()
1213

@@ -48,4 +49,7 @@ program.addCommand(createDevCommand())
4849
// 7. Command to manage wallet (includes account creation)
4950
program.addCommand(createWalletCommand())
5051

52+
// 8. Command to lookup account data
53+
program.addCommand(createAccountCommand())
54+
5155
program.parse(process.argv)

‎test/tests/wallet-account.ts‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,31 @@ import {Chains} from '@wharfkit/common'
55

66
import {createAccount} from 'src/commands/wallet/account'
77
import * as utils from 'src/utils'
8+
import * as walletUtils from 'src/commands/wallet/utils'
89

910
suite('Wallet Account Create', () => {
1011
let sandbox: sinon.SinonSandbox
1112
let fetchStub: sinon.SinonStub
1213
let makeClientStub: sinon.SinonStub
1314
let logStub: sinon.SinonStub
15+
let addKeyToWalletStub: sinon.SinonStub
1416

1517
setup(function () {
1618
sandbox = sinon.createSandbox()
1719
fetchStub = sandbox.stub()
1820
makeClientStub = sandbox.stub()
1921
logStub = sandbox.stub()
22+
addKeyToWalletStub = sandbox.stub()
2023

2124
// Mock fetch from node-fetch
2225
sandbox.stub(nodeFetch, 'default').callsFake(fetchStub as any)
2326

2427
// Mock makeClient and log from utils
2528
sandbox.stub(utils, 'makeClient').callsFake(makeClientStub as any)
2629
sandbox.stub(utils, 'log').callsFake(logStub as any)
30+
31+
// Mock addKeyToWallet from wallet utils
32+
sandbox.stub(walletUtils, 'addKeyToWallet').callsFake(addKeyToWalletStub as any)
2733
})
2834

2935
teardown(function () {
@@ -101,6 +107,8 @@ suite('Wallet Account Create', () => {
101107
// Should not log private key when key is provided
102108
const logCalls = logStub.getCalls().map((call) => call.args[0])
103109
assert.isFalse(logCalls.some((msg) => msg.includes('Private Key')))
110+
// Should not import key when only public key is provided
111+
assert.isFalse(addKeyToWalletStub.called)
104112
})
105113

106114
test('generates private key when not provided', async function () {
@@ -116,9 +124,11 @@ suite('Wallet Account Create', () => {
116124
assert.isString(body.activeKey)
117125
assert.include(body.activeKey, 'PUB_K1_')
118126

119-
// Should log private key when it was generated
127+
// Should import private key when it was generated (not log it)
120128
const logCalls = logStub.getCalls().map((call) => call.args[0])
121-
assert.isTrue(logCalls.some((msg) => msg.includes('Private Key')))
129+
assert.isFalse(logCalls.some((msg) => msg.includes('Private Key:')))
130+
assert.isTrue(logCalls.some((msg) => msg.includes('Private key imported into wallet')))
131+
assert.isTrue(addKeyToWalletStub.calledOnce)
122132
})
123133

124134
test('creates account on KylinTestnet chain', async function () {

0 commit comments

Comments
 (0)