@aptos-labs/ts-sdk
Version:
Aptos TypeScript SDK
769 lines • 33.5 kB
JavaScript
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0
import { AccountAddress } from "../core/index.js";
import { ExpirationStatus, SubdomainExpirationPolicy, } from "../types/index.js";
import { GetNames } from "../types/generated/queries.js";
import { Network } from "../utils/apiEndpoints.js";
import { getEnvVar } from "../utils/helpers.js";
import { queryIndexer } from "./general.js";
import { view } from "./view.js";
import { generateTransaction } from "./transactionSubmission.js";
let GRACE_PERIOD_IN_SECONDS;
const RENEWAL_MONTHS_WINDOW = 6;
export const VALIDATION_RULES_DESCRIPTION = [
"A name must be between 3 and 63 characters long,",
"and can only contain lowercase a-z, 0-9, and hyphens.",
"A name may not start or end with a hyphen.",
].join(" ");
/**
* Validate if a given fragment is a valid ANS segment.
* This function checks the length and character constraints of the fragment to ensure it meets the ANS standards.
*
* @param fragment - A fragment of a name, either the domain or subdomain.
* @returns A boolean indicating if the fragment is a valid fragment.
* @group Implementation
*/
export function isValidANSSegment(fragment) {
if (!fragment)
return false;
if (fragment.length < 3)
return false;
if (fragment.length > 63)
return false;
// only lowercase a-z and 0-9 are allowed, along with -. a domain may not start or end with a hyphen
return /^[a-z\d][a-z\d-]{1,61}[a-z\d]$/.test(fragment);
}
/**
* Checks if an ANS name is valid or not.
*
* @param name - A string of the domain name, which can include or exclude the .apt suffix.
* @group Implementation
*/
export function isValidANSName(name) {
const [first, second, ...rest] = name.replace(/\.apt$/, "").split(".");
if (rest.length > 0) {
throw new Error(`${name} is invalid. A name can only have two parts, a domain and a subdomain separated by a "."`);
}
if (!isValidANSSegment(first)) {
throw new Error(`${first} is not valid. ${VALIDATION_RULES_DESCRIPTION}`);
}
if (second && !isValidANSSegment(second)) {
throw new Error(`${second} is not valid. ${VALIDATION_RULES_DESCRIPTION}`);
}
return {
domainName: second || first,
subdomainName: second ? first : undefined,
};
}
/**
* Determines the status of an ANS name's expiration.
*
* @param name - An ANS name returned from one of the functions of the SDK.
* @param gracePeriod - grace period after expiration
* @returns An ExpirationStatus indicating whether the name is Active, InGracePeriod, or Expired.
* @group Implementation
*/
export function getANSExpirationStatus({ name, gracePeriod, }) {
if (!name)
return ExpirationStatus.Expired;
const gracePeriodMs = gracePeriod * 1000; // Convert to milliseconds
const now = Date.now();
const tldExpirationTime = new Date(name.domain_expiration_timestamp).getTime();
const nameExpirationTime = new Date(name.expiration_timestamp).getTime();
const isTLDExpired = tldExpirationTime < now;
const isNameExpired = nameExpirationTime < now;
const isInGracePeriod = isNameExpired && now - nameExpirationTime < gracePeriodMs;
const isTLDInGracePeriod = isTLDExpired && now - tldExpirationTime < gracePeriodMs;
// If we are a subdomain, if our parent is expired we are always expired
if (name.subdomain && isTLDExpired && !isTLDInGracePeriod) {
return ExpirationStatus.Expired;
}
// If we are a subdomain and our expiration policy is to follow the domain, we
// follow the parent's status (since we know our parent is not fully expired by this point)
if (name.subdomain && name.subdomain_expiration_policy === SubdomainExpirationPolicy.FollowsDomain) {
if (isTLDInGracePeriod)
return ExpirationStatus.InGracePeriod;
return ExpirationStatus.Active;
}
// At this point, we are either a TLD or a subdomain with an independent
// expiration policy, check the name's expiration status
if (isInGracePeriod)
return ExpirationStatus.InGracePeriod;
if (isNameExpired)
return ExpirationStatus.Expired;
return ExpirationStatus.Active;
}
export const LOCAL_ANS_ACCOUNT_PK = getEnvVar("ANS_TEST_ACCOUNT_PRIVATE_KEY") ??
"ed25519-priv-0x37368b46ce665362562c6d1d4ec01a08c8644c488690df5a17e13ba163e20221";
export const LOCAL_ANS_ACCOUNT_ADDRESS = getEnvVar("ANS_TEST_ACCOUNT_ADDRESS") ?? "0x585fc9f0f0c54183b039ffc770ca282ebd87307916c215a3e692f2f8e4305e82";
const NetworkToAnsContract = {
[Network.TESTNET]: "0x5f8fd2347449685cf41d4db97926ec3a096eaf381332be4f1318ad4d16a8497c",
[Network.MAINNET]: "0x867ed1f6bf916171b1de3ee92849b8978b7d1b9e0a8cc982a3d19d535dfd9c0c",
[Network.LOCAL]: LOCAL_ANS_ACCOUNT_ADDRESS,
[Network.CUSTOM]: null,
[Network.DEVNET]: null,
[Network.SHELBYNET]: null,
[Network.NETNA]: null,
};
/**
* Retrieves the address of the ANS contract based on the specified Aptos network configuration.
*
* @param aptosConfig - The configuration object for the Aptos network.
* @param aptosConfig.network - The network for which to retrieve the ANS contract address.
*
* @throws Throws an error if the ANS contract is not deployed to the specified network.
* @group Implementation
*/
function getRouterAddress(aptosConfig) {
const address = NetworkToAnsContract[aptosConfig.network];
if (!address)
throw new Error(`The ANS contract is not deployed to ${aptosConfig.network}`);
return address;
}
const unwrapOption = (option) => {
if (!!option && typeof option === "object" && "vec" in option && Array.isArray(option.vec)) {
return option.vec[0];
}
return undefined;
};
/**
* Retrieve the owner address of a specified domain or subdomain.
*
* @param args - The arguments for retrieving the owner address.
* @param args.aptosConfig - The Aptos configuration object.
* @param args.name - The name of the domain or subdomain to query.
* @returns The account address of the owner, or undefined if not found.
* @group Implementation
*/
export async function getOwnerAddress(args) {
const { aptosConfig, name } = args;
const routerAddress = getRouterAddress(aptosConfig);
const { domainName, subdomainName } = isValidANSName(name);
const res = await view({
aptosConfig,
payload: {
function: `${routerAddress}::router::get_owner_addr`,
functionArguments: [domainName, subdomainName],
},
});
const owner = unwrapOption(res[0]);
return owner ? AccountAddress.from(owner) : undefined;
}
/**
* Registers a domain or subdomain with the specified parameters. This function ensures that the provided names and expiration
* policies are valid before proceeding with the registration process.
*
* @param args - The parameters required for registering a name.
* @param args.aptosConfig - The configuration settings for Aptos.
* @param args.expiration - The expiration details for the registration.
* @param args.name - The name to be registered, which can be a domain or subdomain.
* @param args.sender - The account details of the sender initiating the registration.
* @param args.targetAddress - The target address for the registration, which is the address the name will resolve to.
* @param args.toAddress - The address that will be set as the owner_address in records, defaults to sender if not provided.
* @param args.options - Additional options for the registration process.
* @param args.transferable - Indicates whether the registered name is transferable to another account.
*
* @throws Error if the provided expiration policy is invalid for subdomains.
* @throws Error if the domain does not exist.
* @throws Error if the subdomain expiration time exceeds the domain expiration time.
*
* @returns A transaction object representing the registration process.
* @group Implementation
*/
export async function registerName(args) {
const { aptosConfig, expiration, name, sender, targetAddress, toAddress, options, transferable } = args;
const routerAddress = getRouterAddress(aptosConfig);
const { domainName, subdomainName } = isValidANSName(name);
const hasSubdomainPolicy = expiration.policy === "subdomain:independent" || expiration.policy === "subdomain:follow-domain";
if (subdomainName && !hasSubdomainPolicy) {
throw new Error("Subdomains must have an expiration policy of either 'subdomain:independent' or 'subdomain:follow-domain'");
}
if (hasSubdomainPolicy && !subdomainName) {
throw new Error(`Policy is set to ${expiration.policy} but no subdomain was provided`);
}
if (expiration.policy === "domain") {
const years = expiration.years ?? 1;
if (years !== 1) {
throw new Error("For now, names can only be registered for 1 year at a time");
}
const secondsInYear = 31536000;
const registrationDuration = years * secondsInYear;
const data = {
function: `${routerAddress}::router::register_domain`,
functionArguments: [domainName, registrationDuration, targetAddress, toAddress],
};
const transaction = await generateTransaction({
aptosConfig,
sender: AccountAddress.from(sender).toString(),
data,
options,
});
return {
transaction,
data,
};
}
// We are a subdomain
if (!subdomainName) {
throw new Error(`${expiration.policy} requires a subdomain to be provided.`);
}
const tldExpiration = await getExpiration({ aptosConfig, name: domainName });
if (!tldExpiration) {
throw new Error("The domain does not exist");
}
const expirationDateInMillisecondsSinceEpoch = expiration.policy === "subdomain:independent" ? expiration.expirationDate : tldExpiration;
if (expirationDateInMillisecondsSinceEpoch > tldExpiration) {
throw new Error("The subdomain expiration time cannot be greater than the domain expiration time");
}
const data = {
function: `${routerAddress}::router::register_subdomain`,
functionArguments: [
domainName,
subdomainName,
Math.round(expirationDateInMillisecondsSinceEpoch / 1000),
expiration.policy === "subdomain:follow-domain" ? 1 : 0,
!!transferable,
targetAddress,
toAddress,
],
};
const transaction = await generateTransaction({
aptosConfig,
sender: AccountAddress.from(sender).toString(),
data,
options,
});
return {
transaction,
data,
};
}
/**
* Retrieves the expiration time of a specified domain or subdomain in epoch milliseconds.
*
* @param args - The arguments for the function.
* @param args.aptosConfig - The configuration object for Aptos.
* @param args.name - The name of the domain or subdomain to check.
* @returns The expiration time in epoch milliseconds, or undefined if an error occurs.
* @group Implementation
*/
export async function getExpiration(args) {
const { aptosConfig, name } = args;
const routerAddress = getRouterAddress(aptosConfig);
const { domainName, subdomainName } = isValidANSName(name);
try {
const res = await view({
aptosConfig,
payload: {
function: `${routerAddress}::router::get_expiration`,
functionArguments: [domainName, subdomainName],
},
});
// Normalize expiration time from epoch seconds to epoch milliseconds
return Number(res[0]) * 1000;
}
catch {
return undefined;
}
}
/**
* Retrieves the primary name associated with a given account address.
* This function helps in obtaining the complete domain name by combining the subdomain and domain names.
*
* @param args - The arguments for retrieving the primary name.
* @param args.aptosConfig - The Aptos configuration object.
* @param args.address - The account address for which to retrieve the primary name.
* @returns The primary name as a string, or undefined if no domain name exists.
* @group Implementation
*/
export async function getPrimaryName(args) {
const { aptosConfig, address } = args;
const routerAddress = getRouterAddress(aptosConfig);
const res = await view({
aptosConfig,
payload: {
function: `${routerAddress}::router::get_primary_name`,
functionArguments: [AccountAddress.from(address).toString()],
},
});
const domainName = unwrapOption(res[1]);
const subdomainName = unwrapOption(res[0]);
if (!domainName)
return undefined;
return [subdomainName, domainName].filter(Boolean).join(".");
}
/**
* Sets the primary name for the specified account, allowing for the association of a domain or subdomain with the account.
* If no name is provided, it clears the existing primary name.
*
* @param args - The arguments for setting the primary name.
* @param args.aptosConfig - The Aptos configuration object.
* @param args.sender - The account that is sending the transaction.
* @param args.name - The name to set as the primary name. If omitted, the function will clear the primary name.
* @param args.options - Optional transaction generation options.
* @returns A transaction object representing the operation.
* @group Implementation
*/
export async function setPrimaryName(args) {
const { aptosConfig, sender, name, options } = args;
const routerAddress = getRouterAddress(aptosConfig);
if (!name) {
const data = {
function: `${routerAddress}::router::clear_primary_name`,
functionArguments: [],
};
const transaction = await generateTransaction({
aptosConfig,
sender: AccountAddress.from(sender).toString(),
data,
options,
});
return {
transaction,
data,
};
}
const { domainName, subdomainName } = isValidANSName(name);
const data = {
function: `${routerAddress}::router::set_primary_name`,
functionArguments: [domainName, subdomainName],
};
const transaction = await generateTransaction({
aptosConfig,
sender: AccountAddress.from(sender).toString(),
data,
options,
});
return {
transaction,
data,
};
}
/**
* Retrieves the target address associated with a given domain name and subdomain name.
* The target address is different from the owner address - it's the address this name
* resolves to, which may be different from who owns the name.
*
* @param args - The arguments for retrieving the target address.
* @param args.aptosConfig - The Aptos configuration object.
* @param args.name - The name of the domain, which may include a subdomain.
* @returns The target address as an AccountAddress, or undefined if not found.
* @group Implementation
*/
export async function getTargetAddress(args) {
const { aptosConfig, name } = args;
const routerAddress = getRouterAddress(aptosConfig);
const { domainName, subdomainName } = isValidANSName(name);
const res = await view({
aptosConfig,
payload: {
function: `${routerAddress}::router::get_target_addr`,
functionArguments: [domainName, subdomainName],
},
});
const target = unwrapOption(res[0]);
return target ? AccountAddress.from(target) : undefined;
}
/**
* Sets the target address for a specified domain and subdomain in the Aptos network.
* This function helps to associate a given address with a domain name, allowing for easier access and management of resources.
*
* @param args - The arguments for setting the target address.
* @param args.aptosConfig - The configuration settings for the Aptos network.
* @param args.sender - The account that is sending the transaction.
* @param args.name - The name of the domain or subdomain to be set.
* @param args.address - The address to be associated with the domain or subdomain.
* @param args.options - Optional parameters for generating the transaction.
*
* @returns A transaction object representing the set target address operation.
* @group Implementation
*/
export async function setTargetAddress(args) {
const { aptosConfig, sender, name, address, options } = args;
const routerAddress = getRouterAddress(aptosConfig);
const { domainName, subdomainName } = isValidANSName(name);
const data = {
function: `${routerAddress}::router::set_target_addr`,
functionArguments: [domainName, subdomainName, address],
};
const transaction = await generateTransaction({
aptosConfig,
sender: AccountAddress.from(sender).toString(),
data,
options,
});
return {
transaction,
data,
};
}
/**
* Clears the target address for a specified domain and subdomain in the Aptos network.
* This function removes the target address association, effectively clearing where the name resolves to.
*
* @param args - The arguments for clearing the target address.
* @param args.aptosConfig - The configuration settings for the Aptos network.
* @param args.sender - The account that is sending the transaction.
* @param args.name - The name of the domain or subdomain to clear the target address for.
* @param args.options - Optional parameters for generating the transaction.
*
* @returns A transaction object representing the clear target address operation.
* @group Implementation
*/
export async function clearTargetAddress(args) {
const { aptosConfig, sender, name, options } = args;
const routerAddress = getRouterAddress(aptosConfig);
const { domainName, subdomainName } = isValidANSName(name);
const data = {
function: `${routerAddress}::router::clear_target_addr`,
functionArguments: [domainName, subdomainName ?? null],
};
const transaction = await generateTransaction({
aptosConfig,
sender: AccountAddress.from(sender).toString(),
data,
options,
});
return {
transaction,
data,
};
}
/**
* Retrieves the active Aptos name associated with the specified domain and subdomain.
*
* @param args - The parameters for the function.
* @param args.aptosConfig - The configuration object for Aptos.
* @param args.name - The name to look up, which includes the domain and optional subdomain.
* @returns The active Aptos name if it exists; otherwise, returns undefined.
* @group Implementation
*/
export async function getName(args) {
const { aptosConfig, name } = args;
const gracePeriod = await getANSGracePeriod({ aptosConfig });
const { domainName, subdomainName = "" } = isValidANSName(name);
const where = {
domain: { _eq: domainName },
subdomain: { _eq: subdomainName },
};
const data = await queryIndexer({
aptosConfig,
query: {
query: GetNames,
variables: {
where_condition: where,
limit: 1,
},
},
originMethod: "getName",
});
// Convert the expiration_timestamp from an ISO string to milliseconds since epoch
const res = data.current_aptos_names[0];
return res ? sanitizeANSName({ aptosConfig, name: res, gracePeriod }) : undefined;
}
/**
* Retrieves all the current Aptos names owned by an account. This uses the
* owner_address field, not the registered_address field.
*
* @param args - The arguments for retrieving account names.
* @param args.aptosConfig - The configuration object for Aptos.
* @param args.options - Optional parameters for querying account names.
* @param args.options.limit - The maximum number of names to retrieve.
* @param args.options.offset - The number of names to skip before starting to collect the result set.
* @param args.options.orderBy - The field by which to order the results.
* @param args.options.where - Additional conditions to filter the results.
* @param args.accountAddress - The address of the account for which to retrieve names.
*
* @returns An array of sanitized Aptos names associated with the specified account address.
* @group Implementation
*/
export async function getAccountNames(args) {
const { aptosConfig, options, accountAddress } = args;
const gracePeriod = await getANSGracePeriod({ aptosConfig });
const expirationDate = await getANSExpirationDate({ aptosConfig });
const data = await queryIndexer({
aptosConfig,
originMethod: "getAccountNames",
query: {
query: GetNames,
variables: {
limit: options?.limit,
offset: options?.offset,
order_by: options?.orderBy,
where_condition: {
owner_address: { _eq: accountAddress.toString() },
expiration_timestamp: { _gte: expirationDate },
...(args.options?.where ?? {}),
},
},
},
});
const names = data.current_aptos_names.map((name) => sanitizeANSName({ aptosConfig, name, gracePeriod }));
const total = data.current_aptos_names_aggregate.aggregate?.count ?? 0;
return { names, total };
}
/**
* Retrieves the list of top-level domains owned by a specified account,
* using the owner_address field.
*
* @param args - The arguments for retrieving account domains.
* @param args.aptosConfig - The Aptos configuration object.
* @param args.options - Optional parameters for the query.
* @param args.options.limit - The maximum number of results to return.
* @param args.options.offset - The number of results to skip before starting to collect the result set.
* @param args.options.orderBy - The field by which to order the results.
* @param args.options.where - Additional conditions to filter the results.
* @param args.options.where.owner_address - The address of the account whose domains are being queried.
* @param args.options.where.expiration_timestamp - The minimum expiration timestamp for the domains.
* @param args.options.where.subdomain - The specific subdomain to filter by.
*
* @returns An array of sanitized domain names owned by the specified account.
* @group Implementation
*/
export async function getAccountDomains(args) {
const { aptosConfig, options, accountAddress } = args;
const gracePeriod = await getANSGracePeriod({ aptosConfig });
const expirationDate = await getANSExpirationDate({ aptosConfig });
const data = await queryIndexer({
aptosConfig,
originMethod: "getAccountDomains",
query: {
query: GetNames,
variables: {
limit: options?.limit,
offset: options?.offset,
order_by: options?.orderBy,
where_condition: {
owner_address: { _eq: accountAddress.toString() },
expiration_timestamp: { _gte: expirationDate },
subdomain: { _eq: "" },
...(args.options?.where ?? {}),
},
},
},
});
const names = data.current_aptos_names.map((name) => sanitizeANSName({ aptosConfig, name, gracePeriod }));
const total = data.current_aptos_names_aggregate.aggregate?.count ?? 0;
return { names, total };
}
/**
* Retrieves a list of subdomains owned by a specified account address, determined
* by the owner_address field.
*
* @param args - The arguments for retrieving account subdomains.
* @param args.aptosConfig - The configuration object for Aptos.
* @param args.options - Optional parameters for the query.
* @param args.options.limit - The maximum number of results to return.
* @param args.options.offset - The number of results to skip before starting to collect the result set.
* @param args.options.orderBy - The field by which to order the results.
* @param args.options.where - Additional conditions to filter the results.
* @param args.options.where.owner_address - The address of the account to filter by.
* @param args.options.where.expiration_timestamp - The expiration timestamp to filter by.
* @param args.options.where.subdomain - The subdomain condition to filter by.
* @param args.accountAddress - The address of the account whose subdomains are being queried.
* @group Implementation
*/
export async function getAccountSubdomains(args) {
const { aptosConfig, options, accountAddress } = args;
const gracePeriod = await getANSGracePeriod({ aptosConfig });
const data = await queryIndexer({
aptosConfig,
originMethod: "getAccountSubdomains",
query: {
query: GetNames,
variables: {
limit: options?.limit,
offset: options?.offset,
order_by: options?.orderBy,
where_condition: {
owner_address: { _eq: accountAddress.toString() },
subdomain: { _neq: "" },
...(args.options?.where ?? {}),
},
},
},
});
const names = data.current_aptos_names.map((name) => sanitizeANSName({ aptosConfig, name, gracePeriod }));
const total = data.current_aptos_names_aggregate.aggregate?.count ?? 0;
return { names, total };
}
/**
* Retrieve the active subdomains associated with a specified domain,
* regardless of who is in possession of the domain.
*
* This function queries by domain name only, not by owner, so it will return
* all subdomains of a domain even if they're owned by different accounts.
*
* @param args - The arguments for retrieving subdomains.
* @param args.aptosConfig - The configuration settings for Aptos.
* @param args.options - Optional parameters for the query.
* @param args.options.limit - The maximum number of results to return.
* @param args.options.offset - The number of results to skip before starting to collect the results.
* @param args.options.orderBy - The field by which to order the results.
* @param args.options.where - Additional conditions to filter the results.
* @param args.domain - The domain for which to retrieve subdomains.
*
* @returns An array of active subdomain names.
* @group Implementation
*/
export async function getDomainSubdomains(args) {
const { aptosConfig, options, domain } = args;
const gracePeriod = await getANSGracePeriod({ aptosConfig });
const data = await queryIndexer({
aptosConfig,
originMethod: "getDomainSubdomains",
query: {
query: GetNames,
variables: {
limit: options?.limit,
offset: options?.offset,
order_by: options?.orderBy,
where_condition: {
domain: { _eq: domain },
subdomain: { _neq: "" },
...(args.options?.where ?? {}),
},
},
},
});
const names = data.current_aptos_names.map((name) => sanitizeANSName({ aptosConfig, name, gracePeriod }));
const total = data.current_aptos_names_aggregate.aggregate?.count ?? 0;
return { names, total };
}
/**
* This function returns the expiration date in which a name is fully expired as
* defined by the contract. The grace period allows for names to be past
* expiration for a certain amount of time before they are released to the
* public. The names will not function as normal, but the owner can renew
* without others taking ownership of the name. At the time of writing, the
* contract specified 30 days.
*
* @param args - The arguments for the function.
* @param args.aptosConfig - An AptosConfig object containing the configuration settings.
* @returns The expiration date in ISO 8601 format.
* @group Implementation
*/
async function getANSExpirationDate(args) {
const { aptosConfig } = args;
const gracePeriodInSeconds = await getANSGracePeriod({ aptosConfig });
const gracePeriodInDays = gracePeriodInSeconds / 60 / 60 / 24;
const now = () => new Date();
return new Date(now().setDate(now().getDate() - gracePeriodInDays)).toISOString();
}
/**
* This function returns the grace period in seconds as defined by the contract.
* A name that is past expiration but within the grace period is considered in
* grace period and can't be claimed by others.
*
* @param args - The arguments for the function.
* @param args.aptosConfig - An AptosConfig object containing the configuration settings.
* @returns The grace period in seconds.
* @group Implementation
*/
export async function getANSGracePeriod(args) {
if (GRACE_PERIOD_IN_SECONDS) {
return GRACE_PERIOD_IN_SECONDS;
}
const { aptosConfig } = args;
const routerAddress = getRouterAddress(aptosConfig);
const [gracePeriodInSeconds] = await view({
aptosConfig,
payload: {
function: `${routerAddress}::config::reregistration_grace_sec`,
functionArguments: [],
},
});
GRACE_PERIOD_IN_SECONDS = gracePeriodInSeconds;
return gracePeriodInSeconds;
}
/**
* Renews a domain for a specified duration. This function allows you to extend the registration of a domain for one year.
*
* @param args - The parameters required to renew the domain.
* @param args.aptosConfig - The configuration settings for Aptos.
* @param args.sender - The account that is sending the renewal transaction.
* @param args.name - The name of the domain to renew.
* @param args.years - The number of years to renew the domain for. Currently, only 1 year renewals are supported. (optional, default is 1)
* @param args.options - Additional options for generating the transaction. (optional)
* @throws Error if the name contains a subdomain or if the years parameter is not equal to 1.
* @group Implementation
*/
export async function renewDomain(args) {
const { aptosConfig, sender, name, years = 1, options } = args;
const routerAddress = getRouterAddress(aptosConfig);
const renewalDuration = years * 31536000;
const { domainName, subdomainName } = isValidANSName(name);
if (subdomainName) {
throw new Error("Subdomains cannot be renewed");
}
if (years !== 1) {
throw new Error("Currently, only 1 year renewals are supported");
}
const data = {
function: `${routerAddress}::router::renew_domain`,
functionArguments: [domainName, renewalDuration],
};
const transaction = await generateTransaction({
aptosConfig,
sender: AccountAddress.from(sender).toString(),
data,
options,
});
return {
transaction,
data,
};
}
/**
* The indexer returns ISO strings for expiration, however the contract works in
* epoch milliseconds. This function converts the ISO string to epoch
* milliseconds. In the future, if other properties need sanitization, this can
* be extended.
*
* @param name - The ANS name response to sanitize.
* @param aptosConfig - config for the aptos client
* @param gracePeriod - grace period after expiration
* @param name.expiration_timestamp - The expiration timestamp in ISO string format.
* @group Implementation
*/
function sanitizeANSName({ name, aptosConfig, gracePeriod, }) {
const expiration_timestamp = `${name.expiration_timestamp}Z`;
const domain_expiration_timestamp = `${name.domain_expiration_timestamp}Z`;
const isSubdomain = !!name.subdomain;
const expirationPolicy = name.subdomain_expiration_policy;
const expiration = isSubdomain && expirationPolicy === SubdomainExpirationPolicy.FollowsDomain
? domain_expiration_timestamp
: expiration_timestamp;
const expiration_status = getANSExpirationStatus({ aptosConfig, name, gracePeriod });
let isInRenewablePeriod = false;
if (expiration_status === ExpirationStatus.InGracePeriod) {
isInRenewablePeriod = true;
}
else if (expiration_status === ExpirationStatus.Active) {
// Check if the name is within the renewal window (6 months before expiration)
const renewalWindowDate = new Date();
renewalWindowDate.setMonth(renewalWindowDate.getMonth() + RENEWAL_MONTHS_WINDOW);
const expirationDate = new Date(expiration);
isInRenewablePeriod = expirationDate < renewalWindowDate;
}
// Pass nullable indexer fields through as `undefined` rather than fabricating
// placeholder values ("N/A", "v2", false, ""). Consumers that need these
// fields should treat a missing value as a bad row from the indexer.
// `subdomain_expiration_policy` keeps a default of `FollowsDomain` because
// that is the contract's semantic fallback for subdomains, not a fabrication.
return {
domain: name.domain ?? undefined,
subdomain: name.subdomain || undefined,
expiration_timestamp,
expiration_status,
domain_expiration_timestamp,
expiration: new Date(expiration),
token_standard: name.token_standard ?? undefined,
is_primary: name.is_primary ?? undefined,
subdomain_expiration_policy: name.subdomain_expiration_policy ?? SubdomainExpirationPolicy.FollowsDomain,
owner_address: name.owner_address ?? undefined,
registered_address: name.registered_address ?? undefined,
isInRenewablePeriod,
};
}
//# sourceMappingURL=ans.js.map