@ar.io/sdk 4.0.0-solana.18 → 4.0.0-solana.20
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/lib/esm/solana/escrow.js +103 -38
- package/lib/esm/solana/index.js +7 -1
- package/lib/esm/version.js +1 -1
- package/lib/types/solana/escrow.d.ts +84 -15
- package/lib/types/solana/index.d.ts +1 -1
- package/lib/types/version.d.ts +1 -1
- package/package.json +1 -1
package/lib/esm/solana/escrow.js
CHANGED
|
@@ -274,10 +274,36 @@ export class ANTEscrow {
|
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* Forward clock-skew buffer (seconds) added to `vault_end_timestamp` before
|
|
279
|
+
* the SDK considers a vault claimable. The SDK reads wall-clock time
|
|
280
|
+
* (`Date.now()`) while the on-chain gate reads Solana cluster time, and
|
|
281
|
+
* the two can disagree by several seconds. The buffer biases every skew
|
|
282
|
+
* race into the *friendly* direction: the SDK rejects when the chain
|
|
283
|
+
* would actually accept (user retries 30s later, succeeds), never the
|
|
284
|
+
* reverse (user submits a doomed tx and sees the raw on-chain error).
|
|
285
|
+
*
|
|
286
|
+
* 30s is conservative — Solana cluster clock typically drifts <2s vs
|
|
287
|
+
* wall clock — but matches the order of magnitude of the previously-used
|
|
288
|
+
* `60s` introspection tolerance in the removed `vault_introspect` module.
|
|
289
|
+
*/
|
|
290
|
+
export const CLOCK_SKEW_TOLERANCE_SECONDS = 30n;
|
|
291
|
+
/**
|
|
292
|
+
* Returns `true` when a vault escrow is past its unlock timestamp by at
|
|
293
|
+
* least {@link CLOCK_SKEW_TOLERANCE_SECONDS}. Non-throwing companion to
|
|
294
|
+
* {@link assertVaultClaimable} for UI gating (e.g. enabling/disabling a
|
|
295
|
+
* Submit button without showing an error).
|
|
296
|
+
*/
|
|
297
|
+
export function isVaultClaimable(escrow) {
|
|
298
|
+
const nowSeconds = BigInt(Math.floor(Date.now() / 1000));
|
|
299
|
+
return nowSeconds >= escrow.vaultEndTimestamp + CLOCK_SKEW_TOLERANCE_SECONDS;
|
|
300
|
+
}
|
|
277
301
|
/**
|
|
278
302
|
* Pre-flight the on-chain `VaultStillLocked` gate (ADR-022): refuse to build
|
|
279
|
-
* a claim tx while the vault is still locked
|
|
280
|
-
*
|
|
303
|
+
* a claim tx while the vault is still locked, with a small forward
|
|
304
|
+
* {@link CLOCK_SKEW_TOLERANCE_SECONDS} buffer so wall/cluster clock skew
|
|
305
|
+
* biases into the friendly direction. Surfaces the unlock timestamp so
|
|
306
|
+
* callers / UIs can show "claimable after <date>" instead of a doomed tx.
|
|
281
307
|
*
|
|
282
308
|
* Exported for unit-testability; not part of the public SDK surface — call the
|
|
283
309
|
* high-level `claimVaultArweave` / `claimVaultEthereum` instead, which invoke
|
|
@@ -286,14 +312,15 @@ export class ANTEscrow {
|
|
|
286
312
|
* @internal
|
|
287
313
|
*/
|
|
288
314
|
export function assertVaultClaimable(escrow) {
|
|
289
|
-
|
|
290
|
-
if (escrow.vaultEndTimestamp > nowSeconds) {
|
|
315
|
+
if (!isVaultClaimable(escrow)) {
|
|
291
316
|
const unlockIso = new Date(Number(escrow.vaultEndTimestamp) * 1000).toISOString();
|
|
292
317
|
throw new Error(`Vault escrow is still locked until ${unlockIso} ` +
|
|
293
|
-
`(vault_end_timestamp=${escrow.vaultEndTimestamp}
|
|
294
|
-
`
|
|
295
|
-
`
|
|
296
|
-
`
|
|
318
|
+
`(vault_end_timestamp=${escrow.vaultEndTimestamp}; ` +
|
|
319
|
+
`the SDK adds a ${CLOCK_SKEW_TOLERANCE_SECONDS}s clock-skew buffer ` +
|
|
320
|
+
`before allowing a claim). Active (still-locked) vault claims are ` +
|
|
321
|
+
`rejected on-chain with VaultStillLocked (ADR-022) — wait until ` +
|
|
322
|
+
`after the unlock timestamp + buffer, then claim again to receive ` +
|
|
323
|
+
`the tokens liquid.`);
|
|
297
324
|
}
|
|
298
325
|
}
|
|
299
326
|
/** Map the Codama-generated `EscrowToken` raw decoded type to our public
|
|
@@ -558,47 +585,85 @@ export class TokenEscrow {
|
|
|
558
585
|
}, { programAddress: this.programId });
|
|
559
586
|
}
|
|
560
587
|
/**
|
|
561
|
-
*
|
|
562
|
-
*
|
|
563
|
-
* `
|
|
588
|
+
* **Use {@link claimVaultArweaveIx} instead** — this single-send wrapper
|
|
589
|
+
* cannot work end-to-end for the Arweave attested vault-claim path,
|
|
590
|
+
* because the on-chain `claim_vault_arweave_attested` handler requires
|
|
591
|
+
* an Ed25519Program native sigverify ix at idx-1 of the claim ix
|
|
592
|
+
* (introspected via `instructions_sysvar`). That sigverify ix carries
|
|
593
|
+
* the attestor's Ed25519 signature over the canonical claim message
|
|
594
|
+
* and is built by the *integrator* (who calls the off-chain attestor
|
|
595
|
+
* service for the signature, per ADR-017) — the SDK has no attestor
|
|
596
|
+
* URL or client to do this for the caller.
|
|
597
|
+
*
|
|
598
|
+
* Calling this method instead of composing via
|
|
599
|
+
* {@link claimVaultArweaveIx} will hit `MissingAttestation` on-chain.
|
|
600
|
+
* It is kept only for ABI continuity; new code must use
|
|
601
|
+
* {@link claimVaultArweaveIx} and prepend the attestor sigverify ix.
|
|
564
602
|
*
|
|
565
|
-
*
|
|
566
|
-
* (still-locked) vault claims are rejected on-chain with `VaultStillLocked`
|
|
567
|
-
* (ADR-022 / BD-107: the former active re-lock path was removed because its
|
|
568
|
-
* sibling-`vaulted_transfer` introspection had no 1:1 claim↔re-lock binding
|
|
569
|
-
* → reuse / relayer skim). This method pre-flights the same gate and throws
|
|
570
|
-
* a clear `vault still locked until <ISO>` error rather than building a tx
|
|
571
|
-
* that will fail on-chain. To revive "claim early, stay locked" see the
|
|
572
|
-
* restoration playbook in the contracts repo
|
|
573
|
-
* (`docs/RESTORE_ACTIVE_VAULT_RELOCK.md`).
|
|
603
|
+
* @deprecated cannot succeed alone — see {@link claimVaultArweaveIx}.
|
|
574
604
|
*/
|
|
575
|
-
async claimVaultArweave(
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
605
|
+
async claimVaultArweave(_args) {
|
|
606
|
+
throw new Error('claimVaultArweave cannot complete the Arweave attested vault-claim ' +
|
|
607
|
+
'in a single SDK call: the on-chain handler requires an ' +
|
|
608
|
+
'Ed25519Program sigverify ix at idx-1 of the claim ix, which ' +
|
|
609
|
+
'must carry the attestor service’s signature over the canonical ' +
|
|
610
|
+
'claim message (ADR-017). The SDK does not know your attestor URL. ' +
|
|
611
|
+
'Use claimVaultArweaveIx() instead and bundle the sigverify ix ' +
|
|
612
|
+
'yourself: ' +
|
|
613
|
+
'[createAtaIx?, ed25519SigverifyIx, claimVaultArweaveIx, ...]. ' +
|
|
614
|
+
'See ar-io-solana-escrow-app/src/pages/ClaimPage.tsx for the ' +
|
|
615
|
+
'reference composition, and ADR-022 / VaultStillLocked for the ' +
|
|
616
|
+
'still-locked rejection (use isVaultClaimable() to pre-flight).');
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Build a `claim_vault_arweave_attested` instruction (without sending).
|
|
620
|
+
* Mirror of {@link claimTokensArweaveIx} for the vault-claim path:
|
|
621
|
+
* returns the bare claim ix so the caller can prepend the attestor's
|
|
622
|
+
* Ed25519Program sigverify ix and submit the bundle in one tx.
|
|
623
|
+
*
|
|
624
|
+
* The on-chain handler:
|
|
625
|
+
* - Reads the preceding Ed25519Program native sigverify ix from
|
|
626
|
+
* `instructions_sysvar` to confirm the attestor signed the canonical
|
|
627
|
+
* claim message.
|
|
628
|
+
* - Rejects with `VaultStillLocked` if `clock < vault_end_timestamp`
|
|
629
|
+
* (ADR-022). Callers should pre-flight with {@link isVaultClaimable}
|
|
630
|
+
* (non-throwing) or {@link assertVaultClaimable} (throws with the
|
|
631
|
+
* unlock timestamp) before composing the tx.
|
|
632
|
+
* - On the expired path, transfers the escrowed amount liquid to
|
|
633
|
+
* `claimantTokenAccount` and closes the escrow PDA.
|
|
634
|
+
*
|
|
635
|
+
* Composition (frontend pattern):
|
|
636
|
+
* ```ts
|
|
637
|
+
* const escrow = await tokenEscrow.requireVaultEscrow(depositor, assetId);
|
|
638
|
+
* assertVaultClaimable(escrow); // pre-flight
|
|
639
|
+
* const canonical = canonicalMessage({ ... }); // build message
|
|
640
|
+
* const attestation = await attestor.attest({ ... }); // attestor service
|
|
641
|
+
* const ed25519Ix = buildEd25519SigverifyIx(
|
|
642
|
+
* attestation.attestorPubkey, attestation.signature, canonical,
|
|
643
|
+
* );
|
|
644
|
+
* const claimIx = await tokenEscrow.claimVaultArweaveIx({
|
|
645
|
+
* depositor, assetId, claimant, claimantTokenAccount,
|
|
646
|
+
* escrowTokenAccount, messageNonce: escrow.nonce,
|
|
647
|
+
* });
|
|
648
|
+
* // Idempotent-create the claimant ATA if it's the canonical derivation.
|
|
649
|
+
* await sendTx([createAtaIx?, ed25519Ix, claimIx]);
|
|
650
|
+
* ```
|
|
651
|
+
*/
|
|
652
|
+
async claimVaultArweaveIx(args) {
|
|
653
|
+
if (args.messageNonce.length !== 32) {
|
|
654
|
+
throw new Error('messageNonce must be 32 bytes');
|
|
579
655
|
}
|
|
580
|
-
|
|
581
|
-
assertVaultClaimable(escrow);
|
|
582
|
-
const signer = this.requireSigner('claimVaultArweave');
|
|
656
|
+
const signer = this.requireSigner('claimVaultArweaveIx');
|
|
583
657
|
const [escrowPda] = await getEscrowVaultPDA(args.depositor, args.assetId, this.programId);
|
|
584
|
-
|
|
585
|
-
// builder — the on-chain `claim_vault_arweave_attested` ix verifies
|
|
586
|
-
// the attestor's Ed25519 signature via instruction-introspection
|
|
587
|
-
// of a preceding sigverify ix. See doc on `claimArweaveIx`.
|
|
588
|
-
const claimIx = getClaimVaultArweaveAttestedInstruction({
|
|
658
|
+
return getClaimVaultArweaveAttestedInstruction({
|
|
589
659
|
escrow: escrowPda,
|
|
590
660
|
escrowTokenAccount: args.escrowTokenAccount,
|
|
591
661
|
claimantTokenAccount: args.claimantTokenAccount,
|
|
592
662
|
claimant: args.claimant,
|
|
593
663
|
depositor: args.depositor,
|
|
594
664
|
payer: signer,
|
|
595
|
-
messageNonce:
|
|
665
|
+
messageNonce: args.messageNonce,
|
|
596
666
|
}, { programAddress: this.programId });
|
|
597
|
-
const createClaimantAtaIx = await this._createClaimantAtaIfCanonical(args.claimant, args.claimantTokenAccount, escrow.arioMint);
|
|
598
|
-
const ixs = createClaimantAtaIx
|
|
599
|
-
? [createClaimantAtaIx, claimIx]
|
|
600
|
-
: [claimIx];
|
|
601
|
-
return this.send(ixs, 400_000);
|
|
602
667
|
}
|
|
603
668
|
/**
|
|
604
669
|
* Submit an Ethereum ECDSA signature to release escrowed vault tokens. See
|
package/lib/esm/solana/index.js
CHANGED
|
@@ -68,7 +68,13 @@ export { SolanaANTWriteable } from './ant-writeable.js';
|
|
|
68
68
|
export { SolanaANTRegistryReadable } from './ant-registry-readable.js';
|
|
69
69
|
export { SolanaANTRegistryWriteable } from './ant-registry-writeable.js';
|
|
70
70
|
// ANT-escrow client (trustless multi-protocol custody — Arweave RSA-PSS / Ethereum ECDSA)
|
|
71
|
-
export { ANTEscrow, TokenEscrow
|
|
71
|
+
export { ANTEscrow, TokenEscrow,
|
|
72
|
+
// Vault-claim pre-flight helpers (ADR-022 / VaultStillLocked).
|
|
73
|
+
// Exported so downstream UIs can gate their Submit buttons using the
|
|
74
|
+
// SAME forward CLOCK_SKEW_TOLERANCE_SECONDS buffer the SDK's
|
|
75
|
+
// assertVaultClaimable throws use — keeping pre-flight and UI gates
|
|
76
|
+
// in lock-step so users never see a raw on-chain error.
|
|
77
|
+
assertVaultClaimable, isVaultClaimable, CLOCK_SKEW_TOLERANCE_SECONDS, } from './escrow.js';
|
|
72
78
|
// Canonical claim-message helper (byte-equivalent to Rust impl)
|
|
73
79
|
export { canonicalMessage, canonicalMessageV2, bytesToHexLower, } from './canonical-message.js';
|
|
74
80
|
// ANT spawn (mint MPL Core asset + initialize ario-ant state in one tx)
|
package/lib/esm/version.js
CHANGED
|
@@ -185,10 +185,33 @@ export interface EscrowTokenState {
|
|
|
185
185
|
vaultEndTimestamp: bigint;
|
|
186
186
|
vaultRevocable: boolean;
|
|
187
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Forward clock-skew buffer (seconds) added to `vault_end_timestamp` before
|
|
190
|
+
* the SDK considers a vault claimable. The SDK reads wall-clock time
|
|
191
|
+
* (`Date.now()`) while the on-chain gate reads Solana cluster time, and
|
|
192
|
+
* the two can disagree by several seconds. The buffer biases every skew
|
|
193
|
+
* race into the *friendly* direction: the SDK rejects when the chain
|
|
194
|
+
* would actually accept (user retries 30s later, succeeds), never the
|
|
195
|
+
* reverse (user submits a doomed tx and sees the raw on-chain error).
|
|
196
|
+
*
|
|
197
|
+
* 30s is conservative — Solana cluster clock typically drifts <2s vs
|
|
198
|
+
* wall clock — but matches the order of magnitude of the previously-used
|
|
199
|
+
* `60s` introspection tolerance in the removed `vault_introspect` module.
|
|
200
|
+
*/
|
|
201
|
+
export declare const CLOCK_SKEW_TOLERANCE_SECONDS = 30n;
|
|
202
|
+
/**
|
|
203
|
+
* Returns `true` when a vault escrow is past its unlock timestamp by at
|
|
204
|
+
* least {@link CLOCK_SKEW_TOLERANCE_SECONDS}. Non-throwing companion to
|
|
205
|
+
* {@link assertVaultClaimable} for UI gating (e.g. enabling/disabling a
|
|
206
|
+
* Submit button without showing an error).
|
|
207
|
+
*/
|
|
208
|
+
export declare function isVaultClaimable(escrow: EscrowTokenState): boolean;
|
|
188
209
|
/**
|
|
189
210
|
* Pre-flight the on-chain `VaultStillLocked` gate (ADR-022): refuse to build
|
|
190
|
-
* a claim tx while the vault is still locked
|
|
191
|
-
*
|
|
211
|
+
* a claim tx while the vault is still locked, with a small forward
|
|
212
|
+
* {@link CLOCK_SKEW_TOLERANCE_SECONDS} buffer so wall/cluster clock skew
|
|
213
|
+
* biases into the friendly direction. Surfaces the unlock timestamp so
|
|
214
|
+
* callers / UIs can show "claimable after <date>" instead of a doomed tx.
|
|
192
215
|
*
|
|
193
216
|
* Exported for unit-testability; not part of the public SDK surface — call the
|
|
194
217
|
* high-level `claimVaultArweave` / `claimVaultEthereum` instead, which invoke
|
|
@@ -313,21 +336,24 @@ export declare class TokenEscrow {
|
|
|
313
336
|
messageNonce: Uint8Array;
|
|
314
337
|
}): Promise<Instruction>;
|
|
315
338
|
/**
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
* `
|
|
339
|
+
* **Use {@link claimVaultArweaveIx} instead** — this single-send wrapper
|
|
340
|
+
* cannot work end-to-end for the Arweave attested vault-claim path,
|
|
341
|
+
* because the on-chain `claim_vault_arweave_attested` handler requires
|
|
342
|
+
* an Ed25519Program native sigverify ix at idx-1 of the claim ix
|
|
343
|
+
* (introspected via `instructions_sysvar`). That sigverify ix carries
|
|
344
|
+
* the attestor's Ed25519 signature over the canonical claim message
|
|
345
|
+
* and is built by the *integrator* (who calls the off-chain attestor
|
|
346
|
+
* service for the signature, per ADR-017) — the SDK has no attestor
|
|
347
|
+
* URL or client to do this for the caller.
|
|
319
348
|
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
* that will fail on-chain. To revive "claim early, stay locked" see the
|
|
327
|
-
* restoration playbook in the contracts repo
|
|
328
|
-
* (`docs/RESTORE_ACTIVE_VAULT_RELOCK.md`).
|
|
349
|
+
* Calling this method instead of composing via
|
|
350
|
+
* {@link claimVaultArweaveIx} will hit `MissingAttestation` on-chain.
|
|
351
|
+
* It is kept only for ABI continuity; new code must use
|
|
352
|
+
* {@link claimVaultArweaveIx} and prepend the attestor sigverify ix.
|
|
353
|
+
*
|
|
354
|
+
* @deprecated cannot succeed alone — see {@link claimVaultArweaveIx}.
|
|
329
355
|
*/
|
|
330
|
-
claimVaultArweave(
|
|
356
|
+
claimVaultArweave(_args: {
|
|
331
357
|
depositor: Address;
|
|
332
358
|
assetId: Uint8Array;
|
|
333
359
|
claimant: Address;
|
|
@@ -336,6 +362,49 @@ export declare class TokenEscrow {
|
|
|
336
362
|
signature: Uint8Array;
|
|
337
363
|
saltLen?: number;
|
|
338
364
|
}): Promise<string>;
|
|
365
|
+
/**
|
|
366
|
+
* Build a `claim_vault_arweave_attested` instruction (without sending).
|
|
367
|
+
* Mirror of {@link claimTokensArweaveIx} for the vault-claim path:
|
|
368
|
+
* returns the bare claim ix so the caller can prepend the attestor's
|
|
369
|
+
* Ed25519Program sigverify ix and submit the bundle in one tx.
|
|
370
|
+
*
|
|
371
|
+
* The on-chain handler:
|
|
372
|
+
* - Reads the preceding Ed25519Program native sigverify ix from
|
|
373
|
+
* `instructions_sysvar` to confirm the attestor signed the canonical
|
|
374
|
+
* claim message.
|
|
375
|
+
* - Rejects with `VaultStillLocked` if `clock < vault_end_timestamp`
|
|
376
|
+
* (ADR-022). Callers should pre-flight with {@link isVaultClaimable}
|
|
377
|
+
* (non-throwing) or {@link assertVaultClaimable} (throws with the
|
|
378
|
+
* unlock timestamp) before composing the tx.
|
|
379
|
+
* - On the expired path, transfers the escrowed amount liquid to
|
|
380
|
+
* `claimantTokenAccount` and closes the escrow PDA.
|
|
381
|
+
*
|
|
382
|
+
* Composition (frontend pattern):
|
|
383
|
+
* ```ts
|
|
384
|
+
* const escrow = await tokenEscrow.requireVaultEscrow(depositor, assetId);
|
|
385
|
+
* assertVaultClaimable(escrow); // pre-flight
|
|
386
|
+
* const canonical = canonicalMessage({ ... }); // build message
|
|
387
|
+
* const attestation = await attestor.attest({ ... }); // attestor service
|
|
388
|
+
* const ed25519Ix = buildEd25519SigverifyIx(
|
|
389
|
+
* attestation.attestorPubkey, attestation.signature, canonical,
|
|
390
|
+
* );
|
|
391
|
+
* const claimIx = await tokenEscrow.claimVaultArweaveIx({
|
|
392
|
+
* depositor, assetId, claimant, claimantTokenAccount,
|
|
393
|
+
* escrowTokenAccount, messageNonce: escrow.nonce,
|
|
394
|
+
* });
|
|
395
|
+
* // Idempotent-create the claimant ATA if it's the canonical derivation.
|
|
396
|
+
* await sendTx([createAtaIx?, ed25519Ix, claimIx]);
|
|
397
|
+
* ```
|
|
398
|
+
*/
|
|
399
|
+
claimVaultArweaveIx(args: {
|
|
400
|
+
depositor: Address;
|
|
401
|
+
assetId: Uint8Array;
|
|
402
|
+
claimant: Address;
|
|
403
|
+
claimantTokenAccount: Address;
|
|
404
|
+
escrowTokenAccount: Address;
|
|
405
|
+
/** 32-byte nonce from the on-chain EscrowToken PDA (`escrow.nonce`). */
|
|
406
|
+
messageNonce: Uint8Array;
|
|
407
|
+
}): Promise<Instruction>;
|
|
339
408
|
/**
|
|
340
409
|
* Submit an Ethereum ECDSA signature to release escrowed vault tokens. See
|
|
341
410
|
* {@link claimVaultArweave} — same lock semantics: vaults are only claimable
|
|
@@ -51,7 +51,7 @@ export type { SolanaANTRegistryConfig } from './ant-registry-readable.js';
|
|
|
51
51
|
export { SolanaANTRegistryWriteable } from './ant-registry-writeable.js';
|
|
52
52
|
export type { SolanaANTRegistryWriteableConfig } from './ant-registry-writeable.js';
|
|
53
53
|
export type { AclMaintenanceOp, AclMaintenanceRole, } from '../types/ant-registry.js';
|
|
54
|
-
export { ANTEscrow, TokenEscrow } from './escrow.js';
|
|
54
|
+
export { ANTEscrow, TokenEscrow, assertVaultClaimable, isVaultClaimable, CLOCK_SKEW_TOLERANCE_SECONDS, } from './escrow.js';
|
|
55
55
|
export type { ANTEscrowConfig, EscrowAntState, EscrowAssetType, EscrowProtocol, EscrowTokenState, } from './escrow.js';
|
|
56
56
|
export { canonicalMessage, canonicalMessageV2, bytesToHexLower, } from './canonical-message.js';
|
|
57
57
|
export type { CanonicalMessageInput, CanonicalMessageV2Input, EscrowNetwork, } from './canonical-message.js';
|
package/lib/types/version.d.ts
CHANGED