Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
wip fixing currency manager --> chains as objects
  • Loading branch information
alexandre-abrioux committed Feb 21, 2024
commit 1ef0da3d86c27e0a062addc5f0170daed59db83d
82 changes: 56 additions & 26 deletions packages/currency/src/currency-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getSupportedERC20Currencies } from './erc20';
import { getSupportedERC777Currencies } from './erc777';
import { getHash } from './getHash';
import {
Currency,
CurrencyDefinition,
CurrencyInput,
ICurrencyManager,
Expand All @@ -19,6 +20,10 @@ import { ChainManager } from '@requestnetwork/chain';

const { BTC, ERC20, ERC777, ETH, ISO4217 } = RequestLogicTypes.CURRENCY;

type MixedCurrencyType<TMeta = unknown> =
| (CurrencyInput & { id?: string; hash?: string; meta?: TMeta })
| CurrencyDefinition<TMeta>;

/**
* Handles a list of currencies and provide features to retrieve them, as well as convert to/from storage format
*/
Expand All @@ -37,11 +42,17 @@ export class CurrencyManager<TMeta = unknown> implements ICurrencyManager<TMeta>
* @param chainManager A ChainManager instance that describes the supported underlying chains
*/
constructor(
inputCurrencies: (CurrencyInput & { id?: string; meta?: TMeta })[],
inputCurrencies: MixedCurrencyType<TMeta>[],
legacyTokens?: LegacyTokenMap,
conversionPairs?: AggregatorsMap,
chainManager?: ChainTypes.IChainManager,
) {
this.legacyTokens = legacyTokens || CurrencyManager.getDefaultLegacyTokens();
this.conversionPairs = conversionPairs || CurrencyManager.getDefaultConversionPairs();
this.chainManager = chainManager || ChainManager.current();
ChainManager.setCurrent(this.chainManager);

// initialize currencies
this.knownCurrencies = [];
for (const input of inputCurrencies) {
const currency = CurrencyManager.fromInput(input);
Expand All @@ -50,10 +61,6 @@ export class CurrencyManager<TMeta = unknown> implements ICurrencyManager<TMeta>
}
this.knownCurrencies.push(currency);
}
this.legacyTokens = legacyTokens || CurrencyManager.getDefaultLegacyTokens();
this.conversionPairs = conversionPairs || CurrencyManager.getDefaultConversionPairs();
this.chainManager = chainManager || ChainManager.current();
ChainManager.setCurrent(this.chainManager);
}

