From e9a7945d0c77d266d2a6461d22c9aa9cc4c01765 Mon Sep 17 00:00:00 2001 From: yomarion Date: Mon, 15 Jul 2024 13:20:39 +0200 Subject: [PATCH 1/2] feat(processor): any-declarative payment in NEAR --- .../src/payment/any-to-erc20-proxy.ts | 5 +-- .../src/payment/near-any-declarative.ts | 39 +++++++++++++++++++ .../src/payment/near-conversion.ts | 13 ++----- .../src/payment/near-fungible.ts | 9 ++--- .../src/payment/near-input-data.ts | 7 ++-- .../payment-processor/src/payment/utils.ts | 16 ++++---- .../payment-processor/src/utils/validation.ts | 8 ++++ .../test/payment/any-to-near.test.ts | 2 +- 8 files changed, 67 insertions(+), 32 deletions(-) create mode 100644 packages/payment-processor/src/payment/near-any-declarative.ts create mode 100644 packages/payment-processor/src/utils/validation.ts diff --git a/packages/payment-processor/src/payment/any-to-erc20-proxy.ts b/packages/payment-processor/src/payment/any-to-erc20-proxy.ts index 8f49533a5..fc645dffd 100644 --- a/packages/payment-processor/src/payment/any-to-erc20-proxy.ts +++ b/packages/payment-processor/src/payment/any-to-erc20-proxy.ts @@ -17,6 +17,7 @@ import { import { padAmountForChainlink } from '@requestnetwork/payment-detection'; import { IPreparedTransaction } from './prepared-transaction'; import { IConversionPaymentSettings } from './index'; +import { validatePaymentReference } from '../utils/validation'; /** * Processes a transaction to pay a request with an ERC20 currency that is different from the request currency (eg. fiat). @@ -153,9 +154,7 @@ function prepareAnyToErc20Arguments( const { paymentReference, paymentAddress, feeAddress, feeAmount, maxRateTimespan } = getRequestPaymentValues(request); - if (!paymentReference) { - throw new Error('paymentReference is missing'); - } + validatePaymentReference(paymentReference); const amountToPay = padAmountForChainlink(getAmountToPay(request, amount), requestCurrency); const feeToPay = padAmountForChainlink(feeAmountOverride || feeAmount || 0, requestCurrency); return { diff --git a/packages/payment-processor/src/payment/near-any-declarative.ts b/packages/payment-processor/src/payment/near-any-declarative.ts new file mode 100644 index 000000000..1fb3afcfe --- /dev/null +++ b/packages/payment-processor/src/payment/near-any-declarative.ts @@ -0,0 +1,39 @@ +import { BigNumberish } from 'ethers'; +import { WalletConnection } from 'near-api-js'; + +import { ClientTypes, ExtensionTypes } from '@requestnetwork/types'; + +import { getRequestPaymentValues, validateRequest } from './utils'; +import { INearTransactionCallback, processNearPayment } from './utils-near'; +import { NearChains } from '@requestnetwork/currency'; +import { validatePaymentReference } from '../utils/validation'; + +/** + * Processes the transaction to pay a declarative request with NEAR tokens. + * @param request the request to pay, must be declarative, with payment values on a Near network. + * @param walletConnection the Near provider. + * @param amount amount to pay, in NEAR tokens. + */ +export async function payNearAnyDeclarativeRequest( + request: ClientTypes.IRequestData, + walletConnection: WalletConnection, + amount: BigNumberish, + callback?: INearTransactionCallback, +): Promise { + validateRequest(request, ExtensionTypes.PAYMENT_NETWORK_ID.ANY_DECLARATIVE); + + const { paymentReference, paymentAddress, network } = getRequestPaymentValues(request); + + validatePaymentReference(paymentReference); + NearChains.assertChainSupported(network); + + return processNearPayment( + walletConnection, + network, + amount, + paymentAddress, + paymentReference, + '0.2.0', + callback, + ); +} diff --git a/packages/payment-processor/src/payment/near-conversion.ts b/packages/payment-processor/src/payment/near-conversion.ts index a08b5c149..a35793cf2 100644 --- a/packages/payment-processor/src/payment/near-conversion.ts +++ b/packages/payment-processor/src/payment/near-conversion.ts @@ -12,12 +12,13 @@ import { import { INearTransactionCallback, processNearPaymentWithConversion } from './utils-near'; import { IConversionPaymentSettings } from '.'; import { CurrencyManager, NearChains, UnsupportedCurrencyError } from '@requestnetwork/currency'; +import { validatePaymentReference } from '../utils/validation'; /** * Processes the transaction to pay a request in NEAR with on-chain conversion. * @param request the request to pay - * @param walletConnection the Web3 provider, or signer. Defaults to window.ethereum. - * @param amount optionally, the amount to pay. Defaults to remaining amount of the request. + * @param walletConnection the Near provider. + * @param amount optionally, the amount to pay in request currency. Defaults to remaining amount of the request. */ export async function payNearConversionRequest( request: ClientTypes.IRequestData, @@ -37,13 +38,7 @@ export async function payNearConversionRequest( throw new UnsupportedCurrencyError(request.currencyInfo); } - if (!paymentReference) { - throw new Error('Cannot pay without a paymentReference'); - } - - if (!network || !NearChains.isChainSupported(network)) { - throw new Error('Should be a Near network'); - } + validatePaymentReference(paymentReference); NearChains.assertChainSupported(network); const amountToPay = getAmountToPay(request, amount).toString(); diff --git a/packages/payment-processor/src/payment/near-fungible.ts b/packages/payment-processor/src/payment/near-fungible.ts index 938ca6528..114bf2828 100644 --- a/packages/payment-processor/src/payment/near-fungible.ts +++ b/packages/payment-processor/src/payment/near-fungible.ts @@ -11,10 +11,12 @@ import { processNearFungiblePayment, } from './utils-near'; import { NearChains } from '@requestnetwork/currency'; +import { validatePaymentReference } from '../utils/validation'; /** * Processes the transaction to pay a request in fungible token on NEAR with fee (Erc20FeeProxy). * @param request the request to pay + * @param walletConnection the Near provider. */ export async function payNearFungibleRequest( request: ClientTypes.IRequestData, @@ -27,13 +29,8 @@ export async function payNearFungibleRequest( const { paymentReference, paymentAddress, feeAddress, feeAmount, network } = getRequestPaymentValues(request); - if (!paymentReference) { - throw new Error('Cannot pay without a paymentReference'); - } + validatePaymentReference(paymentReference); - if (!network || !NearChains.isChainSupported(network)) { - throw new Error('Should be a Near network'); - } NearChains.assertChainSupported(network); const amountToPay = getAmountToPay(request, amount).toString(); diff --git a/packages/payment-processor/src/payment/near-input-data.ts b/packages/payment-processor/src/payment/near-input-data.ts index bbad8237b..44563a22e 100644 --- a/packages/payment-processor/src/payment/near-input-data.ts +++ b/packages/payment-processor/src/payment/near-input-data.ts @@ -11,11 +11,12 @@ import { } from './utils'; import { INearTransactionCallback, processNearPayment } from './utils-near'; import { NearChains } from '@requestnetwork/currency'; +import { validatePaymentReference } from '../utils/validation'; /** * processes the transaction to pay a Near request. * @param request the request to pay - * @param walletConnection the Web3 provider, or signer. Defaults to window.ethereum. + * @param walletConnection the Near provider. * @param amount optionally, the amount to pay. Defaults to remaining amount of the request. */ export async function payNearInputDataRequest( @@ -34,9 +35,7 @@ export async function payNearInputDataRequest( const { paymentReference, paymentAddress } = getRequestPaymentValues(request); const amountToPay = getAmountToPay(request, amount).toString(); const version = getPaymentExtensionVersion(request); - if (!paymentReference) { - throw new Error('Cannot pay without a paymentReference'); - } + validatePaymentReference(paymentReference); return processNearPayment( walletConnection, diff --git a/packages/payment-processor/src/payment/utils.ts b/packages/payment-processor/src/payment/utils.ts index a340c6fc6..661d15ab0 100644 --- a/packages/payment-processor/src/payment/utils.ts +++ b/packages/payment-processor/src/payment/utils.ts @@ -353,21 +353,19 @@ export function validateERC20TransferableReceivable( } /** - * Computes the amount to pay. - * If `amount` is specified, it will return it. - * Otherwise, it will return the amount left to pay in the request. + * It returns the amount left to pay in the request, unless an amount is specified. * - * @param request the request to pay - * @param amount the optional amount to pay. + * @param request the request to pay. + * @param amount optionally override the returned amount to pay, in request currency. + * @returns the amount to pay, in request currency. */ export function getAmountToPay( request: ClientTypes.IRequestData, amount?: BigNumberish, ): BigNumber { - const amountToPay = - amount === undefined - ? BigNumber.from(request.expectedAmount).sub(request.balance?.balance || 0) - : BigNumber.from(amount); + const amountToPay = amount + ? BigNumber.from(amount) + : BigNumber.from(request.expectedAmount).sub(request.balance?.balance || 0); if (amountToPay.lt(0)) { throw new Error('cannot pay a negative amount'); diff --git a/packages/payment-processor/src/utils/validation.ts b/packages/payment-processor/src/utils/validation.ts new file mode 100644 index 000000000..8827c52f0 --- /dev/null +++ b/packages/payment-processor/src/utils/validation.ts @@ -0,0 +1,8 @@ +/** Validates the presence of the payment reference for payment. */ +export function validatePaymentReference( + paymentReference?: string, +): asserts paymentReference is string { + if (!paymentReference) { + throw new Error('Cannot pay without a paymentReference'); + } +} diff --git a/packages/payment-processor/test/payment/any-to-near.test.ts b/packages/payment-processor/test/payment/any-to-near.test.ts index 88eda1750..97283d220 100644 --- a/packages/payment-processor/test/payment/any-to-near.test.ts +++ b/packages/payment-processor/test/payment/any-to-near.test.ts @@ -170,7 +170,7 @@ describe('payNearWithConversionRequest', () => { await expect( payNearConversionRequest(invalidRequest, mockedNearWalletConnection, conversionSettings), - ).rejects.toThrowError('Should be a Near network'); + ).rejects.toThrowError('Unsupported chain unknown-network'); expect(paymentSpy).not.toHaveBeenCalled(); }); }); From 902c9daf9a9ad9555e36e632dde401ff7c69936d Mon Sep 17 00:00:00 2001 From: yomarion Date: Mon, 15 Jul 2024 14:24:13 +0200 Subject: [PATCH 2/2] replaced the request payment method with a low-level API --- .../src/payment/near-amount-with-ref.ts | 40 +++++++++++++++++++ .../src/payment/near-any-declarative.ts | 39 ------------------ 2 files changed, 40 insertions(+), 39 deletions(-) create mode 100644 packages/payment-processor/src/payment/near-amount-with-ref.ts delete mode 100644 packages/payment-processor/src/payment/near-any-declarative.ts diff --git a/packages/payment-processor/src/payment/near-amount-with-ref.ts b/packages/payment-processor/src/payment/near-amount-with-ref.ts new file mode 100644 index 000000000..62507bbcb --- /dev/null +++ b/packages/payment-processor/src/payment/near-amount-with-ref.ts @@ -0,0 +1,40 @@ +import { BigNumberish } from 'ethers'; +import { WalletConnection } from 'near-api-js'; + +import { CurrencyTypes } from '@requestnetwork/types'; + +import { INearTransactionCallback, processNearPayment } from './utils-near'; +import { NearChains } from '@requestnetwork/currency'; + +/** + * Processes a transaction to make a payment in NEAR token with a reference. + * + * @notice This is used to pay a declarative request, with low-level arguments. + * + * @param paymentAddress must be a valid NEAR address on the given network. + * @param network e.g. 'near' + * @param paymentReference used to index payments. + * @param walletConnection the Near provider. + * @param amount amount to pay, in NEAR tokens. + */ +// FIXME: We could improve the method's interface by enforcing a type on `paymentInfo` for declarative requests. +export async function payNearAmountWithReference( + paymentAddress: string, + paymentReference: string, + network: CurrencyTypes.NearChainName, + walletConnection: WalletConnection, + amount: BigNumberish, + callback?: INearTransactionCallback, +): Promise { + NearChains.assertChainSupported(network); + + return processNearPayment( + walletConnection, + network, + amount, + paymentAddress, + paymentReference, + '0.2.0', + callback, + ); +} diff --git a/packages/payment-processor/src/payment/near-any-declarative.ts b/packages/payment-processor/src/payment/near-any-declarative.ts deleted file mode 100644 index 1fb3afcfe..000000000 --- a/packages/payment-processor/src/payment/near-any-declarative.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { BigNumberish } from 'ethers'; -import { WalletConnection } from 'near-api-js'; - -import { ClientTypes, ExtensionTypes } from '@requestnetwork/types'; - -import { getRequestPaymentValues, validateRequest } from './utils'; -import { INearTransactionCallback, processNearPayment } from './utils-near'; -import { NearChains } from '@requestnetwork/currency'; -import { validatePaymentReference } from '../utils/validation'; - -/** - * Processes the transaction to pay a declarative request with NEAR tokens. - * @param request the request to pay, must be declarative, with payment values on a Near network. - * @param walletConnection the Near provider. - * @param amount amount to pay, in NEAR tokens. - */ -export async function payNearAnyDeclarativeRequest( - request: ClientTypes.IRequestData, - walletConnection: WalletConnection, - amount: BigNumberish, - callback?: INearTransactionCallback, -): Promise { - validateRequest(request, ExtensionTypes.PAYMENT_NETWORK_ID.ANY_DECLARATIVE); - - const { paymentReference, paymentAddress, network } = getRequestPaymentValues(request); - - validatePaymentReference(paymentReference); - NearChains.assertChainSupported(network); - - return processNearPayment( - walletConnection, - network, - amount, - paymentAddress, - paymentReference, - '0.2.0', - callback, - ); -}