Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 68 additions & 3 deletions src/structures/Amount.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import BigNumber from 'bignumber.js';
import type { APICoin, APIMonetary } from '@tipccjs/tipcc-api-types';
import { TipccClient } from './TipccClient';
import { CryptoCurrency, FiatCurrency } from './Currency';

/**
* A class for storing an API amount. This can be used for either fiats or cryptocurrencies.
Expand All @@ -10,15 +12,78 @@ export class Amount {
/** The raw API BigNumber */
public valueRaw: BigNumber;

/** The value */
public get value(): BigNumber | null {
const currency = this.currency;
if (!currency) return null;
return currency.convertFromRaw(this.valueRaw);
}

/** The currency code */
public currency: string;
public currencyCode: string;

/** The currency */
public get currency(): (FiatCurrency | CryptoCurrency) | null {
if (!this.client) return null;
const currency =
this.client.cryptos.get(this.currencyCode) ??
this.client.fiats.get(this.currencyCode);
if (!currency) return null;
return currency;
}

/** The USD value */
public get usdValue(): BigNumber | null {
if (!this.currency) return null;
const exchangeRate =
this.client?.exchangeRates.get(this.currency.code) ?? null;
if (!exchangeRate) return null;
if (!this.value) return null;
return this.currency.convertByExchangeRate(this.value, exchangeRate);
}

/** The currency emoji (Discord Formatted) */
public get emoji(): string | null {
if (!this.client) return null;
const currencyEmoji = this.client.emojis.find(
(e) => e.name.toUpperCase() === this.currencyCode.toUpperCase(),
);
if (!currencyEmoji) return null;
return `<:${currencyEmoji.name}:${currencyEmoji.id}>`;
}

/** The client that instantiated this */
public client: TipccClient | undefined;

/**
* Create an Amount.
* @param payload An amount from the API
*/
constructor(payload: APIMonetary | APICoin) {
constructor(payload: APIMonetary | APICoin, client?: TipccClient) {
this.valueRaw = BigNumber(payload.value);
this.currency = payload.currency;
this.currencyCode = payload.currency;
if (client) this.client = client;
}

public toString(includeUsd = true): string | null {
const emoji = this.emoji;
const value = this.valueRaw;
const currency = this.currency;

if (!value || !currency) return null;

const unit =
currency.format.units
.filter((u) => u.min)
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
.find((u) => BigNumber(u.min!).lte(this.valueRaw)) ??
currency.format.units[0];

const usdValue = this.usdValue;

return `**${emoji ? `${emoji} ` : ''} ${this.valueRaw
.shiftedBy(-unit.scale)
.toFixed(unit.optionalDecimals ?? unit.scale)
.replace(/\.?0+$/, '')} ${unit.singular}**${includeUsd && usdValue ? ` (≈ $${usdValue.lt(0.01) ? usdValue.toFixed(4) : usdValue.toFixed(2)})` : ''}`;
}
}
87 changes: 46 additions & 41 deletions src/structures/Currency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,27 @@ import type {
APICryptoCurrency,
APIFiatCurrency,
} from '@tipccjs/tipcc-api-types';
import { ExchangeRate } from './ExchangeRate';

