@ar.io/sdk 4.0.0-solana.24 → 4.0.0-solana.26

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.
@@ -108,6 +108,12 @@ class BorshReader {
108
108
  return undefined;
109
109
  return this.readU32();
110
110
  }
111
+ readOptionU16() {
112
+ const tag = this.readU8();
113
+ if (tag === 0)
114
+ return undefined;
115
+ return this.readU16();
116
+ }
111
117
  skip(bytes) {
112
118
  this.offset += bytes;
113
119
  }
@@ -332,6 +338,15 @@ export function deserializeGatewayWithAccumulator(data) {
332
338
  const delegateRewardShareRatio = r.readU16() / 100;
333
339
  const minDelegatedStake = r.readU64AsNumber();
334
340
  const allowlistEnabled = r.readBool();
341
+ // GATEWAY_VERSION 1.1.0 added two fields to GatewaySettings2 — MUST read them
342
+ // here to keep the byte stream aligned for every field after `settings`.
343
+ // - pending_delegate_reward_share_ratio: Option<u16> (Fix #7) — basis points
344
+ // of a deferred reward-share change applied at the next epoch's tally.
345
+ // - delegation_disabled_at: Option<i64> (Fix #6) — unix seconds the operator
346
+ // disabled delegation; starts the re-enable cooldown.
347
+ const pendingRatioRaw = r.readOptionU16();
348
+ const pendingDelegateRewardShareRatio = pendingRatioRaw === undefined ? undefined : pendingRatioRaw / 100;
349
+ const delegationDisabledAt = r.readOptionI64();
335
350
  // RegistryIndex (index: u32, _reserved: u8 — was is_registered:bool)
336
351
  r.readU32(); // registryIndex
337
352
  r.readU8(); // _reserved (layout-preserving placeholder for the legacy is_registered byte)
@@ -378,6 +393,8 @@ export function deserializeGatewayWithAccumulator(data) {
378
393
  fqdn,
379
394
  port,
380
395
  protocol: 'https', // protocolIdx: 0=Http, 1=Https — only HTTPS in practice
396
+ pendingDelegateRewardShareRatio, // Fix #7: undefined when no change is queued
397
+ delegationDisabledAt, // Fix #6: undefined when delegation is enabled
381
398
  };
382
399
  return {
383
400
  operator: operator,
@@ -1743,6 +1743,39 @@ export class SolanaARIOReadable {
1743
1743
  }
1744
1744
  return out;
1745
1745
  }
1746
+ /**
1747
+ * Enumerate Joined Gateway PDAs whose delegation has been DISABLED
1748
+ * (`allow_delegated_staking == false`) yet still hold delegated stake
1749
+ * (`total_delegated_stake > 0`) — i.e. delegates that an operator's disable
1750
+ * left stranded (WP §6.3 / Fix #6). Each such gateway's delegates must be
1751
+ * cranked out via
1752
+ * {@link SolanaARIOWriteable.claimDelegateFromDisabledGateway} (enumerate
1753
+ * them with {@link getGatewayDelegates}) before the operator can re-enable
1754
+ * delegation. This is the discovery primitive a cranker uses to sweep them.
1755
+ */
1756
+ async getDisabledGatewaysWithDelegatedStake() {
1757
+ const accounts = await this.getAccountsByDiscriminator(this.garProgram, GATEWAY_DISCRIMINATOR);
1758
+ const decoder = getGatewayDecoder();
1759
+ const out = [];
1760
+ for (const { pubkey, data } of accounts) {
1761
+ try {
1762
+ const g = decoder.decode(data);
1763
+ if (g.status !== GatewayStatus.Joined)
1764
+ continue;
1765
+ if (!g.settings.allowDelegatedStaking && g.totalDelegatedStake > 0n) {
1766
+ out.push({
1767
+ pubkey,
1768
+ operator: g.operator,
1769
+ totalDelegatedStake: g.totalDelegatedStake,
1770
+ });
1771
+ }
1772
+ }
1773
+ catch {
1774
+ // skip malformed
1775
+ }
1776
+ }
1777
+ return out;
1778
+ }
1746
1779
  /**
1747
1780
  * Enumerate Delegation PDAs with `amount == 0`. Eligible for
1748
1781
  * `closeEmptyDelegation` (rent refund to the original delegator).
@@ -38,11 +38,11 @@ function toGeneratedFundingSourceSpec(s) {
38
38
  import { getSyncAttributesInstruction } from '@ar.io/solana-contracts/ant';
39
39
  import { getApprovePrimaryNameInstructionAsync, getCloseExpiredRequestInstruction, getCreateVaultInstructionAsync, getExtendVaultInstructionAsync, getIncreaseVaultInstructionAsync, getReleaseVaultInstructionAsync, getRemovePrimaryNameInstructionAsync, getRequestAndSetPrimaryNameFromFundingPlanInstructionAsync, getRequestAndSetPrimaryNameInstructionAsync, getRequestPrimaryNameFromFundingPlanInstructionAsync, getRequestPrimaryNameInstructionAsync, getRevokeVaultInstructionAsync, getVaultedTransferInstructionAsync, } from '@ar.io/solana-contracts/core';
40
40
  import { getDelegationDecoder, getGatewayDecoder, } from '@ar.io/solana-contracts/gar';
41
- import { Protocol, getAllowDelegateInstructionAsync, getCancelWithdrawalInstruction, getClaimDelegateFromLeavingGatewayInstructionAsync, getClaimWithdrawalInstructionAsync, getCloseDrainedWithdrawalInstruction, getCloseEmptyDelegationInstruction, getCloseEpochInstructionAsync, getCloseObservationInstructionAsync, getCreateEpochInstructionAsync, getDecreaseDelegateStakeInstructionAsync, getDecreaseOperatorStakeInstructionAsync, getDelegateStakeInstructionAsync, getDisallowDelegateInstructionAsync, getDistributeEpochInstructionAsync, getFinalizeGoneInstructionAsync, getIncreaseOperatorStakeInstructionAsync, getInstantWithdrawalInstructionAsync, getJoinNetworkInstructionAsync, getLeaveNetworkInstructionAsync, getPrescribeEpochInstructionAsync, getPruneGatewayInstructionAsync, getRedelegateStakeInstructionAsync, getSaveObservationsInstructionAsync, getSetAllowlistEnabledInstructionAsync, getTallyWeightsInstructionAsync, getUpdateGatewaySettingsInstructionAsync, } from '@ar.io/solana-contracts/gar';
41
+ import { Protocol, getAllowDelegateInstructionAsync, getCancelWithdrawalInstruction, getClaimDelegateFromDisabledGatewayInstructionAsync, getClaimDelegateFromLeavingGatewayInstructionAsync, getClaimWithdrawalInstructionAsync, getCloseDrainedWithdrawalInstruction, getCloseEmptyDelegationInstruction, getCloseEpochInstructionAsync, getCloseObservationInstructionAsync, getCreateEpochInstructionAsync, getDecreaseDelegateStakeInstructionAsync, getDecreaseOperatorStakeInstructionAsync, getDelegateStakeInstructionAsync, getDisallowDelegateInstructionAsync, getDistributeEpochInstructionAsync, getFinalizeGoneInstructionAsync, getIncreaseOperatorStakeInstructionAsync, getInstantWithdrawalInstructionAsync, getJoinNetworkInstructionAsync, getLeaveNetworkInstructionAsync, getPrescribeEpochInstructionAsync, getPruneGatewayInstructionAsync, getRedelegateStakeInstructionAsync, getSaveObservationsInstructionAsync, getSetAllowlistEnabledInstructionAsync, getTallyWeightsInstructionAsync, getUpdateGatewaySettingsInstructionAsync, } from '@ar.io/solana-contracts/gar';
42
42
  import { getTransferCheckedInstruction } from '@solana-program/token';
43
43
  import { ARIO_ANT_PROGRAM_ID, TOKEN_DECIMALS } from './constants.js';
44
44
  import { SolanaARIOReadable } from './io-readable.js';
45
- import { getAntRecordPDA, getArioConfigPDA, getArnsRecordPDA, getArnsRegistryPDA, getArnsSettingsPDA, getDelegationPDA, getDemandFactorPDA, getEpochPDA, getEpochSettingsPDA, getGarSettingsPDA, getGatewayPDA, getGatewayRegistryPDA, getObservationPDA, getObserverLookupPDA, getPrimaryNamePDA, getPrimaryNameRequestPDA, getPrimaryNameReversePDA, getReservedNamePDA, getReturnedNamePDA, getVaultPDA, getWithdrawalCounterPDA, getWithdrawalPDA, hashName, } from './pda.js';
45
+ import { getAntConfigPDA, getAntRecordPDA, getArioConfigPDA, getArnsRecordPDA, getArnsRegistryPDA, getArnsSettingsPDA, getDelegationPDA, getDemandFactorPDA, getEpochPDA, getEpochSettingsPDA, getGarSettingsPDA, getGatewayPDA, getGatewayRegistryPDA, getObservationPDA, getObserverLookupPDA, getPrimaryNamePDA, getPrimaryNameRequestPDA, getPrimaryNameReversePDA, getReservedNamePDA, getReturnedNamePDA, getVaultPDA, getWithdrawalCounterPDA, getWithdrawalPDA, hashName, } from './pda.js';
46
46
  import { predictPrescribedObservers, } from './predict-prescribed-observers.js';
47
47
  import { reclaimLookupTablesForSigner, sendAndConfirm, sendWithEphemeralLookupTable, } from './send.js';
48
48
  const addressDecoder = getAddressDecoder();
@@ -1427,14 +1427,16 @@ export class SolanaARIOWriteable extends SolanaARIOReadable {
1427
1427
  * Build the `remaining_accounts` slice + the `antProgramId` arg the
1428
1428
  * four ario-core primary-name instructions consume. Sprint 2/5
1429
1429
  * reshape (ADR-016): ario-core no longer reads MPL Core asset bytes.
1430
- * Authorization is "caller is the AntRecord.owner for this name" via
1431
- * PDA-seed-pinned lookup.
1430
+ * Authorization is "caller is the effective AntRecord owner for this
1431
+ * name", resolved from the `AntRecord` + `AntConfig` PDAs (freshness-
1432
+ * gated against `AntConfig.last_known_owner` — see ario-core BD-097 /
1433
+ * BD-109). Both are program-PDA-pinned lookups.
1432
1434
  *
1433
1435
  * Layouts the on-chain handlers expect:
1434
1436
  * request_primary_name: [arnsRecord, demandFactor]
1435
- * request_and_set_primary_name: [arnsRecord, demandFactor, antRecord]
1436
- * approve_primary_name: [arnsRecord, antRecord]
1437
- * remove_primary_name_for_base_name: [arnsRecord, antRecord(@)]
1437
+ * request_and_set_primary_name: [arnsRecord, demandFactor, antRecord, antConfig]
1438
+ * approve_primary_name: [arnsRecord, antRecord, antConfig]
1439
+ * remove_primary_name_for_base_name: [arnsRecord, antRecord(@), antConfig]
1438
1440
  *
1439
1441
  * `antRecord` keys off the undername part for undernames (e.g.
1440
1442
  * "blog_arweave" → AntRecord at "blog") or the canonical "@" sentinel
@@ -1506,6 +1508,15 @@ export class SolanaARIOWriteable extends SolanaARIOReadable {
1506
1508
  : undername;
1507
1509
  const [antRecordPda] = await getAntRecordPDA(antMint, antUndername, antProgram);
1508
1510
  remaining.push({ address: antRecordPda, role: AccountRole.READONLY });
1511
+ // AntConfig PDA (ANT-level owner snapshot). ario-core reads
1512
+ // `AntConfig.last_known_owner` as the implicit-owner source and to
1513
+ // freshness-gate the per-record `AntRecord.owner` delegate (BD-097 /
1514
+ // BD-109). Derived under the SAME resolved `antProgram` as the
1515
+ // AntRecord above. Required trailing account for all three ANT-auth
1516
+ // variants (and the funding-plan variant, whose validation_account_count
1517
+ // is derived from this array's length downstream).
1518
+ const [antConfigPda] = await getAntConfigPDA(antMint, antProgram);
1519
+ remaining.push({ address: antConfigPda, role: AccountRole.READONLY });
1509
1520
  }
1510
1521
  return { remaining, antProgram };
1511
1522
  }
@@ -1662,7 +1673,7 @@ export class SolanaARIOWriteable extends SolanaARIOReadable {
1662
1673
  *
1663
1674
  * Mirrors the on-chain `approve_primary_name` instruction
1664
1675
  * (`programs/ario-core/src/instructions/primary_name.rs`).
1665
- * remaining_accounts: [arns_record(base), ant_record(undername | @)].
1676
+ * remaining_accounts: [arns_record(base), ant_record(undername | @), ant_config].
1666
1677
  */
1667
1678
  async approvePrimaryName(params, _options) {
1668
1679
  const { baseName } = splitPrimaryName(params.name);
@@ -1771,6 +1782,47 @@ export class SolanaARIOWriteable extends SolanaARIOReadable {
1771
1782
  return { id: sig };
1772
1783
  }
1773
1784
  // =========================================
1785
+ // Claim delegation from gateway with delegation DISABLED (ario-gar, Fix #6)
1786
+ // =========================================
1787
+ /**
1788
+ * Claim a delegate's stake out of a gateway that has DISABLED delegation
1789
+ * (`allow_delegated_staking == false`), moving it into the delegate's own
1790
+ * withdrawal vault (WP §6.3 / Fix #6). This is the disabled-gateway analog of
1791
+ * {@link claimDelegateFromLeavingGateway}: the on-chain instruction is
1792
+ * permissionless, so a cranker can sweep delegates out (the operator cannot
1793
+ * re-enable delegation until `total_delegated_stake == 0` and the cooldown
1794
+ * elapses). The withdrawal-counter and withdrawal PDAs are seeded by the
1795
+ * DELEGATOR, so a cranker must pass that delegate's `delegatorAddress`.
1796
+ *
1797
+ * @param params.gatewayAddress The gateway whose delegation was disabled.
1798
+ * @param params.delegatorAddress The delegate to claim for. Defaults to the
1799
+ * signer (self-claim). Pass another address to crank on a delegate's behalf;
1800
+ * the signer covers rent (`payer`) but stake still routes to the delegate's
1801
+ * own vault (the delegator key is bound by the delegation PDA seeds).
1802
+ */
1803
+ async claimDelegateFromDisabledGateway(params, _options) {
1804
+ const gateway = address(params.gatewayAddress);
1805
+ const delegator = params.delegatorAddress
1806
+ ? address(params.delegatorAddress)
1807
+ : this.signer.address;
1808
+ const [gatewayPda] = await getGatewayPDA(gateway, this.garProgram);
1809
+ const [delegationPda] = await getDelegationPDA(gateway, delegator, this.garProgram);
1810
+ // Withdrawal counter + vault are PDA-seeded by the delegator, not the payer.
1811
+ const nextId = await this.getNextWithdrawalId(delegator);
1812
+ const [withdrawalPda] = await getWithdrawalPDA(delegator, nextId, this.garProgram);
1813
+ const ix = await getClaimDelegateFromDisabledGatewayInstructionAsync({
1814
+ gateway: gatewayPda,
1815
+ delegation: delegationPda,
1816
+ withdrawal: withdrawalPda,
1817
+ // `delegator` is an unsigned seeds-derivation key; `payer` (the signer)
1818
+ // covers rent on the init_if_needed counter + the new withdrawal.
1819
+ delegator,
1820
+ payer: this.signer,
1821
+ }, { programAddress: this.garProgram });
1822
+ const sig = await this.sendTransaction([ix], 1_000_000);
1823
+ return { id: sig };
1824
+ }
1825
+ // =========================================
1774
1826
  // Delegation allowlist (ario-gar)
1775
1827
  // =========================================
1776
1828
  /** Add an address to the gateway's delegation allowlist. */
@@ -14,4 +14,4 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH
17
- export const version = '4.0.0-solana.24';
17
+ export const version = '4.0.0-solana.26';
@@ -46,6 +46,7 @@ declare class BorshReader {
46
46
  readString(): string;
47
47
  readOptionI64(): number | undefined;
48
48
  readOptionU32(): number | undefined;
49
+ readOptionU16(): number | undefined;
49
50
  skip(bytes: number): void;
50
51
  getOffset(): number;
51
52
  remaining(): number;
@@ -309,6 +309,21 @@ export declare class SolanaARIOReadable {
309
309
  pubkey: Address;
310
310
  operator: Address;
311
311
  }>>;
312
+ /**
313
+ * Enumerate Joined Gateway PDAs whose delegation has been DISABLED
314
+ * (`allow_delegated_staking == false`) yet still hold delegated stake
315
+ * (`total_delegated_stake > 0`) — i.e. delegates that an operator's disable
316
+ * left stranded (WP §6.3 / Fix #6). Each such gateway's delegates must be
317
+ * cranked out via
318
+ * {@link SolanaARIOWriteable.claimDelegateFromDisabledGateway} (enumerate
319
+ * them with {@link getGatewayDelegates}) before the operator can re-enable
320
+ * delegation. This is the discovery primitive a cranker uses to sweep them.
321
+ */
322
+ getDisabledGatewaysWithDelegatedStake(): Promise<Array<{
323
+ pubkey: Address;
324
+ operator: Address;
325
+ totalDelegatedStake: bigint;
326
+ }>>;
312
327
  /**
313
328
  * Enumerate Delegation PDAs with `amount == 0`. Eligible for
314
329
  * `closeEmptyDelegation` (rent refund to the original delegator).
@@ -350,14 +350,16 @@ export declare class SolanaARIOWriteable extends SolanaARIOReadable {
350
350
  * Build the `remaining_accounts` slice + the `antProgramId` arg the
351
351
  * four ario-core primary-name instructions consume. Sprint 2/5
352
352
  * reshape (ADR-016): ario-core no longer reads MPL Core asset bytes.
353
- * Authorization is "caller is the AntRecord.owner for this name" via
354
- * PDA-seed-pinned lookup.
353
+ * Authorization is "caller is the effective AntRecord owner for this
354
+ * name", resolved from the `AntRecord` + `AntConfig` PDAs (freshness-
355
+ * gated against `AntConfig.last_known_owner` — see ario-core BD-097 /
356
+ * BD-109). Both are program-PDA-pinned lookups.
355
357
  *
356
358
  * Layouts the on-chain handlers expect:
357
359
  * request_primary_name: [arnsRecord, demandFactor]
358
- * request_and_set_primary_name: [arnsRecord, demandFactor, antRecord]
359
- * approve_primary_name: [arnsRecord, antRecord]
360
- * remove_primary_name_for_base_name: [arnsRecord, antRecord(@)]
360
+ * request_and_set_primary_name: [arnsRecord, demandFactor, antRecord, antConfig]
361
+ * approve_primary_name: [arnsRecord, antRecord, antConfig]
362
+ * remove_primary_name_for_base_name: [arnsRecord, antRecord(@), antConfig]
361
363
  *
362
364
  * `antRecord` keys off the undername part for undernames (e.g.
363
365
  * "blog_arweave" → AntRecord at "blog") or the canonical "@" sentinel
@@ -400,7 +402,7 @@ export declare class SolanaARIOWriteable extends SolanaARIOReadable {
400
402
  *
401
403
  * Mirrors the on-chain `approve_primary_name` instruction
402
404
  * (`programs/ario-core/src/instructions/primary_name.rs`).
403
- * remaining_accounts: [arns_record(base), ant_record(undername | @)].
405
+ * remaining_accounts: [arns_record(base), ant_record(undername | @), ant_config].
404
406
  */
405
407
  approvePrimaryName(params: {
406
408
  initiator: Address;
@@ -422,6 +424,26 @@ export declare class SolanaARIOWriteable extends SolanaARIOReadable {
422
424
  claimDelegateFromLeavingGateway(params: {
423
425
  gatewayAddress: string;
424
426
  }, _options?: WriteOptions): Promise<MessageResult>;
427
+ /**
428
+ * Claim a delegate's stake out of a gateway that has DISABLED delegation
429
+ * (`allow_delegated_staking == false`), moving it into the delegate's own
430
+ * withdrawal vault (WP §6.3 / Fix #6). This is the disabled-gateway analog of
431
+ * {@link claimDelegateFromLeavingGateway}: the on-chain instruction is
432
+ * permissionless, so a cranker can sweep delegates out (the operator cannot
433
+ * re-enable delegation until `total_delegated_stake == 0` and the cooldown
434
+ * elapses). The withdrawal-counter and withdrawal PDAs are seeded by the
435
+ * DELEGATOR, so a cranker must pass that delegate's `delegatorAddress`.
436
+ *
437
+ * @param params.gatewayAddress The gateway whose delegation was disabled.
438
+ * @param params.delegatorAddress The delegate to claim for. Defaults to the
439
+ * signer (self-claim). Pass another address to crank on a delegate's behalf;
440
+ * the signer covers rent (`payer`) but stake still routes to the delegate's
441
+ * own vault (the delegator key is bound by the delegation PDA seeds).
442
+ */
443
+ claimDelegateFromDisabledGateway(params: {
444
+ gatewayAddress: string;
445
+ delegatorAddress?: string;
446
+ }, _options?: WriteOptions): Promise<MessageResult>;
425
447
  /** Add an address to the gateway's delegation allowlist. */
426
448
  allowDelegate(params: {
427
449
  delegate: string;
@@ -232,6 +232,22 @@ export type GatewaySettings = {
232
232
  fqdn: string;
233
233
  port: number;
234
234
  protocol: 'https';
235
+ /**
236
+ * Solana only (GATEWAY_VERSION 1.1.0+). A `delegateRewardShareRatio` change
237
+ * requested mid-epoch is staged here and applied at the next epoch's
238
+ * `tally_weights` (WP §6.3 / Fix #7), so the active value stays epoch-stable.
239
+ * When set, render the active `delegateRewardShareRatio` as the current rate
240
+ * and this as "pending until next epoch". Percent (0-95), same scale as
241
+ * `delegateRewardShareRatio`. Undefined when no change is queued.
242
+ */
243
+ pendingDelegateRewardShareRatio?: number;
244
+ /**
245
+ * Solana only (GATEWAY_VERSION 1.1.0+). Unix seconds when the operator
246
+ * disabled delegation (WP §6.3 / Fix #6). Re-enabling is blocked until every
247
+ * delegate has been withdrawn AND the withdrawal-period cooldown has elapsed
248
+ * since this time. Undefined when delegation is enabled.
249
+ */
250
+ delegationDisabledAt?: number;
235
251
  };
236
252
  export type BalanceWithAddress = {
237
253
  address: WalletAddress;
@@ -13,4 +13,4 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- export declare const version = "4.0.0-solana.23";
16
+ export declare const version = "4.0.0-solana.25";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ar.io/sdk",
3
- "version": "4.0.0-solana.24",
3
+ "version": "4.0.0-solana.26",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/ar-io/ar-io-sdk.git"
@@ -123,7 +123,7 @@
123
123
  "typescript": "^5.1.6"
124
124
  },
125
125
  "dependencies": {
126
- "@ar.io/solana-contracts": "0.4.0",
126
+ "@ar.io/solana-contracts": "0.5.0-staging.15",
127
127
  "@solana-program/address-lookup-table": "^0.11.0",
128
128
  "@solana-program/compute-budget": "^0.15.0",
129
129
  "@solana-program/token": "^0.13.0",