@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.
- package/README.md +682 -600
- 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 +221 -0
- package/lib/esm/cli/commands/gatewayWriteCommands.js +142 -23
- package/lib/esm/cli/commands/pruneCommands.js +150 -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 +280 -174
- package/lib/esm/common/ant-registry.js +17 -143
- package/lib/esm/common/ant.js +44 -1167
- package/lib/esm/common/faucet.js +11 -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 +51 -263
- 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,90 @@
|
|
|
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
|
+
* Helpers for JSON-RPC reads against {@link SolanaRpc} that return a
|
|
18
|
+
* web3.js-shaped `AccountInfo` (Buffer-backed `data`, plain Address strings).
|
|
19
|
+
* Used by the ACL/AntRegistry path so the deserializers can stay agnostic of
|
|
20
|
+
* the RPC client's response shape.
|
|
21
|
+
*/
|
|
22
|
+
import { address } from '@solana/kit';
|
|
23
|
+
import { withRetry } from './retry.js';
|
|
24
|
+
/**
|
|
25
|
+
* Map an arbitrary commitment string to one of kit's three supported tiers.
|
|
26
|
+
* Anything we don't recognise (or `undefined`) falls back to `confirmed`,
|
|
27
|
+
* which is the project-wide default for read paths.
|
|
28
|
+
*/
|
|
29
|
+
function toKitCommitment(commitment) {
|
|
30
|
+
if (commitment === 'finalized' ||
|
|
31
|
+
commitment === 'confirmed' ||
|
|
32
|
+
commitment === 'processed') {
|
|
33
|
+
return commitment;
|
|
34
|
+
}
|
|
35
|
+
return 'confirmed';
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Fetch a single account and decode its data into a Buffer-backed shape that
|
|
39
|
+
* looks like the legacy `web3.js` `AccountInfo`. Returns `null` if the account
|
|
40
|
+
* doesn't exist (so callers can handle "missing PDA" without try/catch).
|
|
41
|
+
*/
|
|
42
|
+
export async function getAccountInfoLegacy(rpc, pda, commitment) {
|
|
43
|
+
const res = await withRetry(() => rpc
|
|
44
|
+
.getAccountInfo(pda, {
|
|
45
|
+
encoding: 'base64',
|
|
46
|
+
commitment: toKitCommitment(commitment),
|
|
47
|
+
})
|
|
48
|
+
.send());
|
|
49
|
+
if (!res.value)
|
|
50
|
+
return null;
|
|
51
|
+
const [dataB64] = res.value.data;
|
|
52
|
+
return {
|
|
53
|
+
data: Buffer.from(dataB64, 'base64'),
|
|
54
|
+
owner: address(res.value.owner),
|
|
55
|
+
lamports: Number(res.value.lamports),
|
|
56
|
+
executable: res.value.executable,
|
|
57
|
+
rentEpoch: 0,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Fetch multiple accounts in a single RPC round-trip and decode each into
|
|
62
|
+
* the legacy `AccountInfo` shape. Missing accounts come back as `null`,
|
|
63
|
+
* preserving 1:1 alignment with the input `pdas` array.
|
|
64
|
+
*
|
|
65
|
+
* Used by paginated readers (ACL pages, etc.) to load all sibling pages
|
|
66
|
+
* via `getMultipleAccountsInfo` rather than firing N parallel
|
|
67
|
+
* `getAccountInfo` calls.
|
|
68
|
+
*/
|
|
69
|
+
export async function getMultipleAccountsInfoLegacy(rpc, pdas, commitment) {
|
|
70
|
+
if (pdas.length === 0)
|
|
71
|
+
return [];
|
|
72
|
+
const res = await withRetry(() => rpc
|
|
73
|
+
.getMultipleAccounts(pdas, {
|
|
74
|
+
encoding: 'base64',
|
|
75
|
+
commitment: toKitCommitment(commitment),
|
|
76
|
+
})
|
|
77
|
+
.send());
|
|
78
|
+
return res.value.map((acct) => {
|
|
79
|
+
if (!acct)
|
|
80
|
+
return null;
|
|
81
|
+
const [dataB64] = acct.data;
|
|
82
|
+
return {
|
|
83
|
+
data: Buffer.from(dataB64, 'base64'),
|
|
84
|
+
owner: address(acct.owner),
|
|
85
|
+
lamports: Number(acct.lamports),
|
|
86
|
+
executable: acct.executable,
|
|
87
|
+
rentEpoch: 0,
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
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
|
+
* Metaplex-standard JSON metadata builder for AR.IO ANT NFTs.
|
|
18
|
+
*
|
|
19
|
+
* The Core asset's `uri` field on chain points at a JSON file with this
|
|
20
|
+
* shape; marketplaces and DAS providers fetch + parse it for their
|
|
21
|
+
* inline name/image/description display. The 3 ArNS traits (`ArNS Name`,
|
|
22
|
+
* `Type`, `Undername Limit`) live in the on-chain Attributes plugin, NOT
|
|
23
|
+
* in this JSON — see ADR-012.
|
|
24
|
+
*
|
|
25
|
+
* This helper produces the exact same JSON shape as the migration import
|
|
26
|
+
* (`migration/import/src/metadata.ts::buildAntMetadata`); the two paths
|
|
27
|
+
* MUST stay in lock-step or migrated and freshly-spawned ANTs will
|
|
28
|
+
* render differently in marketplace UIs. A regression test in
|
|
29
|
+
* `spawn-ant.test.ts` asserts byte-equality between the two for matching
|
|
30
|
+
* inputs.
|
|
31
|
+
*
|
|
32
|
+
* Pure function. No network, no Turbo, no dependencies. Caller handles
|
|
33
|
+
* uploading the resulting JSON to Arweave (e.g. via `@ardrive/turbo-sdk`
|
|
34
|
+
* with a `HexSolanaSigner` — free for files under 100 KiB) and passes the
|
|
35
|
+
* resulting `ar://${txid}` to `spawnSolanaANT`'s `state.uri`.
|
|
36
|
+
*/
|
|
37
|
+
// Re-export the SDK-canonical AR.IO logo TX ID + ar:// protocol from the
|
|
38
|
+
// shared constants module so this metadata builder stays in lock-step with
|
|
39
|
+
// the rest of the SDK + the migration import package's JSON output.
|
|
40
|
+
export { ARIO_LOGO_TX_ID, AR_IO_PROTOCOL as AR_PROTOCOL, } from '../constants.js';
|
|
41
|
+
import { ARIO_LOGO_TX_ID, AR_IO_PROTOCOL as AR_PROTOCOL, } from '../constants.js';
|
|
42
|
+
function makeUri(txId, gateway) {
|
|
43
|
+
return gateway ? `https://${gateway}/raw/${txId}` : `${AR_PROTOCOL}${txId}`;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Build ANT NFT JSON metadata.
|
|
47
|
+
*
|
|
48
|
+
* Output is byte-identical to the migration import's `buildAntMetadata`
|
|
49
|
+
* for matching inputs. Defaults to `ar://` URLs (ADR-012 canonical); pass
|
|
50
|
+
* `gateway` to opt into https URLs.
|
|
51
|
+
*/
|
|
52
|
+
export function buildAntMetadata(params) {
|
|
53
|
+
const logoTxId = params.logoTxId || ARIO_LOGO_TX_ID;
|
|
54
|
+
const ticker = params.ticker || 'ANT';
|
|
55
|
+
const arnsName = params.arnsName;
|
|
56
|
+
const gateway = params.gateway;
|
|
57
|
+
const description = params.description ||
|
|
58
|
+
(arnsName
|
|
59
|
+
? `Arweave Name Token for ${arnsName}.ar.io`
|
|
60
|
+
: 'Arweave Name Token');
|
|
61
|
+
const logoUri = makeUri(logoTxId, gateway);
|
|
62
|
+
const metadata = {
|
|
63
|
+
name: arnsName || params.name,
|
|
64
|
+
symbol: ticker,
|
|
65
|
+
description,
|
|
66
|
+
image: logoUri,
|
|
67
|
+
properties: {
|
|
68
|
+
files: [{ uri: logoUri, type: 'image/png' }],
|
|
69
|
+
// Metaplex standard categories are image|video|audio|vr|html. The
|
|
70
|
+
// semantic "this is an ANT for an ArNS domain" is conveyed by the
|
|
71
|
+
// on-chain Attributes plugin (Type=lease/permabuy), not this JSON.
|
|
72
|
+
category: 'image',
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
if (arnsName) {
|
|
76
|
+
metadata.external_url = gateway
|
|
77
|
+
? `https://${arnsName}.ar.io`
|
|
78
|
+
: `${AR_PROTOCOL}${arnsName}`;
|
|
79
|
+
}
|
|
80
|
+
return metadata;
|
|
81
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
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
|
+
* Reader for the on-chain Metaplex Core asset (`AssetV1` + plugin chain).
|
|
18
|
+
*
|
|
19
|
+
* The codama-generated decoders in `generated/mpl-core` only cover the
|
|
20
|
+
* fixed-shape header section (`AssetV1`, `PluginHeaderV1`, etc.) — they
|
|
21
|
+
* don't know how to follow the variable-offset registry chain to a
|
|
22
|
+
* specific plugin payload. This module fills that gap with a small
|
|
23
|
+
* walker that:
|
|
24
|
+
*
|
|
25
|
+
* 1. decodes the `AssetV1` prefix (key + owner + update authority +
|
|
26
|
+
* name + uri + seq);
|
|
27
|
+
* 2. follows the `PluginHeaderV1.pluginRegistryOffset` to the
|
|
28
|
+
* `PluginRegistryV1`;
|
|
29
|
+
* 3. iterates registry records to find the requested plugin variant;
|
|
30
|
+
* 4. decodes the `Plugin` enum at the registry record's offset.
|
|
31
|
+
*
|
|
32
|
+
* The same walk lives on-chain in `programs/ario-arns/src/state/mod.rs`
|
|
33
|
+
* (`read_mpl_core_attribute`) and `programs/ario-core/src/mpl_core.rs`
|
|
34
|
+
* (`read_ant_program`). The three implementations are pinned against
|
|
35
|
+
* each other by the migration import's edge-case fixtures and unit
|
|
36
|
+
* tests at all three layers.
|
|
37
|
+
*
|
|
38
|
+
* Reading is best-effort: any parse failure returns `null` rather than
|
|
39
|
+
* throwing, so callers can fall back to canonical defaults
|
|
40
|
+
* (e.g. `ARIO_ANT_PROGRAM_ID`) when an asset was minted without the
|
|
41
|
+
* `ANT Program` attribute or has a layout the walker doesn't recognise.
|
|
42
|
+
*/
|
|
43
|
+
import { fetchEncodedAccount, } from '@solana/kit';
|
|
44
|
+
import { PluginType, getAssetV1Decoder, getPluginDecoder, getPluginHeaderV1Decoder, getPluginRegistryV1Decoder, } from '@ar.io/solana-contracts/mpl-core';
|
|
45
|
+
import { ARIO_ANT_PROGRAM_ID } from './constants.js';
|
|
46
|
+
/**
|
|
47
|
+
* On-chain trait keys. Marketplaces and DAS indexers query these by
|
|
48
|
+
* exact string match — keep in lock-step with:
|
|
49
|
+
* - `migration/import/src/phases/phase2-ants.ts`
|
|
50
|
+
* - `programs/ario-arns/src/mpl_core_cpi.rs::TRAIT_KEY_*`
|
|
51
|
+
* - `programs/ario-core/src/mpl_core.rs::TRAIT_KEY_ANT_PROGRAM`
|
|
52
|
+
*/
|
|
53
|
+
export const TRAIT_KEY_ARNS_NAME = 'ArNS Name';
|
|
54
|
+
export const TRAIT_KEY_TYPE = 'Type';
|
|
55
|
+
export const TRAIT_KEY_UNDERNAME_LIMIT = 'Undername Limit';
|
|
56
|
+
export const TRAIT_KEY_ANT_PROGRAM = 'ANT Program';
|
|
57
|
+
/**
|
|
58
|
+
* Read a single attribute value from a Metaplex Core asset's Attributes
|
|
59
|
+
* plugin. Returns `null` when the asset has no plugin section, has no
|
|
60
|
+
* Attributes plugin, the requested key is absent, or any layer of the
|
|
61
|
+
* walk fails to decode. This is a best-effort lookup — callers should
|
|
62
|
+
* use a canonical default rather than treating absence as an error.
|
|
63
|
+
*
|
|
64
|
+
* `data` is the raw account data (post-discriminator? no — MPL Core
|
|
65
|
+
* doesn't use the 8-byte Anchor discriminator, the asset is the
|
|
66
|
+
* account's full data starting from the `Key` enum byte).
|
|
67
|
+
*/
|
|
68
|
+
export function readAttribute(data, key) {
|
|
69
|
+
try {
|
|
70
|
+
// ── 1. Asset header (variable size — name and uri are length-prefixed).
|
|
71
|
+
const [, afterAsset] = getAssetV1Decoder().read(data, 0);
|
|
72
|
+
// No plugin section → no attributes.
|
|
73
|
+
if (afterAsset >= data.length)
|
|
74
|
+
return null;
|
|
75
|
+
// ── 2. PluginHeaderV1: 9 bytes (1-byte key + u64 registry offset).
|
|
76
|
+
const [pluginHeader] = getPluginHeaderV1Decoder().read(data, afterAsset);
|
|
77
|
+
const registryOffset = Number(pluginHeader.pluginRegistryOffset);
|
|
78
|
+
if (registryOffset >= data.length)
|
|
79
|
+
return null;
|
|
80
|
+
// ── 3. PluginRegistryV1: list of (pluginType, authority, offset).
|
|
81
|
+
const [registry] = getPluginRegistryV1Decoder().read(data, registryOffset);
|
|
82
|
+
for (const record of registry.registry) {
|
|
83
|
+
// `pluginType` is the kit-decoded numeric `PluginType` enum value
|
|
84
|
+
// (e.g. `PluginType.Attributes === 6`). The matching `Plugin`
|
|
85
|
+
// payload at `record.offset` is a discriminated union whose
|
|
86
|
+
// `__kind` is the string variant name — so we use the numeric
|
|
87
|
+
// enum here and the string tag below.
|
|
88
|
+
if (record.pluginType !== PluginType.Attributes)
|
|
89
|
+
continue;
|
|
90
|
+
const [plugin] = getPluginDecoder().read(data, Number(record.offset));
|
|
91
|
+
if (plugin.__kind !== 'Attributes')
|
|
92
|
+
continue;
|
|
93
|
+
const attrs = plugin.fields[0].attributeList;
|
|
94
|
+
const found = attrs.find((a) => a.key === key);
|
|
95
|
+
return found ? found.value : null;
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Any decode failure (truncated buffer, unknown plugin variant, etc.)
|
|
101
|
+
// — treat as "no override" and let the caller fall back.
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Convenience wrapper: fetch the asset account and parse its
|
|
107
|
+
* `ANT Program` attribute. Returns `null` when the account doesn't
|
|
108
|
+
* exist, has no `ANT Program` trait, or the value can't be parsed —
|
|
109
|
+
* callers should fall back to the canonical `ARIO_ANT_PROGRAM_ID`.
|
|
110
|
+
*
|
|
111
|
+
* Importing this here (rather than wiring it directly into the resolver
|
|
112
|
+
* paths) lets `ant-readable.ts` keep its constructor synchronous and
|
|
113
|
+
* have a separate async `fromAsset(rpc, mint)` factory that does the
|
|
114
|
+
* lookup once and caches the result.
|
|
115
|
+
*/
|
|
116
|
+
export async function fetchAntProgramFromAsset(rpc, mint, config) {
|
|
117
|
+
const account = await fetchEncodedAccount(rpc, mint, config);
|
|
118
|
+
if (!account.exists)
|
|
119
|
+
return null;
|
|
120
|
+
const value = readAttribute(account.data, TRAIT_KEY_ANT_PROGRAM);
|
|
121
|
+
if (value === null)
|
|
122
|
+
return null;
|
|
123
|
+
// The ANT Program attribute is a base58-encoded program address.
|
|
124
|
+
// We don't validate length here — the kit `Address` brand is
|
|
125
|
+
// structural and downstream `getProgramDerivedAddress` will reject
|
|
126
|
+
// malformed values. Returning `null` on missing keeps the call site
|
|
127
|
+
// simple ("found a value or didn't"); a malformed value is a holder
|
|
128
|
+
// self-grief that surfaces at the next PDA derivation.
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Decide which ANT program id to trust for a SIGNING (write) transaction.
|
|
133
|
+
*
|
|
134
|
+
* The `ANT Program` trait returned by {@link fetchAntProgramFromAsset} is read
|
|
135
|
+
* from Metaplex Core asset / RPC data and is NOT authenticated — a malicious
|
|
136
|
+
* asset owner, or a spoofed/compromised RPC response, can set it to an
|
|
137
|
+
* attacker-controlled program. Write instructions use this value as the
|
|
138
|
+
* instruction `programAddress` while including the caller as a signer, so
|
|
139
|
+
* silently trusting a non-canonical detected value would let an attacker route
|
|
140
|
+
* a victim's signed transaction to their own program and inherit signer /
|
|
141
|
+
* writable privileges over the included accounts.
|
|
142
|
+
*
|
|
143
|
+
* Trust rules — BYO-ANT (ADR-016 / BD-100) stays supported, but only via
|
|
144
|
+
* explicit opt-in:
|
|
145
|
+
* - An `explicit` program id (the caller passed `antProgramId`) is always
|
|
146
|
+
* honored — the caller has taken responsibility for it.
|
|
147
|
+
* - Otherwise a detected value is honored only when it is `null` (no trait)
|
|
148
|
+
* or equals the canonical {@link ARIO_ANT_PROGRAM_ID}.
|
|
149
|
+
* - A detected NON-canonical value with no explicit opt-in throws, rather
|
|
150
|
+
* than silently signing against an untrusted program.
|
|
151
|
+
*
|
|
152
|
+
* Read-only paths never sign, so they may auto-detect freely; this guard is
|
|
153
|
+
* only for the write path.
|
|
154
|
+
*/
|
|
155
|
+
export function resolveWriteAntProgram(args) {
|
|
156
|
+
const canonical = args.canonical ?? ARIO_ANT_PROGRAM_ID;
|
|
157
|
+
if (args.explicit !== undefined)
|
|
158
|
+
return args.explicit;
|
|
159
|
+
if (args.detected === null || args.detected === canonical)
|
|
160
|
+
return canonical;
|
|
161
|
+
throw new Error(`ANT.init auto-detected a non-canonical ANT program (${args.detected}) ` +
|
|
162
|
+
`from the asset's "${TRAIT_KEY_ANT_PROGRAM}" trait. That value is read ` +
|
|
163
|
+
`from asset/RPC data and is not authenticated, so the SDK refuses to ` +
|
|
164
|
+
`build a *signed* transaction against it implicitly (doing so could ` +
|
|
165
|
+
`route your signature to an attacker-controlled program). To use a ` +
|
|
166
|
+
`third-party (BYO-ANT) program for writes, pass it explicitly: ` +
|
|
167
|
+
`ANT.init({ ..., antProgramId }).`);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Fetch the current owner of an MPL Core asset. Returns `null` when the
|
|
171
|
+
* account doesn't exist or the AssetV1 layout can't be decoded.
|
|
172
|
+
*
|
|
173
|
+
* Used by `SolanaARIOWriteable` to decide whether to bundle
|
|
174
|
+
* `ant.sync_attributes` into a buy / manage tx. The Attributes plugin's
|
|
175
|
+
* `BasePluginAuthority` is `Owner`, so only the current asset holder
|
|
176
|
+
* can sign the inner CPI; if the caller isn't the holder, bundling
|
|
177
|
+
* would abort the whole tx (BD-095 — non-holder ArNS lease management
|
|
178
|
+
* must continue to succeed; the actual ANT owner can call the public
|
|
179
|
+
* `syncAttributes` later to converge state).
|
|
180
|
+
*/
|
|
181
|
+
export async function fetchMplCoreOwner(rpc, mint, config) {
|
|
182
|
+
const account = await fetchEncodedAccount(rpc, mint, config);
|
|
183
|
+
if (!account.exists)
|
|
184
|
+
return null;
|
|
185
|
+
try {
|
|
186
|
+
const [asset] = getAssetV1Decoder().read(account.data, 0);
|
|
187
|
+
return asset.owner;
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|