Skip to content

Commit

Permalink
chore: refactor the payment detection for onchain conversion requests (
Browse files Browse the repository at this point in the history
…#427)

* chore: refactor the payment detection for onchain conversion requests
  • Loading branch information
yomarion committed Mar 5, 2021
1 parent 5aa47a7 commit 9be653c
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 235 deletions.
202 changes: 21 additions & 181 deletions packages/payment-detection/src/any/any-to-erc20-proxy-contract.ts
Expand Up @@ -6,7 +6,9 @@ import {
RequestLogicTypes,
} from '@requestnetwork/types';
import Utils from '@requestnetwork/utils';
import getBalanceErrorObject from '../balance-error';
import PaymentNetworkERC20FeeProxyContract, {
DeploymentInformationGetter,
} from '../erc20/fee-proxy-contract';
import PaymentReferenceCalculator from '../payment-reference-calculator';
import ProxyInfoRetriever from './any-to-erc20-proxy-info-retriever';

Expand All @@ -21,13 +23,17 @@ class VersionNotSupported extends Error {}
/**
* Handle payment networks with conversion proxy contract extension
*/
export default class PaymentNetworkERC20FeeProxyContract implements PaymentTypes.IPaymentNetwork {
private extension: ExtensionTypes.PnAnyToErc20.IAnyToERC20;
export default class PaymentNetworkAnyToERC20 extends PaymentNetworkERC20FeeProxyContract<ExtensionTypes.PnAnyToErc20.IAnyToERC20> {
/**
* @param extension The advanced logic payment network extensions
*/

public constructor({ advancedLogic }: { advancedLogic: AdvancedLogicTypes.IAdvancedLogic }) {
this.extension = advancedLogic.extensions.anyToErc20Proxy;
super({
advancedLogic,
});
this._paymentNetworkId = ExtensionTypes.ID.PAYMENT_NETWORK_ANY_TO_ERC20_PROXY;
this._extension = advancedLogic.extensions.anyToErc20Proxy;
}

/**
Expand All @@ -44,7 +50,7 @@ export default class PaymentNetworkERC20FeeProxyContract implements PaymentTypes
const salt =
paymentNetworkCreationParameters.salt || (await Utils.crypto.generate8randomBytes());

return this.extension.createCreationAction({
return this._extension.createCreationAction({
feeAddress: paymentNetworkCreationParameters.feeAddress,
feeAmount: paymentNetworkCreationParameters.feeAmount,
paymentAddress: paymentNetworkCreationParameters.paymentAddress,
Expand All @@ -57,139 +63,14 @@ export default class PaymentNetworkERC20FeeProxyContract implements PaymentTypes
}

/**
* Creates the extensions data to add payment address
*
* @param parameters to add payment information
* @returns The extensionData object
*/
public createExtensionsDataForAddPaymentInformation(
parameters: ExtensionTypes.PnReferenceBased.IAddPaymentAddressParameters,
): ExtensionTypes.IAction {
return this.extension.createAddPaymentAddressAction({
paymentAddress: parameters.paymentAddress,
});
}

/**
* Creates the extensions data to add refund address
*
* @param Parameters to add refund information
* @returns The extensionData object
*/
public createExtensionsDataForAddRefundInformation(
parameters: ExtensionTypes.PnReferenceBased.IAddRefundAddressParameters,
): ExtensionTypes.IAction {
return this.extension.createAddRefundAddressAction({
refundAddress: parameters.refundAddress,
});
}

/**
* Creates the extensions data to add fee address and amount
*
* @param Parameters to add refund information
* @returns The extensionData object
*/
public createExtensionsDataForAddFeeInformation(
parameters: ExtensionTypes.PnFeeReferenceBased.IAddFeeParameters,
): ExtensionTypes.IAction {
return this.extension.createAddFeeAction({
feeAddress: parameters.feeAddress,
feeAmount: parameters.feeAmount,
});
}

/**
* Gets the balance and the payment/refund events
*
* @param request the request to check
* @param paymentNetworkId payment network id
* @param tokenContractAddress the address of the token contract
* @returns the balance and the payment/refund events
*/
public async getBalance(
request: RequestLogicTypes.IRequest,
): Promise<PaymentTypes.IBalanceWithEvents> {
const paymentNetworkId = ExtensionTypes.ID.PAYMENT_NETWORK_ANY_TO_ERC20_PROXY;
const paymentNetwork = request.extensions[paymentNetworkId];

if (!paymentNetwork) {
return getBalanceErrorObject(
`The request does not have the extension : ${paymentNetworkId}`,
PaymentTypes.BALANCE_ERROR_CODE.WRONG_EXTENSION,
);
}
try {
const paymentAddress = paymentNetwork.values.paymentAddress;
const refundAddress = paymentNetwork.values.refundAddress;
const feeAddress = paymentNetwork.values.feeAddress;
const salt = paymentNetwork.values.salt;

let payments: PaymentTypes.IBalanceWithEvents = { balance: '0', events: [] };
if (paymentAddress) {
payments = await this.extractBalanceAndEvents(
request,
salt,
paymentAddress,
PaymentTypes.EVENTS_NAMES.PAYMENT,
paymentNetwork,
paymentNetwork.values.acceptedTokens,
paymentNetwork.values.maxRateTimespan,
);
}

let refunds: PaymentTypes.IBalanceWithEvents = { balance: '0', events: [] };
if (refundAddress) {
refunds = await this.extractBalanceAndEvents(
request,
salt,
refundAddress,
PaymentTypes.EVENTS_NAMES.REFUND,
paymentNetwork,
paymentNetwork.values.acceptedTokens,
paymentNetwork.values.maxRateTimespan,
);
}

const fees = this.extractFeeAndEvents(feeAddress, [...payments.events, ...refunds.events]);
// TODO (PROT-1219): this is not ideal, since we're directly changing the request extension
// once the fees feature and similar payment extensions are more well established, we should define
// a better place to retrieve them from the request object them.
paymentNetwork.values.feeBalance = fees;

const balance: string = new bigNumber(payments.balance || 0)
.sub(new bigNumber(refunds.balance || 0))
.toString();

const events: PaymentTypes.ERC20PaymentNetworkEvent[] = [
...payments.events,
...refunds.events,
].sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0));

return {
balance,
events,
};
} catch (error) {
let code: PaymentTypes.BALANCE_ERROR_CODE | undefined;
if (error instanceof NetworkNotSupported) {
code = PaymentTypes.BALANCE_ERROR_CODE.NETWORK_NOT_SUPPORTED;
}
if (error instanceof VersionNotSupported) {
code = PaymentTypes.BALANCE_ERROR_CODE.VERSION_NOT_SUPPORTED;
}
return getBalanceErrorObject(error.message, code);
}
}

/**
* Extracts the balance and events of an address
* Extracts the balance and events of a request
*
* @private
* @param address Address to check
* @param request Address to check
* @param salt Payment reference salt
* @param toAddress Payee address
* @param eventName Indicate if it is an address for payment or refund
* @param network The id of network we want to check
* @param tokenContractAddress the address of the token contract
* @param paymentNetwork Payment network state
* @returns The balance and events
*/
public async extractBalanceAndEvents(
Expand All @@ -198,10 +79,10 @@ export default class PaymentNetworkERC20FeeProxyContract implements PaymentTypes
toAddress: string,
eventName: PaymentTypes.EVENTS_NAMES,
paymentNetwork: ExtensionTypes.IState,
acceptedTokens?: string[],
maxRateTimespan: number = 0,
): Promise<PaymentTypes.IBalanceWithEvents> {
const network = paymentNetwork.values.network || 'mainnet';
const network = paymentNetwork.values.network;
const acceptedTokens = paymentNetwork.values.acceptedTokens;
const maxRateTimespan = paymentNetwork.values.maxRateTimespan || 0;

const conversionDeploymentInformation = proxyChainlinkConversionPath.getDeploymentInformation(
network,
Expand Down Expand Up @@ -274,47 +155,6 @@ export default class PaymentNetworkERC20FeeProxyContract implements PaymentTypes
};
}

/**
* Extract the fee balance from a list of payment events
*
* @param feeAddress The fee address the extracted fees will be paid to
* @param paymentEvents The payment events to extract fees from
*/
public extractFeeAndEvents(
feeAddress: string,
paymentEvents: PaymentTypes.ERC20PaymentNetworkEvent[],
): PaymentTypes.IBalanceWithEvents {
if (!feeAddress) {
return {
balance: '0',
events: [],
};
}

return paymentEvents.reduce(
(
feeBalance: PaymentTypes.IBalanceWithEvents,
event: PaymentTypes.IPaymentNetworkEvent<PaymentTypes.IERC20FeePaymentEventParameters>,
): PaymentTypes.IBalanceWithEvents => {
// Skip if feeAddress or feeAmount are not set, or if feeAddress doesn't match the PN one
if (
!event.parameters?.feeAddress ||
!event.parameters?.feeAmount ||
event.parameters.feeAddress !== feeAddress
) {
return feeBalance;
}

feeBalance = {
balance: new bigNumber(feeBalance.balance)
.add(new bigNumber(event.parameters.feeAmount))
.toString(),
events: [...feeBalance.events, event],
};

return feeBalance;
},
{ balance: '0', events: [] },
);
}
protected getDeploymentInformation: DeploymentInformationGetter =
proxyChainlinkConversionPath.getDeploymentInformation;
}

0 comments on commit 9be653c

Please sign in to comment.