@ar.io/sdk 3.13.0-beta.1 → 3.14.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.
Files changed (64) hide show
  1. package/README.md +1 -1
  2. package/bundles/web.bundle.min.js +133 -136
  3. package/lib/cjs/cli/cli.js +8 -0
  4. package/lib/cjs/common/ant.js +102 -1
  5. package/lib/cjs/common/index.js +0 -2
  6. package/lib/cjs/utils/ant.js +32 -1
  7. package/lib/cjs/utils/hash.js +31 -83
  8. package/lib/cjs/version.js +1 -1
  9. package/lib/esm/cli/cli.js +8 -0
  10. package/lib/esm/common/ant.js +103 -2
  11. package/lib/esm/common/index.js +0 -2
  12. package/lib/esm/utils/ant.js +30 -0
  13. package/lib/esm/utils/hash.js +27 -74
  14. package/lib/esm/version.js +1 -1
  15. package/lib/types/common/ant.d.ts +9 -1
  16. package/lib/types/common/index.d.ts +0 -1
  17. package/lib/types/types/ant.d.ts +17 -0
  18. package/lib/types/types/io.d.ts +0 -3
  19. package/lib/types/utils/ant.d.ts +7 -1
  20. package/lib/types/utils/hash.d.ts +4 -8
  21. package/lib/types/version.d.ts +1 -1
  22. package/package.json +1 -2
  23. package/lib/cjs/common/wayfinder/gateways/network.js +0 -48
  24. package/lib/cjs/common/wayfinder/gateways/simple-cache.js +0 -35
  25. package/lib/cjs/common/wayfinder/gateways/static.js +0 -13
  26. package/lib/cjs/common/wayfinder/index.js +0 -48
  27. package/lib/cjs/common/wayfinder/routing/strategies/ping.js +0 -72
  28. package/lib/cjs/common/wayfinder/routing/strategies/preferred-with-fallback.js +0 -50
  29. package/lib/cjs/common/wayfinder/routing/strategies/random.js +0 -13
  30. package/lib/cjs/common/wayfinder/routing/strategies/round-robin.js +0 -42
  31. package/lib/cjs/common/wayfinder/routing/strategies/static.js +0 -29
  32. package/lib/cjs/common/wayfinder/verification/strategies/data-root-verifier.js +0 -110
  33. package/lib/cjs/common/wayfinder/verification/strategies/hash-verifier.js +0 -27
  34. package/lib/cjs/common/wayfinder/verification/trusted.js +0 -125
  35. package/lib/cjs/common/wayfinder/wayfinder.js +0 -508
  36. package/lib/cjs/types/wayfinder.js +0 -3
  37. package/lib/esm/common/wayfinder/gateways/network.js +0 -44
  38. package/lib/esm/common/wayfinder/gateways/simple-cache.js +0 -31
  39. package/lib/esm/common/wayfinder/gateways/static.js +0 -9
  40. package/lib/esm/common/wayfinder/index.js +0 -32
  41. package/lib/esm/common/wayfinder/routing/strategies/ping.js +0 -68
  42. package/lib/esm/common/wayfinder/routing/strategies/preferred-with-fallback.js +0 -46
  43. package/lib/esm/common/wayfinder/routing/strategies/random.js +0 -9
  44. package/lib/esm/common/wayfinder/routing/strategies/round-robin.js +0 -38
  45. package/lib/esm/common/wayfinder/routing/strategies/static.js +0 -25
  46. package/lib/esm/common/wayfinder/verification/strategies/data-root-verifier.js +0 -102
  47. package/lib/esm/common/wayfinder/verification/strategies/hash-verifier.js +0 -23
  48. package/lib/esm/common/wayfinder/verification/trusted.js +0 -121
  49. package/lib/esm/common/wayfinder/wayfinder.js +0 -499
  50. package/lib/esm/types/wayfinder.js +0 -2
  51. package/lib/types/common/wayfinder/gateways/network.d.ts +0 -33
  52. package/lib/types/common/wayfinder/gateways/simple-cache.d.ts +0 -31
  53. package/lib/types/common/wayfinder/gateways/static.d.ts +0 -23
  54. package/lib/types/common/wayfinder/index.d.ts +0 -27
  55. package/lib/types/common/wayfinder/routing/strategies/ping.d.ts +0 -27
  56. package/lib/types/common/wayfinder/routing/strategies/preferred-with-fallback.d.ts +0 -31
  57. package/lib/types/common/wayfinder/routing/strategies/random.d.ts +0 -21
  58. package/lib/types/common/wayfinder/routing/strategies/round-robin.d.ts +0 -29
  59. package/lib/types/common/wayfinder/routing/strategies/static.d.ts +0 -29
  60. package/lib/types/common/wayfinder/verification/strategies/data-root-verifier.d.ts +0 -27
  61. package/lib/types/common/wayfinder/verification/strategies/hash-verifier.d.ts +0 -26
  62. package/lib/types/common/wayfinder/verification/trusted.d.ts +0 -51
  63. package/lib/types/common/wayfinder/wayfinder.d.ts +0 -257
  64. package/lib/types/types/wayfinder.d.ts +0 -66