/**
Expand All @@ -80,7 +87,9 @@ export class CurrencyManager<TMeta = unknown> implements ICurrencyManager<TMeta>

const currencyFromId = this.fromId(currencyIdentifier);

if (currencyFromId) return currencyFromId;
if (currencyFromId) {
return currencyFromId;
}

const parts = currencyIdentifier.split('-');
const currencyFromSymbol =
Expand Down Expand Up @@ -111,7 +120,10 @@ export class CurrencyManager<TMeta = unknown> implements ICurrencyManager<TMeta>
(x) =>
(x.type === ERC20 || x.type === ERC777) &&
x.address === address &&
(!network || x.network === network),
(!network ||
(typeof network === 'string'
? x.network.name === network
: x.network.name === network.name)),
);
if (matches.length > 1) {
const networks = matches.map((x) => ('network' in x ? x.network : '')).join(', ');
Expand Down Expand Up @@ -139,7 +151,7 @@ export class CurrencyManager<TMeta = unknown> implements ICurrencyManager<TMeta>
return this.knownCurrencies.find(
(x) =>
x.symbol.toUpperCase() === symbol &&
(!network || ('network' in x && x.network === ChainManager.getName(network))) &&
(!network || ('network' in x && x.network.name === ChainManager.getName(network))) &&
(!network ||
typeof network === 'string' ||
this.chainManager.ecosystems[network.ecosystem].currencyTypes.includes(x.type)),
Expand All @@ -153,7 +165,7 @@ export class CurrencyManager<TMeta = unknown> implements ICurrencyManager<TMeta>
return this.knownCurrencies.find(
(x) =>
x.hash.toLowerCase() === hash.toLowerCase() &&
(!network || ('network' in x && x.network === ChainManager.getName(network))) &&
(!network || ('network' in x && x.network.name === ChainManager.getName(network))) &&
(!network ||
typeof network === 'string' ||
this.chainManager.ecosystems[network.ecosystem].currencyTypes.includes(x.type)),
Expand All @@ -176,8 +188,8 @@ export class CurrencyManager<TMeta = unknown> implements ICurrencyManager<TMeta>
x.type === currency.type &&
(((x.type === ERC20 || x.type === ERC777) &&
currency.value === x.address &&
x.network === networkOrDefault) ||
((x.type === ETH || x.type === BTC) && x.network === networkOrDefault) ||
x.network.name === networkOrDefault) ||
((x.type === ETH || x.type === BTC) && x.network.name === networkOrDefault) ||
(x.symbol === currency.value && !currency.network)),
);
}
Expand All @@ -190,7 +202,7 @@ export class CurrencyManager<TMeta = unknown> implements ICurrencyManager<TMeta>
network: string | ChainTypes.IChain,
): CurrencyDefinition<TMeta> | undefined {
return this.knownCurrencies.find(
(x) => x.type === type && x.network === ChainManager.getName(network),
(x) => x.type === type && x.network.name === ChainManager.getName(network),
);
}

Expand Down Expand Up @@ -218,7 +230,7 @@ export class CurrencyManager<TMeta = unknown> implements ICurrencyManager<TMeta>
hash,
meta,
...input
}: CurrencyInput & { id?: string; hash?: string; meta?: TMeta }): CurrencyDefinition<TMeta> {
}: MixedCurrencyType<TMeta>): CurrencyDefinition<TMeta> {
if ('address' in input) {
if (input.address.startsWith('0x') && input.address.length === 42) {
input.address = utils.getAddress(input.address);
Expand All @@ -229,7 +241,16 @@ export class CurrencyManager<TMeta = unknown> implements ICurrencyManager<TMeta>
hash: hash || getHash(CurrencyManager.toStorageCurrency(input)),
meta: meta as TMeta,
...input,
};
network:
'network' in input
? typeof input.network === 'string'
? ChainManager.current().fromName(
input.network,
ChainManager.current().getEcosystemsByCurrencyType(input.type),
)
: input.network
: undefined,
} as CurrencyDefinition<TMeta>;
}

/**
Expand All @@ -244,40 +265,46 @@ export class CurrencyManager<TMeta = unknown> implements ICurrencyManager<TMeta>
/**
* Converts a currency to the storage format (ICurrency)
*/
static toStorageCurrency(currency: CurrencyInput): StorageCurrency {
static toStorageCurrency(currency: CurrencyInput | Currency): StorageCurrency {
return {
type: currency.type,
value:
currency.type === ERC20 || currency.type === ERC777 ? currency.address : currency.symbol,
network: currency.type === ISO4217 ? undefined : currency.network,
network:
currency.type === ISO4217
? undefined
: typeof currency.network === 'string'
? currency.network
: currency.network.name,
};
}

/**
* Validates an address for a given currency.
* Throws if the currency is an ISO4217 currency.
*/
validateAddress(address: string, currency: CurrencyInput | StorageCurrency): boolean {
validateAddress(address: string, currency: CurrencyDefinition | StorageCurrency): boolean {
if (currency.type === RequestLogicTypes.CURRENCY.ISO4217) {
throw new Error(`Could not validate an address for an ISO4217 currency`);
}
const chainName =
currency.network &&
(typeof currency.network === 'string' ? currency.network : currency.network.name);
switch (currency.type) {
case RequestLogicTypes.CURRENCY.ETH:
case RequestLogicTypes.CURRENCY.ERC20:
case RequestLogicTypes.CURRENCY.ERC777:
if (
this.chainManager.ecosystems[ChainTypes.ECOSYSTEM.NEAR].isChainSupported(currency.network)
) {
return isValidNearAddress(address, currency.network);
} else if (currency.network === 'tron' || currency.network === 'solana') {
return addressValidator.validate(address, currency.network);
if (this.chainManager.ecosystems[ChainTypes.ECOSYSTEM.NEAR].isChainSupported(chainName)) {
return isValidNearAddress(address, chainName);
} else if (chainName === 'tron' || chainName === 'solana') {
return addressValidator.validate(address, chainName);
}
return addressValidator.validate(address, 'ETH');
case RequestLogicTypes.CURRENCY.BTC:
return addressValidator.validate(
address,
'BTC',
currency.network === 'testnet' ? 'testnet' : 'prod',
chainName === 'testnet' ? 'testnet' : 'prod',
);
default:
throw new Error(`Could not validate an address for an unknown currency type`);
Expand All @@ -292,8 +319,9 @@ export class CurrencyManager<TMeta = unknown> implements ICurrencyManager<TMeta>
currency.type === RequestLogicTypes.CURRENCY.ISO4217 ||
currency.type === RequestLogicTypes.CURRENCY.ETH ||
currency.type === RequestLogicTypes.CURRENCY.BTC
)
) {
return true;
}
return this.validateAddress(currency.value, currency);
}

Expand Down Expand Up @@ -334,7 +362,9 @@ export class CurrencyManager<TMeta = unknown> implements ICurrencyManager<TMeta>
* Returns a default instance of CurrencyManager based on default lists
*/
static getDefault(): CurrencyManager {
if (this.defaultInstance) return this.defaultInstance;
if (this.defaultInstance) {
return this.defaultInstance;
}

this.defaultInstance = new CurrencyManager(
CurrencyManager.getDefaultList(),
Expand Down
28 changes: 16 additions & 12 deletions packages/currency/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ type TokenAddress = string;
type TokenDefinition = { name: string; symbol: string; decimals: number; id?: string };
export type TokenMap = Record<TokenAddress, TokenDefinition>;

/** Native Currency types */
export type NativeCurrencyType = RequestLogicTypes.CURRENCY.BTC | RequestLogicTypes.CURRENCY.ETH;

/**
* A native blockchain token (ETH, MATIC, ETH-rinkeby...)
*/
export type NativeCurrency = {
type: NativeCurrencyType;
symbol: string;
decimals: number;
network: ChainTypes.IChain;
Expand All @@ -20,6 +24,7 @@ export type NativeCurrency = {
* A Fiat currency (EUR, USD...)
*/
export type ISO4217Currency = {
type: RequestLogicTypes.CURRENCY.ISO4217;
symbol: string;
decimals: number;
};
Expand All @@ -28,6 +33,7 @@ export type ISO4217Currency = {
* An ERC20 token (DAI, USDT...)
*/
export type ERC20Currency = {
type: RequestLogicTypes.CURRENCY.ERC20;
symbol: string;
decimals: number;
network: ChainTypes.IEvmChain | ChainTypes.INearChain | ChainTypes.IDeclarativeChain;
Expand All @@ -38,43 +44,36 @@ export type ERC20Currency = {
* An ERC777 SuperToken (DAIx, USDCx...)
*/
export type ERC777Currency = {
type: RequestLogicTypes.CURRENCY.ERC777;
symbol: string;
decimals: number;
network: ChainTypes.IEvmChain;
address: string;
};

/** Native Currency types */
export type NativeCurrencyType = RequestLogicTypes.CURRENCY.BTC | RequestLogicTypes.CURRENCY.ETH;

/**
* The minimum properties of a native Currency
*/
export type NativeCurrencyInput = Omit<NativeCurrency, 'network'> & {
type: NativeCurrencyType;
network: string;
};

/**
* The minimum properties of an ISO4217 Currency
*/
export type ISO4217CurrencyInput = ISO4217Currency & {
type: RequestLogicTypes.CURRENCY.ISO4217;
};
export type ISO4217CurrencyInput = ISO4217Currency;

/**
* The minimum properties of an ERC20 Currency
*/
export type ERC20CurrencyInput = Omit<ERC20Currency, 'network'> & {
type: RequestLogicTypes.CURRENCY.ERC20;
network: string;
};

/**
* The minimum properties of an ERC777 Currency
*/
export type ERC777CurrencyInput = Omit<ERC777Currency, 'network'> & {
type: RequestLogicTypes.CURRENCY.ERC777;
network: string;
};

Expand All @@ -87,11 +86,16 @@ export type CurrencyInput =
| ERC20CurrencyInput
| ERC777CurrencyInput;

/**
* The different representations of a currency
*/
export type Currency = NativeCurrency | ISO4217Currency | ERC20Currency | ERC777Currency;

/**
* The description of Currency, its core properties and some computed properties.
* `meta` enables applications to add any metadata they need to a Currency
*/
export type CurrencyDefinition<TMeta = unknown> = CurrencyInput & {
export type CurrencyDefinition<TMeta = unknown> = Currency & {
id: string;
hash: string;
meta: TMeta;
Expand Down Expand Up @@ -137,13 +141,13 @@ export interface ICurrencyManager<TMeta = unknown> {
currency: Pick<CurrencyDefinition, 'hash'>,
network: string | ChainTypes.IChain,
): boolean;
validateAddress(address: string, currency: CurrencyInput | StorageCurrency): boolean;
validateAddress(address: string, currency: CurrencyDefinition): boolean;
validateCurrency(currency: StorageCurrency): boolean;
}

/**
* A mapping from old to new name for a given currency.
*
* Format { "chainName": {"TOKEN": ["NEW_TOKEN","NEW_CHAIN"]}}
* Format { "chainName": {"TOKEN": ["NEW_TOKEN","NEW_CHAIN"]}}
*/
export type LegacyTokenMap = Record<string, Record<string, [string, string]>>;
Loading