@ar.io/sdk 4.0.0-solana.16 → 4.0.0-solana.18
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 +54 -92
- package/lib/esm/solana/io-writeable.js +23 -18
- package/lib/esm/version.js +1 -1
- package/lib/types/solana/escrow.d.ts +44 -59
- package/lib/types/version.d.ts +1 -1
- package/package.json +2 -2
package/lib/esm/solana/escrow.js
CHANGED
|
@@ -16,11 +16,10 @@
|
|
|
16
16
|
* Borsh codec, and account-meta wiring derived from the on-chain IDL.
|
|
17
17
|
*/
|
|
18
18
|
import { fetchMaybeEscrowAnt, fetchMaybeEscrowToken, getCancelDepositInstruction, getCancelTokenDepositInstruction, getCancelVaultDepositInstruction, getClaimAntArweaveAttestedInstruction, getClaimAntEthereumInstruction, getClaimTokensArweaveAttestedInstruction, getClaimTokensEthereumInstruction, getClaimVaultArweaveAttestedInstruction, getClaimVaultEthereumInstruction, getDepositAntInstruction, getDepositTokensInstruction, getDepositVaultInstruction, getUpdateRecipientInstruction, getUpdateTokenRecipientInstruction, getUpdateVaultRecipientInstruction, } from '@ar.io/solana-contracts/ant-escrow';
|
|
19
|
-
import { fetchMaybeVaultCounter, getVaultedTransferInstructionAsync, } from '@ar.io/solana-contracts/core';
|
|
20
19
|
import { Logger } from '../common/logger.js';
|
|
21
20
|
import { getAssociatedTokenAddressKit } from './ata.js';
|
|
22
21
|
import { ARIO_ANT_ESCROW_PROGRAM_ID, ARIO_CORE_PROGRAM_ID, ESCROW_ARWEAVE_PUBKEY_LEN, ESCROW_ASSET_TYPE_VAULT, ESCROW_ETHEREUM_PUBKEY_LEN, ESCROW_PROTOCOL_ARWEAVE, ESCROW_PROTOCOL_ETHEREUM, } from './constants.js';
|
|
23
|
-
import { getEscrowAntPDA, getEscrowTokenPDA, getEscrowVaultPDA,
|
|
22
|
+
import { getEscrowAntPDA, getEscrowTokenPDA, getEscrowVaultPDA, } from './pda.js';
|
|
24
23
|
import { sendAndConfirm } from './send.js';
|
|
25
24
|
/** Map the Codama-generated `EscrowAnt` raw decoded type to our public
|
|
26
25
|
* `EscrowAntState` with protocol enum + active-prefix pubkey slice. */
|
|
@@ -275,6 +274,28 @@ export class ANTEscrow {
|
|
|
275
274
|
}
|
|
276
275
|
}
|
|
277
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* Pre-flight the on-chain `VaultStillLocked` gate (ADR-022): refuse to build
|
|
279
|
+
* a claim tx while the vault is still locked. Surfaces the unlock timestamp
|
|
280
|
+
* so callers / UIs can show "claimable after <date>" instead of a doomed tx.
|
|
281
|
+
*
|
|
282
|
+
* Exported for unit-testability; not part of the public SDK surface — call the
|
|
283
|
+
* high-level `claimVaultArweave` / `claimVaultEthereum` instead, which invoke
|
|
284
|
+
* this guard internally.
|
|
285
|
+
*
|
|
286
|
+
* @internal
|
|
287
|
+
*/
|
|
288
|
+
export function assertVaultClaimable(escrow) {
|
|
289
|
+
const nowSeconds = BigInt(Math.floor(Date.now() / 1000));
|
|
290
|
+
if (escrow.vaultEndTimestamp > nowSeconds) {
|
|
291
|
+
const unlockIso = new Date(Number(escrow.vaultEndTimestamp) * 1000).toISOString();
|
|
292
|
+
throw new Error(`Vault escrow is still locked until ${unlockIso} ` +
|
|
293
|
+
`(vault_end_timestamp=${escrow.vaultEndTimestamp}). ` +
|
|
294
|
+
`Active (still-locked) vault claims are not supported (ADR-022 / ` +
|
|
295
|
+
`VaultStillLocked) — wait until after the unlock timestamp, then ` +
|
|
296
|
+
`claim again to receive the tokens liquid.`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
278
299
|
/** Map the Codama-generated `EscrowToken` raw decoded type to our public
|
|
279
300
|
* `EscrowTokenState` with protocol enum + active-prefix pubkey slice. */
|
|
280
301
|
function toEscrowTokenState(raw) {
|
|
@@ -537,23 +558,27 @@ export class TokenEscrow {
|
|
|
537
558
|
}, { programAddress: this.programId });
|
|
538
559
|
}
|
|
539
560
|
/**
|
|
540
|
-
* Submit an Arweave
|
|
541
|
-
*
|
|
542
|
-
*
|
|
543
|
-
* introspection: an expired vault sends tokens straight to
|
|
544
|
-
* `claimantTokenAccount`; an active (still-locked) vault routes them to
|
|
545
|
-
* `payerTokenAccount` and requires a matching `ario_core::vaulted_transfer`
|
|
546
|
-
* sibling instruction in the same transaction.
|
|
561
|
+
* Submit an Arweave attestor's Ed25519 signature to release escrowed vault
|
|
562
|
+
* tokens. The on-chain handler delivers liquid tokens directly to
|
|
563
|
+
* `claimantTokenAccount`.
|
|
547
564
|
*
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
565
|
+
* **Vaults are only claimable after `vault_end_timestamp`.** Active
|
|
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`).
|
|
551
574
|
*/
|
|
552
575
|
async claimVaultArweave(args) {
|
|
553
576
|
const escrow = await this.requireVaultEscrow(args.depositor, args.assetId);
|
|
554
577
|
if (escrow.recipientProtocol !== 'arweave') {
|
|
555
578
|
throw new Error(`escrow recipient is ${escrow.recipientProtocol}, not arweave`);
|
|
556
579
|
}
|
|
580
|
+
// ADR-022 / VaultStillLocked: pre-flight the on-chain lock gate.
|
|
581
|
+
assertVaultClaimable(escrow);
|
|
557
582
|
const signer = this.requireSigner('claimVaultArweave');
|
|
558
583
|
const [escrowPda] = await getEscrowVaultPDA(args.depositor, args.assetId, this.programId);
|
|
559
584
|
// `args.signature` and `args.saltLen` are no longer fed to the
|
|
@@ -564,73 +589,56 @@ export class TokenEscrow {
|
|
|
564
589
|
escrow: escrowPda,
|
|
565
590
|
escrowTokenAccount: args.escrowTokenAccount,
|
|
566
591
|
claimantTokenAccount: args.claimantTokenAccount,
|
|
567
|
-
payerTokenAccount: args.payerTokenAccount,
|
|
568
592
|
claimant: args.claimant,
|
|
569
593
|
depositor: args.depositor,
|
|
570
594
|
payer: signer,
|
|
571
595
|
messageNonce: escrow.nonce,
|
|
572
596
|
}, { programAddress: this.programId });
|
|
573
|
-
const
|
|
597
|
+
const createClaimantAtaIx = await this._createClaimantAtaIfCanonical(args.claimant, args.claimantTokenAccount, escrow.arioMint);
|
|
598
|
+
const ixs = createClaimantAtaIx
|
|
599
|
+
? [createClaimantAtaIx, claimIx]
|
|
600
|
+
: [claimIx];
|
|
574
601
|
return this.send(ixs, 400_000);
|
|
575
602
|
}
|
|
576
603
|
/**
|
|
577
604
|
* Submit an Ethereum ECDSA signature to release escrowed vault tokens. See
|
|
578
|
-
* {@link claimVaultArweave}
|
|
579
|
-
*
|
|
605
|
+
* {@link claimVaultArweave} — same lock semantics: vaults are only claimable
|
|
606
|
+
* after `vault_end_timestamp`; active (still-locked) claims throw pre-flight
|
|
607
|
+
* and are rejected on-chain with `VaultStillLocked` (ADR-022 / BD-107).
|
|
580
608
|
*/
|
|
581
609
|
async claimVaultEthereum(args) {
|
|
582
610
|
const escrow = await this.requireVaultEscrow(args.depositor, args.assetId);
|
|
583
611
|
if (escrow.recipientProtocol !== 'ethereum') {
|
|
584
612
|
throw new Error(`escrow recipient is ${escrow.recipientProtocol}, not ethereum`);
|
|
585
613
|
}
|
|
614
|
+
assertVaultClaimable(escrow);
|
|
586
615
|
const signer = this.requireSigner('claimVaultEthereum');
|
|
587
616
|
const [escrowPda] = await getEscrowVaultPDA(args.depositor, args.assetId, this.programId);
|
|
588
617
|
const claimIx = getClaimVaultEthereumInstruction({
|
|
589
618
|
escrow: escrowPda,
|
|
590
619
|
escrowTokenAccount: args.escrowTokenAccount,
|
|
591
620
|
claimantTokenAccount: args.claimantTokenAccount,
|
|
592
|
-
payerTokenAccount: args.payerTokenAccount,
|
|
593
621
|
claimant: args.claimant,
|
|
594
622
|
depositor: args.depositor,
|
|
595
623
|
payer: signer,
|
|
596
624
|
messageNonce: escrow.nonce,
|
|
597
625
|
signature: args.signature,
|
|
598
626
|
}, { programAddress: this.programId });
|
|
599
|
-
const
|
|
627
|
+
const createClaimantAtaIx = await this._createClaimantAtaIfCanonical(args.claimant, args.claimantTokenAccount, escrow.arioMint);
|
|
628
|
+
const ixs = createClaimantAtaIx
|
|
629
|
+
? [createClaimantAtaIx, claimIx]
|
|
630
|
+
: [claimIx];
|
|
600
631
|
return this.send(ixs);
|
|
601
632
|
}
|
|
602
633
|
/**
|
|
603
|
-
* If the vault escrow is still locked (`vaultEndTimestamp` in the future),
|
|
604
|
-
* return `[claimIx, vaultedTransferIx]` so the on-chain claim handler can
|
|
605
|
-
* see the sibling (via `instructions_sysvar`) and re-vault tokens for the
|
|
606
|
-
* claimant. If the vault has expired, just `[claimIx]` — the on-chain
|
|
607
|
-
* handler delivers tokens directly to `claimantTokenAccount`.
|
|
608
|
-
*
|
|
609
|
-
* **Ordering matters at runtime**: `claim` must execute first so it can
|
|
610
|
-
* move tokens `escrow → payerTokenAccount`. `vaulted_transfer` then pulls
|
|
611
|
-
* from `payerTokenAccount` into the new vault. Reversing the order makes
|
|
612
|
-
* `vaulted_transfer` fail with `insufficient funds` because nothing has
|
|
613
|
-
* funded `payerTokenAccount` yet. The introspection check in
|
|
614
|
-
* `vault_introspect::verify_vaulted_transfer_in_tx` is presence-based
|
|
615
|
-
* (reads `instructions_sysvar`) so the *sibling* ordering doesn't matter
|
|
616
|
-
* for the check itself — only for atomic execution.
|
|
617
|
-
*
|
|
618
|
-
* `lockDurationSeconds` is set to the SDK-local `remaining` value. The
|
|
619
|
-
* on-chain handler accepts `lock_duration >= remaining_at_execution - 60s`
|
|
620
|
-
* (see the introspection function's tolerance), so any modest clock skew
|
|
621
|
-
* between client and chain is absorbed.
|
|
622
|
-
*/
|
|
623
634
|
/**
|
|
624
635
|
* Idempotent-create the claimant's canonical ATA when needed.
|
|
625
636
|
*
|
|
626
|
-
* The
|
|
627
|
-
* `claimantTokenAccount`
|
|
628
|
-
*
|
|
629
|
-
*
|
|
630
|
-
*
|
|
631
|
-
* even though the active path doesn't write to it. Either way: if the
|
|
632
|
-
* claimant is a fresh wallet that has never held this mint, the ATA
|
|
633
|
-
* doesn't exist and the tx fails with `AccountNotInitialized` (#3012).
|
|
637
|
+
* The claim handler delivers liquid tokens directly to
|
|
638
|
+
* `claimantTokenAccount` (post-ADR-022 there's only the liquid path for
|
|
639
|
+
* vaults). If the claimant is a fresh wallet that has never held this
|
|
640
|
+
* mint, the ATA doesn't exist and the tx fails with `AccountNotInitialized`
|
|
641
|
+
* (#3012).
|
|
634
642
|
*
|
|
635
643
|
* Returns `null` when the caller passed a non-canonical
|
|
636
644
|
* `claimantTokenAccount` (manually-created non-ATA token account,
|
|
@@ -643,52 +651,6 @@ export class TokenEscrow {
|
|
|
643
651
|
const signer = this.requireSigner('createClaimantAtaIfCanonical');
|
|
644
652
|
return buildCreateAtaIdempotentIx(signer.address, canonical, claimant, mint);
|
|
645
653
|
}
|
|
646
|
-
async maybeBundleVaultedTransfer(escrow, args, claimIx) {
|
|
647
|
-
const nowSeconds = BigInt(Math.floor(Date.now() / 1000));
|
|
648
|
-
const remaining = escrow.vaultEndTimestamp - nowSeconds;
|
|
649
|
-
if (remaining <= 0n) {
|
|
650
|
-
// Expired vault → claim handler delivers liquid to claimantTokenAccount.
|
|
651
|
-
// Idempotent-create that ATA if it's the canonical derivation so a
|
|
652
|
-
// first-time recipient just works.
|
|
653
|
-
const createClaimantAtaIx = await this._createClaimantAtaIfCanonical(args.claimant, args.claimantTokenAccount, escrow.arioMint);
|
|
654
|
-
return createClaimantAtaIx ? [createClaimantAtaIx, claimIx] : [claimIx];
|
|
655
|
-
}
|
|
656
|
-
const signer = this.requireSigner('maybeBundleVaultedTransfer');
|
|
657
|
-
const nextId = await this.getNextVaultId(args.claimant);
|
|
658
|
-
const [vaultPda] = await getVaultPDA(args.claimant, nextId, this.coreProgram);
|
|
659
|
-
const vaultATA = await getAssociatedTokenAddressKit(escrow.arioMint, vaultPda, true);
|
|
660
|
-
// Active-vault path: `claim_vault_*` still validates the claimant ATA
|
|
661
|
-
// at account-load-time (Anchor `Account<TokenAccount>` constraint),
|
|
662
|
-
// even though no liquid is written to it. Idempotent-create so a fresh
|
|
663
|
-
// claimant doesn't fail the ix with AccountNotInitialized (#3012).
|
|
664
|
-
const createClaimantAtaIx = await this._createClaimantAtaIfCanonical(args.claimant, args.claimantTokenAccount, escrow.arioMint);
|
|
665
|
-
// The new vault PDA's ATA must exist before `vaulted_transfer` reads it
|
|
666
|
-
// (else `AccountNotInitialized` #3012). Idempotent so a retry after a
|
|
667
|
-
// partial-failure tx is safe. Placed after the claim ix to preserve
|
|
668
|
-
// the "claim first" tx ordering invariant.
|
|
669
|
-
const createVaultAtaIx = buildCreateAtaIdempotentIx(signer.address, vaultATA, vaultPda, escrow.arioMint);
|
|
670
|
-
const vaultedIx = await getVaultedTransferInstructionAsync({
|
|
671
|
-
vault: vaultPda,
|
|
672
|
-
senderTokenAccount: args.payerTokenAccount,
|
|
673
|
-
vaultTokenAccount: vaultATA,
|
|
674
|
-
recipient: args.claimant,
|
|
675
|
-
sender: signer,
|
|
676
|
-
amount: escrow.amount,
|
|
677
|
-
lockDurationSeconds: remaining,
|
|
678
|
-
revocable: escrow.vaultRevocable,
|
|
679
|
-
}, { programAddress: this.coreProgram });
|
|
680
|
-
const head = createClaimantAtaIx ? [createClaimantAtaIx] : [];
|
|
681
|
-
return [...head, claimIx, createVaultAtaIx, vaultedIx];
|
|
682
|
-
}
|
|
683
|
-
/** Read the recipient's `VaultCounter.nextId`, defaulting to 0n if the
|
|
684
|
-
* counter PDA hasn't been initialised yet (first vault for that owner). */
|
|
685
|
-
async getNextVaultId(owner) {
|
|
686
|
-
const [counterPda] = await getVaultCounterPDA(owner, this.coreProgram);
|
|
687
|
-
const account = await fetchMaybeVaultCounter(this.rpc, counterPda, {
|
|
688
|
-
commitment: this.commitment,
|
|
689
|
-
});
|
|
690
|
-
return account.exists ? account.data.nextId : 0n;
|
|
691
|
-
}
|
|
692
654
|
// -------------------------------------------------------------------
|
|
693
655
|
// Write — cancel
|
|
694
656
|
// -------------------------------------------------------------------
|
|
@@ -1786,39 +1786,44 @@ export class SolanaARIOWriteable extends SolanaARIOReadable {
|
|
|
1786
1786
|
async reassignName(params, _options) {
|
|
1787
1787
|
const newAnt = address(params.processId);
|
|
1788
1788
|
const [arnsRecord] = await getArnsRecordPDA(params.name, this.arnsProgram);
|
|
1789
|
+
// The on-chain `reassign_name` (PR #73 / BD-106 / BD-095) now authorizes
|
|
1790
|
+
// against the CURRENT Metaplex Core holder of `record.ant` via a named
|
|
1791
|
+
// `ant_asset` account constrained to `arns_record.ant`. We must read the
|
|
1792
|
+
// current record to know which asset to pass — that's the OLD ant (the
|
|
1793
|
+
// one we're reassigning AWAY FROM), not `newAnt`.
|
|
1794
|
+
const currentRecord = await this.getArNSRecord({ name: params.name });
|
|
1795
|
+
const currentAnt = address(currentRecord.processId);
|
|
1789
1796
|
const ix = await getReassignNameInstructionAsync(await this.withArnsDefaults({
|
|
1790
1797
|
arnsRecord,
|
|
1798
|
+
antAsset: currentAnt,
|
|
1791
1799
|
caller: this.signer,
|
|
1792
1800
|
newAnt,
|
|
1793
1801
|
}), { programAddress: this.arnsProgram });
|
|
1794
|
-
//
|
|
1795
|
-
//
|
|
1796
|
-
//
|
|
1797
|
-
// the
|
|
1798
|
-
//
|
|
1799
|
-
//
|
|
1800
|
-
//
|
|
1801
|
-
//
|
|
1802
|
-
//
|
|
1803
|
-
// OLD asset, and fail the post-reassign `record.ant == asset.key()`
|
|
1804
|
-
// check. The owner-check inside _buildSyncAttributesIxIfOwner runs
|
|
1805
|
-
// against `newAnt`, so the bundle fires only when the reassign
|
|
1806
|
-
// caller is also the new ANT's holder; otherwise the ix is sent
|
|
1807
|
-
// alone and the new owner runs `syncAttributes()` later (BD-095/096).
|
|
1802
|
+
// Post-reassign the record points at `newAnt`. The bundled
|
|
1803
|
+
// `sync_attributes` MUST target `newAnt` — without the override, the
|
|
1804
|
+
// helper would read the on-chain record at SDK build time (still
|
|
1805
|
+
// pointing at the OLD asset), build a sync ix for the OLD asset, and
|
|
1806
|
+
// fail the post-reassign `record.ant == asset.key()` check. The
|
|
1807
|
+
// owner-check inside _buildSyncAttributesIxIfOwner runs against
|
|
1808
|
+
// `newAnt`, so the bundle fires only when the reassign caller is also
|
|
1809
|
+
// the new ANT's holder; otherwise the ix is sent alone and the new
|
|
1810
|
+
// owner runs `syncAttributes()` later (BD-095/096).
|
|
1808
1811
|
const syncIx = await this._buildSyncAttributesIxIfOwner(params.name, newAnt);
|
|
1809
|
-
const
|
|
1810
|
-
{ address: newAnt, role: AccountRole.READONLY },
|
|
1811
|
-
]);
|
|
1812
|
-
const sig = await this.sendTransaction(syncIx ? [reassignWithMetas, syncIx] : [reassignWithMetas]);
|
|
1812
|
+
const sig = await this.sendTransaction(syncIx ? [ix, syncIx] : [ix]);
|
|
1813
1813
|
return { id: sig };
|
|
1814
1814
|
}
|
|
1815
1815
|
/** Release a permabuy name back to the registry (creates a returned name auction). */
|
|
1816
1816
|
async releaseName(params, _options) {
|
|
1817
1817
|
const [returnedNamePda] = await getReturnedNamePDA(params.name, this.arnsProgram);
|
|
1818
1818
|
const [arnsRecord] = await getArnsRecordPDA(params.name, this.arnsProgram);
|
|
1819
|
+
// PR #73 / BD-106: `release_name` now authorizes against the current
|
|
1820
|
+
// Metaplex Core holder of `record.ant` via a named `ant_asset` account
|
|
1821
|
+
// constrained to `arns_record.ant`. Fetch the record to know which.
|
|
1822
|
+
const currentRecord = await this.getArNSRecord({ name: params.name });
|
|
1819
1823
|
const ix = await getReleaseNameInstructionAsync(await this.withArnsDefaults({
|
|
1820
1824
|
arnsRecord,
|
|
1821
1825
|
returnedName: returnedNamePda,
|
|
1826
|
+
antAsset: address(currentRecord.processId),
|
|
1822
1827
|
caller: this.signer,
|
|
1823
1828
|
}), { programAddress: this.arnsProgram });
|
|
1824
1829
|
// Note: no sync_attributes bundle here — release_name closes the
|
package/lib/esm/version.js
CHANGED
|
@@ -16,15 +16,13 @@
|
|
|
16
16
|
* Borsh codec, and account-meta wiring derived from the on-chain IDL.
|
|
17
17
|
*/
|
|
18
18
|
import { type Address, type Commitment, type Instruction, type TransactionSigner } from '@solana/kit';
|
|
19
|
+
import { type EscrowAnt, type EscrowToken } from '@ar.io/solana-contracts/ant-escrow';
|
|
19
20
|
import type { ILogger } from '../common/logger.js';
|
|
20
21
|
import type { SolanaRpc, SolanaRpcSubscriptions } from './types.js';
|
|
21
22
|
export type EscrowProtocol = 'arweave' | 'ethereum';
|
|
22
23
|
export interface EscrowAntState {
|
|
23
|
-
version
|
|
24
|
-
|
|
25
|
-
minor: number;
|
|
26
|
-
patch: number;
|
|
27
|
-
};
|
|
24
|
+
/** On-chain schema version, as decoded by the generated client. */
|
|
25
|
+
version: EscrowAnt['version'];
|
|
28
26
|
bump: number;
|
|
29
27
|
depositor: Address;
|
|
30
28
|
antMint: Address;
|
|
@@ -39,9 +37,13 @@ export interface ANTEscrowConfig {
|
|
|
39
37
|
signer?: TransactionSigner;
|
|
40
38
|
programId?: Address;
|
|
41
39
|
/**
|
|
42
|
-
* ario-core program id
|
|
43
|
-
* `vaulted_transfer
|
|
44
|
-
*
|
|
40
|
+
* ario-core program id. Currently unused (post-ADR-022 the SDK no longer
|
|
41
|
+
* builds a sibling `vaulted_transfer`; active vault claims are rejected
|
|
42
|
+
* with `VaultStillLocked`). Retained for forward-compat — if the active
|
|
43
|
+
* re-lock path is ever revived via the direct-CPI restoration playbook
|
|
44
|
+
* (contracts `docs/RESTORE_ACTIVE_VAULT_RELOCK.md`), this is the program
|
|
45
|
+
* the new claim ABI would need to reference. Defaults to
|
|
46
|
+
* {@link ARIO_CORE_PROGRAM_ID}.
|
|
45
47
|
*/
|
|
46
48
|
coreProgram?: Address;
|
|
47
49
|
commitment?: Commitment;
|
|
@@ -168,11 +170,8 @@ export declare class ANTEscrow {
|
|
|
168
170
|
}
|
|
169
171
|
export type EscrowAssetType = 'token' | 'vault';
|
|
170
172
|
export interface EscrowTokenState {
|
|
171
|
-
version
|
|
172
|
-
|
|
173
|
-
minor: number;
|
|
174
|
-
patch: number;
|
|
175
|
-
};
|
|
173
|
+
/** On-chain schema version, as decoded by the generated client. */
|
|
174
|
+
version: EscrowToken['version'];
|
|
176
175
|
bump: number;
|
|
177
176
|
depositor: Address;
|
|
178
177
|
assetType: EscrowAssetType;
|
|
@@ -186,6 +185,18 @@ export interface EscrowTokenState {
|
|
|
186
185
|
vaultEndTimestamp: bigint;
|
|
187
186
|
vaultRevocable: boolean;
|
|
188
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Pre-flight the on-chain `VaultStillLocked` gate (ADR-022): refuse to build
|
|
190
|
+
* a claim tx while the vault is still locked. Surfaces the unlock timestamp
|
|
191
|
+
* so callers / UIs can show "claimable after <date>" instead of a doomed tx.
|
|
192
|
+
*
|
|
193
|
+
* Exported for unit-testability; not part of the public SDK surface — call the
|
|
194
|
+
* high-level `claimVaultArweave` / `claimVaultEthereum` instead, which invoke
|
|
195
|
+
* this guard internally.
|
|
196
|
+
*
|
|
197
|
+
* @internal
|
|
198
|
+
*/
|
|
199
|
+
export declare function assertVaultClaimable(escrow: EscrowTokenState): void;
|
|
189
200
|
/**
|
|
190
201
|
* Solana-backed client for the trustless token/vault escrow program. All
|
|
191
202
|
* write methods require both `rpcSubscriptions` and `signer`; read methods
|
|
@@ -302,17 +313,19 @@ export declare class TokenEscrow {
|
|
|
302
313
|
messageNonce: Uint8Array;
|
|
303
314
|
}): Promise<Instruction>;
|
|
304
315
|
/**
|
|
305
|
-
* Submit an Arweave
|
|
316
|
+
* Submit an Arweave attestor's Ed25519 signature to release escrowed vault
|
|
317
|
+
* tokens. The on-chain handler delivers liquid tokens directly to
|
|
318
|
+
* `claimantTokenAccount`.
|
|
306
319
|
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
* `
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
320
|
+
* **Vaults are only claimable after `vault_end_timestamp`.** Active
|
|
321
|
+
* (still-locked) vault claims are rejected on-chain with `VaultStillLocked`
|
|
322
|
+
* (ADR-022 / BD-107: the former active re-lock path was removed because its
|
|
323
|
+
* sibling-`vaulted_transfer` introspection had no 1:1 claim↔re-lock binding
|
|
324
|
+
* → reuse / relayer skim). This method pre-flights the same gate and throws
|
|
325
|
+
* a clear `vault still locked until <ISO>` error rather than building a tx
|
|
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`).
|
|
316
329
|
*/
|
|
317
330
|
claimVaultArweave(args: {
|
|
318
331
|
depositor: Address;
|
|
@@ -320,14 +333,14 @@ export declare class TokenEscrow {
|
|
|
320
333
|
claimant: Address;
|
|
321
334
|
claimantTokenAccount: Address;
|
|
322
335
|
escrowTokenAccount: Address;
|
|
323
|
-
payerTokenAccount: Address;
|
|
324
336
|
signature: Uint8Array;
|
|
325
337
|
saltLen?: number;
|
|
326
338
|
}): Promise<string>;
|
|
327
339
|
/**
|
|
328
340
|
* Submit an Ethereum ECDSA signature to release escrowed vault tokens. See
|
|
329
|
-
* {@link claimVaultArweave}
|
|
330
|
-
*
|
|
341
|
+
* {@link claimVaultArweave} — same lock semantics: vaults are only claimable
|
|
342
|
+
* after `vault_end_timestamp`; active (still-locked) claims throw pre-flight
|
|
343
|
+
* and are rejected on-chain with `VaultStillLocked` (ADR-022 / BD-107).
|
|
331
344
|
*/
|
|
332
345
|
claimVaultEthereum(args: {
|
|
333
346
|
depositor: Address;
|
|
@@ -335,51 +348,23 @@ export declare class TokenEscrow {
|
|
|
335
348
|
claimant: Address;
|
|
336
349
|
claimantTokenAccount: Address;
|
|
337
350
|
escrowTokenAccount: Address;
|
|
338
|
-
payerTokenAccount: Address;
|
|
339
351
|
signature: Uint8Array;
|
|
340
352
|
}): Promise<string>;
|
|
341
353
|
/**
|
|
342
|
-
* If the vault escrow is still locked (`vaultEndTimestamp` in the future),
|
|
343
|
-
* return `[claimIx, vaultedTransferIx]` so the on-chain claim handler can
|
|
344
|
-
* see the sibling (via `instructions_sysvar`) and re-vault tokens for the
|
|
345
|
-
* claimant. If the vault has expired, just `[claimIx]` — the on-chain
|
|
346
|
-
* handler delivers tokens directly to `claimantTokenAccount`.
|
|
347
|
-
*
|
|
348
|
-
* **Ordering matters at runtime**: `claim` must execute first so it can
|
|
349
|
-
* move tokens `escrow → payerTokenAccount`. `vaulted_transfer` then pulls
|
|
350
|
-
* from `payerTokenAccount` into the new vault. Reversing the order makes
|
|
351
|
-
* `vaulted_transfer` fail with `insufficient funds` because nothing has
|
|
352
|
-
* funded `payerTokenAccount` yet. The introspection check in
|
|
353
|
-
* `vault_introspect::verify_vaulted_transfer_in_tx` is presence-based
|
|
354
|
-
* (reads `instructions_sysvar`) so the *sibling* ordering doesn't matter
|
|
355
|
-
* for the check itself — only for atomic execution.
|
|
356
|
-
*
|
|
357
|
-
* `lockDurationSeconds` is set to the SDK-local `remaining` value. The
|
|
358
|
-
* on-chain handler accepts `lock_duration >= remaining_at_execution - 60s`
|
|
359
|
-
* (see the introspection function's tolerance), so any modest clock skew
|
|
360
|
-
* between client and chain is absorbed.
|
|
361
|
-
*/
|
|
362
354
|
/**
|
|
363
355
|
* Idempotent-create the claimant's canonical ATA when needed.
|
|
364
356
|
*
|
|
365
|
-
* The
|
|
366
|
-
* `claimantTokenAccount`
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
* even though the active path doesn't write to it. Either way: if the
|
|
371
|
-
* claimant is a fresh wallet that has never held this mint, the ATA
|
|
372
|
-
* doesn't exist and the tx fails with `AccountNotInitialized` (#3012).
|
|
357
|
+
* The claim handler delivers liquid tokens directly to
|
|
358
|
+
* `claimantTokenAccount` (post-ADR-022 there's only the liquid path for
|
|
359
|
+
* vaults). If the claimant is a fresh wallet that has never held this
|
|
360
|
+
* mint, the ATA doesn't exist and the tx fails with `AccountNotInitialized`
|
|
361
|
+
* (#3012).
|
|
373
362
|
*
|
|
374
363
|
* Returns `null` when the caller passed a non-canonical
|
|
375
364
|
* `claimantTokenAccount` (manually-created non-ATA token account,
|
|
376
365
|
* presumably already exists — caller's responsibility).
|
|
377
366
|
*/
|
|
378
367
|
private _createClaimantAtaIfCanonical;
|
|
379
|
-
private maybeBundleVaultedTransfer;
|
|
380
|
-
/** Read the recipient's `VaultCounter.nextId`, defaulting to 0n if the
|
|
381
|
-
* counter PDA hasn't been initialised yet (first vault for that owner). */
|
|
382
|
-
private getNextVaultId;
|
|
383
368
|
/**
|
|
384
369
|
* Cancel a token or vault escrow deposit and return the tokens to the
|
|
385
370
|
* depositor. Only callable by the original depositor.
|
package/lib/types/version.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ar.io/sdk",
|
|
3
|
-
"version": "4.0.0-solana.
|
|
3
|
+
"version": "4.0.0-solana.18",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/ar-io/ar-io-sdk.git"
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
"typescript": "^5.1.6"
|
|
123
123
|
},
|
|
124
124
|
"dependencies": {
|
|
125
|
-
"@ar.io/solana-contracts": "^0.
|
|
125
|
+
"@ar.io/solana-contracts": "^0.4.0",
|
|
126
126
|
"@solana-program/compute-budget": "^0.15.0",
|
|
127
127
|
"@solana-program/token": "^0.13.0",
|
|
128
128
|
"@solana/kit": "^6.8.0",
|