@ar.io/sdk 3.0.0 → 3.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -154,10 +154,16 @@ const utils_js_1 = require("./utils.js");
154
154
  });
155
155
  (0, utils_js_1.makeCommand)({
156
156
  name: 'get-token-cost',
157
- description: 'Get token cost',
157
+ description: 'Get token cost for an intended action',
158
158
  options: options_js_1.tokenCostOptions,
159
159
  action: readCommands_js_1.getTokenCost,
160
160
  });
161
+ (0, utils_js_1.makeCommand)({
162
+ name: 'get-cost-details',
163
+ description: 'Get expanded cost details for an intended action',
164
+ options: options_js_1.tokenCostOptions,
165
+ action: readCommands_js_1.getCostDetails,
166
+ });
161
167
  (0, utils_js_1.makeCommand)({
162
168
  name: 'list-vaults',
163
169
  description: 'Get all wallet vaults',
@@ -209,7 +215,7 @@ const utils_js_1 = require("./utils.js");
209
215
  .getBalance({ address: (0, utils_js_1.requiredAddressFromOptions)(options) })
210
216
  .then((result) => ({
211
217
  address: (0, utils_js_1.requiredAddressFromOptions)(options),
212
- mIOBalance: result,
218
+ mARIOBalance: result,
213
219
  message: `Provided address current has a balance of ${(0, utils_js_1.formatARIOWithCommas)(new token_js_1.mARIOToken(result).toARIO())} ARIO`,
214
220
  })),
215
221
  });
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getVault = exports.getGatewayVaults = exports.getPrimaryName = exports.getTokenCost = exports.getPrescribedNames = exports.getPrescribedObservers = exports.getEpoch = exports.listArNSReturnedNames = exports.getArNSReturnedName = exports.listArNSReservedNames = exports.getArNSReservedName = exports.listArNSRecords = exports.getArNSRecord = exports.getAllowedDelegates = exports.getDelegations = exports.getGatewayDelegates = exports.listGateways = exports.getGateway = void 0;
3
+ exports.getVault = exports.getGatewayVaults = exports.getPrimaryName = exports.getCostDetails = exports.getTokenCost = exports.getPrescribedNames = exports.getPrescribedObservers = exports.getEpoch = exports.listArNSReturnedNames = exports.getArNSReturnedName = exports.listArNSReservedNames = exports.getArNSReservedName = exports.listArNSRecords = exports.getArNSRecord = exports.getAllowedDelegates = exports.getDelegations = exports.getGatewayDelegates = exports.listGateways = exports.getGateway = void 0;
4
4
  /**
5
5
  * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
6
6
  *
@@ -132,33 +132,33 @@ async function getPrescribedNames(o) {
132
132
  }
133
133
  exports.getPrescribedNames = getPrescribedNames;
134
134
  async function getTokenCost(o) {
135
- o.intent ??= 'Buy-Record';
136
- o.type ??= 'lease';
137
- if (!(0, io_js_1.isValidIntent)(o.intent)) {
138
- throw new Error(`Invalid intent. Valid intents are: ${io_js_1.validIntents.join(', ')}`);
139
- }
140
- if (o.type !== 'lease' && o.type !== 'permabuy') {
141
- throw new Error(`Invalid type. Valid types are: lease, permabuy`);
142
- }
143
- if (o.type === 'lease' &&
144
- io_js_1.intentsUsingYears.includes(o.intent) &&
145
- o.years === undefined) {
146
- throw new Error('Years is required for lease type');
147
- }
148
- const tokenCost = await (0, utils_js_1.readARIOFromOptions)(o).getTokenCost({
149
- type: o.type,
150
- quantity: o.quantity !== undefined ? +o.quantity : undefined,
151
- years: o.years !== undefined ? +o.years : undefined,
152
- intent: o.intent,
153
- name: (0, utils_js_1.requiredStringFromOptions)(o, 'name'),
154
- });
135
+ const tokenCost = await (0, utils_js_1.readARIOFromOptions)(o).getTokenCost((0, utils_js_1.getTokenCostParamsFromOptions)(o));
155
136
  const output = {
156
- mIOTokenCost: tokenCost,
137
+ mARIOTokenCost: tokenCost,
157
138
  message: `The cost of the provided action is ${(0, utils_js_1.formatARIOWithCommas)(new token_js_1.mARIOToken(tokenCost).toARIO())} ARIO`,
158
139
  };
159
140
  return output;
160
141
  }
161
142
  exports.getTokenCost = getTokenCost;
143
+ async function getCostDetails(o) {
144
+ if (o.fundFrom !== undefined) {
145
+ if (!(0, io_js_1.isValidFundFrom)(o.fundFrom)) {
146
+ throw new Error(`Invalid fund from: ${o.fundFrom}. Please use one of ${io_js_1.fundFromOptions.join(', ')}`);
147
+ }
148
+ }
149
+ const costDetails = await (0, utils_js_1.readARIOFromOptions)(o).getCostDetails({
150
+ ...(0, utils_js_1.getTokenCostParamsFromOptions)(o),
151
+ fundFrom: o.fundFrom,
152
+ });
153
+ const output = {
154
+ ...costDetails,
155
+ message: `The cost of the provided action is ${(0, utils_js_1.formatARIOWithCommas)(new token_js_1.mARIOToken(costDetails.tokenCost).toARIO())} ARIO${costDetails.fundingPlan && costDetails.fundingPlan.shortfall > 0
156
+ ? `. Insufficient funds for action. There is a shortfall of ${(0, utils_js_1.formatARIOWithCommas)(new token_js_1.mARIOToken(costDetails.fundingPlan.shortfall).toARIO())} ARIO`
157
+ : ''}`,
158
+ };
159
+ return output;
160
+ }
161
+ exports.getCostDetails = getCostDetails;
162
162
  async function getPrimaryName(o) {
163
163
  const address = (0, utils_js_1.addressFromOptions)(o);
164
164
  const name = o.name;
@@ -237,6 +237,10 @@ exports.optionMap = {
237
237
  description: 'Include failed gateways in the list',
238
238
  type: 'array',
239
239
  },
240
+ fundFrom: {
241
+ alias: '--fund-from <fundFrom>',
242
+ description: 'Where to fund the action from. e.g. "balance", "stakes", or "any',
243
+ },
240
244
  };
241
245
  exports.walletOptions = [
242
246
  exports.optionMap.walletFile,
@@ -274,6 +278,8 @@ exports.tokenCostOptions = [
274
278
  exports.optionMap.type,
275
279
  exports.optionMap.years,
276
280
  exports.optionMap.quantity,
281
+ exports.optionMap.address,
282
+ exports.optionMap.fundFrom,
277
283
  ];
278
284
  exports.transferOptions = [
279
285
  ...exports.writeActionOptions,
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getANTStateFromOptions = exports.requiredPositiveIntegerFromOptions = exports.positiveIntegerFromOptions = exports.requiredStringArrayFromOptions = exports.requiredStringFromOptions = exports.writeANTFromOptions = exports.readANTFromOptions = exports.requiredProcessIdFromOptions = exports.assertConfirmationPrompt = exports.confirmationPrompt = exports.assertEnoughBalance = exports.requiredMIOFromOptions = exports.recordTypeFromOptions = exports.redelegateParamsFromOptions = exports.requiredTargetAndQuantityFromOptions = exports.gatewaySettingsFromOptions = exports.writeActionTagsFromOptions = exports.requiredInitiatorFromOptions = exports.epochInputFromOptions = exports.paginationParamsFromOptions = exports.requiredAddressFromOptions = exports.addressFromOptions = exports.formatARIOWithCommas = exports.writeARIOFromOptions = exports.requiredAoSignerFromOptions = exports.requiredContractSignerFromOptions = exports.readARIOFromOptions = exports.getLoggerFromOptions = exports.jwkToAddress = exports.requiredJwkFromOptions = exports.arioProcessIdFromOptions = exports.makeCommand = exports.applyOptions = exports.runCommand = exports.stringifyJsonForCLIDisplay = void 0;
6
+ exports.getTokenCostParamsFromOptions = exports.getANTStateFromOptions = exports.requiredPositiveIntegerFromOptions = exports.positiveIntegerFromOptions = exports.requiredStringArrayFromOptions = exports.requiredStringFromOptions = exports.writeANTFromOptions = exports.readANTFromOptions = exports.requiredProcessIdFromOptions = exports.assertConfirmationPrompt = exports.confirmationPrompt = exports.assertEnoughBalance = exports.requiredMIOFromOptions = exports.recordTypeFromOptions = exports.redelegateParamsFromOptions = exports.requiredTargetAndQuantityFromOptions = exports.gatewaySettingsFromOptions = exports.writeActionTagsFromOptions = exports.requiredInitiatorFromOptions = exports.epochInputFromOptions = exports.paginationParamsFromOptions = exports.requiredAddressFromOptions = exports.addressFromOptions = exports.formatARIOWithCommas = exports.writeARIOFromOptions = exports.requiredAoSignerFromOptions = exports.requiredContractSignerFromOptions = exports.readARIOFromOptions = exports.getLoggerFromOptions = exports.jwkToAddress = exports.requiredJwkFromOptions = exports.arioProcessIdFromOptions = exports.makeCommand = exports.applyOptions = exports.runCommand = exports.stringifyJsonForCLIDisplay = void 0;
7
7
  /**
8
8
  * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
9
9
  *
@@ -405,3 +405,23 @@ function getANTStateFromOptions(options) {
405
405
  });
406
406
  }
407
407
  exports.getANTStateFromOptions = getANTStateFromOptions;
408
+ function getTokenCostParamsFromOptions(o) {
409
+ o.intent ??= 'Buy-Record';
410
+ o.type ??= 'lease';
411
+ o.years ??= '1';
412
+ if (!(0, index_js_1.isValidIntent)(o.intent)) {
413
+ throw new Error(`Invalid intent. Valid intents are: ${index_js_1.validIntents.join(', ')}`);
414
+ }
415
+ if (o.type !== 'lease' && o.type !== 'permabuy') {
416
+ throw new Error(`Invalid type. Valid types are: lease, permabuy`);
417
+ }
418
+ return {
419
+ type: o.type,
420
+ quantity: o.quantity !== undefined ? +o.quantity : undefined,
421
+ years: +o.years,
422
+ intent: o.intent,
423
+ name: requiredStringFromOptions(o, 'name'),
424
+ fromAddress: addressFromOptions(o),
425
+ };
426
+ }
427
+ exports.getTokenCostParamsFromOptions = getTokenCostParamsFromOptions;
@@ -17,6 +17,7 @@ exports.AOProcess = void 0;
17
17
  * limitations under the License.
18
18
  */
