Skip to content

Commit ac79133

Browse files
authored
feat: any to near advanced logic (#842)
1 parent 97ee435 commit ac79133

File tree

16 files changed

+1237
-53
lines changed

16 files changed

+1237
-53
lines changed

packages/advanced-logic/src/advanced-logic.ts

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import NearNative from './extensions/payment-network/near-native';
2020
import AnyToErc20Proxy from './extensions/payment-network/any-to-erc20-proxy';
2121
import AnyToEthProxy from './extensions/payment-network/any-to-eth-proxy';
2222
import NativeTokenPaymentNetwork from './extensions/payment-network/native-token';
23+
import AnyToNear from './extensions/payment-network/any-to-near';
24+
import AnyToNativeTokenPaymentNetwork from './extensions/payment-network/any-to-native';
2325

2426
/**
2527
* Module to manage Advanced logic extensions
@@ -41,6 +43,7 @@ export default class AdvancedLogic implements AdvancedLogicTypes.IAdvancedLogic
4143
erc777Stream: Erc777Stream;
4244
feeProxyContractEth: FeeProxyContractEth;
4345
anyToEthProxy: AnyToEthProxy;
46+
anyToNativeToken: AnyToNativeTokenPaymentNetwork[];
4447
};
4548

4649
constructor(currencyManager?: ICurrencyManager) {
@@ -61,6 +64,7 @@ export default class AdvancedLogic implements AdvancedLogicTypes.IAdvancedLogic
6164
feeProxyContractEth: new FeeProxyContractEth(),
6265
anyToEthProxy: new AnyToEthProxy(currencyManager),
6366
nativeToken: [new NearNative()],
67+
anyToNativeToken: [new AnyToNear(currencyManager)],
6468
};
6569
}
6670
/**
@@ -115,14 +119,20 @@ export default class AdvancedLogic implements AdvancedLogicTypes.IAdvancedLogic
115119
[ExtensionTypes.ID.PAYMENT_NETWORK_ETH_FEE_PROXY_CONTRACT]:
116120
this.extensions.feeProxyContractEth,
117121
[ExtensionTypes.ID.PAYMENT_NETWORK_ANY_TO_ETH_PROXY]: this.extensions.anyToEthProxy,
122+
[ExtensionTypes.ID.PAYMENT_NETWORK_ANY_TO_NATIVE_TOKEN]:
123+
this.getAnyToNativeTokenExtensionForActionAndState(extensionAction, requestState),
118124
}[id];
119125

120126
if (!extension) {
121-
if (id === ExtensionTypes.ID.PAYMENT_NETWORK_NATIVE_TOKEN) {
122-
throw Error(
123-
`extension with id: ${id} not found for network: ${requestState.currency.network}`,
124-
);
127+
if (
128+
id === ExtensionTypes.ID.PAYMENT_NETWORK_NATIVE_TOKEN ||
129+
id === ExtensionTypes.ID.PAYMENT_NETWORK_ANY_TO_NATIVE_TOKEN
130+
) {
131+
const network =
132+
this.getNetwork(extensionAction, requestState) || requestState.currency.network;
133+
throw Error(`extension with id: ${id} not found for network: ${network}`);
125134
}
135+
126136
throw Error(`extension not recognized, id: ${id}`);
127137
}
128138
return extension;
@@ -148,4 +158,29 @@ export default class AdvancedLogic implements AdvancedLogicTypes.IAdvancedLogic
148158
)
149159
: undefined;
150160
}
161+
162+
protected getAnyToNativeTokenExtensionForActionAndState(
163+
extensionAction: ExtensionTypes.IAction,
164+
requestState: RequestLogicTypes.IRequest,
165+
): ExtensionTypes.IExtension<ExtensionTypes.PnAnyToEth.ICreationParameters> | undefined {
166+
const network = this.getNetwork(extensionAction, requestState);
167+
168+
return network
169+
? this.extensions.anyToNativeToken.find((anyToNativeTokenExtension) =>
170+
anyToNativeTokenExtension.supportedNetworks.includes(network),
171+
)
172+
: undefined;
173+
}
174+
175+
protected getNetwork(
176+
extensionAction: ExtensionTypes.IAction,
177+
requestState: RequestLogicTypes.IRequest,
178+
): string | undefined {
179+
const network =
180+
extensionAction.action === 'create'
181+
? extensionAction.parameters.network
182+
: requestState.extensions[ExtensionTypes.ID.PAYMENT_NETWORK_ANY_TO_NATIVE_TOKEN]?.values
183+
?.network;
184+
return network;
185+
}
151186
}

packages/advanced-logic/src/extensions/payment-network/address-based.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,15 @@ export default abstract class AddressBasedPaymentNetwork<
283283
if (request.currency.type !== this.supportedCurrencyType) {
284284
throw Error(`This extension can be used only on ${this.supportedCurrencyType} requests`);
285285
}
286-
if (request.currency.network && !this.supportedNetworks.includes(request.currency.network)) {
287-
throw new UnsupportedNetworkError(request.currency.network, this.supportedNetworks);
286+
this.throwIfInvalidNetwork(request.currency.network);
287+
}
288+
289+
protected throwIfInvalidNetwork(network?: string): asserts network is string {
290+
if (!network) {
291+
throw Error('network is required');
292+
}
293+
if (network && this.supportedNetworks && !this.supportedNetworks.includes(network)) {
294+
throw new UnsupportedNetworkError(network, this.supportedNetworks);
288295
}
289296
}
290297
}

packages/advanced-logic/src/extensions/payment-network/any-to-erc20-proxy.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,8 @@ export default class AnyToErc20ProxyPaymentNetwork extends Erc20FeeProxyPaymentN
3838
if (creationParameters.acceptedTokens.some((address) => !this.isValidAddress(address))) {
3939
throw Error('acceptedTokens must contains only valid ethereum addresses');
4040
}
41-
4241
const network = creationParameters.network;
43-
if (!network) {
44-
throw Error('network is required');
45-
}
46-
if (!conversionSupportedNetworks.includes(network)) {
47-
throw Error(`network ${network} not supported`);
48-
}
42+
this.throwIfInvalidNetwork(network);
4943

5044
for (const address of creationParameters.acceptedTokens) {
5145
const acceptedCurrency = this.currencyManager.fromAddress(address, network);

packages/advanced-logic/src/extensions/payment-network/any-to-eth-proxy.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,7 @@ export default class AnyToEthProxyPaymentNetwork extends EthereumFeeProxyPayment
2727
public createCreationAction(
2828
creationParameters: ExtensionTypes.PnAnyToEth.ICreationParameters,
2929
): ExtensionTypes.IAction {
30-
const network = creationParameters.network;
31-
if (!network) {
32-
throw Error('network is required');
33-
}
34-
if (!conversionSupportedNetworks.includes(network)) {
35-
throw Error(`network ${network} not supported`);
36-
}
30+
this.throwIfInvalidNetwork(creationParameters.network);
3731
return super.createCreationAction(creationParameters);
3832
}
3933

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { FeeReferenceBasedPaymentNetwork } from './fee-reference-based';
2+
import { ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types';
3+
import { InvalidPaymentAddressError } from './address-based';
4+
5+
export default abstract class AnyToNativeTokenPaymentNetwork extends FeeReferenceBasedPaymentNetwork {
6+
public constructor(
7+
extensionId: ExtensionTypes.ID,
8+
currentVersion: string,
9+
supportedNetworks: string[],
10+
) {
11+
super(extensionId, currentVersion, supportedNetworks, RequestLogicTypes.CURRENCY.ETH);
12+
}
13+
14+
public createCreationAction(
15+
creationParameters: ExtensionTypes.PnAnyToAnyConversion.ICreationParameters,
16+
): ExtensionTypes.IAction<ExtensionTypes.PnAnyToAnyConversion.ICreationParameters> {
17+
const network = creationParameters.network;
18+
this.throwIfInvalidNetwork(network);
19+
20+
if (
21+
creationParameters.paymentAddress &&
22+
!this.isValidAddress(creationParameters.paymentAddress, network)
23+
) {
24+
throw new InvalidPaymentAddressError(creationParameters.paymentAddress);
25+
}
26+
if (
27+
creationParameters.refundAddress &&
28+
!this.isValidAddress(creationParameters.refundAddress, network)
29+
) {
30+
throw new InvalidPaymentAddressError(creationParameters.refundAddress, 'refundAddress');
31+
}
32+
if (
33+
creationParameters.feeAddress &&
34+
!this.isValidAddress(creationParameters.feeAddress, network)
35+
) {
36+
throw new InvalidPaymentAddressError(creationParameters.feeAddress, 'feeAddress');
37+
}
38+
if (creationParameters.maxRateTimespan && creationParameters.maxRateTimespan < 0) {
39+
throw new InvalidMaxRateTimespanError(creationParameters.maxRateTimespan);
40+
}
41+
return super.createCreationAction(creationParameters);
42+
}
43+
44+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
45+
protected abstract isValidAddress(_address: string, _networkName?: string): boolean;
46+
}
47+
48+
export class InvalidMaxRateTimespanError extends Error {
49+
constructor(maxRateTimespan: number) {
50+
super(`${maxRateTimespan} is not a valid maxRateTimespan`);
51+
}
52+
}
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
import { ICurrencyManager, UnsupportedCurrencyError } from '@requestnetwork/currency';
2+
import { ExtensionTypes, IdentityTypes, RequestLogicTypes } from '@requestnetwork/types';
3+
import { UnsupportedNetworkError } from './address-based';
4+
import AnyToNativeTokenPaymentNetwork from './any-to-native';
5+
6+
const CURRENT_VERSION = '0.2.0';
7+
const supportedNetworks = ['aurora', 'aurora-testnet'];
8+
9+
export default class AnyToNearPaymentNetwork extends AnyToNativeTokenPaymentNetwork {
10+
public constructor(
11+
private currencyManager: ICurrencyManager,
12+
extensionId: ExtensionTypes.ID = ExtensionTypes.ID.PAYMENT_NETWORK_ANY_TO_NATIVE_TOKEN,
13+
currentVersion: string = CURRENT_VERSION,
14+
) {
15+
super(extensionId, currentVersion, supportedNetworks);
16+
}
17+
18+
/**
19+
* Check if a near address is valid
20+
*
21+
* @param {string} address address to check
22+
* @returns {boolean} true if address is valid
23+
*/
24+
protected isValidAddress(address: string, networkName?: string): boolean {
25+
switch (networkName) {
26+
case 'aurora':
27+
return this.isValidMainnetAddress(address);
28+
case 'aurora-testnet':
29+
return this.isValidTestnetAddress(address);
30+
case undefined:
31+
return this.isValidMainnetAddress(address) || this.isValidTestnetAddress(address);
32+
default:
33+
throw new UnsupportedNetworkError(networkName, this.supportedNetworks);
34+
}
35+
}
36+
37+
private isValidMainnetAddress(address: string): boolean {
38+
return this.isValidAddressForSymbolAndNetwork(address, 'NEAR', 'aurora');
39+
}
40+
41+
private isValidTestnetAddress(address: string): boolean {
42+
return this.isValidAddressForSymbolAndNetwork(address, 'NEAR-testnet', 'aurora-testnet');
43+
}
44+
45+
/**
46+
* Applies a creation extension action
47+
*
48+
* @param extensionAction action to apply
49+
* @param timestamp action timestamp
50+
*
51+
* @returns state of the extension created
52+
*/
53+
protected applyCreation(
54+
extensionAction: ExtensionTypes.IAction,
55+
timestamp: number,
56+
): ExtensionTypes.IState {
57+
if (!extensionAction.parameters.network || extensionAction.parameters.network.length === 0) {
58+
throw Error('network is required');
59+
}
60+
61+
if (
62+
extensionAction.parameters.paymentAddress &&
63+
!this.isValidAddress(
64+
extensionAction.parameters.paymentAddress,
65+
extensionAction.parameters.network,
66+
)
67+
) {
68+
throw Error(
69+
`paymentAddress ${extensionAction.parameters.paymentAddress} is not a valid address`,
70+
);
71+
}
72+
73+
if (
74+
extensionAction.parameters.feeAddress &&
75+
!this.isValidAddress(
76+
extensionAction.parameters.feeAddress,
77+
extensionAction.parameters.network,
78+
)
79+
) {
80+
throw Error(`feeAddress ${extensionAction.parameters.feeAddress} is not a valid address`);
81+
}
82+
83+
if (
84+
extensionAction.parameters.refundAddress &&
85+
!this.isValidAddress(
86+
extensionAction.parameters.refundAddress,
87+
extensionAction.parameters.network,
88+
)
89+
) {
90+
throw Error(
91+
`refundAddress ${extensionAction.parameters.refundAddress} is not a valid address`,
92+
);
93+
}
94+
95+
const feePNCreationAction = super.applyCreation(extensionAction, timestamp);
96+
97+
return {
98+
...feePNCreationAction,
99+
events: [
100+
{
101+
name: 'create',
102+
parameters: {
103+
feeAddress: extensionAction.parameters.feeAddress,
104+
feeAmount: extensionAction.parameters.feeAmount,
105+
paymentAddress: extensionAction.parameters.paymentAddress,
106+
refundAddress: extensionAction.parameters.refundAddress,
107+
salt: extensionAction.parameters.salt,
108+
network: extensionAction.parameters.network,
109+
maxRateTimespan: extensionAction.parameters.maxRateTimespan,
110+
},
111+
timestamp,
112+
},
113+
],
114+
values: {
115+
...feePNCreationAction.values,
116+
network: extensionAction.parameters.network,
117+
maxRateTimespan: extensionAction.parameters.maxRateTimespan,
118+
},
119+
};
120+
}
121+
122+
/**
123+
* Applies add payment address
124+
*
125+
* @param extensionState previous state of the extension
126+
* @param extensionAction action to apply
127+
* @param requestState request state read-only
128+
* @param actionSigner identity of the signer
129+
*
130+
* @returns state of the extension updated
131+
*/
132+
protected applyAddPaymentAddress(
133+
extensionState: ExtensionTypes.IState,
134+
extensionAction: ExtensionTypes.IAction,
135+
requestState: RequestLogicTypes.IRequest,
136+
actionSigner: IdentityTypes.IIdentity,
137+
timestamp: number,
138+
): ExtensionTypes.IState {
139+
const paymentAddress = extensionAction.parameters.paymentAddress;
140+
if (!this.isValidAddress(paymentAddress, extensionState.values.network)) {
141+
throw new Error(`paymentAddress '${paymentAddress}' is not a valid address`);
142+
}
143+
return super.applyAddPaymentAddress(
144+
extensionState,
145+
extensionAction,
146+
requestState,
147+
actionSigner,
148+
timestamp,
149+
);
150+
}
151+
152+
protected applyAddFee(
153+
extensionState: ExtensionTypes.IState,
154+
extensionAction: ExtensionTypes.IAction,
155+
requestState: RequestLogicTypes.IRequest,
156+
actionSigner: IdentityTypes.IIdentity,
157+
timestamp: number,
158+
): ExtensionTypes.IState {
159+
const network =
160+
requestState.extensions[ExtensionTypes.ID.PAYMENT_NETWORK_ANY_TO_NATIVE_TOKEN].values.network;
161+
if (
162+
extensionAction.parameters.feeAddress &&
163+
!this.isValidAddress(extensionAction.parameters.feeAddress, network)
164+
) {
165+
throw Error('feeAddress is not a valid address');
166+
}
167+
return super.applyAddFee(
168+
extensionState,
169+
extensionAction,
170+
requestState,
171+
actionSigner,
172+
timestamp,
173+
);
174+
}
175+
176+
/**
177+
* Validate that the network and currency coming from the extension and/or action are valid and supported.
178+
* It must throw in case of error.
179+
*/
180+
protected validate(
181+
request: RequestLogicTypes.IRequest,
182+
extensionAction: ExtensionTypes.IAction,
183+
): void {
184+
const network =
185+
extensionAction.action === ExtensionTypes.PnFeeReferenceBased.ACTION.CREATE
186+
? extensionAction.parameters.network
187+
: request.extensions[this.extensionId]?.values.network;
188+
189+
if (!network) {
190+
throw new Error(
191+
`The network must be provided by the creation action or by the extension state`,
192+
);
193+
}
194+
195+
if (!supportedNetworks.includes(network)) {
196+
throw new Error(`The network (${network}) is not supported for this payment network.`);
197+
}
198+
199+
const currency = this.currencyManager.fromStorageCurrency(request.currency);
200+
if (!currency) {
201+
throw new UnsupportedCurrencyError(request.currency);
202+
}
203+
console.log(network);
204+
console.log(currency);
205+
if (!this.currencyManager.supportsConversion(currency, network)) {
206+
throw new Error(
207+
`The currency (${request.currency.value}) of the request is not supported for this payment network.`,
208+
);
209+
}
210+
}
211+
}

0 commit comments

Comments
 (0)