@ar.io/sdk 3.9.0-alpha.4 → 3.9.0-alpha.6

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.
@@ -68,13 +68,14 @@ class AOProcess {
68
68
  processId: this.processId,
69
69
  });
70
70
  if (attempts >= retries) {
71
- this.logger.error(`Maximum read attempts exceeded`, {
71
+ this.logger.debug(`Maximum read attempts exceeded`, {
72
72
  error: error?.message,
73
73
  stack: error?.stack,
74
74
  tags,
75
75
  processId: this.processId,
76
+ ao: JSON.stringify(this.ao),
76
77
  });
77
- throw new Error(`Maximum read attempts exceeded for process ${this.processId}`);
78
+ throw new Error(`Failed to evaluate a dry-run on process ${this.processId}.`);
78
79
  }
79
80
  // exponential backoff
80
81
  await new Promise((resolve) => setTimeout(resolve, 2 ** attempts * 1000));
@@ -92,7 +93,7 @@ class AOProcess {
92
93
  throw new Error(error);
93
94
  }
94
95
  if (result.Messages === undefined || result.Messages.length === 0) {
95
- this.logger.debug(`Process ${this.processId} does not support provided action.`, {
96
+ this.logger.debug(`Empty result - process ${this.processId} does not support provided action.`, {
96
97
  result,
97
98
  tags,
98
99
  processId: this.processId,
@@ -108,32 +109,50 @@ class AOProcess {
108
109
  return response;
109
110
  }
110
111
  async send({ tags, data, signer, retries = 3, }) {
111
- // main purpose of retries is to handle network errors/new process delays
112
- let attempts = 0;
113
112
  let messageId;
113
+ const anchor = (0, base64_js_1.getRandomText)(32); // anchor is a random text produce non-deterministic messages IDs when deterministic signers are provided (ETH)
114
+ try {
115
+ this.logger.debug(`Evaluating send interaction on contract`, {
116
+ tags,
117
+ data,
118
+ processId: this.processId,
119
+ });
120
+ /**
121
+ * DO NOT retry messaging if a message was already sent.
122
+ * This could result in a double entry-like condition when sending tokens for example.
123
+ * If the message fails to send we will throw an error and the caller can retry.
124
+ */
125
+ messageId = await this.ao.message({
126
+ process: this.processId,
127
+ tags: [...tags, { name: 'AR-IO-SDK', value: version_js_1.version }],
128
+ data,
129
+ signer,
130
+ anchor,
131
+ });
132
+ this.logger.debug(`Sent message to process`, {
133
+ messageId,
134
+ processId: this.processId,
135
+ anchor,
136
+ });
137
+ }
138
+ catch (error) {
139
+ this.logger.debug('Error sending message to process', {
140
+ error: error?.message,
141
+ stack: error?.stack,
142
+ processId: this.processId,
143
+ tags,
144
+ });
145
+ // throw the error so it can be handled by the caller
146
+ throw error;
147
+ }
148
+ if (messageId === undefined) {
149
+ throw new Error('Failed to send message to process.');
150
+ }
151
+ // get the result of the message before returning, using retries to handle network errors/new process delays
114
152
  let result = undefined;
115
- // anchor is a random text produce non-deterministic messages IDs when deterministic signers are provided (ETH)
116
- const anchor = (0, base64_js_1.getRandomText)(32);
153
+ let attempts = 0;
117
154
  while (attempts < retries) {
118
155
  try {
119
- this.logger.debug(`Evaluating send interaction on contract`, {
120
- tags,
121
- data,
122
- processId: this.processId,
123
- });
124
- // MUST NOT retry messaging if a message was already sent. This could result in a double entry-like condition when sending tokens for example.
125
- messageId ??= await this.ao.message({
126
- process: this.processId,
127
- tags: [...tags, { name: 'AR-IO-SDK', value: version_js_1.version }],
128
- data,
129
- signer,
130
- anchor,
131
- });
132
- this.logger.debug(`Sent message to process`, {
133
- messageId,
134
- processId: this.processId,
135
- anchor,
136
- });
137
156
  result = await this.ao.result({
138
157
  message: messageId,
139
158
  process: this.processId,
@@ -146,12 +165,6 @@ class AOProcess {
146
165
  break;
147
166
  }
148
167
  catch (error) {
149
- this.logger.error('Error sending message to process', {
150
- error: error?.message,
151
- stack: error?.stack,
152
- processId: this.processId,
153
- tags,
154
- });
155
168
  attempts++;
156
169
  this.logger.debug('Retrying send interaction', {
157
170
  attempts,
@@ -160,20 +173,26 @@ class AOProcess {
160
173
  processId: this.processId,
161
174
  });
162
175
  if (attempts >= retries) {
163
- this.logger.error(`Maximum read result attempts exceeded`, {
176
+ this.logger.debug(`Message was sent to process ${this.processId} with id ${messageId} but result was not returned. Review transactions for more details.`, {
164
177
  error: error?.message,
165
178
  stack: error?.stack,
166
179
  tags,
167
180
  processId: this.processId,
181
+ messageId,
168
182
  });
169
- throw new Error(`Maximum read result attempts exceeded for process ${this.processId}.`);
183
+ return { id: messageId };
170
184
  }
171
185
  // exponential backoff
172
186
  await new Promise((resolve) => setTimeout(resolve, 2 ** attempts * 2000));
173
187
  }
174
188
  }
175
- if (result === undefined || messageId === undefined) {
176
- throw new Error('Unexpected error when evaluating write interaction');
189
+ if (result === undefined) {
190
+ this.logger.debug(`Message was sent to process ${this.processId} with id ${messageId} but the result was not returned. Review transactions for more details.`, {
191
+ tags,
192
+ processId: this.processId,
193
+ messageId,
194
+ });
195
+ return { id: messageId };
177
196
  }
178
197
  const error = (0, index_js_1.errorMessageFromOutput)(result);
179
198
  if (error !== undefined) {
@@ -183,9 +202,6 @@ class AOProcess {
183
202
  if (result.Messages?.length === 0 || result.Messages === undefined) {
184
203
  return { id: messageId };
185
204
  }
186
- if (result.Messages.length === 0) {
187
- throw new Error(`Process ${this.processId} does not support provided action.`);
188
- }
189
205
  if (this.isMessageDataEmpty(result.Messages[0].Data)) {
190
206
  return { id: messageId };
191
207
  }
@@ -84,10 +84,11 @@ class ARIOReadable {
84
84
  const epochIndex = await this.computeEpochIndex(epoch);
85
85
  const currentIndex = await this.computeCurrentEpochIndex();
86
86
  if (epochIndex !== undefined && epochIndex < currentIndex) {
87
- const epochData = await (0, arweave_js_1.getEpochDataFromGql)({
87
+ const epochData = await (0, arweave_js_1.getEpochDataFromGqlWithCUFallback)({
88
88
  arweave: this.arweave,
89
89
  epochIndex: epochIndex,
90
90
  processId: this.process.processId,
91
+ ao: this.process.ao,
91
92
  });
92
93
  return (0, arweave_js_1.removeEligibleRewardsFromEpochData)(epochData);
93
94
  }
@@ -211,7 +212,8 @@ class ARIOReadable {
211
212
  const epochIndex = await this.computeEpochIndex(epoch);
212
213
  const currentIndex = await this.computeCurrentEpochIndex();
213
214
  if (epochIndex !== undefined && epochIndex < currentIndex) {
214
- const epochData = await (0, arweave_js_1.getEpochDataFromGql)({
215
+ const epochData = await (0, arweave_js_1.getEpochDataFromGqlWithCUFallback)({
216
+ ao: this.process.ao,
215
217
  arweave: this.arweave,
216
218
  epochIndex: epochIndex,
217
219
  processId: this.process.processId,
@@ -233,10 +235,11 @@ class ARIOReadable {
233
235
  const epochIndex = await this.computeEpochIndex(epoch);
234
236
  const currentIndex = await this.computeCurrentEpochIndex();
235
237
  if (epochIndex !== undefined && epochIndex < currentIndex) {
236
- const epochData = await (0, arweave_js_1.getEpochDataFromGql)({
238
+ const epochData = await (0, arweave_js_1.getEpochDataFromGqlWithCUFallback)({
237
239
  arweave: this.arweave,
238
240
  epochIndex: epochIndex,
239
241
  processId: this.process.processId,
242
+ ao: this.process.ao,
240
243
  });
241
244
  return epochData?.prescribedNames;
242
245
  }
@@ -256,10 +259,11 @@ class ARIOReadable {
256
259
  const epochIndex = await this.computeEpochIndex(epoch);
257
260
  const currentIndex = await this.computeCurrentEpochIndex();
258
261
  if (epochIndex !== undefined && epochIndex < currentIndex) {
259
- const epochData = await (0, arweave_js_1.getEpochDataFromGql)({
262
+ const epochData = await (0, arweave_js_1.getEpochDataFromGqlWithCUFallback)({
260
263
  arweave: this.arweave,
261
264
  epochIndex: epochIndex,
262
265
  processId: this.process.processId,
266
+ ao: this.process.ao,
263
267
  });
264
268
  return epochData?.observations;
265
269
  }
@@ -279,10 +283,11 @@ class ARIOReadable {
279
283
  const epochIndex = await this.computeEpochIndex(epoch);
280
284
  const currentIndex = await this.computeCurrentEpochIndex();
281
285
  if (epochIndex !== undefined && epochIndex < currentIndex) {
282
- const epochData = await (0, arweave_js_1.getEpochDataFromGql)({
286
+ const epochData = await (0, arweave_js_1.getEpochDataFromGqlWithCUFallback)({
283
287
  arweave: this.arweave,
284
288
  epochIndex: epochIndex,
285
289
  processId: this.process.processId,
290
+ ao: this.process.ao,
286
291
  });
287
292
  return epochData?.distributions;
288
293
  }
@@ -302,10 +307,11 @@ class ARIOReadable {
302
307
  const epochIndex = await this.computeEpochIndex(epoch);
303
308
  const currentIndex = await this.computeCurrentEpochIndex();
304
309
  if (epochIndex !== undefined && epochIndex < currentIndex) {
305
- const epochData = await (0, arweave_js_1.getEpochDataFromGql)({
310
+ const epochData = await (0, arweave_js_1.getEpochDataFromGqlWithCUFallback)({
306
311
  arweave: this.arweave,
307
312
  epochIndex: epochIndex,
308
313
  processId: this.process.processId,
314
+ ao: this.process.ao,
309
315
  });
310
316
  return (0, arweave_js_1.sortAndPaginateEpochDataIntoEligibleDistributions)(epochData, params);
311
317
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.removeEligibleRewardsFromEpochData = exports.sortAndPaginateEpochDataIntoEligibleDistributions = exports.epochDistributionNoticeGqlQuery = exports.getEpochDataFromGql = exports.paginationParamsToTags = exports.pruneTags = exports.isBlockHeight = exports.validateArweaveId = void 0;
3
+ exports.removeEligibleRewardsFromEpochData = exports.sortAndPaginateEpochDataIntoEligibleDistributions = exports.epochDistributionNoticeGqlQueryFallback = exports.epochDistributionNoticeGqlQuery = exports.getEpochDataFromGqlFallback = exports.getEpochDataFromGqlWithCUFallback = exports.getEpochDataFromGql = exports.paginationParamsToTags = exports.pruneTags = exports.isBlockHeight = exports.validateArweaveId = void 0;
4
4
  const constants_js_1 = require("../constants.js");
5
5
  const io_js_1 = require("../types/io.js");
6
6
  const ao_js_1 = require("./ao.js");
@@ -69,6 +69,66 @@ const getEpochDataFromGql = async ({ arweave, epochIndex, processId = constants_
69
69
  return undefined;
70
70
  };
71
71
  exports.getEpochDataFromGql = getEpochDataFromGql;
72
+ const getEpochDataFromGqlWithCUFallback = async ({ arweave, ao, epochIndex, processId = constants_js_1.ARIO_MAINNET_PROCESS_ID, }) => {
73
+ const gqlResult = await (0, exports.getEpochDataFromGql)({
74
+ arweave,
75
+ epochIndex,
76
+ processId,
77
+ });
78
+ if (gqlResult) {
79
+ return gqlResult;
80
+ }
81
+ const gqlFallbackResult = await (0, exports.getEpochDataFromGqlFallback)({
82
+ ao,
83
+ epochIndex,
84
+ processId,
85
+ });
86
+ if (gqlFallbackResult) {
87
+ return gqlFallbackResult;
88
+ }
89
+ return undefined;
90
+ };
91
+ exports.getEpochDataFromGqlWithCUFallback = getEpochDataFromGqlWithCUFallback;
92
+ const getEpochDataFromGqlFallback = async ({ ao, epochIndex, processId = constants_js_1.ARIO_MAINNET_PROCESS_ID, gqlUrl = 'https://arweave-search.goldsky.com/graphql', }) => {
93
+ const query = (0, exports.epochDistributionNoticeGqlQueryFallback)({
94
+ epochIndex,
95
+ processId,
96
+ });
97
+ const response = await fetch(gqlUrl, {
98
+ method: 'POST',
99
+ body: query,
100
+ headers: {
101
+ 'Content-Type': 'application/json',
102
+ },
103
+ });
104
+ const responseJson = (await response.json());
105
+ if (responseJson.data.transactions.edges.length === 0) {
106
+ return undefined;
107
+ }
108
+ for (const edge of responseJson.data.transactions.edges) {
109
+ const id = edge.node.id;
110
+ const messageResult = await ao
111
+ .result({
112
+ message: id,
113
+ process: processId,
114
+ })
115
+ .catch(() => undefined);
116
+ if (!messageResult) {
117
+ continue;
118
+ }
119
+ for (const message of messageResult.Messages) {
120
+ const data = JSON.parse(message.Data);
121
+ const tags = message.Tags;
122
+ // check if the message results include epoch-distribution-notice for the requested epoch index
123
+ if (tags.some((tag) => tag.name === 'Action' && tag.value === 'Epoch-Distribution-Notice') &&
124
+ tags.some((tag) => tag.name === 'Epoch-Index' && tag.value === epochIndex.toString())) {
125
+ return (0, ao_js_1.parseAoEpochData)(data);
126
+ }
127
+ }
128
+ }
129
+ return undefined;
130
+ };
131
+ exports.getEpochDataFromGqlFallback = getEpochDataFromGqlFallback;
72
132
  /**
73
133
  * Get the epoch with distribution data for the current epoch
74
134
  * @param arweave - The Arweave instance
@@ -104,6 +164,32 @@ const epochDistributionNoticeGqlQuery = ({ epochIndex, processId = constants_js_
104
164
  return gqlQuery;
105
165
  };
106
166
  exports.epochDistributionNoticeGqlQuery = epochDistributionNoticeGqlQuery;
167
+ // fallback query if the distribution notice does not get cranked
168
+ const epochDistributionNoticeGqlQueryFallback = ({ processId = constants_js_1.ARIO_MAINNET_PROCESS_ID, owners = ['OAb-n-ZugyN598kZNpfOy0ACelGVmwCQ0kYbgNGDUK8'], // ar.io team wallet ticks once a day
169
+ }) => {
170
+ return JSON.stringify({
171
+ query: `
172
+ query {
173
+ transactions(
174
+ tags: [
175
+ { name: "Action", values: ["Tick"] }
176
+ ],
177
+ first: 100,
178
+ owners: [${owners.map((a) => `"${a}"`).join(',')}],
179
+ recipients: ["${processId}"],
180
+ sort: HEIGHT_DESC
181
+ ) {
182
+ edges {
183
+ node {
184
+ id
185
+ }
186
+ }
187
+ }
188
+ }
189
+ `,
190
+ });
191
+ };
192
+ exports.epochDistributionNoticeGqlQueryFallback = epochDistributionNoticeGqlQueryFallback;
107
193
  function sortAndPaginateEpochDataIntoEligibleDistributions(epochData, params) {
108
194
  const rewards = [];
109
195
  const sortBy = params?.sortBy ?? 'eligibleReward';
@@ -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.9.0-alpha.4';
20
+ exports.version = '3.9.0-alpha.6';
@@ -65,13 +65,14 @@ export class AOProcess {
65
65
  processId: this.processId,
66
66
  });
67
67
  if (attempts >= retries) {
68
- this.logger.error(`Maximum read attempts exceeded`, {
68
+ this.logger.debug(`Maximum read attempts exceeded`, {
69
69
  error: error?.message,
70
70
  stack: error?.stack,
71
71
  tags,
72
72
  processId: this.processId,
73
+ ao: JSON.stringify(this.ao),
73
74
  });
74
- throw new Error(`Maximum read attempts exceeded for process ${this.processId}`);
75
+ throw new Error(`Failed to evaluate a dry-run on process ${this.processId}.`);
75
76
  }
76
77
  // exponential backoff
77
78
  await new Promise((resolve) => setTimeout(resolve, 2 ** attempts * 1000));
@@ -89,7 +90,7 @@ export class AOProcess {
89
90
  throw new Error(error);
90
91
  }
91
92
  if (result.Messages === undefined || result.Messages.length === 0) {
92
- this.logger.debug(`Process ${this.processId} does not support provided action.`, {
93
+ this.logger.debug(`Empty result - process ${this.processId} does not support provided action.`, {
93
94
  result,
94
95
  tags,
95
96
  processId: this.processId,
@@ -105,32 +106,50 @@ export class AOProcess {
105
106
  return response;
106
107
  }
107
108
  async send({ tags, data, signer, retries = 3, }) {
108
- // main purpose of retries is to handle network errors/new process delays
109
- let attempts = 0;
110
109
  let messageId;
110
+ const anchor = getRandomText(32); // anchor is a random text produce non-deterministic messages IDs when deterministic signers are provided (ETH)
111
+ try {
112
+ this.logger.debug(`Evaluating send interaction on contract`, {
113
+ tags,
114
+ data,
115
+ processId: this.processId,
116
+ });
117
+ /**
118
+ * DO NOT retry messaging if a message was already sent.
119
+ * This could result in a double entry-like condition when sending tokens for example.
120
+ * If the message fails to send we will throw an error and the caller can retry.
121
+ */
122
+ messageId = await this.ao.message({
123
+ process: this.processId,
124
+ tags: [...tags, { name: 'AR-IO-SDK', value: version }],
125
+ data,
126
+ signer,
127
+ anchor,
128
+ });
129
+ this.logger.debug(`Sent message to process`, {
130
+ messageId,
131
+ processId: this.processId,
132
+ anchor,
133
+ });
134
+ }
135
+ catch (error) {
136
+ this.logger.debug('Error sending message to process', {
137
+ error: error?.message,
138
+ stack: error?.stack,
139
+ processId: this.processId,
140
+ tags,
141
+ });
142
+ // throw the error so it can be handled by the caller
143
+ throw error;
144
+ }
145
+ if (messageId === undefined) {
146
+ throw new Error('Failed to send message to process.');
147
+ }
148
+ // get the result of the message before returning, using retries to handle network errors/new process delays
111
149
  let result = undefined;
112
- // anchor is a random text produce non-deterministic messages IDs when deterministic signers are provided (ETH)
113
- const anchor = getRandomText(32);
150
+ let attempts = 0;
114
151
  while (attempts < retries) {
115
152
  try {
116
- this.logger.debug(`Evaluating send interaction on contract`, {
117
- tags,
118
- data,
119
- processId: this.processId,
120
- });
121
- // MUST NOT retry messaging if a message was already sent. This could result in a double entry-like condition when sending tokens for example.
122
- messageId ??= await this.ao.message({
123
- process: this.processId,
124
- tags: [...tags, { name: 'AR-IO-SDK', value: version }],
125
- data,
126
- signer,
127
- anchor,
128
- });
129
- this.logger.debug(`Sent message to process`, {
130
- messageId,
131
- processId: this.processId,
132
- anchor,
133
- });
134
153
  result = await this.ao.result({
135
154
  message: messageId,
136
155
  process: this.processId,
@@ -143,12 +162,6 @@ export class AOProcess {
143
162
  break;
144
163
  }
145
164
  catch (error) {
146
- this.logger.error('Error sending message to process', {
147
- error: error?.message,
148
- stack: error?.stack,
149
- processId: this.processId,
150
- tags,
151
- });
152
165
  attempts++;
153
166
  this.logger.debug('Retrying send interaction', {
154
167
  attempts,
@@ -157,20 +170,26 @@ export class AOProcess {
157
170
  processId: this.processId,
158
171
  });
159
172
  if (attempts >= retries) {
160
- this.logger.error(`Maximum read result attempts exceeded`, {
173
+ this.logger.debug(`Message was sent to process ${this.processId} with id ${messageId} but result was not returned. Review transactions for more details.`, {
161
174
  error: error?.message,
162
175
  stack: error?.stack,
163
176
  tags,
164
177
  processId: this.processId,
178
+ messageId,
165
179
  });
166
- throw new Error(`Maximum read result attempts exceeded for process ${this.processId}.`);
180
+ return { id: messageId };
167
181
  }
168
182
  // exponential backoff
169
183
  await new Promise((resolve) => setTimeout(resolve, 2 ** attempts * 2000));
170
184
  }
171
185
  }
172
- if (result === undefined || messageId === undefined) {
173
- throw new Error('Unexpected error when evaluating write interaction');
186
+ if (result === undefined) {
187
+ this.logger.debug(`Message was sent to process ${this.processId} with id ${messageId} but the result was not returned. Review transactions for more details.`, {
188
+ tags,
189
+ processId: this.processId,
190
+ messageId,
191
+ });
192
+ return { id: messageId };
174
193
  }
175
194
  const error = errorMessageFromOutput(result);
176
195
  if (error !== undefined) {
@@ -180,9 +199,6 @@ export class AOProcess {
180
199
  if (result.Messages?.length === 0 || result.Messages === undefined) {
181
200
  return { id: messageId };
182
201
  }
183
- if (result.Messages.length === 0) {
184
- throw new Error(`Process ${this.processId} does not support provided action.`);
185
- }
186
202
  if (this.isMessageDataEmpty(result.Messages[0].Data)) {
187
203
  return { id: messageId };
188
204
  }
@@ -1,7 +1,7 @@
1
1
  import { ARIO_MAINNET_PROCESS_ID } from '../constants.js';
2
2
  import { isProcessConfiguration, isProcessIdConfiguration, } from '../types/io.js';
3
3
  import { createAoSigner } from '../utils/ao.js';
4
- import { getEpochDataFromGql, paginationParamsToTags, pruneTags, removeEligibleRewardsFromEpochData, sortAndPaginateEpochDataIntoEligibleDistributions, } from '../utils/arweave.js';
4
+ import { getEpochDataFromGqlWithCUFallback, paginationParamsToTags, pruneTags, removeEligibleRewardsFromEpochData, sortAndPaginateEpochDataIntoEligibleDistributions, } from '../utils/arweave.js';
5
5
  import { defaultArweave } from './arweave.js';
6
6
  import { AOProcess } from './contracts/ao-process.js';
7
7
  import { InvalidContractConfigurationError } from './error.js';
@@ -80,10 +80,11 @@ export class ARIOReadable {
80
80
  const epochIndex = await this.computeEpochIndex(epoch);
81
81
  const currentIndex = await this.computeCurrentEpochIndex();
82
82
  if (epochIndex !== undefined && epochIndex < currentIndex) {
83
- const epochData = await getEpochDataFromGql({
83
+ const epochData = await getEpochDataFromGqlWithCUFallback({
84
84
  arweave: this.arweave,
85
85
  epochIndex: epochIndex,
86
86
  processId: this.process.processId,
87
+ ao: this.process.ao,
87
88
  });
88
89
  return removeEligibleRewardsFromEpochData(epochData);
89
90
  }
@@ -207,7 +208,8 @@ export class ARIOReadable {
207
208
  const epochIndex = await this.computeEpochIndex(epoch);
208
209
  const currentIndex = await this.computeCurrentEpochIndex();
209
210
  if (epochIndex !== undefined && epochIndex < currentIndex) {
210
- const epochData = await getEpochDataFromGql({
211
+ const epochData = await getEpochDataFromGqlWithCUFallback({
212
+ ao: this.process.ao,
211
213
  arweave: this.arweave,
212
214
  epochIndex: epochIndex,
213
215
  processId: this.process.processId,
@@ -229,10 +231,11 @@ export class ARIOReadable {
229
231
  const epochIndex = await this.computeEpochIndex(epoch);
230
232
  const currentIndex = await this.computeCurrentEpochIndex();
231
233
  if (epochIndex !== undefined && epochIndex < currentIndex) {
232
- const epochData = await getEpochDataFromGql({
234
+ const epochData = await getEpochDataFromGqlWithCUFallback({
233
235
  arweave: this.arweave,
234
236
  epochIndex: epochIndex,
235
237
  processId: this.process.processId,
238
+ ao: this.process.ao,
236
239
  });
237
240
  return epochData?.prescribedNames;
238
241
  }
@@ -252,10 +255,11 @@ export class ARIOReadable {
252
255
  const epochIndex = await this.computeEpochIndex(epoch);
253
256
  const currentIndex = await this.computeCurrentEpochIndex();
254
257
  if (epochIndex !== undefined && epochIndex < currentIndex) {
255
- const epochData = await getEpochDataFromGql({
258
+ const epochData = await getEpochDataFromGqlWithCUFallback({
256
259
  arweave: this.arweave,
257
260
  epochIndex: epochIndex,
258
261
  processId: this.process.processId,
262
+ ao: this.process.ao,
259
263
  });
260
264
  return epochData?.observations;
261
265
  }
@@ -275,10 +279,11 @@ export class ARIOReadable {
275
279
  const epochIndex = await this.computeEpochIndex(epoch);
276
280
  const currentIndex = await this.computeCurrentEpochIndex();
277
281
  if (epochIndex !== undefined && epochIndex < currentIndex) {
278
- const epochData = await getEpochDataFromGql({
282
+ const epochData = await getEpochDataFromGqlWithCUFallback({
279
283
  arweave: this.arweave,
280
284
  epochIndex: epochIndex,
281
285
  processId: this.process.processId,
286
+ ao: this.process.ao,
282
287
  });
283
288
  return epochData?.distributions;
284
289
  }
@@ -298,10 +303,11 @@ export class ARIOReadable {
298
303
  const epochIndex = await this.computeEpochIndex(epoch);
299
304
  const currentIndex = await this.computeCurrentEpochIndex();
300
305
  if (epochIndex !== undefined && epochIndex < currentIndex) {
301
- const epochData = await getEpochDataFromGql({
306
+ const epochData = await getEpochDataFromGqlWithCUFallback({
302
307
  arweave: this.arweave,
303
308
  epochIndex: epochIndex,
304
309
  processId: this.process.processId,
310
+ ao: this.process.ao,
305
311
  });
306
312
  return sortAndPaginateEpochDataIntoEligibleDistributions(epochData, params);
307
313
  }
@@ -61,6 +61,64 @@ export const getEpochDataFromGql = async ({ arweave, epochIndex, processId = ARI
61
61
  }
62
62
  return undefined;
63
63
  };
64
+ export const getEpochDataFromGqlWithCUFallback = async ({ arweave, ao, epochIndex, processId = ARIO_MAINNET_PROCESS_ID, }) => {
65
+ const gqlResult = await getEpochDataFromGql({
66
+ arweave,
67
+ epochIndex,
68
+ processId,
69
+ });
70
+ if (gqlResult) {
71
+ return gqlResult;
72
+ }
73
+ const gqlFallbackResult = await getEpochDataFromGqlFallback({
74
+ ao,
75
+ epochIndex,
76
+ processId,
77
+ });
78
+ if (gqlFallbackResult) {
79
+ return gqlFallbackResult;
80
+ }
81
+ return undefined;
82
+ };
83
+ export const getEpochDataFromGqlFallback = async ({ ao, epochIndex, processId = ARIO_MAINNET_PROCESS_ID, gqlUrl = 'https://arweave-search.goldsky.com/graphql', }) => {
84
+ const query = epochDistributionNoticeGqlQueryFallback({
85
+ epochIndex,
86
+ processId,
87
+ });
88
+ const response = await fetch(gqlUrl, {
89
+ method: 'POST',
90
+ body: query,
91
+ headers: {
92
+ 'Content-Type': 'application/json',
93
+ },
94
+ });
95
+ const responseJson = (await response.json());
96
+ if (responseJson.data.transactions.edges.length === 0) {
97
+ return undefined;
98
+ }
99
+ for (const edge of responseJson.data.transactions.edges) {
100
+ const id = edge.node.id;
101
+ const messageResult = await ao
102
+ .result({
103
+ message: id,
104
+ process: processId,
105
+ })
106
+ .catch(() => undefined);
107
+ if (!messageResult) {
108
+ continue;
109
+ }
110
+ for (const message of messageResult.Messages) {
111
+ const data = JSON.parse(message.Data);
112
+ const tags = message.Tags;
113
+ // check if the message results include epoch-distribution-notice for the requested epoch index
114
+ if (tags.some((tag) => tag.name === 'Action' && tag.value === 'Epoch-Distribution-Notice') &&
115
+ tags.some((tag) => tag.name === 'Epoch-Index' && tag.value === epochIndex.toString())) {
116
+ return parseAoEpochData(data);
117
+ }
118
+ }
119
+ }
120
+ return undefined;
121
+ };
64
122
  /**
65
123
  * Get the epoch with distribution data for the current epoch
66
124
  * @param arweave - The Arweave instance
@@ -95,6 +153,31 @@ export const epochDistributionNoticeGqlQuery = ({ epochIndex, processId = ARIO_M
95
153
  });
96
154
  return gqlQuery;
97
155
  };
156
+ // fallback query if the distribution notice does not get cranked
157
+ export const epochDistributionNoticeGqlQueryFallback = ({ processId = ARIO_MAINNET_PROCESS_ID, owners = ['OAb-n-ZugyN598kZNpfOy0ACelGVmwCQ0kYbgNGDUK8'], // ar.io team wallet ticks once a day
158
+ }) => {
159
+ return JSON.stringify({
160
+ query: `
161
+ query {
162
+ transactions(
163
+ tags: [
164
+ { name: "Action", values: ["Tick"] }
165
+ ],
166
+ first: 100,
167
+ owners: [${owners.map((a) => `"${a}"`).join(',')}],
168
+ recipients: ["${processId}"],
169
+ sort: HEIGHT_DESC
170
+ ) {
171
+ edges {
172
+ node {
173
+ id
174
+ }
175
+ }
176
+ }
177
+ }
178
+ `,
179
+ });
180
+ };
98
181
  export function sortAndPaginateEpochDataIntoEligibleDistributions(epochData, params) {
99
182
  const rewards = [];
100
183
  const sortBy = params?.sortBy ?? 'eligibleReward';