diff --git a/.circleci/config.yml b/.circleci/config.yml index fd0d8d2cdb..99725cf279 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -81,6 +81,9 @@ jobs: - run: name: Build utils command: yarn workspace @requestnetwork/utils run build + - run: + name: Build chain + command: yarn workspace @requestnetwork/chain run build - run: name: Build currency command: yarn workspace @requestnetwork/currency run build @@ -284,6 +287,18 @@ jobs: command: 'yarn workspace @requestnetwork/utils run test --ci --maxWorkers=1' - store_test_results: path: packages/utils/reports/ + test-chain: + docker: + - *node_image + working_directory: *working_directory + steps: + - attach_workspace: + at: *working_directory + - run: + name: 'Test chain' + command: 'yarn workspace @requestnetwork/chain run test --ci --maxWorkers=1' + - store_test_results: + path: packages/chain/reports/ test-currency: docker: - *node_image @@ -555,6 +570,9 @@ workflows: - test-utils: requires: - build + - test-chain: + requires: + - build - test-currency: requires: - build @@ -588,6 +606,7 @@ workflows: requires: - lint - test-advanced-logic + - test-chain - test-currency - test-data-access - test-data-format diff --git a/packages/advanced-logic/src/advanced-logic.ts b/packages/advanced-logic/src/advanced-logic.ts index 4784ee0179..87d0e4673e 100644 --- a/packages/advanced-logic/src/advanced-logic.ts +++ b/packages/advanced-logic/src/advanced-logic.ts @@ -1,11 +1,11 @@ import { AdvancedLogicTypes, - CurrencyTypes, + ChainTypes, ExtensionTypes, IdentityTypes, RequestLogicTypes, } from '@requestnetwork/types'; -import { ICurrencyManager, NearChains, isSameChain } from '@requestnetwork/currency'; +import { ICurrencyManager } from '@requestnetwork/currency'; import ContentData from './extensions/content-data'; import AddressBasedBtc from './extensions/payment-network/bitcoin/mainnet-address-based'; @@ -27,11 +27,34 @@ import NativeToken from './extensions/payment-network/native-token'; import AnyToNative from './extensions/payment-network/any-to-native'; import Erc20TransferableReceivablePaymentNetwork from './extensions/payment-network/erc20/transferable-receivable'; +const { ECOSYSTEM, VM_ECOSYSTEMS } = ChainTypes; + /** * Module to manage Advanced logic extensions * Package to route the format and parsing of extensions following their id */ export default class AdvancedLogic implements AdvancedLogicTypes.IAdvancedLogic { + public static supportedEcosystemsForExtension: Record< + ExtensionTypes.ID, + readonly ChainTypes.ECOSYSTEM[] + > = { + [ExtensionTypes.ID.CONTENT_DATA]: [ECOSYSTEM.EVM], + [ExtensionTypes.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED]: [ECOSYSTEM.BTC], + [ExtensionTypes.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED]: [ECOSYSTEM.BTC], + [ExtensionTypes.PAYMENT_NETWORK_ID.ANY_DECLARATIVE]: [], + [ExtensionTypes.PAYMENT_NETWORK_ID.ERC20_ADDRESS_BASED]: VM_ECOSYSTEMS, + [ExtensionTypes.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT]: VM_ECOSYSTEMS, + [ExtensionTypes.PAYMENT_NETWORK_ID.ERC20_FEE_PROXY_CONTRACT]: VM_ECOSYSTEMS, + [ExtensionTypes.PAYMENT_NETWORK_ID.ERC777_STREAM]: [ECOSYSTEM.EVM], + [ExtensionTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA]: [ECOSYSTEM.EVM], + [ExtensionTypes.PAYMENT_NETWORK_ID.NATIVE_TOKEN]: [ECOSYSTEM.NEAR], + [ExtensionTypes.PAYMENT_NETWORK_ID.ANY_TO_ERC20_PROXY]: VM_ECOSYSTEMS, + [ExtensionTypes.PAYMENT_NETWORK_ID.ETH_FEE_PROXY_CONTRACT]: [ECOSYSTEM.EVM], + [ExtensionTypes.PAYMENT_NETWORK_ID.ANY_TO_ETH_PROXY]: [ECOSYSTEM.EVM], + [ExtensionTypes.PAYMENT_NETWORK_ID.ANY_TO_NATIVE_TOKEN]: [ECOSYSTEM.NEAR], + [ExtensionTypes.PAYMENT_NETWORK_ID.ERC20_TRANSFERABLE_RECEIVABLE]: [ECOSYSTEM.EVM], + }; + /** Give access to the functions specific of the extensions supported */ public extensions: { addressBasedBtc: AddressBasedBtc; @@ -108,8 +131,26 @@ export default class AdvancedLogic implements AdvancedLogicTypes.IAdvancedLogic requestState: RequestLogicTypes.IRequest, ): ExtensionTypes.IExtension { const id: ExtensionTypes.ID = extensionAction.id; - const network = this.getNetwork(extensionAction, requestState) || requestState.currency.network; - const extension: ExtensionTypes.IExtension | undefined = { + if (!(id in AdvancedLogic.supportedEcosystemsForExtension)) { + throw Error(`extension not recognized, id: ${id}`); + } + const ecosystems = AdvancedLogic.supportedEcosystemsForExtension[id]; + if (!ecosystems) { + throw Error(`chain ecosystem not recognized for extension: ${id}`); + } + const chain = this.getChainForActionAndState(extensionAction, requestState, ecosystems); + const extensions = this.getExtensionsForChain(chain); + const extension = extensions[id]; + if (!extension) { + throw Error(`extension with id: ${id} not found for network: ${chain}`); + } + return extension; + } + + protected getExtensionsForChain( + chain?: ChainTypes.IChain, + ): Record { + const extensions: Record = { [ExtensionTypes.ID.CONTENT_DATA]: this.extensions.contentData, [ExtensionTypes.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED]: this.extensions.addressBasedBtc, [ExtensionTypes.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED]: @@ -118,36 +159,37 @@ export default class AdvancedLogic implements AdvancedLogicTypes.IAdvancedLogic [ExtensionTypes.PAYMENT_NETWORK_ID.ERC20_ADDRESS_BASED]: this.extensions.addressBasedErc20, [ExtensionTypes.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT]: this.extensions.proxyContractErc20, [ExtensionTypes.PAYMENT_NETWORK_ID.ERC20_FEE_PROXY_CONTRACT]: - this.getFeeProxyContractErc20ForNetwork(network), + this.getFeeProxyContractErc20ForNetwork(chain), [ExtensionTypes.PAYMENT_NETWORK_ID.ERC777_STREAM]: this.extensions.erc777Stream, [ExtensionTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA]: this.extensions.ethereumInputData, [ExtensionTypes.PAYMENT_NETWORK_ID.NATIVE_TOKEN]: - this.getNativeTokenExtensionForNetwork(network), + this.getNativeTokenExtensionForNetwork(chain), [ExtensionTypes.PAYMENT_NETWORK_ID.ANY_TO_ERC20_PROXY]: this.extensions.anyToErc20Proxy, [ExtensionTypes.PAYMENT_NETWORK_ID.ETH_FEE_PROXY_CONTRACT]: this.extensions.feeProxyContractEth, [ExtensionTypes.PAYMENT_NETWORK_ID.ANY_TO_ETH_PROXY]: this.extensions.anyToEthProxy, [ExtensionTypes.PAYMENT_NETWORK_ID.ANY_TO_NATIVE_TOKEN]: - this.getAnyToNativeTokenExtensionForNetwork(network), + this.getAnyToNativeTokenExtensionForNetwork(chain), [ExtensionTypes.PAYMENT_NETWORK_ID.ERC20_TRANSFERABLE_RECEIVABLE]: this.extensions.erc20TransferableReceivable, - }[id]; - - if (!extension) { - if ( - id === ExtensionTypes.PAYMENT_NETWORK_ID.NATIVE_TOKEN || - id === ExtensionTypes.PAYMENT_NETWORK_ID.ANY_TO_NATIVE_TOKEN - ) { - throw Error(`extension with id: ${id} not found for network: ${network}`); - } - - throw Error(`extension not recognized, id: ${id}`); - } - return extension; + }; + if (!chain) return extensions; + // filter-out unsupported extensions for this chain ecosystem + return (Object.keys(extensions) as ExtensionTypes.ID[]).reduce( + (filteredExtensions, extensionId) => { + filteredExtensions[extensionId] = AdvancedLogic.supportedEcosystemsForExtension[ + extensionId + ].includes(chain.ecosystem) + ? extensions[extensionId] + : undefined; + return filteredExtensions; + }, + {} as Record, + ); } public getNativeTokenExtensionForNetwork( - network?: CurrencyTypes.ChainName, + network?: ChainTypes.IChain, ): ExtensionTypes.IExtension | undefined { return network ? this.extensions.nativeToken.find((nativeTokenExtension) => @@ -157,7 +199,7 @@ export default class AdvancedLogic implements AdvancedLogicTypes.IAdvancedLogic } public getAnyToNativeTokenExtensionForNetwork( - network?: CurrencyTypes.ChainName, + network?: ChainTypes.IChain, ): AnyToNative | undefined { return network ? this.extensions.anyToNativeToken.find((anyToNativeTokenExtension) => @@ -166,30 +208,46 @@ export default class AdvancedLogic implements AdvancedLogicTypes.IAdvancedLogic : undefined; } - public getFeeProxyContractErc20ForNetwork(network?: string): FeeProxyContractErc20 { - return NearChains.isChainSupported(network) + public getFeeProxyContractErc20ForNetwork(network?: ChainTypes.IChain): FeeProxyContractErc20 { + return this.currencyManager.chainManager.ecosystems[ECOSYSTEM.NEAR].isChainSupported(network) ? new FeeProxyContractErc20(this.currencyManager, undefined, undefined, network) : this.extensions.feeProxyContractErc20; } - protected getNetwork( + private getChainForActionAndState( extensionAction: ExtensionTypes.IAction, requestState: RequestLogicTypes.IRequest, - ): CurrencyTypes.ChainName | undefined { + ecosystems: readonly ChainTypes.ECOSYSTEM[], + ): ChainTypes.IChain | undefined { + // This check is only used for the "native-token" extension + // (notice the "extensionAction.parameters.paymentNetworkName" property). + // This is important because "isSameChain" throws an error + // when "extensionAction.parameters.paymentNetworkName" + // and the chain supporting "requestState.currency" + // are not part of the same ecosystem. + // This should not happen in this case. if ( requestState.currency.network && extensionAction.parameters.paymentNetworkName && - !isSameChain(requestState.currency.network, extensionAction.parameters.paymentNetworkName) + !this.currencyManager.chainManager.isSameChain( + requestState.currency.network, + extensionAction.parameters.paymentNetworkName, + ecosystems, + ) ) { throw new Error( - `Cannot apply action for network ${extensionAction.parameters.paymentNetworkName} on state with payment network: ${requestState.currency.network}`, + `Cannot apply action for extension ${extensionAction.parameters.paymentNetworkName} on state with chain: ${requestState.currency.network}`, ); } - const network = - extensionAction.action === 'create' + + const chainName = + (extensionAction.action === 'create' ? extensionAction.parameters.network : requestState.extensions[ExtensionTypes.PAYMENT_NETWORK_ID.ANY_TO_NATIVE_TOKEN]?.values - ?.network; - return network; + ?.network) || requestState.currency.network; + + if (!chainName) return; + + return this.currencyManager.chainManager.fromName(chainName, ecosystems); } } diff --git a/packages/advanced-logic/src/extensions/abstract-extension.ts b/packages/advanced-logic/src/extensions/abstract-extension.ts index 1b545a4543..32a49522fa 100644 --- a/packages/advanced-logic/src/extensions/abstract-extension.ts +++ b/packages/advanced-logic/src/extensions/abstract-extension.ts @@ -1,4 +1,9 @@ -import { ExtensionTypes, IdentityTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { + ChainTypes, + ExtensionTypes, + IdentityTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; import { deepCopy } from '@requestnetwork/utils'; /** @@ -6,6 +11,7 @@ import { deepCopy } from '@requestnetwork/utils'; */ export abstract class AbstractExtension implements ExtensionTypes.IExtension { protected actions: ExtensionTypes.SupportedActions; + static supportedEcosystems: ChainTypes.ECOSYSTEM[] = [ChainTypes.ECOSYSTEM.EVM]; protected constructor( public readonly extensionType: ExtensionTypes.TYPE, diff --git a/packages/advanced-logic/src/extensions/payment-network/address-based.ts b/packages/advanced-logic/src/extensions/payment-network/address-based.ts index c9a3f12985..46b0ba653e 100644 --- a/packages/advanced-logic/src/extensions/payment-network/address-based.ts +++ b/packages/advanced-logic/src/extensions/payment-network/address-based.ts @@ -1,6 +1,6 @@ import { ICurrencyManager, UnsupportedCurrencyError } from '@requestnetwork/currency'; import { - CurrencyTypes, + ChainTypes, ExtensionTypes, IdentityTypes, RequestLogicTypes, @@ -151,7 +151,11 @@ export default abstract class AddressBasedPaymentNetwork< case RequestLogicTypes.CURRENCY.ETH: case RequestLogicTypes.CURRENCY.ERC20: case RequestLogicTypes.CURRENCY.ERC777: - return this.isValidAddressForSymbolAndNetwork(address, 'ETH', 'mainnet'); + return this.isValidAddressForSymbolAndNetwork( + address, + 'ETH', + this.currencyManager.chainManager.fromName('mainnet', [ChainTypes.ECOSYSTEM.EVM]), + ); default: throw new Error( `Default implementation of isValidAddressForNetwork() does not support currency type ${this.supportedCurrencyType}. Please override this method if needed.`, @@ -162,11 +166,11 @@ export default abstract class AddressBasedPaymentNetwork< protected isValidAddressForSymbolAndNetwork( address: string, symbol: string, - network: CurrencyTypes.ChainName, + network: ChainTypes.IChain, ): boolean { const currency = this.currencyManager.from(symbol, network); if (!currency) { - throw new UnsupportedCurrencyError({ value: symbol, network }); + throw new UnsupportedCurrencyError({ value: symbol, network: network.name }); } return this.currencyManager.validateAddress(address, currency); } @@ -276,10 +280,21 @@ export default abstract class AddressBasedPaymentNetwork< this.throwIfInvalidNetwork(request.currency.network); } - protected throwIfInvalidNetwork(network?: string): asserts network is string { - if (!network) { + protected throwIfInvalidNetwork(chain?: string | ChainTypes.IChain): ChainTypes.IChain { + if (!chain) { throw Error('network is required'); } + const supportedEcosystems = this.currencyManager.chainManager.getEcosystemsByCurrencyType( + this.supportedCurrencyType, + ); + if (typeof chain === 'string') { + // throws if network not found + return this.currencyManager.chainManager.fromName(chain, supportedEcosystems); + } + if (!supportedEcosystems.includes(chain.ecosystem)) { + throw Error(`Payment network ${this.constructor.name} does not support chain ${chain.name}`); + } + return chain; } } @@ -291,12 +306,20 @@ export class InvalidPaymentAddressError extends Error { } export class UnsupportedNetworkError extends Error { - constructor(unsupportedNetworkName: string, supportedNetworks?: string[]) { - const supportedNetworkDetails = supportedNetworks - ? ` (only ${supportedNetworks.join(', ')})` + constructor( + extension: string, + unsupportedChain: string | ChainTypes.IChain, + supportedChains?: string[] | ChainTypes.IChain[], + ) { + const unsupportedChainName = + typeof unsupportedChain === 'string' ? unsupportedChain : unsupportedChain.name; + const supportedNetworkDetails = supportedChains + ? ` (only "${supportedChains + .map((chain) => (typeof chain === 'string' ? chain : chain.name)) + .join(', ')}")` : ''; super( - `Payment network '${unsupportedNetworkName}' is not supported by this extension${supportedNetworkDetails}`, + `The extension "${extension}" does not support the chain "${unsupportedChainName}"${supportedNetworkDetails}`, ); } } diff --git a/packages/advanced-logic/src/extensions/payment-network/any-to-erc20-proxy.ts b/packages/advanced-logic/src/extensions/payment-network/any-to-erc20-proxy.ts index c6ff3cb629..ca6d44d295 100644 --- a/packages/advanced-logic/src/extensions/payment-network/any-to-erc20-proxy.ts +++ b/packages/advanced-logic/src/extensions/payment-network/any-to-erc20-proxy.ts @@ -48,7 +48,7 @@ export default class AnyToErc20ProxyPaymentNetwork extends Erc20FeeProxyPaymentN network, }); } - if (!this.currencyManager.supportsConversion(acceptedCurrency, network)) { + if (!network || !this.currencyManager.supportsConversion(acceptedCurrency, network)) { throw Error( `acceptedTokens must contain only supported token addresses (ERC20 only). ${address} is not supported for ${network}.`, ); diff --git a/packages/advanced-logic/src/extensions/payment-network/any-to-native.ts b/packages/advanced-logic/src/extensions/payment-network/any-to-native.ts index a090ed8336..172130fe96 100644 --- a/packages/advanced-logic/src/extensions/payment-network/any-to-native.ts +++ b/packages/advanced-logic/src/extensions/payment-network/any-to-native.ts @@ -1,5 +1,5 @@ import { FeeReferenceBasedPaymentNetwork } from './fee-reference-based'; -import { CurrencyTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; import { InvalidPaymentAddressError, UnsupportedNetworkError } from './address-based'; import { ICurrencyManager } from '@requestnetwork/currency'; @@ -8,7 +8,7 @@ export default abstract class AnyToNativeTokenPaymentNetwork extends FeeReferenc currencyManager: ICurrencyManager, extensionId: ExtensionTypes.PAYMENT_NETWORK_ID, currentVersion: string, - public readonly supportedNetworks: CurrencyTypes.ChainName[], + public readonly supportedNetworks: ChainTypes.IChain[], ) { super(currencyManager, extensionId, currentVersion, RequestLogicTypes.CURRENCY.ETH); } @@ -46,13 +46,15 @@ export default abstract class AnyToNativeTokenPaymentNetwork extends FeeReferenc ); } - protected throwIfInvalidNetwork( - network?: CurrencyTypes.ChainName, - ): asserts network is CurrencyTypes.ChainName { - super.throwIfInvalidNetwork(network); - if (this.supportedNetworks && !this.supportedNetworks.includes(network)) { - throw new UnsupportedNetworkError(network, this.supportedNetworks); + protected throwIfInvalidNetwork(chain?: string | ChainTypes.IChain): ChainTypes.IChain { + const _chain = super.throwIfInvalidNetwork(chain); + if ( + this.supportedNetworks && + !this.supportedNetworks.some((supportedChain) => supportedChain.eq(_chain)) + ) { + throw new UnsupportedNetworkError(this.constructor.name, _chain.name, this.supportedNetworks); } + return _chain; } } diff --git a/packages/advanced-logic/src/extensions/payment-network/bitcoin/mainnet-address-based.ts b/packages/advanced-logic/src/extensions/payment-network/bitcoin/mainnet-address-based.ts index e8ca2ab647..008d7d937d 100644 --- a/packages/advanced-logic/src/extensions/payment-network/bitcoin/mainnet-address-based.ts +++ b/packages/advanced-logic/src/extensions/payment-network/bitcoin/mainnet-address-based.ts @@ -1,4 +1,4 @@ -import { ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; import AddressBasedPaymentNetwork from '../address-based'; import { ICurrencyManager } from '@requestnetwork/currency'; @@ -21,6 +21,10 @@ export default class BitcoinAddressBasedPaymentNetwork extends AddressBasedPayme } protected isValidAddress(address: string): boolean { - return this.isValidAddressForSymbolAndNetwork(address, 'BTC', BITCOIN_NETWORK); + return this.isValidAddressForSymbolAndNetwork( + address, + 'BTC', + this.currencyManager.chainManager.fromName(BITCOIN_NETWORK, [ChainTypes.ECOSYSTEM.BTC]), + ); } } diff --git a/packages/advanced-logic/src/extensions/payment-network/bitcoin/testnet-address-based.ts b/packages/advanced-logic/src/extensions/payment-network/bitcoin/testnet-address-based.ts index 962d62d51b..31d55c4e17 100644 --- a/packages/advanced-logic/src/extensions/payment-network/bitcoin/testnet-address-based.ts +++ b/packages/advanced-logic/src/extensions/payment-network/bitcoin/testnet-address-based.ts @@ -1,5 +1,5 @@ import BitcoinAddressBasedPaymentNetwork from './mainnet-address-based'; -import { ExtensionTypes } from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes } from '@requestnetwork/types'; import { ICurrencyManager } from '@requestnetwork/currency'; const BITCOIN_NETWORK = 'testnet'; @@ -17,6 +17,10 @@ export default class BitcoinTestnetAddressBasedPaymentNetwork extends BitcoinAdd } protected isValidAddress(address: string): boolean { - return this.isValidAddressForSymbolAndNetwork(address, 'BTC-testnet', BITCOIN_NETWORK); + return this.isValidAddressForSymbolAndNetwork( + address, + 'BTC-testnet', + this.currencyManager.chainManager.fromName(BITCOIN_NETWORK, [ChainTypes.ECOSYSTEM.BTC]), + ); } } diff --git a/packages/advanced-logic/src/extensions/payment-network/erc20/fee-proxy-contract.ts b/packages/advanced-logic/src/extensions/payment-network/erc20/fee-proxy-contract.ts index d156322edb..4c0facaccf 100644 --- a/packages/advanced-logic/src/extensions/payment-network/erc20/fee-proxy-contract.ts +++ b/packages/advanced-logic/src/extensions/payment-network/erc20/fee-proxy-contract.ts @@ -1,5 +1,5 @@ -import { CurrencyTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; -import { ICurrencyManager, NearChains, isSameChain } from '@requestnetwork/currency'; +import { ChainTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ICurrencyManager } from '@requestnetwork/currency'; import { UnsupportedNetworkError } from '../address-based'; import { FeeReferenceBasedPaymentNetwork } from '../fee-reference-based'; @@ -21,21 +21,30 @@ export default class Erc20FeeProxyPaymentNetwork< extensionId: ExtensionTypes.PAYMENT_NETWORK_ID = ExtensionTypes.PAYMENT_NETWORK_ID .ERC20_FEE_PROXY_CONTRACT, currentVersion?: string | undefined, - protected network?: string | undefined, + protected network?: ChainTypes.IChain | undefined, ) { super( currencyManager, extensionId, - currentVersion ?? Erc20FeeProxyPaymentNetwork.getDefaultCurrencyVersion(network), + currentVersion ?? + Erc20FeeProxyPaymentNetwork.getDefaultCurrencyVersion( + currencyManager.chainManager, + network, + ), RequestLogicTypes.CURRENCY.ERC20, ); } - protected static getDefaultCurrencyVersion(network?: string): string { - return NearChains.isChainSupported(network) ? NEAR_CURRENT_VERSION : EVM_CURRENT_VERSION; + protected static getDefaultCurrencyVersion( + chainManager: ChainTypes.IChainManager, + network?: ChainTypes.IChain | undefined, + ): string { + return chainManager.ecosystems[ChainTypes.ECOSYSTEM.NEAR].isChainSupported(network) + ? NEAR_CURRENT_VERSION + : EVM_CURRENT_VERSION; } - // Override `validate` to account for network-specific instanciation (non-EVM only) + // Override `validate` to account for network-specific instantiation (non-EVM only) protected validate( request: RequestLogicTypes.IRequest, extensionAction: ExtensionTypes.IAction, @@ -43,20 +52,38 @@ export default class Erc20FeeProxyPaymentNetwork< if ( this.network && request.currency.network && - !isSameChain(this.network, request.currency.network) + !this.currencyManager.chainManager.isSameChain( + this.network, + request.currency.network, + ChainTypes.VM_ECOSYSTEMS, + ) ) { - throw new UnsupportedNetworkError(request.currency.network, [this.network]); + throw new UnsupportedNetworkError(this.constructor.name, request.currency.network, [ + this.network.name, + ]); } super.validate(request, extensionAction); } - // Override `isValidAddress` to account for network-specific instanciation (non-EVM only) + // Override `isValidAddress` to account for network-specific instantiation (non-EVM only) protected isValidAddress(address: string): boolean { - if (NearChains.isChainSupported(this.network)) { - if (NearChains.isTestnet(this.network as CurrencyTypes.NearChainName)) { - return this.isValidAddressForSymbolAndNetwork(address, 'NEAR-testnet', 'near-testnet'); + if ( + this.currencyManager.chainManager.ecosystems[ChainTypes.ECOSYSTEM.NEAR].isChainSupported( + this.network, + ) + ) { + if (this.network?.testnet) { + return this.isValidAddressForSymbolAndNetwork( + address, + 'NEAR-testnet', + this.currencyManager.chainManager.fromName('near-testnet', [ChainTypes.ECOSYSTEM.NEAR]), + ); } else { - return this.isValidAddressForSymbolAndNetwork(address, 'NEAR', 'near'); + return this.isValidAddressForSymbolAndNetwork( + address, + 'NEAR', + this.currencyManager.chainManager.fromName('near', [ChainTypes.ECOSYSTEM.NEAR]), + ); } } else { return super.isValidAddress(address); diff --git a/packages/advanced-logic/src/extensions/payment-network/native-token.ts b/packages/advanced-logic/src/extensions/payment-network/native-token.ts index 03548c40eb..6197069d5c 100644 --- a/packages/advanced-logic/src/extensions/payment-network/native-token.ts +++ b/packages/advanced-logic/src/extensions/payment-network/native-token.ts @@ -1,4 +1,4 @@ -import { CurrencyTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; import { InvalidPaymentAddressError, UnsupportedNetworkError } from './address-based'; import ReferenceBasedPaymentNetwork from './reference-based'; @@ -12,7 +12,7 @@ export default abstract class NativeTokenPaymentNetwork extends ReferenceBasedPa currencyManager: ICurrencyManager, extensionId: ExtensionTypes.PAYMENT_NETWORK_ID, currentVersion: string, - public readonly supportedNetworks: CurrencyTypes.ChainName[], + public readonly supportedNetworks: ChainTypes.IChain[], ) { super(currencyManager, extensionId, currentVersion, RequestLogicTypes.CURRENCY.ETH); } @@ -52,12 +52,14 @@ export default abstract class NativeTokenPaymentNetwork extends ReferenceBasedPa ); } - protected throwIfInvalidNetwork( - network?: CurrencyTypes.ChainName, - ): asserts network is CurrencyTypes.ChainName { - super.throwIfInvalidNetwork(network); - if (this.supportedNetworks && !this.supportedNetworks.includes(network)) { - throw new UnsupportedNetworkError(network, this.supportedNetworks); + protected throwIfInvalidNetwork(chain?: string | ChainTypes.IChain): ChainTypes.IChain { + const _chain = super.throwIfInvalidNetwork(chain); + if ( + this.supportedNetworks && + !this.supportedNetworks.some((supportedChain) => supportedChain.eq(_chain)) + ) { + throw new UnsupportedNetworkError(this.constructor.name, _chain, this.supportedNetworks); } + return _chain; } } diff --git a/packages/advanced-logic/src/extensions/payment-network/near/any-to-near-testnet.ts b/packages/advanced-logic/src/extensions/payment-network/near/any-to-near-testnet.ts index 0c848ea398..1eda99fc1d 100644 --- a/packages/advanced-logic/src/extensions/payment-network/near/any-to-near-testnet.ts +++ b/packages/advanced-logic/src/extensions/payment-network/near/any-to-near-testnet.ts @@ -1,10 +1,13 @@ import { ICurrencyManager } from '@requestnetwork/currency'; import AnyToNearPaymentNetwork from './any-to-near'; +import { ChainTypes } from '@requestnetwork/types'; export default class AnyToNearTestnetPaymentNetwork extends AnyToNearPaymentNetwork { public constructor(currencyManager: ICurrencyManager) { // testnet PN version is the same as mainnet, can be overridden here if needed - super(currencyManager, ['aurora-testnet', 'near-testnet']); + super(currencyManager, [ + currencyManager.chainManager.fromName('aurora-testnet', [ChainTypes.ECOSYSTEM.NEAR]), + ]); } /** @@ -14,6 +17,10 @@ export default class AnyToNearTestnetPaymentNetwork extends AnyToNearPaymentNetw * @returns {boolean} true if address is valid */ protected isValidAddress(address: string): boolean { - return this.isValidAddressForSymbolAndNetwork(address, 'NEAR-testnet', 'aurora-testnet'); + return this.isValidAddressForSymbolAndNetwork( + address, + 'NEAR-testnet', + this.currencyManager.chainManager.fromName('aurora-testnet', [ChainTypes.ECOSYSTEM.NEAR]), + ); } } diff --git a/packages/advanced-logic/src/extensions/payment-network/near/any-to-near.ts b/packages/advanced-logic/src/extensions/payment-network/near/any-to-near.ts index 187550fee6..f558da8594 100644 --- a/packages/advanced-logic/src/extensions/payment-network/near/any-to-near.ts +++ b/packages/advanced-logic/src/extensions/payment-network/near/any-to-near.ts @@ -1,6 +1,6 @@ import { ICurrencyManager, UnsupportedCurrencyError } from '@requestnetwork/currency'; import { - CurrencyTypes, + ChainTypes, ExtensionTypes, IdentityTypes, RequestLogicTypes, @@ -12,18 +12,16 @@ const CURRENT_VERSION = '0.1.0'; export default class AnyToNearPaymentNetwork extends AnyToNativeTokenPaymentNetwork { public constructor( currencyManager: ICurrencyManager, - supportedNetworks: CurrencyTypes.NearChainName[] = [ - 'aurora', - // FIXME: enable near network support - // 'near' - ], + supportedNetworks?: ChainTypes.INearChain[], currentVersion: string = CURRENT_VERSION, ) { super( currencyManager, ExtensionTypes.PAYMENT_NETWORK_ID.ANY_TO_NATIVE_TOKEN, currentVersion, - supportedNetworks, + supportedNetworks ?? [ + currencyManager.chainManager.fromName('aurora', [ChainTypes.ECOSYSTEM.NEAR]), + ], ); } @@ -34,7 +32,11 @@ export default class AnyToNearPaymentNetwork extends AnyToNativeTokenPaymentNetw * @returns {boolean} true if address is valid */ protected isValidAddress(address: string): boolean { - return this.isValidAddressForSymbolAndNetwork(address, 'NEAR', 'aurora'); + return this.isValidAddressForSymbolAndNetwork( + address, + 'NEAR', + this.currencyManager.chainManager.fromName('aurora', [ChainTypes.ECOSYSTEM.NEAR]), + ); } /** diff --git a/packages/advanced-logic/src/extensions/payment-network/near/near-native.ts b/packages/advanced-logic/src/extensions/payment-network/near/near-native.ts index 245ebf40e7..226d6a9fec 100644 --- a/packages/advanced-logic/src/extensions/payment-network/near/near-native.ts +++ b/packages/advanced-logic/src/extensions/payment-network/near/near-native.ts @@ -1,4 +1,4 @@ -import { CurrencyTypes, ExtensionTypes } from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes } from '@requestnetwork/types'; import NativeTokenPaymentNetwork from '../native-token'; import { ICurrencyManager } from '@requestnetwork/currency'; @@ -10,18 +10,16 @@ const CURRENT_VERSION = '0.2.0'; export default class NearNativePaymentNetwork extends NativeTokenPaymentNetwork { public constructor( currencyManager: ICurrencyManager, - supportedNetworks: CurrencyTypes.NearChainName[] = [ - 'aurora', - // FIXME: enable near network support - // 'near' - ], + supportedNetworks?: ChainTypes.INearChain[], currentVersion: string = CURRENT_VERSION, ) { super( currencyManager, ExtensionTypes.PAYMENT_NETWORK_ID.NATIVE_TOKEN, currentVersion, - supportedNetworks, + supportedNetworks ?? [ + currencyManager.chainManager.fromName('aurora', [ChainTypes.ECOSYSTEM.NEAR]), + ], ); } @@ -32,6 +30,10 @@ export default class NearNativePaymentNetwork extends NativeTokenPaymentNetwork * @returns {boolean} true if address is valid */ protected isValidAddress(address: string): boolean { - return this.isValidAddressForSymbolAndNetwork(address, 'NEAR', 'aurora'); + return this.isValidAddressForSymbolAndNetwork( + address, + 'NEAR', + this.currencyManager.chainManager.fromName('aurora', [ChainTypes.ECOSYSTEM.NEAR]), + ); } } diff --git a/packages/advanced-logic/src/extensions/payment-network/near/near-testnet-native.ts b/packages/advanced-logic/src/extensions/payment-network/near/near-testnet-native.ts index af114b2753..ab7237e0a3 100644 --- a/packages/advanced-logic/src/extensions/payment-network/near/near-testnet-native.ts +++ b/packages/advanced-logic/src/extensions/payment-network/near/near-testnet-native.ts @@ -1,5 +1,6 @@ import NearNativePaymentNetwork from './near-native'; import { ICurrencyManager } from '@requestnetwork/currency'; +import { ChainTypes } from '@requestnetwork/types'; /** * Implementation of the payment network to pay in Near on testnet based on input data. @@ -7,7 +8,9 @@ import { ICurrencyManager } from '@requestnetwork/currency'; export default class NearTestnetNativeNativePaymentNetwork extends NearNativePaymentNetwork { public constructor(currencyManager: ICurrencyManager) { // testnet PN version is the same as mainnet, can be overridden here if needed - super(currencyManager, ['aurora-testnet', 'near-testnet']); + super(currencyManager, [ + currencyManager.chainManager.fromName('aurora-testnet', [ChainTypes.ECOSYSTEM.NEAR]), + ]); } /** @@ -17,6 +20,10 @@ export default class NearTestnetNativeNativePaymentNetwork extends NearNativePay * @returns {boolean} true if address is valid */ protected isValidAddress(address: string): boolean { - return this.isValidAddressForSymbolAndNetwork(address, 'NEAR-testnet', 'aurora-testnet'); + return this.isValidAddressForSymbolAndNetwork( + address, + 'NEAR-testnet', + this.currencyManager.chainManager.fromName('aurora-testnet', [ChainTypes.ECOSYSTEM.NEAR]), + ); } } diff --git a/packages/advanced-logic/test/extensions/payment-network/address-based.test.ts b/packages/advanced-logic/test/extensions/payment-network/address-based.test.ts index 90eb197ae9..390cc9efcd 100644 --- a/packages/advanced-logic/test/extensions/payment-network/address-based.test.ts +++ b/packages/advanced-logic/test/extensions/payment-network/address-based.test.ts @@ -1,6 +1,7 @@ import { CurrencyManager, UnsupportedCurrencyError } from '@requestnetwork/currency'; -import { ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; import AddressBasedPaymentNetwork from '../../../src/extensions/payment-network/address-based'; +import { ChainManager } from '@requestnetwork/chain'; describe('extensions/payment-network/address-based', () => { it('address validation should throw when using unsupported currency type', () => { @@ -37,7 +38,11 @@ describe('extensions/payment-network/address-based', () => { super(CurrencyManager.getDefault(), extensionId, currentVersion, supportedCurrencyType); } public testIsValidAddress() { - this.isValidAddressForSymbolAndNetwork('test', 'test', 'mainnet'); + this.isValidAddressForSymbolAndNetwork( + 'test', + 'test', + ChainManager.current().fromName('mainnet', [ChainTypes.ECOSYSTEM.EVM]), + ); } } expect(() => { diff --git a/packages/advanced-logic/test/extensions/payment-network/any-to-eth-proxy.test.ts b/packages/advanced-logic/test/extensions/payment-network/any-to-eth-proxy.test.ts index 8525fbcdb4..9c33c9d562 100644 --- a/packages/advanced-logic/test/extensions/payment-network/any-to-eth-proxy.test.ts +++ b/packages/advanced-logic/test/extensions/payment-network/any-to-eth-proxy.test.ts @@ -1,4 +1,4 @@ -import { CurrencyTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; import { deepCopy } from '@requestnetwork/utils'; import { CurrencyManager, UnsupportedCurrencyError } from '@requestnetwork/currency'; @@ -146,7 +146,7 @@ describe('extensions/payment-network/ethereum/any-to-eth-fee-proxy-contract', () requestCreatedNoExtension.currency = { type: RequestLogicTypes.CURRENCY.ETH, value: 'invalid value', - network: 'invalid network' as CurrencyTypes.EvmChainName, + network: 'invalid network', }; const action: ExtensionTypes.IAction = deepCopy( diff --git a/packages/advanced-logic/test/extensions/payment-network/any-to-near.test.ts b/packages/advanced-logic/test/extensions/payment-network/any-to-near.test.ts index ce3f3daa2f..336dcf70de 100644 --- a/packages/advanced-logic/test/extensions/payment-network/any-to-near.test.ts +++ b/packages/advanced-logic/test/extensions/payment-network/any-to-near.test.ts @@ -8,7 +8,7 @@ import { } from '../../utils/payment-network/any/generator-data-create'; import { AdvancedLogic } from '../../../src'; import { arbitraryTimestamp, payeeRaw, payerRaw } from '../../utils/test-data-generator'; -import { CurrencyTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; import AnyToNearPaymentNetwork from '../../../src/extensions/payment-network/near/any-to-near'; import AnyToNativeTokenPaymentNetwork from '../../../src/extensions/payment-network/any-to-native'; import { CurrencyManager } from '@requestnetwork/currency'; @@ -206,10 +206,10 @@ describe('extensions/payment-network/any-to-native-token', () => { expect(() => { new AnyToNearPaymentNetwork(currencyManager).createCreationAction({ ...partialCreationParams, - network: 'another-chain' as CurrencyTypes.NearChainName, + network: 'another-chain', }); }).toThrowError( - `Payment network 'another-chain' is not supported by this extension (only aurora)`, + `No chain found with "name=another-chain" for ecosystem(s) "DECLARATIVE,EVM,NEAR"`, ); }); it('throws when payment network is missing', () => { diff --git a/packages/advanced-logic/test/extensions/payment-network/erc20/fee-proxy-contract.test.ts b/packages/advanced-logic/test/extensions/payment-network/erc20/fee-proxy-contract.test.ts index d3e831c9a7..0876455bb4 100644 --- a/packages/advanced-logic/test/extensions/payment-network/erc20/fee-proxy-contract.test.ts +++ b/packages/advanced-logic/test/extensions/payment-network/erc20/fee-proxy-contract.test.ts @@ -1,4 +1,4 @@ -import { ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; import * as DataERC20FeeAddData from '../../../utils/payment-network/erc20/fee-proxy-contract-add-data-generator'; import * as DataERC20FeeCreate from '../../../utils/payment-network/erc20/fee-proxy-contract-create-data-generator'; @@ -7,6 +7,7 @@ import * as TestData from '../../../utils/test-data-generator'; import { deepCopy } from '@requestnetwork/utils'; import { CurrencyManager } from '@requestnetwork/currency'; import { AdvancedLogic } from '../../../../src'; +import { ChainManager } from '@requestnetwork/chain'; const currencyManager = new CurrencyManager(CurrencyManager.getDefaultList()); @@ -118,7 +119,9 @@ describe('extensions/payment-network/erc20/fee-proxy-contract', () => { }); describe('on Near testnet', () => { - const extension = advancedLogic.getFeeProxyContractErc20ForNetwork('near-testnet'); + const extension = advancedLogic.getFeeProxyContractErc20ForNetwork( + ChainManager.current().fromName('near-testnet', [ChainTypes.ECOSYSTEM.NEAR]), + ); it('can create a create action with all parameters', () => { expect( extension.createCreationAction({ @@ -382,7 +385,9 @@ describe('extensions/payment-network/erc20/fee-proxy-contract', () => { }); describe('on Near testnet', () => { - const extension = advancedLogic.getFeeProxyContractErc20ForNetwork('near-testnet'); + const extension = advancedLogic.getFeeProxyContractErc20ForNetwork( + ChainManager.current().fromName('near-testnet', [ChainTypes.ECOSYSTEM.NEAR]), + ); it('can applyActionToExtensions of creation', () => { expect( extension.applyActionToExtension( @@ -406,7 +411,7 @@ describe('extensions/payment-network/erc20/fee-proxy-contract', () => { TestData.arbitraryTimestamp, ), ).toThrowError( - "Payment network 'mainnet' is not supported by this extension (only near-testnet)", + `The extension "Erc20FeeProxyPaymentNetwork" does not support the chain "mainnet" (only "near-testnet")`, ); }); }); diff --git a/packages/advanced-logic/test/extensions/payment-network/native-token.test.ts b/packages/advanced-logic/test/extensions/payment-network/native-token.test.ts index e6a2be6c7e..6ab653ec13 100644 --- a/packages/advanced-logic/test/extensions/payment-network/native-token.test.ts +++ b/packages/advanced-logic/test/extensions/payment-network/native-token.test.ts @@ -11,7 +11,7 @@ import { } from '../../utils/payment-network/mocked_native_data'; import { AdvancedLogic } from '../../../src'; import { arbitraryTimestamp, payeeRaw } from '../../utils/test-data-generator'; -import { CurrencyTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; import { CurrencyManager } from '@requestnetwork/currency'; import NearTestnetNativeNativePaymentNetwork from '../../../src/extensions/payment-network/near/near-testnet-native'; @@ -154,10 +154,10 @@ describe('extensions/payment-network/native-token', () => { expect(() => { new NearNativePaymentNetwork(currencyManager).createCreationAction({ ...partialCreationParams, - paymentNetworkName: 'another-chain' as CurrencyTypes.NearChainName, + paymentNetworkName: 'another-chain', }); }).toThrowError( - `Payment network 'another-chain' is not supported by this extension (only aurora)`, + `No chain found with "name=another-chain" for ecosystem(s) "DECLARATIVE,EVM,NEAR"`, ); }); it('createCreationAction() throws without payment network', () => { @@ -274,7 +274,7 @@ describe('extensions/payment-network/native-token', () => { arbitraryTimestamp, ); }).toThrowError( - 'Cannot apply action for network aurora-testnet on state with payment network: aurora', + 'Cannot apply action for extension aurora-testnet on state with chain: aurora', ); }); it('throws when adding a payment address a different network', () => { @@ -331,7 +331,7 @@ describe('extensions/payment-network/native-token', () => { }); it('throws on a wrong payment network', () => { const advancedLogic = new AdvancedLogic(currencyManager); - const wrongNetwork = `wrong network` as CurrencyTypes.EvmChainName; + const wrongNetwork = `wrong network`; const wrongNativeTokenRequestState: typeof requestStateNoExtensions = { ...requestStateNoExtensions, @@ -357,7 +357,7 @@ describe('extensions/payment-network/native-token', () => { payeeRaw.identity, arbitraryTimestamp, ), - ).toThrowError('extension with id: pn-native-token not found for network: wrong network'); + ).toThrowError('No chain found with "name=wrong network" for ecosystem(s) "NEAR"'); }); it('throws on a different payment network', () => { const advancedLogic = new AdvancedLogic(currencyManager); @@ -384,7 +384,7 @@ describe('extensions/payment-network/native-token', () => { arbitraryTimestamp, ), ).toThrowError( - `Cannot apply action for network ${mainnetTestCase.wrongCurrency.network} on state with payment network: ${mainnetTestCase.currency.network}`, + `Cannot apply action for extension ${mainnetTestCase.wrongCurrency.network} on state with chain: ${mainnetTestCase.currency.network}`, ); }); diff --git a/packages/chain/.nycrc b/packages/chain/.nycrc new file mode 100644 index 0000000000..7d71de2515 --- /dev/null +++ b/packages/chain/.nycrc @@ -0,0 +1,8 @@ +{ + "extension": [".ts"], + "include": ["src/*.ts", "src/**/*.ts"], + "require": ["ts-node/register"], + "reporter": ["text-summary", "json", "html"], + "sourceMap": true, + "all": true +} diff --git a/packages/chain/.vscode/settings.json b/packages/chain/.vscode/settings.json new file mode 100644 index 0000000000..379bec3a42 --- /dev/null +++ b/packages/chain/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "mochaExplorer.files": "**/test/**/*.ts", + "mochaExplorer.require": "ts-node/register", + "mochaExplorer.cwd": "../.." +} diff --git a/packages/chain/README.md b/packages/chain/README.md new file mode 100644 index 0000000000..dbcf3e4409 --- /dev/null +++ b/packages/chain/README.md @@ -0,0 +1,40 @@ +# @requestnetwork/chain + +`@requestnetwork/chain` is a typescript library part of the [Request Network protocol](https://github.com/RequestNetwork/requestNetwork). +It is a collection of tools for the currencies and chains shared between the @requestnetwork packages. + +## Installation + +```bash +npm install @requestnetwork/chain +``` + +## Contributing + +Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. +[Read the contributing guide](/CONTRIBUTING.md) + +### Adding a new chain + +Supported chains are listed in `src/chains`: + +- `src/chains/btc/data` for BTC type chains +- `src/chains/evm/data` for EVM type chains +- `src/chains/near/data` for NEAR type chains + +The chain names are subjective, but they are unique and uniform across all Request Network packages. +They are formatted with the kebab-case naming convention. + +In order to add a new chain, first create a file `[nameOfTheChain].ts` in the correct directory. +Its internal structure should conform with the corresponding type, respectively: + +- `BtcChain` +- `EvmChain` +- `NearChain` + +These types are described in the `index.ts` file of each chain subdirectory. +Please add the `testnet: true` property for staging chains. + +## License + +[MIT](/LICENSE) diff --git a/packages/chain/jest.config.js b/packages/chain/jest.config.js new file mode 100644 index 0000000000..be20f1cb6c --- /dev/null +++ b/packages/chain/jest.config.js @@ -0,0 +1,6 @@ +const jestCommonConfig = require('../../jest.config'); + +/** @type {import('jest').Config} */ +module.exports = { + ...jestCommonConfig, +}; diff --git a/packages/chain/package.json b/packages/chain/package.json new file mode 100644 index 0000000000..71a16f8434 --- /dev/null +++ b/packages/chain/package.json @@ -0,0 +1,55 @@ +{ + "name": "@requestnetwork/chain", + "version": "0.13.0", + "publishConfig": { + "access": "public" + }, + "description": "Chain tools for Request Network packages.", + "keywords": [ + "requestnetwork", + "chain", + "utils" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/RequestNetwork/requestNetwork.git" + }, + "homepage": "https://github.com/RequestNetwork/requestNetwork/tree/master/packages/chain#readme", + "bugs": { + "url": "https://github.com/RequestNetwork/requestNetwork/issues" + }, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "directories": { + "lib": "src", + "test": "test" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsc -b tsconfig.build.json", + "clean": "rm -rf dist tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo", + "lint": "eslint . --fix", + "lint:check": "eslint .", + "prepare": "yarn run build", + "test": "jest", + "test:watch": "yarn test --watch" + }, + "dependencies": { + "@requestnetwork/types": "0.40.0" + }, + "devDependencies": { + "@types/jest": "29.5.6", + "jest": "29.5.0", + "jest-junit": "16.0.0", + "source-map-support": "0.5.19", + "ts-jest": "29.1.0", + "ts-node": "10.9.1", + "typescript": "5.1.3" + } +} diff --git a/packages/chain/src/chain-manager.ts b/packages/chain/src/chain-manager.ts new file mode 100644 index 0000000000..097e31b1e7 --- /dev/null +++ b/packages/chain/src/chain-manager.ts @@ -0,0 +1,126 @@ +import { ChainDefinition } from './types'; +import BtcEcosystem from './chains/btc/btc-ecosystem'; +import DeclarativeEcosystem from './chains/declarative/declarative-ecosystem'; +import EvmEcosystem from './chains/evm/evm-ecosystem'; +import NearEcosystem from './chains/near/near-ecosystem'; +import { initializeChains } from './chains/utils'; +import { ChainTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ChainAbstract } from './chains/chain-abstract'; + +export class ChainManager implements ChainTypes.IChainManager { + private static instance: ChainTypes.IChainManager; + + public readonly ecosystems: { + [E in ChainTypes.ECOSYSTEM]: ChainTypes.IEcosystem; + } = { + [ChainTypes.ECOSYSTEM.BTC]: BtcEcosystem, + [ChainTypes.ECOSYSTEM.DECLARATIVE]: DeclarativeEcosystem, + [ChainTypes.ECOSYSTEM.EVM]: EvmEcosystem, + [ChainTypes.ECOSYSTEM.NEAR]: NearEcosystem, + }; + + constructor(inputChains?: Record>) { + if (!inputChains) return; + for (const ecosystemName in this.ecosystems) { + const ecosystem = this.ecosystems[ecosystemName as ChainTypes.ECOSYSTEM]; + if (!inputChains[ecosystem.name]) continue; + const chainDefinitions = inputChains[ecosystem.name]; + const chains = initializeChains(ecosystem.chainClass, chainDefinitions); + Object.assign(ecosystem.chains, chains); + } + } + + /** + * Returns the list of supported chains + */ + get chains(): ChainTypes.IChain[] { + return Object.keys(this.ecosystems).reduce( + (chains, ecosystemName) => + chains.concat(Object.values(this.ecosystems[ecosystemName as ChainTypes.ECOSYSTEM].chains)), + [] as ChainTypes.IChain[], + ); + } + + getEcosystemsByCurrencyType(currencyType: RequestLogicTypes.CURRENCY): ChainTypes.ECOSYSTEM[] { + return (Object.keys(this.ecosystems) as ChainTypes.ECOSYSTEM[]).filter((ecosystemName) => { + return this.ecosystems[ecosystemName].currencyTypes.includes(currencyType); + }); + } + + static getName(chain: string | ChainTypes.IChain): string { + if (typeof chain === 'string') return chain.toLowerCase(); + return chain.name; + } + + /** + * Gets a supported chain from its name or ID + */ + protected fromGeneric( + propertyName: 'id' | 'name', + propertyValue: string, + ecosystemsFilter?: T, + ): ChainTypes.ChainTypeByEcosystem[T[number]] { + const chains = this.chains.filter( + (chain) => + chain[propertyName] === propertyValue && + (ecosystemsFilter ? ecosystemsFilter.includes(chain.ecosystem) : true), + ); + if (chains.length < 1) { + throw new Error( + `No chain found with "${propertyName}=${propertyValue}" for ecosystem(s) "${ecosystemsFilter}"`, + ); + } + if (chains.length > 1) { + throw new Error( + `There is more than one chain with "${propertyName}=${propertyValue}" for ecosystem(s) "${ecosystemsFilter}"`, + ); + } + return chains[0] as ChainTypes.ChainTypeByEcosystem[T[number]]; + } + + /** + * Gets a supported chain from its name + */ + fromName( + chainName: string, + ecosystemsFilter?: T, + ): ChainTypes.ChainTypeByEcosystem[T[number]] { + return this.fromGeneric('name', chainName, ecosystemsFilter); + } + + /** + * Gets a supported chain from its ID + */ + fromId( + chainId: string, + ecosystemsFilter?: T, + ): ChainTypes.ChainTypeByEcosystem[T[number]] { + return this.fromGeneric('id', chainId, ecosystemsFilter); + } + + static setCurrent(chainManager: ChainTypes.IChainManager): void { + this.instance = chainManager; + } + + static current(): ChainTypes.IChainManager { + if (this.instance) return this.instance; + this.instance = new ChainManager(); + return this.instance; + } + + /** + * Returns true if both chains are equal or aliases. + * The third argument "chainsEcosystem" is only needed when comparing chains as strings. + */ + isSameChain = ( + chain1: string | ChainTypes.IChain, + chain2: string | ChainTypes.IChain, + chainsEcosystem?: readonly ChainTypes.ECOSYSTEM[], + ): boolean => { + const chain1Object = + typeof chain1 === 'string' ? this.fromName(chain1, chainsEcosystem) : chain1; + const chain2Object = + typeof chain2 === 'string' ? this.fromName(chain2, chainsEcosystem) : chain2; + return chain1Object.eq(chain2Object); + }; +} diff --git a/packages/chain/src/chains/btc/btc-chain.ts b/packages/chain/src/chains/btc/btc-chain.ts new file mode 100644 index 0000000000..cb19f87ac8 --- /dev/null +++ b/packages/chain/src/chains/btc/btc-chain.ts @@ -0,0 +1,6 @@ +import { ChainAbstract } from '../chain-abstract'; +import { ChainTypes } from '@requestnetwork/types'; + +export class BtcChain extends ChainAbstract implements ChainTypes.IBtcChain { + public readonly ecosystem = ChainTypes.ECOSYSTEM.BTC; +} diff --git a/packages/chain/src/chains/btc/btc-ecosystem.ts b/packages/chain/src/chains/btc/btc-ecosystem.ts new file mode 100644 index 0000000000..3980189e7c --- /dev/null +++ b/packages/chain/src/chains/btc/btc-ecosystem.ts @@ -0,0 +1,9 @@ +import { EcosystemAbstract } from '../ecosystem-abstract'; +import { ChainTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { chains } from './index'; +import { BtcChain } from './btc-chain'; + +class BtcEcosystem extends EcosystemAbstract {} +export default new BtcEcosystem(ChainTypes.ECOSYSTEM.BTC, BtcChain, chains, [ + RequestLogicTypes.CURRENCY.BTC, +]); diff --git a/packages/currency/src/chains/btc/data/mainnet.ts b/packages/chain/src/chains/btc/data/mainnet.ts similarity index 100% rename from packages/currency/src/chains/btc/data/mainnet.ts rename to packages/chain/src/chains/btc/data/mainnet.ts diff --git a/packages/currency/src/chains/btc/data/testnet.ts b/packages/chain/src/chains/btc/data/testnet.ts similarity index 100% rename from packages/currency/src/chains/btc/data/testnet.ts rename to packages/chain/src/chains/btc/data/testnet.ts diff --git a/packages/chain/src/chains/btc/index.ts b/packages/chain/src/chains/btc/index.ts new file mode 100644 index 0000000000..75859a1bf4 --- /dev/null +++ b/packages/chain/src/chains/btc/index.ts @@ -0,0 +1,13 @@ +import { ChainDefinition } from '../../types'; + +import * as MainnetDefinition from './data/mainnet'; +import * as TestnetDefinition from './data/testnet'; +import { BtcChain } from './btc-chain'; +import { initializeChains } from '../utils'; + +const chainDefinitions: Record = { + mainnet: MainnetDefinition, + testnet: TestnetDefinition, +}; + +export const chains = initializeChains(BtcChain, chainDefinitions); diff --git a/packages/chain/src/chains/chain-abstract.ts b/packages/chain/src/chains/chain-abstract.ts new file mode 100644 index 0000000000..7f3f9913e9 --- /dev/null +++ b/packages/chain/src/chains/chain-abstract.ts @@ -0,0 +1,15 @@ +import { ChainTypes } from '@requestnetwork/types'; + +export abstract class ChainAbstract implements ChainTypes.IChainCommon { + public declare readonly ecosystem: ChainTypes.ECOSYSTEM; + constructor( + public readonly id: string, + public readonly name: string, + public readonly testnet: boolean = false, + ) { + this.name = this.name.toLowerCase(); + } + public eq(chain: ChainTypes.IChain): boolean { + return this === chain || (this.ecosystem === chain.ecosystem && this.id === chain.id); + } +} diff --git a/packages/currency/src/chains/declarative/data/solana.ts b/packages/chain/src/chains/declarative/data/solana.ts similarity index 100% rename from packages/currency/src/chains/declarative/data/solana.ts rename to packages/chain/src/chains/declarative/data/solana.ts diff --git a/packages/currency/src/chains/declarative/data/tron.ts b/packages/chain/src/chains/declarative/data/tron.ts similarity index 100% rename from packages/currency/src/chains/declarative/data/tron.ts rename to packages/chain/src/chains/declarative/data/tron.ts diff --git a/packages/chain/src/chains/declarative/declarative-chain.ts b/packages/chain/src/chains/declarative/declarative-chain.ts new file mode 100644 index 0000000000..3c6024c782 --- /dev/null +++ b/packages/chain/src/chains/declarative/declarative-chain.ts @@ -0,0 +1,6 @@ +import { ChainTypes } from '@requestnetwork/types'; +import { ChainAbstract } from '../chain-abstract'; + +export class DeclarativeChain extends ChainAbstract implements ChainTypes.IDeclarativeChain { + public readonly ecosystem = ChainTypes.ECOSYSTEM.DECLARATIVE; +} diff --git a/packages/chain/src/chains/declarative/declarative-ecosystem.ts b/packages/chain/src/chains/declarative/declarative-ecosystem.ts new file mode 100644 index 0000000000..0a945f487f --- /dev/null +++ b/packages/chain/src/chains/declarative/declarative-ecosystem.ts @@ -0,0 +1,18 @@ +import { EcosystemAbstract } from '../ecosystem-abstract'; +import { ChainTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { DeclarativeChain } from './declarative-chain'; +import { chains } from './index'; + +class DeclarativeEcosystem extends EcosystemAbstract { + constructor(chains: Record) { + super(ChainTypes.ECOSYSTEM.DECLARATIVE, DeclarativeChain, chains, [ + RequestLogicTypes.CURRENCY.ISO4217, + RequestLogicTypes.CURRENCY.BTC, + RequestLogicTypes.CURRENCY.ETH, + RequestLogicTypes.CURRENCY.ERC20, + RequestLogicTypes.CURRENCY.ERC777, + ]); + } +} + +export default new DeclarativeEcosystem(chains); diff --git a/packages/chain/src/chains/declarative/index.ts b/packages/chain/src/chains/declarative/index.ts new file mode 100644 index 0000000000..d0f88a8e39 --- /dev/null +++ b/packages/chain/src/chains/declarative/index.ts @@ -0,0 +1,13 @@ +import { ChainDefinition } from '../../types'; + +import * as TronDefinition from './data/tron'; +import * as SolanaDefinition from './data/solana'; +import { DeclarativeChain } from './declarative-chain'; +import { initializeChains } from '../utils'; + +const chainDefinitions: Record = { + tron: TronDefinition, + solana: SolanaDefinition, +}; + +export const chains = initializeChains(DeclarativeChain, chainDefinitions); diff --git a/packages/chain/src/chains/ecosystem-abstract.ts b/packages/chain/src/chains/ecosystem-abstract.ts new file mode 100644 index 0000000000..67d8d46f05 --- /dev/null +++ b/packages/chain/src/chains/ecosystem-abstract.ts @@ -0,0 +1,64 @@ +import { ChainTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ChainAbstract } from './chain-abstract'; + +export abstract class EcosystemAbstract + implements ChainTypes.IEcosystem +{ + constructor( + public name: ECOSYSTEM, + public chainClass: new ( + id: string, + name: string, + testnet?: boolean, + ) => ChainTypes.ChainTypeByEcosystem[ECOSYSTEM], + public chains: Record, + public currencyTypes: RequestLogicTypes.CURRENCY[], + ) {} + + get chainNames(): string[] { + return Object.keys(this.chains); + } + + /** + * Check if chainName lives amongst the list of supported chains by this chain type. + * Throws in the case it's not supported. + */ + public assertChainNameSupported(chainName?: string): asserts chainName is string { + if (!this.isChainSupported(chainName)) throw new Error(`Unsupported chain ${chainName}`); + } + + /** + * Check if chainName lives amongst the list of supported chains by this chain type. + * Throws in the case it's not supported. + */ + public assertChainSupported( + chain?: ChainTypes.IChain, + ): asserts chain is ChainTypes.ChainTypeByEcosystem[ECOSYSTEM] { + if (!this.isChainSupported(chain)) throw new Error(`Unsupported chain ${chain?.name}`); + } + + /** + * Check if chainName lives amongst the list of supported chains by this chain type. + */ + public isChainSupported(chainName?: string | ChainAbstract): boolean { + return ( + !!chainName && + this.chainNames.includes(chainName instanceof ChainAbstract ? chainName.name : chainName) + ); + } + + /** + * @returns true if both chains have the same ID or same name + */ + public isSameChainFromString = (chain1: string, chain2: string): boolean => { + try { + this.assertChainNameSupported(chain1); + this.assertChainNameSupported(chain2); + } catch { + return false; + } + const chain1Object = this.chains[chain1]; + const chain2Object = this.chains[chain2]; + return chain1Object.eq(chain2Object); + }; +} diff --git a/packages/chain/src/chains/evm/data/alfajores.ts b/packages/chain/src/chains/evm/data/alfajores.ts new file mode 100644 index 0000000000..93ebc879f7 --- /dev/null +++ b/packages/chain/src/chains/evm/data/alfajores.ts @@ -0,0 +1 @@ +export const chainId = '44787'; diff --git a/packages/chain/src/chains/evm/data/arbitrum-one.ts b/packages/chain/src/chains/evm/data/arbitrum-one.ts new file mode 100644 index 0000000000..594beaa41e --- /dev/null +++ b/packages/chain/src/chains/evm/data/arbitrum-one.ts @@ -0,0 +1 @@ +export const chainId = '42161'; diff --git a/packages/chain/src/chains/evm/data/arbitrum-rinkeby.ts b/packages/chain/src/chains/evm/data/arbitrum-rinkeby.ts new file mode 100644 index 0000000000..052b36ed95 --- /dev/null +++ b/packages/chain/src/chains/evm/data/arbitrum-rinkeby.ts @@ -0,0 +1,2 @@ +export const chainId = '421611'; +export const testnet = true; diff --git a/packages/chain/src/chains/evm/data/avalanche.ts b/packages/chain/src/chains/evm/data/avalanche.ts new file mode 100644 index 0000000000..f1385eba17 --- /dev/null +++ b/packages/chain/src/chains/evm/data/avalanche.ts @@ -0,0 +1 @@ +export const chainId = '43114'; diff --git a/packages/chain/src/chains/evm/data/base.ts b/packages/chain/src/chains/evm/data/base.ts new file mode 100644 index 0000000000..7f0a26c5ae --- /dev/null +++ b/packages/chain/src/chains/evm/data/base.ts @@ -0,0 +1 @@ +export const chainId = '8453'; diff --git a/packages/chain/src/chains/evm/data/bsc.ts b/packages/chain/src/chains/evm/data/bsc.ts new file mode 100644 index 0000000000..1b5520fa57 --- /dev/null +++ b/packages/chain/src/chains/evm/data/bsc.ts @@ -0,0 +1 @@ +export const chainId = '56'; diff --git a/packages/chain/src/chains/evm/data/bsctest.ts b/packages/chain/src/chains/evm/data/bsctest.ts new file mode 100644 index 0000000000..2fe0d0b668 --- /dev/null +++ b/packages/chain/src/chains/evm/data/bsctest.ts @@ -0,0 +1 @@ +export const chainId = '97'; diff --git a/packages/chain/src/chains/evm/data/celo.ts b/packages/chain/src/chains/evm/data/celo.ts new file mode 100644 index 0000000000..b09d4be5fd --- /dev/null +++ b/packages/chain/src/chains/evm/data/celo.ts @@ -0,0 +1 @@ +export const chainId = '42220'; diff --git a/packages/chain/src/chains/evm/data/core.ts b/packages/chain/src/chains/evm/data/core.ts new file mode 100644 index 0000000000..325b50fd10 --- /dev/null +++ b/packages/chain/src/chains/evm/data/core.ts @@ -0,0 +1 @@ +export const chainId = '1116'; diff --git a/packages/chain/src/chains/evm/data/fantom.ts b/packages/chain/src/chains/evm/data/fantom.ts new file mode 100644 index 0000000000..b1d91043cd --- /dev/null +++ b/packages/chain/src/chains/evm/data/fantom.ts @@ -0,0 +1 @@ +export const chainId = '250'; diff --git a/packages/chain/src/chains/evm/data/fuse.ts b/packages/chain/src/chains/evm/data/fuse.ts new file mode 100644 index 0000000000..e15d0c13a9 --- /dev/null +++ b/packages/chain/src/chains/evm/data/fuse.ts @@ -0,0 +1 @@ +export const chainId = '122'; diff --git a/packages/currency/src/chains/evm/data/zksync-era-testnet.ts b/packages/chain/src/chains/evm/data/goerli.ts similarity index 50% rename from packages/currency/src/chains/evm/data/zksync-era-testnet.ts rename to packages/chain/src/chains/evm/data/goerli.ts index 728f31719b..71e65f0fc5 100644 --- a/packages/currency/src/chains/evm/data/zksync-era-testnet.ts +++ b/packages/chain/src/chains/evm/data/goerli.ts @@ -1,2 +1,2 @@ -export const chainId = 280; +export const chainId = '5'; export const testnet = true; diff --git a/packages/chain/src/chains/evm/data/mainnet.ts b/packages/chain/src/chains/evm/data/mainnet.ts new file mode 100644 index 0000000000..0b2acfaa17 --- /dev/null +++ b/packages/chain/src/chains/evm/data/mainnet.ts @@ -0,0 +1 @@ +export const chainId = '1'; diff --git a/packages/chain/src/chains/evm/data/mantle-testnet.ts b/packages/chain/src/chains/evm/data/mantle-testnet.ts new file mode 100644 index 0000000000..bb05b33cb7 --- /dev/null +++ b/packages/chain/src/chains/evm/data/mantle-testnet.ts @@ -0,0 +1,2 @@ +export const chainId = '5001'; +export const testnet = true; diff --git a/packages/chain/src/chains/evm/data/mantle.ts b/packages/chain/src/chains/evm/data/mantle.ts new file mode 100644 index 0000000000..e63ae4f87f --- /dev/null +++ b/packages/chain/src/chains/evm/data/mantle.ts @@ -0,0 +1 @@ +export const chainId = '5000'; diff --git a/packages/chain/src/chains/evm/data/matic.ts b/packages/chain/src/chains/evm/data/matic.ts new file mode 100644 index 0000000000..3cadbc6ef0 --- /dev/null +++ b/packages/chain/src/chains/evm/data/matic.ts @@ -0,0 +1 @@ +export const chainId = '137'; diff --git a/packages/chain/src/chains/evm/data/moonbeam.ts b/packages/chain/src/chains/evm/data/moonbeam.ts new file mode 100644 index 0000000000..b1c2507e4f --- /dev/null +++ b/packages/chain/src/chains/evm/data/moonbeam.ts @@ -0,0 +1 @@ +export const chainId = '1284'; diff --git a/packages/chain/src/chains/evm/data/mumbai.ts b/packages/chain/src/chains/evm/data/mumbai.ts new file mode 100644 index 0000000000..663d649b2b --- /dev/null +++ b/packages/chain/src/chains/evm/data/mumbai.ts @@ -0,0 +1,2 @@ +export const chainId = '80001'; +export const testnet = true; diff --git a/packages/chain/src/chains/evm/data/optimism.ts b/packages/chain/src/chains/evm/data/optimism.ts new file mode 100644 index 0000000000..7c623307bd --- /dev/null +++ b/packages/chain/src/chains/evm/data/optimism.ts @@ -0,0 +1 @@ +export const chainId = '10'; diff --git a/packages/chain/src/chains/evm/data/private.ts b/packages/chain/src/chains/evm/data/private.ts new file mode 100644 index 0000000000..9b724bb018 --- /dev/null +++ b/packages/chain/src/chains/evm/data/private.ts @@ -0,0 +1 @@ +export const chainId = '0'; diff --git a/packages/currency/src/chains/evm/data/mantle-testnet.ts b/packages/chain/src/chains/evm/data/rinkeby.ts similarity index 50% rename from packages/currency/src/chains/evm/data/mantle-testnet.ts rename to packages/chain/src/chains/evm/data/rinkeby.ts index 9e7a5a5dd8..632dcc8377 100644 --- a/packages/currency/src/chains/evm/data/mantle-testnet.ts +++ b/packages/chain/src/chains/evm/data/rinkeby.ts @@ -1,2 +1,2 @@ -export const chainId = 5001; +export const chainId = '4'; export const testnet = true; diff --git a/packages/chain/src/chains/evm/data/ronin.ts b/packages/chain/src/chains/evm/data/ronin.ts new file mode 100644 index 0000000000..db757a30f8 --- /dev/null +++ b/packages/chain/src/chains/evm/data/ronin.ts @@ -0,0 +1 @@ +export const chainId = '2020'; diff --git a/packages/chain/src/chains/evm/data/sepolia.ts b/packages/chain/src/chains/evm/data/sepolia.ts new file mode 100644 index 0000000000..91f9161b5e --- /dev/null +++ b/packages/chain/src/chains/evm/data/sepolia.ts @@ -0,0 +1,2 @@ +export const chainId = '11155111'; +export const testnet = true; diff --git a/packages/chain/src/chains/evm/data/sokol.ts b/packages/chain/src/chains/evm/data/sokol.ts new file mode 100644 index 0000000000..a46ea22248 --- /dev/null +++ b/packages/chain/src/chains/evm/data/sokol.ts @@ -0,0 +1 @@ +export const chainId = '77'; diff --git a/packages/chain/src/chains/evm/data/tombchain.ts b/packages/chain/src/chains/evm/data/tombchain.ts new file mode 100644 index 0000000000..8f9214959b --- /dev/null +++ b/packages/chain/src/chains/evm/data/tombchain.ts @@ -0,0 +1 @@ +export const chainId = '6969'; diff --git a/packages/chain/src/chains/evm/data/xdai.ts b/packages/chain/src/chains/evm/data/xdai.ts new file mode 100644 index 0000000000..48d540230f --- /dev/null +++ b/packages/chain/src/chains/evm/data/xdai.ts @@ -0,0 +1 @@ +export const chainId = '100'; diff --git a/packages/chain/src/chains/evm/data/zksync-era-testnet.ts b/packages/chain/src/chains/evm/data/zksync-era-testnet.ts new file mode 100644 index 0000000000..8f31d4d87a --- /dev/null +++ b/packages/chain/src/chains/evm/data/zksync-era-testnet.ts @@ -0,0 +1,2 @@ +export const chainId = '280'; +export const testnet = true; diff --git a/packages/chain/src/chains/evm/data/zksync-era.ts b/packages/chain/src/chains/evm/data/zksync-era.ts new file mode 100644 index 0000000000..82bec8c179 --- /dev/null +++ b/packages/chain/src/chains/evm/data/zksync-era.ts @@ -0,0 +1 @@ +export const chainId = '324'; diff --git a/packages/chain/src/chains/evm/evm-chain.ts b/packages/chain/src/chains/evm/evm-chain.ts new file mode 100644 index 0000000000..596643c344 --- /dev/null +++ b/packages/chain/src/chains/evm/evm-chain.ts @@ -0,0 +1,6 @@ +import { ChainAbstract } from '../chain-abstract'; +import { ChainTypes } from '@requestnetwork/types'; + +export class EvmChain extends ChainAbstract implements ChainTypes.IEvmChain { + public readonly ecosystem = ChainTypes.ECOSYSTEM.EVM; +} diff --git a/packages/chain/src/chains/evm/evm-ecosystem.ts b/packages/chain/src/chains/evm/evm-ecosystem.ts new file mode 100644 index 0000000000..f361003bee --- /dev/null +++ b/packages/chain/src/chains/evm/evm-ecosystem.ts @@ -0,0 +1,16 @@ +import { EcosystemAbstract } from '../ecosystem-abstract'; +import { ChainTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { chains } from './index'; +import { EvmChain } from './evm-chain'; + +class EvmEcosystem extends EcosystemAbstract { + constructor(chains: Record) { + super(ChainTypes.ECOSYSTEM.EVM, EvmChain, chains, [ + RequestLogicTypes.CURRENCY.ETH, + RequestLogicTypes.CURRENCY.ERC20, + RequestLogicTypes.CURRENCY.ERC777, + ]); + } +} + +export default new EvmEcosystem(chains); diff --git a/packages/currency/src/chains/evm/index.ts b/packages/chain/src/chains/evm/index.ts similarity index 90% rename from packages/currency/src/chains/evm/index.ts rename to packages/chain/src/chains/evm/index.ts index 445b9fbec6..4ae3cdd3b6 100644 --- a/packages/currency/src/chains/evm/index.ts +++ b/packages/chain/src/chains/evm/index.ts @@ -1,10 +1,10 @@ -import { CurrencyTypes } from '@requestnetwork/types'; -import { Chain } from '../../types'; +import { ChainDefinition } from '../../types'; import * as AlfajoresDefinition from './data/alfajores'; import * as ArbitrumOneDefinition from './data/arbitrum-one'; import * as ArbitrumRinkebyDefinition from './data/arbitrum-rinkeby'; import * as AvalancheDefinition from './data/avalanche'; +import * as BaseDefinition from './data/base'; import * as BscDefinition from './data/bsc'; import * as BscTestDefinition from './data/bsctest'; import * as CeloDefinition from './data/celo'; @@ -28,17 +28,15 @@ import * as XDaiDefinition from './data/xdai'; import * as SepoliaDefinition from './data/sepolia'; import * as ZkSyncEraTestnetDefinition from './data/zksync-era-testnet'; import * as ZkSyncEraDefinition from './data/zksync-era'; -import * as BaseDefinition from './data/base'; - -export type EvmChain = Chain & { - chainId: number; -}; +import { initializeChains } from '../utils'; +import { EvmChain } from './evm-chain'; -export const chains: Record = { +export const chainDefinitions: Record = { alfajores: AlfajoresDefinition, 'arbitrum-one': ArbitrumOneDefinition, 'arbitrum-rinkeby': ArbitrumRinkebyDefinition, avalanche: AvalancheDefinition, + base: BaseDefinition, bsc: BscDefinition, bsctest: BscTestDefinition, celo: CeloDefinition, @@ -62,5 +60,6 @@ export const chains: Record = { sepolia: SepoliaDefinition, zksynceratestnet: ZkSyncEraTestnetDefinition, zksyncera: ZkSyncEraDefinition, - base: BaseDefinition, }; + +export const chains = initializeChains(EvmChain, chainDefinitions); diff --git a/packages/currency/src/chains/near/data/near-testnet.ts b/packages/chain/src/chains/near/data/near-testnet.ts similarity index 100% rename from packages/currency/src/chains/near/data/near-testnet.ts rename to packages/chain/src/chains/near/data/near-testnet.ts diff --git a/packages/currency/src/chains/near/data/near.ts b/packages/chain/src/chains/near/data/near.ts similarity index 100% rename from packages/currency/src/chains/near/data/near.ts rename to packages/chain/src/chains/near/data/near.ts diff --git a/packages/chain/src/chains/near/index.ts b/packages/chain/src/chains/near/index.ts new file mode 100644 index 0000000000..c57fc88cd2 --- /dev/null +++ b/packages/chain/src/chains/near/index.ts @@ -0,0 +1,14 @@ +import * as NearDefinition from './data/near'; +import * as NearTestnetDefinition from './data/near-testnet'; +import { ChainDefinition } from '../../types'; +import { initializeChains } from '../utils'; +import { NearChain } from './near-chain'; + +const chainDefinitions: Record = { + aurora: NearDefinition, + 'aurora-testnet': NearTestnetDefinition, + near: NearDefinition, + 'near-testnet': NearTestnetDefinition, +}; + +export const chains = initializeChains(NearChain, chainDefinitions); diff --git a/packages/chain/src/chains/near/near-chain.ts b/packages/chain/src/chains/near/near-chain.ts new file mode 100644 index 0000000000..9aa1abb586 --- /dev/null +++ b/packages/chain/src/chains/near/near-chain.ts @@ -0,0 +1,6 @@ +import { ChainAbstract } from '../chain-abstract'; +import { ChainTypes } from '@requestnetwork/types'; + +export class NearChain extends ChainAbstract implements ChainTypes.INearChain { + public readonly ecosystem = ChainTypes.ECOSYSTEM.NEAR; +} diff --git a/packages/chain/src/chains/near/near-ecosystem.ts b/packages/chain/src/chains/near/near-ecosystem.ts new file mode 100644 index 0000000000..b1c3a38781 --- /dev/null +++ b/packages/chain/src/chains/near/near-ecosystem.ts @@ -0,0 +1,15 @@ +import { EcosystemAbstract } from '../ecosystem-abstract'; +import { ChainTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { chains } from './index'; +import { NearChain } from './near-chain'; + +class NearEcosystem extends EcosystemAbstract { + constructor(chains: Record) { + super(ChainTypes.ECOSYSTEM.NEAR, NearChain, chains, [ + RequestLogicTypes.CURRENCY.ETH, + RequestLogicTypes.CURRENCY.ERC20, + ]); + } +} + +export default new NearEcosystem(chains); diff --git a/packages/chain/src/chains/utils.ts b/packages/chain/src/chains/utils.ts new file mode 100644 index 0000000000..92caed3525 --- /dev/null +++ b/packages/chain/src/chains/utils.ts @@ -0,0 +1,18 @@ +import { ChainAbstract } from './chain-abstract'; +import { ChainDefinition } from '../types'; + +export const initializeChains = ( + chainClass: new (chainId: string, chainName: string, testnet?: boolean) => CHAIN_CLASS, + chainDefinitions: Record, +): Record => + Object.keys(chainDefinitions).reduce( + (chains, chainName) => { + chains[chainName] = new chainClass( + chainDefinitions[chainName].chainId, + chainName, + chainDefinitions[chainName].testnet, + ); + return chains; + }, + {} as Record, + ); diff --git a/packages/chain/src/index.ts b/packages/chain/src/index.ts new file mode 100644 index 0000000000..5e3f74d887 --- /dev/null +++ b/packages/chain/src/index.ts @@ -0,0 +1,2 @@ +export * from './types'; +export { ChainManager } from './chain-manager'; diff --git a/packages/chain/src/types.ts b/packages/chain/src/types.ts new file mode 100644 index 0000000000..dc4039e1a0 --- /dev/null +++ b/packages/chain/src/types.ts @@ -0,0 +1,7 @@ +/** + * Common types used in chain configuration files + */ +export type ChainDefinition = { + chainId: string; + testnet?: boolean; +}; diff --git a/packages/chain/test/chain.test.ts b/packages/chain/test/chain.test.ts new file mode 100644 index 0000000000..ac971d40d0 --- /dev/null +++ b/packages/chain/test/chain.test.ts @@ -0,0 +1,50 @@ +import EvmEcosystem from '../src/chains/evm/evm-ecosystem'; +import { ChainManager } from '../src'; +import { ChainTypes } from '@requestnetwork/types'; +import NearEcosystem from '../src/chains/near/near-ecosystem'; + +describe('chain equality', () => { + it('Should return true for 2 identical EVMs', () => { + const chain1 = ChainManager.current().fromName('arbitrum-one', [ChainTypes.ECOSYSTEM.EVM]); + const chain2 = ChainManager.current().fromName('arbitrum-one', [ChainTypes.ECOSYSTEM.EVM]); + expect(chain1.eq(chain2)).toBe(true); + }); + it('Should return false for 2 different EVMs', () => { + const chain1 = ChainManager.current().fromName('mainnet', [ChainTypes.ECOSYSTEM.EVM]); + const chain2 = ChainManager.current().fromName('arbitrum-one', [ChainTypes.ECOSYSTEM.EVM]); + expect(chain1.eq(chain2)).toBe(false); + }); + // FIXME: get rid of all aurora alias and mentions + it('Should return true for 2 identical NEAR', () => { + const chain1 = ChainManager.current().fromName('aurora-testnet', [ChainTypes.ECOSYSTEM.NEAR]); + const chain2 = ChainManager.current().fromName('near-testnet', [ChainTypes.ECOSYSTEM.NEAR]); + expect(chain1.eq(chain2)).toBe(true); + }); + it('Should return false for 2 different chains on 2 different ecosystems', () => { + const chain2 = ChainManager.current().fromName('arbitrum-one', [ChainTypes.ECOSYSTEM.EVM]); + const chain1 = ChainManager.current().fromName('aurora-testnet', [ChainTypes.ECOSYSTEM.NEAR]); + expect(chain1.eq(chain2)).toBe(false); + }); +}); + +describe('ecosystem isChainSupported', () => { + describe('NearEcosystem', () => { + it('returns true for near', () => { + expect(NearEcosystem.isChainSupported('near')).toEqual(true); + }); + it('returns true for aurora', () => { + expect(NearEcosystem.isChainSupported('aurora')).toEqual(true); + }); + it('returns false for mainnet', () => { + expect(NearEcosystem.isChainSupported('mainnet')).toEqual(false); + }); + }); + describe('EvmEcosystem', () => { + it('returns true for mainnet', () => { + expect(EvmEcosystem.isChainSupported('mainnet')).toEqual(true); + }); + it('returns false for near', () => { + expect(EvmEcosystem.isChainSupported('near')).toEqual(false); + }); + }); +}); diff --git a/packages/chain/tsconfig.build.json b/packages/chain/tsconfig.build.json new file mode 100644 index 0000000000..b2e8e973a9 --- /dev/null +++ b/packages/chain/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "exclude": ["test/", "scripts/", "**/*.test.ts", "**/*.spec.ts"], + "references": [ + { "path": "../types/tsconfig.build.json" }, + { "path": "../utils/tsconfig.build.json" } + ] +} diff --git a/packages/chain/tsconfig.json b/packages/chain/tsconfig.json new file mode 100644 index 0000000000..8a3a37edff --- /dev/null +++ b/packages/chain/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "esModuleInterop": true + }, + "include": ["src/", "test/"] +} diff --git a/packages/currency/README.md b/packages/currency/README.md index 7d4784be2d..9769df6dc7 100644 --- a/packages/currency/README.md +++ b/packages/currency/README.md @@ -1,7 +1,7 @@ # @requestnetwork/currency `@requestnetwork/currency` is a typescript library part of the [Request Network protocol](https://github.com/RequestNetwork/requestNetwork). -It is a collection of tools for the currencies and chains shared between the @requestnetwork packages. +It is a collection of tools for the currencies shared between the @requestnetwork packages. ## Installation @@ -51,27 +51,6 @@ console.log(FAUToken.symbol); // FAU Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. [Read the contributing guide](/CONTRIBUTING.md) -### Adding a new chain - -Supported chains are listed in `src/chains`: - -- `src/chains/btc/data` for BTC type chains -- `src/chains/evm/data` for EVM type chains -- `src/chains/near/data` for NEAR type chains - -The chain names are subjective, but they are unique and uniform across all Request Network packages. -They are formatted with the kebab-case naming convention. - -In order to add a new chain, first create a file `[nameOfTheChain].ts` in the correct directory. -Its internal structure should conform with the corresponding type, respectively: - -- `BtcChain` -- `EvmChain` -- `NearChain` - -These types are described in the `index.ts` file of each chain subdirectory. -Please add the `testnet: true` property for staging chains. - ## License [MIT](/LICENSE) diff --git a/packages/currency/package.json b/packages/currency/package.json index ef9d75e3b9..832859c960 100644 --- a/packages/currency/package.json +++ b/packages/currency/package.json @@ -43,6 +43,7 @@ }, "dependencies": { "@metamask/contract-metadata": "1.31.0", + "@requestnetwork/chain": "0.13.0", "@requestnetwork/types": "0.40.0", "@requestnetwork/utils": "0.40.0", "multicoin-address-validator": "0.5.15", diff --git a/packages/currency/src/chains/ChainsAbstract.ts b/packages/currency/src/chains/ChainsAbstract.ts deleted file mode 100644 index d498baa959..0000000000 --- a/packages/currency/src/chains/ChainsAbstract.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { Chain, NamedNativeCurrency, TokenMap } from '../types'; -import { CurrencyTypes, RequestLogicTypes } from '@requestnetwork/types'; -import { nativeCurrencies } from '../native'; - -export abstract class ChainsAbstract< - CHAIN_NAME extends CurrencyTypes.ChainName, - CHAIN extends Chain, - CHAIN_ID extends string | number, -> { - public chains: Record; - public chainNames: CHAIN_NAME[]; - - constructor( - chains: Record, - currencyType: RequestLogicTypes.CURRENCY.ETH | RequestLogicTypes.CURRENCY.BTC, - ) { - this.chains = chains; - this.chainNames = Object.keys(chains) as CHAIN_NAME[]; - this.addNativeCurrenciesToChains(currencyType); - } - - /** - * Adds the native currency to the list of currencies supported by each chain - */ - private addNativeCurrenciesToChains( - currencyType: RequestLogicTypes.CURRENCY.ETH | RequestLogicTypes.CURRENCY.BTC, - ): void { - this.chainNames.forEach((chainName) => { - const nativeCurrency = (nativeCurrencies[currencyType] as NamedNativeCurrency[]).find( - (currency) => currency.network === chainName, - ); - if (nativeCurrency) { - const chainCurrencies: TokenMap = this.chains[chainName].currencies || {}; - chainCurrencies.native = nativeCurrency; - this.chains[chainName].currencies = chainCurrencies; - } - }); - } - - /** - * Check if chainName lives amongst the list of supported chains by this chain type. - * Throws in the case it's not supported. - */ - public assertChainSupported(chainName?: string): asserts chainName is CHAIN_NAME { - if (!this.isChainSupported(chainName)) throw new Error(`Unsupported chain ${chainName}`); - } - - /** - * Check if chainName lives amongst the list of supported chains by this chain type. - */ - public isChainSupported(chainName?: string): chainName is CHAIN_NAME { - return !!chainName && (this.chainNames as string[]).includes(chainName); - } - - /** - * Retrieve the corresponding chain ID from Request Network's internal chain name representation - */ - public getChainId(chainName: CHAIN_NAME): CHAIN_ID { - return this.chains[chainName].chainId as CHAIN_ID; - } - - /** - * Retrieve Request Network's internal chain name representation from the corresponding chain ID - */ - public getChainName(chainId: CHAIN_ID): CHAIN_NAME | undefined { - return this.chainNames.find((chainName) => this.chains[chainName].chainId === chainId); - } - - /** - * Returns true is the chain is a testnet chain - */ - public isTestnet(chainName: CHAIN_NAME): boolean { - return Boolean(this.chains[chainName].testnet); - } - - /** - * @returns true if both chains have the same ID or same name - */ - public isSameChain = (chain1: CHAIN_NAME, chain2: CHAIN_NAME): boolean => { - return chain1 === chain2 || this.getChainId(chain1) === this.getChainId(chain2); - }; - - /** - * @returns true if both chains have the same ID or same name - */ - public isSameChainFromString = (chain1: string, chain2: string): boolean => { - try { - this.assertChainSupported(chain1); - this.assertChainSupported(chain2); - } catch { - return false; - } - return this.isSameChain(chain1, chain2); - }; -} diff --git a/packages/currency/src/chains/btc/BtcChains.ts b/packages/currency/src/chains/btc/BtcChains.ts deleted file mode 100644 index defd944134..0000000000 --- a/packages/currency/src/chains/btc/BtcChains.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ChainsAbstract } from '../ChainsAbstract'; -import { CurrencyTypes, RequestLogicTypes } from '@requestnetwork/types'; -import { BtcChain, chains } from './index'; - -class BtcChains extends ChainsAbstract {} -export default new BtcChains(chains, RequestLogicTypes.CURRENCY.BTC); diff --git a/packages/currency/src/chains/btc/index.ts b/packages/currency/src/chains/btc/index.ts deleted file mode 100644 index 7c46a09b8e..0000000000 --- a/packages/currency/src/chains/btc/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { CurrencyTypes } from '@requestnetwork/types'; -import { Chain } from '../../types'; - -import * as MainnetDefinition from './data/mainnet'; -import * as TestnetDefinition from './data/testnet'; - -export type BtcChain = Chain & { - chainId: string; -}; - -export const chains: Record = { - mainnet: MainnetDefinition, - testnet: TestnetDefinition, -}; diff --git a/packages/currency/src/chains/declarative/DeclarativeChains.ts b/packages/currency/src/chains/declarative/DeclarativeChains.ts deleted file mode 100644 index 68f36f7454..0000000000 --- a/packages/currency/src/chains/declarative/DeclarativeChains.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ChainsAbstract } from '../ChainsAbstract'; -import { CurrencyTypes, RequestLogicTypes } from '@requestnetwork/types'; -import { DeclarativeChain, chains } from './index'; - -class DeclarativeChains extends ChainsAbstract< - CurrencyTypes.DeclarativeChainName, - DeclarativeChain, - string -> {} -export default new DeclarativeChains(chains, RequestLogicTypes.CURRENCY.ETH); diff --git a/packages/currency/src/chains/declarative/index.ts b/packages/currency/src/chains/declarative/index.ts deleted file mode 100644 index 6e38952bd8..0000000000 --- a/packages/currency/src/chains/declarative/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { CurrencyTypes } from '@requestnetwork/types'; -import { Chain } from '../../types'; - -import * as TronDefinition from './data/tron'; -import * as SolanaDefinition from './data/solana'; - -export type DeclarativeChain = Chain; - -export const chains: Record = { - tron: TronDefinition, - solana: SolanaDefinition, -}; diff --git a/packages/currency/src/chains/evm/EvmChains.ts b/packages/currency/src/chains/evm/EvmChains.ts deleted file mode 100644 index f9ee82f6de..0000000000 --- a/packages/currency/src/chains/evm/EvmChains.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ChainsAbstract } from '../ChainsAbstract'; -import { CurrencyTypes, RequestLogicTypes } from '@requestnetwork/types'; -import { chains, EvmChain } from './index'; - -class EvmChains extends ChainsAbstract {} -export default new EvmChains(chains, RequestLogicTypes.CURRENCY.ETH); diff --git a/packages/currency/src/chains/evm/data/alfajores.ts b/packages/currency/src/chains/evm/data/alfajores.ts deleted file mode 100644 index 8614655cb8..0000000000 --- a/packages/currency/src/chains/evm/data/alfajores.ts +++ /dev/null @@ -1 +0,0 @@ -export const chainId = 44787; diff --git a/packages/currency/src/chains/evm/data/arbitrum-one.ts b/packages/currency/src/chains/evm/data/arbitrum-one.ts deleted file mode 100644 index 7dd7a3a62d..0000000000 --- a/packages/currency/src/chains/evm/data/arbitrum-one.ts +++ /dev/null @@ -1 +0,0 @@ -export const chainId = 42161; diff --git a/packages/currency/src/chains/evm/data/arbitrum-rinkeby.ts b/packages/currency/src/chains/evm/data/arbitrum-rinkeby.ts deleted file mode 100644 index 19da716ff9..0000000000 --- a/packages/currency/src/chains/evm/data/arbitrum-rinkeby.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const chainId = 421611; -export const testnet = true; diff --git a/packages/currency/src/chains/evm/data/avalanche.ts b/packages/currency/src/chains/evm/data/avalanche.ts deleted file mode 100644 index 6bd1c9f5d6..0000000000 --- a/packages/currency/src/chains/evm/data/avalanche.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { TokenMap } from '../../../types'; -import { supportedAvalancheERC20 } from '../../../erc20/chains/avalanche'; - -export const chainId = 43114; -export const currencies: TokenMap = { - ...supportedAvalancheERC20, -}; diff --git a/packages/currency/src/chains/evm/data/base.ts b/packages/currency/src/chains/evm/data/base.ts deleted file mode 100644 index e9e169645e..0000000000 --- a/packages/currency/src/chains/evm/data/base.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { TokenMap } from '../../../types'; -import { supportedBaseERC20 } from '../../../erc20/chains/base'; - -export const chainId = 8453; -export const currencies: TokenMap = { - ...supportedBaseERC20, -}; diff --git a/packages/currency/src/chains/evm/data/bsc.ts b/packages/currency/src/chains/evm/data/bsc.ts deleted file mode 100644 index b13f8f522a..0000000000 --- a/packages/currency/src/chains/evm/data/bsc.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { TokenMap } from '../../../types'; -import { supportedBSCERC20 } from '../../../erc20/chains/bsc'; - -export const chainId = 56; -export const currencies: TokenMap = { - ...supportedBSCERC20, -}; diff --git a/packages/currency/src/chains/evm/data/bsctest.ts b/packages/currency/src/chains/evm/data/bsctest.ts deleted file mode 100644 index 7fc2bc374e..0000000000 --- a/packages/currency/src/chains/evm/data/bsctest.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { TokenMap } from '../../../types'; -import { supportedBSCTestERC20 } from '../../../erc20/chains/bsctest'; - -export const chainId = 97; -export const currencies: TokenMap = { - ...supportedBSCTestERC20, -}; diff --git a/packages/currency/src/chains/evm/data/celo.ts b/packages/currency/src/chains/evm/data/celo.ts deleted file mode 100644 index f07c603cd4..0000000000 --- a/packages/currency/src/chains/evm/data/celo.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { TokenMap } from '../../../types'; -import { supportedCeloERC20 } from '../../../erc20/chains/celo'; - -export const chainId = 42220; -export const currencies: TokenMap = { - ...supportedCeloERC20, -}; diff --git a/packages/currency/src/chains/evm/data/core.ts b/packages/currency/src/chains/evm/data/core.ts deleted file mode 100644 index d148ecf595..0000000000 --- a/packages/currency/src/chains/evm/data/core.ts +++ /dev/null @@ -1 +0,0 @@ -export const chainId = 1116; diff --git a/packages/currency/src/chains/evm/data/fantom.ts b/packages/currency/src/chains/evm/data/fantom.ts deleted file mode 100644 index 596a2db0db..0000000000 --- a/packages/currency/src/chains/evm/data/fantom.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { TokenMap } from '../../../types'; -import { supportedFantomERC20 } from '../../../erc20/chains/fantom'; - -export const chainId = 250; -export const currencies: TokenMap = { - ...supportedFantomERC20, -}; diff --git a/packages/currency/src/chains/evm/data/fuse.ts b/packages/currency/src/chains/evm/data/fuse.ts deleted file mode 100644 index 010d964245..0000000000 --- a/packages/currency/src/chains/evm/data/fuse.ts +++ /dev/null @@ -1 +0,0 @@ -export const chainId = 122; diff --git a/packages/currency/src/chains/evm/data/goerli.ts b/packages/currency/src/chains/evm/data/goerli.ts deleted file mode 100644 index 2f700bcfbd..0000000000 --- a/packages/currency/src/chains/evm/data/goerli.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { TokenMap } from '../../../types'; -import { supportedGoerliERC20 } from '../../../erc20/chains/goerli'; - -export const chainId = 5; -export const testnet = true; -export const currencies: TokenMap = { - ...supportedGoerliERC20, -}; diff --git a/packages/currency/src/chains/evm/data/mainnet.ts b/packages/currency/src/chains/evm/data/mainnet.ts deleted file mode 100644 index 4e6bb83ceb..0000000000 --- a/packages/currency/src/chains/evm/data/mainnet.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { TokenMap } from '../../../types'; -import { supportedMainnetERC20 } from '../../../erc20/chains/mainnet'; - -export const chainId = 1; -export const currencies: TokenMap = { - ...supportedMainnetERC20, -}; diff --git a/packages/currency/src/chains/evm/data/mantle.ts b/packages/currency/src/chains/evm/data/mantle.ts deleted file mode 100644 index 7528d6abaa..0000000000 --- a/packages/currency/src/chains/evm/data/mantle.ts +++ /dev/null @@ -1 +0,0 @@ -export const chainId = 5000; diff --git a/packages/currency/src/chains/evm/data/matic.ts b/packages/currency/src/chains/evm/data/matic.ts deleted file mode 100644 index a7738d898c..0000000000 --- a/packages/currency/src/chains/evm/data/matic.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { TokenMap } from '../../../types'; -import { supportedMaticERC20 } from '../../../erc20/chains/matic'; - -export const chainId = 137; -export const currencies: TokenMap = { - ...supportedMaticERC20, -}; diff --git a/packages/currency/src/chains/evm/data/moonbeam.ts b/packages/currency/src/chains/evm/data/moonbeam.ts deleted file mode 100644 index de586f0b7b..0000000000 --- a/packages/currency/src/chains/evm/data/moonbeam.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { TokenMap } from '../../../types'; -import { supportedMoonbeamERC20 } from '../../../erc20/chains/moonbeam'; - -export const chainId = 1284; -export const currencies: TokenMap = { - ...supportedMoonbeamERC20, -}; diff --git a/packages/currency/src/chains/evm/data/mumbai.ts b/packages/currency/src/chains/evm/data/mumbai.ts deleted file mode 100644 index a5b0bd6e95..0000000000 --- a/packages/currency/src/chains/evm/data/mumbai.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const chainId = 80001; -export const testnet = true; diff --git a/packages/currency/src/chains/evm/data/optimism.ts b/packages/currency/src/chains/evm/data/optimism.ts deleted file mode 100644 index 447d2f2300..0000000000 --- a/packages/currency/src/chains/evm/data/optimism.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { TokenMap } from '../../../types'; -import { supportedOptimismERC20 } from '../../../erc20/chains/optimism'; - -export const chainId = 10; -export const currencies: TokenMap = { - ...supportedOptimismERC20, -}; diff --git a/packages/currency/src/chains/evm/data/private.ts b/packages/currency/src/chains/evm/data/private.ts deleted file mode 100644 index 8334ef3906..0000000000 --- a/packages/currency/src/chains/evm/data/private.ts +++ /dev/null @@ -1 +0,0 @@ -export const chainId = 0; diff --git a/packages/currency/src/chains/evm/data/rinkeby.ts b/packages/currency/src/chains/evm/data/rinkeby.ts deleted file mode 100644 index aa9da936d3..0000000000 --- a/packages/currency/src/chains/evm/data/rinkeby.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { TokenMap } from '../../../types'; -import { supportedRinkebyERC20 } from '../../../erc20/chains/rinkeby'; -import { supportedRinkebyERC777 } from '../../../erc777/chains/rinkeby'; - -export const chainId = 4; -export const testnet = true; -export const currencies: TokenMap = { - ...supportedRinkebyERC20, - ...supportedRinkebyERC777, -}; diff --git a/packages/currency/src/chains/evm/data/ronin.ts b/packages/currency/src/chains/evm/data/ronin.ts deleted file mode 100644 index ca79b559f7..0000000000 --- a/packages/currency/src/chains/evm/data/ronin.ts +++ /dev/null @@ -1 +0,0 @@ -export const chainId = 2020; diff --git a/packages/currency/src/chains/evm/data/sepolia.ts b/packages/currency/src/chains/evm/data/sepolia.ts deleted file mode 100644 index 9b90d7f875..0000000000 --- a/packages/currency/src/chains/evm/data/sepolia.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { TokenMap } from '../../../types'; -import { supportedSepoliaERC20 } from '../../../erc20/chains/sepolia'; - -export const chainId = 11155111; -export const testnet = true; -export const currencies: TokenMap = { - ...supportedSepoliaERC20, -}; diff --git a/packages/currency/src/chains/evm/data/sokol.ts b/packages/currency/src/chains/evm/data/sokol.ts deleted file mode 100644 index ba023a8140..0000000000 --- a/packages/currency/src/chains/evm/data/sokol.ts +++ /dev/null @@ -1 +0,0 @@ -export const chainId = 77; diff --git a/packages/currency/src/chains/evm/data/tombchain.ts b/packages/currency/src/chains/evm/data/tombchain.ts deleted file mode 100644 index 43cc4b72b6..0000000000 --- a/packages/currency/src/chains/evm/data/tombchain.ts +++ /dev/null @@ -1 +0,0 @@ -export const chainId = 6969; diff --git a/packages/currency/src/chains/evm/data/xdai.ts b/packages/currency/src/chains/evm/data/xdai.ts deleted file mode 100644 index edbf8ba0ce..0000000000 --- a/packages/currency/src/chains/evm/data/xdai.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { TokenMap } from '../../../types'; -import { supportedXDAIERC20 } from '../../../erc20/chains/xdai'; - -export const chainId = 100; -export const currencies: TokenMap = { - ...supportedXDAIERC20, -}; diff --git a/packages/currency/src/chains/evm/data/zksync-era.ts b/packages/currency/src/chains/evm/data/zksync-era.ts deleted file mode 100644 index 293661efc3..0000000000 --- a/packages/currency/src/chains/evm/data/zksync-era.ts +++ /dev/null @@ -1 +0,0 @@ -export const chainId = 324; diff --git a/packages/currency/src/chains/index.ts b/packages/currency/src/chains/index.ts deleted file mode 100644 index 3bcd7acd23..0000000000 --- a/packages/currency/src/chains/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import BtcChains from './btc/BtcChains'; -import EvmChains from './evm/EvmChains'; -import NearChains from './near/NearChains'; -import DeclarativeChains from './declarative/DeclarativeChains'; -import { isSameChain } from './utils'; - -export { BtcChains, EvmChains, NearChains, DeclarativeChains, isSameChain }; diff --git a/packages/currency/src/chains/near/NearChains.ts b/packages/currency/src/chains/near/NearChains.ts deleted file mode 100644 index 411d734c56..0000000000 --- a/packages/currency/src/chains/near/NearChains.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ChainsAbstract } from '../ChainsAbstract'; -import { CurrencyTypes, RequestLogicTypes } from '@requestnetwork/types'; -import { NearChain, chains } from './index'; - -class NearChains extends ChainsAbstract {} -export default new NearChains(chains, RequestLogicTypes.CURRENCY.ETH); diff --git a/packages/currency/src/chains/near/index.ts b/packages/currency/src/chains/near/index.ts deleted file mode 100644 index 923cce384f..0000000000 --- a/packages/currency/src/chains/near/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { CurrencyTypes } from '@requestnetwork/types'; -import { Chain } from '../../types'; - -import * as NearDefinition from './data/near'; -import * as NearTestnetDefinition from './data/near-testnet'; - -export type NearChain = Chain; - -export const chains: Record = { - aurora: NearDefinition, - 'aurora-testnet': NearTestnetDefinition, - near: NearDefinition, - 'near-testnet': NearTestnetDefinition, -}; diff --git a/packages/currency/src/chains/utils.ts b/packages/currency/src/chains/utils.ts deleted file mode 100644 index 8850405067..0000000000 --- a/packages/currency/src/chains/utils.ts +++ /dev/null @@ -1,13 +0,0 @@ -import BtcChains from './btc/BtcChains'; -import EvmChains from './evm/EvmChains'; -import NearChains from './near/NearChains'; - -// Returns true if both chains are equal or aliases -export const isSameChain = (chain1: string, chain2: string): boolean => { - return ( - chain1 === chain2 || - !![EvmChains, NearChains, BtcChains].find((chainSystem) => { - return chainSystem.isSameChainFromString(chain1, chain2); - }) - ); -}; diff --git a/packages/currency/src/conversion-aggregators.ts b/packages/currency/src/conversion-aggregators.ts index b37affbe4a..052edd772e 100644 --- a/packages/currency/src/conversion-aggregators.ts +++ b/packages/currency/src/conversion-aggregators.ts @@ -8,7 +8,6 @@ import sepoliaAggregator from './aggregators/sepolia.json'; import rinkebyAggregator from './aggregators/rinkeby.json'; import maticAggregator from './aggregators/matic.json'; import fantomAggregator from './aggregators/fantom.json'; -import { CurrencyTypes } from '@requestnetwork/types'; /** * currencyFrom => currencyTo => cost @@ -16,16 +15,14 @@ import { CurrencyTypes } from '@requestnetwork/types'; export type CurrencyPairs = Record>; /** - * Aggregators maps define pairs of currencies for which an onchain oracle exists, by network. + * Aggregator maps define pairs of currencies for which an onchain oracle exists, by network. * * Network => currencyFrom => currencyTo => cost */ -export type AggregatorsMap = Partial< - Record ->; +export type AggregatorsMap = Partial>; // Pairs supported by Chainlink (can be generated from requestNetwork/toolbox/src/chainlinkConversionPathTools.ts) -const chainlinkCurrencyPairs: AggregatorsMap = { +const chainlinkCurrencyPairs: AggregatorsMap = { private: privateAggregator, goerli: goerliAggregator, rinkeby: rinkebyAggregator, @@ -59,9 +56,7 @@ export const defaultConversionPairs: AggregatorsMap = { ...noConversionNetworks, }; -export const conversionSupportedNetworks = Object.keys( - defaultConversionPairs, -) as CurrencyTypes.ChainName[]; +export const conversionSupportedNetworks = Object.keys(defaultConversionPairs); /** * Gets the on-chain conversion path between two currencies. @@ -76,7 +71,7 @@ export const conversionSupportedNetworks = Object.keys( export function getPath( currencyFrom: Pick, currencyTo: Pick, - network: CurrencyTypes.ChainName = 'mainnet', + network = 'mainnet', pairs = defaultConversionPairs, ): string[] | null { if (!pairs[network]) { diff --git a/packages/currency/src/currencyManager.ts b/packages/currency/src/currency-manager.ts similarity index 64% rename from packages/currency/src/currencyManager.ts rename to packages/currency/src/currency-manager.ts index d5e9d22907..11143a364b 100644 --- a/packages/currency/src/currencyManager.ts +++ b/packages/currency/src/currency-manager.ts @@ -1,23 +1,24 @@ -import { CurrencyTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ChainTypes, RequestLogicTypes } from '@requestnetwork/types'; import { utils } from 'ethers'; import addressValidator from 'multicoin-address-validator'; -import { getSupportedERC20Tokens } from './erc20'; -import { getSupportedERC777Tokens } from './erc777'; +import { getSupportedERC20Currencies } from './erc20'; +import { getSupportedERC777Currencies } from './erc777'; import { getHash } from './getHash'; -import iso4217 from './iso4217'; -import { nativeCurrencies } from './native'; import { - StorageCurrency, + Currency, CurrencyDefinition, CurrencyInput, - ERC20Currency, ICurrencyManager, LegacyTokenMap, + MixedCurrencyType, NativeCurrencyType, + StorageCurrency, } from './types'; -import { defaultConversionPairs, AggregatorsMap, getPath } from './conversion-aggregators'; +import { AggregatorsMap, defaultConversionPairs, getPath } from './conversion-aggregators'; import { isValidNearAddress } from './currency-utils'; -import { NearChains } from './chains'; +import { getSupportedNativeCurrencies } from './native'; +import { ChainManager } from '@requestnetwork/chain'; +import { getSupportedIso4217Currencies } from './iso4217'; const { BTC, ERC20, ERC777, ETH, ISO4217 } = RequestLogicTypes.CURRENCY; @@ -28,20 +29,28 @@ export class CurrencyManager implements ICurrencyManager private readonly knownCurrencies: CurrencyDefinition[]; private readonly legacyTokens: LegacyTokenMap; private readonly conversionPairs: AggregatorsMap; + public readonly chainManager: ChainTypes.IChainManager; private static defaultInstance: CurrencyManager; /** - * * @param inputCurrencies The list of currencies known by the Manager. * @param legacyTokens A mapping of legacy currency name or network name, in the format { "chainName": {"TOKEN": ["NEW_TOKEN","NEW_CHAIN"]}} * @param conversionPairs A mapping of possible conversions by network (network => currencyFrom => currencyTo => cost) + * @param chainManager A ChainManager instance that describes the supported underlying chains */ constructor( - inputCurrencies: (CurrencyInput & { id?: string; meta?: TMeta })[], + inputCurrencies: MixedCurrencyType[], 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); @@ -50,8 +59,6 @@ export class CurrencyManager implements ICurrencyManager } this.knownCurrencies.push(currency); } - this.legacyTokens = legacyTokens || CurrencyManager.getDefaultLegacyTokens(); - this.conversionPairs = conversionPairs || CurrencyManager.getDefaultConversionPairs(); } /** @@ -63,7 +70,7 @@ export class CurrencyManager implements ICurrencyManager */ from( currencyIdentifier: string | undefined, - network?: CurrencyTypes.ChainName, + network?: string | ChainTypes.IChain, ): CurrencyDefinition | undefined { if (!currencyIdentifier) { return; @@ -72,17 +79,19 @@ export class CurrencyManager implements ICurrencyManager return this.fromAddress(currencyIdentifier, network); } - if (network && currencyIdentifier.indexOf(network) === -1) { + if (network && currencyIdentifier.indexOf(ChainManager.getName(network)) === -1) { currencyIdentifier = CurrencyManager.currencyId({ symbol: currencyIdentifier, network }); } const currencyFromId = this.fromId(currencyIdentifier); - if (currencyFromId) return currencyFromId; + if (currencyFromId) { + return currencyFromId; + } const parts = currencyIdentifier.split('-'); const currencyFromSymbol = - this.fromSymbol(parts[0], network || (parts[1] as CurrencyTypes.ChainName)) || + this.fromSymbol(parts[0], network || parts[1]) || // try without splitting the symbol to support currencies like ETH-rinkeby this.fromSymbol(currencyIdentifier, network); @@ -100,16 +109,22 @@ export class CurrencyManager implements ICurrencyManager * Gets a supported currency from its address and network. * If more than one currency are found, undefined is returned */ - fromAddress(address: string, network?: string): CurrencyDefinition | undefined { + fromAddress( + address: string, + network?: string | ChainTypes.IChain, + ): CurrencyDefinition | undefined { address = utils.getAddress(address); const matches = this.knownCurrencies.filter( (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) => (x as ERC20Currency).network).join(', '); + const networks = matches.map((x) => ('network' in x ? x.network.name : '')).join(', '); console.warn( `${address} has several matches on ${networks}. To avoid errors, specify a network.`, ); @@ -123,12 +138,10 @@ export class CurrencyManager implements ICurrencyManager */ fromSymbol( symbol: string, - network?: CurrencyTypes.ChainName, + network?: string | ChainTypes.IChain, ): CurrencyDefinition | undefined { symbol = symbol?.toUpperCase(); - network = network?.toLowerCase() as CurrencyTypes.ChainName | undefined; - - const legacy = network ? this.legacyTokens[network]?.[symbol] : undefined; + const legacy = network ? this.legacyTokens[ChainManager.getName(network)]?.[symbol] : undefined; if (legacy) { [symbol, network] = legacy; } @@ -136,17 +149,27 @@ export class CurrencyManager implements ICurrencyManager return this.knownCurrencies.find( (x) => x.symbol.toUpperCase() === symbol && - ((x.type === ISO4217 && !network) || ('network' in x && x.network === network) || !network), + (!network || ('network' in x && x.network.name === ChainManager.getName(network))) && + (!network || + typeof network === 'string' || + this.chainManager.ecosystems[network.ecosystem].currencyTypes.includes(x.type)), ); } - fromHash(hash: string, network?: string): CurrencyDefinition | undefined { + fromHash( + hash: string, + network?: string | ChainTypes.IChain, + ): CurrencyDefinition | undefined { return this.knownCurrencies.find( (x) => x.hash.toLowerCase() === hash.toLowerCase() && - ((x.type === ISO4217 && !network) || ('network' in x && x.network === network) || !network), + (!network || ('network' in x && x.network.name === ChainManager.getName(network))) && + (!network || + typeof network === 'string' || + this.chainManager.ecosystems[network.ecosystem].currencyTypes.includes(x.type)), ); } + /** * Retrieves a currency given its storage format (ICurrency) */ @@ -163,8 +186,8 @@ export class CurrencyManager implements ICurrencyManager 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)), ); } @@ -174,27 +197,26 @@ export class CurrencyManager implements ICurrencyManager */ getNativeCurrency( type: NativeCurrencyType, - network: string, + network: string | ChainTypes.IChain, ): CurrencyDefinition | undefined { - return this.knownCurrencies.find((x) => x.type === type && x.network === network); + return this.knownCurrencies.find( + (x) => x.type === type && x.network.name === ChainManager.getName(network), + ); } getConversionPath( from: Pick, to: Pick, - network: CurrencyTypes.ChainName, + network: string | ChainTypes.IChain, ): string[] | null { try { - return getPath(from, to, network, this.conversionPairs); + return getPath(from, to, ChainManager.getName(network), this.conversionPairs); } catch (e) { return null; } } - supportsConversion( - currency: Pick, - network: CurrencyTypes.ChainName, - ): boolean { + supportsConversion(currency: Pick, network: string): boolean { return !!this.conversionPairs[network]?.[currency.hash.toLowerCase()]; } @@ -206,7 +228,7 @@ export class CurrencyManager implements ICurrencyManager hash, meta, ...input - }: CurrencyInput & { id?: string; hash?: string; meta?: TMeta }): CurrencyDefinition { + }: MixedCurrencyType): CurrencyDefinition { if ('address' in input) { if (input.address.startsWith('0x') && input.address.length === 42) { input.address = utils.getAddress(input.address); @@ -217,25 +239,41 @@ export class CurrencyManager implements ICurrencyManager 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; } /** * Utility function to compute the unique identifier */ - static currencyId(currency: { symbol: string; network?: string }): string { - return 'network' in currency ? `${currency.symbol}-${currency.network}` : currency.symbol; + static currencyId(currency: { symbol: string; network?: string | ChainTypes.IChain }): string { + return currency.network + ? `${currency.symbol}-${ChainManager.getName(currency.network)}` + : currency.symbol; } /** * 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, }; } @@ -243,25 +281,28 @@ export class CurrencyManager implements ICurrencyManager * 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 (NearChains.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`); @@ -276,8 +317,9 @@ export class CurrencyManager implements ICurrencyManager currency.type === RequestLogicTypes.CURRENCY.ISO4217 || currency.type === RequestLogicTypes.CURRENCY.ETH || currency.type === RequestLogicTypes.CURRENCY.BTC - ) + ) { return true; + } return this.validateAddress(currency.value, currency); } @@ -292,27 +334,11 @@ export class CurrencyManager implements ICurrencyManager * - ETH-rinkeby, FAU-rinkeby, CTBK-rinkeby */ static getDefaultList(): CurrencyDefinition[] { - const isoCurrencies: CurrencyInput[] = iso4217.map((cc) => ({ - decimals: cc.digits, - name: cc.currency, - symbol: cc.code, - type: ISO4217, - })); - - const eth: CurrencyInput[] = nativeCurrencies.ETH.map((x) => ({ ...x, type: ETH })); - const btc: CurrencyInput[] = nativeCurrencies.BTC.map((x) => ({ ...x, type: BTC })); - - const erc20Tokens = getSupportedERC20Tokens(); - const erc20Currencies: CurrencyInput[] = erc20Tokens.map((x) => ({ ...x, type: ERC20 })); - - const erc777Tokens = getSupportedERC777Tokens(); - const erc777Currencies: CurrencyInput[] = erc777Tokens.map((x) => ({ ...x, type: ERC777 })); - - return isoCurrencies - .concat(erc20Currencies) - .concat(erc777Currencies) - .concat(eth) - .concat(btc) + return ([] as CurrencyInput[]) + .concat(getSupportedIso4217Currencies()) + .concat(getSupportedERC20Currencies()) + .concat(getSupportedERC777Currencies()) + .concat(getSupportedNativeCurrencies()) .map(CurrencyManager.fromInput); } @@ -335,7 +361,9 @@ export class CurrencyManager implements ICurrencyManager * 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(), diff --git a/packages/currency/src/erc20/chains/index.ts b/packages/currency/src/erc20/chains/index.ts index fa2dad08d9..d09b8a375b 100644 --- a/packages/currency/src/erc20/chains/index.ts +++ b/packages/currency/src/erc20/chains/index.ts @@ -1,5 +1,4 @@ import { TokenMap } from '../../types'; -import { CurrencyTypes } from '@requestnetwork/types'; import { supportedAvalancheERC20 } from './avalanche'; import { supportedBSCERC20 } from './bsc'; @@ -15,7 +14,7 @@ import { supportedRinkebyERC20 } from './rinkeby'; import { supportedXDAIERC20 } from './xdai'; import { supportedSepoliaERC20 } from './sepolia'; -export const supportedNetworks: Partial> = { +export const supportedNetworks: Record = { celo: supportedCeloERC20, // FIXME: Rinkeby is deprecated rinkeby: supportedRinkebyERC20, diff --git a/packages/currency/src/erc20/index.ts b/packages/currency/src/erc20/index.ts index b0f4f6b316..fedb95101c 100644 --- a/packages/currency/src/erc20/index.ts +++ b/packages/currency/src/erc20/index.ts @@ -1,24 +1,28 @@ -import { ERC20Currency, TokenMap } from '../types'; +import { ERC20CurrencyInput, TokenMap } from '../types'; import { supportedNetworks } from './chains'; -import { CurrencyTypes } from '@requestnetwork/types'; +import { RequestLogicTypes } from '@requestnetwork/types'; /** * Returns a list of supported ERC20 tokens * * @returns List of supported ERC20 tokens */ -export function getSupportedERC20Tokens(): ERC20Currency[] { - return (Object.entries(supportedNetworks) as [CurrencyTypes.EvmChainName, TokenMap][]).reduce( - (acc: ERC20Currency[], [networkName, supportedCurrencies]) => { +export function getSupportedERC20Currencies(): ERC20CurrencyInput[] { + return (Object.entries(supportedNetworks) as [string, TokenMap][]).reduce( + (acc: ERC20CurrencyInput[], [networkName, supportedCurrencies]) => { return [ ...acc, - ...Object.entries(supportedCurrencies).map(([address, token]) => ({ - address, - network: networkName, - decimals: token.decimals, - symbol: token.symbol, - id: token.id, - })), + ...Object.entries(supportedCurrencies).map( + ([address, token]) => + ({ + type: RequestLogicTypes.CURRENCY.ERC20, + address, + network: networkName, + decimals: token.decimals, + symbol: token.symbol, + id: token.id, + }) as const, + ), ]; }, [], diff --git a/packages/currency/src/erc777/chains/index.ts b/packages/currency/src/erc777/chains/index.ts index 05d09408bb..e76ffff928 100644 --- a/packages/currency/src/erc777/chains/index.ts +++ b/packages/currency/src/erc777/chains/index.ts @@ -1,7 +1,6 @@ import { supportedRinkebyERC777 } from './rinkeby'; import { TokenMap } from '../../types'; -import { CurrencyTypes } from '@requestnetwork/types'; -export const supportedNetworks: Partial> = { +export const supportedNetworks: Record = { rinkeby: supportedRinkebyERC777, }; diff --git a/packages/currency/src/erc777/index.ts b/packages/currency/src/erc777/index.ts index 36e2f5f53a..c0c8c73d44 100644 --- a/packages/currency/src/erc777/index.ts +++ b/packages/currency/src/erc777/index.ts @@ -1,23 +1,27 @@ -import { ERC777Currency, TokenMap } from '../types'; +import { ERC777CurrencyInput, TokenMap } from '../types'; import { supportedNetworks } from './chains'; -import { CurrencyTypes } from '@requestnetwork/types'; +import { RequestLogicTypes } from '@requestnetwork/types'; /** * Returns a list of supported ERC777 tokens * * @returns List of supported ERC777 tokens */ -export function getSupportedERC777Tokens(): ERC777Currency[] { - return (Object.entries(supportedNetworks) as [CurrencyTypes.EvmChainName, TokenMap][]).reduce( - (acc: ERC777Currency[], [networkName, supportedCurrencies]) => { +export function getSupportedERC777Currencies(): ERC777CurrencyInput[] { + return (Object.entries(supportedNetworks) as [string, TokenMap][]).reduce( + (acc: ERC777CurrencyInput[], [networkName, supportedCurrencies]) => { return [ ...acc, - ...Object.entries(supportedCurrencies).map(([address, token]) => ({ - address, - network: networkName, - decimals: token.decimals, - symbol: token.symbol, - })), + ...Object.entries(supportedCurrencies).map( + ([address, token]) => + ({ + type: RequestLogicTypes.CURRENCY.ERC777, + address, + network: networkName, + decimals: token.decimals, + symbol: token.symbol, + }) as const, + ), ]; }, [], diff --git a/packages/currency/src/index.ts b/packages/currency/src/index.ts index e3f77834d2..eaf3bfab42 100644 --- a/packages/currency/src/index.ts +++ b/packages/currency/src/index.ts @@ -1,13 +1,12 @@ -export * from './chains'; -export { getSupportedERC20Tokens } from './erc20'; -export { getSupportedERC777Tokens } from './erc777'; +export { getSupportedERC20Currencies } from './erc20'; +export { getSupportedERC777Currencies } from './erc777'; export { conversionSupportedNetworks, CurrencyPairs, AggregatorsMap, } from './conversion-aggregators'; export { getHash as getCurrencyHash } from './getHash'; -export { CurrencyManager } from './currencyManager'; +export { CurrencyManager } from './currency-manager'; export * from './types'; export * from './errors'; export * from './currency-utils'; diff --git a/packages/currency/src/iso4217.ts b/packages/currency/src/iso4217.ts index 4a648f8717..b3eea08fc0 100644 --- a/packages/currency/src/iso4217.ts +++ b/packages/currency/src/iso4217.ts @@ -4,7 +4,10 @@ Data last updated 2018-08-29 */ -export default [ +import { ISO4217CurrencyInput } from './types'; +import { RequestLogicTypes } from '@requestnetwork/types'; + +const iso4217Currencies = [ { code: 'AED', number: '784', @@ -1354,3 +1357,19 @@ export default [ countries: ['Zimbabwe'], }, ]; + +export default iso4217Currencies; + +/** + * Returns a list of supported ISO-4217 currencies + * + * @returns List of supported ISO-4217 currencies + */ +export function getSupportedIso4217Currencies(): ISO4217CurrencyInput[] { + return iso4217Currencies.map((cc) => ({ + type: RequestLogicTypes.CURRENCY.ISO4217, + decimals: cc.digits, + name: cc.currency, + symbol: cc.code, + })); +} diff --git a/packages/currency/src/native.ts b/packages/currency/src/native.ts index 70c99ee419..a9fc9cfc99 100644 --- a/packages/currency/src/native.ts +++ b/packages/currency/src/native.ts @@ -1,183 +1,175 @@ -import { CurrencyTypes, RequestLogicTypes } from '@requestnetwork/types'; -import { NamedNativeCurrency } from './types'; +import { RequestLogicTypes } from '@requestnetwork/types'; +import { NativeCurrencyInput, TokenMap } from './types'; -type NativeEthCurrency = NamedNativeCurrency & { - network: CurrencyTypes.EvmChainName | CurrencyTypes.NearChainName; -}; -type NativeBtcCurrency = NamedNativeCurrency & { network: CurrencyTypes.BtcChainName }; - -export const nativeCurrencies: Record & - Record = { - [RequestLogicTypes.CURRENCY.ETH]: [ - { +export const nativeCurrencies: Record< + RequestLogicTypes.CURRENCY.BTC | RequestLogicTypes.CURRENCY.ETH, + TokenMap +> = { + [RequestLogicTypes.CURRENCY.ETH]: { + private: { symbol: 'ETH-private', decimals: 18, name: 'Ether', - network: 'private', }, - { + mainnet: { symbol: 'ETH', decimals: 18, name: 'Ether', - network: 'mainnet', }, - { + rinkeby: { symbol: 'ETH-rinkeby', decimals: 18, name: 'Rinkeby Ether', - network: 'rinkeby', }, - { + goerli: { symbol: 'ETH-goerli', decimals: 18, name: 'Goerli Ether', - network: 'goerli', }, - { + matic: { symbol: 'MATIC', decimals: 18, name: 'Matic', - network: 'matic', }, - { + xdai: { symbol: 'xDAI', decimals: 18, name: 'xDAI', - network: 'xdai', }, - { + sokol: { symbol: 'POA', decimals: 18, name: 'POA Sokol Ether', - network: 'sokol', }, - { + fuse: { symbol: 'FUSE', decimals: 18, name: 'FUSE', - network: 'fuse', }, - { + celo: { symbol: 'CELO', decimals: 18, name: 'CELO', - network: 'celo', }, - { + fantom: { symbol: 'FTM', decimals: 18, name: 'Fantom', - network: 'fantom', }, - { + bsc: { symbol: 'BNB', decimals: 18, name: 'BNB', - network: 'bsc', }, - { + aurora: { symbol: 'NEAR', decimals: 24, name: 'Near', - network: 'aurora', }, - { + 'aurora-testnet': { symbol: 'NEAR-testnet', decimals: 24, name: 'Near Testnet', - network: 'aurora-testnet', }, - { + 'near-testnet': { symbol: 'NEAR-testnet', decimals: 24, name: 'Test Near', - network: 'near-testnet', }, - { + 'arbitrum-rinkeby': { symbol: 'ARETH', decimals: 18, name: 'Arbitrum Testnet', - network: 'arbitrum-rinkeby', }, - { + 'arbitrum-one': { symbol: 'AETH', decimals: 18, name: 'Arbitrum Ether', - network: 'arbitrum-one', }, - { + avalanche: { symbol: 'AVAX', decimals: 18, name: 'AVAX', - network: 'avalanche', }, - { + optimism: { symbol: 'ETH-optimism', decimals: 18, name: 'Optimism Ether', - network: 'optimism', }, - { + moonbeam: { symbol: 'GLMR', decimals: 18, name: 'Glimmer', - network: 'moonbeam', }, - { + tombchain: { symbol: 'TOMB', decimals: 18, name: 'Tomb', - network: 'tombchain', }, - { + mantle: { symbol: 'MNT', decimals: 18, name: 'Mantle', - network: 'mantle', }, - { + 'mantle-testnet': { symbol: 'MNT-testnet', decimals: 18, name: 'Mantle Testnet', - network: 'mantle-testnet', }, - { + core: { symbol: 'CORE', decimals: 18, name: 'Core', - network: 'core', }, - { + sepolia: { symbol: 'ETH-sepolia', decimals: 18, name: 'Sepolia Ether', - network: 'sepolia', }, - { + zksyncera: { symbol: 'ETH-zksync', decimals: 18, name: 'Ether', - network: 'zksyncera', }, - { + zksynceratestnet: { symbol: 'ETH-zksync-testnet', decimals: 18, name: 'Ether', - network: 'zksynceratestnet', }, - ], - [RequestLogicTypes.CURRENCY.BTC]: [ - { + }, + [RequestLogicTypes.CURRENCY.BTC]: { + mainnet: { symbol: 'BTC', decimals: 8, name: 'Bitcoin', - network: 'mainnet', }, - { + testnet: { symbol: 'BTC-testnet', decimals: 8, name: 'Test Bitcoin', - network: 'testnet', }, - ], + }, }; + +/** + * Returns a list of supported native tokens + * + * @returns List of supported native tokens + */ +export function getSupportedNativeCurrencies(): NativeCurrencyInput[] { + return Object.entries(nativeCurrencies).reduce( + (acc: NativeCurrencyInput[], [CurrencyType, supportedCurrencies]) => + acc.concat( + Object.entries(supportedCurrencies).map( + ([networkName, token]) => + ({ + type: CurrencyType, + network: networkName, + decimals: token.decimals, + symbol: token.symbol, + }) as NativeCurrencyInput, + ), + ), + [], + ); +} diff --git a/packages/currency/src/types.ts b/packages/currency/src/types.ts index c976b3871b..cdc3f47401 100644 --- a/packages/currency/src/types.ts +++ b/packages/currency/src/types.ts @@ -1,4 +1,4 @@ -import { CurrencyTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ChainTypes, RequestLogicTypes } from '@requestnetwork/types'; /** * Common types used in token configuration files @@ -7,33 +7,24 @@ type TokenAddress = string; type TokenDefinition = { name: string; symbol: string; decimals: number; id?: string }; export type TokenMap = Record; -/** - * Common types used in chain configuration files - */ -export type Chain = { - chainId: number | string; - testnet?: boolean; - currencies?: TokenMap; -}; +/** 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: CurrencyTypes.ChainName; + network: ChainTypes.IChain; }; -type NamedCurrency = { name: string }; -export type NamedNativeCurrency = NativeCurrency & NamedCurrency; - -/** Native Currency types */ -export type NativeCurrencyType = RequestLogicTypes.CURRENCY.BTC | RequestLogicTypes.CURRENCY.ETH; /** * A Fiat currency (EUR, USD...) */ export type ISO4217Currency = { + type: RequestLogicTypes.CURRENCY.ISO4217; symbol: string; decimals: number; }; @@ -42,12 +33,10 @@ export type ISO4217Currency = { * An ERC20 token (DAI, USDT...) */ export type ERC20Currency = { + type: RequestLogicTypes.CURRENCY.ERC20; symbol: string; decimals: number; - network: - | CurrencyTypes.EvmChainName - | CurrencyTypes.NearChainName - | CurrencyTypes.DeclarativeChainName; + network: ChainTypes.IEvmChain | ChainTypes.INearChain | ChainTypes.IDeclarativeChain; address: string; }; @@ -55,39 +44,38 @@ export type ERC20Currency = { * An ERC777 SuperToken (DAIx, USDCx...) */ export type ERC777Currency = { + type: RequestLogicTypes.CURRENCY.ERC777; symbol: string; decimals: number; - network: CurrencyTypes.EvmChainName; + network: ChainTypes.IEvmChain; address: string; }; /** * The minimum properties of a native Currency */ -export type NativeCurrencyInput = { - type: RequestLogicTypes.CURRENCY.ETH | RequestLogicTypes.CURRENCY.BTC; -} & NativeCurrency; +export type NativeCurrencyInput = Omit & { + network: string; +}; /** * The minimum properties of an ISO4217 Currency */ -export type ISO4217CurrencyInput = { - type: RequestLogicTypes.CURRENCY.ISO4217; -} & ISO4217Currency; +export type ISO4217CurrencyInput = ISO4217Currency; /** * The minimum properties of an ERC20 Currency */ -export type ERC20CurrencyInput = { - type: RequestLogicTypes.CURRENCY.ERC20; -} & ERC20Currency; +export type ERC20CurrencyInput = Omit & { + network: string; +}; /** * The minimum properties of an ERC777 Currency */ -export type ERC777CurrencyInput = { - type: RequestLogicTypes.CURRENCY.ERC777; -} & ERC777Currency; +export type ERC777CurrencyInput = Omit & { + network: string; +}; /** * The minimum properties of a Currency @@ -98,16 +86,28 @@ 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 = CurrencyInput & { +export type CurrencyDefinition = Currency & { id: string; hash: string; meta: TMeta; }; +/** + * Allowed inputs to instantiate the CurrencyManager + */ +export type MixedCurrencyType = + | (CurrencyInput & Partial<{ id?: string; hash?: string; meta?: TMeta }>) + | CurrencyDefinition; + /** * Alias for ICurrency for clarity in the context */ @@ -117,28 +117,44 @@ export type StorageCurrency = RequestLogicTypes.ICurrency; * A Currency manager handles a list of currencies and provides utility to retrieve and change format */ export interface ICurrencyManager { - from(symbolOrAddress: string, network?: string): CurrencyDefinition | undefined; - fromAddress(address: string, network?: string): CurrencyDefinition | undefined; - fromSymbol(symbol: string, network?: string): CurrencyDefinition | undefined; - fromHash(hash: string, network?: string): CurrencyDefinition | undefined; + chainManager: ChainTypes.IChainManager; + from( + symbolOrAddress: string, + network?: string | ChainTypes.IChain, + ): CurrencyDefinition | undefined; + fromAddress( + address: string, + network?: string | ChainTypes.IChain, + ): CurrencyDefinition | undefined; + fromSymbol( + symbol: string, + network?: string | ChainTypes.IChain, + ): CurrencyDefinition | undefined; + fromHash( + hash: string, + network?: string | ChainTypes.IChain, + ): CurrencyDefinition | undefined; fromStorageCurrency(currency: StorageCurrency): CurrencyDefinition | undefined; getNativeCurrency( type: NativeCurrencyType, - network: string, + network: string | ChainTypes.IChain, ): CurrencyDefinition | undefined; getConversionPath( from: Pick, to: Pick, - network: string, + network: string | ChainTypes.IChain, ): string[] | null; - supportsConversion(currency: Pick, network: string): boolean; - validateAddress(address: string, currency: CurrencyInput | StorageCurrency): boolean; + supportsConversion( + currency: Pick, + network: string | ChainTypes.IChain, + ): 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>; +export type LegacyTokenMap = Record>; diff --git a/packages/currency/test/chain-utils.test.ts b/packages/currency/test/chain-utils.test.ts deleted file mode 100644 index aa08e06209..0000000000 --- a/packages/currency/test/chain-utils.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { EvmChains, NearChains, isSameChain } from '../src/index'; - -describe('isSameChain', () => { - it('Should return true for 2 identical EVMs', () => { - expect(isSameChain('arbitrum-one', 'arbitrum-one')).toBe(true); - }); - it('Should return false for 2 different EVMs', () => { - expect(isSameChain('mainnet', 'arbitrum-one')).toBe(false); - }); - // FIXME: get rid of all aurora alias and mentions - it('Should return true for 2 identical NEAR', () => { - expect(isSameChain('aurora-testnet', 'near-testnet')).toBe(true); - }); - it('Should return false for 2 different chains on 2 different ecosystems', () => { - expect(isSameChain('aurora-testnet', 'arbitrum-one')).toBe(false); - }); -}); - -describe('isChainSupported', () => { - describe('NearChains', () => { - it('returns true for near', () => { - expect(NearChains.isChainSupported('near')).toEqual(true); - }); - it('returns true for aurora', () => { - expect(NearChains.isChainSupported('aurora')).toEqual(true); - }); - it('returns false for mainnet', () => { - expect(NearChains.isChainSupported('mainnet')).toEqual(false); - }); - }); - describe('EvmChains', () => { - it('returns true for mainnet', () => { - expect(EvmChains.isChainSupported('mainnet')).toEqual(true); - }); - it('returns false for near', () => { - expect(EvmChains.isChainSupported('near')).toEqual(false); - }); - }); -}); diff --git a/packages/currency/test/conversion-supported-currencies.test.ts b/packages/currency/test/conversion-supported-currencies.test.ts index 04f6ed14e0..b8bde61383 100644 --- a/packages/currency/test/conversion-supported-currencies.test.ts +++ b/packages/currency/test/conversion-supported-currencies.test.ts @@ -1,5 +1,5 @@ import { CurrencyManager } from '../src'; -import { CurrencyTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { RequestLogicTypes } from '@requestnetwork/types'; const currencyManager = new CurrencyManager([ ...CurrencyManager.getDefaultList(), @@ -14,14 +14,12 @@ const currencyManager = new CurrencyManager([ describe('supported currencies with oracles from chainlink', () => { describe('fiat currencies', () => { - ( - Object.entries({ - mainnet: ['AUD', 'CAD', 'CHF', 'EUR', 'GBP', 'SGD', 'USD'], - private: ['EUR', 'USD'], - matic: ['AUD', 'CAD', 'CHF', 'EUR', 'GBP', 'SGD', 'USD'], - fantom: ['USD', 'CHF'], - }) as [CurrencyTypes.EvmChainName, string[]][] - ).forEach(([network, symbols]) => { + Object.entries({ + mainnet: ['AUD', 'CAD', 'CHF', 'EUR', 'GBP', 'SGD', 'USD'], + private: ['EUR', 'USD'], + matic: ['AUD', 'CAD', 'CHF', 'EUR', 'GBP', 'SGD', 'USD'], + fantom: ['USD', 'CHF'], + }).forEach(([network, symbols]) => { describe(network, () => { symbols.forEach((symbol) => { it(symbol, () => { @@ -35,12 +33,10 @@ describe('supported currencies with oracles from chainlink', () => { }); describe('native currencies', () => { - ( - Object.entries({ - mainnet: ['ETH'], - fantom: ['FTM'], - }) as [CurrencyTypes.EvmChainName, string[]][] - ).forEach(([network, symbols]) => { + Object.entries({ + mainnet: ['ETH'], + fantom: ['FTM'], + }).forEach(([network, symbols]) => { describe(network, () => { symbols.forEach((symbol) => { it(symbol, () => { @@ -54,31 +50,29 @@ describe('supported currencies with oracles from chainlink', () => { }); describe('ERC20 tokens', () => { - ( - Object.entries({ - mainnet: [ - '0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c', - '0x3845badade8e6dff049820680d1f14bd3903a5d0', - '0x4e15361fd6b4bb609fa63c81a2be19d873717870', - '0x6b175474e89094c44da98b954eedeac495271d0f', - '0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9', - '0x8290333cef9e6d528dd5618fb97a76f268f3edd4', - '0x8ab7404063ec4dbcfd4598215992dc3f8ec853d7', - '0x967da4048cd07ab37855c090aaf366e4ce1b9f48', - '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', - '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - '0xa117000000f279d81a1d3cc75430faa017fa5a2e', - '0xc944e90c64b2c07662a292be6244bdf05cda44a7', - '0xdac17f958d2ee523a2206206994597c13d831ec7', - ], - matic: [ - '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', - '0x8f3cf7ad23cd3cadbd9735aff958023239c6a063', - '0xc2132d05d31c914a87c6611c10748aeb04b58e8f', - ], - private: ['0x38cf23c52bb4b13f051aec09580a2de845a7fa35'], - }) as [CurrencyTypes.EvmChainName, string[]][] - ).forEach(([network, addresses]) => { + Object.entries({ + mainnet: [ + '0x1f573d6fb3f13d689ff844b4ce37794d79a7ff1c', + '0x3845badade8e6dff049820680d1f14bd3903a5d0', + '0x4e15361fd6b4bb609fa63c81a2be19d873717870', + '0x6b175474e89094c44da98b954eedeac495271d0f', + '0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9', + '0x8290333cef9e6d528dd5618fb97a76f268f3edd4', + '0x8ab7404063ec4dbcfd4598215992dc3f8ec853d7', + '0x967da4048cd07ab37855c090aaf366e4ce1b9f48', + '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', + '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + '0xa117000000f279d81a1d3cc75430faa017fa5a2e', + '0xc944e90c64b2c07662a292be6244bdf05cda44a7', + '0xdac17f958d2ee523a2206206994597c13d831ec7', + ], + matic: [ + '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', + '0x8f3cf7ad23cd3cadbd9735aff958023239c6a063', + '0xc2132d05d31c914a87c6611c10748aeb04b58e8f', + ], + private: ['0x38cf23c52bb4b13f051aec09580a2de845a7fa35'], + }).forEach(([network, addresses]) => { describe(network, () => { addresses.forEach((address) => { const currency = currencyManager.fromAddress(address, network)!; diff --git a/packages/currency/test/currency/erc20.test.ts b/packages/currency/test/currency/erc20.test.ts index 5e39700650..e59e049391 100644 --- a/packages/currency/test/currency/erc20.test.ts +++ b/packages/currency/test/currency/erc20.test.ts @@ -1,4 +1,4 @@ -import { getSupportedERC20Tokens } from '../../src/erc20'; +import { getSupportedERC20Currencies } from '../../src/erc20'; import * as metamaskContractMap from '@metamask/contract-metadata'; import { extraERC20Tokens } from '../../src/erc20/chains/mainnet'; import { utils } from 'ethers'; @@ -12,7 +12,7 @@ describe('erc20', () => { }); }); describe('uses checksumed addresses', () => { - getSupportedERC20Tokens().map(({ address, symbol }) => { + getSupportedERC20Currencies().map(({ address, symbol }) => { it(`${symbol} is checksumed`, () => { expect(address).toEqual(utils.getAddress(address)); }); diff --git a/packages/currency/test/currency/erc777.test.ts b/packages/currency/test/currency/erc777.test.ts index 1b35680c6b..6142d87778 100644 --- a/packages/currency/test/currency/erc777.test.ts +++ b/packages/currency/test/currency/erc777.test.ts @@ -1,9 +1,9 @@ -import { getSupportedERC777Tokens } from '../../src/erc777'; +import { getSupportedERC777Currencies } from '../../src/erc777'; import { utils } from 'ethers'; describe('erc777', () => { describe('uses checksumed addresses', () => { - getSupportedERC777Tokens().map(({ address, symbol }) => { + getSupportedERC777Currencies().map(({ address, symbol }) => { it(`${symbol} is checksumed`, () => { expect(address).toEqual(utils.getAddress(address)); }); diff --git a/packages/currency/test/currencyManager.test.ts b/packages/currency/test/currencyManager.test.ts index 9a20b14103..68da9aa5ec 100644 --- a/packages/currency/test/currencyManager.test.ts +++ b/packages/currency/test/currencyManager.test.ts @@ -1,4 +1,4 @@ -import { RequestLogicTypes } from '@requestnetwork/types'; +import { ChainTypes, RequestLogicTypes } from '@requestnetwork/types'; import { CurrencyInput, CurrencyDefinition, @@ -6,8 +6,9 @@ import { ERC20Currency, StorageCurrency, } from '../src'; +import { ChainManager } from '@requestnetwork/chain'; -const testCasesPerNetwork: Record>> = { +const testCasesPerNetwork: Record>> = { mainnet: { ETH: { symbol: 'ETH', network: 'mainnet' }, SAI: { @@ -90,7 +91,7 @@ const testCasesPerNetwork: Record { it('fails if there is a duplicate token in the list', () => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const dai = CurrencyManager.getDefaultList().find((x) => x.id === 'DAI-mainnet')!; - const list: CurrencyInput[] = [dai, dai, dai, dai]; + const list: CurrencyDefinition[] = [dai, dai, dai, dai]; expect(() => new CurrencyManager(list)).toThrowError('Duplicate found: DAI-mainnet'); }); @@ -159,78 +160,78 @@ describe('CurrencyManager', () => { it('access a common token by its symbol', () => { expect(currencyManager.from('DAI')).toMatchObject({ symbol: 'DAI', - network: 'mainnet', + network: expect.objectContaining({ name: 'mainnet' }), }); expect(currencyManager.fromSymbol('DAI')).toBeDefined(); }); it('access a chain-specific token by its symbol', () => { expect(currencyManager.from('CELO')).toMatchObject({ - network: 'celo', + network: expect.objectContaining({ name: 'celo' }), }); expect(currencyManager.fromSymbol('CELO')).toMatchObject({ - network: 'celo', + network: expect.objectContaining({ name: 'celo' }), }); }); it('access a multichain token by its symbol and network', () => { expect(currencyManager.from('DAI-matic')).toMatchObject({ symbol: 'DAI', - network: 'matic', + network: expect.objectContaining({ name: 'matic' }), }); expect(currencyManager.fromSymbol('DAI-matic')).toBeUndefined(); expect(currencyManager.from('DAI', 'matic')).toMatchObject({ symbol: 'DAI', - network: 'matic', + network: expect.objectContaining({ name: 'matic' }), }); expect(currencyManager.fromSymbol('DAI', 'matic')).toMatchObject({ symbol: 'DAI', - network: 'matic', + network: expect.objectContaining({ name: 'matic' }), }); }); it('access a currency by its id', () => { expect(currencyManager.from('ETH-rinkeby-rinkeby')).toMatchObject({ symbol: 'ETH-rinkeby', - network: 'rinkeby', + network: expect.objectContaining({ name: 'rinkeby' }), }); }); it('access a mainnet token by its address with or without network', () => { expect(currencyManager.from('0x6B175474E89094C44Da98b954EedeAC495271d0F')).toMatchObject({ symbol: 'DAI', - network: 'mainnet', + network: expect.objectContaining({ name: 'mainnet' }), }); expect( currencyManager.from('0x6B175474E89094C44Da98b954EedeAC495271d0F', 'mainnet'), ).toMatchObject({ symbol: 'DAI', - network: 'mainnet', + network: expect.objectContaining({ name: 'mainnet' }), }); expect( currencyManager.fromAddress('0x6B175474E89094C44Da98b954EedeAC495271d0F'), ).toMatchObject({ symbol: 'DAI', - network: 'mainnet', + network: expect.objectContaining({ name: 'mainnet' }), }); expect( currencyManager.fromAddress('0x6B175474E89094C44Da98b954EedeAC495271d0F', 'mainnet'), ).toMatchObject({ symbol: 'DAI', - network: 'mainnet', + network: expect.objectContaining({ name: 'mainnet' }), }); }); it('access a mainnet token by its address whatever the case', () => { expect(currencyManager.from('0x6b175474e89094c44da98b954eedeac495271d0f')).toMatchObject({ symbol: 'DAI', - network: 'mainnet', + network: expect.objectContaining({ name: 'mainnet' }), }); expect(currencyManager.from('0x6B175474E89094C44DA98B954EEDEAC495271D0F')).toMatchObject({ symbol: 'DAI', - network: 'mainnet', + network: expect.objectContaining({ name: 'mainnet' }), }); }); @@ -239,38 +240,38 @@ describe('CurrencyManager', () => { currencyManager.from('0xD3b71117E6C1558c1553305b44988cd944e97300', 'matic'), ).toMatchObject({ symbol: 'YEL', - network: 'matic', + network: expect.objectContaining({ name: 'matic' }), }); expect( currencyManager.from('0xD3b71117E6C1558c1553305b44988cd944e97300', 'fantom'), ).toMatchObject({ symbol: 'YEL', - network: 'fantom', + network: expect.objectContaining({ name: 'fantom' }), }); expect( currencyManager.fromAddress('0xD3b71117E6C1558c1553305b44988cd944e97300', 'matic'), ).toMatchObject({ symbol: 'YEL', - network: 'matic', + network: expect.objectContaining({ name: 'matic' }), }); expect( currencyManager.fromAddress('0xD3b71117E6C1558c1553305b44988cd944e97300', 'fantom'), ).toMatchObject({ symbol: 'YEL', - network: 'fantom', + network: expect.objectContaining({ name: 'fantom' }), }); }); it('defaults to mainnet for native tokens with a mainnet equivalent', () => { expect(currencyManager.from('MATIC')).toMatchObject({ symbol: 'MATIC', - network: 'mainnet', + network: expect.objectContaining({ name: 'mainnet' }), type: RequestLogicTypes.CURRENCY.ERC20, }); expect(currencyManager.from('MATIC-matic')).toMatchObject({ symbol: 'MATIC', - network: 'matic', + network: expect.objectContaining({ name: 'matic' }), type: RequestLogicTypes.CURRENCY.ETH, }); }); @@ -405,7 +406,13 @@ describe('CurrencyManager', () => { describe(network, () => { Object.entries(testCases).forEach(([symbol, expected]) => { it(`Resolves ${symbol}`, () => { - expect(currencyManager.from(symbol)).toMatchObject(expected); + expect(currencyManager.from(symbol)).toMatchObject({ + ...expected, + network: + 'network' in expected + ? expect.objectContaining({ name: expected.network }) + : undefined, + }); }); }); }); @@ -493,10 +500,7 @@ describe('CurrencyManager', () => { '0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb', ]; - const extendedTestCasesPerNetwork: Record< - string, - Record> - > = { + const extendedTestCasesPerNetwork: Record>> = { ...testCasesPerNetwork, aurora: { NEAR: { @@ -548,7 +552,10 @@ describe('CurrencyManager', () => { switch (currency.symbol) { case 'NEAR': case 'NEAR-testnet': - testValidateAddressForCurrency(nearAddresses[currency.network], currency); + testValidateAddressForCurrency( + nearAddresses[currency.network.name], + currency, + ); break; default: eip55Addresses.forEach((address) => @@ -557,7 +564,7 @@ describe('CurrencyManager', () => { } break; case RequestLogicTypes.CURRENCY.BTC: - testValidateAddressForCurrency(bitcoinAddresses[currency.network], currency); + testValidateAddressForCurrency(bitcoinAddresses[currency.network.name], currency); break; default: throw new Error(`Could not generate a valid address for an unknown type`); @@ -743,7 +750,7 @@ describe('CurrencyManager', () => { it('Detects USDCe matic by USDC-matic id', () => { expect(currencyManager.from('USDC-matic')).toMatchObject({ type: RequestLogicTypes.CURRENCY.ERC20, - network: 'matic', + network: expect.objectContaining({ name: 'matic' }), symbol: 'USDCe', id: 'USDC-matic', address: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', @@ -752,7 +759,7 @@ describe('CurrencyManager', () => { it('Detects native USDC matic by USDCn-matic id', () => { expect(currencyManager.from('USDCn-matic')).toMatchObject({ type: RequestLogicTypes.CURRENCY.ERC20, - network: 'matic', + network: expect.objectContaining({ name: 'matic' }), symbol: 'USDC', id: 'USDCn-matic', address: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', diff --git a/packages/ethereum-storage/src/config.ts b/packages/ethereum-storage/src/config.ts index 81dc1b006d..e597909e0a 100644 --- a/packages/ethereum-storage/src/config.ts +++ b/packages/ethereum-storage/src/config.ts @@ -1,4 +1,4 @@ -import { CurrencyTypes, StorageTypes } from '@requestnetwork/types'; +import { StorageTypes } from '@requestnetwork/types'; import { BigNumber } from 'ethers'; // This contains default values used to use Ethereum Network and IPFS @@ -63,7 +63,7 @@ export function getDefaultEthereumProviderTimeout(): number { * Retrieve from config the default name of the network for Ethereum * @returns the name of the network */ -export function getDefaultEthereumNetwork(): CurrencyTypes.EvmChainName { +export function getDefaultEthereumNetwork(): string { return config.ethereum.default; } diff --git a/packages/ethereum-storage/src/ethereum-tx-submitter.ts b/packages/ethereum-storage/src/ethereum-tx-submitter.ts index 3fdacbc702..b6b4f7c30a 100644 --- a/packages/ethereum-storage/src/ethereum-tx-submitter.ts +++ b/packages/ethereum-storage/src/ethereum-tx-submitter.ts @@ -1,5 +1,5 @@ import { BigNumber, ContractTransaction, providers, Signer, utils } from 'ethers'; -import { CurrencyTypes, LogTypes, StorageTypes } from '@requestnetwork/types'; +import { LogTypes, StorageTypes } from '@requestnetwork/types'; import { requestHashSubmitterArtifact } from '@requestnetwork/smart-contracts'; import { RequestOpenHashSubmitter } from '@requestnetwork/smart-contracts/types'; import { GasFeeDefiner } from './gas-fee-definer'; @@ -22,7 +22,7 @@ export type SubmitterProps = { * The default is 100, which does not change the value (100 is equal to x1, 200 is equal to x2). */ gasPriceMultiplier?: number; - network: CurrencyTypes.EvmChainName; + network: string; logger?: LogTypes.ILogger; debugProvider?: boolean; }; diff --git a/packages/integration-test/test/node-client.test.ts b/packages/integration-test/test/node-client.test.ts index 03f41e1fbf..d290c75b62 100644 --- a/packages/integration-test/test/node-client.test.ts +++ b/packages/integration-test/test/node-client.test.ts @@ -7,12 +7,18 @@ import { PaymentTypes, RequestLogicTypes, ExtensionTypes, + ChainTypes, } from '@requestnetwork/types'; import { payRequest, approveErc20ForProxyConversionIfNeeded, } from '@requestnetwork/payment-processor'; -import { CurrencyInput, CurrencyManager } from '@requestnetwork/currency'; +import { + Currency, + CurrencyDefinition, + CurrencyInput, + CurrencyManager, +} from '@requestnetwork/currency'; import { Wallet, providers, BigNumber } from 'ethers'; import { @@ -24,6 +30,8 @@ import { signatureProvider, } from './scheduled/fixtures'; import { getCurrentTimestampInSecond, normalizeKeccak256Hash } from '@requestnetwork/utils'; +import { ChainManager } from '@requestnetwork/chain/src'; +import { MixedCurrencyType } from '@requestnetwork/currency/src'; const mnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat'; const provider = new providers.JsonRpcProvider('http://localhost:8545'); @@ -561,7 +569,7 @@ describe('ERC20 localhost request creation and detection test', () => { it('can create ERC20 requests with any to erc20 proxy', async () => { const tokenContractAddress = '0x38cF23C52Bb4B13F051Aec09580a2dE845a7FA35'; - const currencies: CurrencyInput[] = [ + const currencies: MixedCurrencyType[] = [ ...CurrencyManager.getDefaultList(), { address: tokenContractAddress, @@ -702,7 +710,7 @@ describe('ETH localhost request creation and detection test', () => { }); it('can create & pay a request with any to eth proxy', async () => { - const currencies: CurrencyInput[] = [ + const currencies: MixedCurrencyType[] = [ ...CurrencyManager.getDefaultList(), { network: 'private', diff --git a/packages/integration-test/test/scheduled/any-to-erc20-detector.test.ts b/packages/integration-test/test/scheduled/any-to-erc20-detector.test.ts index 4c4873875b..e1122f6e8e 100644 --- a/packages/integration-test/test/scheduled/any-to-erc20-detector.test.ts +++ b/packages/integration-test/test/scheduled/any-to-erc20-detector.test.ts @@ -1,5 +1,5 @@ import { PaymentNetworkFactory } from '@requestnetwork/payment-detection'; -import { CurrencyTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; import { CurrencyManager } from '@requestnetwork/currency'; import { mockAdvancedLogic } from './mocks'; @@ -8,7 +8,7 @@ import { createMockConversionErc20Request } from '../utils'; const pnFactory = new PaymentNetworkFactory(mockAdvancedLogic, CurrencyManager.getDefault()); const paidEURRequest = { - network: 'matic' as CurrencyTypes.EvmChainName, + network: 'matic', requestId: '0117d7a59a48e5031b3c56c92621453149e4a4462dba6eaeb3271a995c4201448b', paymentAddress: '0x4E64C2d06d19D13061e62E291b2C4e9fe5679b93', salt: '5ddb1c1645ac2daf', diff --git a/packages/integration-test/test/scheduled/any-to-eth-detector.test.ts b/packages/integration-test/test/scheduled/any-to-eth-detector.test.ts index c1e71e5547..d60a9bb1fa 100644 --- a/packages/integration-test/test/scheduled/any-to-eth-detector.test.ts +++ b/packages/integration-test/test/scheduled/any-to-eth-detector.test.ts @@ -1,5 +1,5 @@ import { PaymentNetworkFactory } from '@requestnetwork/payment-detection'; -import { CurrencyTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; import { CurrencyManager } from '@requestnetwork/currency'; import { mockAdvancedLogic } from './mocks'; @@ -8,7 +8,7 @@ import { createMockConversionEthTokenRequest } from '../utils'; const pnFactory = new PaymentNetworkFactory(mockAdvancedLogic, CurrencyManager.getDefault()); const paidEURRequest = { - network: 'matic' as CurrencyTypes.EvmChainName, + network: 'matic', requestId: '01814304b39265cbf0c2abb4f3c7e8432d1e2c8779be6022e545d25f95144360e0', paymentAddress: '0x4E64C2d06d19D13061e62E291b2C4e9fe5679b93', salt: 'b3f2e478374bff64', diff --git a/packages/integration-test/test/scheduled/erc20-fee-proxy.test.ts b/packages/integration-test/test/scheduled/erc20-fee-proxy.test.ts index e3062b6dd9..9a5c4feb89 100644 --- a/packages/integration-test/test/scheduled/erc20-fee-proxy.test.ts +++ b/packages/integration-test/test/scheduled/erc20-fee-proxy.test.ts @@ -1,6 +1,6 @@ import { CurrencyManager } from '@requestnetwork/currency'; import { PaymentNetworkFactory } from '@requestnetwork/payment-detection'; -import { CurrencyTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; import { Types, Utils } from '@requestnetwork/request-client.js'; import { mockAdvancedLogic } from './mocks'; @@ -20,7 +20,7 @@ automine(); const pnFactory = new PaymentNetworkFactory(mockAdvancedLogic, CurrencyManager.getDefault()); const paidRequest = { - network: 'matic' as CurrencyTypes.EvmChainName, + network: 'matic', requestId: '014bcd076791fb915af457df1d3f26c81ff66f7e278e4a18f0e48a1705572a6306', paymentAddress: '0x4E64C2d06d19D13061e62E291b2C4e9fe5679b93', salt: '8c5ea6f8b4a14fe0', diff --git a/packages/integration-test/test/scheduled/erc777-stream.test.ts b/packages/integration-test/test/scheduled/erc777-stream.test.ts index a57d187432..aca5430701 100644 --- a/packages/integration-test/test/scheduled/erc777-stream.test.ts +++ b/packages/integration-test/test/scheduled/erc777-stream.test.ts @@ -1,6 +1,5 @@ import { SuperFluidPaymentDetector } from '@requestnetwork/payment-detection'; import { - CurrencyTypes, ExtensionTypes, IdentityTypes, PaymentTypes, @@ -16,9 +15,10 @@ const createMockRequest = ({ paymentAddress, salt, requestId, -}: Record<'tokenAddress' | 'paymentAddress' | 'salt' | 'requestId', string> & { - network: CurrencyTypes.EvmChainName; -}): RequestLogicTypes.IRequest => ({ +}: Record< + 'network' | 'tokenAddress' | 'paymentAddress' | 'salt' | 'requestId', + string +>): RequestLogicTypes.IRequest => ({ creator: { type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, value: '0x2' }, currency: { network, diff --git a/packages/integration-test/test/utils.ts b/packages/integration-test/test/utils.ts index 0621a4d46d..963ae3cc12 100644 --- a/packages/integration-test/test/utils.ts +++ b/packages/integration-test/test/utils.ts @@ -1,5 +1,5 @@ import { - CurrencyTypes, + ChainTypes, ExtensionTypes, IdentityTypes, RequestLogicTypes, @@ -14,9 +14,9 @@ export const createMockErc20FeeRequest = ({ feeAddress, feeAmount, }: Record< - 'tokenAddress' | 'paymentAddress' | 'salt' | 'requestId' | 'feeAddress' | 'feeAmount', + 'network' | 'tokenAddress' | 'paymentAddress' | 'salt' | 'requestId' | 'feeAddress' | 'feeAmount', string -> & { network: CurrencyTypes.EvmChainName }): RequestLogicTypes.IRequest => ({ +>): RequestLogicTypes.IRequest => ({ creator: { type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, value: '0x2' }, currency: { network, @@ -56,10 +56,9 @@ export const createMockConversionErc20Request = ({ feeAmount, currency, }: Record< - 'tokenAddress' | 'paymentAddress' | 'salt' | 'requestId' | 'feeAddress' | 'feeAmount', + 'network' | 'tokenAddress' | 'paymentAddress' | 'salt' | 'requestId' | 'feeAddress' | 'feeAmount', string > & { - network: CurrencyTypes.EvmChainName; currency: RequestLogicTypes.ICurrency; }): RequestLogicTypes.IRequest => ({ creator: { type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, value: '0x2' }, @@ -98,11 +97,15 @@ export const createMockNativeTokenRequest = ({ feeAmount, nativeTokenCode, }: Record< - 'paymentAddress' | 'salt' | 'requestId' | 'feeAddress' | 'feeAmount' | 'nativeTokenCode', + | 'network' + | 'paymentAddress' + | 'salt' + | 'requestId' + | 'feeAddress' + | 'feeAmount' + | 'nativeTokenCode', string -> & { - network: CurrencyTypes.EvmChainName; -}): RequestLogicTypes.IRequest => ({ +>): RequestLogicTypes.IRequest => ({ creator: { type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, value: '0x2' }, currency: { network, @@ -140,8 +143,10 @@ export const createMockConversionEthTokenRequest = ({ feeAddress, feeAmount, currency, -}: Record<'paymentAddress' | 'salt' | 'requestId' | 'feeAddress' | 'feeAmount', string> & { - network: CurrencyTypes.EvmChainName; +}: Record< + 'network' | 'paymentAddress' | 'salt' | 'requestId' | 'feeAddress' | 'feeAmount', + string +> & { currency: RequestLogicTypes.ICurrency; }): RequestLogicTypes.IRequest => ({ creator: { type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, value: '0x2' }, diff --git a/packages/payment-detection/src/any-to-any-detector.ts b/packages/payment-detection/src/any-to-any-detector.ts index 14930f68bf..451dc6bce1 100644 --- a/packages/payment-detection/src/any-to-any-detector.ts +++ b/packages/payment-detection/src/any-to-any-detector.ts @@ -1,4 +1,4 @@ -import { ExtensionTypes } from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes } from '@requestnetwork/types'; import { FeeReferenceBasedDetector } from './fee-reference-based-detector'; import { ICurrencyManager } from '@requestnetwork/currency'; import { generate8randomBytes } from '@requestnetwork/utils'; @@ -17,8 +17,9 @@ export abstract class AnyToAnyDetector< paymentNetworkId: ExtensionTypes.PAYMENT_NETWORK_ID, extension: TExtension, currencyManager: ICurrencyManager, + allowedEcosystems: ChainTypes.ECOSYSTEM[], ) { - super(paymentNetworkId, extension, currencyManager); + super(paymentNetworkId, extension, currencyManager, allowedEcosystems); } /** diff --git a/packages/payment-detection/src/any-to-native-detector.ts b/packages/payment-detection/src/any-to-native-detector.ts index 1174431174..95d45c9921 100644 --- a/packages/payment-detection/src/any-to-native-detector.ts +++ b/packages/payment-detection/src/any-to-native-detector.ts @@ -1,4 +1,4 @@ -import { ExtensionTypes, PaymentTypes } from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes, PaymentTypes } from '@requestnetwork/types'; import { AnyToAnyDetector } from './any-to-any-detector'; import { NativeDetectorOptions } from './types'; @@ -18,6 +18,6 @@ export abstract class AnyToNativeDetector extends AnyToAnyDetector< if (!extension) { throw new Error(`the ${extensionId} extension is not supported for the network ${network}`); } - super(extensionId, extension, currencyManager); + super(extensionId, extension, currencyManager, [ChainTypes.ECOSYSTEM.NEAR]); } } diff --git a/packages/payment-detection/src/any/any-to-erc20-proxy.ts b/packages/payment-detection/src/any/any-to-erc20-proxy.ts index 10ae5e737f..6396ad5215 100644 --- a/packages/payment-detection/src/any/any-to-erc20-proxy.ts +++ b/packages/payment-detection/src/any/any-to-erc20-proxy.ts @@ -1,17 +1,11 @@ import { erc20ConversionProxy } from '@requestnetwork/smart-contracts'; -import { - CurrencyTypes, - ExtensionTypes, - PaymentTypes, - RequestLogicTypes, -} from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; import { ERC20FeeProxyPaymentDetectorBase } from '../erc20/fee-proxy-contract'; import { AnyToErc20InfoRetriever } from './retrievers/any-to-erc20-proxy'; import { TheGraphConversionInfoRetriever } from '../thegraph/conversion-info-retriever'; import { makeGetDeploymentInformation } from '../utils'; import { PaymentNetworkOptions, ReferenceBasedDetectorOptions, TGetSubGraphClient } from '../types'; import { generate8randomBytes } from '@requestnetwork/utils'; -import { EvmChains } from '@requestnetwork/currency'; const PROXY_CONTRACT_ADDRESS_MAP = { ['0.1.0']: '0.1.0', @@ -24,18 +18,14 @@ export class AnyToERC20PaymentDetector extends ERC20FeeProxyPaymentDetectorBase< ExtensionTypes.PnAnyToErc20.IAnyToERC20, PaymentTypes.IERC20FeePaymentEventParameters > { - private readonly getSubgraphClient: TGetSubGraphClient; - - /** - * @param extension The advanced logic payment network extensions - */ + private readonly getSubgraphClient: TGetSubGraphClient; public constructor({ advancedLogic, currencyManager, getSubgraphClient, }: ReferenceBasedDetectorOptions & - Pick, 'getSubgraphClient'>) { + Pick, 'getSubgraphClient'>) { super( ExtensionTypes.PAYMENT_NETWORK_ID.ANY_TO_ERC20_PROXY, advancedLogic.extensions.anyToErc20Proxy, @@ -85,7 +75,7 @@ export class AnyToERC20PaymentDetector extends ERC20FeeProxyPaymentDetectorBase< toAddress: string | undefined, paymentReference: string, requestCurrency: RequestLogicTypes.ICurrency, - paymentChain: CurrencyTypes.EvmChainName, + paymentChain: ChainTypes.IEvmChain, paymentNetwork: ExtensionTypes.IState, ): Promise> { if (!toAddress) { @@ -141,13 +131,12 @@ export class AnyToERC20PaymentDetector extends ERC20FeeProxyPaymentDetectorBase< }; } - protected getPaymentChain(request: RequestLogicTypes.IRequest): CurrencyTypes.EvmChainName { + protected getPaymentChain(request: RequestLogicTypes.IRequest): ChainTypes.IEvmChain { const network = this.getPaymentExtension(request).values.network; if (!network) { throw Error(`request.extensions[${this.paymentNetworkId}].values.network must be defined`); } - EvmChains.assertChainSupported(network); - return network; + return this.currencyManager.chainManager.fromName(network, [ChainTypes.ECOSYSTEM.EVM]); } public static getDeploymentInformation = makeGetDeploymentInformation( diff --git a/packages/payment-detection/src/any/any-to-eth-proxy.ts b/packages/payment-detection/src/any/any-to-eth-proxy.ts index f66b6cbde5..e7b3ff62d8 100644 --- a/packages/payment-detection/src/any/any-to-eth-proxy.ts +++ b/packages/payment-detection/src/any/any-to-eth-proxy.ts @@ -1,12 +1,7 @@ import * as SmartContracts from '@requestnetwork/smart-contracts'; -import { - CurrencyTypes, - ExtensionTypes, - PaymentTypes, - RequestLogicTypes, -} from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; -import { EvmChains, UnsupportedCurrencyError } from '@requestnetwork/currency'; +import { UnsupportedCurrencyError } from '@requestnetwork/currency'; import { AnyToEthInfoRetriever } from './retrievers/any-to-eth-proxy'; import { AnyToAnyDetector } from '../any-to-any-detector'; @@ -31,7 +26,7 @@ export class AnyToEthFeeProxyPaymentDetector extends AnyToAnyDetector< ExtensionTypes.PnAnyToEth.IAnyToEth, PaymentTypes.IETHFeePaymentEventParameters > { - private readonly getSubgraphClient: TGetSubGraphClient; + private readonly getSubgraphClient: TGetSubGraphClient; /** * @param extension The advanced logic payment network extensions */ @@ -40,11 +35,12 @@ export class AnyToEthFeeProxyPaymentDetector extends AnyToAnyDetector< currencyManager, getSubgraphClient, }: ReferenceBasedDetectorOptions & - Pick, 'getSubgraphClient'>) { + Pick, 'getSubgraphClient'>) { super( ExtensionTypes.PAYMENT_NETWORK_ID.ANY_TO_ETH_PROXY, advancedLogic.extensions.anyToEthProxy, currencyManager, + [ChainTypes.ECOSYSTEM.EVM], ); this.getSubgraphClient = getSubgraphClient; } @@ -64,7 +60,7 @@ export class AnyToEthFeeProxyPaymentDetector extends AnyToAnyDetector< toAddress: string | undefined, paymentReference: string, requestCurrency: RequestLogicTypes.ICurrency, - paymentChain: CurrencyTypes.EvmChainName, + paymentChain: ChainTypes.IEvmChain, paymentNetwork: ExtensionTypes.IState, ): Promise> { if (!toAddress) { @@ -127,13 +123,12 @@ export class AnyToEthFeeProxyPaymentDetector extends AnyToAnyDetector< * @param paymentNetwork the payment network * @returns The network of payment */ - protected getPaymentChain(request: RequestLogicTypes.IRequest): CurrencyTypes.EvmChainName { + protected getPaymentChain(request: RequestLogicTypes.IRequest): ChainTypes.IEvmChain { const network = this.getPaymentExtension(request).values.network; if (!network) { throw Error(`request.extensions[${this.paymentNetworkId}].values.network must be defined`); } - EvmChains.assertChainSupported(network); - return network; + return this.currencyManager.chainManager.fromName(network, [ChainTypes.ECOSYSTEM.EVM]); } /* diff --git a/packages/payment-detection/src/any/retrievers/any-to-any-proxy.ts b/packages/payment-detection/src/any/retrievers/any-to-any-proxy.ts index 3963d2949a..2f665ac516 100644 --- a/packages/payment-detection/src/any/retrievers/any-to-any-proxy.ts +++ b/packages/payment-detection/src/any/retrievers/any-to-any-proxy.ts @@ -1,5 +1,5 @@ import { CurrencyDefinition } from '@requestnetwork/currency'; -import { PaymentTypes } from '@requestnetwork/types'; +import { ChainTypes, PaymentTypes } from '@requestnetwork/types'; import { BigNumber, ethers } from 'ethers'; import { parseLogArgs, unpadAmountFromChainlink } from '../../utils'; import type { JsonFragment } from '@ethersproject/abi'; @@ -48,7 +48,7 @@ export abstract class ConversionInfoRetriever { protected conversionProxyContractAbiFragment: JsonFragment[], protected toAddress: string, protected eventName: PaymentTypes.EVENTS_NAMES, - protected network: string, + protected network: ChainTypes.IChain, protected acceptedTokens?: string[], protected maxRateTimespan: number = 0, ) { diff --git a/packages/payment-detection/src/erc20/currency.ts b/packages/payment-detection/src/erc20/currency.ts index a8d938a673..f1e32524e8 100644 --- a/packages/payment-detection/src/erc20/currency.ts +++ b/packages/payment-detection/src/erc20/currency.ts @@ -1,7 +1,7 @@ import { CurrencyDefinition, CurrencyManager, - EvmChains, + ERC20Currency, getCurrencyHash, StorageCurrency, } from '@requestnetwork/currency'; @@ -9,7 +9,8 @@ import { RequestLogicTypes } from '@requestnetwork/types'; import { ERC20__factory } from '@requestnetwork/smart-contracts/types'; import { isAddress } from 'ethers/lib/utils'; import { getDefaultProvider } from '@requestnetwork/utils'; -import { ERC20CurrencyInput } from '@requestnetwork/currency/src'; +import { CurrencyInput, ERC20CurrencyInput } from '@requestnetwork/currency/src'; +import { ChainManager } from '@requestnetwork/chain/src'; export const loadCurrencyFromContract = async ( currency: StorageCurrency, @@ -20,7 +21,6 @@ export const loadCurrencyFromContract = async ( if (!network || !isAddress(value)) { return null; } - EvmChains.assertChainSupported(network); const contract = ERC20__factory.connect(value, getDefaultProvider(network)); const decimals = await contract.decimals(); @@ -34,13 +34,16 @@ export const loadCurrencyFromContract = async ( return null; } - const definition: ERC20CurrencyInput = { + const definition = { address: value, decimals, symbol, - network: network, + network: ChainManager.current().fromName( + network, + ChainManager.current().getEcosystemsByCurrencyType(RequestLogicTypes.CURRENCY.ERC20), + ), type: RequestLogicTypes.CURRENCY.ERC20, - }; + } as ERC20Currency; return { ...definition, diff --git a/packages/payment-detection/src/erc20/escrow-info-retriever.ts b/packages/payment-detection/src/erc20/escrow-info-retriever.ts index 2f128d97e8..30c647260a 100644 --- a/packages/payment-detection/src/erc20/escrow-info-retriever.ts +++ b/packages/payment-detection/src/erc20/escrow-info-retriever.ts @@ -1,4 +1,4 @@ -import { PaymentTypes } from '@requestnetwork/types'; +import { ChainTypes, PaymentTypes } from '@requestnetwork/types'; import { erc20EscrowToPayArtifact } from '@requestnetwork/smart-contracts'; import { BigNumber, ethers } from 'ethers'; import { IEventRetriever } from '../types'; @@ -51,7 +51,7 @@ export class EscrowERC20InfoRetriever private escrowCreationBlockNumber: number, private tokenContractAddress: string, private toAddress: string, - private network: string, + private network: ChainTypes.IEvmChain, private eventName?: PaymentTypes.ESCROW_EVENTS_NAMES, ) { // Creates a local or default provider. diff --git a/packages/payment-detection/src/erc20/fee-proxy-contract.ts b/packages/payment-detection/src/erc20/fee-proxy-contract.ts index e562ff76a8..4210ca2624 100644 --- a/packages/payment-detection/src/erc20/fee-proxy-contract.ts +++ b/packages/payment-detection/src/erc20/fee-proxy-contract.ts @@ -1,24 +1,17 @@ import { erc20FeeProxyArtifact } from '@requestnetwork/smart-contracts'; -import { - CurrencyTypes, - ExtensionTypes, - PaymentTypes, - RequestLogicTypes, -} from '@requestnetwork/types'; -import { - CurrencyDefinition, - EvmChains, - ICurrencyManager, - NearChains, - isSameChain, -} from '@requestnetwork/currency'; +import { ChainTypes, ExtensionTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { CurrencyDefinition, ICurrencyManager } from '@requestnetwork/currency'; import ProxyInfoRetriever from './proxy-info-retriever'; import { loadCurrencyFromContract } from './currency'; import { FeeReferenceBasedDetector } from '../fee-reference-based-detector'; import { makeGetDeploymentInformation } from '../utils'; import { TheGraphClient, TheGraphInfoRetriever } from '../thegraph'; -import { ReferenceBasedDetectorOptions, TGetSubGraphClient } from '../types'; +import { + ITheGraphBaseInfoRetriever, + ReferenceBasedDetectorOptions, + TGetSubGraphClient, +} from '../types'; import { NearInfoRetriever } from '../near'; import { NetworkNotSupported } from '../balance-error'; @@ -44,7 +37,7 @@ export abstract class ERC20FeeProxyPaymentDetectorBase< extension: TExtension, currencyManager: ICurrencyManager, ) { - super(paymentNetworkId, extension, currencyManager); + super(paymentNetworkId, extension, currencyManager, ChainTypes.VM_ECOSYSTEMS); } protected async getCurrency( @@ -81,13 +74,14 @@ export abstract class ERC20FeeProxyPaymentDetectorBase< * Handle payment networks with ERC20 fee proxy contract extension on EVM (default) or Near chains */ export class ERC20FeeProxyPaymentDetector< - TChain extends CurrencyTypes.VMChainName = CurrencyTypes.EvmChainName, + TChain extends ChainTypes.IVmChain = ChainTypes.IEvmChain, > extends ERC20FeeProxyPaymentDetectorBase< ExtensionTypes.PnFeeReferenceBased.IFeeReferenceBased, PaymentTypes.IERC20FeePaymentEventParameters > { private readonly getSubgraphClient: TGetSubGraphClient; protected readonly network: TChain | undefined; + constructor({ advancedLogic, currencyManager, @@ -118,7 +112,7 @@ export class ERC20FeeProxyPaymentDetector< paymentChain: TChain, paymentNetwork: ExtensionTypes.IState, ): Promise> { - if (this.network && !isSameChain(paymentChain, this.network)) { + if (this.network && paymentChain.eq(this.network)) { throw new NetworkNotSupported( `Unsupported network '${paymentChain}' for payment detector instanciated with '${this.network}'`, ); @@ -132,53 +126,49 @@ export class ERC20FeeProxyPaymentDetector< const { address: proxyContractAddress, creationBlockNumber: proxyCreationBlockNumber } = ERC20FeeProxyPaymentDetector.getDeploymentInformation(paymentChain, paymentNetwork.version); - const subgraphClient = this.getSubgraphClient(paymentChain); - if (subgraphClient) { - const graphInfoRetriever = this.getTheGraphInfoRetriever(paymentChain, subgraphClient); + // with TheGraph + const graphInfoRetriever = this.getTheGraphInfoRetriever(paymentChain); + if (graphInfoRetriever) { return graphInfoRetriever.getTransferEvents({ eventName, paymentReference, toAddress, contractAddress: proxyContractAddress, - paymentChain, acceptedTokens: [requestCurrency.value], - }); - } else { - if (!EvmChains.isChainSupported(paymentChain)) { - throw new Error( - `Could not get a TheGraph-based info retriever for chain ${paymentChain} and RPC-based info retrievers are only compatible with EVM chains.`, - ); - } - const proxyInfoRetriever = new ProxyInfoRetriever( - paymentReference, - proxyContractAddress, - proxyCreationBlockNumber, - requestCurrency.value, - toAddress, - eventName, paymentChain, + }); + } + + if (paymentChain.ecosystem !== ChainTypes.ECOSYSTEM.EVM) { + throw new Error( + `Could not get a TheGraph-based info retriever for chain ${paymentChain} and RPC-based info retrievers are only compatible with EVM chains.`, ); - const paymentEvents = await proxyInfoRetriever.getTransferEvents(); - return { - paymentEvents, - }; } + + // without TheGraph + const proxyInfoRetriever = new ProxyInfoRetriever( + paymentReference, + proxyContractAddress, + proxyCreationBlockNumber, + requestCurrency.value, + toAddress, + eventName, + paymentChain, + ); + const paymentEvents = await proxyInfoRetriever.getTransferEvents(); + return { + paymentEvents, + }; } protected getTheGraphInfoRetriever( paymentChain: TChain, - subgraphClient: TheGraphClient | TheGraphClient, - ): TheGraphInfoRetriever | NearInfoRetriever { - const graphInfoRetriever = EvmChains.isChainSupported(paymentChain) - ? new TheGraphInfoRetriever(subgraphClient as TheGraphClient, this.currencyManager) - : NearChains.isChainSupported(paymentChain) && this.network - ? new NearInfoRetriever(subgraphClient as TheGraphClient) + ): ITheGraphBaseInfoRetriever | undefined { + const subgraphClient = this.getSubgraphClient(paymentChain); + return subgraphClient + ? paymentChain.ecosystem === ChainTypes.ECOSYSTEM.NEAR + ? new NearInfoRetriever(subgraphClient as TheGraphClient) + : new TheGraphInfoRetriever(subgraphClient as TheGraphClient, this.currencyManager) : undefined; - if (!graphInfoRetriever) { - throw new Error( - `Could not find graphInfoRetriever for chain ${paymentChain} in payment detector`, - ); - } - return graphInfoRetriever; } } diff --git a/packages/payment-detection/src/erc20/proxy-contract.ts b/packages/payment-detection/src/erc20/proxy-contract.ts index ed7205509c..4e3b7e05c9 100644 --- a/packages/payment-detection/src/erc20/proxy-contract.ts +++ b/packages/payment-detection/src/erc20/proxy-contract.ts @@ -1,9 +1,4 @@ -import { - CurrencyTypes, - ExtensionTypes, - PaymentTypes, - RequestLogicTypes, -} from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; import { erc20ProxyArtifact } from '@requestnetwork/smart-contracts'; import ProxyInfoRetriever from './proxy-info-retriever'; import { TheGraphInfoRetriever } from '../thegraph'; @@ -22,7 +17,7 @@ export class ERC20ProxyPaymentDetector extends ReferenceBasedDetector< ExtensionTypes.PnReferenceBased.IReferenceBased, PaymentTypes.IERC20PaymentEventParameters > { - private readonly getSubgraphClient: TGetSubGraphClient; + private readonly getSubgraphClient: TGetSubGraphClient; /** * @param extension The advanced logic payment network extensions @@ -32,11 +27,12 @@ export class ERC20ProxyPaymentDetector extends ReferenceBasedDetector< currencyManager, getSubgraphClient, }: ReferenceBasedDetectorOptions & - Pick, 'getSubgraphClient'>) { + Pick, 'getSubgraphClient'>) { super( ExtensionTypes.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT, advancedLogic.extensions.proxyContractErc20, currencyManager, + [ChainTypes.ECOSYSTEM.EVM], ); this.getSubgraphClient = getSubgraphClient; } @@ -56,7 +52,7 @@ export class ERC20ProxyPaymentDetector extends ReferenceBasedDetector< toAddress: string | undefined, paymentReference: string, requestCurrency: RequestLogicTypes.ICurrency, - paymentChain: CurrencyTypes.EvmChainName, + paymentChain: ChainTypes.IEvmChain, paymentNetwork: ExtensionTypes.IState, ): Promise> { if (!toAddress) { diff --git a/packages/payment-detection/src/erc20/proxy-info-retriever.ts b/packages/payment-detection/src/erc20/proxy-info-retriever.ts index 8dcbf7466c..7baae06c78 100644 --- a/packages/payment-detection/src/erc20/proxy-info-retriever.ts +++ b/packages/payment-detection/src/erc20/proxy-info-retriever.ts @@ -1,4 +1,4 @@ -import { PaymentTypes } from '@requestnetwork/types'; +import { ChainTypes, PaymentTypes } from '@requestnetwork/types'; import { IPaymentRetriever } from '../types'; import { BigNumber, ethers } from 'ethers'; import { parseLogArgs } from '../utils'; @@ -49,7 +49,7 @@ export default class ProxyERC20InfoRetriever private tokenContractAddress: string, private toAddress: string, private eventName: PaymentTypes.EVENTS_NAMES, - private network: string, + private network: ChainTypes.IVmChain, ) { // Creates a local or default provider this.provider = getDefaultProvider(this.network); diff --git a/packages/payment-detection/src/erc20/transferable-receivable.ts b/packages/payment-detection/src/erc20/transferable-receivable.ts index 7ee8243752..7ae5b56a31 100644 --- a/packages/payment-detection/src/erc20/transferable-receivable.ts +++ b/packages/payment-detection/src/erc20/transferable-receivable.ts @@ -1,9 +1,4 @@ -import { - ExtensionTypes, - PaymentTypes, - RequestLogicTypes, - CurrencyTypes, -} from '@requestnetwork/types'; +import { ExtensionTypes, PaymentTypes, RequestLogicTypes, ChainTypes } from '@requestnetwork/types'; import { TheGraphInfoRetriever } from '../thegraph'; import { erc20TransferableReceivableArtifact } from '@requestnetwork/smart-contracts'; @@ -24,7 +19,7 @@ export class ERC20TransferableReceivablePaymentDetector extends FeeReferenceBase ExtensionTypes.PnFeeReferenceBased.IFeeReferenceBased, PaymentTypes.IERC20PaymentEventParameters > { - private readonly getSubgraphClient: TGetSubGraphClient; + private readonly getSubgraphClient: TGetSubGraphClient; /** * @param extension The advanced logic payment network extensions @@ -34,11 +29,12 @@ export class ERC20TransferableReceivablePaymentDetector extends FeeReferenceBase currencyManager, getSubgraphClient, }: ReferenceBasedDetectorOptions & - Pick, 'getSubgraphClient'>) { + Pick, 'getSubgraphClient'>) { super( ExtensionTypes.PAYMENT_NETWORK_ID.ERC20_TRANSFERABLE_RECEIVABLE, advancedLogic.extensions.erc20TransferableReceivable, currencyManager, + [ChainTypes.ECOSYSTEM.EVM], ); this.getSubgraphClient = getSubgraphClient; } @@ -58,7 +54,7 @@ export class ERC20TransferableReceivablePaymentDetector extends FeeReferenceBase toAddress: string | undefined, paymentReference: string, requestCurrency: RequestLogicTypes.ICurrency, - paymentChain: CurrencyTypes.EvmChainName, + paymentChain: ChainTypes.IEvmChain, paymentNetwork: ExtensionTypes.IState, ): Promise> { // To satisfy typescript diff --git a/packages/payment-detection/src/erc777/superfluid-detector.ts b/packages/payment-detection/src/erc777/superfluid-detector.ts index 3f329554b8..109367a151 100644 --- a/packages/payment-detection/src/erc777/superfluid-detector.ts +++ b/packages/payment-detection/src/erc777/superfluid-detector.ts @@ -1,9 +1,4 @@ -import { - CurrencyTypes, - ExtensionTypes, - PaymentTypes, - RequestLogicTypes, -} from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; import { SuperFluidInfoRetriever } from './superfluid-retriever'; import { ReferenceBasedDetector } from '../reference-based-detector'; import * as PaymentReferenceCalculator from '../payment-reference-calculator'; @@ -24,6 +19,7 @@ export class SuperFluidPaymentDetector extends ReferenceBasedDetector< ExtensionTypes.PAYMENT_NETWORK_ID.ERC777_STREAM, advancedLogic.extensions.erc777Stream, currencyManager, + [ChainTypes.ECOSYSTEM.EVM], ); } @@ -96,7 +92,7 @@ export class SuperFluidPaymentDetector extends ReferenceBasedDetector< address: string | undefined, paymentReference: string, requestCurrency: RequestLogicTypes.ICurrency, - paymentChain: CurrencyTypes.EvmChainName, + paymentChain: ChainTypes.IEvmChain, ): Promise> { if (!address) { return { diff --git a/packages/payment-detection/src/erc777/superfluid-retriever.ts b/packages/payment-detection/src/erc777/superfluid-retriever.ts index 314491cfda..8babcdcfa5 100644 --- a/packages/payment-detection/src/erc777/superfluid-retriever.ts +++ b/packages/payment-detection/src/erc777/superfluid-retriever.ts @@ -1,4 +1,4 @@ -import { PaymentTypes } from '@requestnetwork/types'; +import { ChainTypes, PaymentTypes } from '@requestnetwork/types'; import { FlowUpdatedEvent, SentEvent } from '../thegraph/generated/graphql-superfluid'; import { getTheGraphSuperfluidClient, @@ -23,7 +23,7 @@ export class SuperFluidInfoRetriever { * @param tokenContractAddress The address of the ERC777 contract * @param toAddress Address of the balance we want to check * @param eventName Indicate if it is an address for payment or refund - * @param network The Ethereum network to use + * @param chain The Ethereum network to use * @param options Extra options to GraphQL client */ constructor( @@ -31,10 +31,10 @@ export class SuperFluidInfoRetriever { private tokenContractAddress: string | null, private toAddress: string, private eventName: PaymentTypes.EVENTS_NAMES, - private network: string, + private chain: ChainTypes.IEvmChain, private options?: TheGraphClientOptions, ) { - this.client = getTheGraphSuperfluidClient(this.network, this.options); + this.client = getTheGraphSuperfluidClient(this.chain, this.options); } private getGraphVariables(): GraphPaymentQueryParams { @@ -66,7 +66,7 @@ export class SuperFluidInfoRetriever { } /** - * First MVP version which convert : + * First MVP version which converts * stream events queried from SuperFluid subgraph * into payment events with the parameters expected by extractEvents function * to compute balance from amounts in ERC20 style transactions diff --git a/packages/payment-detection/src/eth/fee-proxy-detector.ts b/packages/payment-detection/src/eth/fee-proxy-detector.ts index 2f27e1b43f..2805e8de2d 100644 --- a/packages/payment-detection/src/eth/fee-proxy-detector.ts +++ b/packages/payment-detection/src/eth/fee-proxy-detector.ts @@ -1,10 +1,5 @@ import * as SmartContracts from '@requestnetwork/smart-contracts'; -import { - CurrencyTypes, - ExtensionTypes, - PaymentTypes, - RequestLogicTypes, -} from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; import { EthProxyInfoRetriever } from './proxy-info-retriever'; import { FeeReferenceBasedDetector } from '../fee-reference-based-detector'; @@ -29,7 +24,7 @@ export class EthFeeProxyPaymentDetector extends FeeReferenceBasedDetector< ExtensionTypes.PnFeeReferenceBased.IFeeReferenceBased, PaymentTypes.IETHFeePaymentEventParameters > { - private readonly getSubgraphClient: TGetSubGraphClient; + private readonly getSubgraphClient: TGetSubGraphClient; /** * @param extension The advanced logic payment network extensions */ @@ -38,11 +33,12 @@ export class EthFeeProxyPaymentDetector extends FeeReferenceBasedDetector< currencyManager, getSubgraphClient, }: ReferenceBasedDetectorOptions & - Pick, 'getSubgraphClient'>) { + Pick, 'getSubgraphClient'>) { super( ExtensionTypes.PAYMENT_NETWORK_ID.ETH_FEE_PROXY_CONTRACT, advancedLogic.extensions.feeProxyContractEth, currencyManager, + [ChainTypes.ECOSYSTEM.EVM], ); this.getSubgraphClient = getSubgraphClient; } @@ -63,7 +59,7 @@ export class EthFeeProxyPaymentDetector extends FeeReferenceBasedDetector< toAddress: string | undefined, paymentReference: string, _requestCurrency: RequestLogicTypes.ICurrency, - paymentChain: CurrencyTypes.EvmChainName, + paymentChain: ChainTypes.IEvmChain, paymentNetwork: ExtensionTypes.PnFeeReferenceBased.IFeeReferenceBased extends ExtensionTypes.IExtension< infer X > diff --git a/packages/payment-detection/src/eth/info-retriever.ts b/packages/payment-detection/src/eth/info-retriever.ts index 689ab80802..24f56b921b 100644 --- a/packages/payment-detection/src/eth/info-retriever.ts +++ b/packages/payment-detection/src/eth/info-retriever.ts @@ -1,4 +1,4 @@ -import { PaymentTypes } from '@requestnetwork/types'; +import { ChainTypes, PaymentTypes } from '@requestnetwork/types'; import { IPaymentRetriever } from '../types'; import { MultichainExplorerApiProvider } from './multichainExplorerApiProvider'; @@ -18,18 +18,18 @@ export class EthInputDataInfoRetriever constructor( private toAddress: string, private eventName: PaymentTypes.EVENTS_NAMES, - private network: string, + private network: ChainTypes.IChain, private paymentReference: string, private explorerApiKey?: string, ) {} public async getTransferEvents(): Promise { - if (this.network === 'private') { + if (this.network.name === 'private') { throw new Error( 'ETH input data info-retriever works with etherscan and cannot work on a local network', ); } - const provider = new MultichainExplorerApiProvider(this.network, this.explorerApiKey); + const provider = new MultichainExplorerApiProvider(this.network.name, this.explorerApiKey); const history = await provider.getHistory(this.toAddress); const events = history diff --git a/packages/payment-detection/src/eth/input-data.ts b/packages/payment-detection/src/eth/input-data.ts index 75e822d755..c4d3aea52e 100644 --- a/packages/payment-detection/src/eth/input-data.ts +++ b/packages/payment-detection/src/eth/input-data.ts @@ -1,10 +1,5 @@ import * as SmartContracts from '@requestnetwork/smart-contracts'; -import { - CurrencyTypes, - ExtensionTypes, - PaymentTypes, - RequestLogicTypes, -} from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; import { EthInputDataInfoRetriever } from './info-retriever'; import { EthProxyInfoRetriever } from './proxy-info-retriever'; import { ReferenceBasedDetector } from '../reference-based-detector'; @@ -31,8 +26,8 @@ export class EthInputDataPaymentDetector extends ReferenceBasedDetector< ExtensionTypes.PnReferenceBased.IReferenceBased, PaymentTypes.IETHPaymentEventParameters > { - private explorerApiKeys: Partial>; - private readonly getSubgraphClient: TGetSubGraphClient; + private explorerApiKeys: Partial>; + private readonly getSubgraphClient: TGetSubGraphClient; /** * @param extension The advanced logic payment network extensions @@ -43,14 +38,12 @@ export class EthInputDataPaymentDetector extends ReferenceBasedDetector< explorerApiKeys, getSubgraphClient, }: ReferenceBasedDetectorOptions & - Pick< - PaymentNetworkOptions, - 'explorerApiKeys' | 'getSubgraphClient' - >) { + Pick, 'explorerApiKeys' | 'getSubgraphClient'>) { super( ExtensionTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA, advancedLogic.extensions.ethereumInputData, currencyManager, + [ChainTypes.ECOSYSTEM.EVM], ); this.explorerApiKeys = explorerApiKeys || {}; this.getSubgraphClient = getSubgraphClient; @@ -71,7 +64,7 @@ export class EthInputDataPaymentDetector extends ReferenceBasedDetector< toAddress: string | undefined, paymentReference: string, _requestCurrency: RequestLogicTypes.ICurrency, - paymentChain: CurrencyTypes.EvmChainName, + paymentChain: ChainTypes.IEvmChain, paymentNetwork: ExtensionTypes.IState, ): Promise< PaymentTypes.AllNetworkEvents< @@ -88,7 +81,7 @@ export class EthInputDataPaymentDetector extends ReferenceBasedDetector< eventName, paymentChain, paymentReference, - this.explorerApiKeys[paymentChain], + this.explorerApiKeys[paymentChain.name], ); const events = await infoRetriever.getTransferEvents(); const proxyContractArtifact = EthInputDataPaymentDetector.getDeploymentInformation( diff --git a/packages/payment-detection/src/eth/proxy-info-retriever.ts b/packages/payment-detection/src/eth/proxy-info-retriever.ts index 789cefdd23..6b4ba92fb3 100644 --- a/packages/payment-detection/src/eth/proxy-info-retriever.ts +++ b/packages/payment-detection/src/eth/proxy-info-retriever.ts @@ -1,4 +1,4 @@ -import { PaymentTypes } from '@requestnetwork/types'; +import { ChainTypes, PaymentTypes } from '@requestnetwork/types'; import { IPaymentRetriever } from '../types'; import { BigNumber, ethers } from 'ethers'; import { parseLogArgs } from '../utils'; @@ -46,7 +46,7 @@ export class EthProxyInfoRetriever private proxyCreationBlockNumber: number, private toAddress: string, private eventName: PaymentTypes.EVENTS_NAMES, - private network: string, + private network: ChainTypes.IChain, ) { // Creates a local or default provider this.provider = getDefaultProvider(this.network); diff --git a/packages/payment-detection/src/fee-reference-based-detector.ts b/packages/payment-detection/src/fee-reference-based-detector.ts index 9ba9342ee0..a1cf8aff70 100644 --- a/packages/payment-detection/src/fee-reference-based-detector.ts +++ b/packages/payment-detection/src/fee-reference-based-detector.ts @@ -1,5 +1,5 @@ import { BigNumber } from 'ethers'; -import { ExtensionTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; import { ICurrencyManager } from '@requestnetwork/currency'; import { ReferenceBasedDetector } from './reference-based-detector'; import { generate8randomBytes } from '@requestnetwork/utils'; @@ -22,8 +22,9 @@ export abstract class FeeReferenceBasedDetector< paymentNetworkId: ExtensionTypes.PAYMENT_NETWORK_ID, extension: TExtension, currencyManager: ICurrencyManager, + allowedEcosystems: readonly ChainTypes.ECOSYSTEM[], ) { - super(paymentNetworkId, extension, currencyManager); + super(paymentNetworkId, extension, currencyManager, allowedEcosystems); } /** diff --git a/packages/payment-detection/src/native-token-detector.ts b/packages/payment-detection/src/native-token-detector.ts index 943d769160..1bdcd226dd 100644 --- a/packages/payment-detection/src/native-token-detector.ts +++ b/packages/payment-detection/src/native-token-detector.ts @@ -1,4 +1,4 @@ -import { CurrencyTypes, ExtensionTypes, PaymentTypes } from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes, PaymentTypes } from '@requestnetwork/types'; import { ReferenceBasedDetector } from './reference-based-detector'; import { NativeDetectorOptions } from './types'; @@ -10,7 +10,7 @@ export abstract class NativeTokenPaymentDetector extends ReferenceBasedDetector< ExtensionTypes.PnReferenceBased.IReferenceBased, PaymentTypes.IETHPaymentEventParameters > { - protected readonly network: CurrencyTypes.NearChainName | undefined; + protected readonly network: ChainTypes.INearChain | undefined; protected readonly getSubgraphClient: NativeDetectorOptions['getSubgraphClient']; protected constructor({ network, @@ -25,7 +25,7 @@ export abstract class NativeTokenPaymentDetector extends ReferenceBasedDetector< if (!extension) { throw new Error(`the ${extensionId} extension is not supported for the network ${network}`); } - super(extensionId, extension, currencyManager); + super(extensionId, extension, currencyManager, [ChainTypes.ECOSYSTEM.NEAR]); this.getSubgraphClient = getSubgraphClient; this.network = network; } diff --git a/packages/payment-detection/src/near/near-conversion-detector.ts b/packages/payment-detection/src/near/near-conversion-detector.ts index fadac7f48f..8d1dc014b4 100644 --- a/packages/payment-detection/src/near/near-conversion-detector.ts +++ b/packages/payment-detection/src/near/near-conversion-detector.ts @@ -1,10 +1,5 @@ -import { - CurrencyTypes, - ExtensionTypes, - PaymentTypes, - RequestLogicTypes, -} from '@requestnetwork/types'; -import { NearChains, UnsupportedCurrencyError } from '@requestnetwork/currency'; +import { ChainTypes, ExtensionTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { UnsupportedCurrencyError } from '@requestnetwork/currency'; import { NearConversionInfoRetriever } from './retrievers/near-conversion-info-retriever'; import { AnyToNativeDetector } from '../any-to-native-detector'; import { NetworkNotSupported } from '../balance-error'; @@ -31,12 +26,12 @@ export class NearConversionNativeTokenPaymentDetector extends AnyToNativeDetecto } public static getContractName = ( - chainName: CurrencyTypes.NearChainName, + chain: ChainTypes.INearChain, paymentNetworkVersion = '0.1.0', ): string => { const version = NearConversionNativeTokenPaymentDetector.getVersionOrThrow(paymentNetworkVersion); - const versionMap: Record> = { + const versionMap: Record> = { aurora: { '0.1.0': 'native.conversion.reqnetwork.near' }, near: { '0.1.0': 'native.conversion.reqnetwork.near' }, 'aurora-testnet': { @@ -46,11 +41,11 @@ export class NearConversionNativeTokenPaymentDetector extends AnyToNativeDetecto '0.1.0': 'native.conversion.reqnetwork.testnet', }, }; - if (versionMap[chainName]?.[version]) { - return versionMap[chainName][version]; + if (versionMap[chain.name]?.[version]) { + return versionMap[chain.name][version]; } throw new NetworkNotSupported( - `Unconfigured near-conversion-detector chain '${chainName}' and version '${version}'`, + `Unconfigured near-conversion-detector chain '${chain.name}' and version '${version}'`, ); }; @@ -69,7 +64,7 @@ export class NearConversionNativeTokenPaymentDetector extends AnyToNativeDetecto toAddress: string | undefined, paymentReference: string, requestCurrency: RequestLogicTypes.ICurrency, - paymentChain: CurrencyTypes.NearChainName, + paymentChain: ChainTypes.INearChain, paymentNetwork: ExtensionTypes.IState, ): Promise> { if (!toAddress) { @@ -104,13 +99,12 @@ export class NearConversionNativeTokenPaymentDetector extends AnyToNativeDetecto return transferEvents; } - protected getPaymentChain(request: RequestLogicTypes.IRequest): CurrencyTypes.NearChainName { + protected getPaymentChain(request: RequestLogicTypes.IRequest): ChainTypes.INearChain { const network = this.getPaymentExtension(request).values.network; if (!network) { throw Error(`request.extensions[${this.paymentNetworkId}].values.network must be defined`); } - NearChains.assertChainSupported(network); - return network; + return this.currencyManager.chainManager.fromName(network, [ChainTypes.ECOSYSTEM.NEAR]); } protected static getVersionOrThrow = (paymentNetworkVersion: string): string => { diff --git a/packages/payment-detection/src/near/near-detector.ts b/packages/payment-detection/src/near/near-detector.ts index 97311a46fd..5a547129ed 100644 --- a/packages/payment-detection/src/near/near-detector.ts +++ b/packages/payment-detection/src/near/near-detector.ts @@ -1,9 +1,4 @@ -import { - CurrencyTypes, - ExtensionTypes, - PaymentTypes, - RequestLogicTypes, -} from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; import { NearInfoRetriever } from './retrievers/near-info-retriever'; import { NativeTokenPaymentDetector } from '../native-token-detector'; import { NetworkNotSupported } from '../balance-error'; @@ -29,11 +24,11 @@ export class NearNativeTokenPaymentDetector extends NativeTokenPaymentDetector { } public static getContractName = ( - chainName: CurrencyTypes.NearChainName, + chainName: ChainTypes.INearChain, paymentNetworkVersion = '0.2.0', ): string => { const version = NearNativeTokenPaymentDetector.getVersionOrThrow(paymentNetworkVersion); - const versionMap: Record> = { + const versionMap: Record> = { aurora: { '0.1.0': 'requestnetwork.near', '0.2.0': 'requestnetwork.near' }, near: { '0.1.0': 'requestnetwork.near', '0.2.0': 'requestnetwork.near' }, 'aurora-testnet': { @@ -45,8 +40,8 @@ export class NearNativeTokenPaymentDetector extends NativeTokenPaymentDetector { '0.2.0': 'dev-1631521265288-35171138540673', }, }; - if (versionMap[chainName]?.[version]) { - return versionMap[chainName][version]; + if (versionMap[chainName.name]?.[version]) { + return versionMap[chainName.name][version]; } throw new NetworkNotSupported( `Unconfigured near-detector chain '${chainName}' and version '${version}'`, @@ -67,7 +62,7 @@ export class NearNativeTokenPaymentDetector extends NativeTokenPaymentDetector { address: string | undefined, paymentReference: string, _requestCurrency: RequestLogicTypes.ICurrency, - paymentChain: CurrencyTypes.NearChainName, + paymentChain: ChainTypes.INearChain, paymentNetwork: ExtensionTypes.IState, ): Promise> { if (!address) { diff --git a/packages/payment-detection/src/near/retrievers/near-conversion-info-retriever.ts b/packages/payment-detection/src/near/retrievers/near-conversion-info-retriever.ts index 3e4280bf0c..5315e58215 100644 --- a/packages/payment-detection/src/near/retrievers/near-conversion-info-retriever.ts +++ b/packages/payment-detection/src/near/retrievers/near-conversion-info-retriever.ts @@ -1,4 +1,4 @@ -import { CurrencyTypes, PaymentTypes } from '@requestnetwork/types'; +import { ChainTypes, PaymentTypes } from '@requestnetwork/types'; import { CurrencyDefinition } from '@requestnetwork/currency'; import { NearInfoRetriever, NearPaymentEvent } from './near-info-retriever'; import { TheGraphClient } from '../../thegraph'; @@ -13,7 +13,7 @@ export type TransferEventsParams = { /** The address of the payment proxy */ contractAddress: string; /** The chain to check for payment */ - paymentChain: CurrencyTypes.VMChainName; + paymentChain: ChainTypes.INearChain; /** Indicates if it is an address for payment or refund */ eventName: PaymentTypes.EVENTS_NAMES; /** The maximum span between the time the rate was fetched and the payment */ @@ -24,13 +24,7 @@ export type TransferEventsParams = { * Gets a list of transfer events for a set of Near payment details */ export class NearConversionInfoRetriever extends NearInfoRetriever { - /** - * @param paymentReference The reference to identify the payment - * @param toAddress Address to check - * @param eventName Indicate if it is an address for payment or refund - * @param network The id of network we want to check - */ - constructor(protected readonly client: TheGraphClient) { + constructor(protected readonly client: TheGraphClient) { super(client); } public async getTransferEvents( diff --git a/packages/payment-detection/src/near/retrievers/near-info-retriever.ts b/packages/payment-detection/src/near/retrievers/near-info-retriever.ts index 855bdc87f0..437e78da0b 100644 --- a/packages/payment-detection/src/near/retrievers/near-info-retriever.ts +++ b/packages/payment-detection/src/near/retrievers/near-info-retriever.ts @@ -1,4 +1,4 @@ -import { CurrencyTypes, PaymentTypes } from '@requestnetwork/types'; +import { ChainTypes, PaymentTypes } from '@requestnetwork/types'; import { TheGraphClient } from '../../thegraph'; import { GetNearPaymentsQuery } from 'payment-detection/src/thegraph/generated/graphql-near'; import { ITheGraphBaseInfoRetriever } from 'payment-detection/src/types'; @@ -16,7 +16,7 @@ export type TransferEventsParams = { /** The address of the payment proxy */ contractAddress: string; /** The chain to check for payment */ - paymentChain: CurrencyTypes.VMChainName; + paymentChain: ChainTypes.INearChain; /** Indicates if it is an address for payment or refund */ eventName: PaymentTypes.EVENTS_NAMES; /** The list of ERC20 tokens addresses accepted for payments and refunds. Set to `undefined` for payments in NEAR token. */ @@ -24,16 +24,10 @@ export type TransferEventsParams = { }; /** * Gets a list of transfer events for a set of Near payment details - * TheGraph-based etriever for ERC20 Fee Proxy and Native token payments. + * TheGraph-based retriever for ERC20 Fee Proxy and Native token payments. */ export class NearInfoRetriever implements ITheGraphBaseInfoRetriever { - /** - * @param paymentReference The reference to identify the payment - * @param toAddress Address to check - * @param eventName Indicate if it is an address for payment or refund - * - */ - constructor(protected readonly client: TheGraphClient) {} + constructor(protected readonly client: TheGraphClient) {} public async getTransferEvents( params: TransferEventsParams, diff --git a/packages/payment-detection/src/payment-network-factory.ts b/packages/payment-detection/src/payment-network-factory.ts index 9dc2a66a02..a7fc5d12b5 100644 --- a/packages/payment-detection/src/payment-network-factory.ts +++ b/packages/payment-detection/src/payment-network-factory.ts @@ -1,6 +1,6 @@ import { AdvancedLogicTypes, - CurrencyTypes, + ChainTypes, ExtensionTypes, PaymentTypes, RequestLogicTypes, @@ -27,11 +27,13 @@ import { NearConversionNativeTokenPaymentDetector, NearNativeTokenPaymentDetecto import { getPaymentNetworkExtension } from './utils'; import { defaultGetTheGraphClient } from './thegraph'; import { getDefaultProvider } from 'ethers'; +import { AdvancedLogic } from '@requestnetwork/advanced-logic'; const PN_ID = ExtensionTypes.PAYMENT_NETWORK_ID; /** Register the payment network by currency and type */ const supportedPaymentNetwork: ISupportedPaymentNetworkByCurrency = { + ISO4217: {}, BTC: { mainnet: { [PN_ID.BITCOIN_ADDRESS_BASED]: BtcMainnetAddressBasedDetector, @@ -47,13 +49,13 @@ const supportedPaymentNetwork: ISupportedPaymentNetworkByCurrency = { }, ERC20: { aurora: { - [PN_ID.ERC20_FEE_PROXY_CONTRACT]: ERC20FeeProxyPaymentDetector, + [PN_ID.ERC20_FEE_PROXY_CONTRACT]: ERC20FeeProxyPaymentDetector, }, 'aurora-testnet': { - [PN_ID.ERC20_FEE_PROXY_CONTRACT]: ERC20FeeProxyPaymentDetector, + [PN_ID.ERC20_FEE_PROXY_CONTRACT]: ERC20FeeProxyPaymentDetector, }, 'near-testnet': { - [PN_ID.ERC20_FEE_PROXY_CONTRACT]: ERC20FeeProxyPaymentDetector, + [PN_ID.ERC20_FEE_PROXY_CONTRACT]: ERC20FeeProxyPaymentDetector, }, '*': { @@ -117,21 +119,24 @@ export class PaymentNetworkFactory { * * @param paymentNetworkId the ID of the payment network to instantiate * @param currencyType the currency type of the request - * @param paymentChain Different from request.currency.network for on-chain conversion payment networks (any-to-something) + * @param paymentChainName Different from request.currency.network for on-chain conversion payment networks (any-to-something) * @returns the module to handle the payment network */ public createPaymentNetwork( paymentNetworkId: PN_ID, currencyType: RequestLogicTypes.CURRENCY, - paymentChain?: CurrencyTypes.ChainName, + paymentChainName?: string, paymentNetworkVersion?: string, ): PaymentTypes.IPaymentNetwork< PaymentTypes.GenericEventParameters, Extract['parameters'] > { - const network = paymentChain ?? 'mainnet'; + const paymentChain = this.currencyManager.chainManager.fromName( + paymentChainName ?? 'mainnet', + AdvancedLogic.supportedEcosystemsForExtension[paymentNetworkId], + ); const currencyPaymentMap = - supportedPaymentNetwork[currencyType]?.[network] || + supportedPaymentNetwork[currencyType]?.[paymentChain.name] || supportedPaymentNetwork[currencyType]?.['*'] || {}; const paymentNetworkMap = { @@ -143,12 +148,12 @@ export class PaymentNetworkFactory { if (!detectorClass) { throw new Error( - `the payment network id: ${paymentNetworkId} is not supported for the currency: ${currencyType} on network ${network}`, + `the extension id: ${paymentNetworkId} is not supported for the currency: ${currencyType} on network ${paymentChain.name}`, ); } const detector = new detectorClass({ - network, + network: paymentChain, advancedLogic: this.advancedLogic, currencyManager: this.currencyManager, ...this.options, @@ -157,7 +162,7 @@ export class PaymentNetworkFactory { if (detector.extension && 'getDeploymentInformation' in detectorClass) { // this throws when the contract isn't deployed and was mandatory for payment detection (detectorClass as ContractBasedDetector).getDeploymentInformation( - network as CurrencyTypes.VMChainName, + paymentChain as ChainTypes.IVmChain, paymentNetworkVersion || detector.extension.currentVersion, ); } diff --git a/packages/payment-detection/src/reference-based-detector.ts b/packages/payment-detection/src/reference-based-detector.ts index 433fd52700..cb6f82c859 100644 --- a/packages/payment-detection/src/reference-based-detector.ts +++ b/packages/payment-detection/src/reference-based-detector.ts @@ -1,5 +1,5 @@ import { - CurrencyTypes, + ChainTypes, ExtensionTypes, PaymentTypes, RequestLogicTypes, @@ -25,11 +25,13 @@ export abstract class ReferenceBasedDetector< * @param paymentNetworkId Example : ExtensionTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA * @param extension The advanced logic payment network extension, reference based * @param currencyManager The currency manager + * @param allowedEcosystems The ecosystems that this payment detector supports */ protected constructor( paymentNetworkId: ExtensionTypes.PAYMENT_NETWORK_ID, extension: TExtension, protected readonly currencyManager: ICurrencyManager, + protected readonly allowedEcosystems: readonly ChainTypes.ECOSYSTEM[], ) { super(paymentNetworkId, extension); if (!TypesUtils.isPaymentNetworkId(paymentNetworkId)) { @@ -147,7 +149,7 @@ export abstract class ReferenceBasedDetector< address: string | undefined, paymentReference: string, requestCurrency: RequestLogicTypes.ICurrency, - paymentChain: CurrencyTypes.ChainName, + paymentChain: ChainTypes.IChain, paymentNetwork: TExtension extends ExtensionTypes.IExtension ? ExtensionTypes.IState : never, @@ -157,12 +159,12 @@ export abstract class ReferenceBasedDetector< * Get the network of the payment * @returns The network of payment */ - protected getPaymentChain(request: RequestLogicTypes.IRequest): CurrencyTypes.ChainName { + protected getPaymentChain(request: RequestLogicTypes.IRequest): ChainTypes.IChain { const network = request.currency.network; if (!network) { throw Error(`request.currency.network must be defined for ${this.paymentNetworkId}`); } - return network; + return this.currencyManager.chainManager.fromName(network, this.allowedEcosystems); } protected getPaymentReference(request: RequestLogicTypes.IRequest): string { diff --git a/packages/payment-detection/src/thegraph/client.ts b/packages/payment-detection/src/thegraph/client.ts index 944ce13305..df7f8ac18e 100644 --- a/packages/payment-detection/src/thegraph/client.ts +++ b/packages/payment-detection/src/thegraph/client.ts @@ -1,10 +1,9 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { CurrencyTypes } from '@requestnetwork/types'; -import { NearChains } from '@requestnetwork/currency'; import { GraphQLClient } from 'graphql-request'; -import { Block_Height, Maybe, getSdk } from './generated/graphql'; +import { Block_Height, getSdk, Maybe } from './generated/graphql'; import { getSdk as getNearSdk } from './generated/graphql-near'; import { RequestConfig } from 'graphql-request/src/types'; +import { ChainTypes } from '@requestnetwork/types'; const HOSTED_THE_GRAPH_URL = 'https://api.thegraph.com/subgraphs/name/requestnetwork/request-payments-'; @@ -27,8 +26,8 @@ const THE_GRAPH_URL_STUDIO_ZKSYNC = * * @type TGraphClientVariant: null if no variant, 'near' if native token payments detection on Near */ -export type TheGraphClient = - (TChain extends CurrencyTypes.NearChainName +export type TheGraphClient = + (TChain extends ChainTypes.INearChain ? ReturnType : ReturnType) & { options?: TheGraphQueryOptions; @@ -64,23 +63,25 @@ const extractClientOptions = ( return [clientOptions, queryOptions]; }; -export const getTheGraphClient = (network: string, url: string, options?: TheGraphClientOptions) => - NearChains.isChainSupported(network) +export const getTheGraphClient = ( + chain: ChainTypes.IChain, + url: string, + options?: TheGraphClientOptions, +) => + chain.ecosystem === ChainTypes.ECOSYSTEM.NEAR ? getTheGraphNearClient(url, options) : getTheGraphEvmClient(url, options); export const getTheGraphEvmClient = (url: string, options?: TheGraphClientOptions) => { const [clientOptions, queryOptions] = extractClientOptions(url, options); - const sdk: TheGraphClient = getSdk( - new GraphQLClient(url, clientOptions), - ); + const sdk: TheGraphClient = getSdk(new GraphQLClient(url, clientOptions)); sdk.options = queryOptions; return sdk; }; export const getTheGraphNearClient = (url: string, options?: TheGraphClientOptions) => { const [clientOptions, queryOptions] = extractClientOptions(url, options); - const sdk: TheGraphClient = getNearSdk( + const sdk: TheGraphClient = getNearSdk( new GraphQLClient(url, clientOptions), ); sdk.options = queryOptions; @@ -88,18 +89,21 @@ export const getTheGraphNearClient = (url: string, options?: TheGraphClientOptio }; export const defaultGetTheGraphClient = ( - network: CurrencyTypes.ChainName, + chain: ChainTypes.IChain, options?: TheGraphClientOptions, ) => { - return network === 'private' + return chain.name === 'private' ? undefined - : NearChains.isChainSupported(network) - ? getTheGraphNearClient(`${HOSTED_THE_GRAPH_URL}${network.replace('aurora', 'near')}`, options) - : network === 'mantle' + : chain.ecosystem === ChainTypes.ECOSYSTEM.NEAR + ? getTheGraphNearClient( + `${HOSTED_THE_GRAPH_URL}${chain.name.replace('aurora', 'near')}`, + options, + ) + : chain.name === 'mantle' ? getTheGraphEvmClient(THE_GRAPH_URL_MANTLE, options) - : network === 'mantle-testnet' + : chain.name === 'mantle-testnet' ? getTheGraphEvmClient(THE_GRAPH_URL_MANTLE_TESTNET, options) - : network === 'zksyncera' + : chain.name === 'zksyncera' ? getTheGraphEvmClient(THE_GRAPH_URL_STUDIO_ZKSYNC, options) - : getTheGraphEvmClient(`${HOSTED_THE_GRAPH_URL}${network}`, options); + : getTheGraphEvmClient(`${HOSTED_THE_GRAPH_URL}${chain}`, options); }; diff --git a/packages/payment-detection/src/thegraph/superfluid.ts b/packages/payment-detection/src/thegraph/superfluid.ts index e869dd7366..539337a11e 100644 --- a/packages/payment-detection/src/thegraph/superfluid.ts +++ b/packages/payment-detection/src/thegraph/superfluid.ts @@ -1,6 +1,7 @@ import { GraphQLClient } from 'graphql-request'; import { getSdk } from './generated/graphql-superfluid'; import { RequestConfig } from 'graphql-request/src/types'; +import { ChainTypes } from '@requestnetwork/types'; const BASE_URL = `https://api.thegraph.com`; const NETWORK_TO_URL: Record = { @@ -20,19 +21,19 @@ export type TheGraphClientOptions = RequestConfig & { }; export const getTheGraphSuperfluidClient = ( - network: string, + chain: ChainTypes.IEvmChain, options?: TheGraphClientOptions, ): TheGraphSuperfluidClient => { const { baseUrl: _baseUrl, ...clientOptions } = options ?? {}; - const baseUrl = _baseUrl || network === 'private' ? 'http://localhost:8000' : BASE_URL; + const baseUrl = _baseUrl || chain.name === 'private' ? 'http://localhost:8000' : BASE_URL; // Note: it is also possible to use the IPFS hash of the subgraph // eg. /subgraphs/id/QmcCaSkefrmhe4xQj6Y6BBbHiFkbrn6UGDEBUWER7nt399 // which is a better security but would require an update of the // library each time the subgraph is updated, which isn't ideal // for early testing. const url = `${baseUrl}/subgraphs/name/superfluid-finance/protocol-v1-${ - NETWORK_TO_URL[network] || network + NETWORK_TO_URL[chain.name] || chain.name }`; return getSdk(new GraphQLClient(url, clientOptions)); }; diff --git a/packages/payment-detection/src/types.ts b/packages/payment-detection/src/types.ts index 2ffa154f38..ed39a71ac7 100644 --- a/packages/payment-detection/src/types.ts +++ b/packages/payment-detection/src/types.ts @@ -1,8 +1,9 @@ import { AdvancedLogicTypes, - CurrencyTypes, + ChainTypes, ExtensionTypes, PaymentTypes, + RequestLogicTypes, } from '@requestnetwork/types'; import { PaymentDetectorBase } from './payment-detector-base'; import { GetDeploymentInformation } from './utils'; @@ -25,7 +26,7 @@ export type TransferEventsParams = { /** The address of the payment proxy */ contractAddress: string; /** The chain to check for payment */ - paymentChain: CurrencyTypes.VMChainName; + paymentChain: ChainTypes.IVmChain; /** Indicates if it is an address for payment or refund */ eventName: PaymentTypes.EVENTS_NAMES; /** The list of ERC20 tokens addresses accepted for payments and refunds OR undefined for native tokens (e.g. ETH) */ @@ -87,28 +88,26 @@ export interface ISupportedPaymentNetworkByNetwork< } /** Object interface to list the payment network id and its module by currency */ -export interface ISupportedPaymentNetworkByCurrency< +export type ISupportedPaymentNetworkByCurrency< TPaymentEventParameters extends PaymentTypes.GenericEventParameters = PaymentTypes.GenericEventParameters, -> { - [currency: string]: ISupportedPaymentNetworkByNetwork; -} +> = { + [key in RequestLogicTypes.CURRENCY]: ISupportedPaymentNetworkByNetwork; +}; -export type TGetSubGraphClient = ( - network: CurrencyTypes.ChainName, -) => TChain extends CurrencyTypes.VMChainName ? TheGraphClient | undefined : undefined; +export type TGetSubGraphClient = ( + network: ChainTypes.IChain, +) => TChain extends ChainTypes.IVmChain ? TheGraphClient | undefined : undefined; -export type PaymentNetworkOptions< - TChain extends CurrencyTypes.ChainName = CurrencyTypes.ChainName, -> = { +export type PaymentNetworkOptions = { /** override default bitcoin detection provider */ bitcoinDetectionProvider?: PaymentTypes.IBitcoinDetectionProvider; /** the explorer API (e.g. Etherscan) api keys, for PNs that rely on it. Record by network name */ - explorerApiKeys: Partial>; + explorerApiKeys: Partial>; /** override the default Subgraph for payment detection (EVM, Near) */ getSubgraphClient: TGetSubGraphClient; /** override the default RPC provider (EVM) */ - getRpcProvider: (network: CurrencyTypes.ChainName) => providers.Provider; + getRpcProvider: (network: string) => providers.Provider; }; export type ReferenceBasedDetectorOptions = { @@ -117,7 +116,7 @@ export type ReferenceBasedDetectorOptions = { }; export type NativeDetectorOptions = ReferenceBasedDetectorOptions & { - network: CurrencyTypes.NearChainName; + network: ChainTypes.INearChain; /** override the default Subgraph for payment detection (EVM, Near) */ - getSubgraphClient: TGetSubGraphClient; + getSubgraphClient: TGetSubGraphClient; }; diff --git a/packages/payment-detection/src/utils.ts b/packages/payment-detection/src/utils.ts index ba76116e96..40464b7467 100644 --- a/packages/payment-detection/src/utils.ts +++ b/packages/payment-detection/src/utils.ts @@ -1,10 +1,5 @@ import { CurrencyDefinition, isValidNearAddress } from '@requestnetwork/currency'; -import { - CurrencyTypes, - ExtensionTypes, - PaymentTypes, - RequestLogicTypes, -} from '@requestnetwork/types'; +import { ChainTypes, ExtensionTypes, PaymentTypes, RequestLogicTypes } from '@requestnetwork/types'; import { BigNumber, BigNumberish, Contract, errors, logger } from 'ethers'; import { getAddress, keccak256, LogDescription } from 'ethers/lib/utils'; import { ContractArtifact, DeploymentInformation } from '@requestnetwork/smart-contracts'; @@ -62,7 +57,7 @@ const getChainlinkPaddingSize = ({ export type DeploymentInformationWithVersion = DeploymentInformation & { contractVersion: string }; export type GetDeploymentInformation = ( - network: CurrencyTypes.VMChainName, + network: ChainTypes.IVmChain, paymentNetworkVersion: string, ) => TAllowUndefined extends false ? DeploymentInformationWithVersion @@ -87,15 +82,17 @@ export const makeGetDeploymentInformation = < `No contract matches payment network version: ${paymentNetworkVersion}.`, ); } - const info = artifact.getOptionalDeploymentInformation(network, contractVersion); + const info = artifact.getOptionalDeploymentInformation(network.name, contractVersion); if (!info) { if (!allowUndefined) { - if (artifact.getOptionalDeploymentInformation(network)) { + if (artifact.getOptionalDeploymentInformation(network.name)) { throw new VersionNotSupported( `Payment network version not supported: ${paymentNetworkVersion}`, ); } - throw new NetworkNotSupported(`Network not supported for this payment network: ${network}`); + throw new NetworkNotSupported( + `Network not supported for this payment network: ${network.name}`, + ); } return null as ReturnType>; } diff --git a/packages/payment-detection/test/any/any-to-erc20-proxy-contract.test.ts b/packages/payment-detection/test/any/any-to-erc20-proxy-contract.test.ts index 2a4c976137..d77048a12d 100644 --- a/packages/payment-detection/test/any/any-to-erc20-proxy-contract.test.ts +++ b/packages/payment-detection/test/any/any-to-erc20-proxy-contract.test.ts @@ -56,7 +56,7 @@ describe('api/any/conversion-fee-proxy-contract', () => { jest.clearAllMocks(); }); - const testSuite = (network: CurrencyTypes.EvmChainName) => { + const testSuite = (network: ChainTypes.IEvmChain) => { it(`can createExtensionsDataForCreation on ${network}`, async () => { await anyToErc20Proxy.createExtensionsDataForCreation({ paymentAddress: 'ethereum address', diff --git a/packages/payment-detection/test/erc20/fee-proxy-contract.test.ts b/packages/payment-detection/test/erc20/fee-proxy-contract.test.ts index 255e3e635f..1af1a5adb1 100644 --- a/packages/payment-detection/test/erc20/fee-proxy-contract.test.ts +++ b/packages/payment-detection/test/erc20/fee-proxy-contract.test.ts @@ -10,7 +10,7 @@ import { CurrencyManager } from '@requestnetwork/currency'; import { ERC20FeeProxyPaymentDetector } from '../../src/erc20/fee-proxy-contract'; import { mockAdvancedLogicBase } from '../utils'; -let erc20FeeProxyContract: ERC20FeeProxyPaymentDetector; +let erc20FeeProxyContract: ERC20FeeProxyPaymentDetector; const createAddPaymentAddressAction = jest.fn(); const createAddRefundAddressAction = jest.fn(); @@ -473,7 +473,7 @@ describe('api/erc20/fee-proxy-contract', () => { describe('on Near', () => { beforeEach(() => { // Same Detector, but instanciated with a Near network and a mocked Near graph client - erc20FeeProxyContract = new ERC20FeeProxyPaymentDetector({ + erc20FeeProxyContract = new ERC20FeeProxyPaymentDetector({ advancedLogic: mockAdvancedLogic, currencyManager, network: 'aurora-testnet', diff --git a/packages/payment-detection/test/erc20/transferable-receivable.test.ts b/packages/payment-detection/test/erc20/transferable-receivable.test.ts index c0bf1f4dab..a3a3f872a9 100644 --- a/packages/payment-detection/test/erc20/transferable-receivable.test.ts +++ b/packages/payment-detection/test/erc20/transferable-receivable.test.ts @@ -379,7 +379,7 @@ describe('api/erc20/transferable-receivable-contract', () => { txHash: '0x3e2d6cc2534b1d340ba2954f34e6cc819d6da64ff76863ea89c6d34b15d13c97', from: '0x186e7fe6c34ea0eca7f9c2fd29651fc0443e3f29', to: paymentAddress, - network: 'rinkeby' as CurrencyTypes.EvmChainName, + network: 'rinkeby', salt: '0ee84db293a752c6', amount: '30000000000000', requestId: '0188791633ff0ec72a7dbdefb886d2db6cccfa98287320839c2f173c7a4e3ce7e1', diff --git a/packages/payment-detection/test/erc777/superfluid-detector.test.ts b/packages/payment-detection/test/erc777/superfluid-detector.test.ts index 33238f026e..01cbdd957f 100644 --- a/packages/payment-detection/test/erc777/superfluid-detector.test.ts +++ b/packages/payment-detection/test/erc777/superfluid-detector.test.ts @@ -36,7 +36,7 @@ const mockAdvancedLogic: AdvancedLogicTypes.IAdvancedLogic = { const baseRequestData = { creator: { type: IdentityTypes.TYPE.ETHEREUM_ADDRESS, value: '0x2' }, currency: { - network: 'private' as CurrencyTypes.EvmChainName, + network: 'private', type: RequestLogicTypes.CURRENCY.ERC20, value: '0x9FBDa871d559710256a2502A2517b794B482Db40', // local ERC20 token }, diff --git a/packages/payment-detection/test/near/near-native-conversion.test.ts b/packages/payment-detection/test/near/near-native-conversion.test.ts index 5f4fcb54e1..c291acb10d 100644 --- a/packages/payment-detection/test/near/near-native-conversion.test.ts +++ b/packages/payment-detection/test/near/near-native-conversion.test.ts @@ -69,7 +69,7 @@ const client = { GetAnyToNativePayments: jest.fn().mockImplementation(() => ({ payments: [graphPaymentEvent], })), -} as any as TheGraphClient; +} as any as TheGraphClient; const infoRetriever = new NearConversionInfoRetriever(client); const mockedGetSubgraphClient = jest.fn().mockImplementation(() => client); diff --git a/packages/payment-detection/test/near/near-native.test.ts b/packages/payment-detection/test/near/near-native.test.ts index 31c14b20ec..0c0ba9f5db 100644 --- a/packages/payment-detection/test/near/near-native.test.ts +++ b/packages/payment-detection/test/near/near-native.test.ts @@ -49,7 +49,7 @@ const client = { GetNearPayments: jest.fn().mockImplementation(() => ({ payments: [graphPaymentEvent], })), -} as any as TheGraphClient; +} as any as TheGraphClient; const mockedGetSubgraphClient = jest.fn().mockImplementation(() => client); const infoRetriever = new NearInfoRetriever(client); diff --git a/packages/payment-detection/test/payment-network-factory.test.ts b/packages/payment-detection/test/payment-network-factory.test.ts index acd60a1da4..dee4342fca 100644 --- a/packages/payment-detection/test/payment-network-factory.test.ts +++ b/packages/payment-detection/test/payment-network-factory.test.ts @@ -2,12 +2,14 @@ import { ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; import { CurrencyManager } from '@requestnetwork/currency'; import { BtcMainnetAddressBasedDetector } from '../src/btc'; import { + AnyToERC20PaymentDetector, DeclarativePaymentDetector, EthInputDataPaymentDetector, PaymentNetworkFactory, } from '../src'; import { AdvancedLogic } from '@requestnetwork/advanced-logic'; import { ERC20FeeProxyPaymentDetector } from '../src/erc20/fee-proxy-contract'; +import { IRequest } from '@requestnetwork/types/src/request-logic-types'; const currencyManager = CurrencyManager.getDefault(); const advancedLogic = new AdvancedLogic(currencyManager); @@ -81,6 +83,30 @@ describe('api/payment-network/payment-network-factory', () => { BtcMainnetAddressBasedDetector, ); }); + + it('can getPaymentNetworkFromRequest for payment with conversion', async () => { + const request: any = { + currency: { + type: RequestLogicTypes.CURRENCY.ISO4217, + value: 'USD', + }, + extensions: { + [ExtensionTypes.PAYMENT_NETWORK_ID.ANY_TO_ERC20_PROXY as string]: { + id: ExtensionTypes.PAYMENT_NETWORK_ID.ANY_TO_ERC20_PROXY, + type: ExtensionTypes.TYPE.PAYMENT_NETWORK, + values: { + network: 'mainnet', + }, + }, + }, + }; + + // 'createPayment createPaymentNetwork' + expect(paymentNetworkFactory.getPaymentNetworkFromRequest(request)).toBeInstanceOf( + AnyToERC20PaymentDetector, + ); + }); + it('can getPaymentNetworkFromRequest with a request without payment network', async () => { const request: any = { currency: { diff --git a/packages/payment-processor/package.json b/packages/payment-processor/package.json index ea395525be..2b323b3a39 100644 --- a/packages/payment-processor/package.json +++ b/packages/payment-processor/package.json @@ -40,6 +40,7 @@ }, "dependencies": { "@openzeppelin/contracts": "4.9.5", + "@requestnetwork/chain": "0.13.0", "@requestnetwork/currency": "0.13.0", "@requestnetwork/payment-detection": "0.40.0", "@requestnetwork/smart-contracts": "0.33.0", diff --git a/packages/payment-processor/src/payment/any-to-eth-proxy.ts b/packages/payment-processor/src/payment/any-to-eth-proxy.ts index 6c311160d6..cb21d33dee 100644 --- a/packages/payment-processor/src/payment/any-to-eth-proxy.ts +++ b/packages/payment-processor/src/payment/any-to-eth-proxy.ts @@ -10,7 +10,13 @@ import { EthConversionProxy__factory } from '@requestnetwork/smart-contracts/typ import { ClientTypes, RequestLogicTypes } from '@requestnetwork/types'; import { ITransactionOverrides } from './transaction-overrides'; -import { getAmountToPay, getProvider, getRequestPaymentValues, getSigner } from './utils'; +import { + ensureEvmChain, + getAmountToPay, + getProvider, + getRequestPaymentValues, + getSigner, +} from './utils'; import { padAmountForChainlink } from '@requestnetwork/payment-detection'; import { IPreparedTransaction } from './prepared-transaction'; import { IConversionPaymentSettings } from './index'; @@ -117,10 +123,7 @@ export function getConversionPathForEthRequest( } const { network } = getRequestPaymentValues(request); - - if (!network) { - throw new Error(`missing network`); - } + ensureEvmChain(network); const paymentCurrency = currencyManager.getNativeCurrency( RequestLogicTypes.CURRENCY.ETH, diff --git a/packages/payment-processor/src/payment/batch-conversion-proxy.ts b/packages/payment-processor/src/payment/batch-conversion-proxy.ts index ea77ce0d6d..1345b0dc01 100644 --- a/packages/payment-processor/src/payment/batch-conversion-proxy.ts +++ b/packages/payment-processor/src/payment/batch-conversion-proxy.ts @@ -2,8 +2,8 @@ import { ContractTransaction, Signer, providers, BigNumber, constants } from 'et import { batchConversionPaymentsArtifact } from '@requestnetwork/smart-contracts'; import { BatchConversionPayments__factory } from '@requestnetwork/smart-contracts/types'; import { + ChainTypes, ClientTypes, - CurrencyTypes, ExtensionTypes, PaymentTypes, RequestLogicTypes, @@ -328,7 +328,7 @@ const getBatchTxValue = (enrichedRequests: EnrichedRequest[]) => { */ function getUSDPathsForFeeLimit( requestDetails: PaymentTypes.RequestDetail[], - network: string, + network: ChainTypes.IVmChain, skipFeeUSDLimit: boolean, currencyManager: ICurrencyManager, hasNativePayment: boolean, @@ -373,10 +373,10 @@ function getUSDPathsForFeeLimit( * @returns */ function getBatchDeploymentInformation( - network: CurrencyTypes.EvmChainName, + network: ChainTypes.IVmChain, version?: string, ): { address: string } | null { - return { address: batchConversionPaymentsArtifact.getAddress(network, version) }; + return { address: batchConversionPaymentsArtifact.getAddress(network.name, version) }; } /** diff --git a/packages/payment-processor/src/payment/batch-proxy.ts b/packages/payment-processor/src/payment/batch-proxy.ts index 7b3d750a57..fbcf4265db 100644 --- a/packages/payment-processor/src/payment/batch-proxy.ts +++ b/packages/payment-processor/src/payment/batch-proxy.ts @@ -1,4 +1,4 @@ -import { ContractTransaction, Signer, providers, constants, BigNumber } from 'ethers'; +import { BigNumber, constants, ContractTransaction, providers, Signer } from 'ethers'; import { batchPaymentsArtifact } from '@requestnetwork/smart-contracts'; import { BatchPayments__factory } from '@requestnetwork/smart-contracts/types'; import { ClientTypes } from '@requestnetwork/types'; @@ -6,6 +6,7 @@ import { getPaymentNetworkExtension } from '@requestnetwork/payment-detection'; import { ITransactionOverrides } from './transaction-overrides'; import { comparePnTypeAndVersion, + ensureEvmChain, getAmountToPay, getProvider, getRequestPaymentValues, @@ -15,7 +16,6 @@ import { import { validateEthFeeProxyRequest } from './eth-fee-proxy'; import { IPreparedTransaction } from './prepared-transaction'; import { checkErc20Allowance, encodeApproveAnyErc20 } from './erc20'; -import { EvmChains } from '@requestnetwork/currency'; /** * ERC20 Batch Proxy payment details: @@ -224,7 +224,7 @@ export function getBatchProxyAddress(request: ClientTypes.IRequestData, version: if (!network) { throw new Error('No currency network'); } - EvmChains.assertChainSupported(network); + ensureEvmChain(network); const proxyAddress = batchPaymentsArtifact.getAddress(network, version); diff --git a/packages/payment-processor/src/payment/erc20-escrow-payment.ts b/packages/payment-processor/src/payment/erc20-escrow-payment.ts index 4652b307be..ec049fef31 100644 --- a/packages/payment-processor/src/payment/erc20-escrow-payment.ts +++ b/packages/payment-processor/src/payment/erc20-escrow-payment.ts @@ -4,6 +4,7 @@ import { erc20EscrowToPayArtifact } from '@requestnetwork/smart-contracts'; import { ERC20EscrowToPay__factory } from '@requestnetwork/smart-contracts/types'; import { ClientTypes, ExtensionTypes } from '@requestnetwork/types'; import { + ensureEvmChain, getAmountToPay, getProvider, getRequestPaymentValues, @@ -13,7 +14,6 @@ import { import { ITransactionOverrides } from './transaction-overrides'; import { encodeApproveAnyErc20 } from './erc20'; import { IPreparedTransaction } from './prepared-transaction'; -import { EvmChains } from '@requestnetwork/currency'; /** * Returns the EscrowToPay contract address corresponding to the request payment network @@ -21,7 +21,7 @@ import { EvmChains } from '@requestnetwork/currency'; */ function getContractAddress(request: ClientTypes.IRequestData) { const { network } = request.currencyInfo; - EvmChains.assertChainSupported(network!); + ensureEvmChain(network); return erc20EscrowToPayArtifact.getAddress(network); } diff --git a/packages/payment-processor/src/payment/erc20-fee-proxy.ts b/packages/payment-processor/src/payment/erc20-fee-proxy.ts index 9d21d4b45e..efcb3b8739 100644 --- a/packages/payment-processor/src/payment/erc20-fee-proxy.ts +++ b/packages/payment-processor/src/payment/erc20-fee-proxy.ts @@ -1,13 +1,13 @@ -import { constants, ContractTransaction, Signer, BigNumberish, providers, BigNumber } from 'ethers'; +import { BigNumber, BigNumberish, constants, ContractTransaction, providers, Signer } from 'ethers'; import { erc20FeeProxyArtifact } from '@requestnetwork/smart-contracts'; import { ERC20FeeProxy__factory } from '@requestnetwork/smart-contracts/types'; import { ClientTypes, ExtensionTypes } from '@requestnetwork/types'; import { getPaymentNetworkExtension } from '@requestnetwork/payment-detection'; -import { EvmChains } from '@requestnetwork/currency'; import { ITransactionOverrides } from './transaction-overrides'; import { + ensureEvmChain, getAmountToPay, getProvider, getRequestPaymentValues, @@ -82,7 +82,7 @@ export function _getErc20FeeProxyPaymentUrl( validateRequest(request, ExtensionTypes.PAYMENT_NETWORK_ID.ERC20_FEE_PROXY_CONTRACT); const { paymentReference, paymentAddress, feeAddress, feeAmount, version, network } = getRequestPaymentValues(request); - EvmChains.assertChainSupported(network!); + ensureEvmChain(network); const contractAddress = erc20FeeProxyArtifact.getAddress(network, version); const amountToPay = getAmountToPay(request, amount); const feeToPay = feeAmountOverride || BigNumber.from(feeAmount || 0); @@ -103,9 +103,8 @@ export function prepareErc20FeeProxyPaymentTransaction( feeAmountOverride?: BigNumberish, ): IPreparedTransaction { validateErc20FeeProxyRequest(request, amount, feeAmountOverride); - const { network } = request.currencyInfo; - EvmChains.assertChainSupported(network!); + ensureEvmChain(network); const encodedTx = encodePayErc20FeeRequest(request, amount, feeAmountOverride); const pn = getPaymentNetworkExtension(request); const proxyAddress = erc20FeeProxyArtifact.getAddress(network, pn?.version); diff --git a/packages/payment-processor/src/payment/index.ts b/packages/payment-processor/src/payment/index.ts index 342e2213de..7ff2f7c83e 100644 --- a/packages/payment-processor/src/payment/index.ts +++ b/packages/payment-processor/src/payment/index.ts @@ -1,6 +1,6 @@ import { ContractTransaction, Signer, BigNumber, BigNumberish, providers } from 'ethers'; -import { ClientTypes, ExtensionTypes, TypesUtils } from '@requestnetwork/types'; +import { ChainTypes, ClientTypes, ExtensionTypes, TypesUtils } from '@requestnetwork/types'; import { getBtcPaymentUrl } from './btc-address-based'; import { _getErc20PaymentUrl, getAnyErc20Balance } from './erc20'; @@ -16,7 +16,7 @@ import { payAnyToErc20ProxyRequest } from './any-to-erc20-proxy'; import { payAnyToEthProxyRequest } from './any-to-eth-proxy'; import { WalletConnection } from 'near-api-js'; import { isNearAccountSolvent } from './utils-near'; -import { ICurrencyManager, NearChains } from '@requestnetwork/currency'; +import { CurrencyManager, ICurrencyManager } from '@requestnetwork/currency'; import { encodeRequestErc20Approval } from './encoder-approval'; import { encodeRequestPayment } from './encoder-payment'; import { IPreparedTransaction } from './prepared-transaction'; @@ -260,7 +260,12 @@ export async function isSolvent({ needsGas?: boolean; }): Promise { // Near case - if (NearChains.isChainSupported(currency.network) && providerOptions?.nearWalletConnection) { + if ( + CurrencyManager.getDefault().chainManager.ecosystems[ + ChainTypes.ECOSYSTEM.NEAR + ].isChainSupported(currency.network) && + providerOptions?.nearWalletConnection + ) { return isNearAccountSolvent(amount, providerOptions.nearWalletConnection, currency); } // Main case (EVM) @@ -343,7 +348,12 @@ export function _getPaymentUrl(request: ClientTypes.IRequestData, amount?: BigNu // FIXME: should also compare the signer.chainId with the request.currencyInfo.network... const throwIfNotWeb3 = (request: ClientTypes.IRequestData) => { // FIXME: there is a near web3Provider equivalent: https://github.com/aurora-is-near/near-web3-provider - if (request.currencyInfo?.network && NearChains.isChainSupported(request.currencyInfo.network)) { + if ( + request.currencyInfo?.network && + CurrencyManager.getDefault().chainManager.ecosystems[ + ChainTypes.ECOSYSTEM.NEAR + ].isChainSupported(request.currencyInfo.network) + ) { throw new UnsupportedPaymentChain(request.currencyInfo.network); } }; diff --git a/packages/payment-processor/src/payment/near-conversion.ts b/packages/payment-processor/src/payment/near-conversion.ts index a08b5c149f..c2ad64db8c 100644 --- a/packages/payment-processor/src/payment/near-conversion.ts +++ b/packages/payment-processor/src/payment/near-conversion.ts @@ -1,17 +1,18 @@ import { BigNumberish } from 'ethers'; import { WalletConnection } from 'near-api-js'; -import { ClientTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ChainTypes, ClientTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; import { - getRequestPaymentValues, - validateRequest, getAmountToPay, getPaymentExtensionVersion, + getRequestPaymentValues, + validateRequest, } from './utils'; import { INearTransactionCallback, processNearPaymentWithConversion } from './utils-near'; import { IConversionPaymentSettings } from '.'; -import { CurrencyManager, NearChains, UnsupportedCurrencyError } from '@requestnetwork/currency'; +import { CurrencyManager, UnsupportedCurrencyError } from '@requestnetwork/currency'; +import { ChainManager } from '@requestnetwork/chain'; /** * Processes the transaction to pay a request in NEAR with on-chain conversion. @@ -41,17 +42,20 @@ export async function payNearConversionRequest( throw new Error('Cannot pay without a paymentReference'); } - if (!network || !NearChains.isChainSupported(network)) { + if ( + !network || + !ChainManager.current().ecosystems[ChainTypes.ECOSYSTEM.NEAR].isChainSupported(network) + ) { throw new Error('Should be a Near network'); } - NearChains.assertChainSupported(network); + const chain = ChainManager.current().fromName(network, [ChainTypes.ECOSYSTEM.NEAR]); const amountToPay = getAmountToPay(request, amount).toString(); const version = getPaymentExtensionVersion(request); return processNearPaymentWithConversion( walletConnection, - network, + chain, amountToPay, paymentAddress, paymentReference, diff --git a/packages/payment-processor/src/payment/near-fungible.ts b/packages/payment-processor/src/payment/near-fungible.ts index 938ca65285..14c8165884 100644 --- a/packages/payment-processor/src/payment/near-fungible.ts +++ b/packages/payment-processor/src/payment/near-fungible.ts @@ -2,7 +2,7 @@ import { BigNumberish } from 'ethers'; import { WalletConnection } from 'near-api-js'; import { erc20FeeProxyArtifact } from '@requestnetwork/smart-contracts'; -import { ClientTypes, ExtensionTypes } from '@requestnetwork/types'; +import { ChainTypes, ClientTypes, ExtensionTypes } from '@requestnetwork/types'; import { getRequestPaymentValues, validateRequest, getAmountToPay } from './utils'; import { @@ -10,7 +10,7 @@ import { isReceiverReady, processNearFungiblePayment, } from './utils-near'; -import { NearChains } from '@requestnetwork/currency'; +import { ChainManager } from '@requestnetwork/chain'; /** * Processes the transaction to pay a request in fungible token on NEAR with fee (Erc20FeeProxy). @@ -31,10 +31,13 @@ export async function payNearFungibleRequest( throw new Error('Cannot pay without a paymentReference'); } - if (!network || !NearChains.isChainSupported(network)) { + if ( + !network || + !ChainManager.current().ecosystems[ChainTypes.ECOSYSTEM.NEAR].isChainSupported(network) + ) { throw new Error('Should be a Near network'); } - NearChains.assertChainSupported(network); + const chain = ChainManager.current().fromName(network, [ChainTypes.ECOSYSTEM.NEAR]); const amountToPay = getAmountToPay(request, amount).toString(); @@ -50,7 +53,7 @@ export async function payNearFungibleRequest( return processNearFungiblePayment( walletConnection, - network, + chain, amountToPay, paymentAddress, paymentReference, diff --git a/packages/payment-processor/src/payment/near-input-data.ts b/packages/payment-processor/src/payment/near-input-data.ts index bbad8237b3..b0736939e7 100644 --- a/packages/payment-processor/src/payment/near-input-data.ts +++ b/packages/payment-processor/src/payment/near-input-data.ts @@ -1,7 +1,7 @@ import { BigNumberish } from 'ethers'; import { WalletConnection } from 'near-api-js'; -import { ClientTypes, ExtensionTypes } from '@requestnetwork/types'; +import { ChainTypes, ClientTypes, ExtensionTypes } from '@requestnetwork/types'; import { getAmountToPay, @@ -10,7 +10,7 @@ import { validateRequest, } from './utils'; import { INearTransactionCallback, processNearPayment } from './utils-near'; -import { NearChains } from '@requestnetwork/currency'; +import { ChainManager } from '@requestnetwork/chain'; /** * processes the transaction to pay a Near request. @@ -24,11 +24,18 @@ export async function payNearInputDataRequest( amount?: BigNumberish, callback?: INearTransactionCallback, ): Promise { - if (!request.currencyInfo.network || !NearChains.isChainSupported(request.currencyInfo.network)) { + if ( + !request.currencyInfo.network || + !ChainManager.current().ecosystems[ChainTypes.ECOSYSTEM.NEAR].isChainSupported( + request.currencyInfo.network, + ) + ) { throw new Error('request.currencyInfo should be a Near network'); } - NearChains.assertChainSupported(request.currencyInfo.network); + const chain = ChainManager.current().fromName(request.currencyInfo.network, [ + ChainTypes.ECOSYSTEM.NEAR, + ]); validateRequest(request, ExtensionTypes.PAYMENT_NETWORK_ID.NATIVE_TOKEN); const { paymentReference, paymentAddress } = getRequestPaymentValues(request); @@ -40,7 +47,7 @@ export async function payNearInputDataRequest( return processNearPayment( walletConnection, - request.currencyInfo.network, + chain, amountToPay, paymentAddress, paymentReference, diff --git a/packages/payment-processor/src/payment/swap-any-to-erc20.ts b/packages/payment-processor/src/payment/swap-any-to-erc20.ts index 24e0ef2200..12885e1038 100644 --- a/packages/payment-processor/src/payment/swap-any-to-erc20.ts +++ b/packages/payment-processor/src/payment/swap-any-to-erc20.ts @@ -6,6 +6,7 @@ import { ERC20SwapToConversion__factory } from '@requestnetwork/smart-contracts/ import { ClientTypes, ExtensionTypes } from '@requestnetwork/types'; import { + ensureEvmChain, getAmountToPay, getProvider, getProxyAddress, @@ -13,7 +14,7 @@ import { getSigner, validateConversionFeeProxyRequest, } from './utils'; -import { CurrencyManager, EvmChains, UnsupportedCurrencyError } from '@requestnetwork/currency'; +import { CurrencyManager, UnsupportedCurrencyError } from '@requestnetwork/currency'; import { IRequestPaymentOptions } from '../types'; import { IPreparedTransaction } from './prepared-transaction'; @@ -91,7 +92,7 @@ export function encodeSwapToPayAnyToErc20Request( if (!network) { throw new Error(`Currency in conversion settings must have a network`); } - EvmChains.assertChainSupported(network); + ensureEvmChain(network); const requestCurrency = currencyManager.fromStorageCurrency(request.currencyInfo); if (!requestCurrency) { diff --git a/packages/payment-processor/src/payment/swap-erc20-fee-proxy.ts b/packages/payment-processor/src/payment/swap-erc20-fee-proxy.ts index 6adc58379b..4163a93815 100644 --- a/packages/payment-processor/src/payment/swap-erc20-fee-proxy.ts +++ b/packages/payment-processor/src/payment/swap-erc20-fee-proxy.ts @@ -6,6 +6,7 @@ import { ClientTypes } from '@requestnetwork/types'; import { ITransactionOverrides } from './transaction-overrides'; import { + ensureEvmChain, getAmountToPay, getProvider, getProxyAddress, @@ -15,7 +16,6 @@ import { } from './utils'; import { IPreparedTransaction } from './prepared-transaction'; import { Erc20PaymentNetwork } from '@requestnetwork/payment-detection'; -import { EvmChains } from '@requestnetwork/currency'; /** * Details required for a token swap: @@ -85,7 +85,7 @@ export function prepareSwapToPayErc20FeeRequest( options?: ISwapTransactionOptions, ): IPreparedTransaction { const { network } = request.currencyInfo; - EvmChains.assertChainSupported(network!); + ensureEvmChain(network); const encodedTx = encodeSwapToPayErc20FeeRequest( request, signerOrProvider, @@ -116,7 +116,7 @@ export function encodeSwapToPayErc20FeeRequest( ): string { const { paymentReference, paymentAddress, feeAddress, feeAmount, network } = getRequestPaymentValues(request); - EvmChains.assertChainSupported(network!); + ensureEvmChain(network); validateErc20FeeProxyRequest(request, options?.amount, options?.feeAmount); diff --git a/packages/payment-processor/src/payment/swap-erc20.ts b/packages/payment-processor/src/payment/swap-erc20.ts index 655078de51..3dc6a30cd1 100644 --- a/packages/payment-processor/src/payment/swap-erc20.ts +++ b/packages/payment-processor/src/payment/swap-erc20.ts @@ -4,10 +4,9 @@ import { erc20SwapToPayArtifact } from '@requestnetwork/smart-contracts'; import { ClientTypes } from '@requestnetwork/types'; import { ITransactionOverrides } from './transaction-overrides'; -import { getProvider, getSigner } from './utils'; +import { ensureEvmChain, getProvider, getSigner } from './utils'; import { checkErc20Allowance, encodeApproveAnyErc20 } from './erc20'; import { IPreparedTransaction } from './prepared-transaction'; -import { EvmChains } from '@requestnetwork/currency'; /** * Processes the approval transaction of a given payment ERC20 to be spent by the swap router, @@ -56,10 +55,7 @@ export async function hasApprovalErc20ForSwapToPay( minAmount: BigNumberish, ): Promise { const { network } = request.currencyInfo; - if (!network) { - throw new Error('Request currency network is missing'); - } - EvmChains.assertChainSupported(network); + ensureEvmChain(network); return checkErc20Allowance( ownerAddress, erc20SwapToPayArtifact.getAddress(network), @@ -108,7 +104,7 @@ export function prepareApprovalErc20ForSwapToPay( amount?: BigNumber, ): IPreparedTransaction { const { network } = request.currencyInfo; - EvmChains.assertChainSupported(network!); + ensureEvmChain(network); const encodedTx = encodeApproveAnyErc20( paymentTokenAddress, erc20SwapToPayArtifact.getAddress(network), diff --git a/packages/payment-processor/src/payment/utils-near.ts b/packages/payment-processor/src/payment/utils-near.ts index a88bd093ff..d7f7d523f5 100644 --- a/packages/payment-processor/src/payment/utils-near.ts +++ b/packages/payment-processor/src/payment/utils-near.ts @@ -4,7 +4,7 @@ import { NearConversionNativeTokenPaymentDetector, NearNativeTokenPaymentDetector, } from '@requestnetwork/payment-detection'; -import { CurrencyTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ChainTypes, RequestLogicTypes } from '@requestnetwork/types'; import { erc20FeeProxyArtifact } from '@requestnetwork/smart-contracts'; /** @@ -62,7 +62,7 @@ const GAS_LIMIT_FUNGIBLE_PROXY = GAS_LIMIT.mul(4).toString(); // 400 TGas export const processNearPayment = async ( walletConnection: WalletConnection, - network: CurrencyTypes.NearChainName, + network: ChainTypes.INearChain, amount: BigNumberish, to: string, paymentReference: string, @@ -114,7 +114,7 @@ export const processNearPayment = async ( */ export const processNearPaymentWithConversion = async ( walletConnection: WalletConnection, - network: CurrencyTypes.NearChainName, + network: ChainTypes.INearChain, amount: BigNumberish, to: string, paymentReference: string, @@ -169,7 +169,7 @@ export const processNearPaymentWithConversion = async ( export const processNearFungiblePayment = async ( walletConnection: WalletConnection, - network: CurrencyTypes.NearChainName, + network: ChainTypes.INearChain, amount: BigNumberish, to: string, paymentReference: string, @@ -183,7 +183,7 @@ export const processNearFungiblePayment = async ( viewMethods: [], }) as any; - const proxyAddress = erc20FeeProxyArtifact.getAddress(network, 'near'); + const proxyAddress = erc20FeeProxyArtifact.getAddress(network.name, 'near'); await fungibleContract.ft_transfer_call({ args: { receiver_id: proxyAddress, diff --git a/packages/payment-processor/src/payment/utils.ts b/packages/payment-processor/src/payment/utils.ts index a340c6fc69..009f306556 100644 --- a/packages/payment-processor/src/payment/utils.ts +++ b/packages/payment-processor/src/payment/utils.ts @@ -1,16 +1,12 @@ import { ethers, Signer, providers, BigNumber, BigNumberish, ContractTransaction } from 'ethers'; import { getDefaultProvider, getPaymentReference } from '@requestnetwork/payment-detection'; -import { - ClientTypes, - CurrencyTypes, - ExtensionTypes, - RequestLogicTypes, -} from '@requestnetwork/types'; -import { EvmChains, getCurrencyHash } from '@requestnetwork/currency'; +import { ChainTypes, ClientTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { getCurrencyHash } from '@requestnetwork/currency'; import { ERC20__factory } from '@requestnetwork/smart-contracts/types'; import { getPaymentNetworkExtension } from '@requestnetwork/payment-detection'; import { getReceivableTokenIdForRequest } from './erc20-transferable-receivable'; +import { ChainManager } from '@requestnetwork/chain'; /** @constant MAX_ALLOWANCE set to the max uint256 value */ export const MAX_ALLOWANCE = BigNumber.from(2).pow(256).sub(1); @@ -79,7 +75,7 @@ export function getRequestPaymentValues(request: ClientTypes.IRequestData): { expectedStartDate?: string; acceptedTokens?: string[]; maxRateTimespan?: string; - network?: CurrencyTypes.ChainName; + network?: string; version: string; } { const extension = getPaymentNetworkExtension(request); @@ -109,14 +105,10 @@ export function getPaymentExtensionVersion(request: ClientTypes.IRequestData): s export const getProxyNetwork = ( pn: ExtensionTypes.IState, currency: RequestLogicTypes.ICurrency, -): string => { - if (pn.values.network) { - return pn.values.network; - } - if (currency.network) { - return currency.network; - } - throw new Error('Payment currency must have a network'); +): ChainTypes.IVmChain => { + const chainName = pn.values.network || currency.network; + if (!chainName) throw new Error('Payment currency must have a network'); + return ChainManager.current().fromName(chainName, ChainTypes.VM_ECOSYSTEMS); }; /** @@ -125,7 +117,7 @@ export const getProxyNetwork = ( */ export function getPnAndNetwork(request: ClientTypes.IRequestData): { paymentNetwork: ExtensionTypes.IState; - network: string; + network: ChainTypes.IVmChain; } { const pn = getPaymentNetworkExtension(request); if (!pn) { @@ -142,13 +134,12 @@ export function getPnAndNetwork(request: ClientTypes.IRequestData): { export const getProxyAddress = ( request: ClientTypes.IRequestData, getDeploymentInformation: ( - network: CurrencyTypes.EvmChainName, + network: ChainTypes.IVmChain, version: string, ) => { address: string } | null, version?: string, ): string => { const { paymentNetwork, network } = getPnAndNetwork(request); - EvmChains.assertChainSupported(network); const deploymentInfo = getDeploymentInformation(network, version || paymentNetwork.version); if (!deploymentInfo) { throw new Error( @@ -420,3 +411,14 @@ export async function revokeErc20Approval( const tx = await signer.sendTransaction(preparedTx); return tx; } + +/** + * Typescript assertions cannot be used directly from class methods at the moment, + * see: https://github.com/microsoft/TypeScript/issues/36931#issuecomment-589753014 + * We use this helper function to avoid repetitions. + */ +export function ensureEvmChain(chainName?: string): asserts chainName is string { + const ensureEvm: ChainTypes.IEcosystem['assertChainNameSupported'] = + ChainManager.current().ecosystems[ChainTypes.ECOSYSTEM.EVM].assertChainNameSupported; + ensureEvm(chainName); +} diff --git a/packages/payment-processor/test/payment/batch-proxy.test.ts b/packages/payment-processor/test/payment/batch-proxy.test.ts index 094291f24d..268bfd5790 100644 --- a/packages/payment-processor/test/payment/batch-proxy.test.ts +++ b/packages/payment-processor/test/payment/batch-proxy.test.ts @@ -775,7 +775,7 @@ describe('batch-proxy', () => { }); it("should throw an error if one request's currencyInfo has no network", async () => { - FAURequest.currencyInfo.network = '' as CurrencyTypes.ChainName; + FAURequest.currencyInfo.network = '' as ChainTypes.IChain; await expect( payBatchConversionProxyRequest(enrichedRequests, wallet, options), ).rejects.toThrowError( diff --git a/packages/payment-processor/test/payment/encoder-approval.test.ts b/packages/payment-processor/test/payment/encoder-approval.test.ts index 69f6d606ba..5481c60082 100644 --- a/packages/payment-processor/test/payment/encoder-approval.test.ts +++ b/packages/payment-processor/test/payment/encoder-approval.test.ts @@ -257,12 +257,12 @@ beforeAll(async () => { await revokeErc20Approval(proxyERC20Conv, alphaContractAddress, wallet); proxyERC20Swap = erc20SwapToPayArtifact.getAddress( - validRequestERC20FeeProxy.currencyInfo.network! as CurrencyTypes.EvmChainName, + validRequestERC20FeeProxy.currencyInfo.network!, ); await revokeErc20Approval(proxyERC20Swap, alphaContractAddress, wallet); proxyERC20SwapConv = erc20SwapConversionArtifact.getAddress( - validRequestERC20FeeProxy.currencyInfo.network! as CurrencyTypes.EvmChainName, + validRequestERC20FeeProxy.currencyInfo.network!, ); await revokeErc20Approval(proxyERC20SwapConv, alphaContractAddress, wallet); }); diff --git a/packages/payment-processor/test/payment/encoder-payment.test.ts b/packages/payment-processor/test/payment/encoder-payment.test.ts index 58f1ceed1d..26af66b180 100644 --- a/packages/payment-processor/test/payment/encoder-payment.test.ts +++ b/packages/payment-processor/test/payment/encoder-payment.test.ts @@ -316,7 +316,7 @@ describe('Payment encoder handles ERC20 Swap Proxy', () => { }); const proxyAddress = erc20SwapToPayArtifact.getAddress( - validRequestERC20FeeProxy.currencyInfo.network! as CurrencyTypes.EvmChainName, + validRequestERC20FeeProxy.currencyInfo.network!, ); expect(paymentTransaction).toEqual({ @@ -335,7 +335,7 @@ describe('Payment encoder handles ERC20 Swap & Conversion Proxy', () => { }); const proxyAddress = erc20SwapConversionArtifact.getAddress( - alphaConversionSettings.currency.network as CurrencyTypes.EvmChainName, + alphaConversionSettings.currency.network, ); expect(paymentTransaction).toEqual({ diff --git a/packages/payment-processor/test/payment/erc20-batch-proxy.test.ts b/packages/payment-processor/test/payment/erc20-batch-proxy.test.ts index 867fa2d718..77eb20210d 100644 --- a/packages/payment-processor/test/payment/erc20-batch-proxy.test.ts +++ b/packages/payment-processor/test/payment/erc20-batch-proxy.test.ts @@ -139,7 +139,7 @@ const testSuite = ( }); it("should throw an error if one request's currencyInfo has no network", async () => { - request2.currencyInfo.network = '' as CurrencyTypes.ChainName; + request2.currencyInfo.network = '' as ChainTypes.IChain; await expect( payBatchProxyRequest([request1, request2], batchVersion, wallet, batchFee), ).rejects.toThrowError( diff --git a/packages/payment-processor/test/payment/erc20-escrow-payment.test.ts b/packages/payment-processor/test/payment/erc20-escrow-payment.test.ts index 489f4c9f1d..818df0c63c 100644 --- a/packages/payment-processor/test/payment/erc20-escrow-payment.test.ts +++ b/packages/payment-processor/test/payment/erc20-escrow-payment.test.ts @@ -67,9 +67,7 @@ const validRequest: ClientTypes.IRequestData = { version: '1.0', }; -const escrowAddress = erc20EscrowToPayArtifact.getAddress( - validRequest.currencyInfo.network! as CurrencyTypes.EvmChainName, -); +const escrowAddress = erc20EscrowToPayArtifact.getAddress(validRequest.currencyInfo.network!); const payerAddress = wallet.address; describe('erc20-escrow-payment tests:', () => { @@ -105,7 +103,7 @@ describe('erc20-escrow-payment tests:', () => { }); it('Should throw an error if currencyInfo has no network', async () => { const request = deepCopy(validRequest); - request.currencyInfo.network = '' as CurrencyTypes.ChainName; + request.currencyInfo.network = '' as ChainTypes.IChain; await expect(Escrow.payEscrow(request, wallet)).rejects.toThrowError('Unsupported chain '); }); it('Should throw an error if request has no extension', async () => { diff --git a/packages/payment-processor/test/payment/erc20-fee-proxy.test.ts b/packages/payment-processor/test/payment/erc20-fee-proxy.test.ts index eaf7f1af5c..6283d588ee 100644 --- a/packages/payment-processor/test/payment/erc20-fee-proxy.test.ts +++ b/packages/payment-processor/test/payment/erc20-fee-proxy.test.ts @@ -108,7 +108,7 @@ describe('erc20-fee-proxy', () => { it('should throw an error if currencyInfo has no network', async () => { const request = deepCopy(validRequest); - request.currencyInfo.network = '' as CurrencyTypes.ChainName; + request.currencyInfo.network = '' as ChainTypes.IChain; await expect(payErc20FeeProxyRequest(request, wallet)).rejects.toThrowError( 'request cannot be processed, or is not an pn-erc20-fee-proxy-contract request', ); diff --git a/packages/payment-processor/test/payment/erc20-proxy.test.ts b/packages/payment-processor/test/payment/erc20-proxy.test.ts index dee210f1c8..f97a8ae9c2 100644 --- a/packages/payment-processor/test/payment/erc20-proxy.test.ts +++ b/packages/payment-processor/test/payment/erc20-proxy.test.ts @@ -92,7 +92,7 @@ describe('payErc20ProxyRequest', () => { it('should throw an error if currencyInfo has no network', async () => { const request = deepCopy(validRequest); - request.currencyInfo.network = '' as CurrencyTypes.EvmChainName; + request.currencyInfo.network = ''; await expect(payErc20ProxyRequest(request, wallet)).rejects.toThrowError( 'request cannot be processed, or is not an pn-erc20-proxy-contract request', ); diff --git a/packages/payment-processor/test/payment/erc777-stream.test.ts b/packages/payment-processor/test/payment/erc777-stream.test.ts index ebd8bc9d0e..c2f983b1f9 100644 --- a/packages/payment-processor/test/payment/erc777-stream.test.ts +++ b/packages/payment-processor/test/payment/erc777-stream.test.ts @@ -111,7 +111,7 @@ describe('erc777-stream', () => { it('should throw an error if currencyInfo has no network', async () => { const request = deepCopy(validRequest); - request.currencyInfo.network = '' as CurrencyTypes.EvmChainName; + request.currencyInfo.network = ''; await expect(payErc777StreamRequest(request, wallet)).rejects.toThrowError( 'request cannot be processed, or is not an pn-erc777-stream request', ); diff --git a/packages/payment-processor/test/payment/eth-batch-proxy.test.ts b/packages/payment-processor/test/payment/eth-batch-proxy.test.ts index 3123bd22a2..6ed4daa24f 100644 --- a/packages/payment-processor/test/payment/eth-batch-proxy.test.ts +++ b/packages/payment-processor/test/payment/eth-batch-proxy.test.ts @@ -106,7 +106,7 @@ describe('payBatchProxyRequest', () => { it('should throw an error if in one request, currencyInfo has no network', async () => { const request = deepCopy(validRequest); - request.currencyInfo.network = '' as CurrencyTypes.EvmChainName; + request.currencyInfo.network = ''; await expect( payBatchProxyRequest([validRequest, request], batchVersion, wallet, batchFee), ).rejects.toThrowError( diff --git a/packages/payment-processor/test/payment/eth-fee-proxy.test.ts b/packages/payment-processor/test/payment/eth-fee-proxy.test.ts index 260cb503d3..dba583ef8d 100644 --- a/packages/payment-processor/test/payment/eth-fee-proxy.test.ts +++ b/packages/payment-processor/test/payment/eth-fee-proxy.test.ts @@ -86,7 +86,7 @@ describe('payEthFeeProxyRequest', () => { it('should throw an error if currencyInfo has no network', async () => { const request = deepCopy(validRequest); - request.currencyInfo.network = '' as CurrencyTypes.EvmChainName; + request.currencyInfo.network = ''; await expect(payEthFeeProxyRequest(request, wallet)).rejects.toThrowError( 'request cannot be processed, or is not an pn-eth-fee-proxy-contract request', ); diff --git a/packages/payment-processor/test/payment/eth-input-data.test.ts b/packages/payment-processor/test/payment/eth-input-data.test.ts index 0d38f1c37e..d645e5a56f 100644 --- a/packages/payment-processor/test/payment/eth-input-data.test.ts +++ b/packages/payment-processor/test/payment/eth-input-data.test.ts @@ -81,7 +81,7 @@ describe('payEthInputDataRequest', () => { it('should throw an error if currencyInfo has no network', async () => { const request = deepCopy(validRequest); - request.currencyInfo.network = '' as CurrencyTypes.EvmChainName; + request.currencyInfo.network = ''; await expect(payEthInputDataRequest(request, wallet)).rejects.toThrowError( 'request cannot be processed, or is not an pn-eth-input-data request', ); diff --git a/packages/payment-processor/test/payment/eth-proxy.test.ts b/packages/payment-processor/test/payment/eth-proxy.test.ts index 5a85a1842f..e9ebee4294 100644 --- a/packages/payment-processor/test/payment/eth-proxy.test.ts +++ b/packages/payment-processor/test/payment/eth-proxy.test.ts @@ -87,7 +87,7 @@ describe('payEthProxyRequest', () => { it('should throw an error if currencyInfo has no network', async () => { const request = deepCopy(validRequest); - request.currencyInfo.network = '' as CurrencyTypes.EvmChainName; + request.currencyInfo.network = ''; await expect(payEthProxyRequest(request, wallet)).rejects.toThrowError( 'request cannot be processed, or is not an pn-eth-input-data request', ); diff --git a/packages/payment-processor/test/payment/swap-erc20-fee-proxy.test.ts b/packages/payment-processor/test/payment/swap-erc20-fee-proxy.test.ts index 28db587659..9ce8525d04 100644 --- a/packages/payment-processor/test/payment/swap-erc20-fee-proxy.test.ts +++ b/packages/payment-processor/test/payment/swap-erc20-fee-proxy.test.ts @@ -85,9 +85,7 @@ describe('swap-erc20-fee-proxy', () => { beforeAll(async () => { // revoke erc20SwapToPay approval await revokeErc20Approval( - erc20SwapToPayArtifact.getAddress( - validRequest.currencyInfo.network! as CurrencyTypes.EvmChainName, - ), + erc20SwapToPayArtifact.getAddress(validRequest.currencyInfo.network!), alphaErc20Address, wallet.provider, ); @@ -96,9 +94,7 @@ describe('swap-erc20-fee-proxy', () => { beforeAll(async () => { // revoke erc20SwapToPay approval await revokeErc20Approval( - erc20SwapToPayArtifact.getAddress( - validRequest.currencyInfo.network! as CurrencyTypes.EvmChainName, - ), + erc20SwapToPayArtifact.getAddress(validRequest.currencyInfo.network!), alphaErc20Address, wallet.provider, ); @@ -126,7 +122,7 @@ describe('swap-erc20-fee-proxy', () => { it('should throw an error if currencyInfo has no network', async () => { const request = deepCopy(validRequest); - request.currencyInfo.network = '' as CurrencyTypes.EvmChainName; + request.currencyInfo.network = ''; await expect( swapErc20FeeProxyRequest(request, wallet, validSwapSettings), ).rejects.toThrowError('Unsupported chain '); diff --git a/packages/request-client.js/package.json b/packages/request-client.js/package.json index c90dcc67f5..6783e4086a 100644 --- a/packages/request-client.js/package.json +++ b/packages/request-client.js/package.json @@ -43,6 +43,7 @@ }, "dependencies": { "@requestnetwork/advanced-logic": "0.39.0", + "@requestnetwork/chain": "0.13.0", "@requestnetwork/currency": "0.13.0", "@requestnetwork/data-access": "0.31.0", "@requestnetwork/data-format": "0.14.0", diff --git a/packages/request-client.js/src/api/request-network.ts b/packages/request-client.js/src/api/request-network.ts index cf4721fb83..935634cf02 100644 --- a/packages/request-client.js/src/api/request-network.ts +++ b/packages/request-client.js/src/api/request-network.ts @@ -398,9 +398,10 @@ export default class RequestNetwork { copiedRequestParameters.extensionsData = []; const detectionChain = - parameters?.paymentNetwork?.parameters && 'network' in parameters.paymentNetwork.parameters - ? parameters.paymentNetwork.parameters.network ?? requestParameters.currency.network - : requestParameters.currency.network; + (parameters?.paymentNetwork?.parameters && + 'network' in parameters.paymentNetwork.parameters && + parameters.paymentNetwork.parameters.network) || + requestParameters.currency.network; const paymentNetwork = parameters.paymentNetwork ? this.paymentNetworkFactory.createPaymentNetwork( diff --git a/packages/request-client.js/src/api/request.ts b/packages/request-client.js/src/api/request.ts index c40656814d..269b55cd83 100644 --- a/packages/request-client.js/src/api/request.ts +++ b/packages/request-client.js/src/api/request.ts @@ -4,7 +4,7 @@ import { EscrowERC20InfoRetriever, } from '@requestnetwork/payment-detection'; import { - CurrencyTypes, + ChainTypes, EncryptionTypes, IdentityTypes, PaymentTypes, @@ -709,16 +709,16 @@ export default class Request { public async getEscrowData( paymentReference: string, - network: CurrencyTypes.EvmChainName, + chain: ChainTypes.IEvmChain, ): Promise { - const escrowContractAddress = erc20EscrowToPayArtifact.getAddress(network); + const escrowContractAddress = erc20EscrowToPayArtifact.getAddress(chain.name); const escrowInfoRetriever = new EscrowERC20InfoRetriever( paymentReference, escrowContractAddress, 0, '', '', - network, + chain, ); return await escrowInfoRetriever.getEscrowRequestMapping(); } diff --git a/packages/request-client.js/src/http-metamask-data-access.ts b/packages/request-client.js/src/http-metamask-data-access.ts index f3c7ab2777..fe9d6f74f2 100644 --- a/packages/request-client.js/src/http-metamask-data-access.ts +++ b/packages/request-client.js/src/http-metamask-data-access.ts @@ -1,9 +1,10 @@ import { Block } from '@requestnetwork/data-access'; import { requestHashSubmitterArtifact } from '@requestnetwork/smart-contracts'; -import { ClientTypes, CurrencyTypes, DataAccessTypes, StorageTypes } from '@requestnetwork/types'; +import { ChainTypes, ClientTypes, DataAccessTypes, StorageTypes } from '@requestnetwork/types'; import { ethers } from 'ethers'; import { EventEmitter } from 'events'; import HttpDataAccess, { NodeConnectionConfig } from './http-data-access'; +import { ChainManager } from '@requestnetwork/chain'; /** * Exposes a Data-Access module over HTTP @@ -20,7 +21,9 @@ export default class HttpMetaMaskDataAccess extends HttpDataAccess { } = {}; private provider: ethers.providers.JsonRpcProvider | ethers.providers.Web3Provider; - private networkName: CurrencyTypes.EvmChainName = 'private'; + private storageChain: ChainTypes.IEvmChain = ChainManager.current().fromName('private', [ + ChainTypes.ECOSYSTEM.EVM, + ]); /** * Creates an instance of HttpDataAccess. @@ -73,14 +76,14 @@ export default class HttpMetaMaskDataAccess extends HttpDataAccess { channelId: string, topics?: string[], ): Promise { - if (!this.networkName) { + if (!this.storageChain) { const network = await this.provider.getNetwork(); - - this.networkName = - network.chainId === 1 ? 'mainnet' : network.chainId === 4 ? 'rinkeby' : 'private'; + this.storageChain = ChainManager.current().fromId(network.chainId.toString(), [ + ChainTypes.ECOSYSTEM.EVM, + ]); } const submitterContract = requestHashSubmitterArtifact.connect( - this.networkName, + this.storageChain.name, this.provider.getSigner(), ); @@ -94,7 +97,7 @@ export default class HttpMetaMaskDataAccess extends HttpDataAccess { topics, ); - // store the block on ipfs and get the the ipfs hash and size + // store the block on ipfs and get the ipfs hash and size const { ipfsHash, ipfsSize } = await this.fetch<{ ipfsHash: string; ipfsSize: number }>( 'POST', '/ipfsAdd', @@ -120,7 +123,7 @@ export default class HttpMetaMaskDataAccess extends HttpDataAccess { blockNumber: tx.blockNumber ?? -1, blockTimestamp: ethBlock.timestamp, fee: fee.toString(), - networkName: this.networkName, + networkName: this.storageChain.name, smartContractAddress: tx.to ?? '', transactionHash: tx.hash, }; @@ -157,7 +160,7 @@ export default class HttpMetaMaskDataAccess extends HttpDataAccess { blockNumber: txConfirmed.blockNumber, blockTimestamp: ethBlock.timestamp, fee: fee.toString(), - networkName: this.networkName, + networkName: this.storageChain.name, smartContractAddress: txConfirmed.to, transactionHash: txConfirmed.transactionHash, }, diff --git a/packages/request-client.js/src/http-request-network.ts b/packages/request-client.js/src/http-request-network.ts index 6762f1343d..63b60e150c 100644 --- a/packages/request-client.js/src/http-request-network.ts +++ b/packages/request-client.js/src/http-request-network.ts @@ -1,4 +1,4 @@ -import { CurrencyInput, CurrencyManager, ICurrencyManager } from '@requestnetwork/currency'; +import { MixedCurrencyType, CurrencyManager, ICurrencyManager } from '@requestnetwork/currency'; import { ClientTypes, DataAccessTypes, @@ -41,7 +41,7 @@ export default class HttpRequestNetwork extends RequestNetwork { nodeConnectionConfig?: Partial; signatureProvider?: SignatureProviderTypes.ISignatureProvider; useMockStorage?: boolean; - currencies?: CurrencyInput[]; + currencies?: MixedCurrencyType[]; currencyManager?: ICurrencyManager; paymentOptions?: Partial; } = { diff --git a/packages/request-node/package.json b/packages/request-node/package.json index d51a5e8aba..5c2b21e871 100644 --- a/packages/request-node/package.json +++ b/packages/request-node/package.json @@ -42,6 +42,7 @@ }, "dependencies": { "@ethersproject/experimental": "5.5.0", + "@requestnetwork/chain": "0.13.0", "@requestnetwork/currency": "0.13.0", "@requestnetwork/data-access": "0.31.0", "@requestnetwork/ethereum-storage": "0.31.0", diff --git a/packages/request-node/src/dataAccess.ts b/packages/request-node/src/dataAccess.ts index 9f51609b41..292a788f5d 100644 --- a/packages/request-node/src/dataAccess.ts +++ b/packages/request-node/src/dataAccess.ts @@ -1,6 +1,6 @@ import { providers, Wallet } from 'ethers'; import { NonceManager } from '@ethersproject/experimental'; -import { CurrencyTypes, DataAccessTypes, LogTypes, StorageTypes } from '@requestnetwork/types'; +import { ChainTypes, DataAccessTypes, LogTypes, StorageTypes } from '@requestnetwork/types'; import * as config from './config'; import { TheGraphDataAccess } from '@requestnetwork/thegraph-data-access'; @@ -8,7 +8,7 @@ import { PendingStore } from '@requestnetwork/data-access'; import { EthereumStorage, EthereumTransactionSubmitter } from '@requestnetwork/ethereum-storage'; export function getDataAccess( - network: CurrencyTypes.EvmChainName, + chain: ChainTypes.IEvmChain, ipfsStorage: StorageTypes.IIpfsStorage, logger: LogTypes.ILogger, ): DataAccessTypes.IDataAccess { @@ -25,7 +25,7 @@ export function getDataAccess( const gasPriceMultiplier = config.getGasPriceMultiplier(); const blockConfirmations = config.getBlockConfirmations(); const txSubmitter = new EthereumTransactionSubmitter({ - network, + network: chain.name, logger, gasPriceMin, gasPriceMax, @@ -42,7 +42,7 @@ export function getDataAccess( return new TheGraphDataAccess({ graphql: { url: graphNodeUrl }, storage, - network, + network: chain.name, logger, pendingStore, }); diff --git a/packages/request-node/src/request/confirmedTransactionStore.ts b/packages/request-node/src/request/confirmedTransactionStore.ts index 85a9ac14d7..09f1c33149 100644 --- a/packages/request-node/src/request/confirmedTransactionStore.ts +++ b/packages/request-node/src/request/confirmedTransactionStore.ts @@ -1,4 +1,4 @@ -import { DataAccessTypes, StorageTypes } from '@requestnetwork/types'; +import { ChainTypes, DataAccessTypes, StorageTypes } from '@requestnetwork/types'; import { SubgraphClient } from '@requestnetwork/thegraph-data-access'; /** @@ -13,7 +13,7 @@ export default class ConfirmedTransactionStore { */ constructor( private readonly subgraphClient: SubgraphClient, - private readonly networkName: string, + private readonly storageChain: ChainTypes.IEvmChain, ) {} public async getConfirmedTransaction( @@ -37,7 +37,7 @@ export default class ConfirmedTransactionStore { blockConfirmation: blockNumber - transaction.blockNumber, blockTimestamp: transaction.blockTimestamp, blockNumber: transaction.blockNumber, - networkName: this.networkName, + networkName: this.storageChain.name, smartContractAddress: transaction.smartContractAddress, transactionHash: transaction.transactionHash, }, diff --git a/packages/request-node/src/server.ts b/packages/request-node/src/server.ts index 2422492cfd..e8048786c0 100755 --- a/packages/request-node/src/server.ts +++ b/packages/request-node/src/server.ts @@ -5,33 +5,33 @@ import { RequestNode } from './requestNode'; import { getDataAccess } from './dataAccess'; import { getDataStorage } from './dataStorage'; import ConfirmedTransactionStore from './request/confirmedTransactionStore'; -import { EvmChains } from '@requestnetwork/currency'; import { getEthereumStorageNetworkNameFromId } from '@requestnetwork/ethereum-storage'; import { SubgraphClient } from '@requestnetwork/thegraph-data-access'; +import { ChainManager } from '@requestnetwork/chain'; +import { ChainTypes } from '@requestnetwork/types'; // Initialize the node logger const logger = new Logger(config.getLogLevel(), config.getLogMode()); -const getNetwork = () => { - const network = getEthereumStorageNetworkNameFromId(config.getStorageNetworkId()) as any; +const getStorageChain = (): ChainTypes.IEvmChain => { + const network = getEthereumStorageNetworkNameFromId(config.getStorageNetworkId()); if (!network) { throw new Error(`Storage network not supported: ${config.getStorageNetworkId()}`); } - EvmChains.assertChainSupported(network); - return network; + return ChainManager.current().fromName(network, [ChainTypes.ECOSYSTEM.EVM]); }; export const getRequestNode = (): RequestNode => { - const network = getNetwork(); + const storageChain = getStorageChain(); const storage = getDataStorage(logger); - const dataAccess = getDataAccess(network, storage, logger); + const dataAccess = getDataAccess(storageChain, storage, logger); // we access the subgraph client directly, not through the data access, // because this feature is specific to RN use with Request Node. Without a node, // the confirmation process would be different, so this doesn't fit in the data access layer const confirmedTransactionStore = new ConfirmedTransactionStore( new SubgraphClient(config.getGraphNodeUrl()), - network, + storageChain, ); return new RequestNode(dataAccess, storage, confirmedTransactionStore, logger); diff --git a/packages/smart-contracts/deploy/deploy-zk-batch-contracts.ts b/packages/smart-contracts/deploy/deploy-zk-batch-contracts.ts index ecb64c0293..d65d23187e 100644 --- a/packages/smart-contracts/deploy/deploy-zk-batch-contracts.ts +++ b/packages/smart-contracts/deploy/deploy-zk-batch-contracts.ts @@ -1,7 +1,6 @@ import { erc20FeeProxyArtifact, ethereumFeeProxyArtifact } from '../src/lib'; import { deployContract } from './utils-zk'; import * as hre from 'hardhat'; -import { CurrencyTypes } from '@requestnetwork/types'; /** * Deploys Batch payments contracts to zkSync network. @@ -11,8 +10,8 @@ import { CurrencyTypes } from '@requestnetwork/types'; export default async function () { const [deployer] = await hre.ethers.getSigners(); const constructorArguments = [ - erc20FeeProxyArtifact.getAddress(hre.network.name as CurrencyTypes.EvmChainName), - ethereumFeeProxyArtifact.getAddress(hre.network.name as CurrencyTypes.EvmChainName), + erc20FeeProxyArtifact.getAddress(hre.network.name), + ethereumFeeProxyArtifact.getAddress(hre.network.name), hre.ethers.constants.AddressZero, hre.ethers.constants.AddressZero, hre.ethers.constants.AddressZero, diff --git a/packages/smart-contracts/package.json b/packages/smart-contracts/package.json index 3ba30d487e..9d1e9c88fb 100644 --- a/packages/smart-contracts/package.json +++ b/packages/smart-contracts/package.json @@ -65,6 +65,7 @@ "@nomiclabs/hardhat-web3": "2.0.0", "@openzeppelin/contracts": "4.9.5", "@rainbow-me/fee-suggestions": "2.1.0", + "@requestnetwork/chain": "0.13.0", "@requestnetwork/currency": "0.13.0", "@requestnetwork/types": "0.40.0", "@requestnetwork/utils": "0.40.0", diff --git a/packages/smart-contracts/scripts-create2/compute-one-address.ts b/packages/smart-contracts/scripts-create2/compute-one-address.ts index 2996f1ca96..f8277ea669 100644 --- a/packages/smart-contracts/scripts-create2/compute-one-address.ts +++ b/packages/smart-contracts/scripts-create2/compute-one-address.ts @@ -2,7 +2,6 @@ import { HardhatRuntimeEnvironmentExtended, IDeploymentParams } from './types'; import { requestDeployer } from '../src/lib'; import { create2ContractDeploymentList } from './utils'; import { getConstructorArgs } from './constructor-args'; -import { EvmChains } from '@requestnetwork/currency'; // Deploys, set up the contracts export async function computeCreate2DeploymentAddress( @@ -48,7 +47,6 @@ export const computeCreate2DeploymentAddressesFromList = async ( hre: HardhatRuntimeEnvironmentExtended, ): Promise => { const chain = hre.network.name; - EvmChains.assertChainSupported(chain); await Promise.all( create2ContractDeploymentList.map(async (contract) => { let address: string; diff --git a/packages/smart-contracts/scripts-create2/constructor-args.ts b/packages/smart-contracts/scripts-create2/constructor-args.ts index 042da3569c..722e6d0a0b 100644 --- a/packages/smart-contracts/scripts-create2/constructor-args.ts +++ b/packages/smart-contracts/scripts-create2/constructor-args.ts @@ -1,5 +1,4 @@ import * as artifacts from '../src/lib'; -import { CurrencyTypes } from '@requestnetwork/types'; const getAdminWalletAddress = (contract: string): string => { if (!process.env.ADMIN_WALLET_ADDRESS) { @@ -8,10 +7,7 @@ const getAdminWalletAddress = (contract: string): string => { return process.env.ADMIN_WALLET_ADDRESS; }; -export const getConstructorArgs = ( - contract: string, - network?: CurrencyTypes.EvmChainName, -): string[] => { +export const getConstructorArgs = (contract: string, network?: string): string[] => { switch (contract) { case 'ChainlinkConversionPath': { return ['0x0000000000000000000000000000000000000000', getAdminWalletAddress(contract)]; diff --git a/packages/smart-contracts/scripts-create2/contract-setup/adminTasks.ts b/packages/smart-contracts/scripts-create2/contract-setup/adminTasks.ts index 16be222ba5..eb42f9eef8 100644 --- a/packages/smart-contracts/scripts-create2/contract-setup/adminTasks.ts +++ b/packages/smart-contracts/scripts-create2/contract-setup/adminTasks.ts @@ -10,7 +10,7 @@ import { getDefaultProvider, normalizeGasFees, } from '@requestnetwork/utils'; -import { CurrencyTypes } from '@requestnetwork/types'; + import { suggestFeesEip1559 } from '../fee-suggestion'; import { executeContractMethod } from './execute-contract-method'; @@ -34,7 +34,7 @@ const BATCH_FEE_AMOUNT_USD_LIMIT = parseUnits('150', 8); */ export const updateChainlinkConversionPath = async ( contract: Contract, - network: CurrencyTypes.EvmChainName, + network: string, txOverrides: Overrides, signer: Wallet, signWithEoa: boolean, @@ -195,7 +195,7 @@ export const updateBatchPaymentFeeAmountUSDLimit = async ( */ export const updatePaymentFeeProxyAddress = async ( contract: Contract, - network: CurrencyTypes.EvmChainName, + network: string, txOverrides: Overrides, proxyType: 'native' | 'erc20', signer: Wallet, @@ -239,7 +239,7 @@ export const updatePaymentFeeProxyAddress = async ( */ export const updateBatchConversionProxy = async ( contract: Contract, - network: CurrencyTypes.EvmChainName, + network: string, txOverrides: Overrides, proxyName: string, signer: Wallet, diff --git a/packages/smart-contracts/scripts-create2/contract-setup/execute-contract-method.ts b/packages/smart-contracts/scripts-create2/contract-setup/execute-contract-method.ts index a5dde97c15..9af540252f 100644 --- a/packages/smart-contracts/scripts-create2/contract-setup/execute-contract-method.ts +++ b/packages/smart-contracts/scripts-create2/contract-setup/execute-contract-method.ts @@ -3,7 +3,6 @@ import { Contract, Overrides, Wallet } from 'ethers'; import { safeAdminArtifact } from '../../src/lib/'; import Safe, { EthersAdapter, EthersAdapterConfig } from '@safe-global/protocol-kit'; import { ethers } from 'ethers'; -import { CurrencyTypes } from '@requestnetwork/types'; const txServiceUrls: Record = { mainnet: 'https://safe-transaction-mainnet.safe.global/', @@ -36,7 +35,7 @@ export const executeContractMethod = async ({ signer: Wallet; signWithEoa?: boolean; }): Promise => { - const safeAddress = safeAdminArtifact.getAddress(network as CurrencyTypes.VMChainName); + const safeAddress = safeAdminArtifact.getAddress(network); const txServiceUrl = txServiceUrls[network]; if (!signWithEoa && !!safeAddress && !!txServiceUrl) { const ethAdapter = new EthersAdapter({ diff --git a/packages/smart-contracts/scripts-create2/contract-setup/setupBatchConversionPayments.ts b/packages/smart-contracts/scripts-create2/contract-setup/setupBatchConversionPayments.ts index 48f25c6f56..a17b263325 100644 --- a/packages/smart-contracts/scripts-create2/contract-setup/setupBatchConversionPayments.ts +++ b/packages/smart-contracts/scripts-create2/contract-setup/setupBatchConversionPayments.ts @@ -7,8 +7,8 @@ import { updateBatchPaymentFees, updateNativeAndUSDAddress, } from './adminTasks'; -import { CurrencyManager, EvmChains } from '@requestnetwork/currency'; -import { CurrencyTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { CurrencyManager } from '@requestnetwork/currency'; +import { RequestLogicTypes } from '@requestnetwork/types'; /** * Updates the values of the batch fees of the BatchConversionPayments contract, if needed. @@ -31,7 +31,7 @@ export const setupBatchConversionPayments = async ({ // constants related to chainlink and conversion rate const currencyManager = CurrencyManager.getDefault(); - const setUpActions = async (network: CurrencyTypes.EvmChainName) => { + const setUpActions = async (network: string) => { console.log(`Setup BatchConversionPayments on ${network}`); if (!contractAddress) { @@ -93,7 +93,6 @@ export const setupBatchConversionPayments = async ({ }; for (const network of hre.config.xdeploy.networks) { try { - EvmChains.assertChainSupported(network); await setUpActions(network); } catch (err) { console.warn(`An error occurred during the setup of BatchConversion on ${network}`); diff --git a/packages/smart-contracts/scripts-create2/contract-setup/setupChainlinkConversionPath.ts b/packages/smart-contracts/scripts-create2/contract-setup/setupChainlinkConversionPath.ts index 4986562a1f..9dd8546af2 100644 --- a/packages/smart-contracts/scripts-create2/contract-setup/setupChainlinkConversionPath.ts +++ b/packages/smart-contracts/scripts-create2/contract-setup/setupChainlinkConversionPath.ts @@ -1,4 +1,4 @@ -import { CurrencyManager, EvmChains } from '@requestnetwork/currency'; +import { CurrencyManager } from '@requestnetwork/currency'; import { RequestLogicTypes } from '@requestnetwork/types'; import { chainlinkConversionPath } from '../../src/lib'; import { HardhatRuntimeEnvironmentExtended } from '../types'; @@ -24,7 +24,6 @@ export const setupChainlinkConversionPath = async ({ await Promise.all( hre.config.xdeploy.networks.map(async (network: string) => { try { - EvmChains.assertChainSupported(network); if (!contractAddress) { contractAddress = chainlinkConversionPath.getAddress(network); } diff --git a/packages/smart-contracts/scripts-create2/contract-setup/setupERC20SwapToConversion.ts b/packages/smart-contracts/scripts-create2/contract-setup/setupERC20SwapToConversion.ts index f655e138a6..5df7f8ab2d 100644 --- a/packages/smart-contracts/scripts-create2/contract-setup/setupERC20SwapToConversion.ts +++ b/packages/smart-contracts/scripts-create2/contract-setup/setupERC20SwapToConversion.ts @@ -6,7 +6,6 @@ import { updateRequestSwapFees, updateSwapRouter, } from './adminTasks'; -import { EvmChains } from '@requestnetwork/currency'; /** * Updates the values of the chainlinkConversionPath and swap router of the ERC20SwapToConversion contract @@ -27,7 +26,6 @@ export const setupERC20SwapToConversion = async ({ await Promise.all( hre.config.xdeploy.networks.map(async (network: string) => { try { - EvmChains.assertChainSupported(network); if (!contractAddress) { contractAddress = erc20SwapConversionArtifact.getAddress(network); } diff --git a/packages/smart-contracts/scripts-create2/contract-setup/setupERC20SwapToPay.ts b/packages/smart-contracts/scripts-create2/contract-setup/setupERC20SwapToPay.ts index 2b153190f9..4cc9e0ee0a 100644 --- a/packages/smart-contracts/scripts-create2/contract-setup/setupERC20SwapToPay.ts +++ b/packages/smart-contracts/scripts-create2/contract-setup/setupERC20SwapToPay.ts @@ -1,4 +1,3 @@ -import { EvmChains } from '@requestnetwork/currency'; import { erc20SwapToPayArtifact } from '../../src/lib'; import { HardhatRuntimeEnvironmentExtended } from '../types'; import { getSignerAndGasFees, updateRequestSwapFees, updateSwapRouter } from './adminTasks'; @@ -22,7 +21,6 @@ export const setupERC20SwapToPay = async ({ await Promise.all( hre.config.xdeploy.networks.map(async (network: string) => { try { - EvmChains.assertChainSupported(network); if (!contractAddress) { contractAddress = erc20SwapToPayArtifact.getAddress(network); } diff --git a/packages/smart-contracts/scripts-create2/contract-setup/setupETHConversionProxy.ts b/packages/smart-contracts/scripts-create2/contract-setup/setupETHConversionProxy.ts index 097d4e627b..e760127d94 100644 --- a/packages/smart-contracts/scripts-create2/contract-setup/setupETHConversionProxy.ts +++ b/packages/smart-contracts/scripts-create2/contract-setup/setupETHConversionProxy.ts @@ -1,4 +1,4 @@ -import { CurrencyManager, EvmChains } from '@requestnetwork/currency'; +import { CurrencyManager } from '@requestnetwork/currency'; import { RequestLogicTypes } from '@requestnetwork/types'; import { ethConversionArtifact } from '../../src/lib'; import { HardhatRuntimeEnvironmentExtended } from '../types'; @@ -28,7 +28,6 @@ export const setupETHConversionProxy = async ({ await Promise.all( hre.config.xdeploy.networks.map(async (network: string) => { try { - EvmChains.assertChainSupported(network); if (!contractAddress) { contractAddress = ethConversionArtifact.getAddress(network); } diff --git a/packages/smart-contracts/scripts-create2/contract-setup/setupErc20ConversionProxy.ts b/packages/smart-contracts/scripts-create2/contract-setup/setupErc20ConversionProxy.ts index 5d372d11ae..0d553e0f37 100644 --- a/packages/smart-contracts/scripts-create2/contract-setup/setupErc20ConversionProxy.ts +++ b/packages/smart-contracts/scripts-create2/contract-setup/setupErc20ConversionProxy.ts @@ -5,7 +5,6 @@ import { updateChainlinkConversionPath, updatePaymentFeeProxyAddress, } from './adminTasks'; -import { EvmChains } from '@requestnetwork/currency'; const ERC20ConversionVersion = '0.1.2'; @@ -28,7 +27,6 @@ export const setupErc20ConversionProxy = async ({ await Promise.all( hre.config.xdeploy.networks.map(async (network: string) => { try { - EvmChains.assertChainSupported(network); if (!contractAddress) { contractAddress = erc20ConversionProxy.getAddress(network); } diff --git a/packages/smart-contracts/scripts-create2/contract-setup/update-owner.ts b/packages/smart-contracts/scripts-create2/contract-setup/update-owner.ts index 22e20a5b9e..ee60429096 100644 --- a/packages/smart-contracts/scripts-create2/contract-setup/update-owner.ts +++ b/packages/smart-contracts/scripts-create2/contract-setup/update-owner.ts @@ -2,7 +2,6 @@ import { safeAdminArtifact } from '../../src/lib'; import { HardhatRuntimeEnvironmentExtended } from '../types'; import { getArtifact } from '../utils'; import { getSignerAndGasFees } from './adminTasks'; -import { EvmChains } from '@requestnetwork/currency'; import { executeContractMethod } from './execute-contract-method'; import { Contract } from 'ethers'; @@ -23,7 +22,6 @@ export const updateOwner = async ({ }): Promise => { for (const network of hre.config.xdeploy.networks) { try { - EvmChains.assertChainSupported(network); const contractArtifact = getArtifact(contract); const contractAddress = contractArtifact.getAddress(network); const { signer, txOverrides } = await getSignerAndGasFees(network, hre); diff --git a/packages/smart-contracts/scripts-create2/contract-setup/update-whitelisted-role.ts b/packages/smart-contracts/scripts-create2/contract-setup/update-whitelisted-role.ts index 3a0b2c72d5..7a213a43e2 100644 --- a/packages/smart-contracts/scripts-create2/contract-setup/update-whitelisted-role.ts +++ b/packages/smart-contracts/scripts-create2/contract-setup/update-whitelisted-role.ts @@ -2,7 +2,6 @@ import { safeAdminArtifact } from '../../src/lib'; import { HardhatRuntimeEnvironmentExtended } from '../types'; import { getArtifact } from '../utils'; import { getSignerAndGasFees } from './adminTasks'; -import { EvmChains } from '@requestnetwork/currency'; import { executeContractMethod } from './execute-contract-method'; import { Contract } from 'ethers'; @@ -65,7 +64,6 @@ export const updateWhitelistedRole = async ({ }): Promise => { for (const network of hre.config.xdeploy.networks) { try { - EvmChains.assertChainSupported(network); const contractArtifact = getArtifact(contract); const contractAddress = contractArtifact.getAddress(network); const { signer, txOverrides } = await getSignerAndGasFees(network, hre); diff --git a/packages/smart-contracts/scripts-create2/deploy.ts b/packages/smart-contracts/scripts-create2/deploy.ts index 4eee974b57..522a561abf 100644 --- a/packages/smart-contracts/scripts-create2/deploy.ts +++ b/packages/smart-contracts/scripts-create2/deploy.ts @@ -2,7 +2,6 @@ import { create2ContractDeploymentList, isContractDeployed } from './utils'; import { HardhatRuntimeEnvironmentExtended, IDeploymentParams } from './types'; import { xdeploy } from './xdeployer'; import { getConstructorArgs } from './constructor-args'; -import { EvmChains } from '@requestnetwork/currency'; import { setupContract } from './contract-setup/setups'; /** @@ -55,7 +54,6 @@ export const deployWithCreate2FromList = async ( ): Promise => { for (const contract of create2ContractDeploymentList) { const network = hre.config.xdeploy.networks[0]; - EvmChains.assertChainSupported(network); const constructorArgs = getConstructorArgs(contract, network); const address = await deployOneWithCreate2({ contract, constructorArgs }, hre); await setupContract({ diff --git a/packages/smart-contracts/scripts-create2/tenderly.ts b/packages/smart-contracts/scripts-create2/tenderly.ts index 14d831ce48..fee9d3253e 100644 --- a/packages/smart-contracts/scripts-create2/tenderly.ts +++ b/packages/smart-contracts/scripts-create2/tenderly.ts @@ -3,8 +3,8 @@ import * as artifacts from '../src/lib/artifacts'; import { ContractArtifact } from '../src/lib'; import { Contract } from 'ethers'; import * as console from 'console'; -import { EvmChains } from '@requestnetwork/currency'; -import { CurrencyTypes } from '@requestnetwork/types'; +import { ChainTypes } from '@requestnetwork/types'; +import { ChainManager } from '@requestnetwork/chain'; const tenderlyBaseURL = 'https://api.tenderly.co'; const makeTenderlyClient = @@ -25,7 +25,7 @@ const capitalizeFirstLetter = (string: string) => string.charAt(0).toUpperCase() * Chains supported by Tenderly. * Supported testnet chains are commented out. */ -const supportedTenderlyChains: CurrencyTypes.EvmChainName[] = [ +const supportedTenderlyChains: ChainTypes.IEvmChain[] = [ 'arbitrum-one', 'arbitrum-rinkeby', 'avalanche', @@ -39,7 +39,9 @@ const supportedTenderlyChains: CurrencyTypes.EvmChainName[] = [ 'optimism', 'rinkeby', 'xdai', -]; +].map((chainName: string) => + ChainManager.current().fromName(chainName, [ChainTypes.ECOSYSTEM.EVM]), +); type TenderlyContract = { address: string; chainId: number }; @@ -58,13 +60,19 @@ export const tenderlyImportAll = async (hre: HardhatRuntimeEnvironmentExtended): const deployments = artifact.getAllAddressesFromAllNetworks(); for (const deployment of deployments) { const { networkName, address, version } = deployment; + let deploymentChain: ChainTypes.IEvmChain; try { - EvmChains.assertChainSupported(networkName); + deploymentChain = ChainManager.current().fromName(networkName, [ + ChainTypes.ECOSYSTEM.EVM, + ]); } catch { continue; } - if (!supportedTenderlyChains.includes(networkName)) continue; - const chainId = EvmChains.getChainId(networkName); + const chain = supportedTenderlyChains.find((tenderlyChain) => + tenderlyChain.eq(deploymentChain), + ); + if (!chain) continue; + const chainId = parseInt(chain.id); const contract: TenderlyContract = { address, chainId, @@ -76,7 +84,7 @@ export const tenderlyImportAll = async (hre: HardhatRuntimeEnvironmentExtended): }; versions[version] ??= new Set(); versions[version].add(contractId); - (EvmChains.isTestnet(networkName) ? testnetContracts : mainnetContracts).add(contractId); + (chain.testnet ? testnetContracts : mainnetContracts).add(contractId); } } console.log(`> Retrieved ${Object.keys(contracts).length} contracts from protocol artifacts`); diff --git a/packages/smart-contracts/scripts-create2/utils.ts b/packages/smart-contracts/scripts-create2/utils.ts index d004440416..98b236212e 100644 --- a/packages/smart-contracts/scripts-create2/utils.ts +++ b/packages/smart-contracts/scripts-create2/utils.ts @@ -1,6 +1,5 @@ import { Contract } from 'ethers'; import * as artifacts from '../src/lib'; -import { EvmChains } from '@requestnetwork/currency'; /** * List of smart contract that we deploy using the CREATE2 scheme through the Request Deployer contract @@ -74,7 +73,6 @@ export const isContractDeployed = ( computedAddress: string, ): boolean => { try { - EvmChains.assertChainSupported(network); const contractArtifact = getArtifact(contract); const addresses = contractArtifact.getAllAddresses(network); return addresses.some((x) => x.address === computedAddress); diff --git a/packages/smart-contracts/scripts-create2/verify.ts b/packages/smart-contracts/scripts-create2/verify.ts index b128b711f9..3d2c66e570 100644 --- a/packages/smart-contracts/scripts-create2/verify.ts +++ b/packages/smart-contracts/scripts-create2/verify.ts @@ -2,7 +2,6 @@ import { computeCreate2DeploymentAddress } from './compute-one-address'; import { getConstructorArgs } from './constructor-args'; import { HardhatRuntimeEnvironmentExtended, IDeploymentParams } from './types'; import { create2ContractDeploymentList } from './utils'; -import { EvmChains } from '@requestnetwork/currency'; export const verifyOne = async ( contractAddress: string, @@ -49,7 +48,6 @@ export async function VerifyCreate2FromList(hre: HardhatRuntimeEnvironmentExtend case 'BatchConversionPayments': case 'ERC20TransferableReceivable': { const network = hre.config.xdeploy.networks[0]; - EvmChains.assertChainSupported(network); const constructorArgs = getConstructorArgs(contract, network); address = await computeCreate2DeploymentAddress({ contract, constructorArgs }, hre); await verifyOne(address, { contract, constructorArgs }, hre); diff --git a/packages/smart-contracts/scripts/deploy-one.ts b/packages/smart-contracts/scripts/deploy-one.ts index 1bfb1f04d1..06ab69e806 100644 --- a/packages/smart-contracts/scripts/deploy-one.ts +++ b/packages/smart-contracts/scripts/deploy-one.ts @@ -2,8 +2,6 @@ import '@nomiclabs/hardhat-ethers'; import { HardhatRuntimeEnvironment } from 'hardhat/types'; import { Contract } from 'ethers'; import { ContractArtifact } from '../src/lib'; -import { EvmChains } from '@requestnetwork/currency'; - export interface DeploymentResult { address: string; contractName: string; @@ -61,7 +59,6 @@ export async function deployOne( if (options?.artifact) { try { const chain = hre.network.name; - EvmChains.assertChainSupported(chain); address = options.artifact.getAddress(chain, options.version); const action = args.force ? '(forcing deployment)' : '(skipping)'; console.log( diff --git a/packages/smart-contracts/scripts/test-deploy-batch-conversion-deployment.ts b/packages/smart-contracts/scripts/test-deploy-batch-conversion-deployment.ts index 2cb168930f..d8abdd0324 100644 --- a/packages/smart-contracts/scripts/test-deploy-batch-conversion-deployment.ts +++ b/packages/smart-contracts/scripts/test-deploy-batch-conversion-deployment.ts @@ -11,7 +11,7 @@ import { ethConversionArtifact, ethereumFeeProxyArtifact, } from '../src/lib'; -import { CurrencyManager, EvmChains } from '@requestnetwork/currency'; +import { CurrencyManager } from '@requestnetwork/currency'; import { deployAddressChecking } from './utils'; import { BigNumber } from 'ethers'; import { PRECISION_RATE } from './test-deploy_chainlink_contract'; @@ -26,7 +26,6 @@ export async function deployBatchConversionPayment( try { console.log('Deploy BatchConversionPayments'); const chain = hre.network.name; - EvmChains.assertChainSupported(chain); const _ERC20FeeProxyAddress = erc20FeeProxyArtifact.getAddress('private'); const _EthereumFeeProxyAddress = ethereumFeeProxyArtifact.getAddress('private'); const _paymentErc20ConversionFeeProxy = erc20ConversionProxy.getAddress('private'); diff --git a/packages/smart-contracts/scripts/test-deploy-batch-erc-eth-deployment.ts b/packages/smart-contracts/scripts/test-deploy-batch-erc-eth-deployment.ts index 2d115a3f07..19db1e45d8 100644 --- a/packages/smart-contracts/scripts/test-deploy-batch-erc-eth-deployment.ts +++ b/packages/smart-contracts/scripts/test-deploy-batch-erc-eth-deployment.ts @@ -4,13 +4,11 @@ import { deployOne } from '../scripts/deploy-one'; import { batchPaymentsArtifact } from '../src/lib'; import { deployAddressChecking } from './utils'; -import { EvmChains } from '@requestnetwork/currency'; // Deploys, set up the contracts export async function deployBatchPayment(args: any, hre: HardhatRuntimeEnvironment): Promise { try { const chain = hre.network.name; - EvmChains.assertChainSupported(chain); const ERC20FeeProxyAddress = '0x75c35C980C0d37ef46DF04d31A140b65503c0eEd'; const EthereumFeeProxyAddress = '0x3d49d1eF2adE060a33c6E6Aa213513A7EE9a6241'; diff --git a/packages/smart-contracts/src/lib/ContractArtifact.ts b/packages/smart-contracts/src/lib/ContractArtifact.ts index cf86e6ec7c..80402e7a26 100644 --- a/packages/smart-contracts/src/lib/ContractArtifact.ts +++ b/packages/smart-contracts/src/lib/ContractArtifact.ts @@ -1,6 +1,5 @@ import { Contract, providers, Signer } from 'ethers'; import type { JsonFragment } from '@ethersproject/abi'; -import type { CurrencyTypes } from '@requestnetwork/types'; /** * Contract information specific to a network @@ -13,16 +12,16 @@ export type ArtifactNetworkInfo = { }; /** Deployment information and ABI per network */ -export type ArtifactDeploymentInfo = { +export type ArtifactDeploymentInfo = { abi: JsonFragment[]; - deployment: Partial>; + deployment: Partial>; }; /** Deployment information and ABI per version and network */ -export type ArtifactInfo< - TVersion extends string = string, - TNetwork extends CurrencyTypes.VMChainName = CurrencyTypes.VMChainName, -> = Record>; +export type ArtifactInfo = Record< + TVersion, + ArtifactDeploymentInfo +>; export type DeploymentInformation = { address: string; @@ -52,7 +51,7 @@ export class ContractArtifact { * Returns an ethers contract instance for the given `networkName` */ connect( - networkName: CurrencyTypes.EvmChainName, + networkName: string, signerOrProvider: Signer | providers.Provider, version: string = this.lastVersion, ): TContract { @@ -81,7 +80,7 @@ export class ContractArtifact { * @param networkName the name of the network where the contract is deployed * @returns the address of the deployed contract */ - getAddress(networkName: CurrencyTypes.VMChainName, version = this.lastVersion): string { + getAddress(networkName: string, version = this.lastVersion): string { return this.getDeploymentInformation(networkName, version).address; } @@ -90,9 +89,7 @@ export class ContractArtifact { * @param networkName the name of the network where the contract is deployed * @returns the addresses of the deployed contract and the associated version. */ - getAllAddresses( - networkName: CurrencyTypes.VMChainName, - ): { version: string; address: string | undefined }[] { + getAllAddresses(networkName: string): { version: string; address: string | undefined }[] { const entries = Object.entries(this.info); return entries.map(([version, { deployment }]) => ({ version, @@ -107,11 +104,11 @@ export class ContractArtifact { getAllAddressesFromAllNetworks(): { version: string; address: string; - networkName: CurrencyTypes.VMChainName; + networkName: string; }[] { const deployments = []; for (const version in this.info) { - let networkName: CurrencyTypes.VMChainName; + let networkName: string; for (networkName in this.info[version].deployment) { const address = this.info[version].deployment[networkName]?.address; if (!address) continue; @@ -131,10 +128,7 @@ export class ContractArtifact { * @param networkName the name of the network where the contract is deployed * @returns the number of the block where the contract was deployed */ - getCreationBlockNumber( - networkName: CurrencyTypes.VMChainName, - version = this.lastVersion, - ): number { + getCreationBlockNumber(networkName: string, version = this.lastVersion): number { return this.getDeploymentInformation(networkName, version).creationBlockNumber; } @@ -144,10 +138,7 @@ export class ContractArtifact { * @param networkName the name of the network where the contract is deployed * @returns The address and the number of the creation block */ - getDeploymentInformation( - networkName: CurrencyTypes.VMChainName, - version = this.lastVersion, - ): DeploymentInformation { + getDeploymentInformation(networkName: string, version = this.lastVersion): DeploymentInformation { const versionInfo = this.info[version]; if (!versionInfo) { throw Error(`No deployment for version: ${version}.`); @@ -167,7 +158,7 @@ export class ContractArtifact { * @returns The address and the number of the creation block, or null if not found */ getOptionalDeploymentInformation( - networkName: CurrencyTypes.VMChainName, + networkName: string, version = this.lastVersion, ): DeploymentInformation | null { return this.info[version]?.deployment[networkName] || null; diff --git a/packages/smart-contracts/test/contracts/BatchConversionPayments.test.ts b/packages/smart-contracts/test/contracts/BatchConversionPayments.test.ts index c2b630c61e..ad5808f2eb 100644 --- a/packages/smart-contracts/test/contracts/BatchConversionPayments.test.ts +++ b/packages/smart-contracts/test/contracts/BatchConversionPayments.test.ts @@ -13,7 +13,7 @@ import { import { PaymentTypes } from '@requestnetwork/types'; import { BigNumber, ContractTransaction, Signer } from 'ethers'; import { expect } from 'chai'; -import { CurrencyManager, EvmChains } from '@requestnetwork/currency'; +import { CurrencyManager } from '@requestnetwork/currency'; import { chainlinkConversionPath } from '../../src/lib'; import { FAU_USD_RATE } from '../../scripts/test-deploy-batch-conversion-deployment'; import { localERC20AlphaArtifact, secondLocalERC20AlphaArtifact } from './localArtifacts'; @@ -29,7 +29,6 @@ const BATCH_PAYMENT_NETWORK_ID = PaymentTypes.BATCH_PAYMENT_NETWORK_ID; describe('contract: BatchConversionPayments', async () => { const networkConfig = network.config as HttpNetworkConfig; - EvmChains.assertChainSupported(network.name); const provider = new ethers.providers.JsonRpcProvider(networkConfig.url); let adminAddress: string; @@ -124,7 +123,6 @@ describe('contract: BatchConversionPayments', async () => { [adminAddress, from, to, feeAddress] = (await ethers.getSigners()).map((s) => s.address); [adminSigner, fromSigner, , , signer4] = await ethers.getSigners(); - EvmChains.assertChainSupported(network.name); chainlinkPath = chainlinkConversionPath.connect(network.name, fromSigner); const erc20FeeProxy = await new ERC20FeeProxy__factory(adminSigner).deploy(); diff --git a/packages/smart-contracts/test/contracts/BatchNoConversionErc20Payments.test.ts b/packages/smart-contracts/test/contracts/BatchNoConversionErc20Payments.test.ts index 7807c93903..0820fea5e3 100644 --- a/packages/smart-contracts/test/contracts/BatchNoConversionErc20Payments.test.ts +++ b/packages/smart-contracts/test/contracts/BatchNoConversionErc20Payments.test.ts @@ -12,7 +12,7 @@ import { TestERC20__factory, } from '../../src/types'; import { chainlinkConversionPath } from '../../src/lib'; -import { CurrencyManager, EvmChains } from '@requestnetwork/currency'; +import { CurrencyManager } from '@requestnetwork/currency'; import { PaymentTypes } from '@requestnetwork/types'; const logGasInfos = false; @@ -67,7 +67,6 @@ describe('contract: batchNoConversionPayments: ERC20', () => { erc20FeeProxy = await new ERC20FeeProxy__factory(owner).deploy(); const ethFeeProxy = await new EthereumFeeProxy__factory(owner).deploy(); - EvmChains.assertChainSupported(network.name); chainlinkPath = chainlinkConversionPath.connect(network.name, owner); batch = await new BatchNoConversionPayments__factory(owner).deploy( erc20FeeProxy.address, diff --git a/packages/smart-contracts/test/contracts/BatchNoConversionEthPayments.test.ts b/packages/smart-contracts/test/contracts/BatchNoConversionEthPayments.test.ts index 6b9b2a48c3..a72937472a 100644 --- a/packages/smart-contracts/test/contracts/BatchNoConversionEthPayments.test.ts +++ b/packages/smart-contracts/test/contracts/BatchNoConversionEthPayments.test.ts @@ -12,7 +12,7 @@ import { EthereumFeeProxy, BatchNoConversionPayments } from '../../src/types'; import { chainlinkConversionPath } from '../../src/lib'; import { HttpNetworkConfig } from 'hardhat/types'; import { PaymentTypes } from '@requestnetwork/types'; -import { CurrencyManager, EvmChains } from '@requestnetwork/currency'; +import { CurrencyManager } from '@requestnetwork/currency'; const logGasInfos = false; @@ -65,7 +65,6 @@ describe('contract: batchNoConversionPayments: Ethereum', () => { const erc20FeeProxy = await new ERC20FeeProxy__factory(owner).deploy(); ethFeeProxy = await new EthereumFeeProxy__factory(owner).deploy(); - EvmChains.assertChainSupported(network.name); chainlinkPath = chainlinkConversionPath.connect(network.name, owner); batch = await new BatchNoConversionPayments__factory(owner).deploy( erc20FeeProxy.address, diff --git a/packages/smart-contracts/test/contracts/ChainlinkConversionPath.test.ts b/packages/smart-contracts/test/contracts/ChainlinkConversionPath.test.ts index 1a8806e68b..e62d36c7c9 100644 --- a/packages/smart-contracts/test/contracts/ChainlinkConversionPath.test.ts +++ b/packages/smart-contracts/test/contracts/ChainlinkConversionPath.test.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { CurrencyManager, EvmChains } from '@requestnetwork/currency'; +import { CurrencyManager } from '@requestnetwork/currency'; import { ethers, network } from 'hardhat'; import '@nomiclabs/hardhat-ethers'; import { chainlinkConversionPath as chainlinkConvArtifact } from '../../src/lib'; @@ -25,7 +25,6 @@ let conversionPathInstance: ChainlinkConversionPath; describe('contract: ChainlinkConversionPath', () => { before(async () => { const [signer] = await ethers.getSigners(); - EvmChains.assertChainSupported(network.name); conversionPathInstance = chainlinkConvArtifact.connect(network.name, signer); USDT_address = localUSDTArtifact.getAddress(network.name); DAI_address = localERC20AlphaArtifact.getAddress(network.name); diff --git a/packages/smart-contracts/test/contracts/ERC20SwapToConversion.test.ts b/packages/smart-contracts/test/contracts/ERC20SwapToConversion.test.ts index 768f7987b3..67dcb76591 100644 --- a/packages/smart-contracts/test/contracts/ERC20SwapToConversion.test.ts +++ b/packages/smart-contracts/test/contracts/ERC20SwapToConversion.test.ts @@ -2,7 +2,7 @@ import { ethers, network } from 'hardhat'; import { BigNumber, Signer } from 'ethers'; import { expect, use } from 'chai'; import { solidity } from 'ethereum-waffle'; -import { CurrencyManager, EvmChains } from '@requestnetwork/currency'; +import { CurrencyManager } from '@requestnetwork/currency'; import { AggregatorMock__factory, ChainlinkConversionPath, @@ -49,7 +49,6 @@ describe('contract: ERC20SwapToConversion', () => { const erc20Liquidity = erc20Decimal.mul(100); before(async () => { - EvmChains.assertChainSupported(network.name); [, from, to, builder] = (await ethers.getSigners()).map((s) => s.address); [adminSigner, signer] = await ethers.getSigners(); chainlinkConversion = chainlinkConvArtifact.connect(network.name, adminSigner); diff --git a/packages/smart-contracts/test/contracts/ERC20SwapToPay.test.ts b/packages/smart-contracts/test/contracts/ERC20SwapToPay.test.ts index 072ed2bfde..21e0b20b11 100644 --- a/packages/smart-contracts/test/contracts/ERC20SwapToPay.test.ts +++ b/packages/smart-contracts/test/contracts/ERC20SwapToPay.test.ts @@ -13,7 +13,6 @@ import { TestERC20__factory, } from '../../src/types'; import { erc20FeeProxyArtifact, erc20SwapToPayArtifact } from '../../src/lib'; -import { EvmChains } from '@requestnetwork/currency'; use(solidity); @@ -39,7 +38,6 @@ describe('contract: SwapToPay', () => { const erc20Liquidity = erc20Decimal.mul(100); before(async () => { - EvmChains.assertChainSupported(network.name); [, from, to, builder] = (await ethers.getSigners()).map((s) => s.address); [adminSigner, signer] = await ethers.getSigners(); erc20FeeProxy = erc20FeeProxyArtifact.connect(network.name, adminSigner); diff --git a/packages/smart-contracts/test/contracts/Erc20ConversionProxy.test.ts b/packages/smart-contracts/test/contracts/Erc20ConversionProxy.test.ts index 68d8b38891..51b289f746 100644 --- a/packages/smart-contracts/test/contracts/Erc20ConversionProxy.test.ts +++ b/packages/smart-contracts/test/contracts/Erc20ConversionProxy.test.ts @@ -11,7 +11,7 @@ import { import { BigNumber, Signer } from 'ethers'; import { expect, use } from 'chai'; import { solidity } from 'ethereum-waffle'; -import { CurrencyManager, EvmChains } from '@requestnetwork/currency'; +import { CurrencyManager } from '@requestnetwork/currency'; import { chainlinkConversionPath } from '../../src/lib'; import { localERC20AlphaArtifact } from './localArtifacts'; @@ -41,7 +41,6 @@ describe('contract: Erc20ConversionProxy', () => { let chainlinkPath: ChainlinkConversionPath; before(async () => { - EvmChains.assertChainSupported(network.name); [from, to, feeAddress] = (await ethers.getSigners()).map((s) => s.address); [signer] = await ethers.getSigners(); chainlinkPath = chainlinkConversionPath.connect(network.name, signer); diff --git a/packages/smart-contracts/test/contracts/EthConversionProxy.test.ts b/packages/smart-contracts/test/contracts/EthConversionProxy.test.ts index e902d86815..8d3dd1637e 100644 --- a/packages/smart-contracts/test/contracts/EthConversionProxy.test.ts +++ b/packages/smart-contracts/test/contracts/EthConversionProxy.test.ts @@ -15,7 +15,7 @@ import { import { BigNumber, Signer } from 'ethers'; import { expect, use } from 'chai'; import { solidity } from 'ethereum-waffle'; -import { CurrencyManager, EvmChains } from '@requestnetwork/currency'; +import { CurrencyManager } from '@requestnetwork/currency'; import { chainlinkConversionPath } from '../../src/lib'; import { HttpNetworkConfig } from 'hardhat/types'; @@ -45,7 +45,6 @@ describe('contract: EthConversionProxy', () => { const provider = new ethers.providers.JsonRpcProvider(networkConfig.url); before(async () => { - EvmChains.assertChainSupported(network.name); [from, to, feeAddress] = (await ethers.getSigners()).map((s) => s.address); [signer] = await ethers.getSigners(); chainlinkPath = chainlinkConversionPath.connect(network.name, signer); diff --git a/packages/smart-contracts/test/contracts/EthereumFeeProxy.test.ts b/packages/smart-contracts/test/contracts/EthereumFeeProxy.test.ts index 395a5b3827..261c310002 100644 --- a/packages/smart-contracts/test/contracts/EthereumFeeProxy.test.ts +++ b/packages/smart-contracts/test/contracts/EthereumFeeProxy.test.ts @@ -11,7 +11,6 @@ import { } from '../../src/types'; import { ethereumFeeProxyArtifact } from '../../src/lib/'; import { HttpNetworkConfig } from 'hardhat/types'; -import { EvmChains } from '@requestnetwork/currency'; use(solidity); @@ -29,7 +28,6 @@ describe('contract: EthereumFeeProxy', () => { const feeAddress = '0xF4255c5e53a08f72b0573D1b8905C5a50aA9c2De'; before(async () => { - EvmChains.assertChainSupported(network.name); [, to] = (await ethers.getSigners()).map((s) => s.address); [signer] = await ethers.getSigners(); ethFeeProxy = ethereumFeeProxyArtifact.connect(network.name, signer); diff --git a/packages/smart-contracts/test/contracts/EthereumProxy.test.ts b/packages/smart-contracts/test/contracts/EthereumProxy.test.ts index 5946e0c3fd..17d0f19d14 100644 --- a/packages/smart-contracts/test/contracts/EthereumProxy.test.ts +++ b/packages/smart-contracts/test/contracts/EthereumProxy.test.ts @@ -11,7 +11,6 @@ import { } from '../../src/types'; import { ethereumProxyArtifact } from '../../src/lib/'; import { HttpNetworkConfig } from 'hardhat/types'; -import { EvmChains } from '@requestnetwork/currency'; use(solidity); @@ -29,7 +28,6 @@ describe('contract: EthereumProxy', () => { const provider = new ethers.providers.JsonRpcProvider(networkConfig.url); before(async () => { - EvmChains.assertChainSupported(network.name); [from, to] = (await ethers.getSigners()).map((s) => s.address); [signer] = await ethers.getSigners(); ethProxy = ethereumProxyArtifact.connect(network.name, signer); diff --git a/packages/smart-contracts/test/lib/artifact.test.ts b/packages/smart-contracts/test/lib/artifact.test.ts index dd98e3475e..1d4059cd2c 100644 --- a/packages/smart-contracts/test/lib/artifact.test.ts +++ b/packages/smart-contracts/test/lib/artifact.test.ts @@ -1,7 +1,6 @@ import { BigNumber, providers } from 'ethers'; import { RequestOpenHashSubmitter } from '../../src/types'; import { erc20FeeProxyArtifact, erc20ProxyArtifact } from '../../src/lib'; -import { CurrencyTypes } from '@requestnetwork/types'; describe('Artifact', () => { it('can get the contract info for latest version', () => { @@ -56,9 +55,9 @@ describe('Artifact', () => { }); it('throws for a non-existing network', () => { - expect(() => - erc20ProxyArtifact.getDeploymentInformation('fakenetwork' as CurrencyTypes.EvmChainName), - ).toThrowError(`No deployment for network: fakenetwork`); + expect(() => erc20ProxyArtifact.getDeploymentInformation('fakenetwork')).toThrowError( + `No deployment for network: fakenetwork`, + ); }); it('throws for a non-existing version', () => { diff --git a/packages/toolbox/src/chainlinkConversionPathTools.ts b/packages/toolbox/src/chainlinkConversionPathTools.ts index 6d165abaff..73856c93b1 100644 --- a/packages/toolbox/src/chainlinkConversionPathTools.ts +++ b/packages/toolbox/src/chainlinkConversionPathTools.ts @@ -5,9 +5,10 @@ import { ChainlinkConversionPath, ChainlinkConversionPath__factory, } from '@requestnetwork/smart-contracts/types'; -import { CurrencyManager, EvmChains, UnsupportedCurrencyError } from '@requestnetwork/currency'; +import { CurrencyManager, UnsupportedCurrencyError } from '@requestnetwork/currency'; import { retry } from '@requestnetwork/utils'; -import { CurrencyTypes } from '@requestnetwork/types'; +import { ChainTypes } from '@requestnetwork/types'; +import { ChainManager } from '@requestnetwork/chain/src'; export interface IOptions { network?: string; @@ -36,7 +37,7 @@ class ChainlinkConversionPathTools { * @param network The Ethereum network to use */ constructor( - private network: CurrencyTypes.EvmChainName, + private network: ChainTypes.IEvmChain, options?: { web3Url?: string; lastBlock?: number; maxRange?: number }, ) { const web3Url = @@ -48,11 +49,11 @@ class ChainlinkConversionPathTools { // Setup the conversion proxy contract interface this.chainLinkConversionPath = ChainlinkConversionPath__factory.connect( - chainlinkConversionPath.getAddress(network), + chainlinkConversionPath.getAddress(network.name), this.provider, ); - this.creationBlockNumber = chainlinkConversionPath.getCreationBlockNumber(this.network); + this.creationBlockNumber = chainlinkConversionPath.getCreationBlockNumber(this.network.name); this.maxRange = options?.maxRange || 1000000; } @@ -139,35 +140,39 @@ const getCurrency = (symbol: string) => { }; export const listAggregators = async (options?: IOptions): Promise => { - let networks: CurrencyTypes.EvmChainName[] = ['private', 'rinkeby', 'mainnet']; + let chains = [ + ChainManager.current().fromName('private', [ChainTypes.ECOSYSTEM.EVM]), + ChainManager.current().fromName('goerli', [ChainTypes.ECOSYSTEM.EVM]), + ChainManager.current().fromName('mainnet', [ChainTypes.ECOSYSTEM.EVM]), + ]; if (options?.network) { - EvmChains.assertChainSupported(options.network); - networks = [options.network]; + const chain = ChainManager.current().fromName(options.network, [ChainTypes.ECOSYSTEM.EVM]); + chains = [chain]; } // Create an Object to be used by a dijkstra algorithm to find the best path between two currencies const allAggregators: Record>> = {}; const aggregatorsNodesForDijkstra: Record>> = {}; - for (const network of networks) { - allAggregators[network] = {}; - const chainlinkConversionPathTools = new ChainlinkConversionPathTools(network, options); - allAggregators[network] = await chainlinkConversionPathTools.getAggregators(); + for (const chain of chains) { + allAggregators[chain.name] = {}; + const chainlinkConversionPathTools = new ChainlinkConversionPathTools(chain, options); + allAggregators[chain.name] = await chainlinkConversionPathTools.getAggregators(); // Include the reverse path of each aggregators - aggregatorsNodesForDijkstra[network] = {}; - for (let ccyIn in allAggregators[network]) { + aggregatorsNodesForDijkstra[chain.name] = {}; + for (let ccyIn in allAggregators[chain.name]) { ccyIn = ccyIn.toLowerCase(); - if (!aggregatorsNodesForDijkstra[network][ccyIn]) { - aggregatorsNodesForDijkstra[network][ccyIn] = {}; + if (!aggregatorsNodesForDijkstra[chain.name][ccyIn]) { + aggregatorsNodesForDijkstra[chain.name][ccyIn] = {}; } - for (let ccyOut in allAggregators[network][ccyIn]) { + for (let ccyOut in allAggregators[chain.name][ccyIn]) { ccyOut = ccyOut.toLowerCase(); - if (!aggregatorsNodesForDijkstra[network][ccyOut]) { - aggregatorsNodesForDijkstra[network][ccyOut] = {}; + if (!aggregatorsNodesForDijkstra[chain.name][ccyOut]) { + aggregatorsNodesForDijkstra[chain.name][ccyOut] = {}; } - aggregatorsNodesForDijkstra[network][ccyIn][ccyOut] = 1; - aggregatorsNodesForDijkstra[network][ccyOut][ccyIn] = 1; + aggregatorsNodesForDijkstra[chain.name][ccyIn][ccyOut] = 1; + aggregatorsNodesForDijkstra[chain.name][ccyOut][ccyIn] = 1; } } } diff --git a/packages/toolbox/src/commands/chainlink/addAggregator.ts b/packages/toolbox/src/commands/chainlink/addAggregator.ts index 7492f8da13..7285dc8253 100644 --- a/packages/toolbox/src/commands/chainlink/addAggregator.ts +++ b/packages/toolbox/src/commands/chainlink/addAggregator.ts @@ -2,7 +2,7 @@ import * as yargs from 'yargs'; import { runUpdate } from './contractUtils'; import { getAllAggregators, getCurrencyManager } from './aggregatorsUtils'; import assert from 'assert'; -import { EvmChains } from '@requestnetwork/currency'; +import { ChainTypes } from '@requestnetwork/types'; type Options = { dryRun: boolean; @@ -58,12 +58,12 @@ export const builder = (): yargs.Argv => export const handler = async (args: Options): Promise => { const { input, output, aggregator: aggregatorArg } = args; - const { network, list } = args; + const { network: chainName, list } = args; - EvmChains.assertChainSupported(network); const currencyManager = await getCurrencyManager(list); - const inputCcy = currencyManager.from(input, network) || currencyManager.from(input); - const outputCcy = currencyManager.from(output, network) || currencyManager.from(output); + const chain = currencyManager.chainManager.fromName(chainName, [ChainTypes.ECOSYSTEM.EVM]); + const inputCcy = currencyManager.from(input, chain) || currencyManager.from(input); + const outputCcy = currencyManager.from(output, chain) || currencyManager.from(output); let inputAddress = input; if (!inputAddress.startsWith('0x')) { @@ -80,7 +80,7 @@ export const handler = async (args: Options): Promise => { const aggregator = aggregatorArg || `${inputCcy?.symbol} / ${outputCcy?.symbol}`; let aggregatorAddress = aggregator; if (!aggregatorAddress.startsWith('0x')) { - const aggregators = await getAllAggregators(network); + const aggregators = await getAllAggregators(chain); const newAggregator = aggregators.find((x) => x.name === aggregator); assert(newAggregator, `aggregator ${aggregator} not found`); aggregatorAddress = newAggregator?.proxyAddress; diff --git a/packages/toolbox/src/commands/chainlink/addAggregators.ts b/packages/toolbox/src/commands/chainlink/addAggregators.ts index 0b036e8988..7f5e9aa8f7 100644 --- a/packages/toolbox/src/commands/chainlink/addAggregators.ts +++ b/packages/toolbox/src/commands/chainlink/addAggregators.ts @@ -2,7 +2,8 @@ import * as yargs from 'yargs'; import inquirer from 'inquirer'; import { runUpdate } from './contractUtils'; import { Aggregator, getAvailableAggregators, getCurrencyManager } from './aggregatorsUtils'; -import { conversionSupportedNetworks, EvmChains } from '@requestnetwork/currency'; +import { conversionSupportedNetworks } from '@requestnetwork/currency'; +import { ChainTypes } from '@requestnetwork/types'; type Options = { dryRun: boolean; @@ -68,21 +69,21 @@ const pickAggregators = async (aggregators: Aggregator[], pairs?: string[]) => { }; export const handler = async (args: Options): Promise => { - const { network, pair } = args; + const { network: chainName, pair } = args; const pairs = pair?.map((x) => x.toLowerCase().trim()); const currencyManager = await getCurrencyManager(args.list); + const chain = currencyManager.chainManager.fromName(chainName, [ChainTypes.ECOSYSTEM.EVM]); - EvmChains.assertChainSupported(network); - if (!conversionSupportedNetworks.includes(network)) { + if (!conversionSupportedNetworks.includes(chainName)) { console.warn( - `WARNING: ${network} is missing in conversionSupportedNetworks from the Currency package.`, - `Add '${network}: {}' to chainlinkCurrencyPairs, in currency/src/conversion-aggregators.ts.`, + `WARNING: ${chainName} is missing in conversionSupportedNetworks from the Currency package.`, + `Add '${chainName}: {}' to chainlinkCurrencyPairs, in currency/src/conversion-aggregators.ts.`, ); } const availableAggregators = await getAvailableAggregators( - network, + chain, currencyManager, pairs, args.listAll, diff --git a/packages/toolbox/src/commands/chainlink/aggregatorsUtils.ts b/packages/toolbox/src/commands/chainlink/aggregatorsUtils.ts index 917b1957d4..286e89bca6 100644 --- a/packages/toolbox/src/commands/chainlink/aggregatorsUtils.ts +++ b/packages/toolbox/src/commands/chainlink/aggregatorsUtils.ts @@ -1,5 +1,5 @@ import { AggregatorsMap, CurrencyInput, CurrencyManager } from '@requestnetwork/currency'; -import { CurrencyTypes, RequestLogicTypes } from '@requestnetwork/types'; +import { ChainTypes, RequestLogicTypes } from '@requestnetwork/types'; type Feed = { name: string; @@ -13,9 +13,7 @@ export type Aggregator = { aggregator: string; }; -const feedMap: Partial< - Record -> = { +const feedMap: Partial> = { mainnet: ['mainnet', 'Ethereum Mainnet'], goerli: ['goerli', 'Goerli Testnet'], sepolia: ['sepolia', 'Sepolia Testnet'], @@ -29,8 +27,8 @@ const feedMap: Partial< moonbeam: ['polkadot-mainnet-moonbeam', 'Moonbeam Mainnet'], }; -export const getAllAggregators = async (network: CurrencyTypes.EvmChainName): Promise => { - const [feedName, networkName] = feedMap[network] || []; +export const getAllAggregators = async (network: ChainTypes.IEvmChain): Promise => { + const [feedName, networkName] = feedMap[network.name] || []; if (!feedName || !networkName) { throw new Error( `network ${network} not supported by feed provider. Is it supported by Chainlink?`, @@ -49,18 +47,18 @@ export const getAllAggregators = async (network: CurrencyTypes.EvmChainName): Pr }; export const getAvailableAggregators = async ( - network: CurrencyTypes.EvmChainName, + chain: ChainTypes.IEvmChain, cm: CurrencyManager, pairs?: string[], listAll?: boolean, ): Promise => { - const feeds = await getAllAggregators(network); + const feeds = await getAllAggregators(chain); const missingAggregators: Aggregator[] = []; for (const feed of feeds) { const [from, to] = feed.name.split(' / '); - const fromCurrency = cm.from(from, network) || cm.from(from); - const toCurrency = cm.from(to, network) || cm.from(to); + const fromCurrency = cm.from(from, chain) || cm.from(from); + const toCurrency = cm.from(to, chain) || cm.from(to); if (pairs && !pairs.includes(`${from}-${to}`.toLowerCase())) { continue; } @@ -70,8 +68,8 @@ export const getAvailableAggregators = async ( fromCurrency.type !== RequestLogicTypes.CURRENCY.BTC && toCurrency.type !== RequestLogicTypes.CURRENCY.BTC && (fromCurrency.type === RequestLogicTypes.CURRENCY.ISO4217 || - fromCurrency.network === network) && - (listAll || !cm.getConversionPath(fromCurrency, toCurrency, network)) + fromCurrency.network.eq(chain)) && + (listAll || !cm.getConversionPath(fromCurrency, toCurrency, chain)) ) { missingAggregators.push({ name: feed.name, diff --git a/packages/toolbox/src/commands/chainlink/contractUtils.ts b/packages/toolbox/src/commands/chainlink/contractUtils.ts index 31ef9b2611..6a643cc20e 100644 --- a/packages/toolbox/src/commands/chainlink/contractUtils.ts +++ b/packages/toolbox/src/commands/chainlink/contractUtils.ts @@ -4,7 +4,8 @@ import { getDefaultProvider } from '@requestnetwork/payment-detection'; import { chainlinkConversionPath } from '@requestnetwork/smart-contracts'; import { GasFeeDefiner } from '@requestnetwork/ethereum-storage'; import { ChainlinkConversionPath } from '@requestnetwork/smart-contracts/types'; -import { EvmChains } from '@requestnetwork/currency'; +import { ChainManager } from '@requestnetwork/chain/src'; +import { ChainTypes } from '@requestnetwork/types'; export const runUpdate = async ( method: T, @@ -77,9 +78,8 @@ const connectChainlinkContracts = ({ dryRun, network, }: SharedOptions): ChainlinkContractWithVersion[] => { - EvmChains.assertChainSupported(network); - - const provider = getDefaultProvider(network); + const chain = ChainManager.current().fromName(network, [ChainTypes.ECOSYSTEM.EVM]); + const provider = getDefaultProvider(chain); const wallet = privateKey ? new Wallet(privateKey).connect(provider as any) // TODO diff --git a/packages/toolbox/src/commands/chainlink/getConversionPath.ts b/packages/toolbox/src/commands/chainlink/getConversionPath.ts index b4bfee0728..c75e296e5f 100644 --- a/packages/toolbox/src/commands/chainlink/getConversionPath.ts +++ b/packages/toolbox/src/commands/chainlink/getConversionPath.ts @@ -1,5 +1,6 @@ import * as yargs from 'yargs'; -import { CurrencyManager, EvmChains } from '@requestnetwork/currency'; +import { CurrencyManager } from '@requestnetwork/currency'; +import { ChainTypes } from '@requestnetwork/types'; type Options = { to: string; from: string; network: string }; @@ -27,6 +28,6 @@ export const handler = (args: yargs.Arguments): void => { const currencyManager = CurrencyManager.getDefault(); const from = currencyManager.from(args.from)!; const to = currencyManager.from(args.to)!; - EvmChains.assertChainSupported(args.network); - console.log(currencyManager.getConversionPath(from, to, args.network)); + const chain = currencyManager.chainManager.fromName(args.network, [ChainTypes.ECOSYSTEM.EVM]); + console.log(currencyManager.getConversionPath(from, to, chain)); }; diff --git a/packages/toolbox/src/commands/chainlink/listMissingAggregators.ts b/packages/toolbox/src/commands/chainlink/listMissingAggregators.ts index c7816cca14..b793ebaac1 100644 --- a/packages/toolbox/src/commands/chainlink/listMissingAggregators.ts +++ b/packages/toolbox/src/commands/chainlink/listMissingAggregators.ts @@ -1,6 +1,6 @@ import * as yargs from 'yargs'; import { getAvailableAggregators, getCurrencyManager } from './aggregatorsUtils'; -import { EvmChains } from '@requestnetwork/currency'; +import { ChainTypes } from '@requestnetwork/types'; type Options = { network: string[]; @@ -30,11 +30,11 @@ export const builder = (): yargs.Argv => export const handler = async (args: Options): Promise => { const { list } = args; const currencyManager = await getCurrencyManager(list); - for (const network of args.network) { - EvmChains.assertChainSupported(network); - const available = await getAvailableAggregators(network, currencyManager); + for (const chainName of args.network) { + const chain = currencyManager.chainManager.fromName(chainName, [ChainTypes.ECOSYSTEM.EVM]); + const available = await getAvailableAggregators(chain, currencyManager); if (available.length > 0) { - console.log(network); + console.log(chainName); console.log(available.map((x) => x.name).join('\n')); console.log(); } diff --git a/packages/toolbox/src/commands/hash/submit.ts b/packages/toolbox/src/commands/hash/submit.ts index a1b75671db..c7d1db2fa6 100644 --- a/packages/toolbox/src/commands/hash/submit.ts +++ b/packages/toolbox/src/commands/hash/submit.ts @@ -5,7 +5,8 @@ import { InferArgs } from '../../types'; import yargs from 'yargs'; import { getWallet } from '../transaction/utils'; import { EthereumTransactionSubmitter, IpfsStorage } from '@requestnetwork/ethereum-storage'; -import { EvmChains } from '@requestnetwork/currency'; +import { ChainManager } from '@requestnetwork/chain/src'; +import { ChainTypes } from '@requestnetwork/types'; export const command = 'hash submit '; export const describe = 'Forces the submission of an IPFS hash to the Request HashStorage contract'; @@ -17,9 +18,9 @@ export const builder = (y: yargs.Argv) => .option('dryRun', { type: 'boolean', default: false }); export const handler = async (argv: yargs.Arguments>>) => { - EvmChains.assertChainSupported(argv.chainName); + const chain = ChainManager.current().fromName(argv.chainName, [ChainTypes.ECOSYSTEM.EVM]); - const wallet = await getWallet({ chainName: argv.chainName, dryRun: argv.dryRun }); + const wallet = await getWallet({ chain, dryRun: argv.dryRun }); const ipfsStorage = new IpfsStorage({ ipfsTimeout: 5000, }); diff --git a/packages/toolbox/src/commands/transaction/nonce.ts b/packages/toolbox/src/commands/transaction/nonce.ts index 9200f639ba..42120afbe9 100644 --- a/packages/toolbox/src/commands/transaction/nonce.ts +++ b/packages/toolbox/src/commands/transaction/nonce.ts @@ -2,6 +2,8 @@ import * as yargs from 'yargs'; import { InferArgs } from '../../types'; import { getProvider, getWallet } from './utils'; +import { ChainManager } from '@requestnetwork/chain/src'; +import { ChainTypes } from '@requestnetwork/types'; export const command = 'nonce'; export const describe = 'Gets a wallet nonce'; @@ -17,10 +19,11 @@ export const builder = (y: yargs.Argv) => { export const handler = async (argv: InferArgs>) => { let address = argv.address; - const provider = await getProvider(argv.chainName); + const chain = ChainManager.current().fromName(argv.chainName, [ChainTypes.ECOSYSTEM.EVM]); + const provider = await getProvider(chain); if (!address) { - const wallet = await getWallet({ chainName: argv.chainName, provider }); + const wallet = await getWallet({ chain, provider }); address = wallet.address; } const nonce = await provider.getTransactionCount(address); diff --git a/packages/toolbox/src/commands/transaction/retry.ts b/packages/toolbox/src/commands/transaction/retry.ts index 3aa66025fa..ede1bc1a93 100644 --- a/packages/toolbox/src/commands/transaction/retry.ts +++ b/packages/toolbox/src/commands/transaction/retry.ts @@ -5,6 +5,8 @@ import { InferArgs } from '../../types'; import yargs from 'yargs'; import { getWallet } from './utils'; import { providers, utils } from 'ethers'; +import { ChainManager } from '@requestnetwork/chain/src'; +import { ChainTypes } from '@requestnetwork/types'; export const command = 'transaction retry '; export const describe = 'Retries sending a pending transaction stuck in the mempool'; @@ -18,7 +20,8 @@ export const builder = (y: yargs.Argv) => .option('dryRun', { type: 'boolean', default: false }); export const handler = async (argv: yargs.Arguments>>) => { - const wallet = await getWallet({ chainName: argv.chainName, dryRun: argv.dryRun }); + const chain = ChainManager.current().fromName(argv.chainName, [ChainTypes.ECOSYSTEM.EVM]); + const wallet = await getWallet({ chain, dryRun: argv.dryRun }); const tx = await wallet.provider.getTransaction(argv.txHash); diff --git a/packages/toolbox/src/commands/transaction/utils.ts b/packages/toolbox/src/commands/transaction/utils.ts index add6f95f62..ebf7539d50 100644 --- a/packages/toolbox/src/commands/transaction/utils.ts +++ b/packages/toolbox/src/commands/transaction/utils.ts @@ -1,19 +1,20 @@ import { getDefaultProvider } from '@requestnetwork/payment-detection'; import { providers, Wallet } from 'ethers'; +import { ChainTypes } from '@requestnetwork/types'; export const getWallet = async ({ - chainName, + chain, dryRun = false, provider, }: { - chainName: string; + chain: ChainTypes.IEvmChain; dryRun?: boolean; provider?: providers.Provider; }): Promise => { const privateKey = process.env.PRIVATE_KEY; if (!privateKey && !dryRun) throw new Error('env var PRIVATE_KEY is required'); if (!provider) { - provider = await getProvider(chainName); + provider = await getProvider(chain); } return dryRun @@ -21,16 +22,15 @@ export const getWallet = async ({ : new Wallet(privateKey || '').connect(provider); }; -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -export const getProvider = async (chainName: string) => { - const chain = await getChainConfig(chainName); - if (chain) { - const rpc = chain.rpcUrls[0] +export const getProvider = async (chain: ChainTypes.IEvmChain) => { + const chainConfig = await getChainConfig(chain?.name); + if (chainConfig) { + const rpc = chainConfig.rpcUrls[0] .replace('{ALCHEMY_API_KEY}', process.env.ALCHEMY_API_KEY || '') .replace('{INFURA_API_KEY}', process.env.INFURA_API_KEY || ''); return new providers.StaticJsonRpcProvider(rpc); } - return getDefaultProvider(chainName); + return getDefaultProvider(chain); }; const getChainConfig = async ( diff --git a/packages/types/src/advanced-logic-types.ts b/packages/types/src/advanced-logic-types.ts index 34bfede9b4..7841e077e5 100644 --- a/packages/types/src/advanced-logic-types.ts +++ b/packages/types/src/advanced-logic-types.ts @@ -1,7 +1,7 @@ import * as Extension from './extension-types'; import * as Identity from './identity-types'; import * as RequestLogic from './request-logic-types'; -import { ChainName } from './currency-types'; +import { IChain } from './chain-types'; /** Advanced Logic extensions */ export interface IAdvancedLogicExtensions { @@ -33,13 +33,13 @@ export interface IAdvancedLogic { timestamp: number, ) => RequestLogic.IExtensionStates; getNativeTokenExtensionForNetwork: ( - network: ChainName, + chain: IChain, ) => Extension.IExtension | undefined; getAnyToNativeTokenExtensionForNetwork: ( - network: ChainName, + chain: IChain, ) => Extension.IExtension | undefined; getFeeProxyContractErc20ForNetwork: ( - network?: ChainName, + chain?: IChain, ) => Extension.PnFeeReferenceBased.IFeeReferenceBased | undefined; extensions: IAdvancedLogicExtensions; } diff --git a/packages/types/src/chain-types.ts b/packages/types/src/chain-types.ts new file mode 100644 index 0000000000..2d8a9b963e --- /dev/null +++ b/packages/types/src/chain-types.ts @@ -0,0 +1,90 @@ +import { RequestLogicTypes } from './index'; + +/** + * List of ecosystems supported by the Request Network protocol + */ +export enum ECOSYSTEM { + BTC = 'BTC', + DECLARATIVE = 'DECLARATIVE', + EVM = 'EVM', + NEAR = 'NEAR', +} + +/** + * List of ecosystems supported by TheGraph + */ +export const VM_ECOSYSTEMS = [ECOSYSTEM.EVM, ECOSYSTEM.NEAR] as const; +export type VmChainEcosystem = (typeof VM_ECOSYSTEMS)[number]; + +export interface IChainCommon { + id: string; + name: string; + testnet: boolean; + ecosystem: ECOSYSTEM; + eq(chain: IChainCommon): boolean; +} + +export interface IBtcChain extends IChainCommon { + ecosystem: ECOSYSTEM.BTC; +} + +export interface IDeclarativeChain extends IChainCommon { + ecosystem: ECOSYSTEM.DECLARATIVE; +} + +export interface IEvmChain extends IChainCommon { + ecosystem: ECOSYSTEM.EVM; +} + +export interface INearChain extends IChainCommon { + ecosystem: ECOSYSTEM.NEAR; +} + +/** + * List of ecosystems supported by the Request Network protocol + */ +export type ChainTypeByEcosystem = { + [ECOSYSTEM.BTC]: IBtcChain; + [ECOSYSTEM.DECLARATIVE]: IDeclarativeChain; + [ECOSYSTEM.EVM]: IEvmChain; + [ECOSYSTEM.NEAR]: INearChain; +}; +export type IChain = ChainTypeByEcosystem[ECOSYSTEM]; + +/** + * VmChains are Virtual Machine chains supported by TheGraph + */ +export type IVmChain = ChainTypeByEcosystem[VmChainEcosystem]; + +export interface IEcosystem { + name: E; + chainClass: new (id: string, name: string, testnet?: boolean) => ChainTypeByEcosystem[E]; + chains: Record; + currencyTypes: RequestLogicTypes.CURRENCY[]; + chainNames: string[]; + assertChainNameSupported(chainName?: string): asserts chainName is string; + assertChainSupported(chain?: IChain): asserts chain is ChainTypeByEcosystem[E]; + isChainSupported(chainName?: string | IChain): boolean; + isSameChainFromString(chain1: string, chain2: string): boolean; +} + +export interface IChainManager { + chains: IChain[]; + ecosystems: { + [E in ECOSYSTEM]: IEcosystem; + }; + getEcosystemsByCurrencyType(currencyType: RequestLogicTypes.CURRENCY): ECOSYSTEM[]; + fromName( + chainName: string, + ecosystemsFilter?: T, + ): ChainTypeByEcosystem[T[number]]; + fromId( + chainId: string, + ecosystemsFilter?: T, + ): ChainTypeByEcosystem[T[number]]; + isSameChain( + chain1: string | IChain, + chain2: string | IChain, + chainsEcosystem?: readonly ECOSYSTEM[], + ): boolean; +} diff --git a/packages/types/src/currency-types.ts b/packages/types/src/currency-types.ts deleted file mode 100644 index 2c0c6ce0a2..0000000000 --- a/packages/types/src/currency-types.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * List of supported EVM chains - */ -export type EvmChainName = - | 'alfajores' - | 'arbitrum-one' - | 'arbitrum-rinkeby' - | 'avalanche' - | 'bsc' - | 'bsctest' - | 'celo' - | 'core' - | 'fantom' - | 'fuse' - | 'goerli' - | 'mainnet' - | 'mantle' - | 'mantle-testnet' - | 'matic' - | 'moonbeam' - | 'mumbai' - | 'optimism' - | 'private' - | 'rinkeby' // FIXME: Rinkeby is deprecated - | 'sepolia' - | 'ronin' - | 'sokol' - | 'tombchain' - | 'xdai' - | 'zksynceratestnet' - | 'zksyncera' - | 'base'; - -/** - * List of supported BTC chains - */ -export type BtcChainName = 'mainnet' | 'testnet'; - -/** - * List of supported Declarative chains - */ -export type DeclarativeChainName = 'tron' | 'solana'; - -/** - * List of supported NEAR chains - * FIXME: get rid of the wrong 'aurora' alias - */ -export type NearChainName = 'aurora' | 'aurora-testnet' | 'near' | 'near-testnet'; - -export type ChainName = EvmChainName | BtcChainName | NearChainName | DeclarativeChainName; - -/** - * Virtual machin chains, where payment proxy contracts can be deployed - */ -export type VMChainName = EvmChainName | NearChainName; diff --git a/packages/types/src/extensions/pn-any-reference-based-types.ts b/packages/types/src/extensions/pn-any-reference-based-types.ts index b9861a38d3..a45bae072d 100644 --- a/packages/types/src/extensions/pn-any-reference-based-types.ts +++ b/packages/types/src/extensions/pn-any-reference-based-types.ts @@ -1,5 +1,4 @@ import { PnAddressBased } from '../extension-types'; -import { ChainName } from '../currency-types'; export { ACTION, IAddPaymentAddressParameters, @@ -18,5 +17,5 @@ export interface IValues extends PnAddressBased.IValues { /** Parameters of creation action */ export interface ICreationParameters extends PnAddressBased.ICreationParameters { salt?: string; - paymentNetworkName?: ChainName; + paymentNetworkName?: string; } diff --git a/packages/types/src/extensions/pn-any-to-any-conversion-types.ts b/packages/types/src/extensions/pn-any-to-any-conversion-types.ts index 93460d3f7d..abd5da04b3 100644 --- a/packages/types/src/extensions/pn-any-to-any-conversion-types.ts +++ b/packages/types/src/extensions/pn-any-to-any-conversion-types.ts @@ -1,5 +1,4 @@ import { PnFeeReferenceBased } from '../extension-types'; -import { ChainName } from '../currency-types'; export { IAddPaymentAddressParameters, IAddRefundAddressParameters, @@ -13,5 +12,5 @@ export type IConversionReferenceBased /** Parameters for the creation action */ export interface ICreationParameters extends PnFeeReferenceBased.ICreationParameters { maxRateTimespan?: number; - network?: ChainName; + network?: string; } diff --git a/packages/types/src/extensions/pn-any-to-erc20-types.ts b/packages/types/src/extensions/pn-any-to-erc20-types.ts index 679ebf1207..4ae926e630 100644 --- a/packages/types/src/extensions/pn-any-to-erc20-types.ts +++ b/packages/types/src/extensions/pn-any-to-erc20-types.ts @@ -1,11 +1,10 @@ import * as PnAnyToAnyConversion from './pn-any-to-any-conversion-types'; -import { EvmChainName } from '../currency-types'; /** Any to ERC20 reference-based payment network extension interface */ export type IAnyToERC20 = PnAnyToAnyConversion.IConversionReferenceBased; /** Parameters for the creation action */ export interface ICreationParameters extends PnAnyToAnyConversion.ICreationParameters { - network: EvmChainName; + network: string; acceptedTokens: string[]; } diff --git a/packages/types/src/extensions/pn-any-to-eth-types.ts b/packages/types/src/extensions/pn-any-to-eth-types.ts index 8c120cea2f..ae654c1db7 100644 --- a/packages/types/src/extensions/pn-any-to-eth-types.ts +++ b/packages/types/src/extensions/pn-any-to-eth-types.ts @@ -1,4 +1,3 @@ -import { ChainName } from '../currency-types'; import * as PnAnyToAnyConversion from './pn-any-to-any-conversion-types'; /** Any to ETH reference-based payment network extension interface */ @@ -6,5 +5,5 @@ export type IAnyToEth = PnAnyToAnyConversion.IConversionReferenceBased & { - network: ChainName; + network: string; }; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 1554830bf0..7129b4b490 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,6 +1,6 @@ import * as AdvancedLogicTypes from './advanced-logic-types'; import * as ClientTypes from './client-types'; -import * as CurrencyTypes from './currency-types'; +import * as ChainTypes from './chain-types'; import * as DataAccessTypes from './data-access-types'; import * as DecryptionProviderTypes from './decryption-provider-types'; import * as EncryptionTypes from './encryption-types'; @@ -20,7 +20,7 @@ import * as FeeTypes from './fees-types'; export { AdvancedLogicTypes, ClientTypes, - CurrencyTypes, + ChainTypes, DataAccessTypes, DecryptionProviderTypes, EncryptionTypes, diff --git a/packages/types/src/request-logic-types.ts b/packages/types/src/request-logic-types.ts index 3aea78052b..10cfb0577b 100644 --- a/packages/types/src/request-logic-types.ts +++ b/packages/types/src/request-logic-types.ts @@ -5,7 +5,6 @@ import * as Extension from './extension-types'; import * as Identity from './identity-types'; import * as Signature from './signature-types'; import * as Transaction from './transaction-types'; -import { CurrencyTypes } from './index'; /** Request Logic layer */ export interface IRequestLogic { @@ -267,7 +266,7 @@ export interface ICurrency { /** The currency value (e.g.: '0x123...789', 'EUR', 'ETH') */ value: string; /** The currency network (e.g.: 'mainnet', 'rinkeby', 'bank_sandbox') */ - network?: CurrencyTypes.ChainName; + network?: string; } /** Enum of name possible in a action */ diff --git a/packages/utils/src/providers.ts b/packages/utils/src/providers.ts index 74cda62767..e27a456e2e 100644 --- a/packages/utils/src/providers.ts +++ b/packages/utils/src/providers.ts @@ -1,4 +1,4 @@ -import { LogTypes } from '@requestnetwork/types'; +import { ChainTypes, LogTypes } from '@requestnetwork/types'; import { providers, constants, utils } from 'ethers'; @@ -133,8 +133,11 @@ const setProviderFactory = (providerFactory?: CurrentProviderFactory): void => { * * @param network the blockchain network. See https://chainid.network/chains.json `network` field for reference */ -const getDefaultProvider = (network?: string): providers.Provider => { - const provider = currentProviderFactory(network, defaultProviderFactory); +const getDefaultProvider = (network?: ChainTypes.IChain | string): providers.Provider => { + const provider = currentProviderFactory( + typeof network === 'string' ? network : network?.name, + defaultProviderFactory, + ); if (typeof provider === 'string') { return new providers.StaticJsonRpcProvider(provider); }