Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 22 additions & 13 deletions packages/integration-test/test/erc20.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// tslint:disable: no-magic-numbers
// tslint:disable: no-invalid-this
import { Types } from '@requestnetwork/request-client.js';
import ERC20AddressedBased from '@requestnetwork/request-client.js/src/api/payment-network/erc20/address-based';
import ERC20InfoRetriever from '@requestnetwork/request-client.js/src/api/payment-network/erc20/info-retriever';
import { AdvancedLogicTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types';
Expand Down Expand Up @@ -31,19 +32,26 @@ describe('ERC20 detection test-suite', function(): void {
this.timeout(10000);

describe('check mainnet payment detection', () => {
Object.entries(tokens).forEach(([symbol, { address, amount, decimals }]) => {
Object.entries(tokens).forEach(([symbol, { address, amount }]) => {
it(`can detect the balance of ${symbol}`, async () => {
const balanceObject = await ERC20InfoRetriever(address, account, 'mainnet');
const infoRetriever = new ERC20InfoRetriever(
address,
account,
Types.EVENTS_NAMES.PAYMENT,
'mainnet',
);
const events = await infoRetriever.getTransferEvents();

expect(balanceObject.decimals).to.be.equal(decimals);
// if this assert fails it means this address received another transaction
expect(balanceObject.tokenEvents).to.have.lengthOf(1);
const event = balanceObject.tokenEvents[0];
delete event.from;
expect(event).to.deep.equal({
to: account,
value: amount,
});
expect(events).to.have.lengthOf(1);
const event = events[0];
expect(event.name).to.equal('payment');
expect(event.amount).to.equal(amount);
expect(event.timestamp).to.be.a('number');
expect(event.parameters!.to).to.equal(account);
expect(event.parameters!.from).to.be.a('string');
expect(event.parameters!.block).to.be.a('number');
expect(event.parameters!.txHash).to.be.a('string');
});
});
});
Expand Down Expand Up @@ -82,12 +90,13 @@ describe('ERC20 detection test-suite', function(): void {
expect(balance.balance).to.be.equal('510000000000000000');
expect(balance.events).to.have.lengthOf(1);
expect(balance.events[0].name).to.be.equal('payment');
expect(balance.events[0].parameters.to).to.be.equal(
expect(balance.events[0].parameters!.to).to.be.equal(
'0x6A08D2C8f251AF1f17B5943f7f7Bb7078c50e29A',
);
expect(balance.events[0].parameters.from).to.be.equal(
expect(balance.events[0].parameters!.from).to.be.equal(
'0x708416775B69E3D3d6c634FfdF91778A161d30Bd',
);
expect(balance.events[0].parameters.value).to.be.equal('510000000000000000');
expect(balance.events[0].amount).to.be.equal('510000000000000000');
expect(balance.events[0].timestamp).to.be.a('number');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,14 @@ export default class PaymentNetworkBTCAddressBased {
request: RequestLogicTypes.IRequest,
paymentNetworkId: ExtensionTypes.ID,
networkId: number,
): Promise<Types.IBalanceWithEvents> {
): Promise<Types.BTCBalanceWithEvents> {
if (!request.extensions[paymentNetworkId]) {
throw new Error(`The request do not have the extension : ̀${paymentNetworkId}`);
}
const paymentAddress = request.extensions[paymentNetworkId].values.paymentAddress;
const refundAddress = request.extensions[paymentNetworkId].values.refundAddress;

let payments: Types.IBalanceWithEvents = { balance: '0', events: [] };
let payments: Types.BTCBalanceWithEvents = { balance: '0', events: [] };
if (paymentAddress) {
payments = await this.extractBalanceAndEvents(
paymentAddress,
Expand All @@ -93,7 +93,7 @@ export default class PaymentNetworkBTCAddressBased {
);
}

let refunds: Types.IBalanceWithEvents = { balance: '0', events: [] };
let refunds: Types.BTCBalanceWithEvents = { balance: '0', events: [] };
if (refundAddress) {
refunds = await this.extractBalanceAndEvents(
refundAddress,
Expand All @@ -106,9 +106,8 @@ export default class PaymentNetworkBTCAddressBased {
.sub(new bigNumber(refunds.balance || 0))
.toString();

const events: Types.IPaymentNetworkEvent[] = [...payments.events, ...refunds.events].sort(
(a: Types.IPaymentNetworkEvent, b: Types.IPaymentNetworkEvent) =>
a.parameters.timestamp - b.parameters.timestamp,
const events: Types.BTCPaymentNetworkEvent[] = [...payments.events, ...refunds.events].sort(
(a, b) => (a.timestamp || 0) - (b.timestamp || 0),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

less typing but more clarity... @romaric-juniet's brain may explode! 😄

);

return {
Expand All @@ -129,7 +128,7 @@ export default class PaymentNetworkBTCAddressBased {
address: string,
eventName: Types.EVENTS_NAMES,
networkId: number,
): Promise<Types.IBalanceWithEvents> {
): Promise<Types.BTCBalanceWithEvents> {
return this.bitcoinDetectionProvider.getAddressBalanceWithEvents(networkId, address, eventName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default class DefaultBitcoinDetectionProvider implements Types.IBitcoinDe
bitcoinNetworkId: number,
address: string,
eventName: Types.EVENTS_NAMES,
): Promise<Types.IBalanceWithEvents> {
): Promise<Types.BTCBalanceWithEvents> {
if (this.providers.length < 2) {
throw new Error('At least two bitcoin providers are needed');
}
Expand Down Expand Up @@ -84,19 +84,19 @@ export default class DefaultBitcoinDetectionProvider implements Types.IBitcoinDe
* @returns Object containing IBalanceWithEvents and the count
*/
private getMostCommonBalance(
array: Types.IBalanceWithEvents[],
): { count: number; value: Types.IBalanceWithEvents } | undefined {
array: Types.BTCBalanceWithEvents[],
): { count: number; value: Types.BTCBalanceWithEvents } | undefined {
// Reduce the array to an object indexed by balance with the count
const duplicatesWithCount: {
[key: string]: { count: number; value: Types.IBalanceWithEvents };
[key: string]: { count: number; value: Types.BTCBalanceWithEvents };
} = array
.filter(info => info.balance !== '-1')
.reduce(
(
accumulator: {
[key: string]: { count: number; value: Types.IBalanceWithEvents };
[key: string]: { count: number; value: Types.BTCBalanceWithEvents };
},
elem: Types.IBalanceWithEvents,
elem: Types.BTCBalanceWithEvents,
) => {
if (!accumulator[elem.balance]) {
accumulator[elem.balance] = { count: 0, value: elem };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default class BlockchainInfo implements Types.IBitcoinDetectionProvider {
bitcoinNetworkId: number,
address: string,
eventName: Types.EVENTS_NAMES,
): Promise<Types.IBalanceWithEvents> {
): Promise<Types.BTCBalanceWithEvents> {
const blockchainInfoUrl = this.getBlockchainInfoUrl(bitcoinNetworkId);

const queryUrl = `${blockchainInfoUrl}/rawaddr/${address}?cors=true`;
Expand Down Expand Up @@ -87,11 +87,11 @@ export default class BlockchainInfo implements Types.IBitcoinDetectionProvider {
* @param eventName Indicates if it is an address for payment or refund
* @returns Balance with events
*/
public parse(addressInfo: any, eventName: Types.EVENTS_NAMES): Types.IBalanceWithEvents {
public parse(addressInfo: any, eventName: Types.EVENTS_NAMES): Types.BTCBalanceWithEvents {
const address = addressInfo.address;
const balance = new bigNumber(addressInfo.total_received).toString();

const events: Types.IPaymentNetworkEvent[] = addressInfo.txs
const events: Types.BTCPaymentNetworkEvent[] = addressInfo.txs
// exclude the transactions coming from the same address
.filter((tx: any) => {
const selfInputs = tx.inputs.filter(
Expand All @@ -112,14 +112,14 @@ export default class BlockchainInfo implements Types.IBitcoinDetectionProvider {
}, [])
.filter((output: any) => output.output.addr === address)
.map(
(output: any): Types.IPaymentNetworkEvent => ({
(output: any): Types.BTCPaymentNetworkEvent => ({
amount: output.output.value.toString(),
Comment thread
benjlevesque marked this conversation as resolved.
name: eventName,
parameters: {
amount: output.output.value.toString(),
block: output.blockHeight,
timestamp: output.timestamp,
txHash: output.txHash,
},
timestamp: output.timestamp,
}),
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default class BlockcypherCom implements Types.IBitcoinDetectionProvider {
bitcoinNetworkId: number,
address: string,
eventName: Types.EVENTS_NAMES,
): Promise<Types.IBalanceWithEvents> {
): Promise<Types.BTCBalanceWithEvents> {
const baseUrl = this.getBaseUrl(bitcoinNetworkId);
const queryUrl = `${baseUrl}/addrs/${address}`;
try {
Expand Down Expand Up @@ -57,28 +57,28 @@ export default class BlockcypherCom implements Types.IBitcoinDetectionProvider {
* @param eventName Indicates if it is an address for payment or refund
* @returns Balance with events
*/
public parse(addressInfo: any, eventName: Types.EVENTS_NAMES): Types.IBalanceWithEvents {
public parse(addressInfo: any, eventName: Types.EVENTS_NAMES): Types.BTCBalanceWithEvents {
const balance = new bigNumber(addressInfo.total_received).toString();

// Retrieves all the transaction hash of the transactions having as input the current address
const inputTxHashes = addressInfo.txrefs
.filter((tx: any) => tx.tx_output_n === -1)
.map((tx: any) => tx.tx_hash);

const events: Types.IPaymentNetworkEvent[] = addressInfo.txrefs
const events: Types.BTCPaymentNetworkEvent[] = addressInfo.txrefs
// keep only the transaction with this address as output
.filter((tx: any) => tx.tx_input_n === -1)
// exclude the transactions coming from the same address
.filter((tx: any) => !inputTxHashes.includes(tx.tx_hash))
.map(
(tx: any): Types.IPaymentNetworkEvent => ({
(tx: any): Types.BTCPaymentNetworkEvent => ({
amount: tx.value.toString(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo, amount should stay in event.parameters because all the events won't have necessary an amount. (e.g "deny a payment")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may need to be discussed with the team

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

discussed with Vince, we agreed that it's OK to have an amount of 0 for specific events.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to be included in this discussion. I'm not sure this makes a lot of sense to me.

name: eventName,
parameters: {
amount: tx.value.toString(),
block: tx.block_height,
// timestamp - not given by this API
txHash: tx.tx_hash,
},
// timestamp - not given by this API
}),
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default class BlockstreamInfo implements Types.IBitcoinDetectionProvider
bitcoinNetworkId: number,
address: string,
eventName: Types.EVENTS_NAMES,
): Promise<Types.IBalanceWithEvents> {
): Promise<Types.BTCBalanceWithEvents> {
const baseUrl = this.getBaseUrl(bitcoinNetworkId);
const queryUrl = `${baseUrl}/address/${address}/txs`;
try {
Expand Down Expand Up @@ -87,8 +87,8 @@ export default class BlockstreamInfo implements Types.IBitcoinDetectionProvider
* @param eventName Indicates if it is an address for payment or refund
* @returns Balance with events
*/
public parse(addressInfo: any, eventName: Types.EVENTS_NAMES): Types.IBalanceWithEvents {
const events: Types.IPaymentNetworkEvent[] = addressInfo.txs
public parse(addressInfo: any, eventName: Types.EVENTS_NAMES): Types.BTCBalanceWithEvents {
const events: Types.BTCPaymentNetworkEvent[] = addressInfo.txs
// exclude the transactions coming from the same address
.filter((tx: any) => {
const autoVin = tx.vin.filter(
Expand All @@ -109,20 +109,20 @@ export default class BlockstreamInfo implements Types.IBitcoinDetectionProvider
}, [])
.filter((output: any) => output.output.scriptpubkey_address === addressInfo.address)
.map(
(output: any): Types.IPaymentNetworkEvent => ({
(output: any): Types.BTCPaymentNetworkEvent => ({
amount: output.output.value.toString(),
name: eventName,
parameters: {
amount: output.output.value.toString(),
block: output.blockHeight,
timestamp: output.timestamp,
txHash: output.txHash,
},
timestamp: output.timestamp,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

timestamp outside parameters make more sense! 👍

}),
);

const balance: string = events
.reduce((balanceAccumulator: any, event: Types.IPaymentNetworkEvent) => {
return balanceAccumulator.add(new bigNumber(event.parameters.amount));
.reduce((balanceAccumulator: any, event: Types.BTCPaymentNetworkEvent) => {
return balanceAccumulator.add(new bigNumber(event.amount));
}, new bigNumber('0'))
.toString();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default class ChainSo implements Types.IBitcoinDetectionProvider {
bitcoinNetworkId: number,
address: string,
eventName: Types.EVENTS_NAMES,
): Promise<Types.IBalanceWithEvents> {
): Promise<Types.BTCBalanceWithEvents> {
const baseUrl = this.getBaseUrl(bitcoinNetworkId);
const queryUrl = `${baseUrl}/${address}`;

Expand Down Expand Up @@ -63,28 +63,28 @@ export default class ChainSo implements Types.IBitcoinDetectionProvider {
* @param eventName Indicates if it is an address for payment or refund
* @returns Balance with events
*/
public parse(addressInfo: any, eventName: Types.EVENTS_NAMES): Types.IBalanceWithEvents {
const events: Types.IPaymentNetworkEvent[] = addressInfo.data.txs
public parse(addressInfo: any, eventName: Types.EVENTS_NAMES): Types.BTCBalanceWithEvents {
const events: Types.BTCPaymentNetworkEvent[] = addressInfo.data.txs
// keep only the transaction with value incoming to the address
.filter((tx: any) => tx.incoming !== undefined)
// delete transactions that are from this address
.filter((tx: any) => tx.outgoing === undefined)
.map(
(tx: any): Types.IPaymentNetworkEvent => ({
(tx: any): Types.BTCPaymentNetworkEvent => ({
amount: converterBTC.toSatoshi(tx.incoming.value).toString(),
name: eventName,
parameters: {
amount: converterBTC.toSatoshi(tx.incoming.value).toString(),
block: tx.block_no,
timestamp: tx.time,
txHash: tx.txid,
},
timestamp: tx.time,
}),
);

// Compute the balance making the sum of all the transactions amount
const balance: string = events
.reduce((balanceAccumulator: any, event: Types.IPaymentNetworkEvent) => {
return balanceAccumulator.add(new bigNumber(event.parameters.amount));
.reduce((balanceAccumulator: any, event: Types.BTCPaymentNetworkEvent) => {
return balanceAccumulator.add(new bigNumber(event.amount));
}, new bigNumber('0'))
.toString();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const MAINNET_BITCOIN_NETWORK_ID = 0;
*
* @class PaymentNetworkBTCAddressBased
*/
export default class PaymentNetworkBTCAddressBased implements Types.IPaymentNetwork {
export default class PaymentNetworkBTCAddressBased
implements Types.IPaymentNetwork<Types.IBTCPaymentEventParameters> {
private btcAddressBased: BTCAddressBased;

/**
Expand Down Expand Up @@ -76,7 +77,9 @@ export default class PaymentNetworkBTCAddressBased implements Types.IPaymentNetw
* @param the request to check
* @returns the balance and the payment/refund events
*/
public async getBalance(request: RequestLogicTypes.IRequest): Promise<Types.IBalanceWithEvents> {
public async getBalance(
request: RequestLogicTypes.IRequest,
): Promise<Types.BTCBalanceWithEvents> {
return this.btcAddressBased.getBalance(
request,
PAYMENT_NETWORK_BITCOIN_ADDRESS_BASED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const TESTNET_BITCOIN_NETWORK_ID = 3;
*
* @class PaymentNetworkBTCAddressBased
*/
export default class PaymentNetworkBTCAddressBased implements Types.IPaymentNetwork {
export default class PaymentNetworkBTCAddressBased
implements Types.IPaymentNetwork<Types.IBTCPaymentEventParameters> {
private btcAddressBased: BTCAddressBased;

/**
Expand Down Expand Up @@ -76,7 +77,9 @@ export default class PaymentNetworkBTCAddressBased implements Types.IPaymentNetw
* @param the request to check
* @returns the balance and the payment/refund events
*/
public async getBalance(request: RequestLogicTypes.IRequest): Promise<Types.IBalanceWithEvents> {
public async getBalance(
request: RequestLogicTypes.IRequest,
): Promise<Types.BTCBalanceWithEvents> {
return this.btcAddressBased.getBalance(
request,
PAYMENT_NETWORK_TESTNET_BITCOIN_ADDRESS_BASED,
Expand Down
Loading