Skip to content

Commit

Permalink
Merge branch 'master' into any-to-eth-wip
Browse files Browse the repository at this point in the history
  • Loading branch information
vrolland committed Sep 25, 2021
2 parents 24e9851 + eb08b85 commit 267a143
Show file tree
Hide file tree
Showing 22 changed files with 535 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types';
import FeeReferenceBasedPaymentNetwork from '../fee-reference-based';
import * as walletAddressValidator from 'wallet-address-validator';

const CURRENT_VERSION = '0.2.0';
const CURRENT_VERSION = '0.1.0';

/**
* Implementation of the payment network to pay in Ethereum, including third-party fees payment, based on a reference provided to a proxy contract.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('extensions/payment-network/ethereum/fee-proxy-contract', () => {
refundAddress: '0x0000000000000000000000000000000000000003',
salt: 'ea3bc7caf64110ca',
},
version: '0.2.0',
version: '0.1.0',
});
});

Expand All @@ -52,7 +52,7 @@ describe('extensions/payment-network/ethereum/fee-proxy-contract', () => {
refundAddress: '0x0000000000000000000000000000000000000002',
salt: 'ea3bc7caf64110ca',
},
version: '0.2.0',
version: '0.1.0',
});
});

Expand All @@ -68,7 +68,7 @@ describe('extensions/payment-network/ethereum/fee-proxy-contract', () => {
parameters: {
salt: 'ea3bc7caf64110ca',
},
version: '0.2.0',
version: '0.1.0',
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const extensionStateWithPaymentAfterCreation = {
values: {
paymentAddress,
},
version: '0.2.0',
version: '0.1.0',
},
};

Expand All @@ -87,7 +87,7 @@ export const extensionStateWithRefundAfterCreation = {
values: {
refundAddress,
},
version: '0.2.0',
version: '0.1.0',
},
};

Expand All @@ -114,7 +114,7 @@ export const extensionStateWithFeeAfterCreation = {
feeAddress,
feeAmount,
},
version: '0.2.0',
version: '0.1.0',
},
};

Expand Down Expand Up @@ -159,7 +159,7 @@ export const requestStateCreatedEmptyThenAddPayment: RequestLogicTypes.IRequest
requestId: TestData.requestIdMock,
state: RequestLogicTypes.STATE.CREATED,
timestamp: TestData.arbitraryTimestamp,
version: '0.2.0',
version: '0.1.0',
};

export const requestStateCreatedEmptyThenAddFee: RequestLogicTypes.IRequest = {
Expand Down Expand Up @@ -201,5 +201,5 @@ export const requestStateCreatedEmptyThenAddFee: RequestLogicTypes.IRequest = {
requestId: TestData.requestIdMock,
state: RequestLogicTypes.STATE.CREATED,
timestamp: TestData.arbitraryTimestamp,
version: '0.2.0',
version: '0.1.0',
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,23 @@ export const actionCreationFull = {
refundAddress,
salt,
},
version: '0.2.0',
version: '0.1.0',
};
export const actionCreationOnlyPayment = {
action: 'create',
id: ExtensionTypes.ID.PAYMENT_NETWORK_ETH_FEE_PROXY_CONTRACT,
parameters: {
paymentAddress,
},
version: '0.2.0',
version: '0.1.0',
};
export const actionCreationOnlyRefund = {
action: 'create',
id: ExtensionTypes.ID.PAYMENT_NETWORK_ETH_FEE_PROXY_CONTRACT,
parameters: {
refundAddress,
},
version: '0.2.0',
version: '0.1.0',
};
export const actionCreationOnlyFee = {
action: 'create',
Expand All @@ -49,13 +49,13 @@ export const actionCreationOnlyFee = {
feeAddress,
feeAmount,
},
version: '0.2.0',
version: '0.1.0',
};
export const actionCreationEmpty = {
action: 'create',
id: ExtensionTypes.ID.PAYMENT_NETWORK_ETH_FEE_PROXY_CONTRACT,
parameters: {},
version: '0.2.0',
version: '0.1.0',
};

// ---------------------------------------------------------------------
Expand Down Expand Up @@ -84,7 +84,7 @@ export const extensionFullState = {
refundAddress,
salt,
},
version: '0.2.0',
version: '0.1.0',
},
};
export const extensionStateCreatedEmpty = {
Expand All @@ -99,7 +99,7 @@ export const extensionStateCreatedEmpty = {
id: ExtensionTypes.ID.PAYMENT_NETWORK_ETH_FEE_PROXY_CONTRACT,
type: ExtensionTypes.TYPE.PAYMENT_NETWORK,
values: {},
version: '0.2.0',
version: '0.1.0',
},
};

Expand Down Expand Up @@ -144,7 +144,7 @@ export const requestStateNoExtensions: RequestLogicTypes.IRequest = {
requestId: TestData.requestIdMock,
state: RequestLogicTypes.STATE.CREATED,
timestamp: TestData.arbitraryTimestamp,
version: '0.2.0',
version: '0.1.0',
};

export const requestFullStateCreated: RequestLogicTypes.IRequest = {
Expand Down Expand Up @@ -186,7 +186,7 @@ export const requestFullStateCreated: RequestLogicTypes.IRequest = {
requestId: TestData.requestIdMock,
state: RequestLogicTypes.STATE.CREATED,
timestamp: TestData.arbitraryTimestamp,
version: '0.2.0',
version: '0.1.0',
};

export const requestStateCreatedEmpty: RequestLogicTypes.IRequest = {
Expand Down Expand Up @@ -228,5 +228,5 @@ export const requestStateCreatedEmpty: RequestLogicTypes.IRequest = {
requestId: TestData.requestIdMock,
state: RequestLogicTypes.STATE.CREATED,
timestamp: TestData.arbitraryTimestamp,
version: '0.2.0',
version: '0.1.0',
};
59 changes: 59 additions & 0 deletions packages/integration-test/test/node-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -624,3 +624,62 @@ describe('ERC20 localhost request creation and detection test', () => {
expect(event?.parameters?.maxRateTimespan).toBe('1000000');
});
});

describe('ETH localhost request creation and detection test', () => {
const ethRequestCreationHash: Types.IRequestInfo = {
currency: {
network: 'private',
type: Types.RequestLogic.CURRENCY.ETH,
value: Types.RequestLogic.CURRENCY.ETH,
},
expectedAmount: '1000',
payee: payeeIdentity,
payer: payerIdentity,
};

it('can create ETH requests and pay with ETH Fee proxy', async () => {
const currencies = [
...CurrencyManager.getDefaultList()
];
const requestNetwork = new RequestNetwork({
signatureProvider,
useMockStorage: true,
currencies,
});

const paymentNetworkETHFeeProxy: PaymentTypes.IPaymentNetworkCreateParameters = {
id: PaymentTypes.PAYMENT_NETWORK_ID.ETH_FEE_PROXY_CONTRACT,
parameters: {
paymentAddress: '0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB',
feeAddress: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2',
feeAmount: '200',
network: 'private',
maxRateTimespan: 1000000,
},
};

const request = await requestNetwork.createRequest({
paymentNetwork: paymentNetworkETHFeeProxy,
requestInfo: ethRequestCreationHash,
signer: payeeIdentity,
});

let data = await request.refresh();
expect(data.balance).toBeNull();

const paymentTx = await payRequest(data, wallet);
await paymentTx.wait();

data = await request.refresh();
expect(data.balance?.balance).toBe('1000');
expect(data.balance?.events.length).toBe(1);
const event = data.balance?.events[0];
expect(event?.amount).toBe('1000');
expect(event?.name).toBe('payment');

expect(event?.parameters?.feeAmount).toBe('200');
expect(event?.parameters?.feeAddress).toBe('0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2');
// amount in crypto after apply the rates of the fake aggregators
expect(event?.parameters?.to).toBe('0xc12F17Da12cd01a9CDBB216949BA0b41A6Ffc4EB');
});
});
81 changes: 81 additions & 0 deletions packages/payment-detection/src/eth/fee-proxy-detector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import * as SmartContracts from '@requestnetwork/smart-contracts';
import { AdvancedLogicTypes, ExtensionTypes, PaymentTypes } from '@requestnetwork/types';

import ProxyEthereumInfoRetriever from './proxy-info-retriever';
import FeeReferenceBasedDetector from '../fee-reference-based-detector';

// interface of the object indexing the proxy contract version
interface IProxyContractVersion {
[version: string]: string;
}

const PROXY_CONTRACT_ADDRESS_MAP: IProxyContractVersion = {
['0.1.0']: '0.1.0',
};

/**
* Handle payment networks with ETH input data extension
*/
export default class ETHFeeProxyDetector extends FeeReferenceBasedDetector<PaymentTypes.IETHPaymentEventParameters> {
/**
* @param extension The advanced logic payment network extensions
*/
public constructor({ advancedLogic }: { advancedLogic: AdvancedLogicTypes.IAdvancedLogic }) {
super(
advancedLogic.extensions.feeProxyContractEth,
ExtensionTypes.ID.PAYMENT_NETWORK_ETH_FEE_PROXY_CONTRACT,
);
}

/**
* Extracts the balance and events of an address
*
* @private
* @param address Address to check
* @param eventName Indicate if it is an address for payment or refund
* @param network The id of network we want to check
* @param paymentReference The reference to identify the payment
* @param paymentNetworkVersion the version of the payment network
* @returns The balance
*/
protected async extractEvents(
address: string,
eventName: PaymentTypes.EVENTS_NAMES,
network: string,
paymentReference: string,
paymentNetworkVersion: string,
): Promise<PaymentTypes.ETHPaymentNetworkEvent[]> {
const proxyContractArtifact = await this.safeGetProxyArtifact(network, paymentNetworkVersion);

if (!proxyContractArtifact) {
throw Error('ETH fee proxy contract not found');
}

const proxyInfoRetriever = new ProxyEthereumInfoRetriever(
paymentReference,
proxyContractArtifact.address,
proxyContractArtifact.creationBlockNumber,
address,
eventName,
network,
);

return await proxyInfoRetriever.getTransferEvents();
}

/*
* Fetches events from the Ethereum Proxy, or returns null
*/
private async safeGetProxyArtifact(network: string, paymentNetworkVersion: string) {
const contractVersion = PROXY_CONTRACT_ADDRESS_MAP[paymentNetworkVersion];
try {
return SmartContracts.ethereumFeeProxyArtifact.getDeploymentInformation(
network,
contractVersion,
);
} catch (error) {
console.warn(error);
}
return null;
}
}
3 changes: 2 additions & 1 deletion packages/payment-detection/src/eth/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import InputData from './input-data';
import FeeProxyDetector from './fee-proxy-detector';

