Skip to content

Commit

Permalink
refactor(account)!: move aecli account spend to aecli spend
Browse files Browse the repository at this point in the history
BREAKING CHANGE: `aecli account spend` renamed to `aecli spend`
  • Loading branch information
davidyuk committed Apr 10, 2024
1 parent cfd7ac5 commit 4af1b4c
Show file tree
Hide file tree
Showing 12 changed files with 179 additions and 147 deletions.
2 changes: 1 addition & 1 deletion CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ Wallet path___________________ /path-to/wallet.json

Using this command, you can send coins to another wallet. Just indicate another account's address and an amount which should be sent.
```
$ aecli account spend ./wallet.json --password top-secret ak_2GN72gRFHYmJd1DD2g2sLADr5ZXa13DPYNtuFajhsZT2y3FiWu 1.23ae
$ aecli spend ./wallet.json --password top-secret ak_2GN72gRFHYmJd1DD2g2sLADr5ZXa13DPYNtuFajhsZT2y3FiWu 1.23ae
```
As an option, you can set _--ttl_ parameter, which limits the lifespan of this transaction.

Expand Down
30 changes: 2 additions & 28 deletions src/actions/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
// This script initialize all `account` function

import fs from 'fs-extra';
import {
generateKeyPair, encode, Encoding, verifyMessage as _verifyMessage,
} from '@aeternity/aepp-sdk';
import { generateKeyPair, verifyMessage as _verifyMessage } from '@aeternity/aepp-sdk';
import { writeWallet } from '../utils/account.js';
import { initSdkByWalletFile, getAccountByWalletFile } from '../utils/cli.js';
import { print, printTransaction, printUnderscored } from '../utils/print.js';
import { print, printUnderscored } from '../utils/print.js';
import { PROMPT_TYPE, prompt } from '../utils/prompt.js';

// ## `Sign message` function
Expand Down Expand Up @@ -67,30 +65,6 @@ export async function sign(walletPath, tx, { networkId: networkIdOpt, json, ...o
}
}

// ## `Spend` function
// this function allow you to `send` coins to another `account`
export async function spend(
walletPath,
receiverNameOrAddress,
{ amount, fraction },
{
ttl, json, nonce, fee, payload, ...options
},
) {
const sdk = await initSdkByWalletFile(walletPath, options);

const tx = await sdk[amount != null ? 'spend' : 'transferFunds'](
amount ?? fraction / 100,
receiverNameOrAddress,
{
ttl, nonce, payload: encode(Buffer.from(payload), Encoding.Bytearray), fee,
},
);

if (!json) print('Transaction mined');
printTransaction(tx, json);
}

