@ar.io/sdk
Version:
[](https://codecov.io/gh/ar-io/ar-io-sdk)
527 lines (526 loc) • 19.2 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.defaultTtlSecondsCLI = void 0;
exports.stringifyJsonForCLIDisplay = stringifyJsonForCLIDisplay;
exports.runCommand = runCommand;
exports.applyOptions = applyOptions;
exports.makeCommand = makeCommand;
exports.arioProcessIdFromOptions = arioProcessIdFromOptions;
exports.requiredJwkFromOptions = requiredJwkFromOptions;
exports.jwkToAddress = jwkToAddress;
exports.getLoggerFromOptions = getLoggerFromOptions;
exports.readARIOFromOptions = readARIOFromOptions;
exports.contractSignerFromOptions = contractSignerFromOptions;
exports.requiredContractSignerFromOptions = requiredContractSignerFromOptions;
exports.requiredAoSignerFromOptions = requiredAoSignerFromOptions;
exports.writeARIOFromOptions = writeARIOFromOptions;
exports.formatARIOWithCommas = formatARIOWithCommas;
exports.formatMARIOToARIOWithCommas = formatMARIOToARIOWithCommas;
exports.addressFromOptions = addressFromOptions;
exports.requiredAddressFromOptions = requiredAddressFromOptions;
exports.paginationParamsFromOptions = paginationParamsFromOptions;
exports.epochInputFromOptions = epochInputFromOptions;
exports.requiredInitiatorFromOptions = requiredInitiatorFromOptions;
exports.customTagsFromOptions = customTagsFromOptions;
exports.gatewaySettingsFromOptions = gatewaySettingsFromOptions;
exports.requiredTargetAndQuantityFromOptions = requiredTargetAndQuantityFromOptions;
exports.redelegateParamsFromOptions = redelegateParamsFromOptions;
exports.recordTypeFromOptions = recordTypeFromOptions;
exports.requiredMARIOFromOptions = requiredMARIOFromOptions;
exports.assertEnoughBalanceForArNSPurchase = assertEnoughBalanceForArNSPurchase;
exports.assertEnoughMARIOBalance = assertEnoughMARIOBalance;
exports.confirmationPrompt = confirmationPrompt;
exports.assertConfirmationPrompt = assertConfirmationPrompt;
exports.requiredProcessIdFromOptions = requiredProcessIdFromOptions;
exports.readANTFromOptions = readANTFromOptions;
exports.writeANTFromOptions = writeANTFromOptions;
exports.booleanFromOptions = booleanFromOptions;
exports.requiredStringFromOptions = requiredStringFromOptions;
exports.stringArrayFromOptions = stringArrayFromOptions;
exports.requiredStringArrayFromOptions = requiredStringArrayFromOptions;
exports.positiveIntegerFromOptions = positiveIntegerFromOptions;
exports.requiredPositiveIntegerFromOptions = requiredPositiveIntegerFromOptions;
exports.getANTStateFromOptions = getANTStateFromOptions;
exports.getTokenCostParamsFromOptions = getTokenCostParamsFromOptions;
exports.fundFromFromOptions = fundFromFromOptions;
exports.referrerFromOptions = referrerFromOptions;
exports.assertLockLengthInRange = assertLockLengthInRange;
/**
* 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.
*/
const arbundles_1 = require("@dha-team/arbundles");
const aoconnect_1 = require("@permaweb/aoconnect");
const commander_1 = require("commander");
const fs_1 = require("fs");
const prompts_1 = __importDefault(require("prompts"));
const index_js_1 = require("../node/index.js");
const options_js_1 = require("./options.js");
exports.defaultTtlSecondsCLI = 3600;
function stringifyJsonForCLIDisplay(json) {
return JSON.stringify(json, null, 2);
}
function logCommandOutput(output) {
console.log(stringifyJsonForCLIDisplay(output));
}
function exitWithErrorLog(error, debug = false) {
let errorLog;
if (error instanceof Error) {
errorLog = error.message;
if (debug && error.stack !== undefined) {
errorLog = error.stack;
}
}
else {
errorLog = stringifyJsonForCLIDisplay(error);
}
console.error(errorLog);
process.exit(1);
}
async function runCommand(command, action) {
const options = command.optsWithGlobals();
try {
const output = await action(options);
logCommandOutput(output);
process.exit(0);
}
catch (error) {
exitWithErrorLog(error, options.debug);
}
}
function applyOptions(command, options) {
[...options].forEach((option) => {
command.option(option.alias, option.description, option.default);
});
return command;
}
function makeCommand({ description, name, options = [], action, }) {
const command = commander_1.program.command(name).description(description);
const appliedCommand = applyOptions(command, [...options, ...options_js_1.globalOptions]);
if (action !== undefined) {
appliedCommand.action(() => runCommand(appliedCommand, action));
}
return appliedCommand;
}
function arioProcessIdFromOptions({ arioProcessId, devnet, testnet, }) {
if (arioProcessId !== undefined) {
return arioProcessId;
}
if (devnet) {
return index_js_1.ARIO_DEVNET_PROCESS_ID;
}
if (testnet) {
return index_js_1.ARIO_TESTNET_PROCESS_ID;
}
return index_js_1.ARIO_MAINNET_PROCESS_ID;
}
function walletFromOptions({ privateKey, walletFile, }) {
if (privateKey !== undefined) {
return JSON.parse(privateKey);
}
if (walletFile !== undefined) {
return JSON.parse((0, fs_1.readFileSync)(walletFile, 'utf-8'));
}
return undefined;
}
function requiredJwkFromOptions(options) {
const jwk = walletFromOptions(options);
if (jwk === undefined) {
throw new Error('No JWK provided for signing!\nPlease provide a stringified JWK with `--private-key` or the file path of a jwk.json file with `--wallet-file`');
}
return jwk;
}
function jwkToAddress(jwk) {
return (0, index_js_1.sha256B64Url)((0, index_js_1.fromB64Url)(jwk.n));
}
function setLoggerIfDebug(options) {
if (options.debug) {
index_js_1.Logger.default.setLogLevel('debug');
}
}
function getLoggerFromOptions(options) {
setLoggerIfDebug(options);
return index_js_1.Logger.default;
}
function aoProcessFromOptions(options) {
return new index_js_1.AOProcess({
processId: arioProcessIdFromOptions(options),
ao: (0, aoconnect_1.connect)({
MODE: 'legacy',
CU_URL: options.cuUrl,
}),
});
}
function readARIOFromOptions(options) {
setLoggerIfDebug(options);
return index_js_1.ARIO.init({
process: aoProcessFromOptions({
cuUrl: 'https://cu.ardrive.io', // default to ardrive cu for ARIO process
...options,
}),
paymentUrl: options.paymentUrl,
});
}
function contractSignerFromOptions(options) {
const wallet = walletFromOptions(options);
if (wallet === undefined) {
return undefined;
}
const token = options.token ?? 'arweave';
if (token === 'ethereum') {
const signer = new arbundles_1.EthereumSigner(wallet);
// For EthereumSigner, we need to convert the JWK to a string
return { signer, signerAddress: signer.publicKey.toString('hex') };
}
// TODO: Support other wallet types
const signer = new index_js_1.ArweaveSigner(wallet);
return { signer, signerAddress: jwkToAddress(wallet) };
}
function requiredContractSignerFromOptions(options) {
const contractSigner = contractSignerFromOptions(options);
if (contractSigner === undefined) {
throw new Error('No signer provided for signing!\nPlease provide a stringified JWK or Ethereum private key with `--private-key` or the file path of an arweave.jwk.json or eth.private.key.txt file with `--wallet-file`');
}
return contractSigner;
}
function requiredAoSignerFromOptions(options) {
return (0, index_js_1.createAoSigner)(requiredContractSignerFromOptions(options).signer);
}
function writeARIOFromOptions(options) {
const { signer, signerAddress } = requiredContractSignerFromOptions(options);
setLoggerIfDebug(options);
return {
ario: index_js_1.ARIO.init({
process: aoProcessFromOptions(options),
signer,
paymentUrl: options.paymentUrl,
}),
signerAddress,
};
}
function formatARIOWithCommas(value) {
const [integerPart, decimalPart] = value.toString().split('.');
const integerWithCommas = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
if (decimalPart === undefined) {
return integerWithCommas;
}
return integerWithCommas + '.' + decimalPart;
}
function formatMARIOToARIOWithCommas(value) {
return formatARIOWithCommas(value.toARIO());
}
/** helper to get address from --address option first, then check wallet options */
function addressFromOptions(options) {
if (options.address !== undefined) {
return options.address;
}
const signer = contractSignerFromOptions(options);
if (signer !== undefined) {
return signer.signerAddress;
}
return undefined;
}
function requiredAddressFromOptions(options) {
const address = addressFromOptions(options);
if (address !== undefined) {
return address;
}
throw new Error('No address provided. Use --address or --wallet-file');
}
const defaultCliPaginationLimit = 10; // more friendly UX than 100
function paginationParamsFromOptions(options) {
const { cursor, limit, sortBy, sortOrder } = options;
if (sortOrder !== undefined && !['asc', 'desc'].includes(sortOrder)) {
throw new Error(`Invalid sort order: ${sortOrder}, must be "asc" or "desc"`);
}
const numberLimit = limit !== undefined ? +limit : defaultCliPaginationLimit;
if (isNaN(numberLimit) || numberLimit <= 0) {
throw new Error(`Invalid limit: ${numberLimit}, must be a positive number`);
}
return {
cursor,
limit: numberLimit,
sortBy: sortBy,
sortOrder,
};
}
function epochInputFromOptions(options) {
if (options.epochIndex !== undefined) {
return { epochIndex: +options.epochIndex };
}
if (options.timestamp !== undefined) {
return { timestamp: +options.timestamp };
}
return undefined;
}
function requiredInitiatorFromOptions(options) {
if (options.initiator !== undefined) {
return options.initiator;
}
return requiredAddressFromOptions(options);
}
function customTagsFromOptions(options) {
if (options.tags === undefined) {
return {};
}
if (!Array.isArray(options.tags)) {
throw new Error('Tags must be an array');
}
if (options.tags.length === 0) {
return {};
}
if (options.tags.length % 2 !== 0) {
throw new Error('Tags must be an array of key-value pairs');
}
const tags = [];
for (let i = 0; i < options.tags.length; i += 2) {
tags.push({
name: options.tags[i],
value: options.tags[i + 1],
});
}
return {
tags,
};
}
function gatewaySettingsFromOptions({ allowDelegatedStaking, autoStake, delegateRewardShareRatio, fqdn, label, minDelegatedStake, note, observerAddress, port, properties, allowedDelegates, }) {
return {
observerAddress,
allowDelegatedStaking,
autoStake,
delegateRewardShareRatio: delegateRewardShareRatio !== undefined
? +delegateRewardShareRatio
: undefined,
allowedDelegates,
fqdn,
label,
minDelegatedStake: minDelegatedStake !== undefined ? +minDelegatedStake : undefined,
note,
port: port !== undefined ? +port : undefined,
properties,
};
}
function requiredTargetAndQuantityFromOptions(options) {
if (options.target === undefined) {
throw new Error('No target provided. Use --target');
}
if (options.quantity === undefined) {
throw new Error('No quantity provided. Use --quantity');
}
return {
target: options.target,
arioQuantity: new index_js_1.ARIOToken(+options.quantity),
};
}
function redelegateParamsFromOptions(options) {
const { target, arioQuantity: aRIOQuantity } = requiredTargetAndQuantityFromOptions(options);
const source = options.source;
if (source === undefined) {
throw new Error('No source provided. Use --source');
}
return {
target,
source,
vaultId: options.vaultId,
stakeQty: aRIOQuantity.toMARIO(),
};
}
function recordTypeFromOptions(options) {
options.type ??= 'lease';
if (options.type !== 'lease' && options.type !== 'permabuy') {
throw new Error(`Invalid type. Valid types are: lease, permabuy`);
}
return options.type;
}
function requiredMARIOFromOptions(options, key) {
if (options[key] === undefined) {
throw new Error(`No ${key} provided. Use --${key} denominated in ARIO`);
}
return new index_js_1.ARIOToken(+options[key]).toMARIO();
}
async function assertEnoughBalanceForArNSPurchase({ ario, address, costDetailsParams, }) {
if (costDetailsParams.fundFrom === 'turbo') {
// TODO: Get turbo balance and assert it is enough -- retain paid-by from balance result and pass to CLI logic
return;
}
const costDetails = await ario.getCostDetails(costDetailsParams);
if (costDetails.fundingPlan) {
if (costDetails.fundingPlan.shortfall > 0) {
throw new Error(`Insufficient balance for action. Shortfall: ${formatMARIOToARIOWithCommas(new index_js_1.mARIOToken(costDetails.fundingPlan.shortfall))}\n${JSON.stringify(costDetails, null, 2)}`);
}
}
else {
await assertEnoughMARIOBalance({
ario,
address,
mARIOQuantity: costDetails.tokenCost,
});
}
}
async function assertEnoughMARIOBalance({ address, ario, mARIOQuantity, }) {
if (typeof mARIOQuantity === 'number') {
mARIOQuantity = new index_js_1.mARIOToken(mARIOQuantity);
}
const balance = await ario.getBalance({ address });
if (balance < mARIOQuantity.valueOf()) {
throw new Error(`Insufficient ARIO balance for action. Balance available: ${formatMARIOToARIOWithCommas(new index_js_1.mARIOToken(balance))} ARIO`);
}
}
async function confirmationPrompt(message) {
const { confirm } = await (0, prompts_1.default)({
type: 'confirm',
name: 'confirm',
message,
});
return confirm;
}
async function assertConfirmationPrompt(message, options) {
if (options.skipConfirmation) {
return true;
}
return confirmationPrompt(message);
}
function requiredProcessIdFromOptions(o) {
if (o.processId === undefined) {
throw new Error('--process-id is required');
}
return o.processId;
}
function ANTProcessFromOptions(options) {
return new index_js_1.AOProcess({
processId: requiredProcessIdFromOptions(options),
ao: (0, aoconnect_1.connect)({
MODE: 'legacy',
CU_URL: options.cuUrl,
}),
});
}
function readANTFromOptions(options) {
return index_js_1.ANT.init({
process: ANTProcessFromOptions(options),
});
}
function writeANTFromOptions(options, signer) {
signer ??= requiredContractSignerFromOptions(options).signer;
return index_js_1.ANT.init({
process: ANTProcessFromOptions(options),
signer,
});
}
function booleanFromOptions(options, key) {
return !!options[key];
}
function requiredStringFromOptions(options, key) {
const value = options[key];
if (value === undefined) {
throw new Error(`--${key} is required`);
}
return value;
}
function stringArrayFromOptions(options, key) {
const value = options[key];
if (value === undefined) {
return undefined;
}
if (!Array.isArray(value)) {
throw new Error(`--${key} must be an array`);
}
return value;
}
function requiredStringArrayFromOptions(options, key) {
const value = stringArrayFromOptions(options, key);
if (value === undefined) {
throw new Error(`--${key} is required`);
}
return value;
}
function positiveIntegerFromOptions(options, key) {
const value = options[key];
if (value === undefined) {
return undefined;
}
const numberValue = +value;
if (isNaN(numberValue) || numberValue <= 0) {
throw new Error(`Invalid ${key}: ${value}, must be a positive number`);
}
return numberValue;
}
function requiredPositiveIntegerFromOptions(options, key) {
const value = positiveIntegerFromOptions(options, key);
if (value === undefined) {
throw new Error(`--${key} is required`);
}
return value;
}
function getANTStateFromOptions(options) {
return (0, index_js_1.initANTStateForAddress)({
owner: requiredAddressFromOptions(options),
targetId: options.target,
controllers: options.controllers,
description: options.description,
ticker: options.ticker,
name: options.name,
keywords: options.keywords,
logo: options.logo,
ttlSeconds: options.ttlSeconds !== undefined
? +options.ttlSeconds
: exports.defaultTtlSecondsCLI,
});
}
function getTokenCostParamsFromOptions(o) {
o.intent ??= 'Buy-Name';
o.type ??= 'lease';
o.years ??= '1';
if (!(0, index_js_1.isValidIntent)(o.intent)) {
throw new Error(`Invalid intent. Valid intents are: ${index_js_1.validIntents.join(', ')}`);
}
if (o.type !== 'lease' && o.type !== 'permabuy') {
throw new Error(`Invalid type. Valid types are: lease, permabuy`);
}
return {
type: o.type,
quantity: o.quantity !== undefined ? +o.quantity : undefined,
years: +o.years,
intent: o.intent,
name: requiredStringFromOptions(o, 'name'),
fromAddress: addressFromOptions(o),
};
}
function fundFromFromOptions(o) {
if (o.fundFrom !== undefined) {
if (!(0, index_js_1.isValidFundFrom)(o.fundFrom)) {
throw new Error(`Invalid fund from: ${o.fundFrom}. Please use one of ${index_js_1.fundFromOptions.join(', ')}`);
}
}
return o.fundFrom ?? 'balance';
}
function referrerFromOptions(o) {
return o.referrer;
}
function assertLockLengthInRange(lockLengthMs, assertMin = true) {
const minLockLengthMs = 1209600000; // 14 days
const maxLockLengthMs = 378432000000; // ~12 years
if (lockLengthMs > maxLockLengthMs) {
throw new Error(`Lock length must be at most 12 years (378432000000 ms). Provided lock length: ${lockLengthMs} ms`);
}
if (!assertMin) {
return;
}
if (lockLengthMs < minLockLengthMs) {
throw new Error(`Lock length must be at least 14 days (1209600000 ms). Provided lock length: ${lockLengthMs} ms`);
}
}
;