@@ -479,6 +479,14 @@ const utils_js_1 = require("./utils.js");
479
479
  return (0, utils_js_1.readANTFromOptions)(options).getTicker();
480
480
  },
481
481
  });
482
+ (0, utils_js_1.makeCommand)({
483
+ name: 'get-ant-logo',
484
+ description: 'Get the logo of an ANT process',
485
+ options: [options_js_1.optionMap.processId],
486
+ action: async (options) => {
487
+ return (0, utils_js_1.readANTFromOptions)(options).getLogo();
488
+ },
489
+ });
482
490
  (0, utils_js_1.makeCommand)({
483
491
  name: 'get-ant-balance',
484
492
  description: 'Get the balance of an ANT process',
@@ -39,6 +39,8 @@ class AoANTReadable {
39
39
  process;
40
40
  processId;
41
41
  strict;
42
+ hyperbeamUrl;
43
+ checkHyperBeamPromise;
42
44
  constructor(config) {
43
45
  this.strict = config.strict || false;
44
46
  if ((0, index_js_1.isProcessConfiguration)(config)) {
@@ -53,8 +55,43 @@ class AoANTReadable {
53
55
  throw new index_js_2.InvalidContractConfigurationError();
54
56
  }
55
57
  this.processId = this.process.processId;
58
+ this.hyperbeamUrl = config.hyperbeamUrl || 'https://permanode.xyz'; // TODO: replace this with hyperbeam.ario.permaweb.services once deployed
59
+ this.checkHyperBeamPromise = this.checkHyperBeamCompatibility();
60
+ }
61
+ /**
62
+ * Check if the process is hyperbeam compatible. If so, we'll use the hyperbeam node to fetch the state.
63
+ *
64
+ * @returns {Promise<boolean>} True if the process is hyperbeam compatible, false otherwise.
65
+ */
66
+ async checkHyperBeamCompatibility() {
67
+ if (this.checkHyperBeamPromise !== undefined) {
68
+ return this.checkHyperBeamPromise;
69
+ }
70
+ const res = await fetch(`https://permanode.xyz/${this.processId}~process@1.0/now/cache`, {
71
+ method: 'HEAD',
72
+ });
73
+ let isHyperBeamCompatible = false;
74
+ if (res.ok) {
75
+ isHyperBeamCompatible = true;
76
+ }
77
+ this.checkHyperBeamPromise = Promise.resolve(isHyperBeamCompatible);
78
+ return isHyperBeamCompatible;
56
79
  }
57
80
  async getState({ strict } = { strict: this.strict }) {
81
+ if (await this.checkHyperBeamCompatibility()) {
82
+ const res = await fetch(`${this.hyperbeamUrl}/${this.processId}~process@1.0/now/cache/serialize~json@1.0`, {
83
+ method: 'GET',
84
+ redirect: 'follow',
85
+ mode: 'cors',
86
+ headers: {
87
+ 'Content-Type': 'application/json',
88
+ },
89
+ });
90
+ if (!res.ok) {
91
+ throw new Error('Failed to fetch ant state');
92
+ }
93
+ return (0, ant_js_2.convertHyperBeamStateToAoANTState)((await res.json()));
94
+ }
58
95
  const tags = [{ name: 'Action', value: 'State' }];
59
96
  const res = await this.process.read({
60
97
  tags,
@@ -67,6 +104,20 @@ class AoANTReadable {
67
104
  return res;
68
105
  }
69
106
  async getInfo({ strict } = { strict: this.strict }) {
107
+ if (await this.checkHyperBeamCompatibility()) {
108
+ const state = await this.getState();
109
+ return {
110
+ Name: state.Name,
111
+ Ticker: state.Ticker,
112
+ Description: state.Description,
113
+ Keywords: state.Keywords,
114
+ Denomination: state.Denomination.toString(),
115
+ Owner: state.Owner,
116
+ Logo: state.Logo,
117
+ 'Total-Supply': state.TotalSupply.toString(),
118
+ Handlers: [], // TODO: support for handler in patched state
119
+ };
120
+ }
70
121
  const tags = [{ name: 'Action', value: 'Info' }];
71
122
  const info = await this.process.read({
72
123
  tags,
@@ -93,6 +144,15 @@ class AoANTReadable {
93
144
  * ```
94
145
  */
95
146
  async getRecord({ undername }, { strict } = { strict: this.strict }) {
147
+ if (await this.checkHyperBeamCompatibility()) {
148
+ const records = await this.getRecords();
149
+ const cachedRecord = records[undername];
150
+ // if the record is not found, throw an error
151
+ if (cachedRecord === undefined) {
152
+ throw new Error('Record not found');
153
+ }
154
+ return cachedRecord;
155
+ }
96
156
  // TODO: use sortedANTRecords to get priority on all records, even if ANT does not have a priority set
97
157
  const record = await this.process.read({
98
158
  tags: [
@@ -113,6 +173,10 @@ class AoANTReadable {
113
173
  * ````
114
174
  */
115
175
  async getRecords({ strict } = { strict: this.strict }) {
176
+ if (await this.checkHyperBeamCompatibility()) {
177
+ const state = await this.getState();
178
+ return (0, ant_js_2.sortANTRecords)(state.Records);
179
+ }
116
180
  const tags = [{ name: 'Action', value: 'Records' }];
117
181
  const records = await this.process.read({
118
182
  tags,
@@ -131,6 +195,10 @@ class AoANTReadable {
131
195
  * ```
132
196
  */
133
197
  async getOwner({ strict } = { strict: this.strict }) {
198
+ if (await this.checkHyperBeamCompatibility()) {
199
+ const state = await this.getState();
200
+ return state.Owner;
201
+ }
134
202
  const info = await this.getInfo({ strict });
135
203
  return info.Owner;
136
204
  }
@@ -143,6 +211,10 @@ class AoANTReadable {
143
211
  * ```
144
212
  */
145
213
  async getControllers({ strict } = { strict: this.strict }) {
214
+ if (await this.checkHyperBeamCompatibility()) {
215
+ const state = await this.getState();
216
+ return state.Controllers;
217
+ }
146
218
  const tags = [{ name: 'Action', value: 'Controllers' }];
147
219
  const controllers = await this.process.read({
148
220
  tags,
@@ -160,6 +232,10 @@ class AoANTReadable {
160
232
  * ```
161
233
  */
162
234
  async getName({ strict } = { strict: this.strict }) {
235
+ if (await this.checkHyperBeamCompatibility()) {
236
+ const state = await this.getState();
237
+ return state.Name;
238
+ }
163
239
  const info = await this.getInfo({ strict });
164
240
  return info.Name;
165
241
  }
@@ -172,6 +248,10 @@ class AoANTReadable {
172
248
  * ```
173
249
  */
174
250
  async getTicker({ strict } = { strict: this.strict }) {
251
+ if (await this.checkHyperBeamCompatibility()) {
252
+ const state = await this.getState();
253
+ return state.Ticker;
254
+ }
175
255
  const info = await this.getInfo({ strict });
176
256
  return info.Ticker;
177
257
  }
@@ -184,6 +264,21 @@ class AoANTReadable {
184
264
  * ```
185
265
  */
186
266
  async getBalances({ strict } = { strict: this.strict }) {
267
+ if (await this.checkHyperBeamCompatibility()) {
268
+ const res = await fetch(`${this.hyperbeamUrl}/${this.processId}~process@1.0/now/cache/balances/serialize~json@1.0`, {
269
+ method: 'GET',
270
+ redirect: 'follow',
271
+ mode: 'cors',
272
+ headers: {
273
+ 'Content-Type': 'application/json',
274
+ },
275
+ });
276
+ if (!res.ok) {
277
+ throw new Error('Failed to fetch ant balances');
278
+ }
279
+ const result = (await res.json());
280
+ return result.balances;
281
+ }
187
282
  const tags = [{ name: 'Action', value: 'Balances' }];
188
283
  const balances = await this.process.read({
189
284
  tags,
@@ -202,6 +297,10 @@ class AoANTReadable {
202
297
  * ```
203
298
  */
204
299
  async getBalance({ address }, { strict } = { strict: this.strict }) {
300
+ if (await this.checkHyperBeamCompatibility()) {
301
+ const balances = await this.getBalances();
302
+ return balances[address] ?? 0;
303
+ }
205
304
  const tags = [
206
305
  { name: 'Action', value: 'Balance' },
207
306
  { name: 'Recipient', value: address },
@@ -222,6 +321,9 @@ class AoANTReadable {
222
321
  * ```
223
322
  */
224
323
  async getHandlers() {
324
+ if (await this.checkHyperBeamCompatibility()) {
325
+ throw new Error('Handlers are not supported on HyperBeam');
326
+ }
225
327
  const info = await this.getInfo();
226
328
  return (info.Handlers ?? info.HandlerNames);
227
329
  }
@@ -486,7 +588,6 @@ class AoANTWriteable extends AoANTReadable {
486
588
  /**
487
589
  * Releases an ArNS name associated with the ANT. This will release the name to the public and allow anyone to register it. All primary names must be removed before the name can be released.
488
590
  *
489
- *
490
591
  * @param name @type {string} The name you want to release. The name will be put up for as a recently returned name on the ARIO contract. 50% of the winning bid will be distributed to the ANT owner at the time of purchase. If no purchase in the recently returned name period (14 epochs), the name will be released and can be reregistered by anyone.
491
592
  * @param arioProcessId @type {string} The processId of the ARIO contract. This is where the ANT will send the message to release the name.
492
593
  * @returns {Promise<AoMessageResult>} The result of the interaction.
@@ -38,5 +38,3 @@ __exportStar(require("./faucet.js"), exports);
38
38
  // ao
39
39
  __exportStar(require("./io.js"), exports);
40
40
  __exportStar(require("./contracts/ao-process.js"), exports);
41
- // wayfinder
42
- __exportStar(require("./wayfinder/index.js"), exports);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sortANTRecords = void 0;
3
+ exports.convertHyperBeamStateToAoANTState = exports.sortANTRecords = void 0;
4
4
  /**
5
5
  * Sorts ANT records by priority and then lexicographically.
6
6
  *
@@ -41,3 +41,34 @@ const sortANTRecords = (antRecords) => {
41
41
  return Object.fromEntries(sortedEntries.map(([a, aRecord], index) => [a, { ...aRecord, index }]));
42
42
  };
43
43
  exports.sortANTRecords = sortANTRecords;
44
+ /**
45
+ * Convert HyperBeam serialized ANT state to backwards compatible format.
46
+ *
47
+ * @param state - The HyperBeam serialized ANT state.
48
+ */
49
+ const convertHyperBeamStateToAoANTState = (state) => {
50
+ return {
51
+ Name: state.name,
52
+ Ticker: state.ticker,
53
+ Description: state.description,
54
+ Keywords: state.keywords,
55
+ Denomination: parseInt(state.denomination),
56
+ Owner: state.owner,
57
+ Controllers: state.controllers,
58
+ Records: Object.entries(state.records).reduce((acc, [key, record]) => {
59
+ acc[key] = {
60
+ transactionId: record.transactionid,
61
+ ttlSeconds: record.ttlseconds,
62
+ ...(record.priority !== undefined
63
+ ? { priority: record.priority }
64
+ : {}),
65
+ };
66
+ return acc;
67
+ }, {}),
68
+ Balances: state.balances,
69
+ Logo: state.logo,
70
+ TotalSupply: state.totalsupply || 1,
71
+ Initialized: state.initialized,
72
+ };
73
+ };
74
+ exports.convertHyperBeamStateToAoANTState = convertHyperBeamStateToAoANTState;
@@ -1,9 +1,6 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.convertDataStreamToDataRoot = exports.hashDataStreamToB64Url = exports.readableStreamToAsyncIterable = exports.isReadableStream = exports.isAsyncIterable = void 0;
3
+ exports.hashBufferToB64Url = exports.hashReadableStreamToB64Url = exports.hashReadableToB64Url = void 0;
7
4
  /**
8
5
  * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
9
6
  *
@@ -19,90 +16,41 @@ exports.convertDataStreamToDataRoot = exports.hashDataStreamToB64Url = exports.r
19
16
  * See the License for the specific language governing permissions and
20
17
  * limitations under the License.
21
18
  */
22
- const arweave_1 = __importDefault(require("arweave"));
23
- const merkle_js_1 = require("arweave/node/lib/merkle.js");
24
19
  const crypto_1 = require("crypto");
25
20
  const base64_js_1 = require("./base64.js");
26
- const isAsyncIterable = (x) => {
27
- return x && typeof x[Symbol.asyncIterator] === 'function';
28
- };
29
- exports.isAsyncIterable = isAsyncIterable;
30
- const isReadableStream = (x) => {
31
- return x && typeof x.getReader === 'function';
21
+ const hashReadableToB64Url = (stream, algorithm = 'sha256') => {
22
+ return new Promise((resolve, reject) => {
23
+ const hash = (0, crypto_1.createHash)(algorithm);
24
+ stream.on('data', (chunk) => hash.update(chunk));
25
+ stream.on('end', () => resolve((0, base64_js_1.toB64Url)(hash.digest())));
26
+ stream.on('error', (err) => reject(err));
27
+ });
32
28
  };
33
- exports.isReadableStream = isReadableStream;
34
- // convert ReadableStream to async iterable
35
- const readableStreamToAsyncIterable = (stream) => ({
36
- async *[Symbol.asyncIterator]() {
29
+ exports.hashReadableToB64Url = hashReadableToB64Url;
30
+ const hashReadableStreamToB64Url = (stream, algorithm = 'sha256') => {
31
+ return new Promise((resolve, reject) => {
32
+ const hash = (0, crypto_1.createHash)(algorithm);
37
33
  const reader = stream.getReader();
38
- try {
39
- while (true) {
34
+ const read = async () => {
35
+ try {
40
36
  const { done, value } = await reader.read();
41
- if (done)
42
- break;
43
- if (value !== undefined)
44
- yield value;
37
+ if (done) {
38
+ resolve((0, base64_js_1.toB64Url)(hash.digest()));
39
+ }
40
+ else {
41
+ hash.update(value);
42
+ read();
43
+ }
45
44
  }
46
- }
47
- finally {
48
- reader.releaseLock();
49
- }
50
- },
51
- });
52
- exports.readableStreamToAsyncIterable = readableStreamToAsyncIterable;
53
- const hashDataStreamToB64Url = async (stream, algorithm = 'sha256') => {
54
- const asyncIterable = (0, exports.isAsyncIterable)(stream)
55
- ? stream
56
- : (0, exports.readableStreamToAsyncIterable)(stream);
57
- const hash = (0, crypto_1.createHash)(algorithm);
58
- for await (const chunk of asyncIterable) {
59
- hash.update(chunk);
60
- }
61
- return (0, base64_js_1.toB64Url)(hash.digest());
62
- };
63
- exports.hashDataStreamToB64Url = hashDataStreamToB64Url;
64
- const convertDataStreamToDataRoot = async ({ dataStream, }) => {
65
- const chunks = [];
66
- let leftover = new Uint8Array(0);
67
- let cursor = 0;
68
- const asyncIterable = (0, exports.isAsyncIterable)(dataStream)
69
- ? dataStream
70
- : (0, exports.readableStreamToAsyncIterable)(dataStream);
71
- for await (const data of asyncIterable) {
72
- const inputChunk = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
73
- const combined = new Uint8Array(leftover.length + inputChunk.length);
74
- combined.set(leftover, 0);
75
- combined.set(inputChunk, leftover.length);
76
- let startIndex = 0;
77
- while (combined.length - startIndex >= merkle_js_1.MAX_CHUNK_SIZE) {
78
- let chunkSize = merkle_js_1.MAX_CHUNK_SIZE;
79
- const remainderAfterThis = combined.length - startIndex - merkle_js_1.MAX_CHUNK_SIZE;
80
- if (remainderAfterThis > 0 && remainderAfterThis < merkle_js_1.MIN_CHUNK_SIZE) {
81
- chunkSize = Math.ceil((combined.length - startIndex) / 2);
45
+ catch (err) {
46
+ reject(err);
82
47
  }
83
- const chunkData = combined.slice(startIndex, startIndex + chunkSize);
84
- const dataHash = await arweave_1.default.crypto.hash(chunkData);
85
- chunks.push({
86
- dataHash,
87
- minByteRange: cursor,
88
- maxByteRange: cursor + chunkSize,
89
- });
90
- cursor += chunkSize;
91
- startIndex += chunkSize;
92
- }
93
- leftover = combined.slice(startIndex);
94
- }
95
- if (leftover.length > 0) {
96
- // TODO: ensure a web friendly crypto hash function is used in web
97
- const dataHash = await arweave_1.default.crypto.hash(leftover);
98
- chunks.push({
99
- dataHash,
100
- minByteRange: cursor,
101
- maxByteRange: cursor + leftover.length,
102
- });
103
- }
104
- const leaves = await (0, merkle_js_1.generateLeaves)(chunks);
105
- const root = await (0, merkle_js_1.buildLayers)(leaves);
106
- return (0, base64_js_1.toB64Url)(Buffer.from(root.id));
48
+ };
49
+ read().catch(reject);
50
+ });
51
+ };
52
+ exports.hashReadableStreamToB64Url = hashReadableStreamToB64Url;
53
+ const hashBufferToB64Url = (buffer, algorithm = 'sha256') => {
54
+ return (0, base64_js_1.toB64Url)((0, crypto_1.createHash)(algorithm).update(buffer).digest());
107
55
  };
108
- exports.convertDataStreamToDataRoot = convertDataStreamToDataRoot;
56
+ exports.hashBufferToB64Url = hashBufferToB64Url;
@@ -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.13.0-beta.1';
20
+ exports.version = '3.14.0-alpha.1';
@@ -477,6 +477,14 @@ makeCommand({
477
477
  return readANTFromOptions(options).getTicker();
478
478
  },
479
479
  });
480
+ makeCommand({
481
+ name: 'get-ant-logo',
482
+ description: 'Get the logo of an ANT process',
483
+ options: [optionMap.processId],
484
+ action: async (options) => {
485
+ return readANTFromOptions(options).getLogo();
486
+ },
487
+ });
480
488
  makeCommand({
481
489
  name: 'get-ant-balance',
482
490
  description: 'Get the balance of an ANT process',
@@ -16,7 +16,7 @@
16
16
  import { z } from 'zod';
17
17
  import { AntBalancesSchema, AntControllersSchema, AntInfoSchema, AntRecordSchema, AntRecordsSchema, AntStateSchema, } from '../types/ant.js';
18
18
  import { isProcessConfiguration, isProcessIdConfiguration, } from '../types/index.js';
19
- import { sortANTRecords } from '../utils/ant.js';
19
+ import { convertHyperBeamStateToAoANTState, sortANTRecords, } from '../utils/ant.js';
20
20
  import { createAoSigner } from '../utils/ao.js';
21
21
  import { parseSchemaResult } from '../utils/schema.js';
22
22
  import { ANTVersions } from './ant-versions.js';
@@ -35,6 +35,8 @@ export class AoANTReadable {
35
35
  process;
36
36
  processId;
37
37
  strict;
38
+ hyperbeamUrl;
39
+ checkHyperBeamPromise;
38
40
  constructor(config) {
39
41
  this.strict = config.strict || false;
40
42
  if (isProcessConfiguration(config)) {
@@ -49,8 +51,43 @@ export class AoANTReadable {
49
51
  throw new InvalidContractConfigurationError();
50
52
  }
51
53
  this.processId = this.process.processId;
54
+ this.hyperbeamUrl = config.hyperbeamUrl || 'https://permanode.xyz'; // TODO: replace this with hyperbeam.ario.permaweb.services once deployed
55
+ this.checkHyperBeamPromise = this.checkHyperBeamCompatibility();
56
+ }
57
+ /**
58
+ * Check if the process is hyperbeam compatible. If so, we'll use the hyperbeam node to fetch the state.
59
+ *
60
+ * @returns {Promise<boolean>} True if the process is hyperbeam compatible, false otherwise.
61
+ */
62
+ async checkHyperBeamCompatibility() {
63
+ if (this.checkHyperBeamPromise !== undefined) {
64
+ return this.checkHyperBeamPromise;
65
+ }
66
+ const res = await fetch(`https://permanode.xyz/${this.processId}~process@1.0/now/cache`, {
67
+ method: 'HEAD',
68
+ });
69
+ let isHyperBeamCompatible = false;
70
+ if (res.ok) {
71
+ isHyperBeamCompatible = true;
72
+ }
73
+ this.checkHyperBeamPromise = Promise.resolve(isHyperBeamCompatible);
74
+ return isHyperBeamCompatible;
52
75
  }
53
76
  async getState({ strict } = { strict: this.strict }) {
77
+ if (await this.checkHyperBeamCompatibility()) {
78
+ const res = await fetch(`${this.hyperbeamUrl}/${this.processId}~process@1.0/now/cache/serialize~json@1.0`, {
79
+ method: 'GET',
80
+ redirect: 'follow',
81
+ mode: 'cors',
82
+ headers: {
83
+ 'Content-Type': 'application/json',
84
+ },
85
+ });
86
+ if (!res.ok) {
87
+ throw new Error('Failed to fetch ant state');
88
+ }
89
+ return convertHyperBeamStateToAoANTState((await res.json()));
90
+ }
54
91
  const tags = [{ name: 'Action', value: 'State' }];
55
92
  const res = await this.process.read({
56
93
  tags,
@@ -63,6 +100,20 @@ export class AoANTReadable {
63
100
  return res;
64
101
  }
65
102
  async getInfo({ strict } = { strict: this.strict }) {
103
+ if (await this.checkHyperBeamCompatibility()) {
104
+ const state = await this.getState();
105
+ return {
106
+ Name: state.Name,
107
+ Ticker: state.Ticker,
108
+ Description: state.Description,
109
+ Keywords: state.Keywords,
110
+ Denomination: state.Denomination.toString(),
111
+ Owner: state.Owner,
112
+ Logo: state.Logo,
113
+ 'Total-Supply': state.TotalSupply.toString(),
114
+ Handlers: [], // TODO: support for handler in patched state
115
+ };
116
+ }
66
117
  const tags = [{ name: 'Action', value: 'Info' }];
67
118
  const info = await this.process.read({
68
119
  tags,
@@ -89,6 +140,15 @@ export class AoANTReadable {
89
140
  * ```
90
141
  */
91
142
  async getRecord({ undername }, { strict } = { strict: this.strict }) {
143
+ if (await this.checkHyperBeamCompatibility()) {
144
+ const records = await this.getRecords();
145
+ const cachedRecord = records[undername];
146
+ // if the record is not found, throw an error
147
+ if (cachedRecord === undefined) {
148
+ throw new Error('Record not found');
149
+ }
150
+ return cachedRecord;
151
+ }
92
152
  // TODO: use sortedANTRecords to get priority on all records, even if ANT does not have a priority set
93
153
  const record = await this.process.read({
94
154
  tags: [
@@ -109,6 +169,10 @@ export class AoANTReadable {
109
169
  * ````
110
170
  */
111
171
  async getRecords({ strict } = { strict: this.strict }) {
172
+ if (await this.checkHyperBeamCompatibility()) {
173
+ const state = await this.getState();
174
+ return sortANTRecords(state.Records);
175
+ }
112
176
  const tags = [{ name: 'Action', value: 'Records' }];
113
177
  const records = await this.process.read({
114
178
  tags,
@@ -127,6 +191,10 @@ export class AoANTReadable {
127
191
  * ```
128
192
  */
129
193
  async getOwner({ strict } = { strict: this.strict }) {
194
+ if (await this.checkHyperBeamCompatibility()) {
195
+ const state = await this.getState();
196
+ return state.Owner;
197
+ }
130
198
  const info = await this.getInfo({ strict });
131
199
  return info.Owner;
132
200
  }
@@ -139,6 +207,10 @@ export class AoANTReadable {
139
207
  * ```
140
208
  */
141
209
  async getControllers({ strict } = { strict: this.strict }) {
210
+ if (await this.checkHyperBeamCompatibility()) {
211
+ const state = await this.getState();
212
+ return state.Controllers;
213
+ }
142
214
  const tags = [{ name: 'Action', value: 'Controllers' }];
143
215
  const controllers = await this.process.read({
144
216
  tags,
@@ -156,6 +228,10 @@ export class AoANTReadable {
156
228
  * ```
157
229
  */
158
230
  async getName({ strict } = { strict: this.strict }) {
231
+ if (await this.checkHyperBeamCompatibility()) {
232
+ const state = await this.getState();
233
+ return state.Name;
234
+ }
159
235
  const info = await this.getInfo({ strict });
160
236
  return info.Name;
161
237
  }
@@ -168,6 +244,10 @@ export class AoANTReadable {
168
244
  * ```
169
245
  */
170
246
  async getTicker({ strict } = { strict: this.strict }) {
247
+ if (await this.checkHyperBeamCompatibility()) {
248
+ const state = await this.getState();
249
+ return state.Ticker;
250
+ }
171
251
  const info = await this.getInfo({ strict });
172
252
  return info.Ticker;
173
253
  }
@@ -180,6 +260,21 @@ export class AoANTReadable {
180
260
  * ```
181
261
  */
182
262
  async getBalances({ strict } = { strict: this.strict }) {
263
+ if (await this.checkHyperBeamCompatibility()) {
264
+ const res = await fetch(`${this.hyperbeamUrl}/${this.processId}~process@1.0/now/cache/balances/serialize~json@1.0`, {
265
+ method: 'GET',
266
+ redirect: 'follow',
267
+ mode: 'cors',
268
+ headers: {
269
+ 'Content-Type': 'application/json',
270
+ },
271
+ });
272
+ if (!res.ok) {
273
+ throw new Error('Failed to fetch ant balances');
274
+ }
275
+ const result = (await res.json());
276
+ return result.balances;
277
+ }
183
278
  const tags = [{ name: 'Action', value: 'Balances' }];
184
279
  const balances = await this.process.read({
185
280
  tags,
@@ -198,6 +293,10 @@ export class AoANTReadable {
198
293
  * ```
199
294
  */
200
295
  async getBalance({ address }, { strict } = { strict: this.strict }) {
296
+ if (await this.checkHyperBeamCompatibility()) {
297
+ const balances = await this.getBalances();
298
+ return balances[address] ?? 0;
299
+ }
201
300
  const tags = [
202
301
  { name: 'Action', value: 'Balance' },
203
302
  { name: 'Recipient', value: address },
@@ -218,6 +317,9 @@ export class AoANTReadable {
218
317
  * ```
219
318
  */
220
319
  async getHandlers() {
320
+ if (await this.checkHyperBeamCompatibility()) {
321
+ throw new Error('Handlers are not supported on HyperBeam');
322
+ }
221
323
  const info = await this.getInfo();
222
324
  return (info.Handlers ?? info.HandlerNames);
223
325
  }
@@ -481,7 +583,6 @@ export class AoANTWriteable extends AoANTReadable {
481
583
  /**
482
584
  * Releases an ArNS name associated with the ANT. This will release the name to the public and allow anyone to register it. All primary names must be removed before the name can be released.
483
585
  *
484
- *
485
586
  * @param name @type {string} The name you want to release. The name will be put up for as a recently returned name on the ARIO contract. 50% of the winning bid will be distributed to the ANT owner at the time of purchase. If no purchase in the recently returned name period (14 epochs), the name will be released and can be reregistered by anyone.
486
587
  * @param arioProcessId @type {string} The processId of the ARIO contract. This is where the ANT will send the message to release the name.
487
588
  * @returns {Promise<AoMessageResult>} The result of the interaction.
@@ -22,5 +22,3 @@ export * from './faucet.js';
22
22
  // ao
23
23
  export * from './io.js';
24
24
  export * from './contracts/ao-process.js';
25
- // wayfinder
26
- export * from './wayfinder/index.js';
@@ -37,3 +37,33 @@ export const sortANTRecords = (antRecords) => {
37
37
  // now that they are sorted, add the index to each record - this is their position in the sorted list and is used to enforce undername limits
38
38
  return Object.fromEntries(sortedEntries.map(([a, aRecord], index) => [a, { ...aRecord, index }]));
39
39
  };
40
+ /**
41
+ * Convert HyperBeam serialized ANT state to backwards compatible format.
42
+ *
43
+ * @param state - The HyperBeam serialized ANT state.
44
+ */
45
+ export const convertHyperBeamStateToAoANTState = (state) => {
46
+ return {
47
+ Name: state.name,
48
+ Ticker: state.ticker,
49
+ Description: state.description,
50
+ Keywords: state.keywords,
51
+ Denomination: parseInt(state.denomination),
52
+ Owner: state.owner,
53
+ Controllers: state.controllers,
54
+ Records: Object.entries(state.records).reduce((acc, [key, record]) => {
55
+ acc[key] = {
56
+ transactionId: record.transactionid,
57
+ ttlSeconds: record.ttlseconds,
58
+ ...(record.priority !== undefined
59
+ ? { priority: record.priority }
60
+ : {}),
61
+ };
62
+ return acc;
63
+ }, {}),
64
+ Balances: state.balances,
65
+ Logo: state.logo,
66
+ TotalSupply: state.totalsupply || 1,
67
+ Initialized: state.initialized,
68
+ };
69
+ };