@scallop-io/sui-scallop-sdk 2.0.0 → 2.0.2-switchboard-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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scallop-io/sui-scallop-sdk",
3
- "version": "2.0.0",
3
+ "version": "2.0.2-switchboard-alpha.1",
4
4
  "description": "Typescript sdk for interacting with Scallop contract on SUI",
5
5
  "keywords": [
6
6
  "sui",
@@ -44,8 +44,9 @@
44
44
  "@noble/hashes": "^1.3.2",
45
45
  "@pythnetwork/price-service-client": "^1.8.2",
46
46
  "@pythnetwork/pyth-sui-js": "2.1.0",
47
- "@scallop-io/sui-kit": "1.3.1",
47
+ "@scallop-io/sui-kit": "1.3.5",
48
48
  "@scure/bip39": "^1.2.1",
49
+ "@switchboard-xyz/sui-sdk": "0.0.22-beta.1",
49
50
  "@tanstack/query-core": "5.51.15",
50
51
  "axios": "^1.6.0",
51
52
  "bech32": "^2.0.0",
@@ -82,7 +83,7 @@
82
83
  "peerDependencies": {
83
84
  "@mysten/bcs": "^1.2.0",
84
85
  "@mysten/sui": "1.3.1",
85
- "@scallop-io/sui-kit": "1.3.1",
86
+ "@scallop-io/sui-kit": "1.3.5",
86
87
  "bn.js": "^5.2.1"
87
88
  },
88
89
  "lint-staged": {
@@ -149,6 +150,8 @@
149
150
  "scripts": {
150
151
  "clean": "rm -rf tsconfig.tsbuildinfo ./dist",
151
152
  "build": "pnpm run build:tsup",
153
+ "build-sourcemap": "pnpm run build:sourcemap",
154
+ "build:sourcemap": "tsup ./src/index.ts --format esm,cjs --splitting --minify --treeshake --dts --sourcemap",
152
155
  "build:tsup": "tsup ./src/index.ts --format esm,cjs --splitting --minify --treeshake --dts",
153
156
  "watch:tsup": "tsup ./src/index.ts --format esm,cjs --clean --splitting --watch",
154
157
  "watch:types": "tsc --watch",
@@ -9,7 +9,7 @@ import type {
9
9
  } from 'src/types';
10
10
  import { xOracleList as X_ORACLE_LIST } from 'src/constants';
11
11
  import { updatePythPriceFeeds } from './pyth';
12
- // import { updateSwitchboardAggregators } from './switchboard';
12
+ import { updateSwitchboardAggregators } from './switchboard';
13
13
 
14
14
  /**
15
15
  * Update the price of the oracle for multiple coin.
@@ -63,22 +63,23 @@ export const updateOracles = async (
63
63
  filterAssetCoinNames(assetCoinName, 'pyth')
64
64
  );
65
65
  if (pythAssetCoinNames.length > 0)
66
- await updatePythPriceFeeds(builder, assetCoinNames, txBlock);
66
+ await updatePythPriceFeeds(builder, pythAssetCoinNames, txBlock);
67
67
  }
68
68
 
69
- // Handle Switchboard on-demand aggregator
70
- // if (flattenedRules.has('switchboard')) {
71
- // const switchboardAssetCoinNames = assetCoinNames.filter((assetCoinName) =>
72
- // filterAssetCoinNames(assetCoinName, 'switchboard')
73
- // );
74
- // if (switchboardAssetCoinNames.length > 0) {
75
- // await updateSwitchboardAggregators(
76
- // builder,
77
- // switchboardAssetCoinNames,
78
- // txBlock
79
- // );
80
- // }
81
- // }
69
+ if (flattenedRules.has('switchboard')) {
70
+ const switchboardAssetCoinNames = assetCoinNames.filter((assetCoinName) =>
71
+ filterAssetCoinNames(assetCoinName, 'switchboard')
72
+ );
73
+
74
+ if (switchboardAssetCoinNames.length > 0) {
75
+ await updateSwitchboardAggregators(
76
+ builder,
77
+ switchboardAssetCoinNames,
78
+ txBlock,
79
+ builder.params.switchboardSolanaRpc
80
+ );
81
+ }
82
+ }
82
83
 
83
84
  // Remove duplicate coin names.
84
85
  const updateAssetCoinNames = [...new Set(assetCoinNames)];
@@ -1,270 +1,233 @@
1
- // import { ScallopBuilder } from 'src/models';
2
- // import {
3
- // // SUI_CLOCK_OBJECT_ID,
4
- // // SUI_TYPE_ARG,
5
- // type SuiTxBlock as SuiKitTxBlock,
6
- // } from '@scallop-io/sui-kit';
7
- // import {
8
- // Aggregator,
9
- // AggregatorData,
10
- // ObjectParsingHelper,
11
- // // AggregatorData,
12
- // // FeedEvalResponse,
13
- // // ObjectParsingHelper,
14
- // // Queue,
15
- // SwitchboardClient,
16
- // } from '@switchboard-xyz/sui-sdk';
17
- // import { queryMultipleObjects } from 'src/queries';
18
- // import { MoveValue, SuiParsedData } from '@mysten/sui/client';
19
- // import { toHex } from '@mysten/bcs';
20
- // // import { CrossbarClient, IOracleJob, OracleJob } from '@switchboard-xyz/common';
21
- // // import axios from 'axios';
22
-
23
- // const getFieldsFromObject = (
24
- // response: SuiParsedData
25
- // ): {
26
- // [key: string]: MoveValue;
27
- // } => {
28
- // // Check if 'data' and 'content' exist and are of the expected type
29
- // if (response.dataType === 'moveObject') {
30
- // // Safely return 'fields' from 'content'
31
- // return response.fields as any;
32
- // }
33
-
34
- // throw new Error('Invalid response data');
35
- // };
36
-
37
- // const parseFeedConfigs = (responses: SuiParsedData[]): AggregatorData[] => {
38
- // return responses.map(getFieldsFromObject).map((aggregatorData) => {
39
- // const currentResult = (aggregatorData.current_result as any).fields;
40
- // const updateState = (aggregatorData.update_state as any).fields;
41
-
42
- // // build the data object
43
- // const data: AggregatorData = {
44
- // id: ObjectParsingHelper.asId(aggregatorData.id),
45
- // authority: ObjectParsingHelper.asString(aggregatorData.authority),
46
- // createdAtMs: ObjectParsingHelper.asNumber(aggregatorData.created_at_ms),
47
- // currentResult: {
48
- // maxResult: ObjectParsingHelper.asBN(currentResult.max_result),
49
- // maxTimestamp: ObjectParsingHelper.asNumber(
50
- // currentResult.max_timestamp_ms
51
- // ),
52
- // mean: ObjectParsingHelper.asBN(currentResult.mean),
53
- // minResult: ObjectParsingHelper.asBN(currentResult.min_result),
54
- // minTimestamp: ObjectParsingHelper.asNumber(
55
- // currentResult.min_timestamp_ms
56
- // ),
57
- // range: ObjectParsingHelper.asBN(currentResult.range),
58
- // result: ObjectParsingHelper.asBN(currentResult.result),
59
- // stdev: ObjectParsingHelper.asBN(currentResult.stdev),
60
- // },
61
- // feedHash: toHex(
62
- // ObjectParsingHelper.asUint8Array(aggregatorData.feed_hash)
63
- // ),
64
- // maxStalenessSeconds: ObjectParsingHelper.asNumber(
65
- // aggregatorData.max_staleness_seconds
66
- // ),
67
- // maxVariance: ObjectParsingHelper.asNumber(aggregatorData.max_variance),
68
- // minResponses: ObjectParsingHelper.asNumber(aggregatorData.min_responses),
69
- // minSampleSize: ObjectParsingHelper.asNumber(
70
- // aggregatorData.min_sample_size
71
- // ),
72
- // name: ObjectParsingHelper.asString(aggregatorData.name),
73
- // queue: ObjectParsingHelper.asString(aggregatorData.queue),
74
- // updateState: {
75
- // currIdx: ObjectParsingHelper.asNumber(updateState.curr_idx),
76
- // results: updateState.results.map((r: any) => {
77
- // const oracleId = r.fields.oracle;
78
- // const value = ObjectParsingHelper.asBN(r.fields.result.fields);
79
- // const timestamp = parseInt(r.fields.timestamp_ms);
80
- // return {
81
- // oracle: oracleId,
82
- // value,
83
- // timestamp,
84
- // };
85
- // }),
86
- // },
87
- // };
88
-
89
- // return data;
1
+ import { ScallopBuilder } from 'src/models';
2
+ import { type SuiTxBlock as SuiKitTxBlock } from '@scallop-io/sui-kit';
3
+ import {
4
+ Aggregator,
5
+ AggregatorData,
6
+ ObjectParsingHelper,
7
+ // suiQueueCache,
8
+ // QueueData,
9
+ SwitchboardClient,
10
+ } from '@switchboard-xyz/sui-sdk';
11
+ import { queryMultipleObjects } from 'src/queries';
12
+ import { SuiParsedData } from '@mysten/sui/client';
13
+ import { toHex } from '@mysten/bcs';
14
+
15
+ const getFieldsFromObject = (
16
+ response: SuiParsedData | null | undefined
17
+ ): any => {
18
+ // Check if 'data' and 'content' exist and are of the expected type
19
+ if (response && response.dataType === 'moveObject') {
20
+ // Safely return 'fields' from 'content'
21
+ return response.fields as any;
22
+ }
23
+
24
+ throw new Error('Invalid response data');
25
+ };
26
+
27
+ const parseFeedConfigs = (responses: SuiParsedData[]): AggregatorData[] => {
28
+ return responses.map(getFieldsFromObject).map((aggregatorData) => {
29
+ const currentResult = (aggregatorData.current_result as any).fields;
30
+ const updateState = (aggregatorData.update_state as any).fields;
31
+
32
+ // build the data object
33
+ const data: AggregatorData = {
34
+ id: ObjectParsingHelper.asId(aggregatorData.id),
35
+ authority: ObjectParsingHelper.asString(aggregatorData.authority),
36
+ createdAtMs: ObjectParsingHelper.asNumber(aggregatorData.created_at_ms),
37
+ currentResult: {
38
+ maxResult: ObjectParsingHelper.asBN(currentResult.max_result),
39
+ maxTimestamp: ObjectParsingHelper.asNumber(
40
+ currentResult.max_timestamp_ms
41
+ ),
42
+ mean: ObjectParsingHelper.asBN(currentResult.mean),
43
+ minResult: ObjectParsingHelper.asBN(currentResult.min_result),
44
+ minTimestamp: ObjectParsingHelper.asNumber(
45
+ currentResult.min_timestamp_ms
46
+ ),
47
+ range: ObjectParsingHelper.asBN(currentResult.range),
48
+ result: ObjectParsingHelper.asBN(currentResult.result),
49
+ stdev: ObjectParsingHelper.asBN(currentResult.stdev),
50
+ },
51
+ feedHash: toHex(
52
+ ObjectParsingHelper.asUint8Array(aggregatorData.feed_hash)
53
+ ),
54
+ maxStalenessSeconds: ObjectParsingHelper.asNumber(
55
+ aggregatorData.max_staleness_seconds
56
+ ),
57
+ maxVariance: ObjectParsingHelper.asNumber(aggregatorData.max_variance),
58
+ minResponses: ObjectParsingHelper.asNumber(aggregatorData.min_responses),
59
+ minSampleSize: ObjectParsingHelper.asNumber(
60
+ aggregatorData.min_sample_size
61
+ ),
62
+ name: ObjectParsingHelper.asString(aggregatorData.name),
63
+ queue: ObjectParsingHelper.asString(aggregatorData.queue),
64
+ updateState: {
65
+ currIdx: ObjectParsingHelper.asNumber(updateState.curr_idx),
66
+ results: updateState.results.map((r: any) => {
67
+ const oracleId = r.fields.oracle;
68
+ const value = ObjectParsingHelper.asBN(r.fields.result.fields);
69
+ const timestamp = parseInt(r.fields.timestamp_ms);
70
+ return {
71
+ oracle: oracleId,
72
+ value,
73
+ timestamp,
74
+ };
75
+ }),
76
+ },
77
+ };
78
+
79
+ return data;
80
+ });
81
+ };
82
+
83
+ // const parseQueueAddresses = async (
84
+ // cache: ScallopCache,
85
+ // queueAddresses: string[]
86
+ // ): Promise<void> => {
87
+ // const queueParsedDatas = (
88
+ // await queryMultipleObjects(cache, queueAddresses)
89
+ // ).map((q) => getFieldsFromObject(q.content));
90
+
91
+ // // Get the existing_oracles table ids
92
+ // const existingOracleTableIds = queueParsedDatas.map((q: any) => {
93
+ // return q.existing_oracles.fields.id.id;
90
94
  // });
91
- // };
92
-
93
- // // const encodeJobs = (jobArray: OracleJob[]) => {
94
- // // return jobArray.map((job) => serializeOracleJob(job).toString('base64'));
95
- // // };
96
-
97
- // // const normalizeOracleJob = (
98
- // // data: string | IOracleJob | Record<string, any>
99
- // // ): OracleJob => {
100
- // // const parseJobObject = (jobData: Record<string, any>) => {
101
- // // if (!jobData) {
102
- // // throw new Error(`No job data provided: ${jobData}`);
103
- // // } else if (!('tasks' in jobData)) {
104
- // // throw new Error('"tasks" property is required');
105
- // // } else if (!(Array.isArray(jobData.tasks) && jobData.tasks.length > 0)) {
106
- // // throw new Error('"tasks" property must be a non-empty array');
107
- // // }
108
- // // return OracleJob.fromObject(jobData);
109
- // // };
110
- // // const parseJobString = (jobString: string) => {
111
- // // // Strip comments using regex from https://regex101.com/r/B8WkuX/1
112
- // // const cleanJson = jobString.replace(
113
- // // /\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/g,
114
- // // ''
115
- // // );
116
- // // return parseJobObject(JSON.parse(cleanJson));
117
- // // };
118
- // // return typeof data === 'string' ? parseJobString(data) : parseJobObject(data);
119
- // // };
120
-
121
- // // const serializeOracleJob = (
122
- // // data: string | IOracleJob | Record<string, any>
123
- // // ): Buffer => {
124
- // // const job = normalizeOracleJob(data);
125
- // // return Buffer.from(OracleJob.encodeDelimited(job).finish());
126
- // // };
127
-
128
- // // const fetchSignatures = async (
129
- // // feedConfig: AggregatorData
130
- // // ): Promise<{
131
- // // responses: FeedEvalResponse[];
132
- // // failures: string[];
133
- // // }> => {
134
- // // const crossbarClient = new CrossbarClient('https://crossbar.switchboard.xyz');
135
-
136
- // // const jobs: OracleJob[] = await crossbarClient
137
- // // .fetch(feedConfig.feedHash)
138
- // // .then((res) => res.jobs.map((job) => OracleJob.fromObject(job)));
139
-
140
- // // const encodedJobs = encodeJobs(jobs);
141
- // // const maxVariance = Math.floor(feedConfig.maxVariance / 1e9) * 1e9;
142
- // // const minResponses = feedConfig.minResponses;
143
- // // const numSignatures = feedConfig.minSampleSize;
144
- // // const recentHash = toBase58(new Uint8Array(32));
145
- // // const useTimestamp = true;
146
-
147
- // // const GATEWAY_URL = 'https://api.mainnet-beta.solana.com';
148
- // // const TIMEOUT = 10000;
149
- // // const url = `${GATEWAY_URL}/gateway/api/v1/fetch_signatures`;
150
- // // const headers = { 'Content-Type': 'application/json' };
151
95
 
152
- // // const body = JSON.stringify({
153
- // // api_version: '1.0.0',
154
- // // jobs_b64_encoded: encodedJobs,
155
- // // recent_chainhash: recentHash,
156
- // // signature_scheme: 'Secp256k1',
157
- // // hash_scheme: 'Sha256',
158
- // // num_oracles: numSignatures,
159
- // // max_variance: maxVariance,
160
- // // min_responses: minResponses,
161
- // // use_timestamp: useTimestamp,
162
- // // });
163
-
164
- // // return await axios
165
- // // .post(url, body, {
166
- // // headers,
167
- // // timeout: TIMEOUT,
168
- // // })
169
- // // .then((r) => r.data);
170
- // // };
171
-
172
- // export const updateSwitchboardAggregators = async (
173
- // builder: ScallopBuilder,
174
- // assetCoinNames: string[],
175
- // txBlock: SuiKitTxBlock
176
- // ) => {
177
- // const switchboardClient = new SwitchboardClient(builder.suiKit.client());
178
- // const onDemandAggObjects = await queryMultipleObjects(
179
- // builder.cache,
180
- // await builder.query.getSwitchboardOnDemandAggregatorObjectIds(
181
- // assetCoinNames
182
- // )
183
- // );
184
-
185
- // const feedConfigs = parseFeedConfigs(
186
- // onDemandAggObjects.map((t) => t.content) as SuiParsedData[]
187
- // );
188
-
189
- // for (const idx in assetCoinNames) {
190
- // // const { switchboardAddress, oracleQueueId } =
191
- // // await switchboardClient.fetchState();
192
-
193
- // // const feedConfig = feedConfigs[idx];
194
-
195
- // // const suiQueue = await new Queue(
196
- // // switchboardClient,
197
- // // oracleQueueId
198
- // // ).loadData();
199
-
200
- // // const { responses, failures } = await fetchSignatures(feedConfig);
201
- // // const validOracles = new Set(
202
- // // suiQueue.existingOracles.map((o) => o.oracleKey)
203
- // // );
204
-
205
- // // const validResponses = responses.filter((r) => {
206
- // // return validOracles.has(toBase58(fromHex(r.oracle_pubkey)));
207
- // // });
208
-
209
- // // // if we have no valid responses (or not enough), fail out
210
- // // if (
211
- // // !validResponses.length ||
212
- // // validResponses.length < feedConfig.minSampleSize
213
- // // ) {
214
- // // // maybe retry by recursing into the same function / add a retry count
215
- // // throw new Error('Not enough valid oracle responses.');
216
- // // }
217
-
218
- // // // split the gas coin into the right amount for each response
219
- // // const coins = txBlock.splitCoins(
220
- // // txBlock.gas,
221
- // // validResponses.map(() => suiQueue.fee)
222
- // // );
96
+ // for (let i = 0; i < existingOracleTableIds.length; i++) {
97
+ // const parentId = existingOracleTableIds[i];
98
+ // const queueAddress = queueAddresses[i];
99
+ // const queueResponse = queueParsedDatas[i];
100
+ // const queueCachedData = await suiQueueCache.get(queueAddress);
101
+ // if (queueCachedData) {
102
+ // return queueCachedData;
103
+ // }
104
+
105
+ // let cursor = null;
106
+ // let nextPage = true;
107
+
108
+ // const existingOraclesIds = [];
109
+ // while (nextPage) {
110
+ // const resp = await cache.queryGetDynamicFields({
111
+ // parentId,
112
+ // limit: 25,
113
+ // cursor,
114
+ // });
115
+
116
+ // if (!resp) break;
117
+ // const { nextCursor, data, hasNextPage } = resp;
118
+ // nextPage = hasNextPage;
119
+ // cursor = nextCursor;
120
+
121
+ // existingOraclesIds.push(...data.map((t) => t.objectId));
122
+ // }
123
+
124
+ // const existingOracles = await queryMultipleObjects(
125
+ // cache,
126
+ // existingOraclesIds
127
+ // ).then((res) => {
128
+ // return res.map((t) => {
129
+ // const fields: any = getFieldsFromObject(t.content);
130
+ // return {
131
+ // oracleId: ObjectParsingHelper.asString(fields.value.fields.oracle_id),
132
+ // oracleKey: toBase58(
133
+ // ObjectParsingHelper.asUint8Array(fields.value.fields.oracle_key)
134
+ // ),
135
+ // };
136
+ // });
137
+ // });
138
+
139
+ // const queueData = {
140
+ // authority: ObjectParsingHelper.asString(queueResponse.authority),
141
+ // existingOracles,
142
+ // // get fee number (though encoded as string)
143
+ // fee: ObjectParsingHelper.asNumber(queueResponse.fee),
144
+
145
+ // // fee recipient address
146
+ // feeRecipient: ObjectParsingHelper.asString(queueResponse.fee_recipient),
147
+
148
+ // // accepted fee coin types
149
+ // feeTypes: ObjectParsingHelper.asArray(queueResponse.fee_types).map(
150
+ // (ft: any) => ft.fields.name
151
+ // ),
223
152
 
224
- // // // map the responses into the tx
225
- // // validResponses.forEach((response, i) => {
226
- // // const oracle = suiQueue.existingOracles.find(
227
- // // (o) => o.oracleKey === toBase58(fromHex(response.oracle_pubkey))
228
- // // )!;
153
+ // // guardian queue id
154
+ // guardianQueueId: ObjectParsingHelper.asString(
155
+ // queueResponse.guardian_queue_id
156
+ // ),
229
157
 
230
- // // const signature = Array.from(fromBase64(response.signature));
231
- // // signature.push(response.recovery_id);
158
+ // // queue id
159
+ // id: ObjectParsingHelper.asId(queueResponse.id),
232
160
 
233
- // // txBlock.moveCall(
234
- // // `${switchboardAddress}::aggregator_submit_result_action::run`,
235
- // // [
236
- // // txBlock.object(onDemandAggObjects[idx].objectId),
237
- // // txBlock.object(suiQueue.id),
238
- // // txBlock.pure.u128(response.success_value),
239
- // // txBlock.pure.bool(response.success_value.startsWith('-')),
240
- // // txBlock.pure.u64(response.timestamp!),
241
- // // txBlock.object(oracle.oracleId),
242
- // // txBlock.pure.vector('u8', signature),
243
- // // txBlock.sharedObjectRef({
244
- // // objectId: SUI_CLOCK_OBJECT_ID,
245
- // // initialSharedVersion: '1',
246
- // // mutable: false,
247
- // // }),
248
- // // coins[i],
249
- // // ],
250
- // // [SUI_TYPE_ARG]
251
- // // );
252
- // // });
161
+ // // last queue override ms
162
+ // lastQueueOverrideMs: ObjectParsingHelper.asNumber(
163
+ // queueResponse.last_queue_override_ms
164
+ // ),
253
165
 
254
- // // return { responses, failures };
166
+ // // minimum attestations
167
+ // minAttestations: ObjectParsingHelper.asNumber(
168
+ // queueResponse.min_attestations
169
+ // ),
255
170
 
256
- // const switchboardAgg = new Aggregator(
257
- // switchboardClient,
258
- // onDemandAggObjects[idx].objectId
259
- // );
171
+ // // queue name
172
+ // name: ObjectParsingHelper.asString(queueResponse.name),
173
+ // oracleValidityLengthMs: ObjectParsingHelper.asNumber(
174
+ // queueResponse.oracle_validity_length_ms
175
+ // ),
260
176
 
261
- // const { responses, failures } = await switchboardAgg.fetchUpdateTx(
262
- // txBlock.txBlock,
263
- // {
264
- // feedConfigs: feedConfigs[idx],
265
- // }
266
- // );
177
+ // // get source queue key
178
+ // queueKey: toBase58(
179
+ // ObjectParsingHelper.asUint8Array(queueResponse.queue_key) as Uint8Array
180
+ // ),
181
+ // } as QueueData;
267
182
 
268
- // return { responses, failures };
183
+ // suiQueueCache.set(queueAddress, queueData);
269
184
  // }
270
185
  // };
186
+
187
+ export const updateSwitchboardAggregators = async (
188
+ builder: ScallopBuilder,
189
+ assetCoinNames: string[],
190
+ txBlock: SuiKitTxBlock,
191
+ solanaRPCUrl: string = 'https://crossbar.switchboard.xyz/rpc/mainnet'
192
+ ) => {
193
+ const switchboardClient = new SwitchboardClient(builder.suiKit.client());
194
+ const onDemandAggObjects = await queryMultipleObjects(
195
+ builder.cache,
196
+ await builder.query.getSwitchboardOnDemandAggregatorObjectIds(
197
+ assetCoinNames
198
+ )
199
+ );
200
+
201
+ const feedConfigs = parseFeedConfigs(
202
+ onDemandAggObjects.map((t) => t.content) as SuiParsedData[]
203
+ );
204
+
205
+ // Get queue addresses
206
+ // const queueAddresses = feedConfigs.map((fc) => fc.queue);
207
+
208
+ // await parseQueueAddresses(builder.cache, queueAddresses);
209
+
210
+ for (let idx = 0; idx < assetCoinNames.length; idx++) {
211
+ // console.log({ coinName: assetCoinNames[idx] });
212
+ const switchboardAgg = new Aggregator(
213
+ switchboardClient,
214
+ onDemandAggObjects[idx].objectId
215
+ );
216
+
217
+ const { responses, failures } = await switchboardAgg.fetchUpdateTx(
218
+ txBlock.txBlock,
219
+ {
220
+ feedConfigs: feedConfigs[idx],
221
+ solanaRPCUrl,
222
+ }
223
+ );
224
+
225
+ console.log({ responses, failures });
226
+ if (failures.length > 0) {
227
+ throw new Error(
228
+ `Failed to update aggregator for ${assetCoinNames[idx]}: ${failures.join(',')}`
229
+ );
230
+ }
231
+ }
232
+ return;
233
+ };