aftermath-ts-sdk
Version:
Aftermath TypeScript SDK
462 lines (461 loc) • 19.4 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Coin = void 0;
const caller_1 = require("../../general/utils/caller");
const helpers_1 = require("../../general/utils/helpers");
const prices_1 = require("../../general/prices/prices");
/**
* The `Coin` class provides functionality to manage and inspect coin types,
* retrieve metadata and prices, and convert balances with respect to coin decimals.
* It can be instantiated with or without a specific `coinType` for convenience.
*
* @example
* ```typescript
*
* const afSdk = new Aftermath("MAINNET");
* await afSdk.init(); // initialize provider
*
* const coin = afSdk.Coin("0x2::sui::SUI");
*
* const metadata = await coin.getCoinMetadata(); // fetch metadata for SUI coin
* ```
*/
class Coin extends caller_1.Caller {
// =========================================================================
// Constructor
// =========================================================================
/**
* Creates a new instance of `Coin`.
*
* @param coinType - The coin's type string (e.g., "0x2::sui::SUI"). If omitted, methods that require a type will need it passed in manually.
* @param config - Optional caller configuration (network, access token).
* @param Provider - An optional `AftermathApi` instance for coin-specific API calls.
*/
constructor(coinType = undefined, config, Provider) {
super(config, "coins");
this.coinType = coinType;
this.Provider = Provider;
// =========================================================================
// Private Helpers
// =========================================================================
/**
* Internal method to retrieve a specialized coin-related API from `AftermathApi`.
* Throws an error if no provider is set.
*/
this.useProvider = () => {
var _a;
const provider = (_a = this.Provider) === null || _a === void 0 ? void 0 : _a.Coin();
if (!provider)
throw new Error("missing AftermathApi Provider");
return provider;
};
this.coinType = coinType;
// Pre-extract segments for convenience
this.coinTypePackageName = this.coinType
? Coin.getCoinTypePackageName(this.coinType)
: "";
this.coinTypeSymbol = this.coinType
? Coin.getCoinTypeSymbol(this.coinType)
: "";
this.innerCoinType = this.coinType
? Coin.getInnerCoinType(this.coinType)
: "";
}
// =========================================================================
// Public Methods
// =========================================================================
// =========================================================================
// Inspections
// =========================================================================
/**
* Retrieves the decimals for multiple coins by calling the Aftermath API for metadata
* and extracting the `decimals` property.
*
* @param inputs - An object containing an array of coin types.
* @returns An object mapping each coin type to a numeric decimal count.
*
* @example
* ```typescript
* const decimals = await coin.getCoinsToDecimals({ coins: ["0x2::sui::SUI", "0x<...>"] });
* console.log(decimals); // { "0x2::sui::SUI": 9, "0x<...>": 6 }
* ```
*/
getCoinsToDecimals(inputs) {
return __awaiter(this, void 0, void 0, function* () {
const { coins } = inputs;
const metadatas = yield this.getCoinMetadatas(inputs);
const coinsToDecimals = metadatas
.map((data) => data.decimals)
.reduce((acc, decimals, index) => {
return Object.assign(Object.assign({}, acc), { [coins[index]]: decimals });
}, {});
return coinsToDecimals;
});
}
/**
* Fetches the metadata (name, symbol, decimals) for this coin type or a provided one,
* caching it if already requested.
*
* @param coin - Optionally override the constructor coinType.
* @returns The `CoinMetadaWithInfo` object containing metadata and optional external references.
* @throws If neither constructor nor argument coinType is available.
*
* @example
* ```typescript
* const metadata = await coin.getCoinMetadata("0x2::sui::SUI");
* console.log(metadata.name, metadata.symbol, metadata.decimals);
* ```
*/
getCoinMetadata(coin) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
if (this.metadata)
return this.metadata;
const coinType = (_a = this.coinType) !== null && _a !== void 0 ? _a : coin;
if (!coinType)
throw new Error("no valid coin type");
const [metadata] = yield this.getCoinMetadatas({ coins: [coinType] });
this.setCoinMetadata(metadata);
return metadata;
});
}
/**
* Fetches metadata for multiple coins at once, returning an array in the same order
* as the coin types requested.
*
* @param inputs - An object with `coins`, an array of coin types.
* @returns An array of `CoinMetadaWithInfo` with length matching `coins`.
*
* @example
* ```typescript
* const metas = await coin.getCoinMetadatas({
* coins: ["0x2::sui::SUI", "0x<custom::TOKEN>"]
* });
* console.log(metas[0].symbol, metas[1].symbol);
* ```
*/
getCoinMetadatas(inputs) {
return __awaiter(this, void 0, void 0, function* () {
return this.fetchApi("metadata", inputs);
});
}
/**
* Manually sets the metadata in this Coin instance, storing it in `this.metadata`.
*
* @param metadata - A `CoinMetadaWithInfo` object to cache in this instance.
*/
setCoinMetadata(metadata) {
this.metadata = metadata;
}
/**
* Retrieves price information (including current price and 24h change) for this coin or a provided coin.
* If already fetched, it returns the cached data.
*
* @param coin - Optionally override the constructor coinType.
* @returns A `CoinPriceInfo` with `price` and `priceChange24HoursPercentage`.
* @throws If no valid coin type is present.
*
* @example
* ```typescript
* const priceInfo = await coin.getPrice("0x2::sui::SUI");
* console.log(priceInfo.price, priceInfo.priceChange24HoursPercentage);
* ```
*/
getPrice(coin) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
if (this.priceInfo !== undefined)
return this.priceInfo;
const coinType = (_a = this.coinType) !== null && _a !== void 0 ? _a : coin;
if (!coinType)
throw new Error("no valid coin type");
const priceInfo = yield new prices_1.Prices(this.config).getCoinPriceInfo({
coin: coinType,
});
// NOTE: do we want this here ? (unexpected behavior)
// if (price <= 0) throw new Error("No price found.")
this.setPriceInfo(priceInfo);
return priceInfo;
});
}
/**
* Manually sets the price info in this Coin instance, storing it in `this.priceInfo`.
*
* @param priceInfo - A `CoinPriceInfo` object to cache in this instance.
*/
setPriceInfo(priceInfo) {
this.priceInfo = priceInfo;
}
/**
* Fetches a list of "verified" coin types from the Aftermath backend. Verified coins
* typically pass certain safety or liquidity checks.
*
* @returns An array of `CoinType` strings that are considered verified.
*
* @example
* ```typescript
* const verified = await coin.getVerifiedCoins();
* console.log(verified); // e.g. ["0x2::sui::SUI", "0x...::MYCOIN", ...]
* ```
*/
getVerifiedCoins() {
return __awaiter(this, void 0, void 0, function* () {
return this.fetchApi("verified");
});
}
}
exports.Coin = Coin;
// =========================================================================
// Constants
// =========================================================================
/**
* Static configuration and defaults for Sui coin types, including the standard
* SUI coin type, default decimals, and coin object type path.
*/
Coin.constants = {
/**
* The canonical coin type string for SUI.
*/
suiCoinType: "0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI",
/**
* The default number of decimals for SUI (9).
*/
suiCoinDecimals: 9,
/**
* The canonical coin object type path for Sui's Move module, used in verifying coin objects.
*/
coinObjectType: "0x0000000000000000000000000000000000000000000000000000000000000002::coin::Coin",
/**
* Default decimals for various blockchains or ecosystems. For instance,
* "sui" => 9, "evm" => 18, etc.
*/
defaultCoinDecimals: {
sui: 9,
evm: 18,
svm: 9,
},
};
// =========================================================================
// Public Static Methods
// =========================================================================
// =========================================================================
// Coin Type
// =========================================================================
/**
* Extracts the Move package name portion from a coin type string.
* E.g., "0x2::sui::SUI" => "sui".
*
* @param coin - The coin type string (e.g., "0x2::sui::SUI").
* @returns The middle segment of the type or empty string if not parseable.
*/
Coin.getCoinTypePackageName = (coin) => {
const splitCoin = coin.split("::");
if (splitCoin.length !== 3)
return "";
const packageName = splitCoin[splitCoin.length - 2];
if (!packageName)
return "";
return packageName;
};
/**
* Extracts the final part of the coin type (the symbol or short name).
* For example, "0x2::sui::SUI" => "SUI".
*
* @param coin - The coin type string.
* @returns The extracted symbol or empty string if not found.
*/
Coin.getCoinTypeSymbol = (coin) => {
const startIndex = coin.lastIndexOf("::") + 2;
// NOTE: should error if coin is not a valid coin type instead of empty string ?
if (startIndex <= 1)
return "";
const foundEndIndex = coin.indexOf(">");
const endIndex = foundEndIndex < 0 ? coin.length : foundEndIndex;
const displayType = coin.slice(startIndex, endIndex);
return displayType;
};
/**
* Extracts the inner generic argument of a coin type if present. E.g.,
* "0x2::coin::Coin<0x2::sui::SUI>" => "0x2::sui::SUI".
*
* @param coin - The coin type with a possible `<...>` suffix.
* @returns The inner type or an empty string if not found.
*/
Coin.getInnerCoinType = (coin) => coin.includes("<") ? coin.split("<")[1].slice(0, -1) : "";
/**
* If a `KeyType` string references a type in angle brackets, extracts the type
* inside. Typically for "0x2::coin::Coin<0x2::mycoin::MYCOIN>" -> "0x2::mycoin::MYCOIN".
*
* @param keyType - The key type string to parse.
* @returns The substring inside `<...>` or the original if no brackets found.
*/
Coin.coinTypeFromKeyType = (keyType) => {
const startIndex = keyType.lastIndexOf("<") + 1;
const endIndex = keyType.indexOf(">", startIndex);
return keyType.slice(startIndex, endIndex);
};
/**
* Checks if a coin type string corresponds to the canonical SUI coin.
*
* @param coin - A coin type string.
* @returns `true` if it matches "0x2::sui::SUI", otherwise `false`.
*/
Coin.isSuiCoin = (coin) => helpers_1.Helpers.stripLeadingZeroesFromType(coin) ===
helpers_1.Helpers.stripLeadingZeroesFromType(Coin.constants.suiCoinType);
/**
* Checks if an object type string is a `Coin<...>` object from the standard Sui Move module.
*
* @param objectType - The object type to test.
* @returns `true` if it matches "0x2::coin::Coin<...>", otherwise `false`.
*/
Coin.isCoinObjectType = (objectType) => helpers_1.Helpers.stripLeadingZeroesFromType(objectType).startsWith(helpers_1.Helpers.stripLeadingZeroesFromType(Coin.constants.coinObjectType));
// =========================================================================
// Helpers
// =========================================================================
/**
* Given a record of coin types => numeric amounts, filters out those
* with zero or negative amounts, returning only the positive pairs.
*
* @param coinAmounts - A record mapping coin types to numeric amounts.
* @returns An object with `coins` array and `amounts` array in matching indexes.
*/
Coin.coinsAndAmountsOverZero = (coinAmounts) => {
// NOTE: will these loops always run in same order (is this a js guarantee or not) ?
const coins = Object.keys(coinAmounts).filter((key) => coinAmounts[key] > 0);
const amounts = Object.values(coinAmounts).filter((amount) => amount > 0);
return { coins, amounts };
};
/**
* Given a record of coin types => bigint balances, filters out those with zero
* or negative balances, returning only the positive pairs.
*
* @param coinsToBalance - A record mapping coin types to bigints.
* @returns An object with `coins` array and `balances` array in matching indexes.
*/
Coin.coinsAndBalancesOverZero = (coinsToBalance) => {
// NOTE: will these loops always run in same order (is this a js guarantee or not) ?
const coins = Object.keys(coinsToBalance).filter((key) => BigInt(coinsToBalance[key]) > BigInt(0));
const balances = Object.values(coinsToBalance)
.map(BigInt)
.filter((amount) => amount > BigInt(0));
return { coins, balances };
};
/**
* Filters a list of `coinTypes` by a textual query, matching against both zero-padded
* and non-padded forms as well as substring checks.
*
* @param inputs - Contains `filter` (the search string) and `coinTypes`.
* @returns An array of coin types that match the filter in either raw or zero-padded form.
*
* @example
* ```typescript
* const filtered = Coin.filterCoinsByType({
* filter: "sui",
* coinTypes: ["0x2::sui::SUI", "0x<...>"]
* });
* ```
*/
Coin.filterCoinsByType = (inputs) => {
var _a;
const filter = inputs.filter.toLowerCase().trim();
return (_a = inputs.coinTypes) === null || _a === void 0 ? void 0 : _a.filter((coinType) => {
try {
return (helpers_1.Helpers.stripLeadingZeroesFromType(coinType)
.toLowerCase()
.includes(helpers_1.Helpers.stripLeadingZeroesFromType(filter)) ||
coinType
.toLowerCase()
.includes(helpers_1.Helpers.addLeadingZeroesToType(filter)));
}
catch (e) { }
return (helpers_1.Helpers.stripLeadingZeroesFromType(coinType)
.toLowerCase()
.includes(filter) || coinType.toLowerCase().includes(filter));
});
};
/**
* Filters a record of coin metadata by a textual query, matching both the coin type
* and the metadata's name/symbol fields.
*
* @param inputs - An object containing `filter` and a record of `coinMetadatas`.
* @returns An array of coin types that match the search criteria.
*/
Coin.filterCoinsByMetadata = (inputs) => {
var _a;
return (_a = Object.entries(inputs.coinMetadatas)) === null || _a === void 0 ? void 0 : _a.filter(([coin, metadata]) => {
const cleanInput = inputs.filter.toLowerCase().trim();
return (coin.startsWith(cleanInput) ||
[metadata.name, metadata.symbol].some((str) => str.toLowerCase().includes(cleanInput)));
}).map(([coin]) => coin);
};
// =========================================================================
// Balance
// =========================================================================
// =========================================================================
// Conversions
// =========================================================================
/**
* Converts a user-friendly decimal number (e.g., 1.5) to a raw on-chain
* integer representation by scaling with the given coin decimals.
* For example, `1.5` with `decimals = 9` => `1500000000n`.
*
* @param balance - The user-friendly balance as a number.
* @param decimals - Number of decimal places for this coin.
* @returns A bigint representing the raw on-chain balance.
*/
Coin.normalizeBalance = (balance, decimals) => BigInt(Math.floor(balance * Math.pow(10, decimals)));
/**
* Scales a raw bigint or numeric `amount` down by `decimals` to get a display-friendly float.
* For example, `1500000000n` with `decimals = 9` => `1.5`.
*
* @param amount - The raw on-chain amount as `bigint` or `number`.
* @param decimals - Number of decimal places for this coin.
* @returns The resulting float as an easily readable balance.
*/
Coin.balanceWithDecimals = (amount, decimals) => {
// TODO: make this conversion via string so no overflow or loss when bigint to number ?
return Number(amount) / Number(Math.pow(10, decimals));
};
/**
* Scales a raw `amount` down by `decimals` and multiplies by a `price` in USD,
* returning a final USD value. E.g., `1500000000n`, `decimals=9`, `price=2.0` => `3.0`.
*
* @param amount - The raw balance as bigint or number.
* @param decimals - The coin decimals.
* @param price - The coin's price in USD.
* @returns The computed float in USD.
*/
Coin.balanceWithDecimalsUsd = (amount, decimals, price) => {
return Coin.balanceWithDecimals(amount, decimals) * price;
};
/**
* Looks up a coin's symbol if it is known in a provided `coinSymbolToCoinTypes`
* record. For instance, if "SUI" => `["0x2::sui::SUI"]`, we can find "SUI" from
* the coin type "0x2::sui::SUI".
*
* @param inputs - An object with `coinType` and `coinSymbolToCoinTypes`.
* @returns The coin symbol string or `undefined` if not found.
*/
Coin.coinSymbolForCoinType = (inputs) => {
const { coinType, coinSymbolToCoinTypes } = inputs;
try {
const fullCoinType = helpers_1.Helpers.addLeadingZeroesToType(coinType);
const foundCoinData = Object.entries(coinSymbolToCoinTypes).find(([, coinsTypes]) => coinsTypes
.map(helpers_1.Helpers.addLeadingZeroesToType)
.includes(fullCoinType));
const foundCoinSymbol = foundCoinData === null || foundCoinData === void 0 ? void 0 : foundCoinData[0];
return foundCoinSymbol;
}
catch (_a) {
return undefined;
}
};