@ar.io/sdk 3.24.0 → 4.0.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 (169) hide show
  1. package/README.md +682 -600
  2. package/lib/esm/cli/cli.js +188 -152
  3. package/lib/esm/cli/commands/antCommands.js +23 -58
  4. package/lib/esm/cli/commands/arnsPurchaseCommands.js +48 -30
  5. package/lib/esm/cli/commands/escrowCommands.js +221 -0
  6. package/lib/esm/cli/commands/gatewayWriteCommands.js +142 -23
  7. package/lib/esm/cli/commands/pruneCommands.js +150 -0
  8. package/lib/esm/cli/commands/readCommands.js +22 -3
  9. package/lib/esm/cli/commands/transfer.js +6 -6
  10. package/lib/esm/cli/options.js +124 -58
  11. package/lib/esm/cli/utils.js +280 -174
  12. package/lib/esm/common/ant-registry.js +17 -143
  13. package/lib/esm/common/ant.js +44 -1167
  14. package/lib/esm/common/faucet.js +11 -6
  15. package/lib/esm/common/index.js +0 -4
  16. package/lib/esm/common/io.js +25 -1412
  17. package/lib/esm/constants.js +13 -19
  18. package/lib/esm/solana/ant-readable.js +724 -0
  19. package/lib/esm/solana/ant-registry-readable.js +133 -0
  20. package/lib/esm/solana/ant-registry-writeable.js +472 -0
  21. package/lib/esm/solana/ant-writeable.js +384 -0
  22. package/lib/esm/solana/ata.js +70 -0
  23. package/lib/esm/solana/canonical-message.js +128 -0
  24. package/lib/esm/solana/clusters.js +111 -0
  25. package/lib/esm/solana/constants.js +146 -0
  26. package/lib/esm/solana/delegation-math.js +112 -0
  27. package/lib/esm/solana/deserialize.js +711 -0
  28. package/lib/esm/solana/escrow.js +839 -0
  29. package/lib/{cjs/utils/json.js → esm/solana/events.js} +15 -10
  30. package/lib/esm/solana/funding-plan.js +699 -0
  31. package/lib/esm/solana/index.js +126 -0
  32. package/lib/esm/solana/instruction.js +39 -0
  33. package/lib/esm/solana/io-readable.js +2182 -0
  34. package/lib/esm/solana/io-writeable.js +3196 -0
  35. package/lib/esm/solana/json-rpc.js +90 -0
  36. package/lib/esm/solana/metadata.js +81 -0
  37. package/lib/esm/solana/mpl-core.js +192 -0
  38. package/lib/esm/solana/pda.js +332 -0
  39. package/lib/esm/solana/predict-prescribed-observers.js +110 -0
  40. package/lib/esm/solana/retry.js +117 -0
  41. package/lib/esm/solana/rpc-circuit-breaker.js +258 -0
  42. package/lib/esm/solana/send.js +372 -0
  43. package/lib/esm/solana/spawn-ant.js +224 -0
  44. package/lib/esm/solana/types.js +1 -0
  45. package/lib/esm/types/ant.js +27 -15
  46. package/lib/esm/types/io.js +8 -11
  47. package/lib/esm/utils/ant.js +0 -63
  48. package/lib/esm/utils/index.js +0 -3
  49. package/lib/esm/version.js +1 -1
  50. package/lib/types/cli/commands/antCommands.d.ts +5 -13
  51. package/lib/types/cli/commands/arnsPurchaseCommands.d.ts +33 -7
  52. package/lib/types/cli/commands/escrowCommands.d.ts +68 -0
  53. package/lib/types/cli/commands/gatewayWriteCommands.d.ts +12 -11
  54. package/lib/types/cli/commands/pruneCommands.d.ts +31 -0
  55. package/lib/types/cli/commands/readCommands.d.ts +27 -22
  56. package/lib/types/cli/commands/transfer.d.ts +9 -9
  57. package/lib/types/cli/options.d.ts +76 -21
  58. package/lib/types/cli/types.d.ts +11 -13
  59. package/lib/types/cli/utils.d.ts +71 -31
  60. package/lib/types/common/ant-registry.d.ts +49 -47
  61. package/lib/types/common/ant.d.ts +54 -539
  62. package/lib/types/common/faucet.d.ts +20 -8
  63. package/lib/types/common/index.d.ts +0 -3
  64. package/lib/types/common/io.d.ts +51 -263
  65. package/lib/types/constants.d.ts +11 -18
  66. package/lib/types/solana/ant-readable.d.ts +180 -0
  67. package/lib/types/solana/ant-registry-readable.d.ts +105 -0
  68. package/lib/types/solana/ant-registry-writeable.d.ts +249 -0
  69. package/lib/types/solana/ant-writeable.d.ts +177 -0
  70. package/lib/types/solana/ata.d.ts +44 -0
  71. package/lib/types/solana/canonical-message.d.ts +121 -0
  72. package/lib/types/solana/clusters.d.ts +109 -0
  73. package/lib/types/solana/constants.d.ts +119 -0
  74. package/lib/types/solana/delegation-math.d.ts +45 -0
  75. package/lib/types/solana/deserialize.d.ts +262 -0
  76. package/lib/types/solana/escrow.d.ts +480 -0
  77. package/lib/types/solana/events.d.ts +38 -0
  78. package/lib/types/solana/funding-plan.d.ts +225 -0
  79. package/lib/types/solana/index.d.ts +87 -0
  80. package/lib/types/solana/instruction.d.ts +39 -0
  81. package/lib/types/solana/io-readable.d.ts +499 -0
  82. package/lib/types/solana/io-writeable.d.ts +893 -0
  83. package/lib/types/solana/json-rpc.d.ts +47 -0
  84. package/lib/types/solana/metadata.d.ts +84 -0
  85. package/lib/types/solana/mpl-core.d.ts +120 -0
  86. package/lib/types/solana/pda.d.ts +95 -0
  87. package/lib/types/solana/predict-prescribed-observers.d.ts +28 -0
  88. package/lib/types/solana/retry.d.ts +62 -0
  89. package/lib/types/solana/rpc-circuit-breaker.d.ts +78 -0
  90. package/lib/types/solana/send.d.ts +94 -0
  91. package/lib/types/solana/spawn-ant.d.ts +145 -0
  92. package/lib/types/solana/types.d.ts +82 -0
  93. package/lib/types/types/ant-registry.d.ts +43 -4
  94. package/lib/types/types/ant.d.ts +114 -96
  95. package/lib/types/types/common.d.ts +18 -74
  96. package/lib/types/types/faucet.d.ts +2 -2
  97. package/lib/types/types/io.d.ts +244 -158
  98. package/lib/types/types/token.d.ts +0 -12
  99. package/lib/types/utils/ant.d.ts +1 -12
  100. package/lib/types/utils/index.d.ts +0 -3
  101. package/lib/types/version.d.ts +1 -1
  102. package/package.json +36 -33
  103. package/lib/cjs/cli/cli.js +0 -822
  104. package/lib/cjs/cli/commands/antCommands.js +0 -113
  105. package/lib/cjs/cli/commands/arnsPurchaseCommands.js +0 -212
  106. package/lib/cjs/cli/commands/gatewayWriteCommands.js +0 -210
  107. package/lib/cjs/cli/commands/readCommands.js +0 -215
  108. package/lib/cjs/cli/commands/transfer.js +0 -159
  109. package/lib/cjs/cli/options.js +0 -470
  110. package/lib/cjs/cli/types.js +0 -2
  111. package/lib/cjs/cli/utils.js +0 -639
  112. package/lib/cjs/common/ant-registry.js +0 -155
  113. package/lib/cjs/common/ant-versions.js +0 -93
  114. package/lib/cjs/common/ant.js +0 -1182
  115. package/lib/cjs/common/arweave.js +0 -27
  116. package/lib/cjs/common/contracts/ao-process.js +0 -224
  117. package/lib/cjs/common/error.js +0 -64
  118. package/lib/cjs/common/faucet.js +0 -150
  119. package/lib/cjs/common/hyperbeam/hb.js +0 -173
  120. package/lib/cjs/common/index.js +0 -42
  121. package/lib/cjs/common/io.js +0 -1423
  122. package/lib/cjs/common/logger.js +0 -83
  123. package/lib/cjs/common/loggers/winston.js +0 -68
  124. package/lib/cjs/common/marketplace.js +0 -731
  125. package/lib/cjs/common/turbo.js +0 -223
  126. package/lib/cjs/constants.js +0 -41
  127. package/lib/cjs/node/index.js +0 -39
  128. package/lib/cjs/package.json +0 -1
  129. package/lib/cjs/types/ant-registry.js +0 -2
  130. package/lib/cjs/types/ant.js +0 -168
  131. package/lib/cjs/types/common.js +0 -2
  132. package/lib/cjs/types/faucet.js +0 -2
  133. package/lib/cjs/types/index.js +0 -37
  134. package/lib/cjs/types/io.js +0 -51
  135. package/lib/cjs/types/token.js +0 -116
  136. package/lib/cjs/utils/ant.js +0 -108
  137. package/lib/cjs/utils/ao.js +0 -432
  138. package/lib/cjs/utils/arweave.js +0 -285
  139. package/lib/cjs/utils/base64.js +0 -62
  140. package/lib/cjs/utils/hash.js +0 -56
  141. package/lib/cjs/utils/index.js +0 -38
  142. package/lib/cjs/utils/processes.js +0 -173
  143. package/lib/cjs/utils/random.js +0 -30
  144. package/lib/cjs/utils/schema.js +0 -15
  145. package/lib/cjs/utils/url.js +0 -37
  146. package/lib/cjs/version.js +0 -20
  147. package/lib/cjs/web/index.js +0 -41
  148. package/lib/esm/common/ant-versions.js +0 -87
  149. package/lib/esm/common/arweave.js +0 -21
  150. package/lib/esm/common/contracts/ao-process.js +0 -220
  151. package/lib/esm/common/hyperbeam/hb.js +0 -169
  152. package/lib/esm/common/marketplace.js +0 -724
  153. package/lib/esm/common/turbo.js +0 -215
  154. package/lib/esm/node/index.js +0 -20
  155. package/lib/esm/utils/ao.js +0 -420
  156. package/lib/esm/utils/arweave.js +0 -271
  157. package/lib/esm/utils/processes.js +0 -167
  158. package/lib/esm/web/index.js +0 -20
  159. package/lib/types/common/ant-versions.d.ts +0 -39
  160. package/lib/types/common/arweave.d.ts +0 -17
  161. package/lib/types/common/contracts/ao-process.d.ts +0 -47
  162. package/lib/types/common/hyperbeam/hb.d.ts +0 -88
  163. package/lib/types/common/marketplace.d.ts +0 -568
  164. package/lib/types/common/turbo.d.ts +0 -61
  165. package/lib/types/node/index.d.ts +0 -20
  166. package/lib/types/utils/ao.d.ts +0 -80
  167. package/lib/types/utils/arweave.d.ts +0 -79
  168. package/lib/types/utils/processes.d.ts +0 -39
  169. package/lib/types/web/index.d.ts +0 -20
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { createHash } from 'crypto';
17
+ /**
18
+ * PDA derivation helpers for AR.IO Solana programs.
19
+ *
20
+ * Each function derives the on-chain address for a given account type,
21
+ * mirroring the seeds defined in the Anchor programs.
22
+ *
23
+ * All helpers are async — kit's `getProgramDerivedAddress` is async because
24
+ * it may perform bump-seed search off the main thread in some environments.
25
+ */
26
+ import { getAddressEncoder, getProgramDerivedAddress, } from '@solana/kit';
27
+ import { ACL_CONFIG_SEED, ACL_PAGE_SEED, ALLOWLIST_SEED, ANT_CONFIG_SEED, ANT_CONTROLLERS_SEED, ANT_RECORD_META_SEED, ANT_RECORD_SEED, ARIO_ANT_ESCROW_PROGRAM_ID, ARIO_ANT_PROGRAM_ID, ARIO_ARNS_PROGRAM_ID, ARIO_CONFIG_SEED, ARIO_CORE_PROGRAM_ID, ARIO_GAR_PROGRAM_ID, ARNS_RECORD_SEED, ARNS_REGISTRY_SEED, ARNS_SETTINGS_SEED, BALANCE_SEED, DELEGATION_SEED, DEMAND_FACTOR_SEED, EPOCH_SEED, EPOCH_SETTINGS_SEED, ESCROW_ANT_SEED, ESCROW_TOKEN_SEED, ESCROW_VAULT_SEED, GAR_SETTINGS_SEED, GATEWAY_REGISTRY_SEED, GATEWAY_SEED, OBSERVATION_SEED, OBSERVER_LOOKUP_SEED, PRIMARY_NAME_REQUEST_SEED, PRIMARY_NAME_REVERSE_SEED, PRIMARY_NAME_SEED, REDELEGATION_SEED, RESERVED_NAME_SEED, RETURNED_NAME_SEED, VAULT_COUNTER_SEED, VAULT_SEED, WITHDRAWAL_COUNTER_SEED, WITHDRAWAL_SEED, } from './constants.js';
28
+ const addressEncoder = getAddressEncoder();
29
+ /**
30
+ * Hash a variable-length name for use as PDA seed.
31
+ * Matches Rust: hash(name.to_lowercase().as_bytes())
32
+ */
33
+ export function hashName(name) {
34
+ return createHash('sha256')
35
+ .update(name.toLowerCase())
36
+ .digest()
37
+ .subarray(0, 32);
38
+ }
39
+ // =========================================
40
+ // ario-core PDAs
41
+ // =========================================
42
+ export async function getArioConfigPDA(programId = ARIO_CORE_PROGRAM_ID) {
43
+ return getProgramDerivedAddress({
44
+ programAddress: programId,
45
+ seeds: [ARIO_CONFIG_SEED],
46
+ });
47
+ }
48
+ export async function getBalancePDA(owner, programId = ARIO_CORE_PROGRAM_ID) {
49
+ return getProgramDerivedAddress({
50
+ programAddress: programId,
51
+ seeds: [BALANCE_SEED, addressEncoder.encode(owner)],
52
+ });
53
+ }
54
+ export async function getVaultPDA(owner, vaultId, programId = ARIO_CORE_PROGRAM_ID) {
55
+ const idBuf = Buffer.alloc(8);
56
+ idBuf.writeBigUInt64LE(BigInt(vaultId));
57
+ return getProgramDerivedAddress({
58
+ programAddress: programId,
59
+ seeds: [VAULT_SEED, addressEncoder.encode(owner), idBuf],
60
+ });
61
+ }
62
+ export async function getVaultCounterPDA(owner, programId = ARIO_CORE_PROGRAM_ID) {
63
+ return getProgramDerivedAddress({
64
+ programAddress: programId,
65
+ seeds: [VAULT_COUNTER_SEED, addressEncoder.encode(owner)],
66
+ });
67
+ }
68
+ export async function getPrimaryNamePDA(owner, programId = ARIO_CORE_PROGRAM_ID) {
69
+ return getProgramDerivedAddress({
70
+ programAddress: programId,
71
+ seeds: [PRIMARY_NAME_SEED, addressEncoder.encode(owner)],
72
+ });
73
+ }
74
+ export async function getPrimaryNameRequestPDA(initiator, programId = ARIO_CORE_PROGRAM_ID) {
75
+ return getProgramDerivedAddress({
76
+ programAddress: programId,
77
+ seeds: [PRIMARY_NAME_REQUEST_SEED, addressEncoder.encode(initiator)],
78
+ });
79
+ }
80
+ export async function getPrimaryNameReversePDA(name, programId = ARIO_CORE_PROGRAM_ID) {
81
+ return getProgramDerivedAddress({
82
+ programAddress: programId,
83
+ seeds: [PRIMARY_NAME_REVERSE_SEED, hashName(name)],
84
+ });
85
+ }
86
+ // =========================================
87
+ // ario-gar PDAs
88
+ // =========================================
89
+ export async function getGatewayRegistryPDA(programId = ARIO_GAR_PROGRAM_ID) {
90
+ return getProgramDerivedAddress({
91
+ programAddress: programId,
92
+ seeds: [GATEWAY_REGISTRY_SEED],
93
+ });
94
+ }
95
+ export async function getGarSettingsPDA(programId = ARIO_GAR_PROGRAM_ID) {
96
+ return getProgramDerivedAddress({
97
+ programAddress: programId,
98
+ seeds: [GAR_SETTINGS_SEED],
99
+ });
100
+ }
101
+ export async function getGatewayPDA(operator, programId = ARIO_GAR_PROGRAM_ID) {
102
+ return getProgramDerivedAddress({
103
+ programAddress: programId,
104
+ seeds: [GATEWAY_SEED, addressEncoder.encode(operator)],
105
+ });
106
+ }
107
+ export async function getDelegationPDA(gateway, delegator, programId = ARIO_GAR_PROGRAM_ID) {
108
+ return getProgramDerivedAddress({
109
+ programAddress: programId,
110
+ seeds: [
111
+ DELEGATION_SEED,
112
+ addressEncoder.encode(gateway),
113
+ addressEncoder.encode(delegator),
114
+ ],
115
+ });
116
+ }
117
+ export async function getWithdrawalPDA(owner, withdrawalId, programId = ARIO_GAR_PROGRAM_ID) {
118
+ const idBuf = Buffer.alloc(8);
119
+ idBuf.writeBigUInt64LE(BigInt(withdrawalId));
120
+ return getProgramDerivedAddress({
121
+ programAddress: programId,
122
+ seeds: [WITHDRAWAL_SEED, addressEncoder.encode(owner), idBuf],
123
+ });
124
+ }
125
+ export async function getWithdrawalCounterPDA(owner, programId = ARIO_GAR_PROGRAM_ID) {
126
+ return getProgramDerivedAddress({
127
+ programAddress: programId,
128
+ seeds: [WITHDRAWAL_COUNTER_SEED, addressEncoder.encode(owner)],
129
+ });
130
+ }
131
+ export async function getAllowlistPDA(gateway, delegate, programId = ARIO_GAR_PROGRAM_ID) {
132
+ return getProgramDerivedAddress({
133
+ programAddress: programId,
134
+ seeds: [
135
+ ALLOWLIST_SEED,
136
+ addressEncoder.encode(gateway),
137
+ addressEncoder.encode(delegate),
138
+ ],
139
+ });
140
+ }
141
+ export async function getEpochPDA(epochIndex, programId = ARIO_GAR_PROGRAM_ID) {
142
+ const indexBuf = Buffer.alloc(8);
143
+ indexBuf.writeBigUInt64LE(BigInt(epochIndex));
144
+ return getProgramDerivedAddress({
145
+ programAddress: programId,
146
+ seeds: [EPOCH_SEED, indexBuf],
147
+ });
148
+ }
149
+ export async function getEpochSettingsPDA(programId = ARIO_GAR_PROGRAM_ID) {
150
+ return getProgramDerivedAddress({
151
+ programAddress: programId,
152
+ seeds: [EPOCH_SETTINGS_SEED],
153
+ });
154
+ }
155
+ export async function getRedelegationRecordPDA(delegator, programId = ARIO_GAR_PROGRAM_ID) {
156
+ return getProgramDerivedAddress({
157
+ programAddress: programId,
158
+ seeds: [REDELEGATION_SEED, addressEncoder.encode(delegator)],
159
+ });
160
+ }
161
+ export async function getObservationPDA(epochIndex, observer, programId = ARIO_GAR_PROGRAM_ID) {
162
+ const indexBuf = Buffer.alloc(8);
163
+ indexBuf.writeBigUInt64LE(BigInt(epochIndex));
164
+ return getProgramDerivedAddress({
165
+ programAddress: programId,
166
+ seeds: [OBSERVATION_SEED, indexBuf, addressEncoder.encode(observer)],
167
+ });
168
+ }
169
+ export async function getObserverLookupPDA(observerAddress, programId = ARIO_GAR_PROGRAM_ID) {
170
+ return getProgramDerivedAddress({
171
+ programAddress: programId,
172
+ seeds: [OBSERVER_LOOKUP_SEED, addressEncoder.encode(observerAddress)],
173
+ });
174
+ }
175
+ // =========================================
176
+ // ario-arns PDAs
177
+ // =========================================
178
+ export async function getArnsRegistryPDA(programId = ARIO_ARNS_PROGRAM_ID) {
179
+ return getProgramDerivedAddress({
180
+ programAddress: programId,
181
+ seeds: [ARNS_REGISTRY_SEED],
182
+ });
183
+ }
184
+ export async function getArnsSettingsPDA(programId = ARIO_ARNS_PROGRAM_ID) {
185
+ return getProgramDerivedAddress({
186
+ programAddress: programId,
187
+ seeds: [ARNS_SETTINGS_SEED],
188
+ });
189
+ }
190
+ export async function getArnsRecordPDA(name, programId = ARIO_ARNS_PROGRAM_ID) {
191
+ return getProgramDerivedAddress({
192
+ programAddress: programId,
193
+ seeds: [ARNS_RECORD_SEED, hashName(name)],
194
+ });
195
+ }
196
+ /**
197
+ * Derive ArNS record PDA from a raw 32-byte name hash.
198
+ * Used when resolving prescribed name hashes from Epoch data.
199
+ */
200
+ export async function getArnsRecordPDAFromHash(nameHash, programId = ARIO_ARNS_PROGRAM_ID) {
201
+ return getProgramDerivedAddress({
202
+ programAddress: programId,
203
+ seeds: [ARNS_RECORD_SEED, nameHash],
204
+ });
205
+ }
206
+ export async function getReservedNamePDA(name, programId = ARIO_ARNS_PROGRAM_ID) {
207
+ return getProgramDerivedAddress({
208
+ programAddress: programId,
209
+ seeds: [RESERVED_NAME_SEED, hashName(name)],
210
+ });
211
+ }
212
+ export async function getReturnedNamePDA(name, programId = ARIO_ARNS_PROGRAM_ID) {
213
+ return getProgramDerivedAddress({
214
+ programAddress: programId,
215
+ seeds: [RETURNED_NAME_SEED, hashName(name)],
216
+ });
217
+ }
218
+ export async function getDemandFactorPDA(programId = ARIO_ARNS_PROGRAM_ID) {
219
+ return getProgramDerivedAddress({
220
+ programAddress: programId,
221
+ seeds: [DEMAND_FACTOR_SEED],
222
+ });
223
+ }
224
+ // =========================================
225
+ // ario-ant PDAs
226
+ // =========================================
227
+ export async function getAntConfigPDA(mint, programId = ARIO_ANT_PROGRAM_ID) {
228
+ return getProgramDerivedAddress({
229
+ programAddress: programId,
230
+ seeds: [ANT_CONFIG_SEED, addressEncoder.encode(mint)],
231
+ });
232
+ }
233
+ export async function getAntControllersPDA(mint, programId = ARIO_ANT_PROGRAM_ID) {
234
+ return getProgramDerivedAddress({
235
+ programAddress: programId,
236
+ seeds: [ANT_CONTROLLERS_SEED, addressEncoder.encode(mint)],
237
+ });
238
+ }
239
+ export async function getAntRecordPDA(mint, undername, programId = ARIO_ANT_PROGRAM_ID) {
240
+ const nameHash = createHash('sha256')
241
+ .update(undername.toLowerCase())
242
+ .digest()
243
+ .subarray(0, 32);
244
+ return getProgramDerivedAddress({
245
+ programAddress: programId,
246
+ seeds: [ANT_RECORD_SEED, addressEncoder.encode(mint), nameHash],
247
+ });
248
+ }
249
+ export async function getAntRecordMetadataPDA(mint, undername, programId = ARIO_ANT_PROGRAM_ID) {
250
+ const nameHash = createHash('sha256')
251
+ .update(undername.toLowerCase())
252
+ .digest()
253
+ .subarray(0, 32);
254
+ return getProgramDerivedAddress({
255
+ programAddress: programId,
256
+ seeds: [ANT_RECORD_META_SEED, addressEncoder.encode(mint), nameHash],
257
+ });
258
+ }
259
+ /**
260
+ * Derive the `AclConfig` head PDA for a given wallet address.
261
+ *
262
+ * Seeds: `["acl_config", user]` under the ario-ant program.
263
+ *
264
+ * See ADR-012 (docs/DECISIONS.md): `AclConfig` is the head record for a
265
+ * user's paginated ACL. It tracks `page_count` (how many `AclPage` PDAs
266
+ * exist) and `total_entries` (sum across pages). Frontends point-read
267
+ * this once, then fan out to each `AclPage` via `getMultipleAccountsInfo`.
268
+ */
269
+ export async function getAclConfigPDA(user, programId = ARIO_ANT_PROGRAM_ID) {
270
+ return getProgramDerivedAddress({
271
+ programAddress: programId,
272
+ seeds: [ACL_CONFIG_SEED, addressEncoder.encode(user)],
273
+ });
274
+ }
275
+ /**
276
+ * Derive an `AclPage` PDA for a given user + page index.
277
+ *
278
+ * Seeds: `["acl_page", user, page_idx_le]` where `page_idx_le` is the
279
+ * 8-byte little-endian encoding of `u64` (matches the contract).
280
+ *
281
+ * Each page address is content-derivable from `(user, page_idx)`, so any
282
+ * single page can be loaded in O(1) without scanning sibling pages.
283
+ */
284
+ export async function getAclPagePDA(user, pageIdx, programId = ARIO_ANT_PROGRAM_ID) {
285
+ const idxBuf = Buffer.alloc(8);
286
+ idxBuf.writeBigUInt64LE(BigInt(pageIdx));
287
+ return getProgramDerivedAddress({
288
+ programAddress: programId,
289
+ seeds: [ACL_PAGE_SEED, addressEncoder.encode(user), idxBuf],
290
+ });
291
+ }
292
+ // =========================================
293
+ // ario-ant-escrow PDAs
294
+ // =========================================
295
+ /**
296
+ * Derive the EscrowAnt PDA for a given ANT mint. Exactly one escrow per
297
+ * ANT — re-deriving the address tells you whether an escrow already
298
+ * exists (look up the account and check `data.length`).
299
+ *
300
+ * Seeds: ["escrow_ant", ant_mint]
301
+ * Source: contracts/programs/ario-ant-escrow/src/state.rs::ESCROW_ANT_SEED
302
+ */
303
+ export async function getEscrowAntPDA(antMint, programId = ARIO_ANT_ESCROW_PROGRAM_ID) {
304
+ return getProgramDerivedAddress({
305
+ programAddress: programId,
306
+ seeds: [ESCROW_ANT_SEED, addressEncoder.encode(antMint)],
307
+ });
308
+ }
309
+ /**
310
+ * Derive the EscrowToken PDA for a given depositor and asset ID.
311
+ *
312
+ * Seeds: ["escrow_token", depositor, asset_id]
313
+ * Source: contracts/programs/ario-ant-escrow/src/state.rs::ESCROW_TOKEN_SEED
314
+ */
315
+ export async function getEscrowTokenPDA(depositor, assetId, programId = ARIO_ANT_ESCROW_PROGRAM_ID) {
316
+ return getProgramDerivedAddress({
317
+ programAddress: programId,
318
+ seeds: [ESCROW_TOKEN_SEED, addressEncoder.encode(depositor), assetId],
319
+ });
320
+ }
321
+ /**
322
+ * Derive the EscrowVault PDA for a given depositor and asset ID.
323
+ *
324
+ * Seeds: ["escrow_vault", depositor, asset_id]
325
+ * Source: contracts/programs/ario-ant-escrow/src/state.rs::ESCROW_VAULT_SEED
326
+ */
327
+ export async function getEscrowVaultPDA(depositor, assetId, programId = ARIO_ANT_ESCROW_PROGRAM_ID) {
328
+ return getProgramDerivedAddress({
329
+ programAddress: programId,
330
+ seeds: [ESCROW_VAULT_SEED, addressEncoder.encode(depositor), assetId],
331
+ });
332
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * Off-chain prediction of `prescribe_epoch`'s observer selection.
18
+ *
19
+ * `ario_gar::prescribe_epoch` selects observers INTERNALLY via a weighted
20
+ * roulette over the `GatewayRegistry`, then uses `remaining_accounts` only as a
21
+ * lookup table for the ~50 selected Gateway PDAs (+ NameRegistry). A cranker
22
+ * that instead supplies every registry gateway hits Solana's
23
+ * `MAX_TX_ACCOUNT_LOCKS = 64` on large registries and the tx is rejected at
24
+ * pre-flight. This helper mirrors the on-chain selection so the caller can
25
+ * supply exactly the selected set.
26
+ *
27
+ * The selection is deterministic from on-chain state that is stable once
28
+ * `epoch.weights_tallied == 1`: the frozen `epoch.hashchain` entropy beacon and
29
+ * the live `registry.gateways[*].composite_weight`.
30
+ *
31
+ * Cross-language parity with the Rust handler
32
+ * (`ar-io-solana-contracts/programs/ario-gar/src/instructions/epoch.rs`, the
33
+ * `prescribe_epoch` observer-selection block) is asserted in
34
+ * `predict-prescribed-observers.test.ts` against vectors generated by the Rust
35
+ * reference example `programs/ario-gar/examples/predict_prescribed_observers.rs`.
36
+ * If you change this algorithm, update that example and the test together.
37
+ */
38
+ import { createHash } from 'crypto';
39
+ function sha256(data) {
40
+ return createHash('sha256').update(data).digest();
41
+ }
42
+ /** `u128::from_le_bytes(bytes[0..16])` — little-endian, first 16 bytes only. */
43
+ function leU128(bytes) {
44
+ let v = 0n;
45
+ for (let i = 15; i >= 0; i--) {
46
+ v = (v << 8n) | BigInt(bytes[i]);
47
+ }
48
+ return v;
49
+ }
50
+ /**
51
+ * Predict the operator pubkeys `prescribe_epoch` will select as observers.
52
+ *
53
+ * Returns the selected `GatewaySlot.address` values (operator pubkeys) in
54
+ * selection order, at most `maxObservers`. These correspond 1:1 to the on-chain
55
+ * `epoch.prescribed_observer_gateways` array, and are what Gateway PDAs are
56
+ * derived from (`[GATEWAY_SEED, operator]`). NOTE this is NOT
57
+ * `epoch.prescribed_observers`, which `prescribe_epoch` later overwrites with
58
+ * each gateway's resolved `observer_address`.
59
+ *
60
+ * @param epochHashchain `epoch.hashchain` — exactly 32 bytes, frozen at
61
+ * `create_epoch`.
62
+ * @param slots `registry.gateways[0 .. epoch.active_gateway_count]` in registry
63
+ * (slot-index) order. Pass the whole prefix including any zero-weight slots —
64
+ * order and the live weight sum must match the on-chain walk exactly. Empty /
65
+ * zero-weight slots contribute nothing and can never be selected.
66
+ * @param maxObservers `epoch_settings.prescribed_observer_count`. Clamped to
67
+ * `slots.length` (the on-chain `min(prescribed_observer_count, active_count)`).
68
+ */
69
+ export function predictPrescribedObservers(epochHashchain, slots, maxObservers) {
70
+ if (epochHashchain.length !== 32) {
71
+ throw new Error(`epochHashchain must be 32 bytes, got ${epochHashchain.length}`);
72
+ }
73
+ const activeCount = slots.length;
74
+ const cap = Math.min(maxObservers, activeCount);
75
+ // Live total weight (epoch.rs: `for i in 0..active_count { total += w }`).
76
+ // u128 sum of u64 weights over <=3000 slots cannot overflow, so the Rust
77
+ // `saturating_add` never actually saturates; plain BigInt addition matches.
78
+ let totalWeight = 0n;
79
+ for (const slot of slots) {
80
+ totalWeight += slot.compositeWeight;
81
+ }
82
+ const selected = [];
83
+ if (totalWeight === 0n || activeCount === 0 || cap === 0) {
84
+ return selected;
85
+ }
86
+ // Initial entropy: sha256(hashchain).
87
+ let hashBytes = sha256(epochHashchain);
88
+ // GAR-019: bounded retries, up to cap * 10 rounds.
89
+ const maxRounds = cap * 10;
90
+ for (let round = 0; round < maxRounds; round++) {
91
+ if (selected.length >= cap)
92
+ break;
93
+ const randomValue = leU128(hashBytes.subarray(0, 16)) % totalWeight;
94
+ let cumulative = 0n;
95
+ for (const slot of slots) {
96
+ cumulative += slot.compositeWeight;
97
+ if (cumulative > randomValue && slot.compositeWeight > 0n) {
98
+ // Anti-duplicate: skip if already chosen, but the round still consumes
99
+ // the roulette hit — break the walk either way (epoch.rs).
100
+ if (!selected.includes(slot.address)) {
101
+ selected.push(slot.address);
102
+ }
103
+ break;
104
+ }
105
+ }
106
+ // Re-hash for the next round: sha256(hash_bytes).
107
+ hashBytes = sha256(hashBytes);
108
+ }
109
+ return selected;
110
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * Lightweight retry helper with exponential back-off + jitter for Solana RPC
18
+ * read calls.
19
+ *
20
+ * Wraps any async function so transient transport errors (HTTP 429/5xx,
21
+ * network timeouts, etc.) are retried automatically while "normal" failures
22
+ * (account-not-found, deserialization) bubble immediately.
23
+ *
24
+ * Usage:
25
+ * ```ts
26
+ * const account = await withRetry(() => fetchEncodedAccount(rpc, pda));
27
+ * ```
28
+ */
29
+ import { Logger } from '../common/logger.js';
30
+ const logger = new Logger({ level: 'error' });
31
+ const DEFAULT_MAX_ATTEMPTS = 3;
32
+ const DEFAULT_BASE_DELAY_MS = 1_000;
33
+ const DEFAULT_MAX_DELAY_MS = 10_000;
34
+ /**
35
+ * Default retryable-error heuristic.
36
+ *
37
+ * We retry on:
38
+ * - Network / fetch errors (`TypeError: fetch failed`)
39
+ * - HTTP 429 (rate-limit) and 5xx (server) surfaced by kit's transport as
40
+ * `SolanaError` with "HTTP error (4xx|5xx)" in the message.
41
+ * - Timeout errors from opossum or native `AbortError`.
42
+ *
43
+ * We do NOT retry on:
44
+ * - JSON-RPC application errors (account not found, invalid params, etc.)
45
+ * - Deserialization / decoding errors
46
+ * - Any error without a recognisable transport signature
47
+ */
48
+ export function isRetryableError(error) {
49
+ if (error == null)
50
+ return false;
51
+ const name = error.name ?? '';
52
+ const message = String(error.message ?? '');
53
+ const code = error.context
54
+ ?.__code;
55
+ // SolanaError from kit's transport for HTTP 429 / 5xx
56
+ if (/HTTP error \(4(?:0[89]|[1-9]\d)|HTTP error \(5\d\d\)/.test(message))
57
+ return true;
58
+ // Specific 429 match (rate-limit) — always retry
59
+ if (/HTTP error \(429\)/.test(message))
60
+ return true;
61
+ // Network-level failures: fetch failed, ECONNRESET, ETIMEDOUT, etc.
62
+ if (name === 'TypeError' && /fetch failed/i.test(message))
63
+ return true;
64
+ if (/ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN/i.test(message))
65
+ return true;
66
+ // Abort / timeout
67
+ if (name === 'AbortError' || name === 'TimeoutError')
68
+ return true;
69
+ if (/timed out/i.test(message))
70
+ return true;
71
+ // Opossum circuit-breaker open — the breaker itself will fallback, but if
72
+ // we're layered on top we shouldn't retry (the breaker handles it).
73
+ if (/breaker is open/i.test(message))
74
+ return false;
75
+ // Numeric Solana JSON-RPC codes that indicate transient overload
76
+ if (typeof code === 'number' && (code === -32005 || code === -32016))
77
+ return true;
78
+ return false;
79
+ }
80
+ function sleep(ms) {
81
+ return new Promise((resolve) => setTimeout(resolve, ms));
82
+ }
83
+ function jitteredDelay(base, attempt, cap) {
84
+ const exponential = base * 2 ** attempt;
85
+ const capped = Math.min(exponential, cap);
86
+ return capped * (0.5 + Math.random() * 0.5);
87
+ }
88
+ /**
89
+ * Execute `fn` with automatic retries on transient failures.
90
+ *
91
+ * ```ts
92
+ * const data = await withRetry(() => rpc.getAccountInfo(addr).send());
93
+ * ```
94
+ */
95
+ export async function withRetry(fn, opts) {
96
+ const maxAttempts = opts?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
97
+ const baseDelayMs = opts?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;
98
+ const maxDelayMs = opts?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
99
+ const retryable = opts?.isRetryable ?? isRetryableError;
100
+ let lastError;
101
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
102
+ try {
103
+ return await fn();
104
+ }
105
+ catch (error) {
106
+ lastError = error;
107
+ const isLast = attempt === maxAttempts - 1;
108
+ if (isLast || !retryable(error)) {
109
+ throw error;
110
+ }
111
+ const delay = jitteredDelay(baseDelayMs, attempt, maxDelayMs);
112
+ logger.debug(`[retry] attempt ${attempt + 1}/${maxAttempts} failed, retrying in ${Math.round(delay)}ms`, { error: String(error) });
113
+ await sleep(delay);
114
+ }
115
+ }
116
+ throw lastError;
117
+ }