export { InputData };
export { InputData, FeeProxyDetector };
31 changes: 29 additions & 2 deletions packages/payment-detection/src/eth/proxy-info-retriever.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { parseLogArgs } from '../utils';
// The Ethereum proxy smart contract ABI fragment containing TransferWithReference event
const ethProxyContractAbiFragment = [
'event TransferWithReference(address to,uint256 amount,bytes indexed paymentReference)',
'event TransferWithReferenceAndFee(address to,uint256 amount,bytes indexed paymentReference,uint256 feeAmount,address feeAddress)',
];

/** TransferWithReference event */
Expand All @@ -15,6 +16,12 @@ type TransferWithReferenceArgs = {
paymentReference: string;
};

/** TransferWithReferenceAndFee event */
type TransferWithReferenceAndFeeArgs = TransferWithReferenceArgs & {
feeAmount: BigNumber;
feeAddress: string;
};

/**
* Retrieves a list of payment events from a payment reference, a destination address, a token address and a proxy contract
*/
Expand Down Expand Up @@ -64,15 +71,32 @@ export default class ProxyEthereumInfoRetriever
filter.toBlock = 'latest';

// Get the event logs
const logs = await this.provider.getLogs(filter);
const proxyLogs = await this.provider.getLogs(filter);

// Create a filter to find all the Fee Transfer logs with the payment reference
const feeFilter = this.contractProxy.filters.TransferWithReferenceAndFee(
null,
null,
'0x' + this.paymentReference,
null,
null,
) as ethers.providers.Filter;
feeFilter.fromBlock = this.proxyCreationBlockNumber;
feeFilter.toBlock = 'latest';

// Get the fee proxy contract event logs
const feeProxyLogs = await this.provider.getLogs(feeFilter);

// Merge both events
const logs = [...proxyLogs, ...feeProxyLogs];

// Parses, filters and creates the events from the logs of the proxy contract
const eventPromises = logs
// Parses the logs
.map((log) => {
const parsedLog = this.contractProxy.interface.parseLog(log);
return {
parsedLog: parseLogArgs<TransferWithReferenceArgs>(parsedLog),
parsedLog: parseLogArgs<TransferWithReferenceAndFeeArgs>(parsedLog),
blockNumber: log.blockNumber,
transactionHash: log.transactionHash,
};
Expand All @@ -86,6 +110,9 @@ export default class ProxyEthereumInfoRetriever
parameters: {
block: blockNumber,
txHash: transactionHash,
to: this.toAddress,
feeAddress: parsedLog.feeAddress,
feeAmount: parsedLog.feeAmount?.toString() || undefined,
},
timestamp: (await this.provider.getBlock(blockNumber || 0)).timestamp,
}));
Expand Down

0 comments on commit 267a143

Please sign in to comment.