/**
* Abstract base class for Token implementations
* This class provides common functionality and interfaces for both Solana and EVM token implementations
* @template P The provider type (Solana Connection or ethers.Provider)
* @template T The transaction type (Solana Transaction or ethers.PopulatedTransaction)
*/
/**
* Interface for token information
*/
export interface TokenInfo {
decimals?: number;
name?: string;
symbol?: string;
uri?: string;
}
export abstract class BaseToken
{
/**
* The provider instance
* @protected
*/
protected _provider: P;
/**
* Cache for token information
* @protected
*/
protected _tokenInfoCache: Map = new Map();
/**
* Constructor for BaseToken
* @param provider The provider instance
*/
constructor(provider: P) {
this._provider = provider;
}
/**
* Set the provider for this Token instance
* @param provider The provider to use
*/
setProvider(provider: P): void {
this._provider = provider;
}
/**
* Get the current provider
* @returns The current provider
*/
getProvider(): P {
return this._provider;
}
/**
* Get the number of decimals for the token
* @param address The token address or mint
* @returns The number of decimals
*/
async getDecimals(address: string): Promise {
// Check if the value is in cache
const tokenInfo = this._tokenInfoCache.get(address);
if (tokenInfo?.decimals !== undefined) {
return tokenInfo.decimals;
}
// If not in cache, fetch and store it
const decimals = await this._fetchDecimals(address);
this._updateTokenInfo(address, { decimals });
return decimals;
}
/**
* Fetch the number of decimals for the token (to be implemented by subclasses)
* @param address The token address or mint
* @returns The number of decimals
* @protected
*/
protected abstract _fetchDecimals(address: string): Promise;
/**
* Get the name of the token
* @param address The token address or mint
* @returns The token name
*/
async getName(address: string): Promise {
// Check if the value is in cache
const tokenInfo = this._tokenInfoCache.get(address);
if (tokenInfo?.name !== undefined) {
return tokenInfo.name;
}
// If not in cache, fetch and store it
const name = await this._fetchName(address);
this._updateTokenInfo(address, { name });
return name;
}
/**
* Fetch the name of the token (to be implemented by subclasses)
* @param address The token address or mint
* @returns The token name
* @protected
*/
protected abstract _fetchName(address: string): Promise;
/**
* Get the symbol of the token
* @param address The token address or mint
* @returns The token symbol
*/
async getSymbol(address: string): Promise {
// Check if the value is in cache
const tokenInfo = this._tokenInfoCache.get(address);
if (tokenInfo?.symbol !== undefined) {
return tokenInfo.symbol;
}
// If not in cache, fetch and store it
const symbol = await this._fetchSymbol(address);
this._updateTokenInfo(address, { symbol });
return symbol;
}
/**
* Fetch the symbol of the token (to be implemented by subclasses)
* @param address The token address or mint
* @returns The token symbol
* @protected
*/
protected abstract _fetchSymbol(address: string): Promise;
/**
* Get the URI for the token metadata
* @param address The token address or mint
* @returns The token URI
*/
async getUri(address: string): Promise {
// Check if the value is in cache
const tokenInfo = this._tokenInfoCache.get(address);
if (tokenInfo?.uri !== undefined) {
return tokenInfo.uri;
}
// If not in cache, fetch and store it
const uri = await this._fetchUri(address);
this._updateTokenInfo(address, { uri });
return uri;
}
/**
* Update token information in the cache
* @param address The token address or mint
* @param info The token information to update
* @private
*/
private _updateTokenInfo(address: string, info: Partial): void {
const existingInfo = this._tokenInfoCache.get(address) || {};
this._tokenInfoCache.set(address, { ...existingInfo, ...info });
}
/**
* Fetch the URI for the token metadata (to be implemented by subclasses)
* @param address The token address or mint
* @returns The token URI
* @protected
*/
protected abstract _fetchUri(address: string): Promise;
/**
* Parse a token amount from human-readable to raw format
* @param address The token address or mint
* @param amount The amount to parse
* @returns The parsed amount
*/
abstract parseAmount(address: string, amount: number | string): Promise;
/**
* Format a raw token amount to human-readable format
* @param address The token address or mint
* @param amount The raw amount to format
* @returns The formatted amount
*/
abstract formatAmount(address: string, amount: any): Promise;
/**
* Get the token balance for a specific owner
* @param owner The address of the token owner
* @param tokenAddress The token address or mint
* @returns The token balance as a number
*/
abstract getBalance(owner: string, tokenAddress: string): Promise;
/**
* Get the allowance amount that a spender is allowed to use on behalf of an owner
* @param owner The address of the token owner
* @param tokenAddress The token address or mint
* @param spender The address of the spender
* @returns The allowance amount as a number
*/
abstract getAllowance(
owner: string,
tokenAddress: string,
spender: string
): Promise;
/**
* Approve a spender to spend tokens on behalf of the owner
* @param tokenAddress The token address or mint
* @param spender The address of the spender
* @param amount The amount to approve
* @returns A populated transaction that can be signed and sent
*/
abstract approve(
tokenAddress: string,
spender: string,
amount: number | string
): Promise;
}