19
19
  const aoconnect_1 = require("@permaweb/aoconnect");
20
+ const base64_js_1 = require("../../utils/base64.js");
20
21
  const json_js_1 = require("../../utils/json.js");
21
22
  const version_js_1 = require("../../version.js");
22
23
  const error_js_1 = require("../error.js");
@@ -30,7 +31,7 @@ class AOProcess {
30
31
  this.logger = logger;
31
32
  this.ao = ao;
32
33
  }
33
- async read({ tags, retries = 3, }) {
34
+ async read({ tags, retries = 3, fromAddress, }) {
34
35
  let attempts = 0;
35
36
  let lastError;
36
37
  while (attempts < retries) {
@@ -39,10 +40,14 @@ class AOProcess {
39
40
  tags,
40
41
  });
41
42
  // map tags to inputs
42
- const result = await this.ao.dryrun({
43
+ const dryRunInput = {
43
44
  process: this.processId,
44
45
  tags,
45
- });
46
+ };
47
+ if (fromAddress !== undefined) {
48
+ dryRunInput['Owner'] = fromAddress;
49
+ }
50
+ const result = await this.ao.dryrun(dryRunInput);
46
51
  this.logger.debug(`Read interaction result`, {
47
52
  result,
48
53
  });
@@ -89,16 +94,20 @@ class AOProcess {
89
94
  processId: this.processId,
90
95
  });
91
96
  // TODO: do a read as a dry run to check if the process supports the action
97
+ // anchor is a random text produce non-deterministic messages IDs when deterministic signers are provided (ETH)
98
+ const anchor = (0, base64_js_1.getRandomText)(32);
92
99
  const messageId = await this.ao.message({
93
100
  process: this.processId,
94
101
  // TODO: any other default tags we want to add?
95
102
  tags: [...tags, { name: 'AR-IO-SDK', value: version_js_1.version }],
96
103
  data,
97
104
  signer,
105
+ anchor,
98
106
  });
99
107
  this.logger.debug(`Sent message to process`, {
100
108
  messageId,
101
109
  processId: this.processId,
110
+ anchor,
102
111
  });
103
112
  // check the result of the send interaction
104
113
  const output = await this.ao.result({
@@ -265,7 +265,7 @@ class ARIOReadable {
265
265
  tags: (0, arweave_js_1.pruneTags)(allTags),
266
266
  });
267
267
  }
268
- async getTokenCost({ intent, type, years, name, quantity, }) {
268
+ async getTokenCost({ intent, type, years, name, quantity, fromAddress, }) {
269
269
  const allTags = [
270
270
  { name: 'Action', value: 'Token-Cost' },
271
271
  {
@@ -302,6 +302,52 @@ class ARIOReadable {
302
302
  ];
303
303
  return this.process.read({
304
304
  tags: (0, arweave_js_1.pruneTags)(allTags),
305
+ fromAddress,
306
+ });
307
+ }
308
+ // TODO: Can overload this function to refine different types of cost details params
309
+ async getCostDetails({ intent, type, years, name, quantity, fromAddress, fundFrom, }) {
310
+ const allTags = [
311
+ { name: 'Action', value: 'Get-Cost-Details-For-Action' },
312
+ {
313
+ name: 'Intent',
314
+ value: intent,
315
+ },
316
+ {
317
+ name: 'Name',
318
+ value: name,
319
+ },
320
+ {
321
+ name: 'Years',
322
+ value: years?.toString(),
323
+ },
324
+ {
325
+ name: 'Quantity',
326
+ value: quantity?.toString(),
327
+ },
328
+ {
329
+ name: 'Purchase-Type',
330
+ value: type,
331
+ },
332
+ {
333
+ name: 'Fund-From',
334
+ value: fundFrom,
335
+ },
336
+ {
337
+ name: 'Timestamp',
338
+ value: (await this.arweave.blocks
339
+ .getCurrent()
340
+ .then((block) => {
341
+ return { timestamp: block.timestamp * 1000 };
342
+ })
343
+ .catch(() => {
344
+ return { timestamp: Date.now() }; // fallback to current time
345
+ })).timestamp.toString(),
346
+ },
347
+ ];
348
+ return this.process.read({
349
+ tags: (0, arweave_js_1.pruneTags)(allTags),
350
+ fromAddress,
305
351
  });
306
352
  }
307
353
  async getRegistrationFees() {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isLeasedArNSRecord = exports.isProcessIdConfiguration = exports.isProcessConfiguration = exports.isValidIntent = exports.intentsUsingYears = exports.validIntents = void 0;
3
+ exports.isLeasedArNSRecord = exports.isProcessIdConfiguration = exports.isProcessConfiguration = exports.isValidFundFrom = exports.fundFromOptions = exports.isValidIntent = exports.intentsUsingYears = exports.validIntents = void 0;
4
4
  const arweave_js_1 = require("../utils/arweave.js");
5
5
  exports.validIntents = [
6
6
  'Buy-Record',
@@ -14,6 +14,11 @@ const isValidIntent = (intent) => {
14
14
  return exports.validIntents.indexOf(intent) !== -1;
15
15
  };
16
16
  exports.isValidIntent = isValidIntent;
17
+ exports.fundFromOptions = ['balance', 'stakes', 'any'];
18
+ const isValidFundFrom = (fundFrom) => {
19
+ return exports.fundFromOptions.indexOf(fundFrom) !== -1;
20
+ };
21
+ exports.isValidFundFrom = isValidFundFrom;
17
22
  // Typeguard functions
18
23
  function isProcessConfiguration(config) {
19
24
  return 'process' in config;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sha256B64Url = exports.toB64Url = exports.fromB64Url = void 0;
3
+ exports.getRandomText = exports.sha256B64Url = exports.toB64Url = exports.fromB64Url = void 0;
4
4
  /**
5
5
  * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
6
6
  *
@@ -52,3 +52,12 @@ function sha256B64Url(input) {
52
52
  return toB64Url((0, crypto_1.createHash)('sha256').update(Uint8Array.from(input)).digest());
53
53
  }
54
54
  exports.sha256B64Url = sha256B64Url;
55
+ function getRandomText(length = 32) {
56
+ // Generate a buffer of random bytes
57
+ const buffer = (0, crypto_1.randomBytes)(length);
58
+ // Convert bytes to hexadecimal string
59
+ return Array.from(buffer, (byte) => byte.toString(16).padStart(2, '0'))
60
+ .join('')
61
+ .slice(0, length);
62
+ }
63
+ exports.getRandomText = getRandomText;
@@ -17,4 +17,4 @@
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.version = void 0;
19
19
  // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH
20
- exports.version = '3.0.0';
20
+ exports.version = '3.1.0-alpha.1';
@@ -20,7 +20,7 @@ import { spawnANT } from '../node/index.js';
20
20
  import { mARIOToken } from '../types/token.js';
21
21
  import { version } from '../version.js';
22
22
  import { cancelWithdrawal, decreaseDelegateStake, decreaseOperatorStake, delegateStake, increaseOperatorStake, instantWithdrawal, joinNetwork, leaveNetwork, redelegateStake, saveObservations, updateGatewaySettings, } from './commands/gatewayWriteCommands.js';
23
- import { getAllowedDelegates, getArNSRecord, getArNSReservedName, getArNSReturnedName, getDelegations, getEpoch, getGateway, getGatewayDelegates, getGatewayVaults, getPrescribedNames, getPrescribedObservers, getPrimaryName, getTokenCost, getVault, listArNSRecords, listArNSReservedNames, listArNSReturnedNames, listGateways, } from './commands/readCommands.js';
23
+ import { getAllowedDelegates, getArNSRecord, getArNSReservedName, getArNSReturnedName, getCostDetails, getDelegations, getEpoch, getGateway, getGatewayDelegates, getGatewayVaults, getPrescribedNames, getPrescribedObservers, getPrimaryName, getTokenCost, getVault, listArNSRecords, listArNSReservedNames, listArNSReturnedNames, listGateways, } from './commands/readCommands.js';
24
24
  import { transfer } from './commands/transfer.js';
25
25
  import { addressAndVaultIdOptions, addressOptions, antStateOptions, buyRecordOptions, decreaseDelegateStakeOptions, delegateStakeOptions, epochOptions, getVaultOptions, globalOptions, initiatorOptions, joinNetworkOptions, nameOptions, nameWriteOptions, operatorStakeOptions, optionMap, paginationAddressOptions, paginationOptions, redelegateStakeOptions, tokenCostOptions, transferOptions, updateGatewaySettingsOptions, writeActionOptions, } from './options.js';
26
26
  import { applyOptions, arioProcessIdFromOptions, assertConfirmationPrompt, epochInputFromOptions, formatARIOWithCommas, getANTStateFromOptions, getLoggerFromOptions, makeCommand, paginationParamsFromOptions, positiveIntegerFromOptions, readANTFromOptions, readARIOFromOptions, recordTypeFromOptions, requiredAddressFromOptions, requiredAoSignerFromOptions, requiredPositiveIntegerFromOptions, requiredStringArrayFromOptions, requiredStringFromOptions, writeANTFromOptions, writeARIOFromOptions, writeActionTagsFromOptions, } from './utils.js';
@@ -152,10 +152,16 @@ makeCommand({
152
152
  });
153
153
  makeCommand({
154
154
  name: 'get-token-cost',
155
- description: 'Get token cost',
155
+ description: 'Get token cost for an intended action',
156
156
  options: tokenCostOptions,
157
157
  action: getTokenCost,
158
158
  });
159
+ makeCommand({
160
+ name: 'get-cost-details',
161
+ description: 'Get expanded cost details for an intended action',
162
+ options: tokenCostOptions,
163
+ action: getCostDetails,
164
+ });
159
165
  makeCommand({
160
166
  name: 'list-vaults',
161
167
  description: 'Get all wallet vaults',
@@ -207,7 +213,7 @@ makeCommand({
207
213
  .getBalance({ address: requiredAddressFromOptions(options) })
208
214
  .then((result) => ({
209
215
  address: requiredAddressFromOptions(options),
210
- mIOBalance: result,
216
+ mARIOBalance: result,
211
217
  message: `Provided address current has a balance of ${formatARIOWithCommas(new mARIOToken(result).toARIO())} ARIO`,
212
218
  })),
213
219
  });
@@ -13,9 +13,9 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { intentsUsingYears, isValidIntent, validIntents, } from '../../types/io.js';
16
+ import { fundFromOptions, isValidFundFrom, } from '../../types/io.js';
17
17
  import { mARIOToken } from '../../types/token.js';
18
- import { addressFromOptions, epochInputFromOptions, formatARIOWithCommas, paginationParamsFromOptions, readARIOFromOptions, requiredAddressFromOptions, requiredStringFromOptions, } from '../utils.js';
18
+ import { addressFromOptions, epochInputFromOptions, formatARIOWithCommas, getTokenCostParamsFromOptions, paginationParamsFromOptions, readARIOFromOptions, requiredAddressFromOptions, requiredStringFromOptions, } from '../utils.js';
19
19
  export async function getGateway(o) {
20
20
  const address = requiredAddressFromOptions(o);
21
21
  const gateway = await readARIOFromOptions(o).getGateway({
@@ -115,29 +115,28 @@ export async function getPrescribedNames(o) {
115
115
  : { message: `No prescribed names found for epoch ${epoch}` };
116
116
  }
117
117
  export async function getTokenCost(o) {
118
- o.intent ??= 'Buy-Record';
119
- o.type ??= 'lease';
120
- if (!isValidIntent(o.intent)) {
121
- throw new Error(`Invalid intent. Valid intents are: ${validIntents.join(', ')}`);
122
- }
123
- if (o.type !== 'lease' && o.type !== 'permabuy') {
124
- throw new Error(`Invalid type. Valid types are: lease, permabuy`);
125
- }
126
- if (o.type === 'lease' &&
127
- intentsUsingYears.includes(o.intent) &&
128
- o.years === undefined) {
129
- throw new Error('Years is required for lease type');
118
+ const tokenCost = await readARIOFromOptions(o).getTokenCost(getTokenCostParamsFromOptions(o));
119
+ const output = {
120
+ mARIOTokenCost: tokenCost,
121
+ message: `The cost of the provided action is ${formatARIOWithCommas(new mARIOToken(tokenCost).toARIO())} ARIO`,
122
+ };
123
+ return output;
124
+ }
125
+ export async function getCostDetails(o) {
126
+ if (o.fundFrom !== undefined) {
127
+ if (!isValidFundFrom(o.fundFrom)) {
128
+ throw new Error(`Invalid fund from: ${o.fundFrom}. Please use one of ${fundFromOptions.join(', ')}`);
129
+ }
130
130
  }
131
- const tokenCost = await readARIOFromOptions(o).getTokenCost({
132
- type: o.type,
133
- quantity: o.quantity !== undefined ? +o.quantity : undefined,
134
- years: o.years !== undefined ? +o.years : undefined,
135
- intent: o.intent,
136
- name: requiredStringFromOptions(o, 'name'),
131
+ const costDetails = await readARIOFromOptions(o).getCostDetails({
132
+ ...getTokenCostParamsFromOptions(o),
133
+ fundFrom: o.fundFrom,
137
134
  });
138
135
  const output = {
139
- mIOTokenCost: tokenCost,
140
- message: `The cost of the provided action is ${formatARIOWithCommas(new mARIOToken(tokenCost).toARIO())} ARIO`,
136
+ ...costDetails,
137
+ message: `The cost of the provided action is ${formatARIOWithCommas(new mARIOToken(costDetails.tokenCost).toARIO())} ARIO${costDetails.fundingPlan && costDetails.fundingPlan.shortfall > 0
138
+ ? `. Insufficient funds for action. There is a shortfall of ${formatARIOWithCommas(new mARIOToken(costDetails.fundingPlan.shortfall).toARIO())} ARIO`
139
+ : ''}`,
141
140
  };
142
141
  return output;
143
142
  }
@@ -234,6 +234,10 @@ export const optionMap = {
234
234
  description: 'Include failed gateways in the list',
235
235
  type: 'array',
236
236
  },
237
+ fundFrom: {
238
+ alias: '--fund-from <fundFrom>',
239
+ description: 'Where to fund the action from. e.g. "balance", "stakes", or "any',
240
+ },
237
241
  };
238
242
  export const walletOptions = [
239
243
  optionMap.walletFile,
@@ -271,6 +275,8 @@ export const tokenCostOptions = [
271
275
  optionMap.type,
272
276
  optionMap.years,
273
277
  optionMap.quantity,
278
+ optionMap.address,
279
+ optionMap.fundFrom,
274
280
  ];
275
281
  export const transferOptions = [
276
282
  ...writeActionOptions,
@@ -17,7 +17,7 @@ import { connect } from '@permaweb/aoconnect';
17
17
  import { program } from 'commander';
18
18
  import { readFileSync } from 'fs';
19
19
  import prompts from 'prompts';
20
- import { ANT, AOProcess, ARIO, ARIOToken, ARIO_DEVNET_PROCESS_ID, ARIO_TESTNET_PROCESS_ID, ArweaveSigner, Logger, createAoSigner, fromB64Url, initANTStateForAddress, mARIOToken, sha256B64Url, } from '../node/index.js';
20
+ import { ANT, AOProcess, ARIO, ARIOToken, ARIO_DEVNET_PROCESS_ID, ARIO_TESTNET_PROCESS_ID, ArweaveSigner, Logger, createAoSigner, fromB64Url, initANTStateForAddress, isValidIntent, mARIOToken, sha256B64Url, validIntents, } from '../node/index.js';
21
21
  import { globalOptions } from './options.js';
22
22
  export function stringifyJsonForCLIDisplay(json) {
23
23
  return JSON.stringify(json, null, 2);
@@ -364,3 +364,22 @@ export function getANTStateFromOptions(options) {
364
364
  ttlSeconds: options.ttlSeconds !== undefined ? +options.ttlSeconds : 3600,
365
365
  });
366
366
  }
367
+ export function getTokenCostParamsFromOptions(o) {
368
+ o.intent ??= 'Buy-Record';
369
+ o.type ??= 'lease';
370
+ o.years ??= '1';
371
+ if (!isValidIntent(o.intent)) {
372
+ throw new Error(`Invalid intent. Valid intents are: ${validIntents.join(', ')}`);
373
+ }
374
+ if (o.type !== 'lease' && o.type !== 'permabuy') {
375
+ throw new Error(`Invalid type. Valid types are: lease, permabuy`);
376
+ }
377
+ return {
378
+ type: o.type,
379
+ quantity: o.quantity !== undefined ? +o.quantity : undefined,
380
+ years: +o.years,
381
+ intent: o.intent,
382
+ name: requiredStringFromOptions(o, 'name'),
383
+ fromAddress: addressFromOptions(o),
384
+ };
385
+ }
@@ -14,6 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { connect } from '@permaweb/aoconnect';
17
+ import { getRandomText } from '../../utils/base64.js';
17
18
  import { safeDecode } from '../../utils/json.js';
18
19
  import { version } from '../../version.js';
19
20
  import { WriteInteractionError } from '../error.js';
@@ -27,7 +28,7 @@ export class AOProcess {
27
28
  this.logger = logger;
28
29
  this.ao = ao;
29
30
  }
30
- async read({ tags, retries = 3, }) {
31
+ async read({ tags, retries = 3, fromAddress, }) {
31
32
  let attempts = 0;
32
33
  let lastError;
33
34
  while (attempts < retries) {
@@ -36,10 +37,14 @@ export class AOProcess {
36
37
  tags,
37
38
  });
38
39
  // map tags to inputs
39
- const result = await this.ao.dryrun({
40
+ const dryRunInput = {
40
41
  process: this.processId,
41
42
  tags,
42
- });
43
+ };
44
+ if (fromAddress !== undefined) {
45
+ dryRunInput['Owner'] = fromAddress;
46
+ }
47
+ const result = await this.ao.dryrun(dryRunInput);
43
48
  this.logger.debug(`Read interaction result`, {
44
49
  result,
45
50
  });
@@ -86,16 +91,20 @@ export class AOProcess {
86
91
  processId: this.processId,
87
92
  });
88
93
  // TODO: do a read as a dry run to check if the process supports the action
94
+ // anchor is a random text produce non-deterministic messages IDs when deterministic signers are provided (ETH)
95
+ const anchor = getRandomText(32);
89
96
  const messageId = await this.ao.message({
90
97
  process: this.processId,
91
98
  // TODO: any other default tags we want to add?
92
99
  tags: [...tags, { name: 'AR-IO-SDK', value: version }],
93
100
  data,
94
101
  signer,
102
+ anchor,
95
103
  });
96
104
  this.logger.debug(`Sent message to process`, {
97
105
  messageId,
98
106
  processId: this.processId,
107
+ anchor,
99
108
  });
100
109
  // check the result of the send interaction
101
110
  const output = await this.ao.result({
@@ -261,7 +261,7 @@ export class ARIOReadable {
261
261
  tags: pruneTags(allTags),
262
262
  });
263
263
  }
264
- async getTokenCost({ intent, type, years, name, quantity, }) {
264
+ async getTokenCost({ intent, type, years, name, quantity, fromAddress, }) {
265
265
  const allTags = [
266
266
  { name: 'Action', value: 'Token-Cost' },
267
267
  {
@@ -298,6 +298,52 @@ export class ARIOReadable {
298
298
  ];
299
299
  return this.process.read({
300
300
  tags: pruneTags(allTags),
301
+ fromAddress,
302
+ });
303
+ }
304
+ // TODO: Can overload this function to refine different types of cost details params
305
+ async getCostDetails({ intent, type, years, name, quantity, fromAddress, fundFrom, }) {
306
+ const allTags = [
307
+ { name: 'Action', value: 'Get-Cost-Details-For-Action' },
308
+ {
309
+ name: 'Intent',
310
+ value: intent,
311
+ },
312
+ {
313
+ name: 'Name',
314
+ value: name,
315
+ },
316
+ {
317
+ name: 'Years',
318
+ value: years?.toString(),
319
+ },
320
+ {
321
+ name: 'Quantity',
322
+ value: quantity?.toString(),
323
+ },
324
+ {
325
+ name: 'Purchase-Type',
326
+ value: type,
327
+ },
328
+ {
329
+ name: 'Fund-From',
330
+ value: fundFrom,
331
+ },
332
+ {
333
+ name: 'Timestamp',
334
+ value: (await this.arweave.blocks
335
+ .getCurrent()
336
+ .then((block) => {
337
+ return { timestamp: block.timestamp * 1000 };
338
+ })
339
+ .catch(() => {
340
+ return { timestamp: Date.now() }; // fallback to current time
341
+ })).timestamp.toString(),
342
+ },
343
+ ];
344
+ return this.process.read({
345
+ tags: pruneTags(allTags),
346
+ fromAddress,
301
347
  });
302
348
  }
303
349
  async getRegistrationFees() {
@@ -10,6 +10,10 @@ export const intentsUsingYears = ['Buy-Record', 'Extend-Lease'];
10
10
  export const isValidIntent = (intent) => {
11
11
  return validIntents.indexOf(intent) !== -1;
12
12
  };
13
+ export const fundFromOptions = ['balance', 'stakes', 'any'];
14
+ export const isValidFundFrom = (fundFrom) => {
15
+ return fundFromOptions.indexOf(fundFrom) !== -1;
16
+ };
13
17
  // Typeguard functions
14
18
  export function isProcessConfiguration(config) {
15
19
  return 'process' in config;
@@ -13,7 +13,7 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { createHash } from 'crypto';
16
+ import { createHash, randomBytes } from 'crypto';
17
17
  // safely encodes and decodes base64url strings to and from buffers
18
18
  const BASE64_CHAR_62 = '+';
19
19
  const BASE64_CHAR_63 = '/';
@@ -46,3 +46,11 @@ export function toB64Url(buffer) {
46
46
  export function sha256B64Url(input) {
47
47
  return toB64Url(createHash('sha256').update(Uint8Array.from(input)).digest());
48
48
  }
49
+ export function getRandomText(length = 32) {
50
+ // Generate a buffer of random bytes
51
+ const buffer = randomBytes(length);
52
+ // Convert bytes to hexadecimal string
53
+ return Array.from(buffer, (byte) => byte.toString(16).padStart(2, '0'))
54
+ .join('')
55
+ .slice(0, length);
56
+ }
@@ -14,4 +14,4 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH
17
- export const version = '3.0.0';
17
+ export const version = '3.1.0-alpha.1';