@ar.io/sdk 3.24.0 → 4.0.0-solana.10
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 +143 -23
- package/lib/esm/cli/cli.js +200 -40
- package/lib/esm/cli/commands/antCommands.js +40 -14
- package/lib/esm/cli/commands/arnsPurchaseCommands.js +46 -7
- package/lib/esm/cli/commands/escrowCommands.js +208 -0
- package/lib/esm/cli/commands/gatewayWriteCommands.js +132 -22
- package/lib/esm/cli/commands/pruneCommands.js +166 -0
- package/lib/esm/cli/commands/readCommands.js +10 -2
- package/lib/esm/cli/commands/transfer.js +6 -6
- package/lib/esm/cli/options.js +141 -2
- package/lib/esm/cli/utils.js +280 -39
- package/lib/esm/common/ant-registry.js +25 -3
- package/lib/esm/common/ant.js +50 -5
- package/lib/esm/common/contracts/ao-process.js +4 -6
- package/lib/esm/common/io.js +41 -0
- package/lib/esm/common/marketplace.js +24 -79
- package/lib/esm/constants.js +13 -1
- package/lib/esm/solana/ant-readable.js +387 -0
- package/lib/esm/solana/ant-registry-readable.js +118 -0
- package/lib/esm/solana/ant-registry-writeable.js +457 -0
- package/lib/esm/solana/ant-writeable.js +352 -0
- package/lib/esm/solana/ata.js +55 -0
- package/lib/esm/solana/canonical-message.js +92 -0
- package/lib/esm/solana/clusters.js +62 -0
- package/lib/esm/solana/constants.js +130 -0
- package/lib/esm/solana/delegation-math.js +63 -0
- package/lib/esm/solana/deserialize.js +1182 -0
- package/lib/esm/solana/escrow.js +797 -0
- package/lib/esm/solana/events.js +210 -0
- package/lib/esm/solana/funding-plan.js +682 -0
- package/lib/esm/solana/generated/ant/events/aclEntryAddedEvent.js +21 -0
- package/lib/esm/solana/generated/ant/events/aclEntryRemovedEvent.js +21 -0
- package/lib/esm/solana/generated/ant/events/antMetadataUpdatedEvent.js +21 -0
- package/lib/esm/solana/generated/ant/events/antReconciledEvent.js +21 -0
- package/lib/esm/solana/generated/ant/events/antTransferredEvent.js +21 -0
- package/lib/esm/solana/generated/ant/events/attributesClearedEvent.js +21 -0
- package/lib/esm/solana/generated/ant/events/attributesSyncedEvent.js +21 -0
- package/lib/esm/solana/generated/ant/events/controllerAddedEvent.js +21 -0
- package/lib/esm/solana/generated/ant/events/controllerRemovedEvent.js +21 -0
- package/lib/esm/solana/generated/ant/events/index.js +16 -0
- package/lib/esm/solana/generated/ant/events/recordMetadataPrunedEvent.js +21 -0
- package/lib/esm/solana/generated/ant/events/recordMetadataRemovedEvent.js +21 -0
- package/lib/esm/solana/generated/ant/events/recordMetadataUpdatedEvent.js +21 -0
- package/lib/esm/solana/generated/ant/events/recordRemovedEvent.js +21 -0
- package/lib/esm/solana/generated/ant/events/recordSetEvent.js +21 -0
- package/lib/esm/solana/generated/ant/events/recordTransferredEvent.js +21 -0
- package/lib/esm/solana/generated/ant-escrow/events/escrowCancelledEvent.js +21 -0
- package/lib/esm/solana/generated/ant-escrow/events/escrowClaimedEvent.js +21 -0
- package/lib/esm/solana/generated/ant-escrow/events/escrowDepositedEvent.js +21 -0
- package/lib/esm/solana/generated/ant-escrow/events/escrowRecipientUpdatedEvent.js +21 -0
- package/lib/esm/solana/generated/ant-escrow/events/index.js +5 -0
- package/lib/esm/solana/generated/arns/events/demandFactorUpdatedEvent.js +21 -0
- package/lib/esm/solana/generated/arns/events/index.js +13 -0
- package/lib/esm/solana/generated/arns/events/leaseExtendedEvent.js +21 -0
- package/lib/esm/solana/generated/arns/events/namePurchasedEvent.js +21 -0
- package/lib/esm/solana/generated/arns/events/nameReassignedEvent.js +21 -0
- package/lib/esm/solana/generated/arns/events/nameReleasedEvent.js +21 -0
- package/lib/esm/solana/generated/arns/events/nameReservedEvent.js +21 -0
- package/lib/esm/solana/generated/arns/events/nameUnreservedEvent.js +21 -0
- package/lib/esm/solana/generated/arns/events/nameUpgradedEvent.js +21 -0
- package/lib/esm/solana/generated/arns/events/namesPrunedEvent.js +21 -0
- package/lib/esm/solana/generated/arns/events/reservedNameClaimedEvent.js +21 -0
- package/lib/esm/solana/generated/arns/events/returnedNamePurchasedEvent.js +21 -0
- package/lib/esm/solana/generated/arns/events/undernameIncreasedEvent.js +21 -0
- package/lib/esm/solana/generated/core/events/configUpdatedEvent.js +21 -0
- package/lib/esm/solana/generated/core/events/coreMigrationFinalizedEvent.js +21 -0
- package/lib/esm/solana/generated/core/events/index.js +14 -0
- package/lib/esm/solana/generated/core/events/primaryNameRemovedEvent.js +21 -0
- package/lib/esm/solana/generated/core/events/primaryNameRequestExpiredEvent.js +21 -0
- package/lib/esm/solana/generated/core/events/primaryNameRequestedEvent.js +21 -0
- package/lib/esm/solana/generated/core/events/primaryNameSetEvent.js +21 -0
- package/lib/esm/solana/generated/core/events/supplyFinalizedEvent.js +21 -0
- package/lib/esm/solana/generated/core/events/transferEvent.js +21 -0
- package/lib/esm/solana/generated/core/events/vaultCreatedEvent.js +21 -0
- package/lib/esm/solana/generated/core/events/vaultExtendedEvent.js +21 -0
- package/lib/esm/solana/generated/core/events/vaultIncreasedEvent.js +21 -0
- package/lib/esm/solana/generated/core/events/vaultReleasedEvent.js +21 -0
- package/lib/esm/solana/generated/core/events/vaultRevokedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/allowlistToggledEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/delegateAllowlistedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/delegationClosedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/delegationDecreasedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/delegationEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/epochClosedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/epochCreatedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/epochDistributedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/epochPrescribedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/epochWeightsTalliedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/epochsToggledEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/fundingPlanAppliedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/garMigrationFinalizedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/gatewayFinalizedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/gatewayJoinedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/gatewayLeavingEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/gatewayPrunedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/gatewaySettingsUpdatedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/index.js +31 -0
- package/lib/esm/solana/generated/gar/events/instantWithdrawalEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/observationSubmittedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/observerAddressUpdatedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/operatorStakeIncreasedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/redelegationEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/residueVaultCreatedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/rewardsCompoundedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/stakePaymentEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/withdrawalCancelledEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/withdrawalClaimedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/withdrawalCreatedEvent.js +21 -0
- package/lib/esm/solana/generated/gar/events/withdrawalPaymentEvent.js +21 -0
- package/lib/esm/solana/generated/mpl-core/accounts/assetV1.js +42 -0
- package/lib/esm/solana/generated/mpl-core/accounts/collectionV1.js +42 -0
- package/lib/esm/solana/generated/mpl-core/accounts/hashedAssetV1.js +45 -0
- package/lib/esm/solana/generated/mpl-core/accounts/index.js +12 -0
- package/lib/esm/solana/generated/mpl-core/accounts/pluginHeaderV1.js +45 -0
- package/lib/esm/solana/generated/mpl-core/accounts/pluginRegistryV1.js +42 -0
- package/lib/esm/solana/generated/mpl-core/errors/index.js +8 -0
- package/lib/esm/solana/generated/mpl-core/errors/mplCore.js +136 -0
- package/lib/esm/solana/generated/mpl-core/index.js +8 -0
- package/lib/esm/solana/generated/mpl-core/instructions/addCollectionExternalPluginAdapterV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/addCollectionPluginV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/addExternalPluginAdapterV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/addPluginV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/approveCollectionPluginAuthorityV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/approvePluginAuthorityV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/burnCollectionV1.js +49 -0
- package/lib/esm/solana/generated/mpl-core/instructions/burnV1.js +49 -0
- package/lib/esm/solana/generated/mpl-core/instructions/collect.js +42 -0
- package/lib/esm/solana/generated/mpl-core/instructions/compressV1.js +50 -0
- package/lib/esm/solana/generated/mpl-core/instructions/createCollectionV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/createCollectionV2.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/createV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/createV2.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/decompressV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/executeV1.js +56 -0
- package/lib/esm/solana/generated/mpl-core/instructions/index.js +40 -0
- package/lib/esm/solana/generated/mpl-core/instructions/removeCollectionExternalPluginAdapterV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/removeCollectionPluginV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/removeExternalPluginAdapterV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/removePluginV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/revokeCollectionPluginAuthorityV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/revokePluginAuthorityV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/transferV1.js +49 -0
- package/lib/esm/solana/generated/mpl-core/instructions/updateCollectionExternalPluginAdapterV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/updateCollectionInfoV1.js +45 -0
- package/lib/esm/solana/generated/mpl-core/instructions/updateCollectionPluginV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/updateCollectionV1.js +52 -0
- package/lib/esm/solana/generated/mpl-core/instructions/updateExternalPluginAdapterV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/updatePluginV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/updateV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/updateV2.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/writeCollectionExternalPluginAdapterDataV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/instructions/writeExternalPluginAdapterDataV1.js +53 -0
- package/lib/esm/solana/generated/mpl-core/program-address.js +1 -0
- package/lib/esm/solana/generated/mpl-core/types/addAssetsToGroupV1Args.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/addBlocker.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/addCollectionsToGroupV1Args.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/addGroupsToGroupV1Args.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/agentIdentity.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/agentIdentityInitInfo.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/agentIdentityUpdateInfo.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/appData.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/appDataInitInfo.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/appDataUpdateInfo.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/attribute.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/attributes.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/authority.js +24 -0
- package/lib/esm/solana/generated/mpl-core/types/autograph.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/autographSignature.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/bubblegumV2.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/burnDelegate.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/closeGroupV1Args.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/compressionProof.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/creator.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/dataSection.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/dataSectionInitInfo.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/dataSectionUpdateInfo.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/dataState.js +23 -0
- package/lib/esm/solana/generated/mpl-core/types/edition.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/externalCheckResult.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/externalPluginAdapter.js +25 -0
- package/lib/esm/solana/generated/mpl-core/types/externalPluginAdapterInitInfo.js +25 -0
- package/lib/esm/solana/generated/mpl-core/types/externalPluginAdapterKey.js +25 -0
- package/lib/esm/solana/generated/mpl-core/types/externalPluginAdapterSchema.js +24 -0
- package/lib/esm/solana/generated/mpl-core/types/externalPluginAdapterType.js +28 -0
- package/lib/esm/solana/generated/mpl-core/types/externalPluginAdapterUpdateInfo.js +25 -0
- package/lib/esm/solana/generated/mpl-core/types/externalRegistryRecord.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/externalValidationResult.js +24 -0
- package/lib/esm/solana/generated/mpl-core/types/extraAccount.js +25 -0
- package/lib/esm/solana/generated/mpl-core/types/freezeDelegate.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/freezeExecute.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/groups.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/hashablePluginSchema.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/hashedAssetSchema.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/hookableLifecycleEvent.js +26 -0
- package/lib/esm/solana/generated/mpl-core/types/immutableMetadata.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/index.js +89 -0
- package/lib/esm/solana/generated/mpl-core/types/key.js +28 -0
- package/lib/esm/solana/generated/mpl-core/types/lifecycleHook.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/lifecycleHookInitInfo.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/lifecycleHookUpdateInfo.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/linkedAppData.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/linkedAppDataInitInfo.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/linkedAppDataUpdateInfo.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/linkedDataKey.js +25 -0
- package/lib/esm/solana/generated/mpl-core/types/linkedLifecycleHook.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/linkedLifecycleHookInitInfo.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/linkedLifecycleHookUpdateInfo.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/masterEdition.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/oracle.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/oracleInitInfo.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/oracleUpdateInfo.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/oracleValidation.js +25 -0
- package/lib/esm/solana/generated/mpl-core/types/permanentBurnDelegate.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/permanentFreezeDelegate.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/permanentFreezeExecute.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/permanentTransferDelegate.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/plugin.js +25 -0
- package/lib/esm/solana/generated/mpl-core/types/pluginAuthorityPair.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/pluginType.js +40 -0
- package/lib/esm/solana/generated/mpl-core/types/registryRecord.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/relationshipKind.js +25 -0
- package/lib/esm/solana/generated/mpl-core/types/removeAssetsFromGroupV1Args.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/removeCollectionsFromGroupV1Args.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/removeGroupsFromGroupV1Args.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/royalties.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/ruleSet.js +24 -0
- package/lib/esm/solana/generated/mpl-core/types/seed.js +24 -0
- package/lib/esm/solana/generated/mpl-core/types/transferDelegate.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/updateAuthority.js +24 -0
- package/lib/esm/solana/generated/mpl-core/types/updateDelegate.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/updateGroupV1Args.js +17 -0
- package/lib/esm/solana/generated/mpl-core/types/updateType.js +24 -0
- package/lib/esm/solana/generated/mpl-core/types/validationResult.js +25 -0
- package/lib/esm/solana/generated/mpl-core/types/validationResultsOffset.js +24 -0
- package/lib/esm/solana/generated/mpl-core/types/verifiedCreators.js +18 -0
- package/lib/esm/solana/generated/mpl-core/types/verifiedCreatorsSignature.js +17 -0
- package/lib/esm/solana/index.js +85 -0
- package/lib/esm/solana/instruction.js +24 -0
- package/lib/esm/solana/io-readable.js +1752 -0
- package/lib/esm/solana/io-writeable.js +2215 -0
- package/lib/esm/solana/json-rpc.js +74 -0
- package/lib/esm/solana/metadata.js +66 -0
- package/lib/esm/solana/mpl-core.js +142 -0
- package/lib/esm/solana/pda.js +317 -0
- package/lib/esm/solana/send.js +117 -0
- package/lib/esm/solana/spawn-ant.js +210 -0
- package/lib/esm/solana/types.js +1 -0
- package/lib/esm/types/ant.js +19 -7
- package/lib/esm/types/io.js +8 -1
- package/lib/esm/utils/ant.js +1 -0
- package/lib/esm/utils/ao.js +1 -0
- package/lib/esm/version.js +1 -1
- package/lib/types/cli/commands/antCommands.d.ts +2 -7
- package/lib/types/cli/commands/arnsPurchaseCommands.d.ts +12 -0
- package/lib/types/cli/commands/escrowCommands.d.ts +62 -0
- package/lib/types/cli/commands/pruneCommands.d.ts +31 -0
- package/lib/types/cli/commands/readCommands.d.ts +2 -0
- package/lib/types/cli/options.d.ts +112 -0
- package/lib/types/cli/types.d.ts +6 -0
- package/lib/types/cli/utils.d.ts +43 -5
- package/lib/types/common/ant-registry.d.ts +41 -0
- package/lib/types/common/ant.d.ts +35 -2
- package/lib/types/common/contracts/ao-process.d.ts +2 -16
- package/lib/types/common/io.d.ts +42 -2
- package/lib/types/common/marketplace.d.ts +2 -14
- package/lib/types/constants.d.ts +11 -1
- package/lib/types/solana/ant-readable.d.ts +121 -0
- package/lib/types/solana/ant-registry-readable.d.ts +90 -0
- package/lib/types/solana/ant-registry-writeable.d.ts +234 -0
- package/lib/types/solana/ant-writeable.d.ts +162 -0
- package/lib/types/solana/ata.d.ts +29 -0
- package/lib/types/solana/canonical-message.d.ts +82 -0
- package/lib/types/solana/clusters.d.ts +62 -0
- package/lib/types/solana/constants.d.ts +103 -0
- package/lib/types/solana/delegation-math.d.ts +20 -0
- package/lib/types/solana/deserialize.d.ts +440 -0
- package/lib/types/solana/escrow.d.ts +403 -0
- package/lib/types/solana/events.d.ts +156 -0
- package/lib/types/solana/funding-plan.d.ts +210 -0
- package/lib/types/solana/generated/ant/events/aclEntryAddedEvent.d.ts +26 -0
- package/lib/types/solana/generated/ant/events/aclEntryRemovedEvent.d.ts +26 -0
- package/lib/types/solana/generated/ant/events/antMetadataUpdatedEvent.d.ts +28 -0
- package/lib/types/solana/generated/ant/events/antReconciledEvent.d.ts +28 -0
- package/lib/types/solana/generated/ant/events/antTransferredEvent.d.ts +26 -0
- package/lib/types/solana/generated/ant/events/attributesClearedEvent.d.ts +26 -0
- package/lib/types/solana/generated/ant/events/attributesSyncedEvent.d.ts +24 -0
- package/lib/types/solana/generated/ant/events/controllerAddedEvent.d.ts +26 -0
- package/lib/types/solana/generated/ant/events/controllerRemovedEvent.d.ts +26 -0
- package/lib/types/solana/generated/ant/events/index.d.ts +15 -0
- package/lib/types/solana/generated/ant/events/recordMetadataPrunedEvent.d.ts +26 -0
- package/lib/types/solana/generated/ant/events/recordMetadataRemovedEvent.d.ts +26 -0
- package/lib/types/solana/generated/ant/events/recordMetadataUpdatedEvent.d.ts +28 -0
- package/lib/types/solana/generated/ant/events/recordRemovedEvent.d.ts +26 -0
- package/lib/types/solana/generated/ant/events/recordSetEvent.d.ts +34 -0
- package/lib/types/solana/generated/ant/events/recordTransferredEvent.d.ts +30 -0
- package/lib/types/solana/generated/ant-escrow/events/escrowCancelledEvent.d.ts +28 -0
- package/lib/types/solana/generated/ant-escrow/events/escrowClaimedEvent.d.ts +32 -0
- package/lib/types/solana/generated/ant-escrow/events/escrowDepositedEvent.d.ts +36 -0
- package/lib/types/solana/generated/ant-escrow/events/escrowRecipientUpdatedEvent.d.ts +26 -0
- package/lib/types/solana/generated/ant-escrow/events/index.d.ts +4 -0
- package/lib/types/solana/generated/arns/events/demandFactorUpdatedEvent.d.ts +28 -0
- package/lib/types/solana/generated/arns/events/index.d.ts +12 -0
- package/lib/types/solana/generated/arns/events/leaseExtendedEvent.d.ts +32 -0
- package/lib/types/solana/generated/arns/events/namePurchasedEvent.d.ts +34 -0
- package/lib/types/solana/generated/arns/events/nameReassignedEvent.d.ts +28 -0
- package/lib/types/solana/generated/arns/events/nameReleasedEvent.d.ts +24 -0
- package/lib/types/solana/generated/arns/events/nameReservedEvent.d.ts +28 -0
- package/lib/types/solana/generated/arns/events/nameUnreservedEvent.d.ts +24 -0
- package/lib/types/solana/generated/arns/events/nameUpgradedEvent.d.ts +28 -0
- package/lib/types/solana/generated/arns/events/namesPrunedEvent.d.ts +26 -0
- package/lib/types/solana/generated/arns/events/reservedNameClaimedEvent.d.ts +24 -0
- package/lib/types/solana/generated/arns/events/returnedNamePurchasedEvent.d.ts +32 -0
- package/lib/types/solana/generated/arns/events/undernameIncreasedEvent.d.ts +32 -0
- package/lib/types/solana/generated/core/events/configUpdatedEvent.d.ts +26 -0
- package/lib/types/solana/generated/core/events/coreMigrationFinalizedEvent.d.ts +26 -0
- package/lib/types/solana/generated/core/events/index.d.ts +13 -0
- package/lib/types/solana/generated/core/events/primaryNameRemovedEvent.d.ts +26 -0
- package/lib/types/solana/generated/core/events/primaryNameRequestExpiredEvent.d.ts +26 -0
- package/lib/types/solana/generated/core/events/primaryNameRequestedEvent.d.ts +30 -0
- package/lib/types/solana/generated/core/events/primaryNameSetEvent.d.ts +24 -0
- package/lib/types/solana/generated/core/events/supplyFinalizedEvent.d.ts +26 -0
- package/lib/types/solana/generated/core/events/transferEvent.d.ts +26 -0
- package/lib/types/solana/generated/core/events/vaultCreatedEvent.d.ts +28 -0
- package/lib/types/solana/generated/core/events/vaultExtendedEvent.d.ts +26 -0
- package/lib/types/solana/generated/core/events/vaultIncreasedEvent.d.ts +28 -0
- package/lib/types/solana/generated/core/events/vaultReleasedEvent.d.ts +26 -0
- package/lib/types/solana/generated/core/events/vaultRevokedEvent.d.ts +28 -0
- package/lib/types/solana/generated/gar/events/allowlistToggledEvent.d.ts +24 -0
- package/lib/types/solana/generated/gar/events/delegateAllowlistedEvent.d.ts +26 -0
- package/lib/types/solana/generated/gar/events/delegationClosedEvent.d.ts +24 -0
- package/lib/types/solana/generated/gar/events/delegationDecreasedEvent.d.ts +28 -0
- package/lib/types/solana/generated/gar/events/delegationEvent.d.ts +28 -0
- package/lib/types/solana/generated/gar/events/epochClosedEvent.d.ts +24 -0
- package/lib/types/solana/generated/gar/events/epochCreatedEvent.d.ts +26 -0
- package/lib/types/solana/generated/gar/events/epochDistributedEvent.d.ts +26 -0
- package/lib/types/solana/generated/gar/events/epochPrescribedEvent.d.ts +28 -0
- package/lib/types/solana/generated/gar/events/epochWeightsTalliedEvent.d.ts +26 -0
- package/lib/types/solana/generated/gar/events/epochsToggledEvent.d.ts +24 -0
- package/lib/types/solana/generated/gar/events/fundingPlanAppliedEvent.d.ts +28 -0
- package/lib/types/solana/generated/gar/events/garMigrationFinalizedEvent.d.ts +26 -0
- package/lib/types/solana/generated/gar/events/gatewayFinalizedEvent.d.ts +24 -0
- package/lib/types/solana/generated/gar/events/gatewayJoinedEvent.d.ts +26 -0
- package/lib/types/solana/generated/gar/events/gatewayLeavingEvent.d.ts +22 -0
- package/lib/types/solana/generated/gar/events/gatewayPrunedEvent.d.ts +26 -0
- package/lib/types/solana/generated/gar/events/gatewaySettingsUpdatedEvent.d.ts +24 -0
- package/lib/types/solana/generated/gar/events/index.d.ts +30 -0
- package/lib/types/solana/generated/gar/events/instantWithdrawalEvent.d.ts +30 -0
- package/lib/types/solana/generated/gar/events/observationSubmittedEvent.d.ts +28 -0
- package/lib/types/solana/generated/gar/events/observerAddressUpdatedEvent.d.ts +24 -0
- package/lib/types/solana/generated/gar/events/operatorStakeIncreasedEvent.d.ts +26 -0
- package/lib/types/solana/generated/gar/events/redelegationEvent.d.ts +30 -0
- package/lib/types/solana/generated/gar/events/residueVaultCreatedEvent.d.ts +30 -0
- package/lib/types/solana/generated/gar/events/rewardsCompoundedEvent.d.ts +26 -0
- package/lib/types/solana/generated/gar/events/stakePaymentEvent.d.ts +28 -0
- package/lib/types/solana/generated/gar/events/withdrawalCancelledEvent.d.ts +30 -0
- package/lib/types/solana/generated/gar/events/withdrawalClaimedEvent.d.ts +26 -0
- package/lib/types/solana/generated/gar/events/withdrawalCreatedEvent.d.ts +28 -0
- package/lib/types/solana/generated/gar/events/withdrawalPaymentEvent.d.ts +28 -0
- package/lib/types/solana/generated/mpl-core/accounts/assetV1.d.ts +37 -0
- package/lib/types/solana/generated/mpl-core/accounts/collectionV1.d.ts +37 -0
- package/lib/types/solana/generated/mpl-core/accounts/hashedAssetV1.d.ts +30 -0
- package/lib/types/solana/generated/mpl-core/accounts/index.d.ts +12 -0
- package/lib/types/solana/generated/mpl-core/accounts/pluginHeaderV1.d.ts +30 -0
- package/lib/types/solana/generated/mpl-core/accounts/pluginRegistryV1.d.ts +31 -0
- package/lib/types/solana/generated/mpl-core/errors/index.d.ts +8 -0
- package/lib/types/solana/generated/mpl-core/errors/mplCore.d.ts +133 -0
- package/lib/types/solana/generated/mpl-core/index.d.ts +8 -0
- package/lib/types/solana/generated/mpl-core/instructions/addCollectionExternalPluginAdapterV1.d.ts +56 -0
- package/lib/types/solana/generated/mpl-core/instructions/addCollectionPluginV1.d.ts +59 -0
- package/lib/types/solana/generated/mpl-core/instructions/addExternalPluginAdapterV1.d.ts +60 -0
- package/lib/types/solana/generated/mpl-core/instructions/addPluginV1.d.ts +63 -0
- package/lib/types/solana/generated/mpl-core/instructions/approveCollectionPluginAuthorityV1.d.ts +59 -0
- package/lib/types/solana/generated/mpl-core/instructions/approvePluginAuthorityV1.d.ts +63 -0
- package/lib/types/solana/generated/mpl-core/instructions/burnCollectionV1.d.ts +52 -0
- package/lib/types/solana/generated/mpl-core/instructions/burnV1.d.ts +60 -0
- package/lib/types/solana/generated/mpl-core/instructions/collect.d.ts +39 -0
- package/lib/types/solana/generated/mpl-core/instructions/compressV1.d.ts +55 -0
- package/lib/types/solana/generated/mpl-core/instructions/createCollectionV1.d.ts +58 -0
- package/lib/types/solana/generated/mpl-core/instructions/createCollectionV2.d.ts +61 -0
- package/lib/types/solana/generated/mpl-core/instructions/createV1.d.ts +77 -0
- package/lib/types/solana/generated/mpl-core/instructions/createV2.d.ts +80 -0
- package/lib/types/solana/generated/mpl-core/instructions/decompressV1.d.ts +60 -0
- package/lib/types/solana/generated/mpl-core/instructions/executeV1.d.ts +63 -0
- package/lib/types/solana/generated/mpl-core/instructions/index.d.ts +40 -0
- package/lib/types/solana/generated/mpl-core/instructions/removeCollectionExternalPluginAdapterV1.d.ts +56 -0
- package/lib/types/solana/generated/mpl-core/instructions/removeCollectionPluginV1.d.ts +56 -0
- package/lib/types/solana/generated/mpl-core/instructions/removeExternalPluginAdapterV1.d.ts +60 -0
- package/lib/types/solana/generated/mpl-core/instructions/removePluginV1.d.ts +60 -0
- package/lib/types/solana/generated/mpl-core/instructions/revokeCollectionPluginAuthorityV1.d.ts +56 -0
- package/lib/types/solana/generated/mpl-core/instructions/revokePluginAuthorityV1.d.ts +60 -0
- package/lib/types/solana/generated/mpl-core/instructions/transferV1.d.ts +64 -0
- package/lib/types/solana/generated/mpl-core/instructions/updateCollectionExternalPluginAdapterV1.d.ts +59 -0
- package/lib/types/solana/generated/mpl-core/instructions/updateCollectionInfoV1.d.ts +47 -0
- package/lib/types/solana/generated/mpl-core/instructions/updateCollectionPluginV1.d.ts +56 -0
- package/lib/types/solana/generated/mpl-core/instructions/updateCollectionV1.d.ts +62 -0
- package/lib/types/solana/generated/mpl-core/instructions/updateExternalPluginAdapterV1.d.ts +63 -0
- package/lib/types/solana/generated/mpl-core/instructions/updatePluginV1.d.ts +60 -0
- package/lib/types/solana/generated/mpl-core/instructions/updateV1.d.ts +66 -0
- package/lib/types/solana/generated/mpl-core/instructions/updateV2.d.ts +70 -0
- package/lib/types/solana/generated/mpl-core/instructions/writeCollectionExternalPluginAdapterDataV1.d.ts +63 -0
- package/lib/types/solana/generated/mpl-core/instructions/writeExternalPluginAdapterDataV1.d.ts +67 -0
- package/lib/types/solana/generated/mpl-core/program-address.d.ts +7 -0
- package/lib/types/solana/generated/mpl-core/types/addAssetsToGroupV1Args.d.ts +13 -0
- package/lib/types/solana/generated/mpl-core/types/addBlocker.d.ts +13 -0
- package/lib/types/solana/generated/mpl-core/types/addCollectionsToGroupV1Args.d.ts +13 -0
- package/lib/types/solana/generated/mpl-core/types/addGroupsToGroupV1Args.d.ts +15 -0
- package/lib/types/solana/generated/mpl-core/types/agentIdentity.d.ts +15 -0
- package/lib/types/solana/generated/mpl-core/types/agentIdentityInitInfo.d.ts +22 -0
- package/lib/types/solana/generated/mpl-core/types/agentIdentityUpdateInfo.d.ts +20 -0
- package/lib/types/solana/generated/mpl-core/types/appData.d.ts +20 -0
- package/lib/types/solana/generated/mpl-core/types/appDataInitInfo.d.ts +22 -0
- package/lib/types/solana/generated/mpl-core/types/appDataUpdateInfo.d.ts +18 -0
- package/lib/types/solana/generated/mpl-core/types/attribute.d.ts +16 -0
- package/lib/types/solana/generated/mpl-core/types/attributes.d.ts +18 -0
- package/lib/types/solana/generated/mpl-core/types/authority.d.ts +29 -0
- package/lib/types/solana/generated/mpl-core/types/autograph.d.ts +18 -0
- package/lib/types/solana/generated/mpl-core/types/autographSignature.d.ts +16 -0
- package/lib/types/solana/generated/mpl-core/types/bubblegumV2.d.ts +13 -0
- package/lib/types/solana/generated/mpl-core/types/burnDelegate.d.ts +13 -0
- package/lib/types/solana/generated/mpl-core/types/closeGroupV1Args.d.ts +13 -0
- package/lib/types/solana/generated/mpl-core/types/compressionProof.d.ts +28 -0
- package/lib/types/solana/generated/mpl-core/types/creator.d.ts +16 -0
- package/lib/types/solana/generated/mpl-core/types/dataSection.d.ts +20 -0
- package/lib/types/solana/generated/mpl-core/types/dataSectionInitInfo.d.ts +20 -0
- package/lib/types/solana/generated/mpl-core/types/dataSectionUpdateInfo.d.ts +13 -0
- package/lib/types/solana/generated/mpl-core/types/dataState.d.ts +16 -0
- package/lib/types/solana/generated/mpl-core/types/edition.d.ts +15 -0
- package/lib/types/solana/generated/mpl-core/types/externalCheckResult.d.ts +15 -0
- package/lib/types/solana/generated/mpl-core/types/externalPluginAdapter.d.ts +66 -0
- package/lib/types/solana/generated/mpl-core/types/externalPluginAdapterInitInfo.d.ts +66 -0
- package/lib/types/solana/generated/mpl-core/types/externalPluginAdapterKey.d.ts +64 -0
- package/lib/types/solana/generated/mpl-core/types/externalPluginAdapterSchema.d.ts +17 -0
- package/lib/types/solana/generated/mpl-core/types/externalPluginAdapterType.d.ts +21 -0
- package/lib/types/solana/generated/mpl-core/types/externalPluginAdapterUpdateInfo.d.ts +59 -0
- package/lib/types/solana/generated/mpl-core/types/externalRegistryRecord.d.ts +28 -0
- package/lib/types/solana/generated/mpl-core/types/externalValidationResult.d.ts +17 -0
- package/lib/types/solana/generated/mpl-core/types/extraAccount.d.ts +86 -0
- package/lib/types/solana/generated/mpl-core/types/freezeDelegate.d.ts +15 -0
- package/lib/types/solana/generated/mpl-core/types/freezeExecute.d.ts +15 -0
- package/lib/types/solana/generated/mpl-core/types/groups.d.ts +15 -0
- package/lib/types/solana/generated/mpl-core/types/hashablePluginSchema.d.ts +22 -0
- package/lib/types/solana/generated/mpl-core/types/hashedAssetSchema.d.ts +16 -0
- package/lib/types/solana/generated/mpl-core/types/hookableLifecycleEvent.d.ts +19 -0
- package/lib/types/solana/generated/mpl-core/types/immutableMetadata.d.ts +13 -0
- package/lib/types/solana/generated/mpl-core/types/index.d.ts +89 -0
- package/lib/types/solana/generated/mpl-core/types/key.d.ts +21 -0
- package/lib/types/solana/generated/mpl-core/types/lifecycleHook.d.ts +24 -0
- package/lib/types/solana/generated/mpl-core/types/lifecycleHookInitInfo.d.ts +28 -0
- package/lib/types/solana/generated/mpl-core/types/lifecycleHookUpdateInfo.d.ts +22 -0
- package/lib/types/solana/generated/mpl-core/types/linkedAppData.d.ts +20 -0
- package/lib/types/solana/generated/mpl-core/types/linkedAppDataInitInfo.d.ts +22 -0
- package/lib/types/solana/generated/mpl-core/types/linkedAppDataUpdateInfo.d.ts +18 -0
- package/lib/types/solana/generated/mpl-core/types/linkedDataKey.d.ts +31 -0
- package/lib/types/solana/generated/mpl-core/types/linkedLifecycleHook.d.ts +24 -0
- package/lib/types/solana/generated/mpl-core/types/linkedLifecycleHookInitInfo.d.ts +28 -0
- package/lib/types/solana/generated/mpl-core/types/linkedLifecycleHookUpdateInfo.d.ts +22 -0
- package/lib/types/solana/generated/mpl-core/types/masterEdition.d.ts +21 -0
- package/lib/types/solana/generated/mpl-core/types/oracle.d.ts +22 -0
- package/lib/types/solana/generated/mpl-core/types/oracleInitInfo.d.ts +26 -0
- package/lib/types/solana/generated/mpl-core/types/oracleUpdateInfo.d.ts +22 -0
- package/lib/types/solana/generated/mpl-core/types/oracleValidation.d.ts +35 -0
- package/lib/types/solana/generated/mpl-core/types/permanentBurnDelegate.d.ts +13 -0
- package/lib/types/solana/generated/mpl-core/types/permanentFreezeDelegate.d.ts +15 -0
- package/lib/types/solana/generated/mpl-core/types/permanentFreezeExecute.d.ts +15 -0
- package/lib/types/solana/generated/mpl-core/types/permanentTransferDelegate.d.ts +13 -0
- package/lib/types/solana/generated/mpl-core/types/plugin.d.ts +150 -0
- package/lib/types/solana/generated/mpl-core/types/pluginAuthorityPair.d.ts +20 -0
- package/lib/types/solana/generated/mpl-core/types/pluginType.d.ts +33 -0
- package/lib/types/solana/generated/mpl-core/types/registryRecord.d.ts +22 -0
- package/lib/types/solana/generated/mpl-core/types/relationshipKind.d.ts +18 -0
- package/lib/types/solana/generated/mpl-core/types/removeAssetsFromGroupV1Args.d.ts +15 -0
- package/lib/types/solana/generated/mpl-core/types/removeCollectionsFromGroupV1Args.d.ts +15 -0
- package/lib/types/solana/generated/mpl-core/types/removeGroupsFromGroupV1Args.d.ts +15 -0
- package/lib/types/solana/generated/mpl-core/types/royalties.d.ts +22 -0
- package/lib/types/solana/generated/mpl-core/types/ruleSet.d.ts +27 -0
- package/lib/types/solana/generated/mpl-core/types/seed.d.ts +36 -0
- package/lib/types/solana/generated/mpl-core/types/transferDelegate.d.ts +13 -0
- package/lib/types/solana/generated/mpl-core/types/updateAuthority.d.ts +27 -0
- package/lib/types/solana/generated/mpl-core/types/updateDelegate.d.ts +15 -0
- package/lib/types/solana/generated/mpl-core/types/updateGroupV1Args.d.ts +19 -0
- package/lib/types/solana/generated/mpl-core/types/updateType.d.ts +17 -0
- package/lib/types/solana/generated/mpl-core/types/validationResult.d.ts +18 -0
- package/lib/types/solana/generated/mpl-core/types/validationResultsOffset.d.ts +33 -0
- package/lib/types/solana/generated/mpl-core/types/verifiedCreators.d.ts +18 -0
- package/lib/types/solana/generated/mpl-core/types/verifiedCreatorsSignature.d.ts +16 -0
- package/lib/types/solana/index.d.ts +63 -0
- package/lib/types/solana/instruction.d.ts +24 -0
- package/lib/types/solana/io-readable.d.ts +360 -0
- package/lib/types/solana/io-writeable.d.ts +533 -0
- package/lib/types/solana/json-rpc.d.ts +32 -0
- package/lib/types/solana/metadata.d.ts +69 -0
- package/lib/types/solana/mpl-core.d.ts +76 -0
- package/lib/types/solana/pda.d.ts +95 -0
- package/lib/types/solana/send.d.ts +16 -0
- package/lib/types/solana/spawn-ant.d.ts +130 -0
- package/lib/types/solana/types.d.ts +67 -0
- package/lib/types/types/ant-registry.d.ts +39 -0
- package/lib/types/types/ant.d.ts +22 -4
- package/lib/types/types/io.d.ts +56 -1
- package/lib/types/version.d.ts +1 -1
- package/package.json +33 -16
- package/lib/cjs/cli/cli.js +0 -822
- package/lib/cjs/cli/commands/antCommands.js +0 -113
- package/lib/cjs/cli/commands/arnsPurchaseCommands.js +0 -212
- package/lib/cjs/cli/commands/gatewayWriteCommands.js +0 -210
- package/lib/cjs/cli/commands/readCommands.js +0 -215
- package/lib/cjs/cli/commands/transfer.js +0 -159
- package/lib/cjs/cli/options.js +0 -470
- package/lib/cjs/cli/types.js +0 -2
- package/lib/cjs/cli/utils.js +0 -639
- package/lib/cjs/common/ant-registry.js +0 -155
- package/lib/cjs/common/ant-versions.js +0 -93
- package/lib/cjs/common/ant.js +0 -1182
- package/lib/cjs/common/arweave.js +0 -27
- package/lib/cjs/common/contracts/ao-process.js +0 -224
- package/lib/cjs/common/error.js +0 -64
- package/lib/cjs/common/faucet.js +0 -150
- package/lib/cjs/common/hyperbeam/hb.js +0 -173
- package/lib/cjs/common/index.js +0 -42
- package/lib/cjs/common/io.js +0 -1423
- package/lib/cjs/common/logger.js +0 -83
- package/lib/cjs/common/loggers/winston.js +0 -68
- package/lib/cjs/common/marketplace.js +0 -731
- package/lib/cjs/common/turbo.js +0 -223
- package/lib/cjs/constants.js +0 -41
- package/lib/cjs/node/index.js +0 -39
- package/lib/cjs/package.json +0 -1
- package/lib/cjs/types/ant-registry.js +0 -2
- package/lib/cjs/types/ant.js +0 -168
- package/lib/cjs/types/common.js +0 -2
- package/lib/cjs/types/faucet.js +0 -2
- package/lib/cjs/types/index.js +0 -37
- package/lib/cjs/types/io.js +0 -51
- package/lib/cjs/types/token.js +0 -116
- package/lib/cjs/utils/ant.js +0 -108
- package/lib/cjs/utils/ao.js +0 -432
- package/lib/cjs/utils/arweave.js +0 -285
- package/lib/cjs/utils/base64.js +0 -62
- package/lib/cjs/utils/hash.js +0 -56
- package/lib/cjs/utils/index.js +0 -38
- package/lib/cjs/utils/json.js +0 -26
- package/lib/cjs/utils/processes.js +0 -173
- package/lib/cjs/utils/random.js +0 -30
- package/lib/cjs/utils/schema.js +0 -15
- package/lib/cjs/utils/url.js +0 -37
- package/lib/cjs/version.js +0 -20
- package/lib/cjs/web/index.js +0 -41
|
@@ -0,0 +1,1752 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solana implementation of the AoARIORead interface.
|
|
3
|
+
*
|
|
4
|
+
* Reads AR.IO protocol state directly from Solana PDAs using RPC,
|
|
5
|
+
* returning the same types that the AO implementation returns.
|
|
6
|
+
* This allows consumers to switch backends transparently.
|
|
7
|
+
*/
|
|
8
|
+
import { address, fetchEncodedAccount, fetchEncodedAccounts, getAddressDecoder, } from '@solana/kit';
|
|
9
|
+
import bs58 from 'bs58';
|
|
10
|
+
import { ARNS_RECORD_DISCRIMINATOR, RESERVED_NAME_DISCRIMINATOR, RETURNED_NAME_DISCRIMINATOR, getArnsConfigDecoder, getArnsRecordDecoder, getReservedNameDecoder, getReturnedNameDecoder, } from '@ar.io/solana-contracts/arns';
|
|
11
|
+
import { PRIMARY_NAME_DISCRIMINATOR, PRIMARY_NAME_REQUEST_DISCRIMINATOR, VAULT_DISCRIMINATOR, getPrimaryNameRequestDecoder, getVaultDecoder, } from '@ar.io/solana-contracts/core';
|
|
12
|
+
import { ALLOWLIST_ENTRY_DISCRIMINATOR, DELEGATION_DISCRIMINATOR, GATEWAY_DISCRIMINATOR, GatewayStatus, OBSERVATION_DISCRIMINATOR, WITHDRAWAL_DISCRIMINATOR, getDelegationDecoder, getGatewayDecoder, getWithdrawalDecoder, } from '@ar.io/solana-contracts/gar';
|
|
13
|
+
import { Logger } from '../common/logger.js';
|
|
14
|
+
import { SolanaANTRegistryReadable } from './ant-registry-readable.js';
|
|
15
|
+
import { getAssociatedTokenAddressKit } from './ata.js';
|
|
16
|
+
import { ARIO_ANT_PROGRAM_ID, ARIO_ARNS_PROGRAM_ID, ARIO_CORE_PROGRAM_ID, ARIO_GAR_PROGRAM_ID, ARNS_RECORD_ANT_OFFSET, RATE_SCALE, } from './constants.js';
|
|
17
|
+
import { computeLiveDelegationBalance } from './delegation-math.js';
|
|
18
|
+
import { deserializeAllowlist, deserializeArioConfig, deserializeArnsRecord, deserializeDelegation, deserializeDemandFactor, deserializeEpoch, deserializeEpochSettings, deserializeEpochSettingsFull, deserializeGarSettings, deserializeGarSupplyCounters, deserializeGateway, deserializeGatewayWithAccumulator, deserializeObservation, deserializePrimaryName, deserializePrimaryNameRequest, deserializeRedelegationRecord, deserializeReservedName, deserializeReturnedName, deserializeVault, deserializeWithdrawal, } from './deserialize.js';
|
|
19
|
+
import { TOKEN_PROGRAM_ADDRESS } from './instruction.js';
|
|
20
|
+
import { getArioConfigPDA, getArnsRecordPDA, getArnsRecordPDAFromHash, getArnsSettingsPDA, getDemandFactorPDA, getEpochPDA, getEpochSettingsPDA, getGarSettingsPDA, getGatewayPDA, getGatewayRegistryPDA, getObserverLookupPDA, getPrimaryNamePDA, getPrimaryNameRequestPDA, getReservedNamePDA, getReturnedNamePDA, getVaultPDA, } from './pda.js';
|
|
21
|
+
const addressDecoder = getAddressDecoder();
|
|
22
|
+
/** All-zero address — equivalent of web3.js `PublicKey.default`. */
|
|
23
|
+
const DEFAULT_ADDRESS = address('11111111111111111111111111111111');
|
|
24
|
+
// =========================================
|
|
25
|
+
// Pagination helper
|
|
26
|
+
// =========================================
|
|
27
|
+
/**
|
|
28
|
+
* Normalize whatever `params.filters?.processId` shape came in (undefined,
|
|
29
|
+
* single string, or array — `PaginationParams` widens it past what ArNS
|
|
30
|
+
* actually supports) into a flat `string[]` ANT-mint list.
|
|
31
|
+
*/
|
|
32
|
+
function normalizeProcessIdFilter(raw) {
|
|
33
|
+
if (raw == null)
|
|
34
|
+
return [];
|
|
35
|
+
if (typeof raw === 'string')
|
|
36
|
+
return [raw];
|
|
37
|
+
if (Array.isArray(raw))
|
|
38
|
+
return raw.filter((v) => typeof v === 'string');
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* On-chain timestamps in the Solana programs are stored in **seconds**
|
|
43
|
+
* (matching the Lua-source convention as ported), but the public SDK
|
|
44
|
+
* surface — shared with the AO backend — exposes them in **milliseconds**
|
|
45
|
+
* because every JS consumer (UI, cranker, migration tools) feeds them into
|
|
46
|
+
* `new Date()`/`Date.now()` arithmetic. This boundary helper converts at
|
|
47
|
+
* the read-path projection layer so internal arithmetic in this file can
|
|
48
|
+
* keep working in seconds against the deserializer output, but everything
|
|
49
|
+
* we hand back to a caller is in JS-millisecond units.
|
|
50
|
+
*
|
|
51
|
+
* Use `toMsTimestamps(obj)` for projection-layer return values, and
|
|
52
|
+
* `secToMs(n)` for one-off scalars.
|
|
53
|
+
*/
|
|
54
|
+
const TIMESTAMP_FIELDS_MS = [
|
|
55
|
+
'startTimestamp',
|
|
56
|
+
'endTimestamp',
|
|
57
|
+
'distributionTimestamp',
|
|
58
|
+
];
|
|
59
|
+
function secToMs(n) {
|
|
60
|
+
return n * 1000;
|
|
61
|
+
}
|
|
62
|
+
function toMsTimestamps(obj) {
|
|
63
|
+
const out = { ...obj };
|
|
64
|
+
for (const f of TIMESTAMP_FIELDS_MS) {
|
|
65
|
+
const v = out[f];
|
|
66
|
+
if (typeof v === 'number')
|
|
67
|
+
out[f] = v * 1000;
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Drop the SDK-internal extras (`name`, `owner`) and the `processId` re-key
|
|
73
|
+
* that `deserializeArnsRecord` adds, projecting back to the cross-backend
|
|
74
|
+
* `AoArNSNameDataWithName` shape consumers expect.
|
|
75
|
+
*
|
|
76
|
+
* Timestamps are converted from on-chain seconds to JS milliseconds here
|
|
77
|
+
* (see `toMsTimestamps` above for rationale).
|
|
78
|
+
*/
|
|
79
|
+
function arnsRecordToWithName(record) {
|
|
80
|
+
return {
|
|
81
|
+
name: record.name,
|
|
82
|
+
processId: record.processId,
|
|
83
|
+
startTimestamp: secToMs(record.startTimestamp),
|
|
84
|
+
undernameLimit: record.undernameLimit,
|
|
85
|
+
purchasePrice: record.purchasePrice,
|
|
86
|
+
type: record.type,
|
|
87
|
+
...('endTimestamp' in record
|
|
88
|
+
? { endTimestamp: secToMs(record.endTimestamp) }
|
|
89
|
+
: {}),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function paginate(items, params) {
|
|
93
|
+
const limit = params?.limit ?? 100;
|
|
94
|
+
const startIdx = params?.cursor ? parseInt(params.cursor, 10) : 0;
|
|
95
|
+
const page = items.slice(startIdx, startIdx + limit);
|
|
96
|
+
const hasMore = startIdx + limit < items.length;
|
|
97
|
+
return {
|
|
98
|
+
items: page,
|
|
99
|
+
limit,
|
|
100
|
+
totalItems: items.length,
|
|
101
|
+
sortOrder: params?.sortOrder ?? 'asc',
|
|
102
|
+
hasMore,
|
|
103
|
+
nextCursor: hasMore ? String(startIdx + limit) : undefined,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Solana-backed read-only client for the AR.IO protocol.
|
|
108
|
+
*
|
|
109
|
+
* Usage:
|
|
110
|
+
* ```ts
|
|
111
|
+
* import { createSolanaRpc } from '@solana/kit';
|
|
112
|
+
* import { SolanaARIOReadable } from '@ar.io/sdk/solana';
|
|
113
|
+
*
|
|
114
|
+
* const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
|
|
115
|
+
* const ario = new SolanaARIOReadable({ rpc });
|
|
116
|
+
*
|
|
117
|
+
* const gateway = await ario.getGateway({ address: 'GatewayOperatorPubkey...' });
|
|
118
|
+
* const record = await ario.getArNSRecord({ name: 'ardrive' });
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export class SolanaARIOReadable {
|
|
122
|
+
rpc;
|
|
123
|
+
commitment;
|
|
124
|
+
logger;
|
|
125
|
+
// Allow overriding program IDs (e.g., for devnet/localnet)
|
|
126
|
+
coreProgram;
|
|
127
|
+
garProgram;
|
|
128
|
+
arnsProgram;
|
|
129
|
+
antProgram;
|
|
130
|
+
// Memoized ARIO mint address (read once from ArioConfig.mint and reused
|
|
131
|
+
// for every SPL-ATA derivation in getBalance/getBalances).
|
|
132
|
+
_arioMint;
|
|
133
|
+
constructor(config) {
|
|
134
|
+
this.rpc = config.rpc;
|
|
135
|
+
this.commitment = config.commitment ?? 'confirmed';
|
|
136
|
+
this.logger = config.logger ?? Logger.default;
|
|
137
|
+
this.coreProgram = config.coreProgramId ?? ARIO_CORE_PROGRAM_ID;
|
|
138
|
+
this.garProgram = config.garProgramId ?? ARIO_GAR_PROGRAM_ID;
|
|
139
|
+
this.arnsProgram = config.arnsProgramId ?? ARIO_ARNS_PROGRAM_ID;
|
|
140
|
+
this.antProgram = config.antProgramId ?? ARIO_ANT_PROGRAM_ID;
|
|
141
|
+
}
|
|
142
|
+
/** Helper to fetch an encoded account (kit's replacement for Connection.getAccountInfo). */
|
|
143
|
+
async getAccount(pda) {
|
|
144
|
+
return fetchEncodedAccount(this.rpc, pda, {
|
|
145
|
+
commitment: this.commitment,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Helper for `getProgramAccounts` with a discriminator memcmp filter.
|
|
150
|
+
*
|
|
151
|
+
* Pass the Codama-generated `<NAME>_DISCRIMINATOR: Uint8Array` constant
|
|
152
|
+
* directly — kit's RPC requires a base58 string for `memcmp.bytes`, so
|
|
153
|
+
* we bs58-encode here to keep call sites from doing it inline (and to
|
|
154
|
+
* keep the IDL-derived bytes as the single source of truth).
|
|
155
|
+
*/
|
|
156
|
+
async getAccountsByDiscriminator(programId, discriminator, extraFilters = []) {
|
|
157
|
+
const filters = [
|
|
158
|
+
{
|
|
159
|
+
memcmp: {
|
|
160
|
+
offset: 0n,
|
|
161
|
+
bytes: bs58.encode(discriminator),
|
|
162
|
+
encoding: 'base58',
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
...extraFilters,
|
|
166
|
+
];
|
|
167
|
+
// Note: kit's getProgramAccounts returns a plain array (no context wrapper)
|
|
168
|
+
// when called without `withContext: true`. With `encoding: 'base64'`, each
|
|
169
|
+
// account's `data` is a `[base64, 'base64']` tuple. We bypass kit's strict
|
|
170
|
+
// generic overload typing here with a cast — the runtime shape is stable.
|
|
171
|
+
const result = (await this.rpc
|
|
172
|
+
.getProgramAccounts(programId, {
|
|
173
|
+
commitment: this.commitment,
|
|
174
|
+
encoding: 'base64',
|
|
175
|
+
filters,
|
|
176
|
+
})
|
|
177
|
+
.send());
|
|
178
|
+
return result.map((entry) => ({
|
|
179
|
+
pubkey: entry.pubkey,
|
|
180
|
+
data: Buffer.from(entry.account.data[0], 'base64'),
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Batch-fetch the `cumulative_reward_per_token` accumulator for every gateway
|
|
185
|
+
* in `operatorAddresses`. Returns a Map keyed by base58 operator address.
|
|
186
|
+
* Used by the delegate readers below to compute the live delegation balance
|
|
187
|
+
* without an on-chain settlement call (see {@link computeLiveDelegationBalance}
|
|
188
|
+
* and `INVARIANTS.md` in the contracts repo). Missing gateways are silently
|
|
189
|
+
* skipped — callers fall back to the stale `Delegation.amount` for those
|
|
190
|
+
* (the accumulator delta is 0 and live == stored anyway when the gateway
|
|
191
|
+
* has no rewards to distribute).
|
|
192
|
+
*/
|
|
193
|
+
async getGatewayAccumulators(operatorAddresses) {
|
|
194
|
+
const unique = Array.from(new Set(operatorAddresses));
|
|
195
|
+
if (unique.length === 0)
|
|
196
|
+
return new Map();
|
|
197
|
+
const pdas = await Promise.all(unique.map(async (op) => (await getGatewayPDA(address(op), this.garProgram))[0]));
|
|
198
|
+
const accounts = await fetchEncodedAccounts(this.rpc, pdas, {
|
|
199
|
+
commitment: this.commitment,
|
|
200
|
+
});
|
|
201
|
+
const out = new Map();
|
|
202
|
+
for (let i = 0; i < accounts.length; i++) {
|
|
203
|
+
const acct = accounts[i];
|
|
204
|
+
if (!acct.exists)
|
|
205
|
+
continue;
|
|
206
|
+
try {
|
|
207
|
+
// Internal variant: surfaces the u128 accumulator that the public
|
|
208
|
+
// `deserializeGateway` deliberately drops (BigInt is not
|
|
209
|
+
// JSON-serializable and would leak through getGateway).
|
|
210
|
+
const gw = deserializeGatewayWithAccumulator(Buffer.from(acct.data));
|
|
211
|
+
out.set(unique[i], gw.cumulativeRewardPerToken);
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// Skip malformed; the caller will fall back to the raw delegation amount.
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return out;
|
|
218
|
+
}
|
|
219
|
+
/** Read the gateway registry and return addresses in registry index order */
|
|
220
|
+
async getRegistryGatewayAddresses() {
|
|
221
|
+
const [registryPda] = await getGatewayRegistryPDA(this.garProgram);
|
|
222
|
+
const registryAccount = await this.getAccount(registryPda);
|
|
223
|
+
if (!registryAccount.exists)
|
|
224
|
+
return [];
|
|
225
|
+
const registryData = Buffer.from(registryAccount.data);
|
|
226
|
+
const count = registryData.readUInt32LE(40); // 8 disc + 32 authority
|
|
227
|
+
const slotsOffset = 48; // 8 + 32 + 4 + 4
|
|
228
|
+
// GatewaySlot: address(32) + composite_weight(8) + start_timestamp(8)
|
|
229
|
+
// + status(1) + _padding(7) = 56 bytes (see ario-gar
|
|
230
|
+
// state/mod.rs::GatewaySlot).
|
|
231
|
+
const SLOT_STRIDE = 56;
|
|
232
|
+
const addresses = [];
|
|
233
|
+
for (let i = 0; i < count && i < 3000; i++) {
|
|
234
|
+
const slotOffset = slotsOffset + i * SLOT_STRIDE;
|
|
235
|
+
const addr = addressDecoder.decode(registryData.subarray(slotOffset, slotOffset + 32));
|
|
236
|
+
addresses.push(addr);
|
|
237
|
+
}
|
|
238
|
+
return addresses;
|
|
239
|
+
}
|
|
240
|
+
// =========================================
|
|
241
|
+
// Protocol info
|
|
242
|
+
// =========================================
|
|
243
|
+
async getInfo() {
|
|
244
|
+
const [[configPda], [epochSettingsPda]] = await Promise.all([
|
|
245
|
+
getArioConfigPDA(this.coreProgram),
|
|
246
|
+
getEpochSettingsPDA(this.garProgram),
|
|
247
|
+
]);
|
|
248
|
+
const [configAccount, epochAccount] = await Promise.all([
|
|
249
|
+
this.getAccount(configPda),
|
|
250
|
+
this.getAccount(epochSettingsPda),
|
|
251
|
+
]);
|
|
252
|
+
const config = configAccount.exists
|
|
253
|
+
? deserializeArioConfig(Buffer.from(configAccount.data))
|
|
254
|
+
: null;
|
|
255
|
+
const epoch = epochAccount.exists
|
|
256
|
+
? deserializeEpochSettings(Buffer.from(epochAccount.data))
|
|
257
|
+
: null;
|
|
258
|
+
return {
|
|
259
|
+
Ticker: 'ARIO',
|
|
260
|
+
Name: 'AR.IO',
|
|
261
|
+
Logo: '',
|
|
262
|
+
Denomination: 6,
|
|
263
|
+
Handlers: [],
|
|
264
|
+
LastCreatedEpochIndex: 0,
|
|
265
|
+
LastDistributedEpochIndex: 0,
|
|
266
|
+
...(config
|
|
267
|
+
? {
|
|
268
|
+
totalSupply: config.totalSupply,
|
|
269
|
+
protocolBalance: config.protocolBalance,
|
|
270
|
+
}
|
|
271
|
+
: {}),
|
|
272
|
+
...(epoch ? { epochSettings: epoch } : {}),
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
async getTokenSupply() {
|
|
276
|
+
const [configPda] = await getArioConfigPDA(this.coreProgram);
|
|
277
|
+
const [garSettingsPda] = await getGarSettingsPDA(this.garProgram);
|
|
278
|
+
const [configAccount, garSettingsAccount] = await Promise.all([
|
|
279
|
+
this.getAccount(configPda),
|
|
280
|
+
this.getAccount(garSettingsPda),
|
|
281
|
+
]);
|
|
282
|
+
if (!configAccount.exists) {
|
|
283
|
+
throw new Error('ArioConfig account not found');
|
|
284
|
+
}
|
|
285
|
+
const config = deserializeArioConfig(Buffer.from(configAccount.data));
|
|
286
|
+
// Supply counters from GatewaySettings (staked/delegated/withdrawn).
|
|
287
|
+
// Falls back to 0 if GatewaySettings doesn't exist yet or is at the
|
|
288
|
+
// old size (pre-supply-counters layout).
|
|
289
|
+
let staked = 0;
|
|
290
|
+
let delegated = 0;
|
|
291
|
+
let withdrawn = 0;
|
|
292
|
+
if (garSettingsAccount.exists) {
|
|
293
|
+
try {
|
|
294
|
+
const counters = deserializeGarSupplyCounters(Buffer.from(garSettingsAccount.data));
|
|
295
|
+
staked = counters.totalStaked;
|
|
296
|
+
delegated = counters.totalDelegated;
|
|
297
|
+
withdrawn = counters.totalWithdrawn;
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
// Old-layout account without supply counters — fall back to 0
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
total: config.totalSupply,
|
|
305
|
+
circulating: config.circulatingSupply,
|
|
306
|
+
locked: config.lockedSupply,
|
|
307
|
+
staked,
|
|
308
|
+
delegated,
|
|
309
|
+
withdrawn,
|
|
310
|
+
protocolBalance: config.protocolBalance,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
// =========================================
|
|
314
|
+
// Balance read methods
|
|
315
|
+
// =========================================
|
|
316
|
+
/**
|
|
317
|
+
* Resolve the ARIO SPL mint address from the on-chain `ArioConfig`.
|
|
318
|
+
*
|
|
319
|
+
* `ArioConfig` layout: [8 disc][32 authority][32 mint][...]. We decode
|
|
320
|
+
* the mint at offset 40 and cache it for the lifetime of this instance —
|
|
321
|
+
* the mint never changes once the protocol is deployed.
|
|
322
|
+
*/
|
|
323
|
+
async getArioMint() {
|
|
324
|
+
if (this._arioMint)
|
|
325
|
+
return this._arioMint;
|
|
326
|
+
const [configPda] = await getArioConfigPDA(this.coreProgram);
|
|
327
|
+
const account = await this.getAccount(configPda);
|
|
328
|
+
if (!account.exists) {
|
|
329
|
+
throw new Error(`ArioConfig not found at ${configPda} on coreProgram ${this.coreProgram} — is the program deployed and initialized?`);
|
|
330
|
+
}
|
|
331
|
+
const data = Buffer.from(account.data);
|
|
332
|
+
const mint = addressDecoder.decode(data.subarray(40, 72));
|
|
333
|
+
this._arioMint = mint;
|
|
334
|
+
return mint;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Liquid ARIO balance for an address.
|
|
338
|
+
*
|
|
339
|
+
* On Solana the ARIO token is a real SPL mint, so the canonical liquid
|
|
340
|
+
* balance lives on the user's Associated Token Account — *not* the
|
|
341
|
+
* `ario-core::Balance` PDA. The Balance PDA is only populated by the
|
|
342
|
+
* one-shot AO-to-Solana migration importer for legacy snapshot accounts;
|
|
343
|
+
* spending it requires a separate claim flow that mints/transfers SPL
|
|
344
|
+
* tokens to the user's ATA. Steady-state instructions like `buy_name`,
|
|
345
|
+
* gateway/delegate stake, and ARIO transfers all move SPL tokens, so the
|
|
346
|
+
* ATA is what every UI and on-chain caller cares about.
|
|
347
|
+
*
|
|
348
|
+
* Returns 0 if the user has no ATA initialized yet.
|
|
349
|
+
*/
|
|
350
|
+
async getBalance({ address: owner, }) {
|
|
351
|
+
const mint = await this.getArioMint();
|
|
352
|
+
const ata = await getAssociatedTokenAddressKit(mint, address(owner));
|
|
353
|
+
const account = await this.getAccount(ata);
|
|
354
|
+
if (!account.exists)
|
|
355
|
+
return 0;
|
|
356
|
+
// SPL Token Account layout: [0..32]=mint, [32..64]=owner, [64..72]=amount(u64 LE), …
|
|
357
|
+
const data = account.data;
|
|
358
|
+
if (data.length < 72)
|
|
359
|
+
return 0;
|
|
360
|
+
// NOTE: avoid `Buffer.readBigUInt64LE` — some browser bundlers (notably
|
|
361
|
+
// arns-react's Vite output) strip the BigInt readers from the
|
|
362
|
+
// `buffer@6.0.3` shim's prototype. Manual little-endian u64 decode is
|
|
363
|
+
// portable across every JS runtime.
|
|
364
|
+
let amount = 0n;
|
|
365
|
+
for (let i = 7; i >= 0; i--) {
|
|
366
|
+
amount = (amount << 8n) | BigInt(data[64 + i]);
|
|
367
|
+
}
|
|
368
|
+
// ARIO supply caps at 1B * 1e6 mARIO ≈ 2^50, well under Number.MAX_SAFE_INTEGER.
|
|
369
|
+
return Number(amount);
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Enumerate liquid ARIO balances by querying the SPL Token program for
|
|
373
|
+
* every initialized token account on the ARIO mint.
|
|
374
|
+
*
|
|
375
|
+
* Filters: token-account size = 165, mint at offset 0. We then decode
|
|
376
|
+
* `owner` (offset 32) and `amount` (offset 64) from each.
|
|
377
|
+
*/
|
|
378
|
+
async getBalances(params) {
|
|
379
|
+
const mint = await this.getArioMint();
|
|
380
|
+
const filters = [
|
|
381
|
+
{ dataSize: 165n },
|
|
382
|
+
{
|
|
383
|
+
memcmp: {
|
|
384
|
+
offset: 0n,
|
|
385
|
+
bytes: mint,
|
|
386
|
+
encoding: 'base58',
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
];
|
|
390
|
+
const result = (await this.rpc
|
|
391
|
+
.getProgramAccounts(TOKEN_PROGRAM_ADDRESS, {
|
|
392
|
+
commitment: this.commitment,
|
|
393
|
+
encoding: 'base64',
|
|
394
|
+
filters,
|
|
395
|
+
})
|
|
396
|
+
.send());
|
|
397
|
+
const items = [];
|
|
398
|
+
for (const entry of result) {
|
|
399
|
+
try {
|
|
400
|
+
const data = Buffer.from(entry.account.data[0], 'base64');
|
|
401
|
+
if (data.length < 72)
|
|
402
|
+
continue;
|
|
403
|
+
const ownerAddress = addressDecoder.decode(data.subarray(32, 64));
|
|
404
|
+
const amount = Number(data.readBigUInt64LE(64));
|
|
405
|
+
if (amount > 0) {
|
|
406
|
+
items.push({ address: ownerAddress, balance: amount });
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
// Skip malformed accounts
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return paginate(items, params);
|
|
414
|
+
}
|
|
415
|
+
// =========================================
|
|
416
|
+
// Vault read methods
|
|
417
|
+
// =========================================
|
|
418
|
+
async getVault({ address: owner, vaultId, }) {
|
|
419
|
+
const [pda] = await getVaultPDA(address(owner), BigInt(vaultId), this.coreProgram);
|
|
420
|
+
const account = await this.getAccount(pda);
|
|
421
|
+
if (!account.exists) {
|
|
422
|
+
throw new Error(`Vault not found for ${owner}:${vaultId}`);
|
|
423
|
+
}
|
|
424
|
+
const vault = deserializeVault(Buffer.from(account.data));
|
|
425
|
+
return {
|
|
426
|
+
balance: vault.balance,
|
|
427
|
+
startTimestamp: secToMs(vault.startTimestamp),
|
|
428
|
+
endTimestamp: secToMs(vault.endTimestamp),
|
|
429
|
+
controller: vault.controller,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
async getVaults(params) {
|
|
433
|
+
const accounts = await this.getAccountsByDiscriminator(this.coreProgram, VAULT_DISCRIMINATOR);
|
|
434
|
+
const items = [];
|
|
435
|
+
for (const { pubkey, data } of accounts) {
|
|
436
|
+
try {
|
|
437
|
+
const vault = deserializeVault(data);
|
|
438
|
+
items.push({
|
|
439
|
+
address: vault.owner,
|
|
440
|
+
vaultId: pubkey,
|
|
441
|
+
balance: vault.balance,
|
|
442
|
+
startTimestamp: secToMs(vault.startTimestamp),
|
|
443
|
+
endTimestamp: secToMs(vault.endTimestamp),
|
|
444
|
+
controller: vault.controller,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
catch {
|
|
448
|
+
// Skip malformed accounts
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return paginate(items, params);
|
|
452
|
+
}
|
|
453
|
+
// =========================================
|
|
454
|
+
// Gateway read methods
|
|
455
|
+
// =========================================
|
|
456
|
+
async getGateway({ address: addr }) {
|
|
457
|
+
const [pda] = await getGatewayPDA(address(addr), this.garProgram);
|
|
458
|
+
const account = await this.getAccount(pda);
|
|
459
|
+
if (!account.exists) {
|
|
460
|
+
throw new Error(`Gateway not found for operator ${addr}`);
|
|
461
|
+
}
|
|
462
|
+
const gw = deserializeGateway(Buffer.from(account.data));
|
|
463
|
+
const { operator: _, ...gateway } = gw;
|
|
464
|
+
return toMsTimestamps(gateway);
|
|
465
|
+
}
|
|
466
|
+
async getGateways(params) {
|
|
467
|
+
const [registryPda] = await getGatewayRegistryPDA(this.garProgram);
|
|
468
|
+
const registryAccount = await this.getAccount(registryPda);
|
|
469
|
+
if (!registryAccount.exists) {
|
|
470
|
+
return paginate([], params);
|
|
471
|
+
}
|
|
472
|
+
const registryData = Buffer.from(registryAccount.data);
|
|
473
|
+
const count = registryData.readUInt32LE(40);
|
|
474
|
+
const slotsOffset = 48;
|
|
475
|
+
// GatewaySlot = address(32) + composite_weight(8) + start_timestamp(8)
|
|
476
|
+
// + status(1) + _padding(7) = 56 bytes (see ario-gar
|
|
477
|
+
// state/mod.rs::GatewaySlot). A previous off-by-16-bytes-per-slot
|
|
478
|
+
// stride silently read garbage for slots 1+, returning at most
|
|
479
|
+
// one gateway no matter how many had joined.
|
|
480
|
+
const SLOT_STRIDE = 56;
|
|
481
|
+
const gatewayAddresses = [];
|
|
482
|
+
for (let i = 0; i < count && i < 3000; i++) {
|
|
483
|
+
const slotOffset = slotsOffset + i * SLOT_STRIDE;
|
|
484
|
+
const addr = addressDecoder.decode(registryData.subarray(slotOffset, slotOffset + 32));
|
|
485
|
+
if (addr !== DEFAULT_ADDRESS) {
|
|
486
|
+
gatewayAddresses.push(addr);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// Batch fetch gateway PDAs (kit has no hard limit but keep 100-at-a-time
|
|
490
|
+
// for sensible RPC request sizes).
|
|
491
|
+
const allItems = [];
|
|
492
|
+
for (let i = 0; i < gatewayAddresses.length; i += 100) {
|
|
493
|
+
const batch = gatewayAddresses.slice(i, i + 100);
|
|
494
|
+
const pdas = await Promise.all(batch.map(async (addr) => (await getGatewayPDA(addr, this.garProgram))[0]));
|
|
495
|
+
const accounts = await fetchEncodedAccounts(this.rpc, pdas, {
|
|
496
|
+
commitment: this.commitment,
|
|
497
|
+
});
|
|
498
|
+
for (const acct of accounts) {
|
|
499
|
+
if (!acct.exists)
|
|
500
|
+
continue;
|
|
501
|
+
try {
|
|
502
|
+
const gw = deserializeGateway(Buffer.from(acct.data));
|
|
503
|
+
allItems.push(toMsTimestamps({ ...gw, gatewayAddress: gw.operator }));
|
|
504
|
+
}
|
|
505
|
+
catch {
|
|
506
|
+
// Skip malformed
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return paginate(allItems, params);
|
|
511
|
+
}
|
|
512
|
+
async getGatewayDelegates(params) {
|
|
513
|
+
const gateway = address(params.address);
|
|
514
|
+
// Filter delegations by gateway pubkey at offset 8 (after discriminator)
|
|
515
|
+
const accounts = await this.getAccountsByDiscriminator(this.garProgram, DELEGATION_DISCRIMINATOR, [
|
|
516
|
+
{
|
|
517
|
+
memcmp: { offset: 8n, bytes: gateway, encoding: 'base58' },
|
|
518
|
+
},
|
|
519
|
+
]);
|
|
520
|
+
// Fetch this gateway's current reward accumulator so we can return live
|
|
521
|
+
// balances (raw `Delegation.amount` is stale between settlements — see
|
|
522
|
+
// INVARIANTS.md and `computeLiveDelegationBalance`).
|
|
523
|
+
const accumulators = await this.getGatewayAccumulators([gateway]);
|
|
524
|
+
const cumulative = accumulators.get(gateway) ?? 0n;
|
|
525
|
+
const items = [];
|
|
526
|
+
for (const { data } of accounts) {
|
|
527
|
+
try {
|
|
528
|
+
const del = deserializeDelegation(data);
|
|
529
|
+
items.push({
|
|
530
|
+
address: del.delegator,
|
|
531
|
+
delegatedStake: computeLiveDelegationBalance({
|
|
532
|
+
delegatedStake: del.delegatedStake,
|
|
533
|
+
rewardDebt: del.rewardDebt,
|
|
534
|
+
cumulativeRewardPerToken: cumulative,
|
|
535
|
+
}),
|
|
536
|
+
startTimestamp: secToMs(del.startTimestamp),
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
catch {
|
|
540
|
+
// Skip malformed
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return paginate(items, params);
|
|
544
|
+
}
|
|
545
|
+
async getGatewayDelegateAllowList(params) {
|
|
546
|
+
const gateway = address(params.address);
|
|
547
|
+
// Filter allowlist entries by gateway pubkey at offset 8
|
|
548
|
+
const accounts = await this.getAccountsByDiscriminator(this.garProgram, ALLOWLIST_ENTRY_DISCRIMINATOR, [
|
|
549
|
+
{
|
|
550
|
+
memcmp: { offset: 8n, bytes: gateway, encoding: 'base58' },
|
|
551
|
+
},
|
|
552
|
+
]);
|
|
553
|
+
const items = [];
|
|
554
|
+
for (const { data } of accounts) {
|
|
555
|
+
try {
|
|
556
|
+
const entry = deserializeAllowlist(data);
|
|
557
|
+
items.push(entry.delegate);
|
|
558
|
+
}
|
|
559
|
+
catch {
|
|
560
|
+
// Skip malformed
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return paginate(items, params);
|
|
564
|
+
}
|
|
565
|
+
async getDelegations(params) {
|
|
566
|
+
const delegator = address(params.address);
|
|
567
|
+
// Filter delegations by delegator pubkey at offset 40 (8 disc + 32 gateway)
|
|
568
|
+
const accounts = await this.getAccountsByDiscriminator(this.garProgram, DELEGATION_DISCRIMINATOR, [
|
|
569
|
+
{
|
|
570
|
+
memcmp: {
|
|
571
|
+
offset: 40n,
|
|
572
|
+
bytes: delegator,
|
|
573
|
+
encoding: 'base58',
|
|
574
|
+
},
|
|
575
|
+
},
|
|
576
|
+
]);
|
|
577
|
+
const decoded = [];
|
|
578
|
+
for (const { pubkey, data } of accounts) {
|
|
579
|
+
try {
|
|
580
|
+
decoded.push({
|
|
581
|
+
pubkey: pubkey,
|
|
582
|
+
del: deserializeDelegation(data),
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
catch {
|
|
586
|
+
// Skip malformed
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
// Batch-fetch each referenced gateway's reward accumulator so we can
|
|
590
|
+
// return live balances. See INVARIANTS.md and `computeLiveDelegationBalance`.
|
|
591
|
+
const accumulators = await this.getGatewayAccumulators(decoded.map(({ del }) => del.gateway));
|
|
592
|
+
const items = decoded.map(({ pubkey, del }) => ({
|
|
593
|
+
type: 'stake',
|
|
594
|
+
gatewayAddress: del.gateway,
|
|
595
|
+
delegationId: pubkey,
|
|
596
|
+
startTimestamp: secToMs(del.startTimestamp),
|
|
597
|
+
balance: computeLiveDelegationBalance({
|
|
598
|
+
delegatedStake: del.delegatedStake,
|
|
599
|
+
rewardDebt: del.rewardDebt,
|
|
600
|
+
cumulativeRewardPerToken: accumulators.get(del.gateway) ?? 0n,
|
|
601
|
+
}),
|
|
602
|
+
}));
|
|
603
|
+
return paginate(items, params);
|
|
604
|
+
}
|
|
605
|
+
async getAllowedDelegates(params) {
|
|
606
|
+
return this.getGatewayDelegateAllowList(params);
|
|
607
|
+
}
|
|
608
|
+
async getGatewayVaults(params) {
|
|
609
|
+
const gateway = address(params.address);
|
|
610
|
+
// Withdrawal: disc(8) + owner(32) + withdrawal_id(8) + gateway(32)
|
|
611
|
+
const accounts = await this.getAccountsByDiscriminator(this.garProgram, WITHDRAWAL_DISCRIMINATOR, [
|
|
612
|
+
{
|
|
613
|
+
memcmp: { offset: 48n, bytes: gateway, encoding: 'base58' },
|
|
614
|
+
},
|
|
615
|
+
]);
|
|
616
|
+
const items = [];
|
|
617
|
+
for (const { pubkey, data } of accounts) {
|
|
618
|
+
try {
|
|
619
|
+
const w = deserializeWithdrawal(data);
|
|
620
|
+
if (!w.isDelegate) {
|
|
621
|
+
items.push({
|
|
622
|
+
cursorId: pubkey,
|
|
623
|
+
vaultId: w.vaultId,
|
|
624
|
+
balance: w.balance,
|
|
625
|
+
startTimestamp: secToMs(w.startTimestamp),
|
|
626
|
+
endTimestamp: secToMs(w.endTimestamp),
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
catch {
|
|
631
|
+
// Skip malformed
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return paginate(items, params);
|
|
635
|
+
}
|
|
636
|
+
// =========================================
|
|
637
|
+
// ArNS read methods
|
|
638
|
+
// =========================================
|
|
639
|
+
async getArNSRecord({ name }) {
|
|
640
|
+
const [pda] = await getArnsRecordPDA(name, this.arnsProgram);
|
|
641
|
+
const account = await this.getAccount(pda);
|
|
642
|
+
if (!account.exists) {
|
|
643
|
+
throw new Error(`ArNS record not found: ${name}`);
|
|
644
|
+
}
|
|
645
|
+
const record = deserializeArnsRecord(Buffer.from(account.data));
|
|
646
|
+
const { name: _, owner: __, ...nameData } = record;
|
|
647
|
+
return nameData;
|
|
648
|
+
}
|
|
649
|
+
async getArNSRecords(params) {
|
|
650
|
+
// `processId` is the only filter the AO backend supports today and the
|
|
651
|
+
// only one that maps to a fixed-offset memcmp on `ArnsRecord` (the
|
|
652
|
+
// `ant` field, see `ARNS_RECORD_ANT_OFFSET`). When supplied we
|
|
653
|
+
// dispatch through the bulk-by-mint path so the RPC does the
|
|
654
|
+
// filtering instead of streaming every record back to the client.
|
|
655
|
+
const filterMints = normalizeProcessIdFilter(params?.filters?.processId);
|
|
656
|
+
if (filterMints.length > 0) {
|
|
657
|
+
const items = await this.fetchArnsRecordsByAntMints(filterMints);
|
|
658
|
+
return paginate(items, params);
|
|
659
|
+
}
|
|
660
|
+
const accounts = await this.getAccountsByDiscriminator(this.arnsProgram, ARNS_RECORD_DISCRIMINATOR);
|
|
661
|
+
const items = [];
|
|
662
|
+
for (const { data } of accounts) {
|
|
663
|
+
try {
|
|
664
|
+
items.push(arnsRecordToWithName(deserializeArnsRecord(data)));
|
|
665
|
+
}
|
|
666
|
+
catch {
|
|
667
|
+
// Skip accounts that don't match
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
return paginate(items, params);
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Fetch every `ArnsRecord` whose `ant` field equals one of `mints`.
|
|
674
|
+
*
|
|
675
|
+
* Issues one `getProgramAccounts` per mint with a memcmp filter at
|
|
676
|
+
* `ARNS_RECORD_ANT_OFFSET`, in parallel. Cheaper than scanning the
|
|
677
|
+
* whole registry as soon as the caller has fewer mints than the
|
|
678
|
+
* registry has records (today the break-even is ≈ a few hundred
|
|
679
|
+
* mints against ≈ 4k records, and rises as the registry grows).
|
|
680
|
+
*
|
|
681
|
+
* The shape mirrors `getArNSRecord` / `getArNSRecords` — same
|
|
682
|
+
* `AoArNSNameDataWithName` items, no pagination wrapper. Callers
|
|
683
|
+
* that want pagination should drive it via `getArNSRecords({
|
|
684
|
+
* filters: { processId: mints } })` instead.
|
|
685
|
+
*/
|
|
686
|
+
async getArNSRecordsByAntMints({ mints, }) {
|
|
687
|
+
return this.fetchArnsRecordsByAntMints(mints);
|
|
688
|
+
}
|
|
689
|
+
async fetchArnsRecordsByAntMints(mints) {
|
|
690
|
+
const unique = Array.from(new Set(mints));
|
|
691
|
+
if (unique.length === 0)
|
|
692
|
+
return [];
|
|
693
|
+
// Parallel fan-out: one filtered gPA per mint. Each request is
|
|
694
|
+
// selective (matches at most one record on a healthy registry),
|
|
695
|
+
// so the marginal cost is mostly the round trip; major RPCs index
|
|
696
|
+
// memcmp filters at stable offsets, keeping this O(N) in network
|
|
697
|
+
// round trips rather than O(N) in registry size.
|
|
698
|
+
const perMint = await Promise.all(unique.map((mint) => this.getAccountsByDiscriminator(this.arnsProgram, ARNS_RECORD_DISCRIMINATOR, [
|
|
699
|
+
{
|
|
700
|
+
memcmp: {
|
|
701
|
+
offset: BigInt(ARNS_RECORD_ANT_OFFSET),
|
|
702
|
+
bytes: mint,
|
|
703
|
+
encoding: 'base58',
|
|
704
|
+
},
|
|
705
|
+
},
|
|
706
|
+
])));
|
|
707
|
+
const items = [];
|
|
708
|
+
const seen = new Set();
|
|
709
|
+
for (const accounts of perMint) {
|
|
710
|
+
for (const { pubkey, data } of accounts) {
|
|
711
|
+
const key = String(pubkey);
|
|
712
|
+
if (seen.has(key))
|
|
713
|
+
continue;
|
|
714
|
+
seen.add(key);
|
|
715
|
+
try {
|
|
716
|
+
items.push(arnsRecordToWithName(deserializeArnsRecord(data)));
|
|
717
|
+
}
|
|
718
|
+
catch {
|
|
719
|
+
// Skip malformed
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return items;
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Resolve every ArNS record currently controlled by `address`.
|
|
727
|
+
*
|
|
728
|
+
* Mirrors the AO backend: walk the on-chain ANT ACL for the wallet
|
|
729
|
+
* (`Owned ∪ Controlled`), then issue point-queries against the
|
|
730
|
+
* ArNS registry for those mints. This is semantically *not* a query
|
|
731
|
+
* over `ArnsRecord.owner` — that field is a write-once "purchase
|
|
732
|
+
* receipt" and never reflects current control on Solana (see
|
|
733
|
+
* ISSUES.md). Authoritative control flows through the ANT NFT
|
|
734
|
+
* owner / `AntControllers`, which is exactly what the ACL
|
|
735
|
+
* indexes.
|
|
736
|
+
*/
|
|
737
|
+
async getArNSRecordsForAddress(params) {
|
|
738
|
+
const registry = new SolanaANTRegistryReadable({
|
|
739
|
+
rpc: this.rpc,
|
|
740
|
+
commitment: this.commitment,
|
|
741
|
+
logger: this.logger,
|
|
742
|
+
antProgramId: this.antProgram,
|
|
743
|
+
});
|
|
744
|
+
const { Owned = [], Controlled = [] } = await registry.accessControlList({
|
|
745
|
+
address: params.address,
|
|
746
|
+
});
|
|
747
|
+
const mints = Array.from(new Set([...Owned, ...Controlled]));
|
|
748
|
+
if (mints.length === 0) {
|
|
749
|
+
return {
|
|
750
|
+
items: [],
|
|
751
|
+
hasMore: false,
|
|
752
|
+
nextCursor: undefined,
|
|
753
|
+
limit: params.limit ?? 100,
|
|
754
|
+
totalItems: 0,
|
|
755
|
+
sortOrder: params.sortOrder ?? 'asc',
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
const items = await this.fetchArnsRecordsByAntMints(mints);
|
|
759
|
+
return paginate(items, params);
|
|
760
|
+
}
|
|
761
|
+
async getArNSReservedNames(params) {
|
|
762
|
+
const accounts = await this.getAccountsByDiscriminator(this.arnsProgram, RESERVED_NAME_DISCRIMINATOR);
|
|
763
|
+
const items = [];
|
|
764
|
+
for (const { data } of accounts) {
|
|
765
|
+
try {
|
|
766
|
+
const reserved = deserializeReservedName(data);
|
|
767
|
+
items.push({
|
|
768
|
+
name: reserved.name,
|
|
769
|
+
target: reserved.target,
|
|
770
|
+
endTimestamp: typeof reserved.endTimestamp === 'number'
|
|
771
|
+
? secToMs(reserved.endTimestamp)
|
|
772
|
+
: reserved.endTimestamp,
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
catch {
|
|
776
|
+
// Skip malformed
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
return paginate(items, params);
|
|
780
|
+
}
|
|
781
|
+
async getArNSReservedName({ name, }) {
|
|
782
|
+
const [pda] = await getReservedNamePDA(name, this.arnsProgram);
|
|
783
|
+
const account = await this.getAccount(pda);
|
|
784
|
+
if (!account.exists) {
|
|
785
|
+
throw new Error(`Reserved name not found: ${name}`);
|
|
786
|
+
}
|
|
787
|
+
const reserved = deserializeReservedName(Buffer.from(account.data));
|
|
788
|
+
return {
|
|
789
|
+
target: reserved.target,
|
|
790
|
+
endTimestamp: typeof reserved.endTimestamp === 'number'
|
|
791
|
+
? secToMs(reserved.endTimestamp)
|
|
792
|
+
: reserved.endTimestamp,
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
async getArNSReturnedNames(params) {
|
|
796
|
+
const accounts = await this.getAccountsByDiscriminator(this.arnsProgram, RETURNED_NAME_DISCRIMINATOR);
|
|
797
|
+
const items = [];
|
|
798
|
+
for (const { data } of accounts) {
|
|
799
|
+
try {
|
|
800
|
+
items.push(toMsTimestamps(deserializeReturnedName(data)));
|
|
801
|
+
}
|
|
802
|
+
catch {
|
|
803
|
+
// Skip malformed
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
return paginate(items, params);
|
|
807
|
+
}
|
|
808
|
+
async getArNSReturnedName({ name, }) {
|
|
809
|
+
const [pda] = await getReturnedNamePDA(name, this.arnsProgram);
|
|
810
|
+
const account = await this.getAccount(pda);
|
|
811
|
+
if (!account.exists) {
|
|
812
|
+
throw new Error(`Returned name not found: ${name}`);
|
|
813
|
+
}
|
|
814
|
+
return toMsTimestamps(deserializeReturnedName(Buffer.from(account.data)));
|
|
815
|
+
}
|
|
816
|
+
// =========================================
|
|
817
|
+
// Epoch read methods
|
|
818
|
+
// =========================================
|
|
819
|
+
async getEpochSettings() {
|
|
820
|
+
const [pda] = await getEpochSettingsPDA(this.garProgram);
|
|
821
|
+
const account = await this.getAccount(pda);
|
|
822
|
+
if (!account.exists) {
|
|
823
|
+
throw new Error('Epoch settings account not found');
|
|
824
|
+
}
|
|
825
|
+
return deserializeEpochSettings(Buffer.from(account.data));
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Resolve an EpochInput to an epoch index number.
|
|
829
|
+
* - undefined: returns current epoch index from EpochSettings
|
|
830
|
+
* - { epochIndex }: returns directly
|
|
831
|
+
* - { timestamp }: computes from genesis timestamp and epoch duration
|
|
832
|
+
*/
|
|
833
|
+
async resolveEpochIndex(epoch) {
|
|
834
|
+
if (epoch && 'epochIndex' in epoch) {
|
|
835
|
+
return epoch.epochIndex;
|
|
836
|
+
}
|
|
837
|
+
const [pda] = await getEpochSettingsPDA(this.garProgram);
|
|
838
|
+
const account = await this.getAccount(pda);
|
|
839
|
+
if (!account.exists)
|
|
840
|
+
throw new Error('EpochSettings account not found');
|
|
841
|
+
const settings = deserializeEpochSettingsFull(Buffer.from(account.data));
|
|
842
|
+
if (!epoch) {
|
|
843
|
+
// On-chain `current_epoch_index` is "NEXT epoch to be created"
|
|
844
|
+
// (incremented inside `create_epoch` AFTER the PDA is initialized
|
|
845
|
+
// — see programs/ario-gar/src/instructions/epoch.rs:161). The
|
|
846
|
+
// currently-active epoch is therefore one back. Floor at 0 for
|
|
847
|
+
// the pre-bootstrap edge case where no epochs have been created
|
|
848
|
+
// yet. Without this adjustment, every call to getEpoch(undefined)
|
|
849
|
+
// sits in the cranker's close_epoch ↔ create_epoch gap and throws
|
|
850
|
+
// "Epoch N not found" — which broke ContractEpochSource on a
|
|
851
|
+
// live cluster (May 2026 devnet).
|
|
852
|
+
return Math.max(0, settings.currentEpochIndex - 1);
|
|
853
|
+
}
|
|
854
|
+
// { timestamp } — compute epoch index. The public API takes `timestamp`
|
|
855
|
+
// in JS milliseconds (matching the AO contract convention), but
|
|
856
|
+
// genesisTimestamp/epochDuration come straight off chain in seconds, so
|
|
857
|
+
// normalize to seconds before doing the division.
|
|
858
|
+
const tsSeconds = Math.floor(epoch.timestamp / 1000);
|
|
859
|
+
const elapsed = tsSeconds - settings.genesisTimestamp;
|
|
860
|
+
return Math.floor(elapsed / settings.epochDuration);
|
|
861
|
+
}
|
|
862
|
+
/** Fetch and deserialize an Epoch account by index */
|
|
863
|
+
async fetchEpoch(epochIndex) {
|
|
864
|
+
const [pda] = await getEpochPDA(epochIndex, this.garProgram);
|
|
865
|
+
const account = await this.getAccount(pda);
|
|
866
|
+
if (!account.exists) {
|
|
867
|
+
throw new Error(`Epoch ${epochIndex} not found`);
|
|
868
|
+
}
|
|
869
|
+
return deserializeEpoch(Buffer.from(account.data));
|
|
870
|
+
}
|
|
871
|
+
async getEpoch(epoch) {
|
|
872
|
+
const epochIndex = await this.resolveEpochIndex(epoch);
|
|
873
|
+
const epochData = await this.fetchEpoch(epochIndex);
|
|
874
|
+
// Build prescribed observers list (only up to observerCount)
|
|
875
|
+
const prescribedObservers = [];
|
|
876
|
+
for (let i = 0; i < epochData.observerCount; i++) {
|
|
877
|
+
const observerAddress = epochData.prescribedObservers[i];
|
|
878
|
+
const gatewayAddress = epochData.prescribedObserverGateways[i];
|
|
879
|
+
if (observerAddress === DEFAULT_ADDRESS)
|
|
880
|
+
continue;
|
|
881
|
+
// Try to fetch gateway data for weights
|
|
882
|
+
let weights = {
|
|
883
|
+
stakeWeight: 0,
|
|
884
|
+
tenureWeight: 0,
|
|
885
|
+
gatewayRewardRatioWeight: 0,
|
|
886
|
+
observerRewardRatioWeight: 0,
|
|
887
|
+
gatewayPerformanceRatio: 0,
|
|
888
|
+
observerPerformanceRatio: 0,
|
|
889
|
+
compositeWeight: 0,
|
|
890
|
+
normalizedCompositeWeight: 0,
|
|
891
|
+
};
|
|
892
|
+
let stake = 0;
|
|
893
|
+
let startTimestamp = 0;
|
|
894
|
+
try {
|
|
895
|
+
const gw = await this.getGateway({ address: gatewayAddress });
|
|
896
|
+
weights = gw.weights;
|
|
897
|
+
stake = gw.operatorStake;
|
|
898
|
+
// gw.startTimestamp is already converted to ms by getGateway.
|
|
899
|
+
startTimestamp = gw.startTimestamp;
|
|
900
|
+
}
|
|
901
|
+
catch {
|
|
902
|
+
// Gateway may no longer exist
|
|
903
|
+
}
|
|
904
|
+
prescribedObservers.push({
|
|
905
|
+
gatewayAddress: gatewayAddress,
|
|
906
|
+
observerAddress: observerAddress,
|
|
907
|
+
stake,
|
|
908
|
+
startTimestamp,
|
|
909
|
+
...weights,
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
// Build prescribed names list by resolving hashes → ArnsRecord PDAs
|
|
913
|
+
const prescribedNames = [];
|
|
914
|
+
const zeroHash = Buffer.alloc(32);
|
|
915
|
+
for (let i = 0; i < epochData.nameCount; i++) {
|
|
916
|
+
const nameHash = epochData.prescribedNameHashes[i];
|
|
917
|
+
if (!nameHash || nameHash.equals(zeroHash))
|
|
918
|
+
continue;
|
|
919
|
+
try {
|
|
920
|
+
const [recordPda] = await getArnsRecordPDAFromHash(nameHash, this.arnsProgram);
|
|
921
|
+
const recordAccount = await this.getAccount(recordPda);
|
|
922
|
+
if (recordAccount.exists) {
|
|
923
|
+
const record = deserializeArnsRecord(Buffer.from(recordAccount.data));
|
|
924
|
+
prescribedNames.push(record.name);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
catch {
|
|
928
|
+
// Record may have been removed
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
// Build observations from Observation PDAs
|
|
932
|
+
const observations = await this.getObservations({ epochIndex });
|
|
933
|
+
// Build distribution totals
|
|
934
|
+
const distributions = {
|
|
935
|
+
totalEligibleGateways: epochData.activeGatewayCount,
|
|
936
|
+
totalEligibleRewards: epochData.totalEligibleRewards,
|
|
937
|
+
totalEligibleObserverReward: epochData.perObserverReward * epochData.observerCount,
|
|
938
|
+
totalEligibleGatewayReward: epochData.perGatewayReward * epochData.activeGatewayCount,
|
|
939
|
+
};
|
|
940
|
+
return {
|
|
941
|
+
epochIndex,
|
|
942
|
+
startHeight: 0, // Solana doesn't use AO block heights
|
|
943
|
+
startTimestamp: secToMs(epochData.startTimestamp),
|
|
944
|
+
endTimestamp: secToMs(epochData.endTimestamp),
|
|
945
|
+
distributionTimestamp: secToMs(epochData.endTimestamp),
|
|
946
|
+
observations,
|
|
947
|
+
prescribedObservers,
|
|
948
|
+
prescribedNames,
|
|
949
|
+
distributions,
|
|
950
|
+
arnsStats: {
|
|
951
|
+
totalReturnedNames: 0,
|
|
952
|
+
totalActiveNames: 0,
|
|
953
|
+
totalGracePeriodNames: 0,
|
|
954
|
+
totalReservedNames: 0,
|
|
955
|
+
},
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
async getCurrentEpoch() {
|
|
959
|
+
return this.getEpoch(undefined);
|
|
960
|
+
}
|
|
961
|
+
async getPrescribedObservers(epoch) {
|
|
962
|
+
const epochData = await this.getEpoch(epoch);
|
|
963
|
+
return epochData.prescribedObservers;
|
|
964
|
+
}
|
|
965
|
+
async getPrescribedNames(epoch) {
|
|
966
|
+
const epochIndex = await this.resolveEpochIndex(epoch);
|
|
967
|
+
const epochData = await this.fetchEpoch(epochIndex);
|
|
968
|
+
const names = [];
|
|
969
|
+
const zeroHash = Buffer.alloc(32);
|
|
970
|
+
for (let i = 0; i < epochData.nameCount; i++) {
|
|
971
|
+
const nameHash = epochData.prescribedNameHashes[i];
|
|
972
|
+
if (!nameHash || nameHash.equals(zeroHash))
|
|
973
|
+
continue;
|
|
974
|
+
try {
|
|
975
|
+
const [recordPda] = await getArnsRecordPDAFromHash(nameHash, this.arnsProgram);
|
|
976
|
+
const recordAccount = await this.getAccount(recordPda);
|
|
977
|
+
if (recordAccount.exists) {
|
|
978
|
+
const record = deserializeArnsRecord(Buffer.from(recordAccount.data));
|
|
979
|
+
names.push(record.name);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
catch {
|
|
983
|
+
// Record may have been removed
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
return names;
|
|
987
|
+
}
|
|
988
|
+
async getObservations(epoch) {
|
|
989
|
+
const epochIndex = await this.resolveEpochIndex(epoch);
|
|
990
|
+
// Fetch all Observation accounts for this epoch
|
|
991
|
+
const epochIndexBuf = Buffer.alloc(8);
|
|
992
|
+
epochIndexBuf.writeBigUInt64LE(BigInt(epochIndex));
|
|
993
|
+
const accounts = await this.getAccountsByDiscriminator(this.garProgram, OBSERVATION_DISCRIMINATOR, [
|
|
994
|
+
{
|
|
995
|
+
memcmp: {
|
|
996
|
+
offset: 8n,
|
|
997
|
+
bytes: bs58.encode(epochIndexBuf),
|
|
998
|
+
encoding: 'base58',
|
|
999
|
+
},
|
|
1000
|
+
},
|
|
1001
|
+
]);
|
|
1002
|
+
const failureSummaries = {};
|
|
1003
|
+
const reports = {};
|
|
1004
|
+
// Read gateway registry to get index-to-address mapping (matches bitfield order)
|
|
1005
|
+
const gatewayAddresses = await this.getRegistryGatewayAddresses();
|
|
1006
|
+
for (const { data } of accounts) {
|
|
1007
|
+
try {
|
|
1008
|
+
const obs = deserializeObservation(data);
|
|
1009
|
+
reports[obs.observer] = obs.reportTxId;
|
|
1010
|
+
// Parse gateway_results bitmap — 1 = passed, 0 = failed (on-chain convention)
|
|
1011
|
+
for (let i = 0; i < obs.gatewayCount && i < gatewayAddresses.length; i++) {
|
|
1012
|
+
const byteIdx = Math.floor(i / 8);
|
|
1013
|
+
const bitIdx = i % 8;
|
|
1014
|
+
const passed = (obs.gatewayResults[byteIdx] >> bitIdx) & 1;
|
|
1015
|
+
if (!passed) {
|
|
1016
|
+
const gwAddr = gatewayAddresses[i];
|
|
1017
|
+
if (!failureSummaries[gwAddr]) {
|
|
1018
|
+
failureSummaries[gwAddr] = [];
|
|
1019
|
+
}
|
|
1020
|
+
failureSummaries[gwAddr].push(obs.observer);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
catch {
|
|
1025
|
+
// Skip malformed
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
return { failureSummaries, reports };
|
|
1029
|
+
}
|
|
1030
|
+
async getDistributions(epoch) {
|
|
1031
|
+
const epochIndex = await this.resolveEpochIndex(epoch);
|
|
1032
|
+
const epochData = await this.fetchEpoch(epochIndex);
|
|
1033
|
+
return {
|
|
1034
|
+
totalEligibleGateways: epochData.activeGatewayCount,
|
|
1035
|
+
totalEligibleRewards: epochData.totalEligibleRewards,
|
|
1036
|
+
totalEligibleObserverReward: epochData.perObserverReward * epochData.observerCount,
|
|
1037
|
+
totalEligibleGatewayReward: epochData.perGatewayReward * epochData.activeGatewayCount,
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
async getEligibleEpochRewards(epoch, params) {
|
|
1041
|
+
const epochIndex = await this.resolveEpochIndex(epoch);
|
|
1042
|
+
const epochData = await this.fetchEpoch(epochIndex);
|
|
1043
|
+
const items = [];
|
|
1044
|
+
// Each gateway operator gets a gateway reward
|
|
1045
|
+
const gatewayAccounts = await this.getAccountsByDiscriminator(this.garProgram, GATEWAY_DISCRIMINATOR);
|
|
1046
|
+
for (const { data } of gatewayAccounts) {
|
|
1047
|
+
try {
|
|
1048
|
+
const gw = deserializeGateway(data);
|
|
1049
|
+
if (gw.status !== 'joined')
|
|
1050
|
+
continue;
|
|
1051
|
+
items.push({
|
|
1052
|
+
type: 'operatorReward',
|
|
1053
|
+
recipient: gw.operator,
|
|
1054
|
+
eligibleReward: epochData.perGatewayReward,
|
|
1055
|
+
gatewayAddress: gw.operator,
|
|
1056
|
+
cursorId: gw.operator,
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
catch {
|
|
1060
|
+
// skip
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
// Each prescribed observer gets an observer reward
|
|
1064
|
+
for (let i = 0; i < epochData.observerCount; i++) {
|
|
1065
|
+
const observerAddr = epochData.prescribedObservers[i];
|
|
1066
|
+
const gatewayAddr = epochData.prescribedObserverGateways[i];
|
|
1067
|
+
if (observerAddr === DEFAULT_ADDRESS)
|
|
1068
|
+
continue;
|
|
1069
|
+
items.push({
|
|
1070
|
+
type: 'operatorReward',
|
|
1071
|
+
recipient: gatewayAddr,
|
|
1072
|
+
eligibleReward: epochData.perObserverReward,
|
|
1073
|
+
gatewayAddress: gatewayAddr,
|
|
1074
|
+
cursorId: `${gatewayAddr}-observer`,
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
return paginate(items, params);
|
|
1078
|
+
}
|
|
1079
|
+
// =========================================
|
|
1080
|
+
// Pricing / cost read methods
|
|
1081
|
+
// =========================================
|
|
1082
|
+
/**
|
|
1083
|
+
* Compute the token cost for an ArNS operation.
|
|
1084
|
+
*
|
|
1085
|
+
* Mirrors the Rust pricing functions in ario-arns/src/pricing.rs.
|
|
1086
|
+
* Uses BigInt for u128-equivalent overflow-safe arithmetic.
|
|
1087
|
+
*/
|
|
1088
|
+
async getTokenCost(params) {
|
|
1089
|
+
const [dfPda] = await getDemandFactorPDA(this.arnsProgram);
|
|
1090
|
+
const dfAccount = await this.getAccount(dfPda);
|
|
1091
|
+
if (!dfAccount.exists)
|
|
1092
|
+
throw new Error('DemandFactor account not found');
|
|
1093
|
+
const df = deserializeDemandFactor(Buffer.from(dfAccount.data));
|
|
1094
|
+
const name = params.name.toLowerCase();
|
|
1095
|
+
const nameLen = Math.min(Math.max(name.length, 1), 51);
|
|
1096
|
+
const baseFee = df.fees[nameLen - 1] ?? df.fees[df.fees.length - 1];
|
|
1097
|
+
// currentDemandFactor is already divided by RATE_SCALE in deserializer,
|
|
1098
|
+
// but we need the raw scaled value for integer math
|
|
1099
|
+
const demandFactorRaw = BigInt(Math.round(df.currentDemandFactor * RATE_SCALE));
|
|
1100
|
+
const scale = BigInt(RATE_SCALE);
|
|
1101
|
+
const bf = BigInt(baseFee);
|
|
1102
|
+
let cost;
|
|
1103
|
+
switch (params.intent) {
|
|
1104
|
+
case 'Buy-Name':
|
|
1105
|
+
case 'Buy-Record': {
|
|
1106
|
+
const purchaseType = params.type ?? 'lease';
|
|
1107
|
+
if (purchaseType === 'permabuy') {
|
|
1108
|
+
cost = (bf * demandFactorRaw * 5n) / scale;
|
|
1109
|
+
}
|
|
1110
|
+
else {
|
|
1111
|
+
const years = BigInt(params.years ?? 1);
|
|
1112
|
+
const annualPct = 200000n;
|
|
1113
|
+
const yearFactor = scale + annualPct * years;
|
|
1114
|
+
cost = (bf * demandFactorRaw * yearFactor) / scale / scale;
|
|
1115
|
+
}
|
|
1116
|
+
try {
|
|
1117
|
+
const returned = await this.getArNSReturnedName({ name });
|
|
1118
|
+
if (returned) {
|
|
1119
|
+
// returned.startTimestamp is in ms (public API convention),
|
|
1120
|
+
// so the rest of this comparison is in ms too.
|
|
1121
|
+
const now = Date.now();
|
|
1122
|
+
const elapsed = now - returned.startTimestamp;
|
|
1123
|
+
const duration = 14 * 86_400_000;
|
|
1124
|
+
if (elapsed < duration) {
|
|
1125
|
+
const remaining = BigInt(duration - elapsed);
|
|
1126
|
+
const dur = BigInt(duration);
|
|
1127
|
+
const pctRemaining = (remaining * scale) / dur;
|
|
1128
|
+
const multiplier = 50n * pctRemaining;
|
|
1129
|
+
cost = (cost * multiplier) / scale;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
catch {
|
|
1134
|
+
// Not a returned name — no premium
|
|
1135
|
+
}
|
|
1136
|
+
break;
|
|
1137
|
+
}
|
|
1138
|
+
case 'Extend-Lease': {
|
|
1139
|
+
const years = BigInt(params.years ?? 1);
|
|
1140
|
+
const annualPct = 200000n;
|
|
1141
|
+
cost = (bf * demandFactorRaw * annualPct * years) / scale / scale;
|
|
1142
|
+
break;
|
|
1143
|
+
}
|
|
1144
|
+
case 'Increase-Undername-Limit': {
|
|
1145
|
+
const qty = BigInt(params.quantity ?? 1);
|
|
1146
|
+
let isPermabuy = false;
|
|
1147
|
+
try {
|
|
1148
|
+
const record = await this.getArNSRecord({ name });
|
|
1149
|
+
isPermabuy = record.type === 'permabuy';
|
|
1150
|
+
}
|
|
1151
|
+
catch {
|
|
1152
|
+
// default to lease pricing
|
|
1153
|
+
}
|
|
1154
|
+
const pct = isPermabuy ? 5000n : 1000n;
|
|
1155
|
+
cost = (bf * demandFactorRaw * pct * qty) / scale / scale;
|
|
1156
|
+
break;
|
|
1157
|
+
}
|
|
1158
|
+
case 'Upgrade-Name': {
|
|
1159
|
+
const permabuyCost = (bf * demandFactorRaw * 5n) / scale;
|
|
1160
|
+
cost = permabuyCost;
|
|
1161
|
+
break;
|
|
1162
|
+
}
|
|
1163
|
+
case 'Primary-Name-Request': {
|
|
1164
|
+
const primaryBaseFee = BigInt(df.fees[50]);
|
|
1165
|
+
const annualPct = 200000n;
|
|
1166
|
+
const yearFactor = scale + annualPct;
|
|
1167
|
+
cost = (primaryBaseFee * demandFactorRaw * yearFactor) / scale / scale;
|
|
1168
|
+
break;
|
|
1169
|
+
}
|
|
1170
|
+
default:
|
|
1171
|
+
throw new Error(`Unknown intent: ${params.intent}`);
|
|
1172
|
+
}
|
|
1173
|
+
return Number(cost);
|
|
1174
|
+
}
|
|
1175
|
+
async getCostDetails(params) {
|
|
1176
|
+
const tokenCost = await this.getTokenCost(params);
|
|
1177
|
+
const discounts = [];
|
|
1178
|
+
if (params.fromAddress) {
|
|
1179
|
+
try {
|
|
1180
|
+
const gw = await this.getGateway({ address: params.fromAddress });
|
|
1181
|
+
if (gw.status === 'joined') {
|
|
1182
|
+
const discountAmount = Math.floor((tokenCost * 200_000) / RATE_SCALE);
|
|
1183
|
+
discounts.push({
|
|
1184
|
+
name: 'Gateway Operator',
|
|
1185
|
+
discountTotal: discountAmount,
|
|
1186
|
+
multiplier: 0.8,
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
catch {
|
|
1191
|
+
// Not a gateway operator — no discount
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
const totalDiscount = discounts.reduce((sum, d) => sum + d.discountTotal, 0);
|
|
1195
|
+
return {
|
|
1196
|
+
tokenCost: tokenCost - totalDiscount,
|
|
1197
|
+
discounts,
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
async getRegistrationFees() {
|
|
1201
|
+
const [dfPda] = await getDemandFactorPDA(this.arnsProgram);
|
|
1202
|
+
const account = await this.getAccount(dfPda);
|
|
1203
|
+
if (!account.exists) {
|
|
1204
|
+
throw new Error('DemandFactor account not found');
|
|
1205
|
+
}
|
|
1206
|
+
const df = deserializeDemandFactor(Buffer.from(account.data));
|
|
1207
|
+
const result = {};
|
|
1208
|
+
for (let len = 1; len <= 51; len++) {
|
|
1209
|
+
const baseFee = df.fees[len - 1] ?? 0;
|
|
1210
|
+
result[len] = {
|
|
1211
|
+
lease: {
|
|
1212
|
+
1: Math.floor(baseFee * (1 + 0.2 * 1) * df.currentDemandFactor),
|
|
1213
|
+
2: Math.floor(baseFee * (1 + 0.2 * 2) * df.currentDemandFactor),
|
|
1214
|
+
3: Math.floor(baseFee * (1 + 0.2 * 3) * df.currentDemandFactor),
|
|
1215
|
+
4: Math.floor(baseFee * (1 + 0.2 * 4) * df.currentDemandFactor),
|
|
1216
|
+
5: Math.floor(baseFee * (1 + 0.2 * 5) * df.currentDemandFactor),
|
|
1217
|
+
},
|
|
1218
|
+
permabuy: Math.floor(baseFee * 5 * df.currentDemandFactor),
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
return result;
|
|
1222
|
+
}
|
|
1223
|
+
async getDemandFactor() {
|
|
1224
|
+
const [pda] = await getDemandFactorPDA(this.arnsProgram);
|
|
1225
|
+
const account = await this.getAccount(pda);
|
|
1226
|
+
if (!account.exists) {
|
|
1227
|
+
throw new Error('DemandFactor account not found');
|
|
1228
|
+
}
|
|
1229
|
+
return deserializeDemandFactor(Buffer.from(account.data))
|
|
1230
|
+
.currentDemandFactor;
|
|
1231
|
+
}
|
|
1232
|
+
async getDemandFactorSettings() {
|
|
1233
|
+
const [pda] = await getDemandFactorPDA(this.arnsProgram);
|
|
1234
|
+
const account = await this.getAccount(pda);
|
|
1235
|
+
if (!account.exists) {
|
|
1236
|
+
throw new Error('DemandFactor account not found');
|
|
1237
|
+
}
|
|
1238
|
+
const df = deserializeDemandFactor(Buffer.from(account.data));
|
|
1239
|
+
return {
|
|
1240
|
+
periodZeroStartTimestamp: df.periodZeroStartTimestamp,
|
|
1241
|
+
movingAvgPeriodCount: 7,
|
|
1242
|
+
periodLengthMs: 86_400 * 1000,
|
|
1243
|
+
demandFactorBaseValue: 1,
|
|
1244
|
+
demandFactorMin: 0.5,
|
|
1245
|
+
demandFactorUpAdjustmentRate: 50,
|
|
1246
|
+
demandFactorDownAdjustmentRate: 25,
|
|
1247
|
+
maxPeriodsAtMinDemandFactor: df.consecutivePeriodsWithMinDemandFactor,
|
|
1248
|
+
criteria: 'revenue',
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
// =========================================
|
|
1252
|
+
// Primary name read methods
|
|
1253
|
+
// =========================================
|
|
1254
|
+
async getPrimaryName(params) {
|
|
1255
|
+
// On-chain `PrimaryName` stores only {owner, name, set_at}. The ANT mint
|
|
1256
|
+
// that AoPrimaryName.processId expects lives on the matching ArnsRecord
|
|
1257
|
+
// (looked up by the base name). Both lookup paths below deserialize the
|
|
1258
|
+
// on-chain account and then enrich with the ArnsRecord lookup.
|
|
1259
|
+
const baseNameOf = (n) => {
|
|
1260
|
+
const parts = n.toLowerCase().split('_');
|
|
1261
|
+
return parts.length === 2 ? parts[1] : parts[0];
|
|
1262
|
+
};
|
|
1263
|
+
const enrich = async (pn) => {
|
|
1264
|
+
const rec = await this.getArNSRecord({ name: baseNameOf(pn.name) });
|
|
1265
|
+
return { ...pn, processId: rec.processId };
|
|
1266
|
+
};
|
|
1267
|
+
if ('address' in params) {
|
|
1268
|
+
const [pda] = await getPrimaryNamePDA(address(params.address), this.coreProgram);
|
|
1269
|
+
const account = await this.getAccount(pda);
|
|
1270
|
+
if (!account.exists) {
|
|
1271
|
+
throw new Error(`Primary name not found for address ${params.address}`);
|
|
1272
|
+
}
|
|
1273
|
+
return enrich(deserializePrimaryName(Buffer.from(account.data)));
|
|
1274
|
+
}
|
|
1275
|
+
// Lookup by name — scan all primary name accounts
|
|
1276
|
+
const accounts = await this.getAccountsByDiscriminator(this.coreProgram, PRIMARY_NAME_DISCRIMINATOR);
|
|
1277
|
+
for (const { data } of accounts) {
|
|
1278
|
+
try {
|
|
1279
|
+
const pn = deserializePrimaryName(data);
|
|
1280
|
+
if (pn.name === params.name) {
|
|
1281
|
+
return enrich(pn);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
catch {
|
|
1285
|
+
// Skip malformed
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
throw new Error(`Primary name not found: ${params.name}`);
|
|
1289
|
+
}
|
|
1290
|
+
async getPrimaryNameRequest(params) {
|
|
1291
|
+
const [pda] = await getPrimaryNameRequestPDA(address(params.initiator), this.coreProgram);
|
|
1292
|
+
const account = await this.getAccount(pda);
|
|
1293
|
+
if (!account.exists) {
|
|
1294
|
+
throw new Error(`Primary name request not found for ${params.initiator}`);
|
|
1295
|
+
}
|
|
1296
|
+
return deserializePrimaryNameRequest(Buffer.from(account.data));
|
|
1297
|
+
}
|
|
1298
|
+
async getPrimaryNameRequests(params) {
|
|
1299
|
+
const accounts = await this.getAccountsByDiscriminator(this.coreProgram, PRIMARY_NAME_REQUEST_DISCRIMINATOR);
|
|
1300
|
+
const items = [];
|
|
1301
|
+
for (const { data } of accounts) {
|
|
1302
|
+
try {
|
|
1303
|
+
items.push(deserializePrimaryNameRequest(data));
|
|
1304
|
+
}
|
|
1305
|
+
catch {
|
|
1306
|
+
// Skip malformed
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
return paginate(items, params);
|
|
1310
|
+
}
|
|
1311
|
+
async getPrimaryNames(params) {
|
|
1312
|
+
const accounts = await this.getAccountsByDiscriminator(this.coreProgram, PRIMARY_NAME_DISCRIMINATOR);
|
|
1313
|
+
// Enrich each on-chain PrimaryName with its ArnsRecord.processId (the
|
|
1314
|
+
// on-chain account doesn't store it; see deserializePrimaryName).
|
|
1315
|
+
// Records that no longer have a matching ArnsRecord are silently
|
|
1316
|
+
// skipped — same forgiveness the per-name lookup already applies.
|
|
1317
|
+
const baseNameOf = (n) => {
|
|
1318
|
+
const parts = n.toLowerCase().split('_');
|
|
1319
|
+
return parts.length === 2 ? parts[1] : parts[0];
|
|
1320
|
+
};
|
|
1321
|
+
const items = [];
|
|
1322
|
+
for (const { data } of accounts) {
|
|
1323
|
+
try {
|
|
1324
|
+
const pn = deserializePrimaryName(data);
|
|
1325
|
+
const rec = await this.getArNSRecord({ name: baseNameOf(pn.name) });
|
|
1326
|
+
items.push({ ...pn, processId: rec.processId });
|
|
1327
|
+
}
|
|
1328
|
+
catch {
|
|
1329
|
+
// Skip malformed or orphaned (ArnsRecord missing).
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
return paginate(items, params);
|
|
1333
|
+
}
|
|
1334
|
+
// =========================================
|
|
1335
|
+
// Redelegation fee
|
|
1336
|
+
// =========================================
|
|
1337
|
+
async getRedelegationFee(params) {
|
|
1338
|
+
const { getRedelegationRecordPDA } = await import('./pda.js');
|
|
1339
|
+
const [pda] = await getRedelegationRecordPDA(address(params.address), this.garProgram);
|
|
1340
|
+
const account = await this.getAccount(pda);
|
|
1341
|
+
if (!account.exists) {
|
|
1342
|
+
return { redelegationFeeRate: 0, feeResetTimestamp: 0 };
|
|
1343
|
+
}
|
|
1344
|
+
const record = deserializeRedelegationRecord(Buffer.from(account.data));
|
|
1345
|
+
const now = Math.floor(Date.now() / 1000);
|
|
1346
|
+
if (now >= record.feeResetAt) {
|
|
1347
|
+
return { redelegationFeeRate: 0, feeResetTimestamp: record.feeResetAt };
|
|
1348
|
+
}
|
|
1349
|
+
const feeRate = Math.min(record.redelegationCount * 10, 60);
|
|
1350
|
+
return {
|
|
1351
|
+
redelegationFeeRate: feeRate,
|
|
1352
|
+
feeResetTimestamp: record.feeResetAt,
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
// =========================================
|
|
1356
|
+
// Gateway registry settings
|
|
1357
|
+
// =========================================
|
|
1358
|
+
async getGatewayRegistrySettings() {
|
|
1359
|
+
const [pda] = await getGarSettingsPDA(this.garProgram);
|
|
1360
|
+
const account = await this.getAccount(pda);
|
|
1361
|
+
if (!account.exists) {
|
|
1362
|
+
throw new Error('GarSettings account not found');
|
|
1363
|
+
}
|
|
1364
|
+
return deserializeGarSettings(Buffer.from(account.data));
|
|
1365
|
+
}
|
|
1366
|
+
// =========================================
|
|
1367
|
+
// Aggregate queries
|
|
1368
|
+
// =========================================
|
|
1369
|
+
async getAllDelegates(params) {
|
|
1370
|
+
const accounts = await this.getAccountsByDiscriminator(this.garProgram, DELEGATION_DISCRIMINATOR);
|
|
1371
|
+
const decoded = [];
|
|
1372
|
+
for (const { pubkey, data } of accounts) {
|
|
1373
|
+
try {
|
|
1374
|
+
decoded.push({
|
|
1375
|
+
pubkey: pubkey,
|
|
1376
|
+
del: deserializeDelegation(data),
|
|
1377
|
+
});
|
|
1378
|
+
}
|
|
1379
|
+
catch {
|
|
1380
|
+
// Skip malformed
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
// Batch-fetch each referenced gateway's reward accumulator so we can
|
|
1384
|
+
// return live balances. See INVARIANTS.md and `computeLiveDelegationBalance`.
|
|
1385
|
+
const accumulators = await this.getGatewayAccumulators(decoded.map(({ del }) => del.gateway));
|
|
1386
|
+
const items = decoded.map(({ pubkey, del }) => ({
|
|
1387
|
+
address: del.delegator,
|
|
1388
|
+
gatewayAddress: del.gateway,
|
|
1389
|
+
delegatedStake: computeLiveDelegationBalance({
|
|
1390
|
+
delegatedStake: del.delegatedStake,
|
|
1391
|
+
rewardDebt: del.rewardDebt,
|
|
1392
|
+
cumulativeRewardPerToken: accumulators.get(del.gateway) ?? 0n,
|
|
1393
|
+
}),
|
|
1394
|
+
startTimestamp: secToMs(del.startTimestamp),
|
|
1395
|
+
vaultedStake: 0,
|
|
1396
|
+
cursorId: pubkey,
|
|
1397
|
+
}));
|
|
1398
|
+
return paginate(items, params);
|
|
1399
|
+
}
|
|
1400
|
+
async getAllGatewayVaults(params) {
|
|
1401
|
+
const accounts = await this.getAccountsByDiscriminator(this.garProgram, WITHDRAWAL_DISCRIMINATOR);
|
|
1402
|
+
const items = [];
|
|
1403
|
+
for (const { pubkey, data } of accounts) {
|
|
1404
|
+
try {
|
|
1405
|
+
const w = deserializeWithdrawal(data);
|
|
1406
|
+
items.push({
|
|
1407
|
+
cursorId: pubkey,
|
|
1408
|
+
vaultId: w.vaultId,
|
|
1409
|
+
balance: w.balance,
|
|
1410
|
+
startTimestamp: secToMs(w.startTimestamp),
|
|
1411
|
+
endTimestamp: secToMs(w.endTimestamp),
|
|
1412
|
+
gatewayAddress: w.gateway,
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
catch {
|
|
1416
|
+
// Skip malformed
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
return paginate(items, params);
|
|
1420
|
+
}
|
|
1421
|
+
// =========================================
|
|
1422
|
+
// Prune / cleanup discovery (Solana-only)
|
|
1423
|
+
// =========================================
|
|
1424
|
+
//
|
|
1425
|
+
// These helpers enumerate accounts eligible for the permissionless prune
|
|
1426
|
+
// ix surface (see SolanaARIOWriteable). All read on-chain via
|
|
1427
|
+
// `getProgramAccounts` + the Codama decoders, then post-filter
|
|
1428
|
+
// client-side because most eligibility predicates can't be expressed as
|
|
1429
|
+
// memcmp filters (variable-length names shift offsets; Option<i64>
|
|
1430
|
+
// adds a tag byte). Volume is bounded — the cranker is expected to
|
|
1431
|
+
// call these once per epoch cycle, not per-tx.
|
|
1432
|
+
//
|
|
1433
|
+
// See `docs/CRANKER_PRUNING_PLAN.md` for the design.
|
|
1434
|
+
/**
|
|
1435
|
+
* Enumerate ArnsRecord PDAs whose lease has fully expired
|
|
1436
|
+
* (`end_timestamp + grace_period + return_auction_duration <= now`).
|
|
1437
|
+
* Permabuys (no `end_timestamp`) are excluded. Pass a unix-seconds `now`.
|
|
1438
|
+
*/
|
|
1439
|
+
async getExpiredArnsRecords(now) {
|
|
1440
|
+
const [arnsConfigPda] = await getArnsSettingsPDA(this.arnsProgram);
|
|
1441
|
+
const cfgAccount = await this.getAccount(arnsConfigPda);
|
|
1442
|
+
if (!cfgAccount.exists)
|
|
1443
|
+
return [];
|
|
1444
|
+
const cfg = getArnsConfigDecoder().decode(cfgAccount.data);
|
|
1445
|
+
const grace = Number(cfg.gracePeriodSeconds);
|
|
1446
|
+
const auction = Number(cfg.returnAuctionDurationSeconds);
|
|
1447
|
+
const accounts = await this.getAccountsByDiscriminator(this.arnsProgram, ARNS_RECORD_DISCRIMINATOR);
|
|
1448
|
+
const decoder = getArnsRecordDecoder();
|
|
1449
|
+
const out = [];
|
|
1450
|
+
for (const { pubkey, data } of accounts) {
|
|
1451
|
+
try {
|
|
1452
|
+
const r = decoder.decode(data);
|
|
1453
|
+
if (r.endTimestamp.__option !== 'Some')
|
|
1454
|
+
continue;
|
|
1455
|
+
const end = Number(r.endTimestamp.value);
|
|
1456
|
+
if (end + grace + auction <= now) {
|
|
1457
|
+
out.push({
|
|
1458
|
+
pubkey,
|
|
1459
|
+
name: r.name,
|
|
1460
|
+
endTimestamp: r.endTimestamp.value,
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
catch {
|
|
1465
|
+
// skip malformed
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
return out;
|
|
1469
|
+
}
|
|
1470
|
+
/**
|
|
1471
|
+
* Enumerate ReturnedName PDAs whose Dutch auction window has fully
|
|
1472
|
+
* elapsed (`returned_at + return_auction_duration <= now`).
|
|
1473
|
+
*/
|
|
1474
|
+
async getExpiredReturnedNames(now) {
|
|
1475
|
+
const [arnsConfigPda] = await getArnsSettingsPDA(this.arnsProgram);
|
|
1476
|
+
const cfgAccount = await this.getAccount(arnsConfigPda);
|
|
1477
|
+
if (!cfgAccount.exists)
|
|
1478
|
+
return [];
|
|
1479
|
+
const cfg = getArnsConfigDecoder().decode(cfgAccount.data);
|
|
1480
|
+
const auction = Number(cfg.returnAuctionDurationSeconds);
|
|
1481
|
+
const accounts = await this.getAccountsByDiscriminator(this.arnsProgram, RETURNED_NAME_DISCRIMINATOR);
|
|
1482
|
+
const decoder = getReturnedNameDecoder();
|
|
1483
|
+
const out = [];
|
|
1484
|
+
for (const { pubkey, data } of accounts) {
|
|
1485
|
+
try {
|
|
1486
|
+
const r = decoder.decode(data);
|
|
1487
|
+
if (Number(r.returnedAt) + auction <= now) {
|
|
1488
|
+
out.push({ pubkey, name: r.name, returnedAt: r.returnedAt });
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
catch {
|
|
1492
|
+
// skip malformed
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
return out;
|
|
1496
|
+
}
|
|
1497
|
+
/**
|
|
1498
|
+
* Enumerate ReservedName PDAs whose `expires_at` has passed.
|
|
1499
|
+
* Permanent reservations (`expires_at: None`) are excluded.
|
|
1500
|
+
*/
|
|
1501
|
+
async getExpiredReservations(now) {
|
|
1502
|
+
const accounts = await this.getAccountsByDiscriminator(this.arnsProgram, RESERVED_NAME_DISCRIMINATOR);
|
|
1503
|
+
const decoder = getReservedNameDecoder();
|
|
1504
|
+
const out = [];
|
|
1505
|
+
for (const { pubkey, data } of accounts) {
|
|
1506
|
+
try {
|
|
1507
|
+
const r = decoder.decode(data);
|
|
1508
|
+
if (r.expiresAt.__option !== 'Some')
|
|
1509
|
+
continue;
|
|
1510
|
+
if (Number(r.expiresAt.value) <= now) {
|
|
1511
|
+
out.push({ pubkey, name: r.name });
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
catch {
|
|
1515
|
+
// skip malformed
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
return out;
|
|
1519
|
+
}
|
|
1520
|
+
/**
|
|
1521
|
+
* Enumerate Gateway PDAs in `Joined` status with
|
|
1522
|
+
* `stats.failed_consecutive >= maxFailures`. These are eligible for
|
|
1523
|
+
* `pruneGateway` (slash + remove from registry).
|
|
1524
|
+
*/
|
|
1525
|
+
async getDeficientGateways(maxFailures) {
|
|
1526
|
+
const accounts = await this.getAccountsByDiscriminator(this.garProgram, GATEWAY_DISCRIMINATOR);
|
|
1527
|
+
const decoder = getGatewayDecoder();
|
|
1528
|
+
const out = [];
|
|
1529
|
+
for (const { pubkey, data } of accounts) {
|
|
1530
|
+
try {
|
|
1531
|
+
const g = decoder.decode(data);
|
|
1532
|
+
if (g.status !== GatewayStatus.Joined)
|
|
1533
|
+
continue;
|
|
1534
|
+
if (g.stats.failedConsecutive >= maxFailures) {
|
|
1535
|
+
out.push({
|
|
1536
|
+
pubkey,
|
|
1537
|
+
operator: g.operator,
|
|
1538
|
+
failedConsecutive: g.stats.failedConsecutive,
|
|
1539
|
+
});
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
catch {
|
|
1543
|
+
// skip malformed
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
return out;
|
|
1547
|
+
}
|
|
1548
|
+
/**
|
|
1549
|
+
* Enumerate Gateway PDAs whose `status == Gone` (already left the
|
|
1550
|
+
* network but PDA not yet GC'd). Eligible for `finalizeGone`.
|
|
1551
|
+
*/
|
|
1552
|
+
async getGoneGateways() {
|
|
1553
|
+
const accounts = await this.getAccountsByDiscriminator(this.garProgram, GATEWAY_DISCRIMINATOR);
|
|
1554
|
+
const decoder = getGatewayDecoder();
|
|
1555
|
+
const out = [];
|
|
1556
|
+
for (const { pubkey, data } of accounts) {
|
|
1557
|
+
try {
|
|
1558
|
+
const g = decoder.decode(data);
|
|
1559
|
+
if (g.status === GatewayStatus.Gone) {
|
|
1560
|
+
out.push({ pubkey, operator: g.operator });
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
catch {
|
|
1564
|
+
// skip malformed
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
return out;
|
|
1568
|
+
}
|
|
1569
|
+
/**
|
|
1570
|
+
* Enumerate Delegation PDAs with `amount == 0`. Eligible for
|
|
1571
|
+
* `closeEmptyDelegation` (rent refund to the original delegator).
|
|
1572
|
+
*/
|
|
1573
|
+
async getEmptyDelegations() {
|
|
1574
|
+
const accounts = await this.getAccountsByDiscriminator(this.garProgram, DELEGATION_DISCRIMINATOR);
|
|
1575
|
+
const decoder = getDelegationDecoder();
|
|
1576
|
+
const out = [];
|
|
1577
|
+
for (const { pubkey, data } of accounts) {
|
|
1578
|
+
try {
|
|
1579
|
+
const d = decoder.decode(data);
|
|
1580
|
+
if (d.amount === 0n) {
|
|
1581
|
+
out.push({ pubkey, gateway: d.gateway, delegator: d.delegator });
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
catch {
|
|
1585
|
+
// skip malformed
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
return out;
|
|
1589
|
+
}
|
|
1590
|
+
/**
|
|
1591
|
+
* Enumerate Withdrawal PDAs with `amount == 0` (drained via
|
|
1592
|
+
* fund-from-withdrawal payments). Eligible for `closeDrainedWithdrawal`
|
|
1593
|
+
* (rent refund to owner).
|
|
1594
|
+
*/
|
|
1595
|
+
async getDrainedWithdrawals() {
|
|
1596
|
+
const accounts = await this.getAccountsByDiscriminator(this.garProgram, WITHDRAWAL_DISCRIMINATOR);
|
|
1597
|
+
const decoder = getWithdrawalDecoder();
|
|
1598
|
+
const out = [];
|
|
1599
|
+
for (const { pubkey, data } of accounts) {
|
|
1600
|
+
try {
|
|
1601
|
+
const w = decoder.decode(data);
|
|
1602
|
+
if (w.amount === 0n) {
|
|
1603
|
+
out.push({ pubkey, owner: w.owner, withdrawalId: w.withdrawalId });
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
catch {
|
|
1607
|
+
// skip malformed
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
return out;
|
|
1611
|
+
}
|
|
1612
|
+
/**
|
|
1613
|
+
* Enumerate Vault PDAs whose `end_timestamp` has passed (eligible for
|
|
1614
|
+
* `releaseVault`). Note: `releaseVault` is owner-signed, so the cranker
|
|
1615
|
+
* can only release its own vaults — the helper still surfaces every
|
|
1616
|
+
* expired vault so other consumers (UIs, indexers) can use it too.
|
|
1617
|
+
*/
|
|
1618
|
+
async getExpiredVaults(now) {
|
|
1619
|
+
const accounts = await this.getAccountsByDiscriminator(this.coreProgram, VAULT_DISCRIMINATOR);
|
|
1620
|
+
const decoder = getVaultDecoder();
|
|
1621
|
+
const out = [];
|
|
1622
|
+
for (const { pubkey, data } of accounts) {
|
|
1623
|
+
try {
|
|
1624
|
+
const v = decoder.decode(data);
|
|
1625
|
+
if (Number(v.endTimestamp) <= now) {
|
|
1626
|
+
out.push({
|
|
1627
|
+
pubkey,
|
|
1628
|
+
owner: v.owner,
|
|
1629
|
+
vaultId: v.vaultId,
|
|
1630
|
+
endTimestamp: v.endTimestamp,
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
catch {
|
|
1635
|
+
// skip malformed
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
return out;
|
|
1639
|
+
}
|
|
1640
|
+
/**
|
|
1641
|
+
* Enumerate PrimaryNameRequest PDAs whose `expires_at` has passed.
|
|
1642
|
+
* Eligible for `closeExpiredRequest` (rent refund to original initiator).
|
|
1643
|
+
*/
|
|
1644
|
+
async getExpiredPrimaryNameRequests(now) {
|
|
1645
|
+
const accounts = await this.getAccountsByDiscriminator(this.coreProgram, PRIMARY_NAME_REQUEST_DISCRIMINATOR);
|
|
1646
|
+
const decoder = getPrimaryNameRequestDecoder();
|
|
1647
|
+
const out = [];
|
|
1648
|
+
for (const { pubkey, data } of accounts) {
|
|
1649
|
+
try {
|
|
1650
|
+
const r = decoder.decode(data);
|
|
1651
|
+
if (Number(r.expiresAt) <= now) {
|
|
1652
|
+
out.push({ pubkey, initiator: r.initiator });
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
catch {
|
|
1656
|
+
// skip malformed
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
return out;
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Read the live `ArnsConfig` (used by the cranker to gate
|
|
1663
|
+
* `pruneExpiredNames` / `pruneReturnedNames` on the
|
|
1664
|
+
* `next_*_prune_timestamp` hints).
|
|
1665
|
+
*/
|
|
1666
|
+
async getArnsConfigRaw() {
|
|
1667
|
+
const [pda] = await getArnsSettingsPDA(this.arnsProgram);
|
|
1668
|
+
const account = await this.getAccount(pda);
|
|
1669
|
+
if (!account.exists)
|
|
1670
|
+
return null;
|
|
1671
|
+
const cfg = getArnsConfigDecoder().decode(account.data);
|
|
1672
|
+
return {
|
|
1673
|
+
nextRecordsPruneTimestamp: cfg.nextRecordsPruneTimestamp,
|
|
1674
|
+
nextReturnedNamesPruneTimestamp: cfg.nextReturnedNamesPruneTimestamp,
|
|
1675
|
+
gracePeriodSeconds: cfg.gracePeriodSeconds,
|
|
1676
|
+
returnAuctionDurationSeconds: cfg.returnAuctionDurationSeconds,
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1679
|
+
// =========================================
|
|
1680
|
+
// Name resolution (ArNSNameResolver)
|
|
1681
|
+
// =========================================
|
|
1682
|
+
async resolveArNSName({ name }) {
|
|
1683
|
+
const parts = name.split('_');
|
|
1684
|
+
const baseName = parts.length > 1 ? parts[parts.length - 1] : parts[0];
|
|
1685
|
+
const record = await this.getArNSRecord({ name: baseName });
|
|
1686
|
+
// TODO: resolve undername via ANT program when undername !== '@'
|
|
1687
|
+
return {
|
|
1688
|
+
name: baseName,
|
|
1689
|
+
txId: '',
|
|
1690
|
+
type: record.type,
|
|
1691
|
+
processId: record.processId,
|
|
1692
|
+
ttlSeconds: 3600,
|
|
1693
|
+
undernameLimit: record.undernameLimit,
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1696
|
+
// =========================================================================
|
|
1697
|
+
// Observer helpers (Solana-only; used by gateway-side report submission)
|
|
1698
|
+
// =========================================================================
|
|
1699
|
+
/**
|
|
1700
|
+
* Resolve the gateway operator pubkey backing a given observer pubkey.
|
|
1701
|
+
* The `ObserverLookup` PDA is written at `join_network` (and rotated by
|
|
1702
|
+
* `update_observer_address`); when present its `gateway` field is the
|
|
1703
|
+
* operator pubkey. Returns `undefined` when the observer isn't
|
|
1704
|
+
* registered on any gateway.
|
|
1705
|
+
*/
|
|
1706
|
+
async getObserverLookup(observer) {
|
|
1707
|
+
const [pda] = await getObserverLookupPDA(observer, this.garProgram);
|
|
1708
|
+
const account = await this.getAccount(pda);
|
|
1709
|
+
if (!account.exists)
|
|
1710
|
+
return undefined;
|
|
1711
|
+
const data = Buffer.from(account.data);
|
|
1712
|
+
// Layout: 8 disc + 32 gateway + 1 bump.
|
|
1713
|
+
const gateway = addressDecoder.decode(data.subarray(8, 40));
|
|
1714
|
+
const bump = data.readUInt8(40);
|
|
1715
|
+
return { gateway, bump };
|
|
1716
|
+
}
|
|
1717
|
+
/**
|
|
1718
|
+
* Pre-flight gate for `save_observations` submission. Reads the Epoch
|
|
1719
|
+
* account once and reports whether the given observer pubkey is:
|
|
1720
|
+
* - `prescribed`: in `epoch.prescribed_observers[..observer_count]`
|
|
1721
|
+
* - `observerIdx`: position in the array (matches the `has_observed`
|
|
1722
|
+
* bit index when prescribed)
|
|
1723
|
+
* - `alreadyObserved`: whether the bit at `observerIdx` is set
|
|
1724
|
+
* - `windowOpen`: whether `now < epoch.end_timestamp`
|
|
1725
|
+
*
|
|
1726
|
+
* Use this from a sink/wrapper to skip cheap-to-skip cases before
|
|
1727
|
+
* paying for a transaction simulation that would just bounce.
|
|
1728
|
+
*/
|
|
1729
|
+
async getEpochObservationStatus(epochIndex, observer) {
|
|
1730
|
+
const epoch = await this.fetchEpoch(epochIndex);
|
|
1731
|
+
let observerIdx = -1;
|
|
1732
|
+
for (let i = 0; i < epoch.observerCount; i++) {
|
|
1733
|
+
if (epoch.prescribedObservers[i] === observer) {
|
|
1734
|
+
observerIdx = i;
|
|
1735
|
+
break;
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
const prescribed = observerIdx !== -1;
|
|
1739
|
+
const alreadyObserved = prescribed &&
|
|
1740
|
+
((epoch.hasObserved[Math.floor(observerIdx / 8)] >> (observerIdx % 8)) &
|
|
1741
|
+
1) ===
|
|
1742
|
+
1;
|
|
1743
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
1744
|
+
return {
|
|
1745
|
+
prescribed,
|
|
1746
|
+
observerIdx,
|
|
1747
|
+
alreadyObserved,
|
|
1748
|
+
windowOpen: nowSec < epoch.endTimestamp,
|
|
1749
|
+
endTimestampSec: epoch.endTimestamp,
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
}
|