UNPKG

@ar.io/sdk

Version:

[![codecov](https://codecov.io/gh/ar-io/ar-io-sdk/graph/badge.svg?token=7dXKcT7dJy)](https://codecov.io/gh/ar-io/ar-io-sdk)

765 lines (764 loc) 28 kB
#!/usr/bin/env node /** * 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. */ // eslint-disable-next-line header/header -- This is a CLI file import { program } from 'commander'; import { AOProcess, spawnANT } from '../node/index.js'; import { mARIOToken } from '../types/token.js'; import { version } from '../version.js'; import { setAntBaseNameCLICommand, setAntRecordCLICommand, } from './commands/antCommands.js'; import { buyRecordCLICommand, extendLeaseCLICommand, increaseUndernameLimitCLICommand, requestPrimaryNameCLICommand, upgradeRecordCLICommand, } from './commands/arnsPurchaseCommands.js'; import { cancelWithdrawal, decreaseDelegateStake, decreaseOperatorStake, delegateStake, increaseOperatorStake, instantWithdrawal, joinNetwork, leaveNetwork, redelegateStake, saveObservations, updateGatewaySettings, } from './commands/gatewayWriteCommands.js'; import { getAllGatewayVaults, getAllowedDelegates, getArNSRecord, getArNSReservedName, getArNSReturnedName, getCostDetails, getDelegations, getEpoch, getGateway, getGatewayDelegates, getGatewayVaults, getPrescribedNames, getPrescribedObservers, getPrimaryName, getTokenCost, getVault, listAllDelegatesCLICommand, listArNSRecords, listArNSReservedNames, listArNSReturnedNames, listGateways, resolveArNSName, } from './commands/readCommands.js'; import { createVaultCLICommand, extendVaultCLICommand, increaseVaultCLICommand, revokeVaultCLICommand, transferCLICommand, vaultedTransferCLICommand, } from './commands/transfer.js'; import { addressAndVaultIdOptions, antStateOptions, arnsPurchaseOptions, buyRecordOptions, decreaseDelegateStakeOptions, delegateStakeOptions, epochOptions, getVaultOptions, globalOptions, joinNetworkOptions, operatorStakeOptions, optionMap, paginationAddressOptions, paginationOptions, redelegateStakeOptions, setAntBaseNameOptions, setAntUndernameOptions, tokenCostOptions, transferOptions, updateGatewaySettingsOptions, vaultedTransferOptions, writeActionOptions, } from './options.js'; import { applyOptions, arioProcessIdFromOptions, assertConfirmationPrompt, customTagsFromOptions, epochInputFromOptions, formatARIOWithCommas, getANTStateFromOptions, getLoggerFromOptions, makeCommand, paginationParamsFromOptions, readANTFromOptions, readARIOFromOptions, requiredAddressFromOptions, requiredAoSignerFromOptions, requiredProcessIdFromOptions, requiredStringArrayFromOptions, requiredStringFromOptions, writeANTFromOptions, } from './utils.js'; applyOptions(program .name('ar.io') .version(version) .description('AR.IO Network CLI') .helpCommand(true), globalOptions); // # Getters makeCommand({ name: 'info', description: 'Get network info', action: (options) => readARIOFromOptions(options).getInfo(), }); makeCommand({ name: 'token-supply', description: 'Get the total token supply', action: (options) => readARIOFromOptions(options).getTokenSupply(), }); makeCommand({ name: 'balance', description: 'Get the balance of an address', options: [optionMap.address], action: (options) => readARIOFromOptions(options) .getBalance({ address: requiredAddressFromOptions(options) }) .then((result) => ({ address: requiredAddressFromOptions(options), mARIOBalance: result, message: `Provided address current has a balance of ${formatARIOWithCommas(new mARIOToken(result).toARIO())} ARIO`, })), }); makeCommand({ name: 'get-registration-fees', description: 'Get registration fees', action: (options) => readARIOFromOptions(options).getRegistrationFees(), }); makeCommand({ name: 'get-demand-factor', description: 'Get demand factor', action: (options) => readARIOFromOptions(options).getDemandFactor(), }); makeCommand({ name: 'get-demand-factor-settings', description: 'Get current settings for demand factor', action: (options) => readARIOFromOptions(options).getDemandFactorSettings(), }); makeCommand({ name: 'get-epoch-settings', description: 'Get current settings for epochs', action: (options) => readARIOFromOptions(options).getEpochSettings(), }); makeCommand({ name: 'get-gateway', description: 'Get the gateway of an address', options: [optionMap.address], action: getGateway, }); makeCommand({ name: 'get-gateway-delegates', description: 'Get the delegates of a gateway', options: paginationAddressOptions, action: getGatewayDelegates, }); makeCommand({ name: 'get-gateway-vaults', description: 'Get the vaults of a gateway', options: paginationAddressOptions, action: getGatewayVaults, }); makeCommand({ name: 'get-delegations', description: 'Get all stake delegated to gateways from this address', options: [optionMap.address], action: getDelegations, }); makeCommand({ name: 'get-allowed-delegates', description: 'Get the allow list of a gateway delegate', options: paginationAddressOptions, action: getAllowedDelegates, }); makeCommand({ name: 'get-arns-record', description: 'Get an ArNS record by name', options: [optionMap.name], action: getArNSRecord, }); makeCommand({ name: 'get-arns-reserved-name', description: 'Get a reserved ArNS name', options: [optionMap.name], action: getArNSReservedName, }); makeCommand({ name: 'get-arns-returned-name', description: 'Get an ArNS returned name by name', options: [optionMap.name], action: getArNSReturnedName, }); makeCommand({ name: 'get-epoch', description: 'Get epoch data', options: epochOptions, action: getEpoch, }); makeCommand({ name: 'get-current-epoch', description: 'Get current epoch data', action: (options) => readARIOFromOptions(options).getCurrentEpoch(), }); makeCommand({ name: 'get-prescribed-observers', description: 'Get prescribed observers for an epoch', options: epochOptions, action: getPrescribedObservers, }); makeCommand({ name: 'get-prescribed-names', description: 'Get prescribed names for an epoch', options: epochOptions, action: getPrescribedNames, }); makeCommand({ name: 'get-observations', description: 'Get observations for an epoch', options: epochOptions, action: (o) => readARIOFromOptions(o) .getObservations(epochInputFromOptions(o)) .then((result) => result ?? { message: 'No observations found for epoch' }), }); makeCommand({ name: 'get-distributions', description: 'Get distributions for an epoch', options: epochOptions, action: (o) => readARIOFromOptions(o) .getDistributions(epochInputFromOptions(o)) .then((result) => result ?? { message: 'No distributions found for epoch' }), }); makeCommand({ name: 'get-eligible-rewards', description: 'Get eligible rewards for an epoch', options: [...epochOptions, ...paginationOptions], action: (o) => readARIOFromOptions(o) .getEligibleEpochRewards(epochInputFromOptions(o), paginationParamsFromOptions(o)) .then((result) => result ?? { message: 'No eligible distributions found for epoch' }), }); makeCommand({ name: 'get-token-cost', description: 'Get token cost for an intended action', options: tokenCostOptions, action: getTokenCost, }); makeCommand({ name: 'get-cost-details', description: 'Get expanded cost details for an intended action', options: tokenCostOptions, action: getCostDetails, }); makeCommand({ name: 'get-primary-name', description: 'Get primary name', options: [optionMap.address, optionMap.name], action: getPrimaryName, }); makeCommand({ name: 'get-primary-name-request', description: 'Get primary name request', options: [optionMap.initiator], action: (o) => readARIOFromOptions(o) .getPrimaryNameRequest({ initiator: requiredStringFromOptions(o, 'initiator'), }) .then((result) => result ?? { message: `No primary name request found`, }), }); makeCommand({ name: 'get-redelegation-fee', description: 'Get redelegation fee', options: [optionMap.address], action: (options) => readARIOFromOptions(options).getRedelegationFee({ address: requiredAddressFromOptions(options), }), }); makeCommand({ name: 'get-vault', description: 'Get the vault of provided address and vault ID', options: getVaultOptions, action: getVault, }); // # ArNS Resolution makeCommand({ name: 'resolve-arns-name', description: 'Resolve an ArNS name', options: [optionMap.name], action: resolveArNSName, }); // # Paginated handlers makeCommand({ name: 'list-gateways', description: 'List the gateways of the network', options: paginationOptions, action: listGateways, }); makeCommand({ name: 'list-all-delegates', description: 'List all paginated delegates from all gateways', options: paginationOptions, action: listAllDelegatesCLICommand, }); makeCommand({ name: 'list-arns-records', description: 'List all ArNS records', options: paginationOptions, action: listArNSRecords, }); makeCommand({ name: 'list-arns-reserved-names', description: 'Get all reserved ArNS names', options: paginationOptions, action: listArNSReservedNames, }); makeCommand({ name: 'list-arns-returned-names', description: 'Get all ArNS recently returned names', options: paginationOptions, action: listArNSReturnedNames, }); makeCommand({ name: 'list-vaults', description: 'Get all wallet vaults', options: paginationOptions, action: (o) => readARIOFromOptions(o) .getVaults(paginationParamsFromOptions(o)) .then((result) => result.items.length ? result : { message: 'No vaults found' }), }); makeCommand({ name: 'list-primary-name-requests', description: 'Get primary name requests', options: paginationOptions, action: (o) => readARIOFromOptions(o) .getPrimaryNameRequests(paginationParamsFromOptions(o)) .then((result) => result.items.length ? result : { message: 'No requests found' }), }); makeCommand({ name: 'list-primary-names', description: 'Get primary names', options: paginationOptions, action: (o) => readARIOFromOptions(o) .getPrimaryNames(paginationParamsFromOptions(o)) .then((result) => result.items.length ? result : { message: 'No names found' }), }); makeCommand({ name: 'list-balances', description: 'List all balances', options: paginationOptions, action: (o) => readARIOFromOptions(o) .getBalances(paginationParamsFromOptions(o)) .then((result) => result.items.length ? result : { message: 'No balances found' }), }); makeCommand({ name: 'list-all-gateway-vaults', description: 'List vaults from all gateways', options: paginationAddressOptions, action: getAllGatewayVaults, }); // # Actions makeCommand({ name: 'transfer', description: 'Transfer ARIO to another address', options: transferOptions, action: transferCLICommand, }); makeCommand({ name: 'vaulted-transfer', description: 'Transfer ARIO to another address into a locked vault', options: vaultedTransferOptions, action: vaultedTransferCLICommand, }); makeCommand({ name: 'revoke-vault', description: 'Revoke a vaulted transfer as the controller', options: [...writeActionOptions, optionMap.vaultId, optionMap.recipient], action: revokeVaultCLICommand, }); makeCommand({ name: 'create-vault', description: 'Create a locked vault with balance from the sender', options: [...writeActionOptions, optionMap.lockLengthMs, optionMap.quantity], action: createVaultCLICommand, }); makeCommand({ name: 'extend-vault', description: 'Extend the lock length of a vault as the recipient', options: [...writeActionOptions, optionMap.vaultId, optionMap.extendLengthMs], action: extendVaultCLICommand, }); makeCommand({ name: 'increase-vault', description: 'Increase the balance of a locked vault as the recipient', options: [...writeActionOptions, optionMap.vaultId, optionMap.quantity], action: increaseVaultCLICommand, }); makeCommand({ name: 'join-network', description: 'Join a gateway to the AR.IO network', options: joinNetworkOptions, action: joinNetwork, }); makeCommand({ name: 'leave-network', description: 'Leave a gateway from the AR.IO network', action: leaveNetwork, }); makeCommand({ name: 'update-gateway-settings', description: 'Update AR.IO gateway settings', options: updateGatewaySettingsOptions, action: updateGatewaySettings, }); makeCommand({ name: 'save-observations', description: 'Save observations', options: [ optionMap.failedGateways, optionMap.transactionId, ...writeActionOptions, ], action: saveObservations, }); makeCommand({ name: 'increase-operator-stake', description: 'Increase operator stake', options: operatorStakeOptions, action: increaseOperatorStake, }); makeCommand({ name: 'decrease-operator-stake', description: 'Decrease operator stake', options: operatorStakeOptions, action: decreaseOperatorStake, }); makeCommand({ name: 'instant-withdrawal', description: 'Instantly withdraw stake from an existing gateway withdrawal vault', options: addressAndVaultIdOptions, action: instantWithdrawal, }); makeCommand({ name: 'cancel-withdrawal', description: 'Cancel a pending gateway withdrawal vault', options: addressAndVaultIdOptions, action: cancelWithdrawal, }); makeCommand({ name: 'delegate-stake', description: 'Delegate stake to a gateway', options: delegateStakeOptions, action: delegateStake, }); makeCommand({ name: 'decrease-delegate-stake', description: 'Decrease delegated stake', options: decreaseDelegateStakeOptions, action: decreaseDelegateStake, }); makeCommand({ name: 'redelegate-stake', description: 'Redelegate stake to another gateway', options: redelegateStakeOptions, action: redelegateStake, }); makeCommand({ name: 'buy-record', description: 'Buy a record', options: buyRecordOptions, action: buyRecordCLICommand, }); makeCommand({ name: 'upgrade-record', description: 'Upgrade the lease of a record to a permabuy', options: arnsPurchaseOptions, action: upgradeRecordCLICommand, }); makeCommand({ name: 'extend-lease', description: 'Extend the lease of a record', options: [...arnsPurchaseOptions, optionMap.years], action: extendLeaseCLICommand, }); makeCommand({ name: 'increase-undername-limit', description: 'Increase the limit of a name', options: [...arnsPurchaseOptions, optionMap.increaseCount], action: increaseUndernameLimitCLICommand, }); makeCommand({ name: 'request-primary-name', description: 'Request a primary name', options: arnsPurchaseOptions, action: requestPrimaryNameCLICommand, }); // # ANTS // # Getters makeCommand({ name: 'get-ant-state', description: 'Get the state of an ANT process', options: [optionMap.processId], action: async (options) => { return readANTFromOptions(options).getState(); }, }); makeCommand({ name: 'get-ant-info', description: 'Get the info of an ANT process', options: [optionMap.processId], action: async (options) => { return readANTFromOptions(options).getInfo(); }, }); makeCommand({ name: 'get-ant-record', description: 'Get a record of an ANT process', options: [optionMap.processId, optionMap.undername], action: async (options) => { return ((await readANTFromOptions(options).getRecord({ undername: requiredStringFromOptions(options, 'undername'), })) ?? { message: 'No record found' }); }, }); makeCommand({ name: 'get-ant-owner', description: 'Get the owner of an ANT process', options: [optionMap.processId], action: async (options) => { return readANTFromOptions(options).getOwner(); }, }); makeCommand({ name: 'get-ant-name', description: 'Get the name of an ANT process', options: [optionMap.processId], action: async (options) => { return readANTFromOptions(options).getName(); }, }); makeCommand({ name: 'get-ant-ticker', description: 'Get the ticker of an ANT process', options: [optionMap.processId], action: async (options) => { return readANTFromOptions(options).getTicker(); }, }); makeCommand({ name: 'get-ant-balance', description: 'Get the balance of an ANT process', options: [optionMap.processId, optionMap.address], action: async (options) => { return readANTFromOptions(options).getBalance({ address: requiredAddressFromOptions(options), }); }, }); // # Spawn makeCommand({ name: 'spawn-ant', description: 'Spawn an ANT process', options: antStateOptions, action: async (options) => { const state = getANTStateFromOptions(options); const antProcessId = await spawnANT({ state, signer: requiredAoSignerFromOptions(options), logger: getLoggerFromOptions(options), }); return { processId: antProcessId, state, message: `Spawned ANT process with process ID ${antProcessId}`, }; }, }); // # ANT Paginated Handlers makeCommand({ name: 'list-ant-records', description: 'Get the records of an ANT process', options: [optionMap.processId], action: async (options) => { return readANTFromOptions(options).getRecords(); }, }); makeCommand({ name: 'list-ant-controllers', description: 'List the controllers of an ANT process', options: [optionMap.processId], action: async (options) => { return readANTFromOptions(options).getControllers(); }, }); makeCommand({ name: 'list-ant-balances', description: 'Get the balances of an ANT process', options: [optionMap.processId], action: async (options) => { return readANTFromOptions(options).getBalances(); }, }); // # Actions makeCommand({ name: 'transfer-ant-ownership', description: 'Transfer ownership of an ANT process', options: [optionMap.processId, optionMap.target, ...writeActionOptions], action: async (options) => { const target = requiredStringFromOptions(options, 'target'); await assertConfirmationPrompt(`Are you sure you want to transfer ANT ownership to ${target}?`, options); return writeANTFromOptions(options).transfer({ target, }, customTagsFromOptions(options)); }, }); makeCommand({ name: 'add-ant-controller', description: 'Add a controller to an ANT process', options: [optionMap.processId, optionMap.controller, ...writeActionOptions], action: async (options) => { const controller = requiredStringFromOptions(options, 'controller'); await assertConfirmationPrompt(`Are you sure you want to add ${controller} as a controller?`, options); return writeANTFromOptions(options).addController({ controller: requiredStringFromOptions(options, 'controller'), }, customTagsFromOptions(options)); }, }); makeCommand({ name: 'remove-ant-controller', description: 'Remove a controller from an ANT process', options: [optionMap.processId, optionMap.controller, ...writeActionOptions], action: async (options) => { return writeANTFromOptions(options).removeController({ controller: requiredStringFromOptions(options, 'controller'), }, customTagsFromOptions(options)); }, }); makeCommand({ name: 'remove-ant-record', description: 'Remove a record from an ANT process', options: [optionMap.processId, optionMap.undername, ...writeActionOptions], action: async (options) => { const undername = requiredStringFromOptions(options, 'undername'); await assertConfirmationPrompt(`Are you sure you want to remove the record with undername ${undername}?`, options); return writeANTFromOptions(options).removeRecord({ undername, }, customTagsFromOptions(options)); }, }); makeCommand({ name: 'set-ant-record', description: 'Set a record of an ANT process. Deprecated: use set-ant-base-name and set-ant-undername', options: setAntUndernameOptions, action: setAntRecordCLICommand, }); makeCommand({ name: 'set-ant-base-name', description: 'Set the base name of an ANT process', options: setAntBaseNameOptions, action: setAntBaseNameCLICommand, }); makeCommand({ name: 'set-ant-undername', description: 'Set an undername of an ANT process', options: setAntUndernameOptions, action: setAntRecordCLICommand, }); makeCommand({ name: 'set-ant-ticker', description: 'Set the ticker of an ANT process', options: [optionMap.processId, optionMap.ticker, ...writeActionOptions], action: async (options) => { const ticker = requiredStringFromOptions(options, 'ticker'); await assertConfirmationPrompt(`Are you sure you want to set the ticker to ${ticker}?`, options); return writeANTFromOptions(options).setTicker({ ticker, }, customTagsFromOptions(options)); }, }); makeCommand({ name: 'set-ant-name', description: 'Set the name of an ANT process', options: [optionMap.processId, optionMap.name, ...writeActionOptions], action: async (options) => { const name = requiredStringFromOptions(options, 'name'); await assertConfirmationPrompt(`Are you sure you want to set the name to ${requiredStringFromOptions(options, 'name')}?`, options); return writeANTFromOptions(options).setName({ name, }, customTagsFromOptions(options)); }, }); makeCommand({ name: 'set-ant-description', description: 'Set the description of an ANT process', options: [optionMap.processId, optionMap.description, ...writeActionOptions], action: async (options) => { const description = requiredStringFromOptions(options, 'description'); await assertConfirmationPrompt(`Are you sure you want to set the ANT description to ${description}?`, options); return writeANTFromOptions(options).setDescription({ description, }, customTagsFromOptions(options)); }, }); makeCommand({ name: 'set-ant-keywords', description: 'Set the keywords of an ANT process', options: [optionMap.processId, optionMap.keywords, ...writeActionOptions], action: async (options) => { const keywords = requiredStringArrayFromOptions(options, 'keywords'); await assertConfirmationPrompt(`Are you sure you want to set the ANT keywords to ${keywords}?`, options); return writeANTFromOptions(options).setKeywords({ keywords, }, customTagsFromOptions(options)); }, }); makeCommand({ name: 'set-ant-logo', description: 'Set the logo of an ANT process', options: [ optionMap.processId, optionMap.transactionId, ...writeActionOptions, ], action: async (options) => { const txId = requiredStringFromOptions(options, 'transactionId'); await assertConfirmationPrompt(`Are you sure you want to set the ANT logo to target Arweave TxID ${txId}?`, options); return writeANTFromOptions(options).setLogo({ txId, }, customTagsFromOptions(options)); }, }); // # ARIO Actions makeCommand({ name: 'release-name', description: 'Release the name of an ANT process', options: [optionMap.processId, optionMap.name, ...writeActionOptions], action: async (options) => { const name = requiredStringFromOptions(options, 'name'); await assertConfirmationPrompt(`Are you sure you want to release the name ${name} back to the protocol?`, options); return writeANTFromOptions(options).releaseName({ name, arioProcessId: arioProcessIdFromOptions(options), }, customTagsFromOptions(options)); }, }); makeCommand({ name: 'reassign-name', description: 'Reassign the name of an ANT process to another ANT process', options: [ optionMap.processId, optionMap.name, optionMap.target, ...writeActionOptions, ], action: async (options) => { const targetProcess = requiredStringFromOptions(options, 'target'); const name = requiredStringFromOptions(options, 'name'); await assertConfirmationPrompt(`Are you sure you want to reassign the name ${name} to ANT process ${targetProcess}?`, options); return writeANTFromOptions(options).reassignName({ name, arioProcessId: arioProcessIdFromOptions(options), antProcessId: targetProcess, }, customTagsFromOptions(options)); }, }); makeCommand({ name: 'approve-primary-name-request', description: 'Approve a primary name request', options: [ optionMap.processId, optionMap.name, optionMap.address, ...writeActionOptions, ], action: async (options) => { const address = requiredAddressFromOptions(options); const name = requiredStringFromOptions(options, 'name'); await assertConfirmationPrompt(`Are you sure you want to approve the primary name request ${name} to ${address}?`, options); return writeANTFromOptions(options).approvePrimaryNameRequest({ name, address, arioProcessId: arioProcessIdFromOptions(options), }, customTagsFromOptions(options)); }, }); makeCommand({ name: 'remove-primary-names', description: 'Remove primary names', options: [optionMap.processId, optionMap.names, ...writeActionOptions], action: async (options) => { const names = requiredStringArrayFromOptions(options, 'names'); await assertConfirmationPrompt(`Are you sure you want to remove the primary names ${names}?`, options); return writeANTFromOptions(options).removePrimaryNames({ names, arioProcessId: arioProcessIdFromOptions(options), }, customTagsFromOptions(options)); }, }); // # Utilities makeCommand({ name: 'write-action', description: 'Send a write action to an AO Process', options: [...writeActionOptions, optionMap.processId], action: async (options) => { const process = new AOProcess({ processId: requiredProcessIdFromOptions(options), logger: getLoggerFromOptions(options), }); return process.send({ tags: customTagsFromOptions(options).tags ?? [], signer: requiredAoSignerFromOptions(options), }); }, }); makeCommand({ name: 'read-action', description: 'Send a dry-run read action to an AO Process', options: [optionMap.processId, optionMap.tags], action: async (options) => { const process = new AOProcess({ processId: requiredProcessIdFromOptions(options), logger: getLoggerFromOptions(options), }); return process.read({ tags: customTagsFromOptions(options).tags ?? [], }); }, }); if (process.argv[1].includes('bin/ar.io') || // Running from global .bin process.argv[1].includes('cli/cli') // Running from source ) { program.parse(process.argv); }