Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: chain manager WIP #1352

Draft
wants to merge 17 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -555,6 +570,9 @@ workflows:
- test-utils:
requires:
- build
- test-chain:
requires:
- build
- test-currency:
requires:
- build
Expand Down Expand Up @@ -588,6 +606,7 @@ workflows:
requires:
- lint
- test-advanced-logic
- test-chain
- test-currency
- test-data-access
- test-data-format
Expand Down
1 change: 1 addition & 0 deletions packages/advanced-logic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"test:watch": "yarn test --watch"
},
"dependencies": {
"@requestnetwork/chain": "0.13.0",
"@requestnetwork/currency": "0.13.0",
"@requestnetwork/types": "0.40.0",
"@requestnetwork/utils": "0.40.0",
Expand Down
46 changes: 27 additions & 19 deletions packages/advanced-logic/src/advanced-logic.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -108,7 +108,7 @@ 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 chain = this.getChain(extensionAction, requestState);
const extension: ExtensionTypes.IExtension | undefined = {
[ExtensionTypes.ID.CONTENT_DATA]: this.extensions.contentData,
[ExtensionTypes.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED]: this.extensions.addressBasedBtc,
Expand All @@ -118,17 +118,17 @@ 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];
Expand All @@ -138,7 +138,7 @@ export default class AdvancedLogic implements AdvancedLogicTypes.IAdvancedLogic
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 with id: ${id} not found for network: ${chain}`);
}

throw Error(`extension not recognized, id: ${id}`);
Expand All @@ -147,7 +147,7 @@ export default class AdvancedLogic implements AdvancedLogicTypes.IAdvancedLogic
}

public getNativeTokenExtensionForNetwork(
network?: CurrencyTypes.ChainName,
network?: ChainTypes.IChain,
): ExtensionTypes.IExtension<ExtensionTypes.PnReferenceBased.ICreationParameters> | undefined {
return network
? this.extensions.nativeToken.find((nativeTokenExtension) =>
Expand All @@ -157,7 +157,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) =>
Expand All @@ -166,30 +166,38 @@ 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.near.isChainSupported(network)
? new FeeProxyContractErc20(this.currencyManager, undefined, undefined, network)
: this.extensions.feeProxyContractErc20;
}

protected getNetwork(
private getChain(
extensionAction: ExtensionTypes.IAction,
requestState: RequestLogicTypes.IRequest,
): CurrencyTypes.ChainName | undefined {
): ChainTypes.IChain | undefined {
if (
requestState.currency.network &&
requestState.currency.type &&
extensionAction.parameters.paymentNetworkName &&
!isSameChain(requestState.currency.network, extensionAction.parameters.paymentNetworkName)
!this.currencyManager.chainManager.isSameChain(
requestState.currency.network,
extensionAction.parameters.paymentNetworkName,
this.currencyManager.chainManager.getEcosystemsByCurrencyType(requestState.currency.type),
)
) {
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);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ICurrencyManager, UnsupportedCurrencyError } from '@requestnetwork/currency';
import {
CurrencyTypes,
ChainTypes,
ExtensionTypes,
IdentityTypes,
RequestLogicTypes,
Expand Down Expand Up @@ -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', ['evm']),
);
default:
throw new Error(
`Default implementation of isValidAddressForNetwork() does not support currency type ${this.supportedCurrencyType}. Please override this method if needed.`,
Expand All @@ -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);
}
Expand Down Expand Up @@ -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;
}
}

Expand All @@ -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}`,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ export default class AnyToErc20ProxyPaymentNetwork extends Erc20FeeProxyPaymentN
if (!acceptedCurrency) {
throw new UnsupportedCurrencyError({
value: address,
network,
network: network?.name,
});
}
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}.`,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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);
}
Expand Down Expand Up @@ -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;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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, ['btc']),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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, ['btc']),
);
}
}
Loading