// ## Get `address` function
// This function allow you retrieve account `public` and `private` keys
export async function getAddress(walletPath, options) {
Expand Down
5 changes: 5 additions & 0 deletions src/aecli-spend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env node
import program from './commands/spend.js';
import { runProgram } from './utils/CliError.js';

await runProgram(program);
4 changes: 2 additions & 2 deletions src/arguments.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const forceOption = new Option('-f, --force', 'Ignore node version compat

export const passwordOption = new Option('-P, --password [password]', 'Wallet Password');

export const ttlOption = (usingNode) => new Option('-T, --ttl [ttl]', 'Validity of the transaction in number of blocks')
.default(noValue, usingNode ? 'current height increased by 3' : 'infinity');
export const ttlOption = (usingNode) => new Option('-T, --ttl [ttl]', 'Validity of the transaction in number of keyblocks, or without this limit if 0')
.default(noValue, usingNode ? 3 : 0);

export const networkIdOption = new Option('--networkId [networkId]', 'Network id');
41 changes: 3 additions & 38 deletions src/commands/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,60 +5,25 @@
import { Command } from 'commander';
import * as Account from '../actions/account.js';
import {
nodeOption,
jsonOption,
coinAmountParser,
feeOption,
forceOption,
passwordOption,
ttlOption,
networkIdOption,
nodeOption, jsonOption, forceOption, passwordOption, networkIdOption,
} from '../arguments.js';

const program = new Command().name('aecli account');

// ## Initialize `options`
const addCommonOptions = (p) => p
.addOption(nodeOption)
.addOption(passwordOption)
.addOption(forceOption)
.addOption(jsonOption);

// ## Initialize `spend` command
//
// You can use this command to send tokens to another account
//
// Example: `aecli account spend ./myWalletKeyFile ak_1241rioefwj23f2wfdsfsdsdfsasdf 100 --password testpassword`
//
// Example: `aecli account spend ./myWalletKeyFile aensAccountName.chain 100 --password testpassword`
//
// You can set transaction `ttl(Time to leave)`. If not set use default.
//
// Example: `aecli account spend ./myWalletKeyFile ak_1241rioefwj23f2wfdsfsdsdfsasdf 100 --password testpassword --ttl 20` --> this tx will leave for 20 blocks
addCommonOptions(program
.command('spend <wallet_path>')
.argument('<receiver>', 'Address or name of recipient account')
.argument(
'<amountOrPercent>',
'Amount of coins to send in aettos/ae (example 1.2ae), or percent of sender balance (example 42%)',
(amount) => {
if (amount.endsWith('%')) return { fraction: +amount.slice(0, -1) };
return { amount: coinAmountParser(amount) };
},
)
.option('--payload [payload]', 'Transaction payload.', '')
.addOption(feeOption)
.addOption(ttlOption(true))
.option('-N, --nonce [nonce]', 'Override the nonce that the transaction is going to be sent with')
.action(Account.spend));

// ## Initialize `sign` command
//
// You can use this command to sign your transaction's
//
// Example: `aecli account sign ./myWalletKeyFile tx_1241rioefwj23f2wfdsfsdsdfsasdf --password testpassword`
addCommonOptions(program
.command('sign <wallet_path> <tx>')
.addOption(nodeOption)
.addOption(forceOption)
.addOption(networkIdOption)
.description('Sign a transaction using wallet')
.action(Account.sign));
Expand Down
1 change: 1 addition & 0 deletions src/commands/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const EXECUTABLE_CMD = [
{ name: 'name', desc: 'AENS system' },
{ name: 'tx', desc: 'Transaction builder' },
{ name: 'oracle', desc: 'Interact with oracles' },
{ name: 'spend', desc: 'Send coins to account or contract' },
];

(() => {
Expand Down
59 changes: 59 additions & 0 deletions src/commands/spend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Command } from 'commander';
import { encode, Encoding } from '@aeternity/aepp-sdk';
import { initSdkByWalletFile } from '../utils/cli.js';
import { print, printTransaction } from '../utils/print.js';
import {
nodeOption,
jsonOption,
coinAmountParser,
feeOption,
forceOption,
passwordOption,
ttlOption,
} from '../arguments.js';

export default new Command('aecli spend')
.description('Sends coins to another account or contract.')
.addHelpText('after', `
Example call:
$ aecli spend ./wallet.json ak_2GN72... 100 --password top-secret
$ aecli spend ./wallet.json aens-name.chain 1.23ae --password top-secret
$ aecli spend ./wallet.json ak_2GN72... 20% --password top-secret --ttl 20`)
.argument('<wallet_path>', 'A path to wallet file')
.argument('<receiver>', 'Address or name of recipient account')
.argument(
'<amountOrPercent>',
'Amount of coins to send in aettos/ae (example: 1.2ae), or percent of sender balance (example: 42%)',
(amount) => {
if (amount.endsWith('%')) return { fraction: +amount.slice(0, -1) };
return { amount: coinAmountParser(amount) };
},
)
.option('--payload [payload]', 'Transaction payload as text', '')
.addOption(feeOption)
.addOption(ttlOption(true))
.option('-N, --nonce [nonce]', 'Override the nonce that the transaction is going to be sent with')
.addOption(nodeOption)
.addOption(passwordOption)
.addOption(forceOption)
.addOption(jsonOption)
.action(async (
walletPath,
receiverNameOrAddress,
{ amount, fraction },
{
ttl, json, nonce, fee, payload, ...options
},
) => {
const sdk = await initSdkByWalletFile(walletPath, options);
const tx = await sdk[amount != null ? 'spend' : 'transferFunds'](
amount ?? fraction / 100,
receiverNameOrAddress,
{
ttl, nonce, payload: encode(Buffer.from(payload), Encoding.Bytearray), fee,
},
);
if (!json) print('Transaction mined');
printTransaction(tx, json);
});
69 changes: 0 additions & 69 deletions test/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,75 +74,6 @@ Secret Key ______________________________ ${keypair.secretKey}
.to.be.a('string');
});

it('Spend coins to another wallet', async () => {
const amount = 100;
const { publicKey } = generateKeyPair();
const resJson = await executeAccount([
'spend', WALLET_NAME, '--password', 'test', publicKey, amount, '--json',
]);
const receiverBalance = await sdk.getBalance(publicKey);
expect(+receiverBalance).to.be.equal(amount);

expect(resJson.tx.fee).to.be.a('string');
expect(resJson).to.eql({
blockHash: resJson.blockHash,
blockHeight: resJson.blockHeight,
encodedTx: resJson.encodedTx,
hash: resJson.hash,
rawTx: resJson.rawTx,
signatures: [resJson.signatures[0]],
tx: {
amount: '100',
fee: resJson.tx.fee,
nonce: 1,
payload: 'ba_Xfbg4g==',
recipientId: resJson.tx.recipientId,
senderId: resJson.tx.senderId,
ttl: resJson.tx.ttl,
type: 'SpendTx',
version: 1,
},
});

const res = await executeAccount([
'spend', WALLET_NAME, '--password', 'test', publicKey, amount,
]);
const lineEndings = res.split('\n').map((l) => l.split(' ').at(-1));
expect(res).to.be.equal(`
Transaction mined
Tx hash _________________________________ ${lineEndings[1]}
Block hash ______________________________ ${lineEndings[2]}
Block height ____________________________ ${lineEndings[3]}
Signatures ______________________________ ${lineEndings[4]}
Tx Type _________________________________ SpendTx
Sender account __________________________ ${resJson.tx.senderId}
Recipient account _______________________ ${resJson.tx.recipientId}
Amount __________________________________ 100
Payload _________________________________ ba_Xfbg4g==
Fee _____________________________________ ${resJson.tx.fee}
Nonce ___________________________________ 2
TTL _____________________________________ ${lineEndings[12]}
Version _________________________________ 1
`.trim());
});

it('Spend coins to another wallet in ae', async () => {
const receiverKeys = generateKeyPair();
const { tx: { fee } } = await executeAccount([
'spend', WALLET_NAME, '--password', 'test', '--json',
receiverKeys.publicKey, '1ae', '--fee', '0.02ae',
]);
expect(await sdk.getBalance(receiverKeys.publicKey)).to.be.equal('1000000000000000000');
expect(fee).to.be.equal('20000000000000000');
});

it('Spend percent of coins to account', async () => {
const { publicKey } = generateKeyPair();
const balanceBefore = await sdk.getBalance(sdk.address);
await executeAccount(['spend', WALLET_NAME, '--password', 'test', publicKey, '42%']);
expect(+await sdk.getBalance(publicKey)).to.be.equal(balanceBefore * 0.42);
});

it('Sign message', async () => {
const data = 'Hello world';
const signedMessage = await executeAccount(['sign-message', WALLET_NAME, data, '--json', '--password', 'test']);
Expand Down
3 changes: 3 additions & 0 deletions test/chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ describe('Chain Module', () => {

before(async () => {
sdk = await getSdk();
for (let i = 0; i < 5; i += 1) {
await sdk.spend(0, sdk.address); // eslint-disable-line no-await-in-loop
}
});

it('prints top', async () => {
Expand Down
9 changes: 6 additions & 3 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,12 @@ export async function executeProgram(program, args) {
try {
const allArgs = [
...args.map((arg) => arg.toString()),
...[
'config', 'select-node', 'select-compiler',
].includes(args[0]) ? [] : ['--url', url],
...['config', 'select-node', 'select-compiler'].includes(args[0])
|| (
// eslint-disable-next-line no-underscore-dangle
program._name === 'aecli account'
&& ['save', 'create', 'address', 'sign-message', 'verify-message'].includes(args[0])
) ? [] : ['--url', url],
...[
'compile', 'deploy', 'call', 'encode-calldata', 'decode-call-result',
].includes(args[0]) && !args.includes('--compilerUrl') ? ['--compilerUrl', compilerUrl] : [],
Expand Down
10 changes: 4 additions & 6 deletions test/name.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import {
} from './index.js';
import nameProgram from '../src/commands/name.js';
import inspectProgram from '../src/commands/inspect.js';
import accountProgram from '../src/commands/account.js';
import spendProgram from '../src/commands/spend.js';

const executeName = (args) => executeProgram(nameProgram, args);
const executeInspect = (args) => executeProgram(inspectProgram, args);
const executeAccount = (args) => executeProgram(accountProgram, args);
const executeSpend = (args) => executeProgram(spendProgram, args);

describe('AENS Module', () => {
const { publicKey } = generateKeyPair();
Expand Down Expand Up @@ -138,8 +138,7 @@ describe('AENS Module', () => {

it('Fail spend by name on invalid input', async () => {
const amount = 100000009;
await executeAccount([
'spend',
await executeSpend([
WALLET_NAME,
'--password',
'test',
Expand All @@ -151,8 +150,7 @@ describe('AENS Module', () => {

it('Spend by name', async () => {
const amount = 100000009;
const { tx: { recipientId } } = await executeAccount([
'spend',
const { tx: { recipientId } } = await executeSpend([
WALLET_NAME,
'--password',
'test',
Expand Down
Loading

0 comments on commit 4af1b4c

Please sign in to comment.