Skip to content

Commit 3de8a78

Browse files
authored
feat: enable specific approval amount (#953)
1 parent a264179 commit 3de8a78

File tree

7 files changed

+228
-98
lines changed

7 files changed

+228
-98
lines changed

packages/payment-processor/src/payment/conversion-erc20.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ContractTransaction, Signer, BigNumberish, providers } from 'ethers';
1+
import { ContractTransaction, Signer, BigNumberish, providers, BigNumber } from 'ethers';
22

33
import { AnyToERC20PaymentDetector } from '@requestnetwork/payment-detection';
44
import { ClientTypes } from '@requestnetwork/types';
@@ -107,9 +107,15 @@ export function prepareApproveErc20ForProxyConversion(
107107
paymentTokenAddress: string,
108108
signerOrProvider: providers.Provider | Signer = getProvider(),
109109
overrides?: ITransactionOverrides,
110+
amount?: BigNumber,
110111
): IPreparedTransaction {
111112
const proxyAddress = getProxyAddress(request, AnyToERC20PaymentDetector.getDeploymentInformation);
112-
const encodedTx = encodeApproveAnyErc20(paymentTokenAddress, proxyAddress, signerOrProvider);
113+
const encodedTx = encodeApproveAnyErc20(
114+
paymentTokenAddress,
115+
proxyAddress,
116+
signerOrProvider,
117+
amount,
118+
);
113119
return {
114120
data: encodedTx,
115121
to: paymentTokenAddress,

packages/payment-processor/src/payment/encoder-approval.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ export function encodeRequestErc20ApprovalWithoutSwap(
108108

109109
switch (paymentNetwork) {
110110
case ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_PROXY_CONTRACT:
111-
return prepareApproveErc20(request, provider, overrides);
111+
return prepareApproveErc20(request, provider, overrides, options?.approval?.amount);
112112
case ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_FEE_PROXY_CONTRACT:
113-
return prepareApproveErc20(request, provider, overrides);
113+
return prepareApproveErc20(request, provider, overrides, options?.approval?.amount);
114114
case ExtensionTypes.ID.PAYMENT_NETWORK_ANY_TO_ERC20_PROXY: {
115115
if (
116116
!options ||
@@ -125,6 +125,7 @@ export function encodeRequestErc20ApprovalWithoutSwap(
125125
options.conversion.currency.value,
126126
provider,
127127
overrides,
128+
options?.approval?.amount,
128129
);
129130
}
130131
}
@@ -147,7 +148,13 @@ export function encodeRequestErc20ApprovalWithSwap(
147148
switch (paymentNetwork) {
148149
case ExtensionTypes.ID.PAYMENT_NETWORK_ERC20_FEE_PROXY_CONTRACT:
149150
if (options && options.swap) {
150-
return prepareApprovalErc20ForSwapToPay(request, options.swap.path[0], provider, overrides);
151+
return prepareApprovalErc20ForSwapToPay(
152+
request,
153+
options.swap.path[0],
154+
provider,
155+
overrides,
156+
options.approval?.amount,
157+
);
151158
} else {
152159
throw new Error('No swap options');
153160
}
@@ -167,6 +174,7 @@ export function encodeRequestErc20ApprovalWithSwap(
167174
options.swap.path[0],
168175
provider,
169176
overrides,
177+
options.approval?.amount,
170178
);
171179
} else {
172180
throw new Error('No swap options');

packages/payment-processor/src/payment/settings.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
import { ICurrencyManager } from '@requestnetwork/currency';
22
import { RequestLogicTypes } from '@requestnetwork/types';
3-
import { BigNumberish } from 'ethers';
3+
import { BigNumber, BigNumberish } from 'ethers';
44
import { ITransactionOverrides } from './transaction-overrides';
55

6+
/**
7+
* Approval settings
8+
*/
9+
export interface IApprovalSettings {
10+
/** The specific amount to approve. Defaults to maximum when left empty */
11+
amount: BigNumber;
12+
}
13+
614
/**
715
* Details required for a token swap
816
*/
@@ -42,4 +50,6 @@ export interface IRequestPaymentOptions {
4250
swap?: ISwapSettings;
4351
/** Used, and required, only for on chain conversion */
4452
conversion?: IConversionSettings;
53+
/** Optional, enable to approve only specific amount of token. Defaults to MAX_ALLOWANCE if not set */
54+
approval?: IApprovalSettings;
4555
}

packages/payment-processor/src/payment/swap-conversion-erc20.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ContractTransaction, providers, Signer, BigNumberish } from 'ethers';
1+
import { ContractTransaction, providers, Signer, BigNumberish, BigNumber } from 'ethers';
22

33
import { erc20SwapConversionArtifact } from '@requestnetwork/smart-contracts';
44
import { ClientTypes, PaymentTypes } from '@requestnetwork/types';
@@ -116,6 +116,7 @@ export function prepareApprovalErc20ForSwapWithConversionToPay(
116116
paymentTokenAddress: string,
117117
signerOrProvider: providers.Provider | Signer = getProvider(),
118118
overrides?: ITransactionOverrides,
119+
amount?: BigNumber,
119120
): IPreparedTransaction {
120121
const network =
121122
request.extensions[PaymentTypes.PAYMENT_NETWORK_ID.ANY_TO_ERC20_PROXY].values.network;
@@ -127,6 +128,7 @@ export function prepareApprovalErc20ForSwapWithConversionToPay(
127128
paymentTokenAddress,
128129
erc20SwapConversionArtifact.getAddress(network),
129130
signerOrProvider,
131+
amount,
130132
);
131133
return {
132134
data: encodedTx,

packages/payment-processor/src/payment/swap-erc20.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ContractTransaction, providers, Signer, BigNumberish } from 'ethers';
1+
import { ContractTransaction, providers, Signer, BigNumberish, BigNumber } from 'ethers';
22

33
import { erc20SwapToPayArtifact } from '@requestnetwork/smart-contracts';
44
import { ClientTypes } from '@requestnetwork/types';
@@ -102,11 +102,13 @@ export function prepareApprovalErc20ForSwapToPay(
102102
paymentTokenAddress: string,
103103
signerOrProvider: providers.Provider | Signer = getProvider(),
104104
overrides?: ITransactionOverrides,
105+
amount?: BigNumber,
105106
): IPreparedTransaction {
106107
const encodedTx = encodeApproveAnyErc20(
107108
paymentTokenAddress,
108109
erc20SwapToPayArtifact.getAddress(request.currencyInfo.network!),
109110
signerOrProvider,
111+
amount,
110112
);
111113
return {
112114
data: encodedTx,

packages/payment-processor/test/payment/encoder-approval.test.ts

Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Wallet, providers, BigNumber } from 'ethers';
1+
import { Wallet, providers, BigNumber, utils } from 'ethers';
22
import {
33
ClientTypes,
44
ExtensionTypes,
@@ -44,15 +44,23 @@ const alphaSwapConversionSettings = {
4444
path: [erc20ContractAddress, alphaContractAddress],
4545
};
4646

47+
// Amount to be approved
48+
const arbitraryApprovalValue = BigNumber.from('100000000');
49+
const approvalSettings = {
50+
amount: arbitraryApprovalValue,
51+
};
52+
4753
const mnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat';
4854
const mnemonicPath = `m/44'/60'/0'/0/19`;
4955
const paymentAddress = '0x821aEa9a577a9b44299B9c15c88cf3087F3b5544';
5056
const provider = new providers.JsonRpcProvider('http://localhost:8545');
5157
let wallet = Wallet.fromMnemonic(mnemonic, mnemonicPath).connect(provider);
52-
const erc20ApprovalData = (proxy: string) => {
53-
return `0x095ea7b3000000000000000000000000${proxy
54-
.slice(2)
55-
.toLowerCase()}ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`;
58+
const erc20ApprovalData = (proxy: string, approvedHexValue?: BigNumber) => {
59+
return `0x095ea7b3000000000000000000000000${proxy.slice(2).toLowerCase()}${
60+
approvedHexValue
61+
? utils.hexZeroPad(arbitraryApprovalValue.toHexString(), 32).slice(2)
62+
: 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
63+
}`;
5664
};
5765

5866
let proxyERC20: string;
@@ -271,7 +279,22 @@ describe('Approval encoder handles ERC20 Proxy', () => {
271279
value: 0,
272280
});
273281
});
282+
it('Should return a valid transaction with specific approval value', async () => {
283+
const approvalTransaction = await encodeRequestErc20ApprovalIfNeeded(
284+
baseValidRequest,
285+
provider,
286+
wallet.address,
287+
{
288+
approval: approvalSettings,
289+
},
290+
);
274291

292+
expect(approvalTransaction).toEqual({
293+
data: erc20ApprovalData(proxyERC20, arbitraryApprovalValue),
294+
to: erc20ContractAddress,
295+
value: 0,
296+
});
297+
});
275298
it('Should not return anything', async () => {
276299
let approvalTransaction = await encodeRequestErc20ApprovalIfNeeded(
277300
baseValidRequest,
@@ -303,6 +326,22 @@ describe('Approval encoder handles ERC20 Fee Proxy', () => {
303326
value: 0,
304327
});
305328
});
329+
it('Should return a valid transaction with specific approved value', async () => {
330+
const approvalTransaction = await encodeRequestErc20ApprovalIfNeeded(
331+
validRequestERC20FeeProxy,
332+
provider,
333+
wallet.address,
334+
{
335+
approval: approvalSettings,
336+
},
337+
);
338+
339+
expect(approvalTransaction).toEqual({
340+
data: erc20ApprovalData(proxyERC20Fee, arbitraryApprovalValue),
341+
to: erc20ContractAddress,
342+
value: 0,
343+
});
344+
});
306345
it('Should not return anything', async () => {
307346
let approvalTransaction = await encodeRequestErc20ApprovalIfNeeded(
308347
validRequestERC20FeeProxy,
@@ -337,7 +376,23 @@ describe('Approval encoder handles ERC20 Conversion Proxy', () => {
337376
value: 0,
338377
});
339378
});
379+
it('Should return a valid transaction with specific approval value', async () => {
380+
let approvalTransaction = await encodeRequestErc20ApprovalIfNeeded(
381+
validRequestERC20ConversionProxy,
382+
provider,
383+
wallet.address,
384+
{
385+
conversion: alphaConversionSettings,
386+
approval: approvalSettings,
387+
},
388+
);
340389

390+
expect(approvalTransaction).toEqual({
391+
data: erc20ApprovalData(proxyERC20Conv, arbitraryApprovalValue),
392+
to: alphaContractAddress,
393+
value: 0,
394+
});
395+
});
341396
it('Should not return anything', async () => {
342397
let approvalTransaction = await encodeRequestErc20ApprovalIfNeeded(
343398
validRequestERC20ConversionProxy,
@@ -388,7 +443,23 @@ describe('Approval encoder handles ERC20 Swap Proxy', () => {
388443
value: 0,
389444
});
390445
});
446+
it('Should return a valid transaction with specific approval value', async () => {
447+
let approvalTransaction = await encodeRequestErc20ApprovalIfNeeded(
448+
validRequestERC20FeeProxy,
449+
provider,
450+
wallet.address,
451+
{
452+
swap: alphaSwapSettings,
453+
approval: approvalSettings,
454+
},
455+
);
391456

457+
expect(approvalTransaction).toEqual({
458+
data: erc20ApprovalData(proxyERC20Swap, arbitraryApprovalValue),
459+
to: alphaContractAddress,
460+
value: 0,
461+
});
462+
});
392463
it('Should not return anything', async () => {
393464
let approvalTransaction = await encodeRequestErc20ApprovalIfNeeded(
394465
validRequestERC20FeeProxy,
@@ -430,7 +501,24 @@ describe('Approval encoder handles ERC20 Swap & Conversion Proxy', () => {
430501
value: 0,
431502
});
432503
});
504+
it('Should return a valid transaction with specififc approval value', async () => {
505+
let approvalTransaction = await encodeRequestErc20ApprovalIfNeeded(
506+
validRequestERC20ConversionProxy,
507+
provider,
508+
wallet.address,
509+
{
510+
swap: alphaSwapConversionSettings,
511+
conversion: alphaConversionSettings,
512+
approval: approvalSettings,
513+
},
514+
);
433515

516+
expect(approvalTransaction).toEqual({
517+
data: erc20ApprovalData(proxyERC20SwapConv, arbitraryApprovalValue),
518+
to: erc20ContractAddress,
519+
value: 0,
520+
});
521+
});
434522
it('Should not return anything', async () => {
435523
let approvalTransaction = await encodeRequestErc20ApprovalIfNeeded(
436524
validRequestERC20ConversionProxy,

0 commit comments

Comments
 (0)