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)

1,312 lines (1,311 loc) 55.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ARIOWriteable = exports.ARIOReadable = exports.ARIO = void 0; /** * 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 aoconnect_1 = require("@permaweb/aoconnect"); const constants_js_1 = require("../constants.js"); const index_js_1 = require("../types/index.js"); const ao_js_1 = require("../utils/ao.js"); const arweave_js_1 = require("../utils/arweave.js"); const ant_registry_js_1 = require("./ant-registry.js"); const ant_js_1 = require("./ant.js"); const arweave_js_2 = require("./arweave.js"); const ao_process_js_1 = require("./contracts/ao-process.js"); const error_js_1 = require("./error.js"); const faucet_js_1 = require("./faucet.js"); const hb_js_1 = require("./hyperbeam/hb.js"); const logger_js_1 = require("./logger.js"); const turbo_js_1 = require("./turbo.js"); class ARIO { // Implementation static init(config) { if (config !== undefined && 'signer' in config) { return new ARIOWriteable(config); } return new ARIOReadable(config); } static mainnet(config) { if (config !== undefined && 'signer' in config) { return new ARIOWriteable({ ...config, process: new ao_process_js_1.AOProcess({ processId: constants_js_1.ARIO_MAINNET_PROCESS_ID, ao: (0, aoconnect_1.connect)({ MODE: 'legacy', CU_URL: 'https://cu.ardrive.io', ...config?.process?.ao, }), }), }); } return new ARIOReadable({ ...config, process: new ao_process_js_1.AOProcess({ processId: constants_js_1.ARIO_MAINNET_PROCESS_ID, ao: (0, aoconnect_1.connect)({ CU_URL: 'https://cu.ardrive.io', MODE: 'legacy', ...config?.process?.ao, }), }), }); } static testnet(config) { if (config !== undefined && 'signer' in config) { return (0, faucet_js_1.createFaucet)({ arioInstance: new ARIOWriteable({ ...config, process: new ao_process_js_1.AOProcess({ processId: constants_js_1.ARIO_TESTNET_PROCESS_ID, ao: (0, aoconnect_1.connect)({ MODE: 'legacy', CU_URL: 'https://cu.ardrive.io', ...config?.process?.ao, }), }), }), faucetApiUrl: config?.faucetUrl, }); } return (0, faucet_js_1.createFaucet)({ arioInstance: new ARIOReadable({ ...config, process: new ao_process_js_1.AOProcess({ processId: constants_js_1.ARIO_TESTNET_PROCESS_ID, ao: (0, aoconnect_1.connect)({ MODE: 'legacy', CU_URL: 'https://cu.ardrive.io', ...config?.process?.ao, }), }), }), faucetApiUrl: config?.faucetUrl, }); } } exports.ARIO = ARIO; class ARIOReadable { process; epochSettings; arweave; hyperbeamUrl; paymentProvider; // TODO: this could be an array/map of payment providers logger = logger_js_1.Logger.default; hb; constructor(config) { this.arweave = config?.arweave ?? arweave_js_2.defaultArweave; if (config === undefined || Object.keys(config).length === 0) { this.process = new ao_process_js_1.AOProcess({ processId: constants_js_1.ARIO_MAINNET_PROCESS_ID, }); } else if ((0, index_js_1.isProcessConfiguration)(config)) { this.process = config.process; } else if ((0, index_js_1.isProcessIdConfiguration)(config)) { this.process = new ao_process_js_1.AOProcess({ processId: config.processId, }); } else { throw new error_js_1.InvalidContractConfigurationError(); } // only use hyperbeam if the client has provided a hyperbeamUrl // this will avoid overwhelming the HyperBeam node with requests // as we shift using HyperBEAM for all ANT operations if (config?.hyperbeamUrl !== undefined) { this.hyperbeamUrl = config.hyperbeamUrl; this.hb = new hb_js_1.HB({ url: this.hyperbeamUrl, processId: this.process.processId, }); this.logger.debug(`Using HyperBEAM node for process ${this.process.processId}`, { hyperbeamUrl: this.hyperbeamUrl, }); } this.paymentProvider = turbo_js_1.TurboArNSPaymentFactory.init({ paymentUrl: config?.paymentUrl, }); } async getInfo() { return this.process.read({ tags: [{ name: 'Action', value: 'Info' }], }); } async getTokenSupply() { return this.process.read({ tags: [{ name: 'Action', value: 'Total-Token-Supply' }], }); } async computeEpochIndexForTimestamp(timestamp) { const epochSettings = await this.getEpochSettings(); const epochZeroStartTimestamp = epochSettings.epochZeroStartTimestamp; const epochLengthMs = epochSettings.durationMs; return Math.floor((timestamp - epochZeroStartTimestamp) / epochLengthMs); } async computeCurrentEpochIndex() { return this.computeEpochIndexForTimestamp(Date.now()); } async computeEpochIndex(params) { const epochIndex = params?.epochIndex; if (epochIndex !== undefined) { return epochIndex; } const timestamp = params?.timestamp; if (timestamp !== undefined) { return this.computeEpochIndexForTimestamp(timestamp); } return undefined; } async getEpochSettings() { return (this.epochSettings ??= await this.process.read({ tags: [{ name: 'Action', value: 'Epoch-Settings' }], })); } async getEpoch(epoch) { const epochIndex = await this.computeEpochIndex(epoch); const currentIndex = await this.computeCurrentEpochIndex(); if (epochIndex !== undefined && epochIndex < currentIndex) { const epochData = await (0, arweave_js_1.getEpochDataFromGqlWithCUFallback)({ arweave: this.arweave, epochIndex: epochIndex, processId: this.process.processId, ao: this.process.ao, }); if (!epochData) { throw new Error('Epoch data not found for epoch index ' + epochIndex); } return (0, arweave_js_1.removeEligibleRewardsFromEpochData)(epochData); } // go to the process epoch and fetch the epoch data const allTags = [ { name: 'Action', value: 'Epoch' }, { name: 'Epoch-Index', value: currentIndex.toString(), }, ]; return this.process.read({ tags: (0, arweave_js_1.pruneTags)(allTags), }); } async getArNSRecord({ name }) { return this.process.read({ tags: [ { name: 'Action', value: 'Record' }, { name: 'Name', value: name }, ], }); } async getArNSRecords(params) { return this.process.read({ tags: [ { name: 'Action', value: 'Paginated-Records' }, ...(0, arweave_js_1.paginationParamsToTags)(params), ], }); } async getArNSReservedNames(params) { return this.process.read({ tags: [ { name: 'Action', value: 'Reserved-Names' }, ...(0, arweave_js_1.paginationParamsToTags)(params), ], }); } async getArNSReservedName({ name, }) { return this.process.read({ tags: [ { name: 'Action', value: 'Reserved-Name' }, { name: 'Name', value: name }, ], }); } async getBalance({ address }) { if (this.hb && (await this.hb.checkHyperBeamCompatibility())) { this.logger.debug('Getting balance from HyperBEAM', { address }); const res = await this.hb .compute({ path: `balances/${address}`, }) .then((res) => Number(res)) .catch((error) => { this.logger.error('Failed to get balance from HyperBEAM', { cause: error, }); return null; }); if (res !== null) return res; // else fall through to CU read this.logger.info('Failed to get balance from HyperBEAM, failing over to to CU read', { address }); } return this.process.read({ tags: [ { name: 'Action', value: 'Balance' }, { name: 'Address', value: address }, ], }); } async getBalances(params) { return this.process.read({ tags: [ { name: 'Action', value: 'Paginated-Balances' }, ...(0, arweave_js_1.paginationParamsToTags)(params), ], }); } async getVault({ address, vaultId, }) { return this.process.read({ tags: [ { name: 'Action', value: 'Vault' }, { name: 'Address', value: address }, { name: 'Vault-Id', value: vaultId }, ], }); } async getVaults(params) { return this.process.read({ tags: [ { name: 'Action', value: 'Paginated-Vaults' }, ...(0, arweave_js_1.paginationParamsToTags)(params), ], }); } async getGateway({ address, }) { return this.process.read({ tags: [ { name: 'Action', value: 'Gateway' }, { name: 'Address', value: address }, ], }); } async getGatewayDelegates({ address, ...pageParams }) { return this.process.read({ tags: [ { name: 'Action', value: 'Paginated-Delegates' }, { name: 'Address', value: address }, ...(0, arweave_js_1.paginationParamsToTags)(pageParams), ], }); } async getGatewayDelegateAllowList({ address, ...pageParams }) { return this.process.read({ tags: [ { name: 'Action', value: 'Paginated-Allowed-Delegates' }, { name: 'Address', value: address }, ...(0, arweave_js_1.paginationParamsToTags)(pageParams), ], }); } async getGateways(pageParams) { return this.process.read({ tags: [ { name: 'Action', value: 'Paginated-Gateways' }, ...(0, arweave_js_1.paginationParamsToTags)(pageParams), ], }); } async getCurrentEpoch() { return this.process.read({ tags: [{ name: 'Action', value: 'Epoch' }], }); } async getPrescribedObservers(epoch) { const epochIndex = await this.computeEpochIndex(epoch); const currentIndex = await this.computeCurrentEpochIndex(); if (epochIndex !== undefined && epochIndex < currentIndex) { const epochData = await (0, arweave_js_1.getEpochDataFromGqlWithCUFallback)({ ao: this.process.ao, arweave: this.arweave, epochIndex: epochIndex, processId: this.process.processId, }); if (!epochData) { throw new Error('Epoch data not found for epoch index ' + epochIndex); } return epochData.prescribedObservers; } const allTags = [ { name: 'Action', value: 'Epoch-Prescribed-Observers' }, { name: 'Epoch-Index', value: currentIndex.toString(), }, ]; return this.process.read({ tags: (0, arweave_js_1.pruneTags)(allTags), }); } async getPrescribedNames(epoch) { const epochIndex = await this.computeEpochIndex(epoch); const currentIndex = await this.computeCurrentEpochIndex(); if (epochIndex !== undefined && epochIndex < currentIndex) { const epochData = await (0, arweave_js_1.getEpochDataFromGqlWithCUFallback)({ arweave: this.arweave, epochIndex: epochIndex, processId: this.process.processId, ao: this.process.ao, }); if (!epochData) { throw new Error('Epoch data not found for epoch index ' + epochIndex); } return epochData.prescribedNames; } const allTags = [ { name: 'Action', value: 'Epoch-Prescribed-Names' }, { name: 'Epoch-Index', value: currentIndex.toString(), }, ]; return this.process.read({ tags: (0, arweave_js_1.pruneTags)(allTags), }); } // we need to find the epoch index for the epoch that is currently being distributed and fetch it from gql async getObservations(epoch) { const epochIndex = await this.computeEpochIndex(epoch); const currentIndex = await this.computeCurrentEpochIndex(); if (epochIndex !== undefined && epochIndex < currentIndex) { const epochData = await (0, arweave_js_1.getEpochDataFromGqlWithCUFallback)({ arweave: this.arweave, epochIndex: epochIndex, processId: this.process.processId, ao: this.process.ao, }); if (!epochData) { throw new Error('Epoch data not found for epoch index ' + epochIndex); } return epochData.observations; } // go to the process epoch and fetch the observations const allTags = [ { name: 'Action', value: 'Epoch-Observations' }, { name: 'Epoch-Index', value: currentIndex.toString(), }, ]; return this.process.read({ tags: (0, arweave_js_1.pruneTags)(allTags), }); } async getDistributions(epoch) { const epochIndex = await this.computeEpochIndex(epoch); const currentIndex = await this.computeCurrentEpochIndex(); if (epochIndex !== undefined && epochIndex < currentIndex) { const epochData = await (0, arweave_js_1.getEpochDataFromGqlWithCUFallback)({ arweave: this.arweave, epochIndex: epochIndex, processId: this.process.processId, ao: this.process.ao, }); if (epochData === undefined) { throw new Error('Epoch data not found for epoch index ' + epochIndex); } return epochData.distributions; } // go to the process epoch and fetch the distributions const allTags = [ { name: 'Action', value: 'Epoch-Distributions' }, { name: 'Epoch-Index', value: currentIndex.toString(), }, ]; return this.process.read({ tags: (0, arweave_js_1.pruneTags)(allTags), }); } async getEligibleEpochRewards(epoch, params) { const epochIndex = await this.computeEpochIndex(epoch); const currentIndex = await this.computeCurrentEpochIndex(); if (epochIndex !== undefined && epochIndex < currentIndex) { const epochData = await (0, arweave_js_1.getEpochDataFromGqlWithCUFallback)({ arweave: this.arweave, epochIndex: epochIndex, processId: this.process.processId, ao: this.process.ao, }); if (!epochData) { throw new Error('Epoch data not found for epoch index ' + epochIndex); } return (0, arweave_js_1.sortAndPaginateEpochDataIntoEligibleDistributions)(epochData, params); } // on current epoch, go to process and fetch the distributions const allTags = [ { name: 'Action', value: 'Epoch-Eligible-Rewards' }, ...(0, arweave_js_1.paginationParamsToTags)(params), ]; return this.process.read({ tags: (0, arweave_js_1.pruneTags)(allTags), }); } async getTokenCost({ intent, type, years, name, quantity, fromAddress, }) { const replacedBuyRecordWithBuyName = intent === 'Buy-Record' ? 'Buy-Name' : intent; const allTags = [ { name: 'Action', value: 'Token-Cost' }, { name: 'Intent', value: replacedBuyRecordWithBuyName, }, { name: 'Name', value: name, }, { name: 'Years', value: years?.toString(), }, { name: 'Quantity', value: quantity?.toString(), }, { name: 'Purchase-Type', value: type, }, ]; return this.process.read({ tags: (0, arweave_js_1.pruneTags)(allTags), fromAddress, }); } // TODO: Can overload this function to refine different types of cost details params async getCostDetails({ intent, type, years, name, quantity, fromAddress, fundFrom, }) { const replacedBuyRecordWithBuyName = intent === 'Buy-Record' ? 'Buy-Name' : intent; if (fundFrom === 'turbo') { const { mARIO, winc } = await this.paymentProvider.getArNSPriceDetails({ intent: replacedBuyRecordWithBuyName, name, quantity, type, years, }); return { tokenCost: mARIO.valueOf(), wincQty: winc, discounts: [], }; } const allTags = [ { name: 'Action', value: 'Cost-Details' }, { name: 'Intent', value: replacedBuyRecordWithBuyName, }, { name: 'Name', value: name, }, { name: 'Years', value: years?.toString(), }, { name: 'Quantity', value: quantity?.toString(), }, { name: 'Purchase-Type', value: type, }, { name: 'Fund-From', value: fundFrom, }, ]; return this.process.read({ tags: (0, arweave_js_1.pruneTags)(allTags), fromAddress, }); } async getRegistrationFees() { return this.process.read({ tags: [{ name: 'Action', value: 'Registration-Fees' }], }); } async getDemandFactor() { return this.process.read({ tags: [{ name: 'Action', value: 'Demand-Factor' }], }); } async getDemandFactorSettings() { return this.process.read({ tags: [{ name: 'Action', value: 'Demand-Factor-Settings' }], }); } async getArNSReturnedNames(params) { return this.process.read({ tags: [ { name: 'Action', value: 'Returned-Names' }, ...(0, arweave_js_1.paginationParamsToTags)(params), ], }); } async getArNSReturnedName({ name, }) { const allTags = [ { name: 'Action', value: 'Returned-Name' }, { name: 'Name', value: name }, ]; return this.process.read({ tags: allTags, }); } async getDelegations(params) { const allTags = [ { name: 'Action', value: 'Paginated-Delegations' }, { name: 'Address', value: params.address }, ...(0, arweave_js_1.paginationParamsToTags)(params), ]; return this.process.read({ tags: (0, arweave_js_1.pruneTags)(allTags), }); } async getAllowedDelegates(params) { return this.getGatewayDelegateAllowList(params); } async getGatewayVaults(params) { return this.process.read({ tags: [ { name: 'Action', value: 'Paginated-Gateway-Vaults' }, { name: 'Address', value: params.address }, ...(0, arweave_js_1.paginationParamsToTags)(params), ], }); } async getPrimaryNameRequest(params) { if (this.hb && (await this.hb.checkHyperBeamCompatibility())) { this.logger.debug('Getting primary name request from HyperBEAM', { initiator: params.initiator, }); const res = await this.hb .compute({ path: `/primary-names/requests/${params.initiator}`, }) .catch((error) => { this.logger.error('Failed to get primary name request from HyperBEAM', { cause: error, }); return null; }); if (res !== null) { // Ensure initiator is included in the result return { ...res, initiator: params.initiator, }; } // else fall through to CU read this.logger.info('Failed to get primary name request from HyperBEAM, failing over to CU read', { initiator: params.initiator }); } const allTags = [ { name: 'Action', value: 'Primary-Name-Request' }, { name: 'Initiator', value: params.initiator, }, ]; return this.process.read({ tags: allTags, }); } async getPrimaryNameRequests(params) { return this.process.read({ tags: [ { name: 'Action', value: 'Primary-Name-Requests' }, ...(0, arweave_js_1.paginationParamsToTags)(params), ], }); } async getPrimaryName(params) { if (this.hb && (await this.hb.checkHyperBeamCompatibility())) { this.logger.debug('Getting primary name from HyperBEAM', { params }); try { let owner; if ('name' in params) { // Step 1: Get owner from /primary-names/names/<name> owner = await this.hb.compute({ path: `/primary-names/names/${params.name}`, }); } else { // If given address, skip the /names/name query owner = params.address; } // Step 2: Get {name, startTimestamp} from /primary-names/owners/<owner> const ownerData = await this.hb.compute({ path: `/primary-names/owners/${owner}`, }); const name = ownerData.name; const startTimestamp = ownerData.startTimestamp; // Step 3: Get processId from getArNSRecord const record = await this.getArNSRecord({ name }); const processId = record.processId; // Combine all data const result = { owner, name, startTimestamp, processId, }; return result; } catch (error) { this.logger.error('Failed to get primary name from HyperBEAM', { cause: error, }); // Fall through to CU read this.logger.info('Failed to get primary name from HyperBEAM, failing over to CU read', { params }); } } const allTags = [ { name: 'Action', value: 'Primary-Name' }, { name: 'Address', value: params?.address, }, { name: 'Name', value: params?.name }, ]; return this.process.read({ tags: (0, arweave_js_1.pruneTags)(allTags), }); } async getPrimaryNames(params) { return this.process.read({ tags: [ { name: 'Action', value: 'Primary-Names' }, ...(0, arweave_js_1.paginationParamsToTags)(params), ], }); } /** * Get current redelegation fee percentage for address * * @param {Object} params - The parameters for fetching redelegation fee * @param {string} params.address - The address to fetch the fee for * @returns {Promise<AoMessageResult>} The redelegation fee result */ async getRedelegationFee(params) { return this.process.read({ tags: [ { name: 'Action', value: 'Redelegation-Fee' }, { name: 'Address', value: params.address }, ], }); } async getGatewayRegistrySettings() { return this.process.read({ tags: [{ name: 'Action', value: 'Gateway-Registry-Settings' }], }); } async getAllDelegates(params) { return this.process.read({ tags: [ { name: 'Action', value: 'All-Paginated-Delegates' }, ...(0, arweave_js_1.paginationParamsToTags)(params), ], }); } async getAllGatewayVaults(params) { return this.process.read({ tags: [ { name: 'Action', value: 'All-Gateway-Vaults' }, ...(0, arweave_js_1.paginationParamsToTags)(params), ], }); } async resolveArNSName({ name, }) { // derive baseName & undername using last underscore const lastUnderscore = name.lastIndexOf('_'); const baseName = lastUnderscore === -1 ? name : name.slice(lastUnderscore + 1); const undername = lastUnderscore === -1 ? '@' : name.slice(0, lastUnderscore); // guard against missing or unregistered ARNS record const nameData = await this.getArNSRecord({ name: baseName }); if (nameData === undefined || nameData.processId === undefined) { throw new Error(`Base ArNS name ${baseName} not found on ARIO contract (${this.process.processId}).`); } const ant = ant_js_1.ANT.init({ process: new ao_process_js_1.AOProcess({ ao: this.process.ao, processId: nameData.processId, }), hyperbeamUrl: this.hyperbeamUrl, }); const [owner, antRecord] = await Promise.all([ ant.getOwner(), ant.getRecord({ undername }), ]); if (antRecord === undefined) { throw new Error(`Record for ${undername} not found on ANT.`); } if (antRecord.ttlSeconds === undefined || antRecord.transactionId === undefined) { throw new Error(`Invalid record on ANT. Must have ttlSeconds and transactionId. Record: ${JSON.stringify(antRecord)}`); } return { name, owner, txId: antRecord.transactionId, ttlSeconds: antRecord.ttlSeconds, priority: antRecord.priority, // NOTE: we may want return the actual index of the record based on sorting // in case ANT tries to set duplicate priority values to get around undername limits processId: nameData.processId, undernameLimit: nameData.undernameLimit, type: nameData.type, }; } /** * Get all ARNS names associated with an address using the provided ANT registry address. * * By default it will use the mainnet ANT registry address. * * @param {Object} params - The parameters for fetching ARNS names * @param {string} params.address - The address to fetch the ARNS names for * @returns {Promise<AoArNSNameData[]>} The ARNS names associated with the address */ async getArNSRecordsForAddress(params) { const { antRegistryProcessId = constants_js_1.ANT_REGISTRY_ID, address } = params; const antRegistry = ant_registry_js_1.ANTRegistry.init({ hyperbeamUrl: this.hyperbeamUrl, process: new ao_process_js_1.AOProcess({ ao: this.process.ao, processId: antRegistryProcessId, }), }); // Note: there could be a race condition here if the ACL changes during pagination requests, resulting in different results from the `getArNSRecords`. // This is an unlikely scenario, so to give the client control, and keep this API consistent with other ArNS APIs, we refetch the ACL for each page, and // return paginated results. const { Controlled = [], Owned = [] } = await antRegistry.accessControlList({ address, }); const allProcessIds = new Set([...Controlled, ...Owned]); if (allProcessIds.size === 0) { return { items: [], hasMore: false, nextCursor: undefined, limit: params.limit ?? 1000, totalItems: 0, sortOrder: params.sortOrder ?? 'asc', }; } const currentPage = await this.getArNSRecords({ ...params, filters: { // NOTE: we confirmed that dry-runs are not limited to the same tag limits as data-items. // Should this change, we'll need to batch the requests. processId: Array.from(allProcessIds), }, }); return currentPage; } } exports.ARIOReadable = ARIOReadable; class ARIOWriteable extends ARIOReadable { signer; paymentProvider; constructor({ signer, paymentUrl, ...config }) { if (config === undefined) { super({ process: new ao_process_js_1.AOProcess({ processId: constants_js_1.ARIO_MAINNET_PROCESS_ID, }), }); } else { super(config); } this.signer = (0, ao_js_1.createAoSigner)(signer); this.paymentProvider = turbo_js_1.TurboArNSPaymentFactory.init({ signer: (0, turbo_js_1.isTurboArNSSigner)(signer) ? signer : undefined, paymentUrl, }); } async transfer({ target, qty, }, options) { const { tags = [] } = options || {}; return this.process.send({ tags: [ ...tags, { name: 'Action', value: 'Transfer' }, { name: 'Recipient', value: target, }, { name: 'Quantity', value: qty.valueOf().toString(), }, ], signer: this.signer, }); } async vaultedTransfer({ recipient, quantity, lockLengthMs, revokable = false, }, options) { const { tags = [] } = options || {}; return this.process.send({ tags: [ ...tags, { name: 'Action', value: 'Vaulted-Transfer' }, { name: 'Recipient', value: recipient }, { name: 'Quantity', value: quantity.toString() }, { name: 'Lock-Length', value: lockLengthMs.toString() }, { name: 'Revokable', value: `${revokable}` }, ], signer: this.signer, }); } async revokeVault({ vaultId, recipient }, options) { const { tags = [] } = options || {}; return this.process.send({ tags: [ ...tags, { name: 'Action', value: 'Revoke-Vault' }, { name: 'Vault-Id', value: vaultId }, { name: 'Recipient', value: recipient }, ], signer: this.signer, }); } async createVault({ lockLengthMs, quantity }, options) { const { tags = [] } = options || {}; return this.process.send({ tags: [ ...tags, { name: 'Action', value: 'Create-Vault' }, { name: 'Lock-Length', value: lockLengthMs.toString() }, { name: 'Quantity', value: quantity.toString() }, ], signer: this.signer, }); } async extendVault({ vaultId, extendLengthMs }, options) { const { tags = [] } = options || {}; return this.process.send({ tags: [ ...tags, { name: 'Action', value: 'Extend-Vault' }, { name: 'Vault-Id', value: vaultId }, { name: 'Extend-Length', value: extendLengthMs.toString() }, ], signer: this.signer, }); } async increaseVault({ vaultId, quantity }, options) { const { tags = [] } = options || {}; return this.process.send({ tags: [ ...tags, { name: 'Action', value: 'Increase-Vault' }, { name: 'Vault-Id', value: vaultId }, { name: 'Quantity', value: quantity.toString() }, ], signer: this.signer, }); } async joinNetwork({ operatorStake, allowDelegatedStaking, allowedDelegates, delegateRewardShareRatio, fqdn, label, minDelegatedStake, note, port, properties, protocol, autoStake, observerAddress, services, }, options) { const { tags = [] } = options || {}; const allTags = [ ...tags, { name: 'Action', value: 'Join-Network' }, { name: 'Operator-Stake', value: operatorStake.valueOf().toString(), }, { name: 'Allow-Delegated-Staking', value: allowDelegatedStaking?.toString(), }, { name: 'Allowed-Delegates', value: allowedDelegates?.join(','), }, { name: 'Delegate-Reward-Share-Ratio', value: delegateRewardShareRatio?.toString(), }, { name: 'FQDN', value: fqdn, }, { name: 'Label', value: label, }, { name: 'Min-Delegated-Stake', value: minDelegatedStake?.valueOf().toString(), }, { name: 'Note', value: note, }, { name: 'Port', value: port?.toString(), }, { name: 'Properties', value: properties, }, { name: 'Protocol', value: protocol, }, { name: 'Auto-Stake', value: autoStake?.toString(), }, { name: 'Observer-Address', value: observerAddress, }, { name: 'Services', value: services ? JSON.stringify(services) : undefined, }, ]; return this.process.send({ signer: this.signer, tags: (0, arweave_js_1.pruneTags)(allTags), }); } async leaveNetwork(options) { const { tags = [] } = options || {}; return this.process.send({ signer: this.signer, tags: [...tags, { name: 'Action', value: 'Leave-Network' }], }); } async updateGatewaySettings({ allowDelegatedStaking, allowedDelegates, delegateRewardShareRatio, fqdn, label, minDelegatedStake, note, port, properties, protocol, autoStake, observerAddress, services, }, options) { const { tags = [] } = options || {}; const allTags = [ ...tags, { name: 'Action', value: 'Update-Gateway-Settings' }, { name: 'Label', value: label }, { name: 'Note', value: note }, { name: 'FQDN', value: fqdn }, { name: 'Port', value: port?.toString() }, { name: 'Properties', value: properties }, { name: 'Protocol', value: protocol }, { name: 'Observer-Address', value: observerAddress }, { name: 'Allow-Delegated-Staking', value: allowDelegatedStaking?.toString(), }, { name: 'Allowed-Delegates', value: allowedDelegates?.join(','), }, { name: 'Delegate-Reward-Share-Ratio', value: delegateRewardShareRatio?.toString(), }, { name: 'Min-Delegated-Stake', value: minDelegatedStake?.valueOf().toString(), }, { name: 'Auto-Stake', value: autoStake?.toString() }, { name: 'Services', value: services ? JSON.stringify(services) : undefined, }, ]; return this.process.send({ signer: this.signer, tags: (0, arweave_js_1.pruneTags)(allTags), }); } async delegateStake(params, options) { const { tags = [] } = options || {}; return this.process.send({ signer: this.signer, tags: [ ...tags, { name: 'Action', value: 'Delegate-Stake' }, { name: 'Target', value: params.target }, { name: 'Quantity', value: params.stakeQty.valueOf().toString() }, ], }); } async decreaseDelegateStake(params, options) { const { tags = [] } = options || {}; return this.process.send({ signer: this.signer, tags: [ ...tags, { name: 'Action', value: 'Decrease-Delegate-Stake' }, { name: 'Target', value: params.target }, { name: 'Quantity', value: params.decreaseQty.valueOf().toString() }, { name: 'Instant', value: `${params.instant || false}` }, ], }); } /** * Initiates an instant withdrawal from a gateway. * * @param {Object} params - The parameters for initiating an instant withdrawal * @param {string} params.address - The gateway address of the withdrawal, if not provided, the signer's address will be used * @param {string} params.vaultId - The vault ID of the withdrawal * @returns {Promise<AoMessageResult>} The result of the withdrawal */ async instantWithdrawal(params, options) { const { tags = [] } = options || {}; const allTags = [ ...tags, { name: 'Action', value: 'Instant-Withdrawal' }, { name: 'Vault-Id', value: params.vaultId }, { name: 'Address', value: params.gatewayAddress }, ]; return this.process.send({ signer: this.signer, tags: (0, arweave_js_1.pruneTags)(allTags), }); } async increaseOperatorStake(params, options) { const { tags = [] } = options || {}; return this.process.send({ signer: this.signer, tags: [ ...tags, { name: 'Action', value: 'Increase-Operator-Stake' }, { name: 'Quantity', value: params.increaseQty.valueOf().toString() }, ], }); } async decreaseOperatorStake(params, options) { const { tags = [] } = options || {}; return this.process.send({ signer: this.signer, tags: [ ...tags, { name: 'Action', value: 'Decrease-Operator-Stake' }, { name: 'Quantity', value: params.decreaseQty.valueOf().toString() }, { name: 'Instant', value: `${params.instant || false}` }, ], }); } async saveObservations(params, options) { const { tags = [] } = options || {}; return this.process.send({ signer: this.signer, tags: [ ...tags, { name: 'Action', value: 'Save-Observations' }, { name: 'Report-Tx-Id', value: params.reportTxId, }, { name: 'Failed-Gateways', value: params.failedGateways.join(','), }, ], }); } async buyRecord(params, options) { // spawn a new ANT if not provided if (params.processId === undefined) { try { // if a Name tag is provided, use it. Else, default to the arns name being purchased. const { nameTag, otherTags } = (options?.tags || []).reduce((acc, tag) => { if (tag.name === 'Name') { acc.nameTag = tag; } else { acc.otherTags.push(tag); } return acc; }, { nameTag: { name: 'Name', value: params.name }, otherTags: [] }); params.processId = await ant_js_1.ANT.spawn({ signer: this.signer, ao: this.process.ao, logger: this.logger, // This lets AOS set the ArNS name as the Name in lua state tags: [nameTag, ...otherTags], onSigningProgress: options?.onSigningProgress, }); } catch (error) { this.logger.error('Failed to spawn ANT for name purchase.', { error, }); throw error; } } options?.onSigningProgress?.('buying-name', { name: params.name, years: params.years, type: params.type, processId: params.processId, fundFrom: params.fundFrom, referrer: params.referrer, }); // pay with turbo credits if available if (params.fundFrom === 'turbo') { if (!(this.paymentProvider instanceof turbo_js_1.TurboArNSPaymentProviderAuthenticated)) { throw new Error('Turbo funding is not supported for this payment provider'); } return this.paymentProvider.initiateArNSPurchase({ intent: 'Buy-Name', name: params.name, years: params.years, type: params.type, processId: params.processId, paidBy: params.paidBy, }); } const { tags = [] } = options || {}; const allTags = [ ...tags, { name: 'Action', value: 'Buy-Name' }, { name: 'Name', value: params.name }, { name: 'Years', value: params.years?.toString() ?? '1' }, { name: 'Process-Id', value: params.processId }, { name: 'Purchase-Type', value: params.type || 'lease' }, { name: 'Fund-From', value: params.fundFrom }, { name: 'Referrer', value: params.referrer }, ]; return this.process.send({ signer: this.signer, tags: (0, arweave_js_1.pruneTags)(allTags), }); } /** * Upgrades an existing leased record to a permabuy. * * @param {Object} params - The parameters for upgrading a record * @param {string} params.name - The name of the record to upgrade * @param {Object} [options] - The options for the upgrade * @returns {Promise<AoMessageResult>} The result of the upgrade */ async upgradeRecord(params, options) { if (params.fundFrom === 'turbo') { if (!(this.paymentProvider instanceof turbo_js_1.TurboArNSPaymentProviderAuthenticated)) { throw new Error('Turbo funding is not supported for this payment provider'); } return this.paymentProvider.initiateArNSPurchase({ intent: 'Upgrade-Name', name: params.name, }); } const { tags = [] } = options || {}; const allTags = [ ...tags, { name: 'Action', value: 'Upgrade-Name' }, { name: 'Name', value: params.name }, { name: 'Fund-From', value: params.fundFrom }, { name: 'Referrer', value: params.referrer }, ]; return this.process.send({ signer: this.signer, tags: (0, arweave_js_1.pruneTags)(allTags), }); } /** * Extends the lease of an existing leased record. * * @param {Object} params - The parameters for extending a lease * @param {string} params.name - The name of the record to extend * @param {number} params.years - The number of years to extend the lease * @param {Object} [options] - The options for the extension * @returns {Promise<AoMessageResult>} The result of the extension */ async extendLease(params, options) { if (params.fundFrom === 'turbo') { if (!(this.paymentProvider instanceof turbo_js_1.TurboArNSPaymentProviderAuthenticated)) { throw new Error('Turbo funding is not supported for this payment provider'); } return this.paymentProvider.initiateArNSPurchase({ intent: 'Extend-Lease', name: params.name, years: params.years, }); } const { tags = [] } = options || {}; const allTags = [ ...tags, { name: 'Action', value: 'Extend-Lease' }, { name: 'Name', value: params.name }, { name: 'Years', value: params.years.toString() }, { name: 'Fund-From', value: params.fundFrom }, { name: 'Referrer', value: params.referrer }, ]; return this.process.send({ signer: this.signer, tags: (0, arweave_js_1.pruneTags)(allTags), }); } async increaseUndernameLimit(params, options) { if (params.fundFrom === 'turbo') { if (!(this.paymentProvider instanceof turbo_js_1.TurboArNSPaymentProviderAuthenticated)) { throw new Error('Turbo funding is not supported for this payment provider'); } return this.paymentProvider.initiateArNSPurchase({ intent: 'Increase-Undername-Limit', quantity: params.increaseCount, name: params.name, }); } const { tags = [] } = options || {}; const allTags = [ ...tags, { name: 'Action', value: 'Increase-Undername-Limit' }, { name: 'Name', value: params.name }, { name: 'Quantity', value: params.increaseCount.toString() }, { name: 'Fund-From', value: params.fundFrom }, { name: 'Referrer', value: params.referrer }, ]; return this.process.send({ signer: this.signer, tags: (0, arweave_js_1.pruneTags)(allTags), }); } /** * Cancel a withdrawal from a gateway. * * @param {Object} params - The parameters for cancelling a withdrawal * @param {string} [params.address] - The address of the withdrawal (optional). If not provided, the signer's address will be used. * @param {string} params.vaultId - The vault ID of the withdrawal. * @param {Object} [options] - The options for the cancellation * @returns {Promise<AoMessageResult>} The result of the cancellation */ async cancelWithdrawal(params, options) { const { tags = [] } = options || {}; const allTags = [ ...tags, { name: 'Action', value: 'Cancel-Withdrawal' }, { name: 'Vault-Id', value: params.vaultId }, { name: 'Address', value: params.gatewayAddress }, ]; return this.process.send({ signer: this.signer, tags: (0, arweave_js_1.pruneTags)(allTags), }); } async requestPrimaryName(params, options) { if (params.fundFrom === 'turbo') { throw new Error('Turbo funding is not yet supported for primary name requests'); } const { tags = [] } = options || {}; const allTags = [ ...tags, { name: 'Action', value: 'Request-Primary-Name' }, { name: 'Name', value: params.name }, { name: 'Fund-From', value: params.fundFrom }, ];