Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: payment-processor transaction overrides #198

Merged
merged 3 commits into from
Apr 15, 2020
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
7 changes: 7 additions & 0 deletions packages/payment-processor/src/payment/erc20-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ClientTypes, PaymentTypes } from '@requestnetwork/types';

import { ERC20Contract } from '../contracts/Erc20Contract';
import { Erc20ProxyContract } from '../contracts/Erc20ProxyContract';
import { ITransactionOverrides } from './transaction-overrides';
import {
getAmountToPay,
getNetworkProvider,
Expand All @@ -21,11 +22,13 @@ import {
* @param request
* @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum.
* @param amount optionally, the amount to pay. Defaults to remaining amount of the request.
* @param overrides optionally, override default transaction values, like gas.
*/
export async function payErc20ProxyRequest(
request: ClientTypes.IRequestData,
signerOrProvider: Web3Provider | Signer = getProvider(),
amount?: BigNumberish,
overrides?: ITransactionOverrides,
): Promise<ContractTransaction> {
const encodedTx = encodePayErc20Request(request, signerOrProvider, amount);
const proxyAddress = erc20ProxyArtifact.getAddress(request.currencyInfo.network!);
Expand All @@ -34,6 +37,7 @@ export async function payErc20ProxyRequest(
data: encodedTx,
to: proxyAddress,
value: 0,
...overrides,
});
return tx;
}
Expand Down Expand Up @@ -92,10 +96,12 @@ export async function hasErc20Approval(
* @param request request to pay
* @param account account that will be used to pay the request
* @param provider the web3 provider. Defaults to Etherscan.
* @param overrides optionally, override default transaction values, like gas.
*/
export async function approveErc20(
request: ClientTypes.IRequestData,
signerOrProvider: Web3Provider | Signer = getProvider(),
overrides?: ITransactionOverrides,
): Promise<ContractTransaction> {
const encodedTx = encodeApproveErc20(request, signerOrProvider);
const signer = getSigner(signerOrProvider);
Expand All @@ -104,6 +110,7 @@ export async function approveErc20(
data: encodedTx,
to: tokenAddress,
value: 0,
...overrides,
});
return tx;
}
Expand Down
4 changes: 4 additions & 0 deletions packages/payment-processor/src/payment/eth-input-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { BigNumberish } from 'ethers/utils';

import { ClientTypes, PaymentTypes } from '@requestnetwork/types';

import { ITransactionOverrides } from './transaction-overrides';
import {
getAmountToPay,
getProvider,
Expand All @@ -17,11 +18,13 @@ import {
* @param request the request to pay
* @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum.
* @param amount optionally, the amount to pay. Defaults to remaining amount of the request.
* @param overrides optionally, override default transaction values, like gas.
*/
export async function payEthInputDataRequest(
request: ClientTypes.IRequestData,
signerOrProvider: Web3Provider | Signer = getProvider(),
amount?: BigNumberish,
overrides?: ITransactionOverrides,
): Promise<ContractTransaction> {
validateRequest(request, PaymentTypes.PAYMENT_NETWORK_ID.ETH_INPUT_DATA);
const signer = getSigner(signerOrProvider);
Expand All @@ -33,6 +36,7 @@ export async function payEthInputDataRequest(
data: `0x${paymentReference}`,
to: paymentAddress,
value: amountToPay,
...overrides,
});
return tx;
}
Expand Down
7 changes: 5 additions & 2 deletions packages/payment-processor/src/payment/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ClientTypes, ExtensionTypes } from '@requestnetwork/types';
import { getBtcPaymentUrl } from './btc-address-based';
import { _getErc20PaymentUrl, getErc20Balance, payErc20ProxyRequest } from './erc20-proxy';
import { _getEthPaymentUrl, payEthInputDataRequest } from './eth-input-data';
import { ITransactionOverrides } from './transaction-overrides';
import { getNetworkProvider, getProvider, getSigner } from './utils';

const getPaymentNetwork = (request: ClientTypes.IRequestData): ExtensionTypes.ID | undefined => {
Expand All @@ -31,19 +32,21 @@ export class UnsupportedNetworkError extends Error {
* @param request the request to pay.
* @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum.
* @param amount optionally, the amount to pay. Defaults to remaining amount of the request.
* @param overrides optionally, override default transaction values, like gas.
*/
export async function payRequest(
request: ClientTypes.IRequestData,
signerOrProvider: Web3Provider | Signer = getProvider(),
amount?: BigNumberish,
overrides?: ITransactionOverrides,
): Promise<ContractTransaction> {
const signer = getSigner(signerOrProvider);
const paymentNetwork = getPaymentNetwork(request);
switch (paymentNetwork) {
case ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT:
return payErc20ProxyRequest(request, signer, amount);
return payErc20ProxyRequest(request, signer, amount, overrides);
case ExtensionTypes.ID.PAYMENT_NETWORK_ETH_INPUT_DATA:
return payEthInputDataRequest(request, signer, amount);
return payEthInputDataRequest(request, signer, amount, overrides);
default:
throw new UnsupportedNetworkError(paymentNetwork);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { TransactionRequest } from 'ethers/providers';

/** Custom values to pass to transaction */
export interface ITransactionOverrides extends Omit<TransactionRequest, 'to' | 'data' | 'value'> {}
30 changes: 30 additions & 0 deletions packages/payment-processor/test/payment/erc20-proxy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { getRequestPaymentValues } from '../../src/payment/utils';
const expect = chai.expect;
chai.use(chaiAsPromised);
chai.use(spies);
const sandbox = chai.spy.sandbox();

const erc20ContractAddress = '0x9FBDa871d559710256a2502A2517b794B482Db40';

Expand Down Expand Up @@ -96,6 +97,20 @@ describe('getErc20Balance', () => {
});

describe('hasErc20Approval & approveErc20', () => {
it('should consider override parameters', async () => {
const spy = sandbox.on(wallet, 'sendTransaction', () => 0);
await approveErc20(validRequest, wallet, {
gasPrice: '20000000000',
});
expect(spy).to.have.been.called.with({
data:
'0x095ea7b30000000000000000000000002c2b9c9a4a25e24b174f26114e8926a9f2128fe4ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
gasPrice: '20000000000',
to: '0x9FBDa871d559710256a2502A2517b794B482Db40',
value: 0,
});
sandbox.restore();
});
it('can check and approve', async () => {
// use another address so it doesn't mess with other tests.
const otherWallet = new Wallet(
Expand Down Expand Up @@ -145,6 +160,21 @@ describe('payErc20ProxyRequest', () => {
);
});

it('should consider override parameters', async () => {
const spy = sandbox.on(wallet, 'sendTransaction', () => 0);
await payErc20ProxyRequest(validRequest, wallet, undefined, {
gasPrice: '20000000000',
});
expect(spy).to.have.been.called.with({
data:
'0x0784bca30000000000000000000000009fbda871d559710256a2502a2517b794b482db40000000000000000000000000f17f52151ebef6c7334fad080c5704d77216b73200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000886dfbccad783599a000000000000000000000000000000000000000000000000',
gasPrice: '20000000000',
to: '0x2c2b9c9a4a25e24b174f26114e8926a9f2128fe4',
value: 0,
});
sandbox.restore();
});

it('should pay an ERC20 request', async () => {
// first approve the contract
const approvalTx = await approveErc20(validRequest, wallet);
Expand Down
16 changes: 16 additions & 0 deletions packages/payment-processor/test/payment/eth-input-data.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ import Utils from '@requestnetwork/utils';

import { _getEthPaymentUrl, payEthInputDataRequest } from '../../src/payment/eth-input-data';
import { getRequestPaymentValues } from '../../src/payment/utils';
import { BigNumber } from 'ethers/utils';

// tslint:disable: no-unused-expression
// tslint:disable: await-promise

const expect = chai.expect;
chai.use(chaiAsPromised);
chai.use(spies);
const sandbox = chai.spy.sandbox();

const mnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat';
const paymentAddress = '0xf17f52151EbEF6C7334FAD080c5704D77216b732';
Expand Down Expand Up @@ -106,6 +108,20 @@ describe('payEthInputDataRequest', () => {
);
});

it('should consider override parameters', async () => {
const spy = sandbox.on(wallet, 'sendTransaction', () => 0);
await payEthInputDataRequest(validRequest, wallet, undefined, {
gasPrice: '20000000001',
});
expect(spy).to.have.been.called.with({
data: '0x86dfbccad783599a',
gasPrice: '20000000001',
to: '0xf17f52151EbEF6C7334FAD080c5704D77216b732',
value: new BigNumber(1),
});
sandbox.restore();
});

it('processes a payment for a pn-eth-input-data request', async () => {
const balanceBefore = await wallet.getBalance();
expect(balanceBefore.gt(0));
Expand Down