@ar.io/sdk 3.24.0 → 4.0.0-alpha.2
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/README.md +757 -589
- package/lib/esm/cli/cli.js +188 -152
- package/lib/esm/cli/commands/antCommands.js +23 -58
- package/lib/esm/cli/commands/arnsPurchaseCommands.js +48 -30
- package/lib/esm/cli/commands/escrowCommands.js +227 -0
- package/lib/esm/cli/commands/gatewayWriteCommands.js +140 -23
- package/lib/esm/cli/commands/pruneCommands.js +154 -0
- package/lib/esm/cli/commands/readCommands.js +22 -3
- package/lib/esm/cli/commands/transfer.js +6 -6
- package/lib/esm/cli/options.js +124 -58
- package/lib/esm/cli/utils.js +303 -175
- package/lib/esm/common/ant-registry.js +17 -143
- package/lib/esm/common/ant.js +44 -1167
- package/lib/esm/common/faucet.js +17 -6
- package/lib/esm/common/index.js +0 -4
- package/lib/esm/common/io.js +25 -1412
- package/lib/esm/constants.js +13 -19
- package/lib/esm/solana/ant-readable.js +724 -0
- package/lib/esm/solana/ant-registry-readable.js +133 -0
- package/lib/esm/solana/ant-registry-writeable.js +472 -0
- package/lib/esm/solana/ant-writeable.js +384 -0
- package/lib/esm/solana/ata.js +70 -0
- package/lib/esm/solana/canonical-message.js +128 -0
- package/lib/esm/solana/clusters.js +111 -0
- package/lib/esm/solana/constants.js +146 -0
- package/lib/esm/solana/delegation-math.js +112 -0
- package/lib/esm/solana/deserialize.js +711 -0
- package/lib/esm/solana/escrow.js +839 -0
- package/lib/{cjs/utils/json.js → esm/solana/events.js} +15 -10
- package/lib/esm/solana/funding-plan.js +699 -0
- package/lib/esm/solana/index.js +126 -0
- package/lib/esm/solana/instruction.js +39 -0
- package/lib/esm/solana/io-readable.js +2182 -0
- package/lib/esm/solana/io-writeable.js +3196 -0
- package/lib/esm/solana/json-rpc.js +90 -0
- package/lib/esm/solana/metadata.js +81 -0
- package/lib/esm/solana/mpl-core.js +192 -0
- package/lib/esm/solana/pda.js +332 -0
- package/lib/esm/solana/predict-prescribed-observers.js +110 -0
- package/lib/esm/solana/retry.js +117 -0
- package/lib/esm/solana/rpc-circuit-breaker.js +258 -0
- package/lib/esm/solana/send.js +372 -0
- package/lib/esm/solana/spawn-ant.js +224 -0
- package/lib/esm/solana/types.js +1 -0
- package/lib/esm/types/ant.js +27 -15
- package/lib/esm/types/io.js +8 -11
- package/lib/esm/utils/ant.js +0 -63
- package/lib/esm/utils/index.js +0 -3
- package/lib/esm/version.js +1 -1
- package/lib/types/cli/commands/antCommands.d.ts +5 -13
- package/lib/types/cli/commands/arnsPurchaseCommands.d.ts +33 -7
- package/lib/types/cli/commands/escrowCommands.d.ts +68 -0
- package/lib/types/cli/commands/gatewayWriteCommands.d.ts +12 -11
- package/lib/types/cli/commands/pruneCommands.d.ts +31 -0
- package/lib/types/cli/commands/readCommands.d.ts +27 -22
- package/lib/types/cli/commands/transfer.d.ts +9 -9
- package/lib/types/cli/options.d.ts +76 -21
- package/lib/types/cli/types.d.ts +11 -13
- package/lib/types/cli/utils.d.ts +71 -31
- package/lib/types/common/ant-registry.d.ts +49 -47
- package/lib/types/common/ant.d.ts +54 -539
- package/lib/types/common/faucet.d.ts +20 -8
- package/lib/types/common/index.d.ts +0 -3
- package/lib/types/common/io.d.ts +66 -258
- package/lib/types/constants.d.ts +11 -18
- package/lib/types/solana/ant-readable.d.ts +180 -0
- package/lib/types/solana/ant-registry-readable.d.ts +105 -0
- package/lib/types/solana/ant-registry-writeable.d.ts +249 -0
- package/lib/types/solana/ant-writeable.d.ts +177 -0
- package/lib/types/solana/ata.d.ts +44 -0
- package/lib/types/solana/canonical-message.d.ts +121 -0
- package/lib/types/solana/clusters.d.ts +109 -0
- package/lib/types/solana/constants.d.ts +119 -0
- package/lib/types/solana/delegation-math.d.ts +45 -0
- package/lib/types/solana/deserialize.d.ts +262 -0
- package/lib/types/solana/escrow.d.ts +480 -0
- package/lib/types/solana/events.d.ts +38 -0
- package/lib/types/solana/funding-plan.d.ts +225 -0
- package/lib/types/solana/index.d.ts +87 -0
- package/lib/types/solana/instruction.d.ts +39 -0
- package/lib/types/solana/io-readable.d.ts +499 -0
- package/lib/types/solana/io-writeable.d.ts +893 -0
- package/lib/types/solana/json-rpc.d.ts +47 -0
- package/lib/types/solana/metadata.d.ts +84 -0
- package/lib/types/solana/mpl-core.d.ts +120 -0
- package/lib/types/solana/pda.d.ts +95 -0
- package/lib/types/solana/predict-prescribed-observers.d.ts +28 -0
- package/lib/types/solana/retry.d.ts +62 -0
- package/lib/types/solana/rpc-circuit-breaker.d.ts +78 -0
- package/lib/types/solana/send.d.ts +94 -0
- package/lib/types/solana/spawn-ant.d.ts +145 -0
- package/lib/types/solana/types.d.ts +82 -0
- package/lib/types/types/ant-registry.d.ts +43 -4
- package/lib/types/types/ant.d.ts +114 -96
- package/lib/types/types/common.d.ts +18 -74
- package/lib/types/types/faucet.d.ts +2 -2
- package/lib/types/types/io.d.ts +244 -158
- package/lib/types/types/token.d.ts +0 -12
- package/lib/types/utils/ant.d.ts +1 -12
- package/lib/types/utils/index.d.ts +0 -3
- package/lib/types/version.d.ts +1 -1
- package/package.json +36 -33
- package/lib/cjs/cli/cli.js +0 -822
- package/lib/cjs/cli/commands/antCommands.js +0 -113
- package/lib/cjs/cli/commands/arnsPurchaseCommands.js +0 -212
- package/lib/cjs/cli/commands/gatewayWriteCommands.js +0 -210
- package/lib/cjs/cli/commands/readCommands.js +0 -215
- package/lib/cjs/cli/commands/transfer.js +0 -159
- package/lib/cjs/cli/options.js +0 -470
- package/lib/cjs/cli/types.js +0 -2
- package/lib/cjs/cli/utils.js +0 -639
- package/lib/cjs/common/ant-registry.js +0 -155
- package/lib/cjs/common/ant-versions.js +0 -93
- package/lib/cjs/common/ant.js +0 -1182
- package/lib/cjs/common/arweave.js +0 -27
- package/lib/cjs/common/contracts/ao-process.js +0 -224
- package/lib/cjs/common/error.js +0 -64
- package/lib/cjs/common/faucet.js +0 -150
- package/lib/cjs/common/hyperbeam/hb.js +0 -173
- package/lib/cjs/common/index.js +0 -42
- package/lib/cjs/common/io.js +0 -1423
- package/lib/cjs/common/logger.js +0 -83
- package/lib/cjs/common/loggers/winston.js +0 -68
- package/lib/cjs/common/marketplace.js +0 -731
- package/lib/cjs/common/turbo.js +0 -223
- package/lib/cjs/constants.js +0 -41
- package/lib/cjs/node/index.js +0 -39
- package/lib/cjs/package.json +0 -1
- package/lib/cjs/types/ant-registry.js +0 -2
- package/lib/cjs/types/ant.js +0 -168
- package/lib/cjs/types/common.js +0 -2
- package/lib/cjs/types/faucet.js +0 -2
- package/lib/cjs/types/index.js +0 -37
- package/lib/cjs/types/io.js +0 -51
- package/lib/cjs/types/token.js +0 -116
- package/lib/cjs/utils/ant.js +0 -108
- package/lib/cjs/utils/ao.js +0 -432
- package/lib/cjs/utils/arweave.js +0 -285
- package/lib/cjs/utils/base64.js +0 -62
- package/lib/cjs/utils/hash.js +0 -56
- package/lib/cjs/utils/index.js +0 -38
- package/lib/cjs/utils/processes.js +0 -173
- package/lib/cjs/utils/random.js +0 -30
- package/lib/cjs/utils/schema.js +0 -15
- package/lib/cjs/utils/url.js +0 -37
- package/lib/cjs/version.js +0 -20
- package/lib/cjs/web/index.js +0 -41
- package/lib/esm/common/ant-versions.js +0 -87
- package/lib/esm/common/arweave.js +0 -21
- package/lib/esm/common/contracts/ao-process.js +0 -220
- package/lib/esm/common/hyperbeam/hb.js +0 -169
- package/lib/esm/common/marketplace.js +0 -724
- package/lib/esm/common/turbo.js +0 -215
- package/lib/esm/node/index.js +0 -20
- package/lib/esm/utils/ao.js +0 -420
- package/lib/esm/utils/arweave.js +0 -271
- package/lib/esm/utils/processes.js +0 -167
- package/lib/esm/web/index.js +0 -20
- package/lib/types/common/ant-versions.d.ts +0 -39
- package/lib/types/common/arweave.d.ts +0 -17
- package/lib/types/common/contracts/ao-process.d.ts +0 -47
- package/lib/types/common/hyperbeam/hb.d.ts +0 -88
- package/lib/types/common/marketplace.d.ts +0 -568
- package/lib/types/common/turbo.d.ts +0 -61
- package/lib/types/node/index.d.ts +0 -20
- package/lib/types/utils/ao.d.ts +0 -80
- package/lib/types/utils/arweave.d.ts +0 -79
- package/lib/types/utils/processes.d.ts +0 -39
- 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
|
+
}
|