/**
* A class for storing an API cryptocurrency.
* A class for storing an API currency.
*
* @category Currency
*/
export class CryptoCurrency {
export class Currency {
/** The currency code */
public code: string;

/** The currency name */
public name: string;

/** The currency icon link */
public icon: string;

/** The currency explorer link */
public explorer: string;

/** An instance of {@link CurrencyFormat} for this currency */
public format: CurrencyFormat;

/**
* Create a CryptoCurrency.
* @param payload The currency from the API
*/
constructor(payload: APICryptoCurrency) {
constructor(payload: { code: string; name: string; format: CurrencyFormat }) {
this.code = payload.code;
this.name = payload.name;
this.icon = payload.icon;
this.explorer = payload.explorer;
this.format = new CurrencyFormat(payload.format);
this.format = payload.format;
}

/**
Expand All @@ -53,46 +42,62 @@ export class CryptoCurrency {
public convertToRaw(value: BigNumber): BigNumber {
return value.shiftedBy(this.format.scale);
}

/**
* Convert the value in this currency to USD as a BigNumber.
*/
public convertByExchangeRate(
value: BigNumber,
exchangeRate: ExchangeRate,
): BigNumber | null {
if (!exchangeRate.usdValue?.value) return null;
return this.convertFromRaw(value).times(exchangeRate.usdValue?.value);
}
}

/**
* A class for storing an API fiat currency.
* A class for storing an API cryptocurrency.
*
* @category Currency
*/
export class FiatCurrency {
/** The currency code */
public code: string;

/** The currency name */
public name: string;
export class CryptoCurrency extends Currency {
/** The currency icon link */
public icon: string;

/** An instance of {@link CurrencyFormat} for this currency */
public format: CurrencyFormat;
/** The currency explorer link */
public explorer: string;

/**
* Create a FiatCurrency.
* Create a CryptoCurrency.
* @param payload The currency from the API
*/
constructor(payload: APIFiatCurrency) {
this.code = payload.code;
this.name = payload.name;
this.format = new CurrencyFormat(payload.format);
}
constructor(payload: APICryptoCurrency) {
super({
code: payload.code,
name: payload.name,
format: new CurrencyFormat(payload.format),
});

/**
* Convert a raw value to a BigNumber in human readable format.
* @param value The raw value
*/
public convertFromRaw(value: BigNumber): BigNumber {
return BigNumber(value).shiftedBy(this.format.scale * -1);
this.icon = payload.icon;
this.explorer = payload.explorer;
}
}

/**
* A class for storing an API fiat currency.
*
* @category Currency
*/
export class FiatCurrency extends Currency {
/**
* Convert a BigNumber value in human readable format to a raw API BigNumber.
* @param value The amount
* Create a FiatCurrency.
* @param payload The currency from the API
*/
public convertToRaw(value: BigNumber): BigNumber {
return value.shiftedBy(this.format.scale);
constructor(payload: APIFiatCurrency) {
super({
code: payload.code,
name: payload.name,
format: new CurrencyFormat(payload.format),
});
}
}
3 changes: 1 addition & 2 deletions src/structures/CurrencyCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,9 @@ export class CurrencyCache<T> extends Array {
* A shortcut to find a currency by code.
* @param code The code to search for
*/
public async get(code: string): Promise<T | null> {
public get(code: string): T | null {
const found = this.find((i) => i.code === code);
if (found) return found;

return null;
}
}
4 changes: 2 additions & 2 deletions src/structures/CurrencyFormatting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class CurrencyUnit {

public minDecimals: number | null;

public optionalDecimals: boolean | null;
public optionalDecimals: number | null;

public min: number | null;

Expand All @@ -41,7 +41,7 @@ export class CurrencyUnit {
this.scale = payload.scale;
this.aliases = payload.aliases ?? [];
this.minDecimals = payload.minDecimals ?? null;
this.optionalDecimals = payload.optionalDecimals ?? null;
this.optionalDecimals = (payload.optionalDecimals as any as number) ?? null;
this.min = payload.min ?? null;
}
}
Expand Down
10 changes: 8 additions & 2 deletions src/structures/ExchangeRate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Amount } from './Amount';
import type { APIExchangeRate } from '@tipccjs/tipcc-api-types';
import { TipccClient } from './TipccClient';

/**
* A class for storing an API exchange rate for a cryptocurrency.
Expand All @@ -16,13 +17,18 @@ export class ExchangeRate {
/** The USD value of {@link ExchangeRate.code currency} */
public usdValue?: Amount;

/** The client that instantiated this */
public client: TipccClient | undefined;

/**
* Create an ExchangeRate.
* @param payload The rate from the API
*/
constructor(payload: APIExchangeRate) {
constructor(payload: APIExchangeRate, client?: TipccClient) {
if (client) this.client = client;
this.code = payload.code;
this.name = payload.name;
if (payload.usd_value) this.usdValue = new Amount(payload.usd_value);
if (payload.usd_value)
this.usdValue = new Amount(payload.usd_value, this.client);
}
}
Loading