@x1id/resolve 0.23.0 → 0.44.0
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 +97 -0
- package/dist/accounts.d.ts +6 -0
- package/dist/accounts.js +6 -1
- package/dist/agent.d.ts +184 -8
- package/dist/agent.js +351 -3
- package/dist/agentPolicy.d.ts +238 -0
- package/dist/agentPolicy.js +364 -0
- package/dist/attestation.d.ts +25 -0
- package/dist/attestation.js +25 -0
- package/dist/checkout.d.ts +216 -0
- package/dist/checkout.js +327 -0
- package/dist/credentials.d.ts +466 -0
- package/dist/credentials.js +669 -0
- package/dist/gate.d.ts +94 -4
- package/dist/gate.js +206 -7
- package/dist/gateClient.d.ts +12 -0
- package/dist/giftLink.d.ts +272 -0
- package/dist/giftLink.js +452 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +32 -0
- package/dist/loginButton.d.ts +69 -0
- package/dist/loginButton.js +207 -0
- package/dist/logo.d.ts +1 -0
- package/dist/logo.js +8 -0
- package/dist/messaging.d.ts +439 -0
- package/dist/messaging.js +979 -0
- package/dist/nsGate.d.ts +98 -0
- package/dist/nsGate.js +172 -0
- package/dist/oidc.d.ts +272 -0
- package/dist/oidc.js +641 -0
- package/dist/pay.d.ts +513 -0
- package/dist/pay.js +756 -0
- package/dist/payment.d.ts +9 -2
- package/dist/payment.js +9 -2
- package/dist/recovery.d.ts +186 -0
- package/dist/recovery.js +276 -0
- package/dist/recoveryNft.d.ts +183 -0
- package/dist/recoveryNft.js +291 -0
- package/dist/shield.d.ts +373 -0
- package/dist/shield.js +695 -0
- package/dist/subscription.d.ts +439 -0
- package/dist/subscription.js +642 -0
- package/dist/textRecords.d.ts +52 -6
- package/dist/textRecords.js +80 -8
- package/dist/types.d.ts +3 -0
- package/dist/x402Receipt.d.ts +246 -0
- package/dist/x402Receipt.js +374 -0
- package/package.json +18 -1
- package/schema/agent-manifest.json +91 -8
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recovery for TOKENIZED names (program upgrade 6, OpenProject #8471; spec
|
|
3
|
+
* docs/specs/recovery-tokenized.md). The untokenized family stays in
|
|
4
|
+
* `recovery.ts` and still refuses a tokenized handle.
|
|
5
|
+
*
|
|
6
|
+
* The holder ARMS recovery by approving the registry PDA `["nft_recovery",
|
|
7
|
+
* handle]` as the pNFT's Metaplex **Transfer delegate** (the token stays in
|
|
8
|
+
* the holder's wallet). The recovery key — any signer, e.g. an axio_multisig
|
|
9
|
+
* vault; it never pays — initiates; after the same timelock as untokenized
|
|
10
|
+
* recovery (`max(Config.recovery_timelock_secs, 24h)`, 7 days by default)
|
|
11
|
+
* `complete_nft_recovery` moves the pNFT to the target with the registry PDA
|
|
12
|
+
* signing, and bumps the holder epoch. Any move of the pNFT (native sale,
|
|
13
|
+
* off-platform transfer) clears the Metaplex delegate and kills the arm;
|
|
14
|
+
* anyone may then disarm it (rent back to whoever paid it).
|
|
15
|
+
*
|
|
16
|
+
* `list_handle` REFUSES a tokenized name whose arm is live
|
|
17
|
+
* (`NftRecoveryArmed`, 6214): the holder disarms first. Its tokenized branch
|
|
18
|
+
* takes the `["nft_recovery", handle]` PDA as a REQUIRED trailing account
|
|
19
|
+
* after `sysvar_instructions` ({@link deriveNftRecoveryPda}); omitting it or
|
|
20
|
+
* passing another address is `NftRecoveryAccountMismatch` (6210). A dead arm
|
|
21
|
+
* (see {@link isNftRecoveryArmLive}) does not block a listing.
|
|
22
|
+
*
|
|
23
|
+
* arm_nft_recovery(recovery) holder refused while listed/auctioned/locked
|
|
24
|
+
* initiate_nft_recovery(target) recovery needs a live arm; refused while locked
|
|
25
|
+
* cancel_nft_recovery holder allowed while locked
|
|
26
|
+
* complete_nft_recovery recovery + payer, after the timelock
|
|
27
|
+
* disarm_nft_recovery holder (live; revokes the delegate) / anyone (dead)
|
|
28
|
+
*/
|
|
29
|
+
import type { AddressLike, BuiltInstruction } from "./delegate.js";
|
|
30
|
+
import type { PdaFinder } from "./namespacePricing.js";
|
|
31
|
+
export declare const NFT_RECOVERY_SEED = "nft_recovery";
|
|
32
|
+
export declare const ARM_NFT_RECOVERY_DISCRIMINATOR: Uint8Array;
|
|
33
|
+
export declare const INITIATE_NFT_RECOVERY_DISCRIMINATOR: Uint8Array;
|
|
34
|
+
export declare const CANCEL_NFT_RECOVERY_DISCRIMINATOR: Uint8Array;
|
|
35
|
+
export declare const COMPLETE_NFT_RECOVERY_DISCRIMINATOR: Uint8Array;
|
|
36
|
+
export declare const DISARM_NFT_RECOVERY_DISCRIMINATOR: Uint8Array;
|
|
37
|
+
/** `sha256("account:NftRecovery")[0..8]`. */
|
|
38
|
+
export declare const NFT_RECOVERY_ACCOUNT_DISCRIMINATOR: Uint8Array;
|
|
39
|
+
/** `8 + NftRecovery::INIT_SPACE`. */
|
|
40
|
+
export declare const NFT_RECOVERY_ACCOUNT_LEN = 258;
|
|
41
|
+
/** Program error codes (NftRecoveryError, offset 6205). */
|
|
42
|
+
export declare const NFT_RECOVERY_ERRORS: Readonly<Record<number, string>>;
|
|
43
|
+
/** Every PDA the tokenized recovery family touches, for one handle + holder. */
|
|
44
|
+
export interface NftRecoveryAccounts {
|
|
45
|
+
readonly nftRecovery: string;
|
|
46
|
+
readonly config: string;
|
|
47
|
+
readonly listing: string;
|
|
48
|
+
readonly auction: string;
|
|
49
|
+
readonly metadata: string;
|
|
50
|
+
readonly masterEdition: string;
|
|
51
|
+
readonly holderTokenAccount: string;
|
|
52
|
+
readonly tokenRecord: string;
|
|
53
|
+
}
|
|
54
|
+
/** `["nft_recovery", handle]` alone: the trailing account `list_handle`'s
|
|
55
|
+
* tokenized branch requires (after `sysvar_instructions`). */
|
|
56
|
+
export declare function deriveNftRecoveryPda(find: PdaFinder, programId: AddressLike, handle: AddressLike): Promise<string>;
|
|
57
|
+
/** Derive the PDAs for `handle`, its pNFT `mint` and the (armed) `holder`. */
|
|
58
|
+
export declare function deriveNftRecoveryAccounts(find: PdaFinder, programId: AddressLike, handle: AddressLike, mint: AddressLike, holder: AddressLike): Promise<NftRecoveryAccounts>;
|
|
59
|
+
/** The target's ATA + token record for `complete_nft_recovery`. */
|
|
60
|
+
export declare function deriveNftRecoveryTargetAccounts(find: PdaFinder, mint: AddressLike, target: AddressLike): Promise<{
|
|
61
|
+
readonly targetTokenAccount: string;
|
|
62
|
+
readonly targetTokenRecord: string;
|
|
63
|
+
}>;
|
|
64
|
+
export interface ArmNftRecoveryParams {
|
|
65
|
+
readonly programId: AddressLike;
|
|
66
|
+
/** The current pNFT holder (signer; pays the PDA rent). */
|
|
67
|
+
readonly holder: AddressLike;
|
|
68
|
+
readonly handle: AddressLike;
|
|
69
|
+
readonly mint: AddressLike;
|
|
70
|
+
/** The recovery key (guardian / multisig vault). */
|
|
71
|
+
readonly recovery: AddressLike;
|
|
72
|
+
readonly accounts: NftRecoveryAccounts;
|
|
73
|
+
}
|
|
74
|
+
/** `arm_nft_recovery(recovery)`. ~95k CU. */
|
|
75
|
+
export declare function buildArmNftRecoveryIx(p: ArmNftRecoveryParams): BuiltInstruction;
|
|
76
|
+
export interface InitiateNftRecoveryParams {
|
|
77
|
+
readonly programId: AddressLike;
|
|
78
|
+
/** The recovery key. Signer (a multisig vault PDA may sign via CPI). */
|
|
79
|
+
readonly recovery: AddressLike;
|
|
80
|
+
readonly handle: AddressLike;
|
|
81
|
+
/** Derived for the ARMED holder. */
|
|
82
|
+
readonly accounts: NftRecoveryAccounts;
|
|
83
|
+
/** Who receives the pNFT (must differ from the armed holder). */
|
|
84
|
+
readonly target: AddressLike;
|
|
85
|
+
}
|
|
86
|
+
/** `initiate_nft_recovery(target)`: starts (or restarts) the timelock. */
|
|
87
|
+
export declare function buildInitiateNftRecoveryIx(p: InitiateNftRecoveryParams): BuiltInstruction;
|
|
88
|
+
export interface CancelNftRecoveryParams {
|
|
89
|
+
readonly programId: AddressLike;
|
|
90
|
+
/** The CURRENT holder. Signer. Allowed while the name is locked. */
|
|
91
|
+
readonly holder: AddressLike;
|
|
92
|
+
readonly handle: AddressLike;
|
|
93
|
+
readonly nftRecovery: AddressLike;
|
|
94
|
+
/** The holder's canonical pNFT ATA (authority proof). */
|
|
95
|
+
readonly holderTokenAccount: AddressLike;
|
|
96
|
+
}
|
|
97
|
+
/** `cancel_nft_recovery()`: clears the pending recovery; the arm stays. */
|
|
98
|
+
export declare function buildCancelNftRecoveryIx(p: CancelNftRecoveryParams): BuiltInstruction;
|
|
99
|
+
export interface DisarmNftRecoveryParams {
|
|
100
|
+
readonly programId: AddressLike;
|
|
101
|
+
/** The armed holder while the arm is live (the delegate is revoked); anyone
|
|
102
|
+
* once it is dead. Signer. */
|
|
103
|
+
readonly signer: AddressLike;
|
|
104
|
+
readonly handle: AddressLike;
|
|
105
|
+
readonly mint: AddressLike;
|
|
106
|
+
/** `NftRecovery.rent_payer` (receives the rent). */
|
|
107
|
+
readonly rentPayer: AddressLike;
|
|
108
|
+
/** Derived for the ARMED holder. */
|
|
109
|
+
readonly accounts: NftRecoveryAccounts;
|
|
110
|
+
}
|
|
111
|
+
/** `disarm_nft_recovery()`: closes the arm. */
|
|
112
|
+
export declare function buildDisarmNftRecoveryIx(p: DisarmNftRecoveryParams): BuiltInstruction;
|
|
113
|
+
export interface CompleteNftRecoveryParams {
|
|
114
|
+
readonly programId: AddressLike;
|
|
115
|
+
/** The recovery key. Signer. Never pays. */
|
|
116
|
+
readonly recovery: AddressLike;
|
|
117
|
+
/** Funds the target's ATA + token record (anyone; never X1ID). Signer. */
|
|
118
|
+
readonly payer: AddressLike;
|
|
119
|
+
readonly handle: AddressLike;
|
|
120
|
+
readonly mint: AddressLike;
|
|
121
|
+
/** `NftRecovery.holder`. */
|
|
122
|
+
readonly holder: AddressLike;
|
|
123
|
+
/** `NftRecovery.target`. */
|
|
124
|
+
readonly target: AddressLike;
|
|
125
|
+
/** `NftRecovery.rent_payer`. */
|
|
126
|
+
readonly rentPayer: AddressLike;
|
|
127
|
+
/** Derived for the ARMED holder. */
|
|
128
|
+
readonly accounts: NftRecoveryAccounts;
|
|
129
|
+
readonly targetAccounts: {
|
|
130
|
+
readonly targetTokenAccount: AddressLike;
|
|
131
|
+
readonly targetTokenRecord: AddressLike;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/** `complete_nft_recovery()`: ~180k CU when the target's ATA is created. */
|
|
135
|
+
export declare function buildCompleteNftRecoveryIx(p: CompleteNftRecoveryParams): BuiltInstruction;
|
|
136
|
+
/** A decoded `NftRecovery` account. */
|
|
137
|
+
export interface NftRecoveryAccount {
|
|
138
|
+
readonly version: number;
|
|
139
|
+
readonly bump: number;
|
|
140
|
+
readonly handle: string;
|
|
141
|
+
readonly mint: string;
|
|
142
|
+
readonly holder: string;
|
|
143
|
+
readonly holderTokenAccount: string;
|
|
144
|
+
readonly recovery: string;
|
|
145
|
+
/** `max(registered_at, holder_epoch)` at arm; a different current value = dead arm. */
|
|
146
|
+
readonly holderEpoch: bigint;
|
|
147
|
+
readonly armedAt: bigint;
|
|
148
|
+
/** 0n = nothing pending. */
|
|
149
|
+
readonly initiatedAt: bigint;
|
|
150
|
+
/** Null when nothing is pending. */
|
|
151
|
+
readonly target: string | null;
|
|
152
|
+
readonly rentPayer: string;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Decode `NftRecovery`: disc | version | bump | handle@10 | mint@42 |
|
|
156
|
+
* holder@74 | holder_token_account@106 | recovery@138 | holder_epoch@170 |
|
|
157
|
+
* armed_at@178 | initiated_at@186 | target@194 | rent_payer@226. Null when
|
|
158
|
+
* not one.
|
|
159
|
+
*/
|
|
160
|
+
export declare function decodeNftRecovery(data: Uint8Array): NftRecoveryAccount | null;
|
|
161
|
+
/** When a pending tokenized recovery can complete (the program's clamp). */
|
|
162
|
+
export declare function nftRecoveryCompletesAt(r: NftRecoveryAccount, configTimelockSecs: bigint | null): bigint | null;
|
|
163
|
+
/**
|
|
164
|
+
* Whether the Metaplex `TokenRecord` data still names `nftRecovery` as the
|
|
165
|
+
* Transfer delegate (the on-chain liveness rule's Metaplex half). Layout:
|
|
166
|
+
* key | bump | state | Option<u64> rule_set_revision | Option<Pubkey>
|
|
167
|
+
* delegate | Option<u8> role.
|
|
168
|
+
*/
|
|
169
|
+
export declare function tokenRecordHasRecoveryDelegate(tokenRecordData: Uint8Array, nftRecovery: AddressLike): boolean;
|
|
170
|
+
/**
|
|
171
|
+
* Client mirror of the program's liveness rule for the parts a UI can see
|
|
172
|
+
* without the handle's epoch: the arm exists, the current `holder` is the
|
|
173
|
+
* armed holder, and the holder's `TokenRecord` still names the PDA as its
|
|
174
|
+
* Transfer delegate. `true` means `list_handle` will refuse
|
|
175
|
+
* (`NftRecoveryArmed`) until the holder disarms. Pass `nftRecoveryData = null`
|
|
176
|
+
* when the account does not exist.
|
|
177
|
+
*/
|
|
178
|
+
export declare function isNftRecoveryArmLive(p: {
|
|
179
|
+
readonly nftRecovery: AddressLike;
|
|
180
|
+
readonly nftRecoveryData: Uint8Array | null;
|
|
181
|
+
readonly holder: AddressLike;
|
|
182
|
+
readonly tokenRecordData: Uint8Array | null;
|
|
183
|
+
}): boolean;
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recovery for TOKENIZED names (program upgrade 6, OpenProject #8471; spec
|
|
3
|
+
* docs/specs/recovery-tokenized.md). The untokenized family stays in
|
|
4
|
+
* `recovery.ts` and still refuses a tokenized handle.
|
|
5
|
+
*
|
|
6
|
+
* The holder ARMS recovery by approving the registry PDA `["nft_recovery",
|
|
7
|
+
* handle]` as the pNFT's Metaplex **Transfer delegate** (the token stays in
|
|
8
|
+
* the holder's wallet). The recovery key — any signer, e.g. an axio_multisig
|
|
9
|
+
* vault; it never pays — initiates; after the same timelock as untokenized
|
|
10
|
+
* recovery (`max(Config.recovery_timelock_secs, 24h)`, 7 days by default)
|
|
11
|
+
* `complete_nft_recovery` moves the pNFT to the target with the registry PDA
|
|
12
|
+
* signing, and bumps the holder epoch. Any move of the pNFT (native sale,
|
|
13
|
+
* off-platform transfer) clears the Metaplex delegate and kills the arm;
|
|
14
|
+
* anyone may then disarm it (rent back to whoever paid it).
|
|
15
|
+
*
|
|
16
|
+
* `list_handle` REFUSES a tokenized name whose arm is live
|
|
17
|
+
* (`NftRecoveryArmed`, 6214): the holder disarms first. Its tokenized branch
|
|
18
|
+
* takes the `["nft_recovery", handle]` PDA as a REQUIRED trailing account
|
|
19
|
+
* after `sysvar_instructions` ({@link deriveNftRecoveryPda}); omitting it or
|
|
20
|
+
* passing another address is `NftRecoveryAccountMismatch` (6210). A dead arm
|
|
21
|
+
* (see {@link isNftRecoveryArmLive}) does not block a listing.
|
|
22
|
+
*
|
|
23
|
+
* arm_nft_recovery(recovery) holder refused while listed/auctioned/locked
|
|
24
|
+
* initiate_nft_recovery(target) recovery needs a live arm; refused while locked
|
|
25
|
+
* cancel_nft_recovery holder allowed while locked
|
|
26
|
+
* complete_nft_recovery recovery + payer, after the timelock
|
|
27
|
+
* disarm_nft_recovery holder (live; revokes the delegate) / anyone (dead)
|
|
28
|
+
*/
|
|
29
|
+
import { encodeBase58, decodeBase58_32 } from "./base58.js";
|
|
30
|
+
import { bytesEqual } from "./accounts.js";
|
|
31
|
+
import { recoveryCompletesAt } from "./recovery.js";
|
|
32
|
+
export const NFT_RECOVERY_SEED = "nft_recovery";
|
|
33
|
+
export const ARM_NFT_RECOVERY_DISCRIMINATOR = Uint8Array.from([155, 147, 102, 165, 32, 203, 158, 250]);
|
|
34
|
+
export const INITIATE_NFT_RECOVERY_DISCRIMINATOR = Uint8Array.from([159, 54, 209, 96, 179, 210, 209, 150]);
|
|
35
|
+
export const CANCEL_NFT_RECOVERY_DISCRIMINATOR = Uint8Array.from([162, 102, 34, 153, 10, 203, 112, 212]);
|
|
36
|
+
export const COMPLETE_NFT_RECOVERY_DISCRIMINATOR = Uint8Array.from([114, 50, 101, 177, 37, 203, 222, 56]);
|
|
37
|
+
export const DISARM_NFT_RECOVERY_DISCRIMINATOR = Uint8Array.from([120, 4, 249, 9, 63, 241, 141, 150]);
|
|
38
|
+
/** `sha256("account:NftRecovery")[0..8]`. */
|
|
39
|
+
export const NFT_RECOVERY_ACCOUNT_DISCRIMINATOR = Uint8Array.from([209, 254, 105, 152, 211, 178, 138, 91]);
|
|
40
|
+
/** `8 + NftRecovery::INIT_SPACE`. */
|
|
41
|
+
export const NFT_RECOVERY_ACCOUNT_LEN = 258;
|
|
42
|
+
/** Program error codes (NftRecoveryError, offset 6205). */
|
|
43
|
+
export const NFT_RECOVERY_ERRORS = {
|
|
44
|
+
6205: "NftRecoveryNotTokenized",
|
|
45
|
+
6206: "NftRecoveryStale",
|
|
46
|
+
6207: "NftRecoveryNotRecoveryKey",
|
|
47
|
+
6208: "NftRecoveryNotPending",
|
|
48
|
+
6209: "NftRecoveryTimelockActive",
|
|
49
|
+
6210: "NftRecoveryAccountMismatch",
|
|
50
|
+
6211: "NftRecoveryMarketActive",
|
|
51
|
+
6212: "NftRecoveryNotHolder",
|
|
52
|
+
6213: "NftRecoveryBadTarget",
|
|
53
|
+
6214: "NftRecoveryArmed",
|
|
54
|
+
};
|
|
55
|
+
const TOKEN_PROGRAM = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
|
|
56
|
+
const ATA_PROGRAM = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
|
|
57
|
+
const METADATA_PROGRAM = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s";
|
|
58
|
+
const SYSVAR_INSTRUCTIONS = "Sysvar1nstructions1111111111111111111111111";
|
|
59
|
+
const SYSTEM_PROGRAM = "11111111111111111111111111111111";
|
|
60
|
+
const TE = new TextEncoder();
|
|
61
|
+
function raw(v, what) {
|
|
62
|
+
if (typeof v === "string") {
|
|
63
|
+
const b = decodeBase58_32(v);
|
|
64
|
+
if (!b)
|
|
65
|
+
throw new Error(`${what} is not a valid base58 address`);
|
|
66
|
+
return b;
|
|
67
|
+
}
|
|
68
|
+
if (v.length !== 32)
|
|
69
|
+
throw new Error(`${what} must be exactly 32 bytes`);
|
|
70
|
+
return v;
|
|
71
|
+
}
|
|
72
|
+
function b58(v, what) {
|
|
73
|
+
return encodeBase58(raw(v, what));
|
|
74
|
+
}
|
|
75
|
+
function nk(v, what, isSigner, isWritable) {
|
|
76
|
+
return { pubkey: b58(v, what), isSigner, isWritable };
|
|
77
|
+
}
|
|
78
|
+
function join(a, b) {
|
|
79
|
+
const out = new Uint8Array(a.length + b.length);
|
|
80
|
+
out.set(a, 0);
|
|
81
|
+
out.set(b, a.length);
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
async function pnftAta(find, owner, mint) {
|
|
85
|
+
return find([raw(owner, "owner"), raw(TOKEN_PROGRAM, "tokenProgram"), raw(mint, "mint")], ATA_PROGRAM);
|
|
86
|
+
}
|
|
87
|
+
async function pnftTokenRecord(find, mint, token) {
|
|
88
|
+
return find([TE.encode("metadata"), raw(METADATA_PROGRAM, "metadataProgram"), raw(mint, "mint"), TE.encode("token_record"), raw(token, "token")], METADATA_PROGRAM);
|
|
89
|
+
}
|
|
90
|
+
/** `["nft_recovery", handle]` alone: the trailing account `list_handle`'s
|
|
91
|
+
* tokenized branch requires (after `sysvar_instructions`). */
|
|
92
|
+
export async function deriveNftRecoveryPda(find, programId, handle) {
|
|
93
|
+
return find([TE.encode(NFT_RECOVERY_SEED), raw(handle, "handle")], b58(programId, "programId"));
|
|
94
|
+
}
|
|
95
|
+
/** Derive the PDAs for `handle`, its pNFT `mint` and the (armed) `holder`. */
|
|
96
|
+
export async function deriveNftRecoveryAccounts(find, programId, handle, mint, holder) {
|
|
97
|
+
const program = b58(programId, "programId");
|
|
98
|
+
const h = raw(handle, "handle");
|
|
99
|
+
const m = raw(mint, "mint");
|
|
100
|
+
const meta = raw(METADATA_PROGRAM, "metadataProgram");
|
|
101
|
+
const holderTokenAccount = await pnftAta(find, holder, mint);
|
|
102
|
+
const [nftRecovery, config, listing, auction, metadata, masterEdition, tokenRecord] = await Promise.all([
|
|
103
|
+
find([TE.encode(NFT_RECOVERY_SEED), h], program),
|
|
104
|
+
find([TE.encode("config")], program),
|
|
105
|
+
find([TE.encode("listing"), h], program),
|
|
106
|
+
find([TE.encode("auction"), h], program),
|
|
107
|
+
find([TE.encode("metadata"), meta, m], METADATA_PROGRAM),
|
|
108
|
+
find([TE.encode("metadata"), meta, m, TE.encode("edition")], METADATA_PROGRAM),
|
|
109
|
+
pnftTokenRecord(find, mint, holderTokenAccount),
|
|
110
|
+
]);
|
|
111
|
+
return { nftRecovery, config, listing, auction, metadata, masterEdition, holderTokenAccount, tokenRecord };
|
|
112
|
+
}
|
|
113
|
+
/** The target's ATA + token record for `complete_nft_recovery`. */
|
|
114
|
+
export async function deriveNftRecoveryTargetAccounts(find, mint, target) {
|
|
115
|
+
const targetTokenAccount = await pnftAta(find, target, mint);
|
|
116
|
+
return { targetTokenAccount, targetTokenRecord: await pnftTokenRecord(find, mint, targetTokenAccount) };
|
|
117
|
+
}
|
|
118
|
+
/** `arm_nft_recovery(recovery)`. ~95k CU. */
|
|
119
|
+
export function buildArmNftRecoveryIx(p) {
|
|
120
|
+
const a = p.accounts;
|
|
121
|
+
return {
|
|
122
|
+
programId: b58(p.programId, "programId"),
|
|
123
|
+
keys: [
|
|
124
|
+
nk(p.holder, "holder", true, true),
|
|
125
|
+
nk(p.handle, "handle", false, false),
|
|
126
|
+
nk(a.nftRecovery, "nftRecovery", false, true),
|
|
127
|
+
nk(p.mint, "mint", false, false),
|
|
128
|
+
nk(a.holderTokenAccount, "holderTokenAccount", false, true),
|
|
129
|
+
nk(a.metadata, "metadata", false, true),
|
|
130
|
+
nk(a.masterEdition, "masterEdition", false, false),
|
|
131
|
+
nk(a.tokenRecord, "tokenRecord", false, true),
|
|
132
|
+
nk(a.listing, "listing", false, false),
|
|
133
|
+
nk(a.auction, "auction", false, false),
|
|
134
|
+
nk(METADATA_PROGRAM, "tokenMetadataProgram", false, false),
|
|
135
|
+
nk(TOKEN_PROGRAM, "tokenProgram", false, false),
|
|
136
|
+
nk(SYSTEM_PROGRAM, "systemProgram", false, false),
|
|
137
|
+
nk(SYSVAR_INSTRUCTIONS, "sysvarInstructions", false, false),
|
|
138
|
+
],
|
|
139
|
+
data: join(ARM_NFT_RECOVERY_DISCRIMINATOR, raw(p.recovery, "recovery")),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/** `initiate_nft_recovery(target)`: starts (or restarts) the timelock. */
|
|
143
|
+
export function buildInitiateNftRecoveryIx(p) {
|
|
144
|
+
const a = p.accounts;
|
|
145
|
+
return {
|
|
146
|
+
programId: b58(p.programId, "programId"),
|
|
147
|
+
keys: [
|
|
148
|
+
nk(p.recovery, "recovery", true, false),
|
|
149
|
+
nk(p.handle, "handle", false, false),
|
|
150
|
+
nk(a.nftRecovery, "nftRecovery", false, true),
|
|
151
|
+
nk(a.holderTokenAccount, "holderTokenAccount", false, false),
|
|
152
|
+
nk(a.tokenRecord, "tokenRecord", false, false),
|
|
153
|
+
nk(a.auction, "auction", false, false),
|
|
154
|
+
],
|
|
155
|
+
data: join(INITIATE_NFT_RECOVERY_DISCRIMINATOR, raw(p.target, "target")),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/** `cancel_nft_recovery()`: clears the pending recovery; the arm stays. */
|
|
159
|
+
export function buildCancelNftRecoveryIx(p) {
|
|
160
|
+
return {
|
|
161
|
+
programId: b58(p.programId, "programId"),
|
|
162
|
+
keys: [
|
|
163
|
+
nk(p.holder, "holder", true, false),
|
|
164
|
+
nk(p.handle, "handle", false, false),
|
|
165
|
+
nk(p.nftRecovery, "nftRecovery", false, true),
|
|
166
|
+
nk(p.holderTokenAccount, "holderTokenAccount", false, false),
|
|
167
|
+
],
|
|
168
|
+
data: CANCEL_NFT_RECOVERY_DISCRIMINATOR.slice(),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/** `disarm_nft_recovery()`: closes the arm. */
|
|
172
|
+
export function buildDisarmNftRecoveryIx(p) {
|
|
173
|
+
const a = p.accounts;
|
|
174
|
+
return {
|
|
175
|
+
programId: b58(p.programId, "programId"),
|
|
176
|
+
keys: [
|
|
177
|
+
nk(p.signer, "signer", true, true),
|
|
178
|
+
nk(p.handle, "handle", false, false),
|
|
179
|
+
nk(a.nftRecovery, "nftRecovery", false, true),
|
|
180
|
+
nk(p.rentPayer, "rentPayer", false, true),
|
|
181
|
+
nk(p.mint, "mint", false, false),
|
|
182
|
+
nk(a.holderTokenAccount, "holderTokenAccount", false, true),
|
|
183
|
+
nk(a.metadata, "metadata", false, true),
|
|
184
|
+
nk(a.masterEdition, "masterEdition", false, false),
|
|
185
|
+
nk(a.tokenRecord, "tokenRecord", false, true),
|
|
186
|
+
nk(METADATA_PROGRAM, "tokenMetadataProgram", false, false),
|
|
187
|
+
nk(TOKEN_PROGRAM, "tokenProgram", false, false),
|
|
188
|
+
nk(SYSTEM_PROGRAM, "systemProgram", false, false),
|
|
189
|
+
nk(SYSVAR_INSTRUCTIONS, "sysvarInstructions", false, false),
|
|
190
|
+
],
|
|
191
|
+
data: DISARM_NFT_RECOVERY_DISCRIMINATOR.slice(),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/** `complete_nft_recovery()`: ~180k CU when the target's ATA is created. */
|
|
195
|
+
export function buildCompleteNftRecoveryIx(p) {
|
|
196
|
+
const a = p.accounts;
|
|
197
|
+
return {
|
|
198
|
+
programId: b58(p.programId, "programId"),
|
|
199
|
+
keys: [
|
|
200
|
+
nk(p.recovery, "recovery", true, false),
|
|
201
|
+
nk(p.payer, "payer", true, true),
|
|
202
|
+
nk(p.handle, "handle", false, true),
|
|
203
|
+
nk(a.config, "config", false, false),
|
|
204
|
+
nk(a.nftRecovery, "nftRecovery", false, true),
|
|
205
|
+
nk(p.rentPayer, "rentPayer", false, true),
|
|
206
|
+
nk(a.listing, "listing", false, false),
|
|
207
|
+
nk(a.auction, "auction", false, false),
|
|
208
|
+
nk(p.mint, "mint", false, false),
|
|
209
|
+
nk(p.holder, "holder", false, false),
|
|
210
|
+
nk(a.holderTokenAccount, "holderTokenAccount", false, true),
|
|
211
|
+
nk(p.target, "target", false, false),
|
|
212
|
+
nk(p.targetAccounts.targetTokenAccount, "targetTokenAccount", false, true),
|
|
213
|
+
nk(a.metadata, "metadata", false, true),
|
|
214
|
+
nk(a.masterEdition, "masterEdition", false, false),
|
|
215
|
+
nk(a.tokenRecord, "tokenRecord", false, true),
|
|
216
|
+
nk(p.targetAccounts.targetTokenRecord, "targetTokenRecord", false, true),
|
|
217
|
+
nk(METADATA_PROGRAM, "tokenMetadataProgram", false, false),
|
|
218
|
+
nk(TOKEN_PROGRAM, "tokenProgram", false, false),
|
|
219
|
+
nk(ATA_PROGRAM, "associatedTokenProgram", false, false),
|
|
220
|
+
nk(SYSTEM_PROGRAM, "systemProgram", false, false),
|
|
221
|
+
nk(SYSVAR_INSTRUCTIONS, "sysvarInstructions", false, false),
|
|
222
|
+
],
|
|
223
|
+
data: COMPLETE_NFT_RECOVERY_DISCRIMINATOR.slice(),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Decode `NftRecovery`: disc | version | bump | handle@10 | mint@42 |
|
|
228
|
+
* holder@74 | holder_token_account@106 | recovery@138 | holder_epoch@170 |
|
|
229
|
+
* armed_at@178 | initiated_at@186 | target@194 | rent_payer@226. Null when
|
|
230
|
+
* not one.
|
|
231
|
+
*/
|
|
232
|
+
export function decodeNftRecovery(data) {
|
|
233
|
+
if (data.length < NFT_RECOVERY_ACCOUNT_LEN || !bytesEqual(data.subarray(0, 8), NFT_RECOVERY_ACCOUNT_DISCRIMINATOR))
|
|
234
|
+
return null;
|
|
235
|
+
const dv = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
236
|
+
const pk = (o) => encodeBase58(data.subarray(o, o + 32));
|
|
237
|
+
const initiatedAt = dv.getBigInt64(186, true);
|
|
238
|
+
return {
|
|
239
|
+
version: data[8],
|
|
240
|
+
bump: data[9],
|
|
241
|
+
handle: pk(10),
|
|
242
|
+
mint: pk(42),
|
|
243
|
+
holder: pk(74),
|
|
244
|
+
holderTokenAccount: pk(106),
|
|
245
|
+
recovery: pk(138),
|
|
246
|
+
holderEpoch: dv.getBigInt64(170, true),
|
|
247
|
+
armedAt: dv.getBigInt64(178, true),
|
|
248
|
+
initiatedAt,
|
|
249
|
+
target: initiatedAt === 0n ? null : pk(194),
|
|
250
|
+
rentPayer: pk(226),
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/** When a pending tokenized recovery can complete (the program's clamp). */
|
|
254
|
+
export function nftRecoveryCompletesAt(r, configTimelockSecs) {
|
|
255
|
+
return recoveryCompletesAt(r.initiatedAt, configTimelockSecs);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Whether the Metaplex `TokenRecord` data still names `nftRecovery` as the
|
|
259
|
+
* Transfer delegate (the on-chain liveness rule's Metaplex half). Layout:
|
|
260
|
+
* key | bump | state | Option<u64> rule_set_revision | Option<Pubkey>
|
|
261
|
+
* delegate | Option<u8> role.
|
|
262
|
+
*/
|
|
263
|
+
export function tokenRecordHasRecoveryDelegate(tokenRecordData, nftRecovery) {
|
|
264
|
+
const d = tokenRecordData;
|
|
265
|
+
if (d.length < 4)
|
|
266
|
+
return false;
|
|
267
|
+
let o = 3;
|
|
268
|
+
o += d[o] === 1 ? 9 : 1;
|
|
269
|
+
if (d[o] !== 1 || d.length < o + 35)
|
|
270
|
+
return false;
|
|
271
|
+
if (!bytesEqual(d.subarray(o + 1, o + 33), raw(nftRecovery, "nftRecovery")))
|
|
272
|
+
return false;
|
|
273
|
+
o += 33;
|
|
274
|
+
return d[o] === 1 && d[o + 1] === 1;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Client mirror of the program's liveness rule for the parts a UI can see
|
|
278
|
+
* without the handle's epoch: the arm exists, the current `holder` is the
|
|
279
|
+
* armed holder, and the holder's `TokenRecord` still names the PDA as its
|
|
280
|
+
* Transfer delegate. `true` means `list_handle` will refuse
|
|
281
|
+
* (`NftRecoveryArmed`) until the holder disarms. Pass `nftRecoveryData = null`
|
|
282
|
+
* when the account does not exist.
|
|
283
|
+
*/
|
|
284
|
+
export function isNftRecoveryArmLive(p) {
|
|
285
|
+
if (!p.nftRecoveryData || !p.tokenRecordData)
|
|
286
|
+
return false;
|
|
287
|
+
const rec = decodeNftRecovery(p.nftRecoveryData);
|
|
288
|
+
if (!rec || rec.holder !== b58(p.holder, "holder"))
|
|
289
|
+
return false;
|
|
290
|
+
return tokenRecordHasRecoveryDelegate(p.tokenRecordData, p.nftRecovery);
|
|
291
|
+
}
|