From 93bd4fcc0ec97223fe64f732c7d9d1223c54f917 Mon Sep 17 00:00:00 2001 From: yomarion Date: Mon, 31 Oct 2022 17:04:46 +0100 Subject: [PATCH] fix: request-client should support conversion and network checks --- .../src/payment-network-factory.ts | 6 +- .../src/api/request-network.ts | 7 +- packages/request-client.js/test/data-test.ts | 11 + .../test/declarative-payments.test.ts | 505 ++++++ packages/request-client.js/test/index.test.ts | 1473 +++++++---------- 5 files changed, 1078 insertions(+), 924 deletions(-) create mode 100644 packages/request-client.js/test/declarative-payments.test.ts diff --git a/packages/payment-detection/src/payment-network-factory.ts b/packages/payment-detection/src/payment-network-factory.ts index 5426ffdc9a..513a0a8ded 100644 --- a/packages/payment-detection/src/payment-network-factory.ts +++ b/packages/payment-detection/src/payment-network-factory.ts @@ -105,16 +105,16 @@ export class PaymentNetworkFactory { * * @param paymentNetworkId the ID of the payment network to instantiate * @param currencyType the currency type of the request - * @param currencyNetwork the network of the currency of the payment to detect + * @param paymentChain 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: PaymentTypes.PAYMENT_NETWORK_ID, currencyType: RequestLogicTypes.CURRENCY, - currencyNetwork?: string, + paymentChain?: string, paymentNetworkVersion?: string, ): PaymentTypes.IPaymentNetwork { - const network = currencyNetwork || 'mainnet'; + const network = paymentChain ?? 'mainnet'; const currencyPaymentMap = supportedPaymentNetwork[currencyType]?.[network] || supportedPaymentNetwork[currencyType]?.['*'] || diff --git a/packages/request-client.js/src/api/request-network.ts b/packages/request-client.js/src/api/request-network.ts index d1a38020ad..022ef6a0dc 100644 --- a/packages/request-client.js/src/api/request-network.ts +++ b/packages/request-client.js/src/api/request-network.ts @@ -394,11 +394,16 @@ export default class RequestNetwork { const copiedRequestParameters = Utils.deepCopy(requestParameters); copiedRequestParameters.extensionsData = []; + const detectionChain = + parameters?.paymentNetwork?.parameters && 'network' in parameters.paymentNetwork.parameters + ? parameters.paymentNetwork.parameters.network ?? requestParameters.currency.network + : requestParameters.currency.network; + const paymentNetwork = parameters.paymentNetwork ? this.paymentNetworkFactory.createPaymentNetwork( parameters.paymentNetwork.id, requestParameters.currency.type, - requestParameters.currency.network, + detectionChain, ) : null; diff --git a/packages/request-client.js/test/data-test.ts b/packages/request-client.js/test/data-test.ts index 69cc1e6598..d02a8498cf 100644 --- a/packages/request-client.js/test/data-test.ts +++ b/packages/request-client.js/test/data-test.ts @@ -93,6 +93,17 @@ export const parametersWithoutExtensionsData: RequestLogicTypes.ICreateParameter payer: payer.identity, timestamp: arbitraryTimestamp, }; +export const parametersUSDWithoutExtensionsData: RequestLogicTypes.ICreateParameters = { + currency: { + type: RequestLogicTypes.CURRENCY.ISO4217, + value: 'USD', + }, + // 345.67 USD + expectedAmount: '34567', + payee: payee.identity, + payer: payer.identity, + timestamp: arbitraryTimestamp, +}; export const parametersWithoutExtensionsDataForSigning: RequestLogicTypes.ICreateParameters = { currency: { network: 'testnet', diff --git a/packages/request-client.js/test/declarative-payments.test.ts b/packages/request-client.js/test/declarative-payments.test.ts new file mode 100644 index 0000000000..aa64ad34b0 --- /dev/null +++ b/packages/request-client.js/test/declarative-payments.test.ts @@ -0,0 +1,505 @@ +import axios from 'axios'; + +import { + ClientTypes, + ExtensionTypes, + PaymentTypes, + RequestLogicTypes, +} from '@requestnetwork/types'; +import { ethers } from 'ethers'; + +import AxiosMockAdapter from 'axios-mock-adapter'; +import { RequestNetwork } from '../src/index'; +import * as TestData from './data-test'; + +import { + PaymentReferenceCalculator, + getPaymentReference, + getPaymentNetworkExtension, +} from '@requestnetwork/payment-detection'; +import { IRequestDataWithEvents } from '../src/types'; +import { CurrencyManager } from '@requestnetwork/currency'; + +const httpConfig: Partial = { + getConfirmationDeferDelay: 0, +}; + +const waitForConfirmation = async ( + dataOrPromise: IRequestDataWithEvents | Promise, +): Promise => { + const data = await dataOrPromise; + return new Promise((resolve, reject) => { + data.on('confirmed', resolve); + data.on('error', reject); + }); +}; + +// Integration tests +/* eslint-disable @typescript-eslint/no-unused-expressions */ +describe('request-client.js: declarative payments', () => { + let mock: AxiosMockAdapter; + afterEach(() => { + jest.clearAllMocks(); + mock.reset(); + }); + beforeEach(() => { + mock = new AxiosMockAdapter(axios); + + const callback = (config: any): any => { + expect(config.baseURL).toBe('http://localhost:3000'); + return [200, {}]; + }; + const spy = jest.fn(callback); + mock.onPost('/persistTransaction').reply(spy); + mock.onGet('/getTransactionsByChannelId').reply(200, { + result: { transactions: [TestData.timestampedTransactionWithDeclarative] }, + }); + mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); + }); + + it('allows to declare a sent payment', async () => { + const requestNetwork = new RequestNetwork({ + httpConfig, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: {}, + }; + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + await request.waitForConfirmation(); + + mock.resetHistory(); + + await waitForConfirmation( + request.declareSentPayment('10', 'sent payment', TestData.payer.identity), + ); + + expect(mock.history.get).toHaveLength(5); + expect(mock.history.post).toHaveLength(1); + }); + + it('allows to declare a received payment', async () => { + const requestNetwork = new RequestNetwork({ + httpConfig, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: {}, + }; + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + await request.waitForConfirmation(); + + mock.resetHistory(); + + await waitForConfirmation( + request.declareReceivedPayment('10', 'received payment', TestData.payee.identity), + ); + + expect(mock.history.get).toHaveLength(5); + expect(mock.history.post).toHaveLength(1); + }); + + it('allows to create a request with delegate', async () => { + const requestNetwork = new RequestNetwork({ + useMockStorage: true, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: {}, + }; + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: { + ...TestData.parametersWithoutExtensionsData, + extensionsData: [ + { + action: ExtensionTypes.PnAnyDeclarative.ACTION.ADD_DELEGATE, + id: ExtensionTypes.ID.PAYMENT_NETWORK_ANY_DECLARATIVE, + parameters: { + delegate: TestData.delegate.identity, + }, + }, + ], + }, + signer: TestData.payee.identity, + }); + await request.waitForConfirmation(); + + const requestData = await waitForConfirmation( + request.declareReceivedPayment('10', 'received payment', TestData.delegate.identity), + ); + expect(requestData.balance!.balance).toEqual('10'); + }); + + it('allows to declare a received payment from delegate', async () => { + const requestNetwork = new RequestNetwork({ + useMockStorage: true, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: {}, + }; + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + await request.waitForConfirmation(); + + await waitForConfirmation( + request.addDeclarativeDelegate(TestData.delegate.identity, TestData.payee.identity), + ); + + const requestData = await waitForConfirmation( + request.declareReceivedPayment('10', 'received payment', TestData.delegate.identity), + ); + expect(requestData.balance!.balance).toEqual('10'); + }); + + it('allows to declare a received payment by providing transaction hash and network', async () => { + const requestNetwork = new RequestNetwork({ + httpConfig, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: {}, + }; + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + await request.waitForConfirmation(); + + mock.resetHistory(); + + await waitForConfirmation( + request.declareReceivedPayment( + '10', + 'received payment', + TestData.payee.identity, + '0x123456789', + 'mainnet', + ), + ); + + expect(mock.history.get).toHaveLength(5); + expect(mock.history.post).toHaveLength(1); + }); + + it('allows to declare a sent refund', async () => { + const requestNetwork = new RequestNetwork({ + httpConfig, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: {}, + }; + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + await request.waitForConfirmation(); + + mock.resetHistory(); + + await waitForConfirmation( + request.declareSentRefund('10', 'sent refund', TestData.payee.identity), + ); + + expect(mock.history.get).toHaveLength(5); + expect(mock.history.post).toHaveLength(1); + }); + + it('allows to declare a received refund', async () => { + const requestNetwork = new RequestNetwork({ + httpConfig, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: {}, + }; + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + await request.waitForConfirmation(); + + mock.resetHistory(); + + await waitForConfirmation( + request.declareReceivedRefund('10', 'received refund', TestData.payer.identity), + ); + + expect(mock.history.get).toHaveLength(5); + expect(mock.history.post).toHaveLength(1); + }); + + it('can have a payment reference on a declarative payment network', async () => { + const requestNetwork = new RequestNetwork({ + httpConfig, + useMockStorage: true, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = + { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: { + paymentInfo: { + IBAN: 'FR123456789123456789', + BIC: 'CE123456789', + }, + salt: 'a1a2a3a4a5a6a7a8', + }, + }; + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + await request.waitForConfirmation(); + + const data = request.getData(); + + const pn = getPaymentNetworkExtension(data)!; + + const paymentReference = PaymentReferenceCalculator.calculate( + data.requestId, + pn.values.salt, + JSON.stringify(pn.values.paymentInfo), + ); + + expect(paymentReference).toHaveLength(16); + expect(paymentReference).toBe(getPaymentReference(data)); + }); + + it('allows to declare a received refund from delegate', async () => { + const requestNetwork = new RequestNetwork({ + useMockStorage: true, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: {}, + }; + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + await request.waitForConfirmation(); + + await waitForConfirmation( + request.addDeclarativeDelegate(TestData.delegate.identity, TestData.payer.identity), + ); + + const requestData = await waitForConfirmation( + request.declareReceivedRefund('11', 'received refund', TestData.delegate.identity), + ); + expect(requestData.balance!.balance).toEqual('-11'); + }); + + it('allows to declare a received refund by providing transaction hash', async () => { + const requestNetwork = new RequestNetwork({ + httpConfig, + signatureProvider: TestData.fakeSignatureProvider, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: {}, + }; + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + await request.waitForConfirmation(); + + mock.resetHistory(); + + await waitForConfirmation( + request.declareReceivedRefund( + '10', + 'received refund', + TestData.payer.identity, + '0x123456789', + ), + ); + + expect(mock.history.get).toHaveLength(5); + expect(mock.history.post).toHaveLength(1); + }); + + it('allows to get the right balance', async () => { + const requestParametersUSD: RequestLogicTypes.ICreateParameters = { + currency: { + type: RequestLogicTypes.CURRENCY.ISO4217, + value: 'USD', + }, + expectedAmount: '100000000000', + payee: TestData.payee.identity, + payer: TestData.payer.identity, + }; + + const requestNetwork = new RequestNetwork({ + signatureProvider: TestData.fakeSignatureProvider, + useMockStorage: true, + }); + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: {}, + }; + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: requestParametersUSD, + signer: TestData.payee.identity, + }); + await request.waitForConfirmation(); + + await waitForConfirmation( + request.declareSentPayment('1', 'sent payment', TestData.payer.identity), + ); + + await waitForConfirmation( + request.declareReceivedRefund('10', 'received refund', TestData.payer.identity), + ); + + await waitForConfirmation( + request.declareSentRefund('100', 'sent refund', TestData.payee.identity), + ); + + await waitForConfirmation( + request.declareReceivedPayment('1000', 'received payment', TestData.payee.identity), + ); + + await waitForConfirmation( + request.addPaymentInformation('payment info added', TestData.payee.identity), + ); + await waitForConfirmation( + request.addRefundInformation('refund info added', TestData.payer.identity), + ); + + const requestData = await request.refresh(); + + expect(requestData.balance?.balance).toBe('990'); + expect(requestData.balance?.events[0].name).toBe('refund'); + expect(requestData.balance?.events[0].amount).toBe('10'); + expect(requestData.balance?.events[0].parameters).toMatchObject({ note: 'received refund' }); + + expect(requestData.balance?.events[1].name).toBe('payment'); + expect(requestData.balance?.events[1].amount).toBe('1000'); + expect(requestData.balance?.events[1].parameters).toMatchObject({ note: 'received payment' }); + }); + + it('can declare payments and refunds on an ANY_TO_ERC20_PROXY request', async () => { + const requestNetwork = new RequestNetwork({ + signatureProvider: TestData.fakeSignatureProvider, + useMockStorage: true, + currencies: [ + ...CurrencyManager.getDefaultList(), + { + type: RequestLogicTypes.CURRENCY.ERC20, + address: '0x38cf23c52bb4b13f051aec09580a2de845a7fa35', + decimals: 18, + network: 'private', + symbol: 'FAKE', + }, + ], + }); + + // provider data is irrelevant in this test + jest.spyOn(ethers.providers.BaseProvider.prototype, 'getLogs').mockResolvedValue([]); + + const salt = 'ea3bc7caf64110ca'; + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.ANY_TO_ERC20_PROXY, + parameters: { + paymentAddress: '0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB', + refundAddress: '0x821aEa9a577a9b44299B9c15c88cf3087F3b5544', + salt, + feeAddress: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2', + feeAmount: '200', + network: 'private', + acceptedTokens: ['0x38cf23c52bb4b13f051aec09580a2de845a7fa35'], + maxRateTimespan: 1000000, + }, + }; + + const requestInfo = { + expectedAmount: '100', // not used + payee: TestData.payee.identity, + payer: TestData.payer.identity, + currency: 'USD', + }; + + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo, + signer: TestData.payee.identity, + }); + await request.waitForConfirmation(); + + await waitForConfirmation( + request.declareSentPayment('10', 'sent payment', TestData.payer.identity), + ); + + await waitForConfirmation( + request.declareSentRefund('2', 'sent refund', TestData.payee.identity), + ); + + await request.refresh(); + expect(request.getData().balance?.balance).toBe('0'); + + await waitForConfirmation( + request.declareReceivedPayment('10', 'received payment', TestData.payee.identity), + ); + + await waitForConfirmation( + request.declareReceivedRefund('2', 'received refund', TestData.payer.identity), + ); + + await request.refresh(); + expect(request.getData().balance?.error).toBeUndefined(); + expect(request.getData().balance?.balance).toBe('8'); + expect(request.getData().balance?.events?.length).toBe(2); + }); +}); diff --git a/packages/request-client.js/test/index.test.ts b/packages/request-client.js/test/index.test.ts index 9bf8bb86a6..e23fa1a473 100644 --- a/packages/request-client.js/test/index.test.ts +++ b/packages/request-client.js/test/index.test.ts @@ -4,7 +4,6 @@ import { ClientTypes, DecryptionProviderTypes, EncryptionTypes, - ExtensionTypes, IdentityTypes, PaymentTypes, RequestLogicTypes, @@ -17,16 +16,11 @@ import { Request, RequestNetwork, RequestNetworkBase } from '../src/index'; import * as TestData from './data-test'; import * as TestDataRealBTC from './data-test-real-btc'; -import { - PaymentReferenceCalculator, - getPaymentReference, - getPaymentNetworkExtension, -} from '@requestnetwork/payment-detection'; +import { PaymentReferenceCalculator } from '@requestnetwork/payment-detection'; import { BigNumber } from 'ethers'; import EtherscanProviderMock from './etherscan-mock'; import httpConfigDefaults from '../src/http-config-defaults'; import { IRequestDataWithEvents } from '../src/types'; -import { CurrencyManager } from '@requestnetwork/currency'; import HttpMetaMaskDataAccess from '../src/http-metamask-data-access'; import MockDataAccess from '../src/mock-data-access'; import MockStorage from '../src/mock-storage'; @@ -104,6 +98,8 @@ const mockBTCProvider = { }, }; +const salt = 'ea3bc7caf64110ca'; + const waitForConfirmation = async ( dataOrPromise: IRequestDataWithEvents | Promise, ): Promise => { @@ -116,706 +112,364 @@ const waitForConfirmation = async ( // Integration tests /* eslint-disable @typescript-eslint/no-unused-expressions */ -describe('index', () => { +describe('request-client.js', () => { afterEach(() => { jest.clearAllMocks(); }); - it('specify the Request Client version in the header', async () => { - const mock = new AxiosMockAdapter(axios); - - const callback = (config: any): any => { - expect(config.headers[httpConfigDefaults.requestClientVersionHeader]).toBe( - packageJson.version, - ); - return [200, {}]; - }; - const spy = jest.fn(callback); - mock.onPost('/persistTransaction').reply(spy); - mock - .onGet('/getTransactionsByChannelId') - .reply(200, { result: { transactions: [TestData.timestampedTransaction] } }); - mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); - - const requestNetwork = new RequestNetwork({ - httpConfig, - signatureProvider: TestData.fakeSignatureProvider, - paymentOptions: { - bitcoinDetectionProvider: mockBTCProvider, - }, - }); - - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, - parameters: {}, - }; - const request = await requestNetwork.createRequest({ - paymentNetwork, - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }); - expect(spy).toHaveBeenCalledTimes(1); + describe('API', () => { + it('specify the Request Client version in the header', async () => { + const mock = new AxiosMockAdapter(axios); - await request.waitForConfirmation(); - }); - - it('uses http://localhost:3000 with signatureProvider and paymentNetwork', async () => { - const mock = new AxiosMockAdapter(axios); - - const callback = (config: any): any => { - expect(config.baseURL).toBe('http://localhost:3000'); - return [200, {}]; - }; - const spy = jest.fn(callback); - mock.onPost('/persistTransaction').reply(spy); - mock - .onGet('/getTransactionsByChannelId') - .reply(200, { result: { transactions: [TestData.timestampedTransaction] } }); - mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); - - const requestNetwork = new RequestNetwork({ - httpConfig, - signatureProvider: TestData.fakeSignatureProvider, - paymentOptions: { - bitcoinDetectionProvider: mockBTCProvider, - }, - }); - - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, - parameters: {}, - }; - const request = await requestNetwork.createRequest({ - paymentNetwork, - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }); - expect(spy).toHaveBeenCalledTimes(1); - - await request.waitForConfirmation(); - }); - - it('uses http://localhost:3000 with persist from local', async () => { - const mock = new AxiosMockAdapter(axios); - const callback = (): any => { - return [200, { ipfsSize: 100, ipfsHash: 'QmZLqH4EsjmB79gjvyzXWBcihbNBZkw8YuELco84PxGzQY' }]; - }; - // const spyPersistTransaction = jest.fn(); - const spyIpfsAdd = jest.fn(callback); - // mock.onPost('/persistTransaction').reply(spyPersistTransaction); - mock.onPost('/persistTransaction').reply(200, { meta: {}, result: {} }); - mock.onPost('/ipfsAdd').reply(spyIpfsAdd); - mock.onGet('/getTransactionsByChannelId').reply(200, { - meta: { storageMeta: [], transactionsStorageLocation: [] }, - result: { transactions: [] }, - }); + const callback = (config: any): any => { + expect(config.headers[httpConfigDefaults.requestClientVersionHeader]).toBe( + packageJson.version, + ); + return [200, {}]; + }; + const spy = jest.fn(callback); + mock.onPost('/persistTransaction').reply(spy); + mock + .onGet('/getTransactionsByChannelId') + .reply(200, { result: { transactions: [TestData.timestampedTransaction] } }); + mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); - const requestNetwork = new RequestNetworkBase({ - dataAccess: new HttpMetaMaskDataAccess({ + const requestNetwork = new RequestNetwork({ httpConfig, - ethereumProviderUrl: 'http://localhost:8545', - }), - signatureProvider: TestData.fakeSignatureProvider, - paymentOptions: { - bitcoinDetectionProvider: mockBTCProvider, - }, - }); - - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED, - parameters: { - paymentAddress: 'mgPKDuVmuS9oeE2D9VPiCQriyU14wxWS1v', - }, - }; - - const request = await requestNetwork.createRequest({ - paymentNetwork, - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }); - expect(spyIpfsAdd).toHaveBeenCalledTimes(1); - - await request.waitForConfirmation(); - }); - - it('uses http://localhost:3000 with signatureProvider and paymentNetwork real btc', async () => { - const mock = new AxiosMockAdapter(axios); - - const callback = (config: any): any => { - expect(config.baseURL).toBe('http://localhost:3000'); - return [200, {}]; - }; - const spy = jest.fn(callback); - mock.onPost('/persistTransaction').reply(spy); - mock.onGet('/getTransactionsByChannelId').reply(200, { - result: { transactions: [TestDataRealBTC.timestampedTransaction] }, - }); - mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); - - const requestNetwork = new RequestNetwork({ - httpConfig, - signatureProvider: TestData.fakeSignatureProvider, - paymentOptions: { - bitcoinDetectionProvider: mockBTCProvider, - }, - }); - - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED, - parameters: { - paymentAddress: '1FersucwSqufU26w9GrGz9M3KcwuNmy6a9', - }, - }; - - const request = await requestNetwork.createRequest({ - paymentNetwork, - requestInfo: requestParameters, - signer: TestData.payee.identity, - }); - expect(spy).toHaveBeenCalledTimes(1); - - await request.waitForConfirmation(); - }); - - it('uses http://localhost:3000 with signatureProvider', async () => { - const mock = new AxiosMockAdapter(axios); - - const callback = (config: any): any => { - expect(config.baseURL).toBe('http://localhost:3000'); - return [200, {}]; - }; - const spy = jest.fn(callback); - mock.onPost('/persistTransaction').reply(spy); - mock.onGet('/getTransactionsByChannelId').reply(200, { - result: { transactions: [TestData.timestampedTransactionWithoutExtensionsData] }, - }); - mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); - - const requestNetwork = new RequestNetwork({ - httpConfig, - signatureProvider: TestData.fakeSignatureProvider, - }); - - await requestNetwork.createRequest({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }); - expect(spy).toHaveBeenCalledTimes(1); - }); - - it('uses baseUrl given in parameter', async () => { - const baseURL = 'http://request.network/api'; - const mock = new AxiosMockAdapter(axios); - - const callback = (config: any): any => { - expect(config.baseURL).toBe(baseURL); - return [200, {}]; - }; - const spy = jest.fn(callback); - mock.onPost('/persistTransaction').reply(spy); - mock.onGet('/getTransactionsByChannelId').reply(200, { - result: { transactions: [TestData.timestampedTransactionWithoutExtensionsData] }, - }); - mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); - - const requestNetwork = new RequestNetwork({ - httpConfig, - nodeConnectionConfig: { baseURL }, - signatureProvider: TestData.fakeSignatureProvider, - }); - const request = await requestNetwork.createRequest({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }); - expect(spy).toHaveBeenCalledTimes(1); - - await request.waitForConfirmation(); - }); - - it('allows to create a request', async () => { - const mock = TestData.mockAxiosRequestNode(); - const requestNetwork = new RequestNetwork({ - httpConfig, - signatureProvider: TestData.fakeSignatureProvider, - }); - - const request = await requestNetwork.createRequest({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }); - await request.waitForConfirmation(); - - expect(request).toBeInstanceOf(Request); - expect(request.requestId).toBeDefined(); - expect(mock.history.get).toHaveLength(3); - expect(mock.history.post).toHaveLength(1); - - // Assert on the length to avoid unnecessary maintenance of the test. 66 = 64 char + '0x' - const requestIdLength = 66; - expect(request.requestId.length).toBe(requestIdLength); - }); - - it('allows to compute a request id', async () => { - const mock = TestData.mockAxiosRequestNode(); - const requestNetwork = new RequestNetwork({ - httpConfig, - signatureProvider: TestData.fakeSignatureProvider, - }); - - mock.resetHistory(); - - const requestId = await requestNetwork.computeRequestId({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }); - - expect(mock.history.get).toHaveLength(0); - expect(mock.history.post).toHaveLength(0); - - // Assert on the length to avoid unnecessary maintenance of the test. 66 = 64 char + '0x' - const requestIdLength = 66; - expect(requestId.length).toBe(requestIdLength); - }); + signatureProvider: TestData.fakeSignatureProvider, + paymentOptions: { + bitcoinDetectionProvider: mockBTCProvider, + }, + }); - it('allows to compute a request id, then generate the request with the same id', async () => { - const mock = TestData.mockAxiosRequestNode(); - const requestNetwork = new RequestNetwork({ - httpConfig, - signatureProvider: TestData.fakeSignatureProvider, - }); + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: {}, + }; + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + expect(spy).toHaveBeenCalledTimes(1); - const requestId = await requestNetwork.computeRequestId({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }); - // Assert on the length to avoid unnecessary maintenance of the test. 66 = 64 char + '0x' - const requestIdLength = 66; - expect(requestId.length).toBe(requestIdLength); - - await new Promise((resolve): any => setTimeout(resolve, 150)); - const request = await requestNetwork.createRequest({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, + await request.waitForConfirmation(); }); - await request.waitForConfirmation(); - expect(request).toBeInstanceOf(Request); - expect(request.requestId).toBe(requestId); - expect(mock.history.get).toHaveLength(3); - expect(mock.history.post).toHaveLength(1); - }); + it('uses http://localhost:3000 with signatureProvider and paymentNetwork', async () => { + const mock = new AxiosMockAdapter(axios); - it('allows to get a request from its ID', async () => { - TestData.mockAxiosRequestNode(); - const requestNetwork = new RequestNetwork({ - httpConfig, - signatureProvider: TestData.fakeSignatureProvider, - }); - - const request = await requestNetwork.createRequest({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }); - await request.waitForConfirmation(); + const callback = (config: any): any => { + expect(config.baseURL).toBe('http://localhost:3000'); + return [200, {}]; + }; + const spy = jest.fn(callback); + mock.onPost('/persistTransaction').reply(spy); + mock + .onGet('/getTransactionsByChannelId') + .reply(200, { result: { transactions: [TestData.timestampedTransaction] } }); + mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); - const requestFromId = await requestNetwork.fromRequestId(request.requestId); + const requestNetwork = new RequestNetwork({ + httpConfig, + signatureProvider: TestData.fakeSignatureProvider, + paymentOptions: { + bitcoinDetectionProvider: mockBTCProvider, + }, + }); - expect(requestFromId.requestId).toBe(request.requestId); - }); + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: {}, + }; + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + expect(spy).toHaveBeenCalledTimes(1); - it('allows to get a request from its ID with a payment network', async () => { - const requestNetwork = new RequestNetwork({ - signatureProvider: TestData.fakeSignatureProvider, - useMockStorage: true, + await request.waitForConfirmation(); }); - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, - parameters: {}, - }; - - const request = await requestNetwork.createRequest({ - paymentNetwork, - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }); - await request.waitForConfirmation(); + it('uses http://localhost:3000 with persist from local', async () => { + const mock = new AxiosMockAdapter(axios); + const callback = (): any => { + return [200, { ipfsSize: 100, ipfsHash: 'QmZLqH4EsjmB79gjvyzXWBcihbNBZkw8YuELco84PxGzQY' }]; + }; + // const spyPersistTransaction = jest.fn(); + const spyIpfsAdd = jest.fn(callback); + // mock.onPost('/persistTransaction').reply(spyPersistTransaction); + mock.onPost('/persistTransaction').reply(200, { meta: {}, result: {} }); + mock.onPost('/ipfsAdd').reply(spyIpfsAdd); + mock.onGet('/getTransactionsByChannelId').reply(200, { + meta: { storageMeta: [], transactionsStorageLocation: [] }, + result: { transactions: [] }, + }); - const requestFromId = await requestNetwork.fromRequestId(request.requestId); + const requestNetwork = new RequestNetworkBase({ + dataAccess: new HttpMetaMaskDataAccess({ + httpConfig, + ethereumProviderUrl: 'http://localhost:8545', + }), + signatureProvider: TestData.fakeSignatureProvider, + paymentOptions: { + bitcoinDetectionProvider: mockBTCProvider, + }, + }); - expect(requestFromId.getData()).toMatchObject({ - requestId: request.requestId, - currency: 'BTC-testnet-testnet', - currencyInfo: { - network: 'testnet', - type: RequestLogicTypes.CURRENCY.BTC, - value: 'BTC', - }, - balance: { - balance: '0', - events: [], - }, - }); - }); + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED, + parameters: { + paymentAddress: 'mgPKDuVmuS9oeE2D9VPiCQriyU14wxWS1v', + }, + }; - it('allows to refresh a request', async () => { - const mock = new AxiosMockAdapter(axios); - mock.onPost('/persistTransaction').reply(200, { result: {} }); - mock.onGet('/getTransactionsByChannelId').reply(200, { - result: { transactions: [TestData.timestampedTransactionWithoutExtensionsData] }, - }); - mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + expect(spyIpfsAdd).toHaveBeenCalledTimes(1); - const requestNetwork = new RequestNetwork({ - httpConfig, - signatureProvider: TestData.fakeSignatureProvider, - }); - const request = await requestNetwork.createRequest({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, + await request.waitForConfirmation(); }); - await request.waitForConfirmation(); - - mock.resetHistory(); - const data = await request.refresh(); + it('uses http://localhost:3000 with signatureProvider and paymentNetwork real btc', async () => { + const mock = new AxiosMockAdapter(axios); - expect(data).toBeDefined(); - expect(data.balance).toBeNull(); - expect(data.meta).toBeDefined(); - expect(mock.history.get).toHaveLength(1); - expect(mock.history.post).toHaveLength(0); - }); - - it('works with mocked storage', async () => { - const mockStorage = new MockStorage(); - const mockDataAccess = new MockDataAccess(mockStorage); - const requestNetwork = new RequestNetworkBase({ - dataAccess: mockDataAccess, - signatureProvider: TestData.fakeSignatureProvider, - }); - const request = await requestNetwork.createRequest({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }); + const callback = (config: any): any => { + expect(config.baseURL).toBe('http://localhost:3000'); + return [200, {}]; + }; + const spy = jest.fn(callback); + mock.onPost('/persistTransaction').reply(spy); + mock.onGet('/getTransactionsByChannelId').reply(200, { + result: { transactions: [TestDataRealBTC.timestampedTransaction] }, + }); + mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); - const data = request.getData(); - expect(data).toBeDefined(); - expect(data.balance).toBeNull(); - expect(data.meta).toBeDefined(); - expect(data.currencyInfo).toMatchObject(TestData.parametersWithoutExtensionsData.currency); - expect(data.state).toBe(RequestLogicTypes.STATE.PENDING); - expect(data.pending?.state).toBe(RequestLogicTypes.STATE.CREATED); - - const dataConfirmed = await request.waitForConfirmation(); - expect(dataConfirmed.state).toBe(RequestLogicTypes.STATE.CREATED); - expect(dataConfirmed.pending).toBeNull(); - }); + const requestNetwork = new RequestNetwork({ + httpConfig, + signatureProvider: TestData.fakeSignatureProvider, + paymentOptions: { + bitcoinDetectionProvider: mockBTCProvider, + }, + }); - it('works with mocked storage emitting error when append', async () => { - const mockStorage = new MockStorage(); - const mockDataAccess = new MockDataAccess(mockStorage); - const requestNetwork = new RequestNetworkBase({ - signatureProvider: TestData.fakeSignatureProvider, - dataAccess: mockDataAccess, - }); + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.BITCOIN_ADDRESS_BASED, + parameters: { + paymentAddress: '1FersucwSqufU26w9GrGz9M3KcwuNmy6a9', + }, + }; - // ask mock up storage to emit error next append call() - mockStorage._makeNextAppendFailInsteadOfConfirmed(); + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: requestParameters, + signer: TestData.payee.identity, + }); + expect(spy).toHaveBeenCalledTimes(1); - const request = await requestNetwork.createRequest({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, + await request.waitForConfirmation(); }); - const data = request.getData(); - expect(data).toBeDefined(); - expect(data.balance).toBeNull(); - expect(data.meta).toBeDefined(); - expect(data.currencyInfo).toMatchObject(TestData.parametersWithoutExtensionsData.currency); - expect(data.state).toBe(RequestLogicTypes.STATE.PENDING); - expect(data.pending?.state).toBe(RequestLogicTypes.STATE.CREATED); - - const errorEmitted: string = await new Promise((resolve): any => request.on('error', resolve)); - expect(errorEmitted).toBe('forced error asked by _makeNextAppendFailInsteadOfConfirmed()'); - - expect(() => request.getData()).toThrow('request confirmation failed'); - await expect(request.refresh()).rejects.toThrowError('request confirmation failed'); - }); + it('uses http://localhost:3000 with signatureProvider', async () => { + const mock = new AxiosMockAdapter(axios); - it('works with mocked storage emitting error when append waitForConfirmation will throw', async () => { - const mockStorage = new MockStorage(); - const mockDataAccess = new MockDataAccess(mockStorage); - const requestNetworkInside = new RequestNetworkBase({ - signatureProvider: TestData.fakeSignatureProvider, - dataAccess: mockDataAccess, - }); + const callback = (config: any): any => { + expect(config.baseURL).toBe('http://localhost:3000'); + return [200, {}]; + }; + const spy = jest.fn(callback); + mock.onPost('/persistTransaction').reply(spy); + mock.onGet('/getTransactionsByChannelId').reply(200, { + result: { transactions: [TestData.timestampedTransactionWithoutExtensionsData] }, + }); + mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); - // ask mock up storage to emit error next append call() - mockStorage._makeNextAppendFailInsteadOfConfirmed(); + const requestNetwork = new RequestNetwork({ + httpConfig, + signatureProvider: TestData.fakeSignatureProvider, + }); - const request = await requestNetworkInside.createRequest({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, + await requestNetwork.createRequest({ + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + expect(spy).toHaveBeenCalledTimes(1); }); - const data = request.getData(); - expect(data).toBeDefined(); - expect(data.balance).toBeNull(); - expect(data.meta).toBeDefined(); - expect(data.currencyInfo).toMatchObject(TestData.parametersWithoutExtensionsData.currency); - expect(data.state).toBe(RequestLogicTypes.STATE.PENDING); - expect(data.pending?.state).toBe(RequestLogicTypes.STATE.CREATED); - - await expect(request.waitForConfirmation()).rejects.toBe( - 'forced error asked by _makeNextAppendFailInsteadOfConfirmed()', - ); + it('uses baseUrl given in parameter', async () => { + const baseURL = 'http://request.network/api'; + const mock = new AxiosMockAdapter(axios); - expect(() => request.getData()).toThrowError('request confirmation failed'); - await expect(request.refresh()).rejects.toThrowError('request confirmation failed'); - }); + const callback = (config: any): any => { + expect(config.baseURL).toBe(baseURL); + return [200, {}]; + }; + const spy = jest.fn(callback); + mock.onPost('/persistTransaction').reply(spy); + mock.onGet('/getTransactionsByChannelId').reply(200, { + result: { transactions: [TestData.timestampedTransactionWithoutExtensionsData] }, + }); + mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); - it('creates a request with error event', async () => { - const requestNetwork = new RequestNetwork({ - signatureProvider: TestData.fakeSignatureProvider, - useMockStorage: true, - }); + const requestNetwork = new RequestNetwork({ + httpConfig, + nodeConnectionConfig: { baseURL }, + signatureProvider: TestData.fakeSignatureProvider, + }); + const request = await requestNetwork.createRequest({ + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + expect(spy).toHaveBeenCalledTimes(1); - const request = await requestNetwork.createRequest({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, + await request.waitForConfirmation(); }); - - const data = request.getData(); - expect(data).toBeDefined(); - expect(data.balance).toBeNull(); - expect(data.meta).toBeDefined(); - expect(data.currencyInfo).toMatchObject(TestData.parametersWithoutExtensionsData.currency); - expect(data.state).toBe(RequestLogicTypes.STATE.PENDING); - expect(data.pending?.state).toBe(RequestLogicTypes.STATE.CREATED); - - const dataConfirmed = await request.waitForConfirmation(); - expect(dataConfirmed.state).toBe(RequestLogicTypes.STATE.CREATED); - expect(dataConfirmed.pending).toBeNull(); }); - it('works with mocked storage and mocked payment network', async () => { - const requestNetwork = new RequestNetwork({ - signatureProvider: TestData.fakeSignatureProvider, - useMockStorage: true, - paymentOptions: { - bitcoinDetectionProvider: mockBTCProvider, - }, - }); - - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED, - parameters: { - paymentAddress: 'mgPKDuVmuS9oeE2D9VPiCQriyU14wxWS1v', - }, - }; + describe('Request Logic without encryption', () => { + it('allows to create a request', async () => { + const mock = TestData.mockAxiosRequestNode(); + const requestNetwork = new RequestNetwork({ + httpConfig, + signatureProvider: TestData.fakeSignatureProvider, + }); - const request = await requestNetwork.createRequest({ - paymentNetwork, - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }); + const request = await requestNetwork.createRequest({ + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + await request.waitForConfirmation(); - const data = request.getData(); - expect(data).toBeDefined(); - expect(data.balance).toBeNull(); - expect(data.meta).toBeDefined(); - expect(data.currencyInfo).toMatchObject(TestData.parametersWithoutExtensionsData.currency); - expect(data.state).toBe(RequestLogicTypes.STATE.PENDING); - expect(data.pending?.state).toBe(RequestLogicTypes.STATE.CREATED); - - const dataConfirmed = await request.waitForConfirmation(); - expect(dataConfirmed.state).toBe(RequestLogicTypes.STATE.CREATED); - expect(dataConfirmed.pending).toBeNull(); - expect(dataConfirmed.balance?.balance).toBe('666743'); - expect(dataConfirmed.balance?.events.length).toBe(1); - }); + expect(request).toBeInstanceOf(Request); + expect(request.requestId).toBeDefined(); + expect(mock.history.get).toHaveLength(3); + expect(mock.history.post).toHaveLength(1); - it('works with mocked storage and content data', async () => { - const requestNetwork = new RequestNetwork({ - signatureProvider: TestData.fakeSignatureProvider, - useMockStorage: true, + // Assert on the length to avoid unnecessary maintenance of the test. 66 = 64 char + '0x' + const requestIdLength = 66; + expect(request.requestId.length).toBe(requestIdLength); }); - const contentData = { - invoice: true, - what: 'ever', - }; + it('allows to compute a request id', async () => { + const mock = TestData.mockAxiosRequestNode(); + const requestNetwork = new RequestNetwork({ + httpConfig, + signatureProvider: TestData.fakeSignatureProvider, + }); - const request = await requestNetwork.createRequest({ - contentData, - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }); + mock.resetHistory(); - const data = request.getData(); - expect(data).toBeDefined(); - expect(data.balance).toBeNull(); - expect(data.meta).toBeDefined(); + const requestId = await requestNetwork.computeRequestId({ + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); - await request.waitForConfirmation(); - }); + expect(mock.history.get).toHaveLength(0); + expect(mock.history.post).toHaveLength(0); - it('allows to accept a request', async () => { - const mock = TestData.mockAxiosRequestNode(); - const requestNetwork = new RequestNetwork({ - httpConfig, - signatureProvider: TestData.fakeSignatureProvider, + // Assert on the length to avoid unnecessary maintenance of the test. 66 = 64 char + '0x' + const requestIdLength = 66; + expect(requestId.length).toBe(requestIdLength); }); - const request = await requestNetwork.createRequest({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }); - await request.waitForConfirmation(); - - mock.resetHistory(); - - const requestDataWithEvents = await request.accept(TestData.payer.identity); - await waitForConfirmation(requestDataWithEvents); - expect(mock.history.get).toHaveLength(5); - expect(mock.history.post).toHaveLength(1); - }); + it('allows to compute a request id, then generate the request with the same id', async () => { + const mock = TestData.mockAxiosRequestNode(); + const requestNetwork = new RequestNetwork({ + httpConfig, + signatureProvider: TestData.fakeSignatureProvider, + }); - it('works with mocked storage emitting error when append an accept', async () => { - const mockStorage = new MockStorage(); - const mockDataAccess = new MockDataAccess(mockStorage); - const requestNetwork = new RequestNetworkBase({ - signatureProvider: TestData.fakeSignatureProvider, - dataAccess: mockDataAccess, - }); + const requestId = await requestNetwork.computeRequestId({ + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + // Assert on the length to avoid unnecessary maintenance of the test. 66 = 64 char + '0x' + const requestIdLength = 66; + expect(requestId.length).toBe(requestIdLength); - const request = await requestNetwork.createRequest({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }); - await request.waitForConfirmation(); - - // ask mock up storage to emit error next append call() - mockStorage._makeNextAppendFailInsteadOfConfirmed(); - await request.accept(TestData.payer.identity); - - let data = request.getData(); - expect(data).toBeDefined(); - expect(data.balance).toBeNull(); - expect(data.meta).toBeDefined(); - expect(data.currencyInfo).toMatchObject(TestData.parametersWithoutExtensionsData.currency); - expect(data.state).toBe(RequestLogicTypes.STATE.CREATED); - expect(data.pending?.state).toBe(RequestLogicTypes.STATE.ACCEPTED); - - const errorEmitted: string = await new Promise((resolve): any => request.on('error', resolve)); - expect(errorEmitted).toBe('forced error asked by _makeNextAppendFailInsteadOfConfirmed()'); - - data = request.getData(); - expect(data.state).toBe(RequestLogicTypes.STATE.CREATED); - expect(data.pending?.state).toBe(RequestLogicTypes.STATE.ACCEPTED); - - // TODO: For now data will be pending forever. - // Ethereum-storage should treat the errors and clean up. - data = await request.refresh(); - expect(data.state).toBe(RequestLogicTypes.STATE.CREATED); - expect(data.pending?.state).toBe(RequestLogicTypes.STATE.ACCEPTED); - }); + await new Promise((resolve): any => setTimeout(resolve, 150)); + const request = await requestNetwork.createRequest({ + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + await request.waitForConfirmation(); - it('allows to cancel a request', async () => { - const mock = TestData.mockAxiosRequestNode(); - const requestNetwork = new RequestNetwork({ - httpConfig, - signatureProvider: TestData.fakeSignatureProvider, - }); - const request = await requestNetwork.createRequest({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, + expect(request).toBeInstanceOf(Request); + expect(request.requestId).toBe(requestId); + expect(mock.history.get).toHaveLength(3); + expect(mock.history.post).toHaveLength(1); }); - await request.waitForConfirmation(); - mock.resetHistory(); + it('allows to get a request from its ID', async () => { + TestData.mockAxiosRequestNode(); + const requestNetwork = new RequestNetwork({ + httpConfig, + signatureProvider: TestData.fakeSignatureProvider, + }); - await waitForConfirmation(request.cancel(TestData.payee.identity)); + const request = await requestNetwork.createRequest({ + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + await request.waitForConfirmation(); - expect(mock.history.get).toHaveLength(5); - expect(mock.history.post).toHaveLength(1); - }); + const requestFromId = await requestNetwork.fromRequestId(request.requestId); - it('allows to increase the expected amount a request', async () => { - const mock = TestData.mockAxiosRequestNode(); - const requestNetwork = new RequestNetwork({ - httpConfig, - signatureProvider: TestData.fakeSignatureProvider, - }); - const request = await requestNetwork.createRequest({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, + expect(requestFromId.requestId).toBe(request.requestId); }); - await request.waitForConfirmation(); - mock.resetHistory(); - - await waitForConfirmation(request.increaseExpectedAmountRequest(3, TestData.payer.identity)); - - expect(mock.history.get).toHaveLength(5); - expect(mock.history.post).toHaveLength(1); - }); - - it('allows to reduce the expected amount a request', async () => { - const mock = TestData.mockAxiosRequestNode(); - const requestNetwork = new RequestNetwork({ - httpConfig, - signatureProvider: TestData.fakeSignatureProvider, - }); - const request = await requestNetwork.createRequest({ - requestInfo: TestData.parametersWithoutExtensionsData, - signer: TestData.payee.identity, - }); - await request.waitForConfirmation(); + it('allows to get a request from its ID with a payment network', async () => { + const requestNetwork = new RequestNetwork({ + signatureProvider: TestData.fakeSignatureProvider, + useMockStorage: true, + }); - mock.resetHistory(); + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, + parameters: {}, + }; - await waitForConfirmation(request.reduceExpectedAmountRequest(3, TestData.payee.identity)); + const request = await requestNetwork.createRequest({ + paymentNetwork, + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + await request.waitForConfirmation(); - expect(mock.history.get).toHaveLength(5); - expect(mock.history.post).toHaveLength(1); - }); + const requestFromId = await requestNetwork.fromRequestId(request.requestId); - describe('tests with declarative payments', () => { - let mock: AxiosMockAdapter; - afterEach(() => { - jest.clearAllMocks(); - mock.reset(); + expect(requestFromId.getData()).toMatchObject({ + requestId: request.requestId, + currency: 'BTC-testnet-testnet', + currencyInfo: { + network: 'testnet', + type: RequestLogicTypes.CURRENCY.BTC, + value: 'BTC', + }, + balance: { + balance: '0', + events: [], + }, + }); }); - beforeEach(() => { - mock = new AxiosMockAdapter(axios); - const callback = (config: any): any => { - expect(config.baseURL).toBe('http://localhost:3000'); - return [200, {}]; - }; - const spy = jest.fn(callback); - mock.onPost('/persistTransaction').reply(spy); + it('allows to refresh a request', async () => { + const mock = new AxiosMockAdapter(axios); + mock.onPost('/persistTransaction').reply(200, { result: {} }); mock.onGet('/getTransactionsByChannelId').reply(200, { - result: { transactions: [TestData.timestampedTransactionWithDeclarative] }, + result: { transactions: [TestData.timestampedTransactionWithoutExtensionsData] }, }); mock.onGet('/getConfirmedTransaction').reply(200, { result: {} }); - }); - it('allows to declare a sent payment', async () => { const requestNetwork = new RequestNetwork({ httpConfig, signatureProvider: TestData.fakeSignatureProvider, }); - - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, - parameters: {}, - }; - const request = await requestNetwork.createRequest({ - paymentNetwork, requestInfo: TestData.parametersWithoutExtensionsData, signer: TestData.payee.identity, }); @@ -823,114 +477,143 @@ describe('index', () => { mock.resetHistory(); - await waitForConfirmation( - request.declareSentPayment('10', 'sent payment', TestData.payer.identity), - ); + const data = await request.refresh(); - expect(mock.history.get).toHaveLength(5); - expect(mock.history.post).toHaveLength(1); + expect(data).toBeDefined(); + expect(data.balance).toBeNull(); + expect(data.meta).toBeDefined(); + expect(mock.history.get).toHaveLength(1); + expect(mock.history.post).toHaveLength(0); + }); + + it('works with mocked storage', async () => { + const mockStorage = new MockStorage(); + const mockDataAccess = new MockDataAccess(mockStorage); + const requestNetwork = new RequestNetworkBase({ + dataAccess: mockDataAccess, + signatureProvider: TestData.fakeSignatureProvider, + }); + const request = await requestNetwork.createRequest({ + requestInfo: TestData.parametersWithoutExtensionsData, + signer: TestData.payee.identity, + }); + + const data = request.getData(); + expect(data).toBeDefined(); + expect(data.balance).toBeNull(); + expect(data.meta).toBeDefined(); + expect(data.currencyInfo).toMatchObject(TestData.parametersWithoutExtensionsData.currency); + expect(data.state).toBe(RequestLogicTypes.STATE.PENDING); + expect(data.pending?.state).toBe(RequestLogicTypes.STATE.CREATED); + + const dataConfirmed = await request.waitForConfirmation(); + expect(dataConfirmed.state).toBe(RequestLogicTypes.STATE.CREATED); + expect(dataConfirmed.pending).toBeNull(); }); - it('allows to declare a received payment', async () => { - const requestNetwork = new RequestNetwork({ - httpConfig, + it('works with mocked storage emitting error when append', async () => { + const mockStorage = new MockStorage(); + const mockDataAccess = new MockDataAccess(mockStorage); + const requestNetwork = new RequestNetworkBase({ signatureProvider: TestData.fakeSignatureProvider, + dataAccess: mockDataAccess, }); - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, - parameters: {}, - }; + // ask mock up storage to emit error next append call() + mockStorage._makeNextAppendFailInsteadOfConfirmed(); const request = await requestNetwork.createRequest({ - paymentNetwork, requestInfo: TestData.parametersWithoutExtensionsData, signer: TestData.payee.identity, }); - await request.waitForConfirmation(); - mock.resetHistory(); + const data = request.getData(); + expect(data).toBeDefined(); + expect(data.balance).toBeNull(); + expect(data.meta).toBeDefined(); + expect(data.currencyInfo).toMatchObject(TestData.parametersWithoutExtensionsData.currency); + expect(data.state).toBe(RequestLogicTypes.STATE.PENDING); + expect(data.pending?.state).toBe(RequestLogicTypes.STATE.CREATED); - await waitForConfirmation( - request.declareReceivedPayment('10', 'received payment', TestData.payee.identity), + const errorEmitted: string = await new Promise((resolve): any => + request.on('error', resolve), ); + expect(errorEmitted).toBe('forced error asked by _makeNextAppendFailInsteadOfConfirmed()'); - expect(mock.history.get).toHaveLength(5); - expect(mock.history.post).toHaveLength(1); + expect(() => request.getData()).toThrow('request confirmation failed'); + await expect(request.refresh()).rejects.toThrowError('request confirmation failed'); }); - it('allows to create a request with delegate', async () => { - const requestNetwork = new RequestNetwork({ - useMockStorage: true, + it('works with mocked storage emitting error when append waitForConfirmation will throw', async () => { + const mockStorage = new MockStorage(); + const mockDataAccess = new MockDataAccess(mockStorage); + const requestNetworkInside = new RequestNetworkBase({ signatureProvider: TestData.fakeSignatureProvider, + dataAccess: mockDataAccess, }); - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, - parameters: {}, - }; + // ask mock up storage to emit error next append call() + mockStorage._makeNextAppendFailInsteadOfConfirmed(); - const request = await requestNetwork.createRequest({ - paymentNetwork, - requestInfo: { - ...TestData.parametersWithoutExtensionsData, - extensionsData: [ - { - action: ExtensionTypes.PnAnyDeclarative.ACTION.ADD_DELEGATE, - id: ExtensionTypes.ID.PAYMENT_NETWORK_ANY_DECLARATIVE, - parameters: { - delegate: TestData.delegate.identity, - }, - }, - ], - }, + const request = await requestNetworkInside.createRequest({ + requestInfo: TestData.parametersWithoutExtensionsData, signer: TestData.payee.identity, }); - await request.waitForConfirmation(); - const requestData = await waitForConfirmation( - request.declareReceivedPayment('10', 'received payment', TestData.delegate.identity), + const data = request.getData(); + expect(data).toBeDefined(); + expect(data.balance).toBeNull(); + expect(data.meta).toBeDefined(); + expect(data.currencyInfo).toMatchObject(TestData.parametersWithoutExtensionsData.currency); + expect(data.state).toBe(RequestLogicTypes.STATE.PENDING); + expect(data.pending?.state).toBe(RequestLogicTypes.STATE.CREATED); + + await expect(request.waitForConfirmation()).rejects.toBe( + 'forced error asked by _makeNextAppendFailInsteadOfConfirmed()', ); - expect(requestData.balance!.balance).toEqual('10'); + + expect(() => request.getData()).toThrowError('request confirmation failed'); + await expect(request.refresh()).rejects.toThrowError('request confirmation failed'); }); - it('allows to declare a received payment from delegate', async () => { + it('creates a request with error event', async () => { const requestNetwork = new RequestNetwork({ - useMockStorage: true, signatureProvider: TestData.fakeSignatureProvider, + useMockStorage: true, }); - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, - parameters: {}, - }; - const request = await requestNetwork.createRequest({ - paymentNetwork, requestInfo: TestData.parametersWithoutExtensionsData, signer: TestData.payee.identity, }); - await request.waitForConfirmation(); - await waitForConfirmation( - request.addDeclarativeDelegate(TestData.delegate.identity, TestData.payee.identity), - ); + const data = request.getData(); + expect(data).toBeDefined(); + expect(data.balance).toBeNull(); + expect(data.meta).toBeDefined(); + expect(data.currencyInfo).toMatchObject(TestData.parametersWithoutExtensionsData.currency); + expect(data.state).toBe(RequestLogicTypes.STATE.PENDING); + expect(data.pending?.state).toBe(RequestLogicTypes.STATE.CREATED); - const requestData = await waitForConfirmation( - request.declareReceivedPayment('10', 'received payment', TestData.delegate.identity), - ); - expect(requestData.balance!.balance).toEqual('10'); + const dataConfirmed = await request.waitForConfirmation(); + expect(dataConfirmed.state).toBe(RequestLogicTypes.STATE.CREATED); + expect(dataConfirmed.pending).toBeNull(); }); - it('allows to declare a received payment by providing transaction hash and network', async () => { + it('works with mocked storage and mocked payment network', async () => { const requestNetwork = new RequestNetwork({ - httpConfig, signatureProvider: TestData.fakeSignatureProvider, + useMockStorage: true, + paymentOptions: { + bitcoinDetectionProvider: mockBTCProvider, + }, }); const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, - parameters: {}, + id: PaymentTypes.PAYMENT_NETWORK_ID.TESTNET_BITCOIN_ADDRESS_BASED, + parameters: { + paymentAddress: 'mgPKDuVmuS9oeE2D9VPiCQriyU14wxWS1v', + }, }; const request = await requestNetwork.createRequest({ @@ -938,65 +621,54 @@ describe('index', () => { requestInfo: TestData.parametersWithoutExtensionsData, signer: TestData.payee.identity, }); - await request.waitForConfirmation(); - - mock.resetHistory(); - await waitForConfirmation( - request.declareReceivedPayment( - '10', - 'received payment', - TestData.payee.identity, - '0x123456789', - 'mainnet', - ), - ); + const data = request.getData(); + expect(data).toBeDefined(); + expect(data.balance).toBeNull(); + expect(data.meta).toBeDefined(); + expect(data.currencyInfo).toMatchObject(TestData.parametersWithoutExtensionsData.currency); + expect(data.state).toBe(RequestLogicTypes.STATE.PENDING); + expect(data.pending?.state).toBe(RequestLogicTypes.STATE.CREATED); - expect(mock.history.get).toHaveLength(5); - expect(mock.history.post).toHaveLength(1); + const dataConfirmed = await request.waitForConfirmation(); + expect(dataConfirmed.state).toBe(RequestLogicTypes.STATE.CREATED); + expect(dataConfirmed.pending).toBeNull(); + expect(dataConfirmed.balance?.balance).toBe('666743'); + expect(dataConfirmed.balance?.events.length).toBe(1); }); - it('allows to declare a sent refund', async () => { + it('works with mocked storage and content data', async () => { const requestNetwork = new RequestNetwork({ - httpConfig, signatureProvider: TestData.fakeSignatureProvider, + useMockStorage: true, }); - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, - parameters: {}, + const contentData = { + invoice: true, + what: 'ever', }; const request = await requestNetwork.createRequest({ - paymentNetwork, + contentData, requestInfo: TestData.parametersWithoutExtensionsData, signer: TestData.payee.identity, }); - await request.waitForConfirmation(); - - mock.resetHistory(); - await waitForConfirmation( - request.declareSentRefund('10', 'sent refund', TestData.payee.identity), - ); + const data = request.getData(); + expect(data).toBeDefined(); + expect(data.balance).toBeNull(); + expect(data.meta).toBeDefined(); - expect(mock.history.get).toHaveLength(5); - expect(mock.history.post).toHaveLength(1); + await request.waitForConfirmation(); }); - it('allows to declare a received refund', async () => { + it('allows to accept a request', async () => { + const mock = TestData.mockAxiosRequestNode(); const requestNetwork = new RequestNetwork({ httpConfig, signatureProvider: TestData.fakeSignatureProvider, }); - - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, - parameters: {}, - }; - const request = await requestNetwork.createRequest({ - paymentNetwork, requestInfo: TestData.parametersWithoutExtensionsData, signer: TestData.payee.identity, }); @@ -1004,95 +676,82 @@ describe('index', () => { mock.resetHistory(); - await waitForConfirmation( - request.declareReceivedRefund('10', 'received refund', TestData.payer.identity), - ); + const requestDataWithEvents = await request.accept(TestData.payer.identity); + await waitForConfirmation(requestDataWithEvents); expect(mock.history.get).toHaveLength(5); expect(mock.history.post).toHaveLength(1); }); - it('can have a payment reference on a declarative payment network', async () => { - const requestNetwork = new RequestNetwork({ - httpConfig, - useMockStorage: true, + it('works with mocked storage emitting error when append an accept', async () => { + const mockStorage = new MockStorage(); + const mockDataAccess = new MockDataAccess(mockStorage); + const requestNetwork = new RequestNetworkBase({ signatureProvider: TestData.fakeSignatureProvider, + dataAccess: mockDataAccess, }); - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = - { - id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, - parameters: { - paymentInfo: { - IBAN: 'FR123456789123456789', - BIC: 'CE123456789', - }, - salt: 'a1a2a3a4a5a6a7a8', - }, - }; - const request = await requestNetwork.createRequest({ - paymentNetwork, requestInfo: TestData.parametersWithoutExtensionsData, signer: TestData.payee.identity, }); await request.waitForConfirmation(); - const data = request.getData(); + // ask mock up storage to emit error next append call() + mockStorage._makeNextAppendFailInsteadOfConfirmed(); + await request.accept(TestData.payer.identity); - const pn = getPaymentNetworkExtension(data)!; + let data = request.getData(); + expect(data).toBeDefined(); + expect(data.balance).toBeNull(); + expect(data.meta).toBeDefined(); + expect(data.currencyInfo).toMatchObject(TestData.parametersWithoutExtensionsData.currency); + expect(data.state).toBe(RequestLogicTypes.STATE.CREATED); + expect(data.pending?.state).toBe(RequestLogicTypes.STATE.ACCEPTED); - const paymentReference = PaymentReferenceCalculator.calculate( - data.requestId, - pn.values.salt, - JSON.stringify(pn.values.paymentInfo), + const errorEmitted: string = await new Promise((resolve): any => + request.on('error', resolve), ); + expect(errorEmitted).toBe('forced error asked by _makeNextAppendFailInsteadOfConfirmed()'); + + data = request.getData(); + expect(data.state).toBe(RequestLogicTypes.STATE.CREATED); + expect(data.pending?.state).toBe(RequestLogicTypes.STATE.ACCEPTED); - expect(paymentReference).toHaveLength(16); - expect(paymentReference).toBe(getPaymentReference(data)); + // TODO: For now data will be pending forever. + // Ethereum-storage should treat the errors and clean up. + data = await request.refresh(); + expect(data.state).toBe(RequestLogicTypes.STATE.CREATED); + expect(data.pending?.state).toBe(RequestLogicTypes.STATE.ACCEPTED); }); - it('allows to declare a received refund from delegate', async () => { + it('allows to cancel a request', async () => { + const mock = TestData.mockAxiosRequestNode(); const requestNetwork = new RequestNetwork({ - useMockStorage: true, + httpConfig, signatureProvider: TestData.fakeSignatureProvider, }); - - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, - parameters: {}, - }; - const request = await requestNetwork.createRequest({ - paymentNetwork, requestInfo: TestData.parametersWithoutExtensionsData, signer: TestData.payee.identity, }); await request.waitForConfirmation(); - await waitForConfirmation( - request.addDeclarativeDelegate(TestData.delegate.identity, TestData.payer.identity), - ); + mock.resetHistory(); - const requestData = await waitForConfirmation( - request.declareReceivedRefund('11', 'received refund', TestData.delegate.identity), - ); - expect(requestData.balance!.balance).toEqual('-11'); + await waitForConfirmation(request.cancel(TestData.payee.identity)); + + expect(mock.history.get).toHaveLength(5); + expect(mock.history.post).toHaveLength(1); }); - it('allows to declare a received refund by providing transaction hash', async () => { + it('allows to increase the expected amount a request', async () => { + const mock = TestData.mockAxiosRequestNode(); const requestNetwork = new RequestNetwork({ httpConfig, signatureProvider: TestData.fakeSignatureProvider, }); - - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, - parameters: {}, - }; - const request = await requestNetwork.createRequest({ - paymentNetwork, requestInfo: TestData.parametersWithoutExtensionsData, signer: TestData.payee.identity, }); @@ -1100,157 +759,34 @@ describe('index', () => { mock.resetHistory(); - await waitForConfirmation( - request.declareReceivedRefund( - '10', - 'received refund', - TestData.payer.identity, - '0x123456789', - ), - ); + await waitForConfirmation(request.increaseExpectedAmountRequest(3, TestData.payer.identity)); expect(mock.history.get).toHaveLength(5); expect(mock.history.post).toHaveLength(1); }); - it('allows to get the right balance', async () => { - const requestParametersUSD: RequestLogicTypes.ICreateParameters = { - currency: { - type: RequestLogicTypes.CURRENCY.ISO4217, - value: 'USD', - }, - expectedAmount: '100000000000', - payee: TestData.payee.identity, - payer: TestData.payer.identity, - }; - + it('allows to reduce the expected amount a request', async () => { + const mock = TestData.mockAxiosRequestNode(); const requestNetwork = new RequestNetwork({ + httpConfig, signatureProvider: TestData.fakeSignatureProvider, - useMockStorage: true, }); - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.DECLARATIVE, - parameters: {}, - }; - const request = await requestNetwork.createRequest({ - paymentNetwork, - requestInfo: requestParametersUSD, + requestInfo: TestData.parametersWithoutExtensionsData, signer: TestData.payee.identity, }); await request.waitForConfirmation(); - await waitForConfirmation( - request.declareSentPayment('1', 'sent payment', TestData.payer.identity), - ); - - await waitForConfirmation( - request.declareReceivedRefund('10', 'received refund', TestData.payer.identity), - ); - - await waitForConfirmation( - request.declareSentRefund('100', 'sent refund', TestData.payee.identity), - ); - - await waitForConfirmation( - request.declareReceivedPayment('1000', 'received payment', TestData.payee.identity), - ); - - await waitForConfirmation( - request.addPaymentInformation('payment info added', TestData.payee.identity), - ); - await waitForConfirmation( - request.addRefundInformation('refund info added', TestData.payer.identity), - ); - - const requestData = await request.refresh(); - - expect(requestData.balance?.balance).toBe('990'); - expect(requestData.balance?.events[0].name).toBe('refund'); - expect(requestData.balance?.events[0].amount).toBe('10'); - expect(requestData.balance?.events[0].parameters).toMatchObject({ note: 'received refund' }); - - expect(requestData.balance?.events[1].name).toBe('payment'); - expect(requestData.balance?.events[1].amount).toBe('1000'); - expect(requestData.balance?.events[1].parameters).toMatchObject({ note: 'received payment' }); - }); - }); - - it('can declare payments and refunds on an ANY_TO_ERC20_PROXY request', async () => { - const requestNetwork = new RequestNetwork({ - signatureProvider: TestData.fakeSignatureProvider, - useMockStorage: true, - currencies: [ - ...CurrencyManager.getDefaultList(), - { - type: RequestLogicTypes.CURRENCY.ERC20, - address: '0x38cf23c52bb4b13f051aec09580a2de845a7fa35', - decimals: 18, - network: 'private', - symbol: 'FAKE', - }, - ], - }); - - // provider data is irrelevant in this test - jest.spyOn(ethers.providers.BaseProvider.prototype, 'getLogs').mockResolvedValue([]); - - const salt = 'ea3bc7caf64110ca'; - - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { - id: PaymentTypes.PAYMENT_NETWORK_ID.ANY_TO_ERC20_PROXY, - parameters: { - paymentAddress: '0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB', - refundAddress: '0x821aEa9a577a9b44299B9c15c88cf3087F3b5544', - salt, - feeAddress: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2', - feeAmount: '200', - network: 'private', - acceptedTokens: ['0x38cf23c52bb4b13f051aec09580a2de845a7fa35'], - maxRateTimespan: 1000000, - }, - }; + mock.resetHistory(); - const requestInfo = { - expectedAmount: '100', // not used - payee: TestData.payee.identity, - payer: TestData.payer.identity, - currency: 'USD', - }; + await waitForConfirmation(request.reduceExpectedAmountRequest(3, TestData.payee.identity)); - const request = await requestNetwork.createRequest({ - paymentNetwork, - requestInfo, - signer: TestData.payee.identity, + expect(mock.history.get).toHaveLength(5); + expect(mock.history.post).toHaveLength(1); }); - await request.waitForConfirmation(); - - await waitForConfirmation( - request.declareSentPayment('10', 'sent payment', TestData.payer.identity), - ); - - await waitForConfirmation( - request.declareSentRefund('2', 'sent refund', TestData.payee.identity), - ); - - await request.refresh(); - expect(request.getData().balance?.balance).toBe('0'); - - await waitForConfirmation( - request.declareReceivedPayment('10', 'received payment', TestData.payee.identity), - ); - - await waitForConfirmation( - request.declareReceivedRefund('2', 'received refund', TestData.payer.identity), - ); - - await request.refresh(); - expect(request.getData().balance?.error).toBeUndefined(); - expect(request.getData().balance?.balance).toBe('8'); - expect(request.getData().balance?.events?.length).toBe(2); }); - describe('tests with encryption', () => { + describe('Request Logic with encryption', () => { afterEach(() => { jest.clearAllMocks(); }); @@ -1563,9 +1099,6 @@ describe('index', () => { signatureProvider: TestData.fakeSignatureProvider, useMockStorage: true, }); - - const salt = 'ea3bc7caf64110ca'; - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { id: PaymentTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA, parameters: { @@ -2013,8 +1546,6 @@ describe('index', () => { signatureProvider: TestData.fakeSignatureProvider, useMockStorage: true, }); - const salt = 'ea3bc7caf64110ca'; - const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { id: PaymentTypes.PAYMENT_NETWORK_ID.ERC20_PROXY_CONTRACT, parameters: { @@ -2084,6 +1615,108 @@ describe('index', () => { }); }); + describe('Conversion requests: payment chain should be deduced from the payment network parameters', () => { + it('creates any-to-erc20 requests', async () => { + const requestNetwork = new RequestNetwork({ + signatureProvider: TestData.fakeSignatureProvider, + useMockStorage: true, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.ANY_TO_ERC20_PROXY, + parameters: { + network: 'goerli', + paymentAddress: '0x6330A553Fc93768F612722BB8c2eC78aC90B3bbc', + acceptedTokens: ['0xBA62BCfcAaFc6622853cca2BE6Ac7d845BC0f2Dc'], + salt, + }, + }; + + const requestInfo = Object.assign({}, TestData.parametersUSDWithoutExtensionsData); + + const request = await requestNetwork.createRequest({ + requestInfo, + paymentNetwork, + signer: TestData.payee.identity, + disablePaymentDetection: true, + }); + const data = await request.waitForConfirmation(); + + expect(data).toBeDefined(); + expect(data.balance?.error).toBeUndefined(); + expect(data.balance?.balance).toBeUndefined(); + expect(data.meta).toBeDefined(); + expect(data.currency).toBe('USD'); + expect(data.extensionsData.length).toBe(1); + expect(data.extensionsData[0].parameters.network).toBe('goerli'); + expect(data.extensionsData[0].id).toBe('pn-any-to-erc20-proxy'); + expect(data.expectedAmount).toBe(TestData.parametersUSDWithoutExtensionsData.expectedAmount); + }); + it('can create any-to-native requests', async () => { + const requestNetwork = new RequestNetwork({ + signatureProvider: TestData.fakeSignatureProvider, + useMockStorage: true, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.ANY_TO_NATIVE, + parameters: { + network: 'aurora', + paymentAddress: 'paymentaddress.near', + salt, + }, + }; + + const requestInfo = Object.assign({}, TestData.parametersUSDWithoutExtensionsData); + + const request = await requestNetwork.createRequest({ + requestInfo, + paymentNetwork, + signer: TestData.payee.identity, + disablePaymentDetection: true, + }); + const data = await request.waitForConfirmation(); + + expect(data).toBeDefined(); + expect(data.balance?.error).toBeUndefined(); + expect(data.balance?.balance).toBeUndefined(); + expect(data.meta).toBeDefined(); + expect(data.currency).toBe('USD'); + expect(data.extensionsData.length).toBe(1); + expect(data.extensionsData[0].parameters.network).toBe('aurora'); + expect(data.extensionsData[0].id).toBe('pn-any-to-native-token'); + expect(data.expectedAmount).toBe(TestData.parametersUSDWithoutExtensionsData.expectedAmount); + }); + it('cannot create conversion requests on networks not supported', async () => { + const requestNetwork = new RequestNetwork({ + signatureProvider: TestData.fakeSignatureProvider, + useMockStorage: true, + }); + + const paymentNetwork: PaymentTypes.IPaymentNetworkCreateParameters = { + id: PaymentTypes.PAYMENT_NETWORK_ID.ANY_TO_NATIVE, + parameters: { + network: 'mainnet', // This network is not supported for any-to-native + paymentAddress: 'paymentaddress.near', + salt, + }, + }; + + const requestInfo = Object.assign({}, TestData.parametersUSDWithoutExtensionsData); + + await expect( + requestNetwork.createRequest({ + requestInfo, + paymentNetwork, + signer: TestData.payee.identity, + disablePaymentDetection: true, + }), + ).rejects.toThrowError( + 'the pn-any-to-native-token extension is not supported for the network mainnet', + ); + }); + }); + describe('Token lists', () => { const testErc20Data = { ...TestData.parametersWithoutExtensionsData,