@aptos-labs/ts-sdk
Version:
Aptos TypeScript SDK
1,213 lines • 54.8 kB
JavaScript
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0
import { getAptosFullNode, getPageWithObfuscatedCursor, paginateWithCursor, paginateWithObfuscatedCursor, } from "../client/index.js";
import { anyPublicKeyVariantToString, } from "../types/index.js";
import { AccountAddress } from "../core/accountAddress.js";
import { Account, Ed25519Account, MultiEd25519Account, MultiKeyAccount, SingleKeyAccount } from "../account/index.js";
import { isKeylessSigner } from "../account/keylessSigner.js";
import { AnyPublicKey } from "../core/crypto/singleKey.js";
import { Ed25519PublicKey } from "../core/crypto/ed25519.js";
import { MultiEd25519PublicKey } from "../core/crypto/multiEd25519.js";
import { AbstractMultiKey, MultiKey } from "../core/crypto/multiKey.js";
import { queryIndexer } from "./general.js";
import { getModule as getModuleUtil, getInfo as getInfoUtil } from "./utils/index.js";
import { GetAccountCoinsCount, GetAccountCoinsData, GetAccountCollectionsWithOwnedTokens, GetObjectData, GetAccountOwnedTokens, GetAccountOwnedTokensFromCollection, GetAccountTokensCount, GetAccountTransactionsCount, GetAuthKeysForPublicKey, GetAccountAddressesForAuthKey, } from "../types/generated/queries.js";
import { Secp256k1PrivateKey } from "../core/crypto/secp256k1.js";
import { Ed25519PrivateKey } from "../core/crypto/ed25519.js";
import { AuthenticationKey } from "../core/authenticationKey.js";
import { createObjectAddress } from "../core/account/utils/address.js";
import { Hex } from "../core/hex.js";
import { getTableItem } from "./table.js";
import { APTOS_COIN } from "../utils/index.js";
import { memoizeAsync } from "../utils/memoize.js";
import { AptosApiError } from "../errors/index.js";
import { Deserializer, U8, MoveVector } from "../bcs/index.js";
import { generateTransaction } from "./transactionSubmission.js";
import { RotationProofChallenge, TypeTagU8, TypeTagVector, } from "../transactions/index.js";
import { accountPublicKeyToBaseAccountPublicKey, accountPublicKeyToSigningScheme } from "../core/crypto/utils.js";
/**
* Retrieves account information for a specified account address.
*
* @param args - The arguments for retrieving account information.
* @param args.aptosConfig - The configuration object for Aptos.
* @param args.accountAddress - The address of the account to retrieve information for.
* @group Implementation
*/
export async function getInfo(args) {
return getInfoUtil(args);
}
/**
* Retrieves the modules associated with a specified account address.
*
* @param args - The arguments for retrieving modules.
* @param args.aptosConfig - The configuration for connecting to the Aptos blockchain.
* @param args.accountAddress - The address of the account whose modules are to be retrieved.
* @param args.options - Optional parameters for pagination and ledger version.
* @param args.options.limit - The maximum number of modules to retrieve (default is 1000).
* @param args.options.offset - The starting point for pagination. Note, this is obfuscated and is not an index.
* @param args.options.ledgerVersion - The specific ledger version to query.
* @group Implementation
*/
export async function getModules(args) {
const { aptosConfig, accountAddress, options } = args;
return paginateWithObfuscatedCursor({
aptosConfig,
originMethod: "getModules",
path: `accounts/${AccountAddress.from(accountAddress).toString()}/modules`,
params: {
ledger_version: options?.ledgerVersion,
limit: options?.limit ?? 1000,
},
});
}
/**
* Retrieves the modules associated with a specified account address.
*
* @param args - The arguments for retrieving modules.
* @param args.aptosConfig - The configuration for connecting to the Aptos blockchain.
* @param args.accountAddress - The address of the account whose modules are to be retrieved.
* @param args.options - Optional parameters for pagination and ledger version.
* @param args.options.cursor - The starting point for pagination. Note, this is obfuscated and is not an index.
* @param args.options.limit - The maximum number of modules to retrieve (default is 100).
* @param args.options.ledgerVersion - The specific ledger version to query.
* @group Implementation
*/
export async function getModulesPage(args) {
const { aptosConfig, accountAddress, options } = args;
const { response, cursor } = await getPageWithObfuscatedCursor({
aptosConfig,
originMethod: "getModulesPage",
path: `accounts/${AccountAddress.from(accountAddress).toString()}/modules`,
params: {
ledger_version: options?.ledgerVersion,
cursor: options?.cursor,
limit: options?.limit ?? 100,
},
});
return { modules: response.data, cursor };
}
/**
* Queries for a move module given an account address and module name.
* This function can help you retrieve the module's ABI and other relevant information.
*
* @param args - The arguments for retrieving the module.
* @param args.aptosConfig - The configuration for the Aptos client.
* @param args.accountAddress - The account address in hex-encoded 32 byte format.
* @param args.moduleName - The name of the module to retrieve.
* @param args.options - Optional parameters for the request.
* @param args.options.ledgerVersion - Specifies the ledger version of transactions. By default, the latest version will be used.
* @returns The move module.
* @group Implementation
*/
export async function getModule(args) {
return getModuleUtil(args);
}
/**
* Retrieves a list of transactions associated with a specific account address.
* This function allows you to paginate through the transactions for better performance and usability.
*
* @param args - The arguments for retrieving transactions.
* @param args.aptosConfig - The configuration settings for Aptos.
* @param args.accountAddress - The account address for which to retrieve transactions.
* @param args.options - Optional pagination parameters.
* @param args.options.offset - The starting point for pagination.
* @param args.options.limit - The maximum number of transactions to retrieve.
* @group Implementation
*/
export async function getTransactions(args) {
const { aptosConfig, accountAddress, options } = args;
return paginateWithCursor({
aptosConfig,
originMethod: "getTransactions",
path: `accounts/${AccountAddress.from(accountAddress).toString()}/transactions`,
params: { start: options?.offset, limit: options?.limit },
});
}
/**
* Retrieves a list of resources associated with a specific account address.
*
* @param args - The arguments for retrieving resources.
* @param args.aptosConfig - The configuration settings for Aptos.
* @param args.accountAddress - The address of the account to fetch resources for.
* @param args.options - Optional pagination and ledger version parameters.
* @param args.options.limit - The maximum number of resources to retrieve (default is 999).
* @param args.options.ledgerVersion - The specific ledger version to query.
* @group Implementation
*/
export async function getResources(args) {
const { aptosConfig, accountAddress, options } = args;
return paginateWithObfuscatedCursor({
aptosConfig,
originMethod: "getResources",
path: `accounts/${AccountAddress.from(accountAddress).toString()}/resources`,
params: {
ledger_version: options?.ledgerVersion,
limit: options?.limit ?? 999,
},
});
}
/**
* Retrieves a page of resources associated with a specific account address.
*
* @param args - The arguments for retrieving resources.
* @param args.aptosConfig - The configuration settings for Aptos.
* @param args.accountAddress - The address of the account to fetch resources for.
* @param args.options - Optional pagination and ledger version parameters.
* @param args.options.cursor - The starting point for pagination. Note, this is obfuscated and is not an index.
* @param args.options.limit - The maximum number of resources to retrieve (default is 100).
* @param args.options.ledgerVersion - The specific ledger version to query.
* @group Implementation
*/
export async function getResourcesPage(args) {
const { aptosConfig, accountAddress, options } = args;
const { response, cursor } = await getPageWithObfuscatedCursor({
aptosConfig,
originMethod: "getResourcesPage",
path: `accounts/${AccountAddress.from(accountAddress).toString()}/resources`,
params: {
ledger_version: options?.ledgerVersion,
cursor: options?.cursor,
limit: options?.limit ?? 100,
},
});
return { resources: response.data, cursor };
}
/**
* Retrieves a specific resource of a given type for the specified account address.
*
* @param args - The arguments for retrieving the resource.
* @param args.aptosConfig - The configuration settings for Aptos.
* @param args.accountAddress - The address of the account from which to retrieve the resource.
* @param args.resourceType - The type of the resource to retrieve, specified as a MoveStructId.
* @param args.options - Optional parameters for specifying the ledger version.
* @group Implementation
*/
export async function getResource(args) {
const { aptosConfig, accountAddress, resourceType, options } = args;
const { data } = await getAptosFullNode({
aptosConfig,
originMethod: "getResource",
path: `accounts/${AccountAddress.from(accountAddress).toString()}/resource/${resourceType}`,
params: { ledger_version: options?.ledgerVersion },
});
// TODO: Fix type checking, so cast is unnecessary
return data.data;
}
export async function getResourceFallible(args) {
try {
return await getResource(args);
}
catch (error) {
// explicitly return null if there is no resource
if (error?.status === 404 && error?.data?.error_code === "resource_not_found") {
return null;
}
throw error;
}
}
/**
* Retrieves the original account address associated with a given authentication key, which is useful for handling key rotations.
*
* @param args - The arguments for the lookup.
* @param args.aptosConfig - The configuration for the Aptos client.
* @param args.authenticationKey - The authentication key for which to look up the original address.
* @param args.options - Optional parameters for specifying the ledger version.
* @returns The original account address or the provided authentication key address if not found.
* @throws Throws an error if the lookup fails for reasons other than the address not being found.
* @group Implementation
*/
export async function lookupOriginalAccountAddress(args) {
const { aptosConfig, authenticationKey, options } = args;
const resource = await getResource({
aptosConfig,
accountAddress: "0x1",
resourceType: "0x1::account::OriginatingAddress",
options,
});
const { address_map: { handle }, } = resource;
const authKeyAddress = AccountAddress.from(authenticationKey);
// If the address is not found in the address map, which means its not rotated
// then return the address as is
try {
const originalAddress = await getTableItem({
aptosConfig,
handle,
data: {
key: authKeyAddress.toString(),
key_type: "address",
value_type: "address",
},
options,
});
return AccountAddress.from(originalAddress);
}
catch (err) {
if (err instanceof AptosApiError && err.data.error_code === "table_item_not_found") {
return authKeyAddress;
}
throw err;
}
}
/**
* Fetches the on-chain `authentication_key` for an account address and memoizes it for ~1 hour,
* keyed by `(network or fullnode URL, address)`. Used by the encrypted-transaction builder to
* derive auth keys when the caller does not pass them explicitly. Callers that just rotated their
* key and need an immediate fresh read should pass the auth key explicitly instead of relying on
* this cache.
*
* If the address has no `0x1::account::Account` resource on chain (a brand-new account, or a light
* account with balance/objects but no explicit resource), returns the address bytes as the
* authentication key — matching the chain's account-creation convention (see
* [`doesAccountExistAtAddress`]). This makes encrypted-transaction builds work for not-yet-created
* signers (e.g., fee-payer sponsorship of an uncreated sender).
*/
export async function fetchAndCacheAuthKeyForAddress(args) {
const { aptosConfig, accountAddress } = args;
const address = AccountAddress.from(accountAddress);
const addr = address.toString();
const cacheKey = `auth-key-${aptosConfig.fullnode ?? aptosConfig.network}-${addr}`;
return memoizeAsync(async () => {
try {
const info = await getInfoUtil({ aptosConfig, accountAddress: addr });
return new AuthenticationKey({ data: info.authentication_key });
}
catch (err) {
if (err instanceof AptosApiError && err.data?.error_code === "account_not_found") {
// Chain convention: with no Account resource the auth key is the address itself.
return new AuthenticationKey({ data: address.toUint8Array() });
}
throw err;
}
}, cacheKey, 60 * 60 * 1000)();
}
/**
* Retrieves the count of tokens owned by a specific account address.
*
* @param args - The arguments for retrieving the account tokens count.
* @param args.aptosConfig - The configuration settings for the Aptos network.
* @param args.accountAddress - The address of the account for which to count the tokens.
* @returns The count of tokens owned by the specified account.
* @group Implementation
*/
export async function getAccountTokensCount(args) {
const { aptosConfig, accountAddress } = args;
const address = AccountAddress.from(accountAddress).toStringLong();
const whereCondition = {
owner_address: { _eq: address },
amount: { _gt: 0 },
};
const graphqlQuery = {
query: GetAccountTokensCount,
variables: { where_condition: whereCondition },
};
const data = await queryIndexer({
aptosConfig,
query: graphqlQuery,
originMethod: "getAccountTokensCount",
});
// commonjs (aka cjs) doesn't handle Nullish Coalescing for some reason
// might be because of how ts infer the graphql generated scheme type
return data.current_token_ownerships_v2_aggregate.aggregate
? data.current_token_ownerships_v2_aggregate.aggregate.count
: 0;
}
/**
* Retrieves the tokens owned by a specified account address.
*
* @param args - The arguments for retrieving the account's tokens.
* @param args.aptosConfig - The configuration for the Aptos client.
* @param args.accountAddress - The address of the account whose tokens are being queried.
* @param args.options - Optional parameters for filtering and pagination.
* @param args.options.tokenStandard - The specific token standard to filter the results.
* @param args.options.offset - The number of records to skip before starting to collect the result set.
* @param args.options.limit - The maximum number of records to return.
* @param args.options.orderBy - The criteria for ordering the results.
* @returns A promise that resolves to the current token ownerships of the specified account.
* @group Implementation
*/
export async function getAccountOwnedTokens(args) {
const { aptosConfig, accountAddress, options } = args;
const address = AccountAddress.from(accountAddress).toStringLong();
const whereCondition = {
owner_address: { _eq: address },
amount: { _gt: 0 },
};
if (options?.tokenStandard) {
whereCondition.token_standard = { _eq: options?.tokenStandard };
}
const graphqlQuery = {
query: GetAccountOwnedTokens,
variables: {
where_condition: whereCondition,
offset: options?.offset,
limit: options?.limit,
order_by: options?.orderBy,
},
};
const data = await queryIndexer({
aptosConfig,
query: graphqlQuery,
originMethod: "getAccountOwnedTokens",
});
return data.current_token_ownerships_v2;
}
/**
* Retrieves the tokens owned by a specific account from a particular collection address.
*
* @param args - The parameters required to fetch the owned tokens.
* @param args.aptosConfig - The Aptos configuration object.
* @param args.accountAddress - The address of the account whose tokens are being queried.
* @param args.collectionAddress - The address of the collection from which tokens are being retrieved.
* @param args.options - Optional parameters for filtering and pagination, including token standard, pagination arguments, and
* order by options.
* @group Implementation
*/
export async function getAccountOwnedTokensFromCollectionAddress(args) {
const { aptosConfig, accountAddress, collectionAddress, options } = args;
const ownerAddress = AccountAddress.from(accountAddress).toStringLong();
const collAddress = AccountAddress.from(collectionAddress).toStringLong();
const whereCondition = {
owner_address: { _eq: ownerAddress },
current_token_data: { collection_id: { _eq: collAddress } },
amount: { _gt: 0 },
};
if (options?.tokenStandard) {
whereCondition.token_standard = { _eq: options?.tokenStandard };
}
const graphqlQuery = {
query: GetAccountOwnedTokensFromCollection,
variables: {
where_condition: whereCondition,
offset: options?.offset,
limit: options?.limit,
order_by: options?.orderBy,
},
};
const data = await queryIndexer({
aptosConfig,
query: graphqlQuery,
originMethod: "getAccountOwnedTokensFromCollectionAddress",
});
return data.current_token_ownerships_v2;
}
/**
* Retrieves the collections owned by a specified account along with the tokens in those collections.
*
* @param args - The arguments for the function.
* @param args.aptosConfig - The configuration for the Aptos client.
* @param args.accountAddress - The address of the account whose collections are being queried.
* @param args.options - Optional parameters for filtering and pagination.
* @param args.options.tokenStandard - An optional token standard to filter the collections.
* @param args.options.offset - An optional offset for pagination.
* @param args.options.limit - An optional limit for the number of results returned.
* @param args.options.orderBy - An optional parameter to specify the order of the results.
* @group Implementation
*/
export async function getAccountCollectionsWithOwnedTokens(args) {
const { aptosConfig, accountAddress, options } = args;
const address = AccountAddress.from(accountAddress).toStringLong();
const whereCondition = {
owner_address: { _eq: address },
};
if (options?.tokenStandard) {
whereCondition.current_collection = {
token_standard: { _eq: options?.tokenStandard },
};
}
const graphqlQuery = {
query: GetAccountCollectionsWithOwnedTokens,
variables: {
where_condition: whereCondition,
offset: options?.offset,
limit: options?.limit,
order_by: options?.orderBy,
},
};
const data = await queryIndexer({
aptosConfig,
query: graphqlQuery,
originMethod: "getAccountCollectionsWithOwnedTokens",
});
return data.current_collection_ownership_v2_view;
}
/**
* Retrieves the count of transactions associated with a specified account.
*
* @param args - The arguments for the function.
* @param args.aptosConfig - The configuration settings for Aptos.
* @param args.accountAddress - The address of the account for which to retrieve the transaction count.
* @returns The number of transactions associated with the specified account.
* @group Implementation
*/
export async function getAccountTransactionsCount(args) {
const { aptosConfig, accountAddress } = args;
const address = AccountAddress.from(accountAddress).toStringLong();
const graphqlQuery = {
query: GetAccountTransactionsCount,
variables: { address },
};
const data = await queryIndexer({
aptosConfig,
query: graphqlQuery,
originMethod: "getAccountTransactionsCount",
});
// commonjs (aka cjs) doesn't handle Nullish Coalescing for some reason
// might be because of how ts infer the graphql generated scheme type
return data.account_transactions_aggregate.aggregate ? data.account_transactions_aggregate.aggregate.count : 0;
}
/**
* Retrieves the amount of a specific coin held by an account.
*
* @param args - The parameters for the request.
* @param args.aptosConfig - The Aptos configuration object.
* @param args.accountAddress - The address of the account to query.
* @param args.coinType - Optional; the type of coin to check the amount for.
* @param args.faMetadataAddress - Optional; the address of the fungible asset metadata.
* @returns The amount of the specified coin held by the account, or 0 if none is found.
* @throws Error if neither coinType nor faMetadataAddress is provided.
* @group Implementation
*/
export async function getAccountCoinAmount(args) {
const { aptosConfig, accountAddress, coinType, faMetadataAddress } = args;
let coinAssetType = coinType;
let faAddress;
if (coinType !== undefined && faMetadataAddress !== undefined) {
faAddress = AccountAddress.from(faMetadataAddress).toStringLong();
}
else if (coinType !== undefined && faMetadataAddress === undefined) {
// TODO Move to a separate function as defined in the AIP for coin migration
if (coinType === APTOS_COIN) {
faAddress = AccountAddress.A.toStringLong();
}
else {
faAddress = createObjectAddress(AccountAddress.A, coinType).toStringLong();
}
}
else if (coinType === undefined && faMetadataAddress !== undefined) {
const addr = AccountAddress.from(faMetadataAddress);
faAddress = addr.toStringLong();
if (addr === AccountAddress.A) {
coinAssetType = APTOS_COIN;
}
// The paired CoinType should be populated outside of this function in another
// async call. We cannot do this internally due to dependency cycles issue.
}
else {
throw new Error("Either coinType, fungibleAssetAddress, or both must be provided");
}
const address = AccountAddress.from(accountAddress).toStringLong();
// Search by fungible asset address, unless it has a coin it migrated from
let where = { asset_type: { _eq: faAddress } };
if (coinAssetType !== undefined) {
where = { asset_type: { _in: [coinAssetType, faAddress] } };
}
const data = await getAccountCoinsData({
aptosConfig,
accountAddress: address,
options: {
where,
},
});
// commonjs (aka cjs) doesn't handle Nullish Coalescing for some reason
// might be because of how ts infer the graphql generated scheme type
return data[0] ? data[0].amount : 0;
}
/**
* Retrieves the current fungible asset balances for a specified account.
*
* @param args - The arguments for retrieving account coins data.
* @param args.aptosConfig - The configuration for connecting to the Aptos network.
* @param args.accountAddress - The address of the account for which to retrieve coin data.
* @param args.options - Optional parameters for pagination and filtering the results.
* @param args.options.offset - The number of items to skip before starting to collect the result set.
* @param args.options.limit - The maximum number of items to return.
* @param args.options.orderBy - The criteria for ordering the results.
* @param args.options.where - Conditions to filter the results based on the current fungible asset balances.
* @group Implementation
*/
export async function getAccountCoinsData(args) {
const { aptosConfig, accountAddress, options } = args;
const address = AccountAddress.from(accountAddress).toStringLong();
const whereCondition = {
...options?.where,
owner_address: { _eq: address },
};
const graphqlQuery = {
query: GetAccountCoinsData,
variables: {
where_condition: whereCondition,
offset: options?.offset,
limit: options?.limit,
order_by: options?.orderBy,
},
};
const data = await queryIndexer({
aptosConfig,
query: graphqlQuery,
originMethod: "getAccountCoinsData",
});
return data.current_fungible_asset_balances;
}
/**
* Retrieves the count of fungible asset coins held by a specified account.
*
* @param args - The arguments for the function.
* @param args.aptosConfig - The configuration settings for the Aptos network.
* @param args.accountAddress - The address of the account for which to retrieve the coin count.
* @throws Error if the count of account coins cannot be retrieved.
* @group Implementation
*/
export async function getAccountCoinsCount(args) {
const { aptosConfig, accountAddress } = args;
const address = AccountAddress.from(accountAddress).toStringLong();
const graphqlQuery = {
query: GetAccountCoinsCount,
variables: { address },
};
const data = await queryIndexer({
aptosConfig,
query: graphqlQuery,
originMethod: "getAccountCoinsCount",
});
if (!data.current_fungible_asset_balances_aggregate.aggregate) {
throw new Error("Failed to get the count of account coins");
}
return data.current_fungible_asset_balances_aggregate.aggregate.count;
}
/**
* Retrieves an account's balance for the given asset via the fullnode REST API.
*
* - `asset` may be a coin type (Move struct ID, e.g. `0x1::aptos_coin::AptosCoin`) or an FA metadata address.
* - Calls: `GET /accounts/{accountAddress}/balance/{asset}` and returns the numeric balance.
*
* @param args - The parameters for the request.
* @param args.aptosConfig - The Aptos configuration object.
* @param args.accountAddress - The account address to query.
* @param args.asset - The asset identifier (coin type or FA metadata address).
* @returns The balance as a number.
* @group Implementation
*/
export async function getBalance(args) {
const { aptosConfig, accountAddress, asset } = args;
const response = await getAptosFullNode({
aptosConfig,
originMethod: "getBalance",
path: `accounts/${accountAddress}/balance/${asset}`,
params: {
accountAddress: accountAddress.toString(),
asset: asset instanceof Uint8Array ? AccountAddress.from(asset).toString() : asset.toString(),
},
});
return parseInt(response.data.toString(), 10);
}
/**
* Retrieves the objects owned by a specified account.
*
* @param args - The parameters for the request.
* @param args.aptosConfig - The configuration for the Aptos client.
* @param args.accountAddress - The address of the account whose owned objects are to be retrieved.
* @param args.options - Optional parameters for pagination and ordering of the results.
* @param args.options.offset - The number of items to skip before starting to collect the result set.
* @param args.options.limit - The maximum number of items to return.
* @param args.options.orderBy - The criteria to order the results by.
* @returns A promise that resolves to the current objects owned by the specified account.
* @group Implementation
*/
export async function getAccountOwnedObjects(args) {
const { aptosConfig, accountAddress, options } = args;
const address = AccountAddress.from(accountAddress).toStringLong();
const whereCondition = {
owner_address: { _eq: address },
};
const graphqlQuery = {
query: GetObjectData,
variables: {
where_condition: whereCondition,
offset: options?.offset,
limit: options?.limit,
order_by: options?.orderBy,
},
};
const data = await queryIndexer({
aptosConfig,
query: graphqlQuery,
originMethod: "getAccountOwnedObjects",
});
return data.current_objects;
}
/**
* Derives an account from the provided private key and Aptos configuration.
*
* This function queries all owned accounts for the provided private key and returns the most
* recently used account. If no account is found, it will throw an error unless `throwIfNoAccountFound` is set to false.
*
* If `throwIfNoAccountFound` is set to false, the function will return the default account for the private key via `Account.fromPrivateKey`.
*
* NOTE: There is a potential issue once the unified single signer scheme is adopted by the community.
* Because one could create two accounts with the same private key with this new authenticator type,
* we'll need to determine the order in which we look up the accounts: first unified scheme and then legacy scheme,
* or first legacy scheme and then unified scheme.
*
* @param args - The arguments for deriving the account.
* @param args.aptosConfig - The Aptos configuration used for account lookup.
* @param args.privateKey - The private key used to derive the account.
* @param args.options.throwIfNoAccountFound - If true, throw an error if no existing account is found on chain. Default is false.
* @throws Error if the account cannot be derived from the private key.
* @group Implementation
* @deprecated Note that more inspection is needed by the user to determine which account exists on-chain
*/
export async function deriveAccountFromPrivateKey(args) {
const { aptosConfig, privateKey, options } = args;
const throwIfNoAccountFound = options?.throwIfNoAccountFound ?? false;
const accounts = await deriveOwnedAccountsFromPrivateKey({ aptosConfig, privateKey });
if (accounts.length === 0) {
if (throwIfNoAccountFound) {
throw new Error(`No existing account found for private key.`);
}
// If no account is found, return the default account. This is a legacy account for Ed25519 private keys.
return Account.fromPrivateKey({ privateKey });
}
return accounts[0];
}
/**
* Checks if an account exists by verifying its information against the Aptos blockchain.
*
* @param args - The arguments for the function.
* @param args.aptosConfig - The configuration for connecting to the Aptos blockchain.
* @param args.authKey - The authentication key used to derive the account address.
* @returns A promise that resolves to a boolean indicating whether the account exists.
*
* @throws Throws an Error if there is an issue while looking for account information.
* @group Implementation
*/
export async function isAccountExist(args) {
const { aptosConfig, authKey } = args;
const accountAddress = await lookupOriginalAccountAddress({
aptosConfig,
authenticationKey: authKey.derivedAddress(),
});
return doesAccountExistAtAddress({ aptosConfig, accountAddress });
}
/**
* Checks if an account exists at a given address.
*
* @param args - The arguments for checking account existence.
* @param args.aptosConfig - The configuration for the Aptos client.
* @param args.accountAddress - The address of the account to check.
* @param args.options.withAuthKey - An optional authentication key which will also be checked against if provided.
* @returns A promise that resolves to a boolean indicating whether the account exists.
* @group Implementation
*/
async function doesAccountExistAtAddress(args) {
const { aptosConfig, accountAddress, options } = args;
try {
// Get the account resources and the balance of the account. We need to check both because
// an account resource can exist with 0 balance and a balance can exist without an account resource (light accounts).
const [accountResource, ownedObjects] = await Promise.all([
getResourceFallible({
aptosConfig,
accountAddress,
resourceType: "0x1::account::Account",
}),
getAccountOwnedObjects({
aptosConfig,
accountAddress,
options: {
limit: 1,
},
}),
]);
// If the account resource is not found and the balance is 0, then the account does not exist.
if (!accountResource && ownedObjects.length === 0) {
return false;
}
// If no auth key is provided as an argument, return true.
if (!options?.withAuthKey) {
return true;
}
// Get the auth key from the account resource if it exists. If the account resource does not exist,
// then the auth key is the account address by default.
let authKey;
if (accountResource) {
authKey = accountResource.authentication_key;
}
else {
authKey = accountAddress.toStringLong();
}
if (authKey !== options.withAuthKey.toString()) {
return false;
}
// Else the account exists and the auth key matches.
return true;
}
catch (error) {
throw new Error(`Error while checking if account exists at ${accountAddress.toString()}: ${error}`);
}
}
// Lazy: instantiating TypeTagU8 at module-init time creates an ESM circular
// import (structEnumParser → internal/account → transactions/index → typeTag)
// where TypeTagU8 may not yet be a constructor when this file evaluates.
// Building the ABI on first use sidesteps the order dependency.
let _rotateAuthKeyAbi;
function rotateAuthKeyAbi() {
if (!_rotateAuthKeyAbi) {
_rotateAuthKeyAbi = {
typeParameters: [],
parameters: [
new TypeTagU8(),
TypeTagVector.u8(),
new TypeTagU8(),
TypeTagVector.u8(),
TypeTagVector.u8(),
TypeTagVector.u8(),
],
};
}
return _rotateAuthKeyAbi;
}
/**
* Rotates the authentication key for a given account.
*
* @param args - The arguments for rotating the authentication key.
* @param args.aptosConfig - The configuration settings for the Aptos network.
* @param args.fromAccount - The account from which the authentication key will be rotated.
* @param args.toAccount - (Optional) The target account to rotate to. Required if not using toNewPrivateKey.
* @param args.toNewPrivateKey - (Optional) The new private key to rotate to. Required if not using toAccount.
*
* @remarks
* This function supports three modes of rotation:
* 1. Using a target Account object (toAccount)
* 2. Using a new private key (toNewPrivateKey)
*
* @returns A simple transaction object that can be submitted to the network.
* @throws Error if the rotation fails or verification fails.
*
* @group Implementation
*/
export async function rotateAuthKey(args) {
const { aptosConfig, fromAccount, options } = args;
if ("toNewPrivateKey" in args) {
return rotateAuthKeyWithChallenge({
aptosConfig,
fromAccount,
toNewPrivateKey: args.toNewPrivateKey,
options,
});
}
if ("toAccount" in args) {
if (args.toAccount instanceof Ed25519Account) {
return rotateAuthKeyWithChallenge({
aptosConfig,
fromAccount,
toNewPrivateKey: args.toAccount.privateKey,
options,
});
}
return rotateAuthKeyWithChallenge({ aptosConfig, fromAccount, toAccount: args.toAccount, options });
}
throw new Error("Invalid arguments");
}
async function rotateAuthKeyWithChallenge(args) {
const { aptosConfig, fromAccount, options } = args;
const accountInfo = await getInfo({
aptosConfig,
accountAddress: fromAccount.accountAddress,
});
let newAccount;
if ("toNewPrivateKey" in args) {
newAccount = Account.fromPrivateKey({ privateKey: args.toNewPrivateKey, legacy: true });
}
else {
newAccount = args.toAccount;
}
const challenge = new RotationProofChallenge({
sequenceNumber: BigInt(accountInfo.sequence_number),
originator: fromAccount.accountAddress,
currentAuthKey: AccountAddress.from(accountInfo.authentication_key),
newPublicKey: newAccount.publicKey,
});
// Sign the challenge
const challengeHex = challenge.bcsToBytes();
const proofSignedByCurrentKey = fromAccount.sign(challengeHex);
const proofSignedByNewKey = newAccount.sign(challengeHex);
// Generate transaction
return generateTransaction({
aptosConfig,
sender: fromAccount.accountAddress,
data: {
function: "0x1::account::rotate_authentication_key",
functionArguments: [
new U8(fromAccount.signingScheme), // from scheme
MoveVector.U8(fromAccount.publicKey.toUint8Array()),
new U8(newAccount.signingScheme), // to scheme
MoveVector.U8(newAccount.publicKey.toUint8Array()),
MoveVector.U8(proofSignedByCurrentKey.toUint8Array()),
MoveVector.U8(proofSignedByNewKey.toUint8Array()),
],
abi: rotateAuthKeyAbi(),
},
options,
});
}
let _rotateAuthKeyUnverifiedAbi;
function rotateAuthKeyUnverifiedAbi() {
if (!_rotateAuthKeyUnverifiedAbi) {
_rotateAuthKeyUnverifiedAbi = {
typeParameters: [],
parameters: [new TypeTagU8(), TypeTagVector.u8()],
};
}
return _rotateAuthKeyUnverifiedAbi;
}
/**
* Rotates the authentication key for a given account without verifying the new key.
*
* @param args - The arguments for rotating the authentication key.
* @param args.aptosConfig - The configuration settings for the Aptos network.
* @param args.fromAccount - The account from which the authentication key will be rotated.
* @param args.toNewPublicKey - The new public key to rotate to.
* @returns A simple transaction object that can be submitted to the network.
* @throws Error if the rotation fails or verification fails.
*
* @group Implementation
*/
export async function rotateAuthKeyUnverified(args) {
const { aptosConfig, fromAccount, toNewPublicKey, options } = args;
return generateTransaction({
aptosConfig,
sender: fromAccount.accountAddress,
data: {
function: "0x1::account::rotate_authentication_key_from_public_key",
functionArguments: [
new U8(accountPublicKeyToSigningScheme(toNewPublicKey)), // to scheme
MoveVector.U8(accountPublicKeyToBaseAccountPublicKey(toNewPublicKey).toUint8Array()),
],
abi: rotateAuthKeyUnverifiedAbi(),
},
options,
});
}
export async function getAccountsForPublicKey(args) {
const { aptosConfig, publicKey, options } = args;
const noMultiKey = options?.noMultiKey ?? false;
if (noMultiKey && publicKey instanceof AbstractMultiKey) {
throw new Error("Multi-key accounts are not supported when noMultiKey is true.");
}
const allPublicKeys = [publicKey];
// For Ed25519, we add the both the legacy Ed25519PublicKey and the new AnyPublicKey form.
if (publicKey instanceof AnyPublicKey && publicKey.publicKey instanceof Ed25519PublicKey) {
allPublicKeys.push(publicKey.publicKey);
}
else if (publicKey instanceof Ed25519PublicKey) {
allPublicKeys.push(new AnyPublicKey(publicKey));
}
// Run both operations in parallel
const [defaultAccountData, multiPublicKeys] = await Promise.all([
// Check the provided public key for the default account. In the case of Ed25519, this will check both the legacy Ed25519PublicKey
// and the AnyPublicKey form and may an existing account for each.
Promise.all(allPublicKeys.map(async (publicKey) => {
const addressAndLastTxnVersion = await getDefaultAccountInfoForPublicKey({ aptosConfig, publicKey });
if (addressAndLastTxnVersion) {
return { ...addressAndLastTxnVersion, publicKey };
}
return undefined;
})),
// Get multi-keys for the provided public key if not already a multi-key.
!(publicKey instanceof AbstractMultiKey) && !noMultiKey
? getMultiKeysForPublicKey({ aptosConfig, publicKey, options })
: Promise.resolve([]),
]);
const result = [];
// Add any default accounts that exist to the result.
for (const data of defaultAccountData) {
if (data) {
result.push(data);
}
}
// Add any multi-keys to allPublicKeys
allPublicKeys.push(...multiPublicKeys);
// Get a map of the auth key to the public key for all public keys.
const authKeyToPublicKey = new Map(allPublicKeys.map((key) => [key.authKey().toString(), key]));
// Get the account addresses for the auth keys.
const authKeyAccountAddressPairs = await getAccountAddressesForAuthKeys({
aptosConfig,
authKeys: allPublicKeys.map((key) => key.authKey()),
options,
});
for (const authKeyAccountAddressPair of authKeyAccountAddressPairs) {
// Skip if the account address is already in the result.
// This can happen in the rare edge case where the default account has been rotated but has been rotated back to the original auth key.
if (result.find((r) => r.accountAddress === authKeyAccountAddressPair.accountAddress)) {
continue;
}
// Get the public key for the auth key using the map we created earlier.
const publicKey = authKeyToPublicKey.get(authKeyAccountAddressPair.authKey.toString());
if (!publicKey) {
throw new Error(`No publicKey found for authentication key ${authKeyAccountAddressPair.authKey}. This should never happen.`);
}
result.push({
accountAddress: authKeyAccountAddressPair.accountAddress,
publicKey,
lastTransactionVersion: authKeyAccountAddressPair.lastTransactionVersion,
});
}
// Sort the result by the last transaction version in descending order (most recent first).
return result.sort((a, b) => b.lastTransactionVersion - a.lastTransactionVersion);
}
export async function deriveOwnedAccountsFromSigner(args) {
const { aptosConfig, signer, options } = args;
if (signer instanceof Ed25519PrivateKey || signer instanceof Secp256k1PrivateKey) {
return deriveOwnedAccountsFromPrivateKey({ aptosConfig, privateKey: signer, options });
}
if (signer instanceof Ed25519Account || signer instanceof SingleKeyAccount) {
return deriveOwnedAccountsFromPrivateKey({ aptosConfig, privateKey: signer.privateKey, options });
}
if (isKeylessSigner(signer)) {
return deriveOwnedAccountsFromKeylessSigner({
aptosConfig,
keylessAccount: signer,
options,
});
}
if (signer instanceof MultiKeyAccount) {
if (signer.signers.length === 1) {
return deriveOwnedAccountsFromSigner({ aptosConfig, signer: signer.signers[0], options });
}
}
if (signer instanceof MultiEd25519Account) {
if (signer.signers.length === 1) {
return deriveOwnedAccountsFromPrivateKey({ aptosConfig, privateKey: signer.signers[0], options });
}
}
throw new Error("Unknown signer type");
}
async function deriveOwnedAccountsFromKeylessSigner(args) {
const { aptosConfig, keylessAccount, options } = args;
const addressesAndPublicKeys = await getAccountsForPublicKey({
aptosConfig,
publicKey: keylessAccount.getAnyPublicKey(),
options,
});
const keylessAccountParams = {
proof: keylessAccount.proofOrPromise,
jwt: keylessAccount.jwt,
ephemeralKeyPair: keylessAccount.ephemeralKeyPair,
pepper: keylessAccount.pepper,
verificationKeyHash: keylessAccount.verificationKeyHash,
};
// Structural discriminator: `FederatedKeylessPublicKey` has a `jwkAddress`
// field; plain `KeylessPublicKey` does not. Avoids pulling the concrete
// keyless account classes (and poseidon-lite) into the static import graph.
const isFederated = "jwkAddress" in keylessAccount.publicKey;
const accounts = [];
for (const { accountAddress, publicKey } of addressesAndPublicKeys) {
if (publicKey instanceof AbstractMultiKey) {
if (publicKey.getSignaturesRequired() > 1) {
continue;
}
if (publicKey instanceof MultiEd25519PublicKey) {
throw new Error("Keyless authentication cannot be used for multi-ed25519 accounts. This should never happen.");
}
else if (publicKey instanceof MultiKey) {
accounts.push(new MultiKeyAccount({ multiKey: publicKey, signers: [keylessAccount], address: accountAddress }));
}
}
else if (isFederated) {
// Dynamic import to avoid pulling poseidon-lite into the `/account` sub-path bundle.
const { FederatedKeylessAccount } = await import("../account/FederatedKeylessAccount.js");
accounts.push(FederatedKeylessAccount.create({
...keylessAccountParams,
address: accountAddress,
jwkAddress: keylessAccount.publicKey.jwkAddress,
}));
}
else {
// Dynamic import to avoid pulling poseidon-lite into the `/account` sub-path bundle.
const { KeylessAccount } = await import("../account/KeylessAccount.js");
accounts.push(KeylessAccount.create({
...keylessAccountParams,
address: accountAddress,
}));
}
}
return accounts;
}
async function deriveOwnedAccountsFromPrivateKey(args) {
const { aptosConfig, privateKey, options } = args;
const singleKeyAccount = Account.fromPrivateKey({ privateKey, legacy: false });
const addressesAndPublicKeys = await getAccountsForPublicKey({
aptosConfig,
publicKey: new AnyPublicKey(privateKey.publicKey()),
options,
});
const accounts = [];
// Iterate through the addressesAndPublicKeys and construct the accounts.
for (const { accountAddress, publicKey } of addressesAndPublicKeys) {
if (publicKey instanceof AbstractMultiKey) {
// Skip multi-key accounts with more than 1 signature required as the user does not have full ownership with just 1 private key.
if (publicKey.getSignaturesRequired() > 1) {
continue;
}
// Construct the appropriate multi-key type.
if (publicKey instanceof MultiEd25519PublicKey) {
if (!(privateKey instanceof Ed25519PrivateKey)) {
throw new Error("Private key not Ed25519 for MultiEd25519 signature");
}
accounts.push(new MultiEd25519Account({ publicKey, signers: [privateKey], address: accountAddress }));
}
else if (publicKey instanceof MultiKey) {
accounts.push(new MultiKeyAccount({ multiKey: publicKey, signers: [singleKeyAccount], address: accountAddress }));
}
}
else {
// Check if the public key is a legacy Ed25519PublicKey, if so, we need to use the legacy account constructor.
const isLegacy = publicKey instanceof Ed25519PublicKey;
accounts.push(Account.fromPrivateKey({ privateKey, address: accountAddress, legacy: isLegacy }));
}
}
return accounts;
}
/**
* Gets the multi-keys for a given public key.
*
* This function retrieves the multi-keys that contain the provided public key.
* It performs the following steps:
* 1. Constructs a where condition for the public key where the public key matches the provided public key.
* 2. Queries the indexer for the multi-keys.
* 3. Returns the multi-keys.
*
* @param args.aptosConfig - The configuration settings for the Aptos network.
* @param args.publicKey - The public key to get the multi-keys for. This public key cannot itself be a multi-key.
* @returns The multi-keys (MultiKey or MultiEd25519PublicKey) that contain the given public key.
*/
async function getMultiKeysForPublicKey(args) {
const { aptosConfig, publicKey, options } = args;
if (publicKey instanceof AbstractMultiKey) {
throw new Error("Public key is a multi-key.");
}
const includeUnverified = options?.includeUnverified ?? false;
const anyPublicKey = publicKey instanceof AnyPublicKey ? publicKey : new AnyPublicKey(publicKey);
const baseKey = anyPublicKey.publicKey;
const variant = anyPublicKeyVariantToString(anyPublicKey.variant);
const whereCondition = {
public_key: { _eq: baseKey.toString() },
public_key_type: { _eq: variant },
account_public_key: { _is_null: false },
...(includeUnverified ? {} : { is_public_key_used: { _eq: true } }),
};
const graphqlQuery = {
query: GetAuthKeysForPublicKey,
variables: {
where_condition: whereCondition,
},
};
const { public_key_auth_keys: data } = await queryIndexer({
aptosConfig,
query: graphqlQuery,
originMethod: "getMultiKeysForPublicKey",
});
const authKeys = data
.filter((entry) => entry.account_public_key !== null)
.map((entry) => {
switch (entry.signature_type) {
case "multi_ed25519_signature":
return MultiEd25519PublicKey.deserializeWithoutLength(Deserializer.fromHex(entry.account_public_key));
case "multi_key_signature":
return MultiKey.deserialize(Deserializer.fromHex(entry.account_public_key));
default:
throw new Error(`Unknown multi-signature type: ${entry.signature_type}`);
}
});
return authKeys;
}
/**
* Gets the account addresses for the given authentication keys.
*
* This function retrieves the account addresses that are associated with the provided authentication keys.
* It performs the following steps:
* 1. Constructs a where condition for the authentication keys where auth key matches any of the provided auth keys.
* 2. Queries the indexer for the account addresses and gets the results ordered by the last transaction version (most recent first).
* 3. Returns the account addresses.
*
* @param args.aptosConfig - The configuration settings for the Aptos network.
* @param args.authKeys - The authentication keys to get the account addresses for.
* @param args.options.includeUnverified - Whether to include unverified accounts in the results. Unverified accounts
* are accounts that can be authenticated with the signer, but there is no history of the signer using the account.
* Default is false.
* @returns The account addresses associated with the given authentication keys.
*/
async function getAccountAddressesForAuthKeys(args) {
const { aptosConfig, authKeys, options } = args;
const includeUnverified = options?.includeUnverified ?? false;
if (authKeys.length === 0) {
throw new Error("No authentication keys provided");
}
const whereCondition = {
auth_key: { _in: authKeys.map((authKey) => authKey.toString()) },
...(includeUnverified ? {} : { is_auth_key_used: { _eq: true } }),
};
const graphqlQuery = {
query: GetAccountAddressesForAuthKey,
variables: {
where_condition: whereCondition,
order_by: [{ last_transaction_version: "desc" }],
},
};
const { auth_key_account_addresses: data } = await queryIndexer({
aptosConfig,
query: graphqlQuery,
originMethod: "getAccountAddressesForAuthKeys",
});
return data.map((entry) => ({
authKey: new AuthenticationKey({ data: entry.auth_key }),
accountAddress: new AccountAddress(Hex.hexInputToUint8Array(entry.account_address)),
lastTransactionVersion: Number(entry.last_transaction_version),
}));
}
/**
* Returns the last transaction version that was signed by an account.
*
* If an account was created but has not signed any transactions, the last transaction version will be 0.
*
* @param args.aptosConfig - The configuration settings for the Aptos network.
* @param args.accountAddress - The account address to get the latest transaction version for.
* @returns The last transaction version that was signed by the account.
*/
async function getLatestTransactionVersionForAddress(args) {
const { aptosConfig, accountAddress } = args;
const transactions = await getTransactions({ aptosConfig, accountAddress, options: { limit: 1 } });
if (transactions.length === 0) {
return 0;
}
return Number(transactions[0].version);
}
/**
* Gets the default account info for a given public key. 'Default account' means the account
* is address is the same as the auth key derived from the public key and the account auth key has
* not been rotated.
*
* @param args - The arguments for getting the default account info for a given public key.
* @param args.aptosConfig - The configuration settings for the Aptos network.
* @param args.publicKey - The public key to use to derive the address.
* @returns An object containing the account address and the last transaction version, or undefined if the account does not exist.
*/
async function getDefaultAccountInfoForPublicKey(args) {
const { aptosConfig, publicKey } = args;
const derivedAddress = publicKey.authKey().derivedAddress();
const [lastTransactionVersion, exists] = await Promise.all([
getLatestTransactionVersionForAddress({
aptosConfig,
accountAddress: derivedAddress,
}),
doesAccountExistAtAddress({
aptosConfig,
accountAddress: derivedAddress,
options: { withAuthKey: publicKey.authKey() },
}),
]);
if (exists) {
return { accountAddress: derivedAddress, lastTransactionVersion };
}
return undefined;
}
//# sourceMappingURL=account.js.map