Skip to content

Commit

Permalink
feat(ar.io cli): init transfer command
Browse files Browse the repository at this point in the history
  • Loading branch information
fedellen committed Nov 21, 2024
1 parent 3c04cac commit 5553584
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 20 deletions.
9 changes: 9 additions & 0 deletions src/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ import { program } from 'commander';
import { version } from '../version.js';
import { balance } from './commands/balance.js';
import { joinNetwork } from './commands/joinNetwork.js';
import { transfer } from './commands/transfer.js';
import {
balanceOptions,
globalOptions,
joinNetworkOptions,
transferOptions,
} from './options.js';
import { makeCommand, runCommand } from './utils.js';

Expand Down Expand Up @@ -63,6 +65,13 @@ makeCommand({
// update-gateway-settings

// transfer
makeCommand({
name: 'transfer',
description: 'Transfer IO to another address',
options: transferOptions,
}).action(async (_, command) => {
await runCommand(command, transfer);
});

// redelegate-stake

Expand Down
42 changes: 22 additions & 20 deletions src/cli/commands/joinNetwork.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export type JoinNetworkOptions = WalletOptions & {

export async function joinNetwork(options: JoinNetworkOptions) {
const jwk = requiredJwkFromOptions(options);
const address = jwkToAddress(jwk);
const io = writeIOFromOptions(options, new ArweaveSigner(jwk));

const {
Expand Down Expand Up @@ -69,7 +70,24 @@ export async function joinNetwork(options: JoinNetworkOptions) {
throw new Error('FQDN is required. Please provide a --fqdn for your node.');
}

const address = jwkToAddress(jwk);
const settings = {
observerAddress: observer,
operatorStake,
allowDelegatedStaking:
disableDelegatedStaking === undefined
? undefined
: !disableDelegatedStaking,
autoStake: disableAutoStake === undefined ? undefined : !disableAutoStake,
delegateRewardShareRatio,
allowedDelegates,
fqdn,
label,
minDelegatedStake,
note,
port,
properties,
};

if (!options.skipConfirmation) {
const balance = await io.getBalance({ address });

Expand All @@ -82,7 +100,7 @@ export async function joinNetwork(options: JoinNetworkOptions) {
const { confirm } = await prompts({
type: 'confirm',
name: 'confirm',
message: `You are about to stake ${operatorStake} IO to join the AR.IO network. Are you sure?`,
message: `Gateway Settings:\n\n${JSON.stringify(settings, null, 2)}\n\nYou are about to stake ${operatorStake} IO to join the AR.IO network\nAre you sure?\n`,
initial: true,
});

Expand All @@ -92,28 +110,12 @@ export async function joinNetwork(options: JoinNetworkOptions) {
}
}

const result = await io.joinNetwork({
observerAddress: observer,
operatorStake,
allowDelegatedStaking:
disableDelegatedStaking === undefined
? undefined
: !disableDelegatedStaking,
autoStake: disableAutoStake === undefined ? undefined : !disableAutoStake,
delegateRewardShareRatio,
allowedDelegates,
fqdn,
label,
minDelegatedStake,
note,
port,
properties,
});
const result = await io.joinNetwork(settings);

const output = {
joinNetworkResult: result,
address,
message: `Congratulations! You have successfully joined the AR.IO network (; `,
message: `Congratulations!\nYou have successfully joined the AR.IO network (;`,
};

console.log(JSON.stringify(output, null, 2));
Expand Down
80 changes: 80 additions & 0 deletions src/cli/commands/transfer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import prompts from 'prompts';

import { IOToken, mIOToken } from '../../types/token.js';
import { WalletOptions } from '../options.js';
import {
formatIOWithCommas,
jwkToAddress,
requiredJwkFromOptions,
writeIOFromOptions,
} from '../utils.js';

export type TransferOptions = WalletOptions & {
quantity: number | undefined;
target: string | undefined;
};

export async function transfer(options: TransferOptions) {
const jwk = requiredJwkFromOptions(options);
const address = jwkToAddress(jwk);
const io = writeIOFromOptions(options);

const { target, quantity } = options;
if (target === undefined) {
throw new Error('Target address is required');
}
if (quantity === undefined) {
throw new Error('Quantity is required');
}

const ioQuantity = new IOToken(+quantity);

if (!options.skipConfirmation) {
const balance = await io.getBalance({ address });

if (balance < quantity) {
throw new Error(
`Insufficient IO balance for transfer. Balance available: ${new mIOToken(balance).toIO()} IO`,
);
}

const { confirm } = await prompts({
type: 'confirm',
name: 'confirm',
message: `Are you sure you want to transfer ${formatIOWithCommas(ioQuantity)} IO to ${target}?`,
});

if (!confirm) {
console.log('Aborted transfer command by user');
return;
}
}

const result = await io.transfer({
target,
qty: ioQuantity.toMIO().valueOf(),
});

const output = {
address: address,
transferResult: result,
message: `Successfully transferred ${formatIOWithCommas(ioQuantity)} IO to ${target}`,
};

console.log(JSON.stringify(output, null, 2));
}
10 changes: 10 additions & 0 deletions src/cli/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ export const optionMap = {
alias: '-a, --address <address>',
description: 'The address to interact with',
},
target: {
alias: '-t, --target <target>',
description: 'The target address to interact with',
},
quantity: {
alias: '-q, --quantity <quantity>',
description: 'The quantity of mIO to interact with',
Expand Down Expand Up @@ -124,6 +128,12 @@ export const walletOptions = [

export const balanceOptions = [...walletOptions, optionMap.address];

export const transferOptions = [
...walletOptions,
optionMap.quantity,
optionMap.target,
];

export const joinNetworkOptions = [
...walletOptions,
optionMap.quantity,
Expand Down
6 changes: 6 additions & 0 deletions src/cli/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
ArweaveSigner,
ContractSigner,
IO,
IOToken,
IO_DEVNET_PROCESS_ID,
IO_TESTNET_PROCESS_ID,
Logger,
Expand Down Expand Up @@ -150,3 +151,8 @@ export function addressFromOptions(options: AddressOptions): string {

throw new Error('No address provided. Use --address or --wallet-file');
}

export function formatIOWithCommas(value: IOToken): string {
const [integerPart, decimalPart] = value.toString().split('.');
return integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',') + '.' + decimalPart;
}

0 comments on commit 5553584

Please sign in to comment.