• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

RequestNetwork / requestNetwork / 90b6f474-c153-48f9-b38d-d27f2b790b01

pending completion
90b6f474-c153-48f9-b38d-d27f2b790b01

Pull #1056

circleci

Alexandre ABRIOUX
rollback addagregator change
Pull Request #1056: refactor: chain configurations

2325 of 2946 branches covered (78.92%)

Branch coverage included in aggregate %.

99 of 99 new or added lines in 28 files covered. (100.0%)

5120 of 5553 relevant lines covered (92.2%)

20.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.48
/packages/payment-processor/src/payment/batch-proxy.ts
1
import { ContractTransaction, Signer, providers, constants, BigNumber } from 'ethers';
14✔
2
import { batchPaymentsArtifact } from '@requestnetwork/smart-contracts';
14✔
3
import { BatchPayments__factory } from '@requestnetwork/smart-contracts/types';
14✔
4
import { ClientTypes } from '@requestnetwork/types';
5
import { getPaymentNetworkExtension } from '@requestnetwork/payment-detection';
14✔
6
import { ITransactionOverrides } from './transaction-overrides';
7
import {
14✔
8
  comparePnTypeAndVersion,
9
  getAmountToPay,
10
  getProvider,
11
  getRequestPaymentValues,
12
  getSigner,
13
  validateErc20FeeProxyRequest,
14
} from './utils';
15
import { validateEthFeeProxyRequest } from './eth-fee-proxy';
14✔
16
import { IPreparedTransaction } from './prepared-transaction';
17
import { checkErc20Allowance, encodeApproveAnyErc20 } from './erc20';
14✔
18
import { EvmChains } from '@requestnetwork/currency';
14✔
19

20
/**
21
 * ERC20 Batch Proxy payment details:
22
 *   batch of request with the same payment network type: ERC20
23
 *   batch of request with the same payment network version
24
 *   2 modes available: single token or multi tokens
25
 * It requires batch proxy's approval
26
 *
27
 * Eth Batch Proxy payment details:
28
 *   batch of request with the same payment network type
29
 *   batch of request with the same payment network version
30
 * -> Eth batch proxy accepts requests with 2 id: ethProxy and ethFeeProxy
31
 *    but only call ethFeeProxy. It can impact payment detection
32
 */
33

34
/**
35
 * Processes a transaction to pay a batch of ETH Requests with fees.
36
 * Requests paymentType must be "ETH" or "ERC20"
37
 * @param requests List of requests
38
 * @param version The version version of the batch proxy, which can be different from request pn version
39
 * @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum.
40
 * @param batchFee Only for batch ETH: additional fee applied to a batch, between 0 and 1000, default value = 10
41
 * @param overrides optionally, override default transaction values, like gas.
42
 */
43
export async function payBatchProxyRequest(
14✔
44
  requests: ClientTypes.IRequestData[],
45
  version: string,
46
  signerOrProvider: providers.Provider | Signer = getProvider(),
47
  batchFee: number,
48
  overrides?: ITransactionOverrides,
49
): Promise<ContractTransaction> {
50
  const { data, to, value } = prepareBatchPaymentTransaction(requests, version, batchFee);
19✔
51
  const signer = getSigner(signerOrProvider);
6✔
52
  return signer.sendTransaction({ data, to, value, ...overrides });
6✔
53
}
54

55
/**
56
 * Prepate the transaction to pay a batch of requests through the batch proxy contract, can be used with a Multisig contract.
57
 * Requests paymentType must be "ETH" or "ERC20"
58
 * @param requests list of ETH requests to pay
59
 * @param version The version version of the batch proxy, which can be different from request pn version
60
 * @param batchFee additional fee applied to a batch
61
 */
62
export function prepareBatchPaymentTransaction(
14✔
63
  requests: ClientTypes.IRequestData[],
64
  version: string,
65
  batchFee: number,
66
): IPreparedTransaction {
67
  const encodedTx = encodePayBatchRequest(requests);
21✔
68
  const proxyAddress = getBatchProxyAddress(requests[0], version);
8✔
69
  let totalAmount = 0;
8✔
70

71
  if (requests[0].currencyInfo.type === 'ETH') {
8✔
72
    const { amountsToPay, feesToPay } = getBatchArgs(requests);
2✔
73

74
    const amountToPay = amountsToPay.reduce((sum, current) => sum.add(current), BigNumber.from(0));
4✔
75
    const batchFeeToPay = BigNumber.from(amountToPay).mul(batchFee).div(1000);
2✔
76
    const feeToPay = feesToPay.reduce(
2✔
77
      (sum, current) => sum.add(current),
4✔
78
      BigNumber.from(batchFeeToPay),
79
    );
80
    totalAmount = amountToPay.add(feeToPay).toNumber();
2✔
81
  }
82

83
  return {
8✔
84
    data: encodedTx,
85
    to: proxyAddress,
86
    value: totalAmount,
87
  };
88
}
89

90
/**
91
 * Encodes the call to pay a batch of requests through the ERC20Bacth or ETHBatch proxy contract,
92
 * can be used with a Multisig contract.
93
 * @param requests list of ECR20 requests to pay
94
 * @dev pn version of the requests is checked to avoid paying with two differents proxies (e.g: erc20proxy v1 and v2)
95
 */
96
export function encodePayBatchRequest(requests: ClientTypes.IRequestData[]): string {
14✔
97
  const {
98
    tokenAddresses,
99
    paymentAddresses,
100
    amountsToPay,
101
    paymentReferences,
102
    feesToPay,
103
    feeAddressUsed,
104
  } = getBatchArgs(requests);
22✔
105

106
  const proxyContract = BatchPayments__factory.createInterface();
11✔
107

108
  if (requests[0].currencyInfo.type === 'ERC20') {
11✔
109
    let isMultiTokens = false;
8✔
110
    for (let i = 0; tokenAddresses.length; i++) {
8✔
111
      if (tokenAddresses[0] !== tokenAddresses[i]) {
20✔
112
        isMultiTokens = true;
8✔
113
        break;
8✔
114
      }
115
    }
116

117
    const pn = getPaymentNetworkExtension(requests[0]);
8✔
118
    for (let i = 0; i < requests.length; i++) {
8✔
119
      validateErc20FeeProxyRequest(requests[i]);
16✔
120
      comparePnTypeAndVersion(pn, requests[i]);
16✔
121
    }
122

123
    if (isMultiTokens) {
6✔
124
      return proxyContract.encodeFunctionData('batchERC20PaymentsMultiTokensWithReference', [
6✔
125
        tokenAddresses,
126
        paymentAddresses,
127
        amountsToPay,
128
        paymentReferences,
129
        feesToPay,
130
        feeAddressUsed,
131
      ]);
132
    } else {
133
      return proxyContract.encodeFunctionData('batchERC20PaymentsWithReference', [
×
134
        tokenAddresses[0],
135
        paymentAddresses,
136
        amountsToPay,
137
        paymentReferences,
138
        feesToPay,
139
        feeAddressUsed,
140
      ]);
141
    }
142
  } else {
143
    tokenAddresses;
3✔
144
    return proxyContract.encodeFunctionData('batchEthPaymentsWithReference', [
3✔
145
      paymentAddresses,
146
      amountsToPay,
147
      paymentReferences,
148
      feesToPay,
149
      feeAddressUsed,
150
    ]);
151
  }
152
}
153

154
/**
155
 * Get batch arguments
156
 * @param requests List of requests
157
 * @param forcedPaymentType It force to considere the request as an ETH or an ERC20 payment
158
 * @returns List with the args required by batch Eth and Erc20 functions,
159
 * @dev tokenAddresses returned is for batch Erc20 functions
160
 */
161
export function getBatchArgs(
14✔
162
  requests: ClientTypes.IRequestData[],
163
  forcedPaymentType?: 'ETH' | 'ERC20',
164
): {
165
  tokenAddresses: Array<string>;
166
  paymentAddresses: Array<string>;
167
  amountsToPay: Array<BigNumber>;
168
  paymentReferences: Array<string>;
169
  feesToPay: Array<BigNumber>;
170
  feeAddressUsed: string;
171
} {
172
  const tokenAddresses: Array<string> = [];
24✔
173
  const paymentAddresses: Array<string> = [];
24✔
174
  const amountsToPay: Array<BigNumber> = [];
24✔
175
  const paymentReferences: Array<string> = [];
24✔
176
  const feesToPay: Array<BigNumber> = [];
24✔
177
  let feeAddressUsed = constants.AddressZero;
24✔
178

179
  const paymentType = forcedPaymentType ?? requests[0].currencyInfo.type;
24✔
180
  for (let i = 0; i < requests.length; i++) {
24✔
181
    if (paymentType === 'ETH') {
48✔
182
      validateEthFeeProxyRequest(requests[i]);
16✔
183
    } else if (paymentType === 'ERC20') {
32✔
184
      validateErc20FeeProxyRequest(requests[i]);
32✔
185
    } else {
186
      throw new Error(`paymentType ${paymentType} is not supported for batch payment`);
×
187
    }
188

189
    const tokenAddress = requests[i].currencyInfo.value;
37✔
190
    const { paymentReference, paymentAddress, feeAddress, feeAmount } = getRequestPaymentValues(
37✔
191
      requests[i],
192
    );
193

194
    tokenAddresses.push(tokenAddress);
37✔
195
    paymentAddresses.push(paymentAddress);
37✔
196
    amountsToPay.push(getAmountToPay(requests[i]));
37✔
197
    paymentReferences.push(`0x${paymentReference}`);
37✔
198
    feesToPay.push(BigNumber.from(feeAmount || 0));
37✔
199
    feeAddressUsed = feeAddress || constants.AddressZero;
37✔
200
  }
201

202
  return {
13✔
203
    tokenAddresses,
204
    paymentAddresses,
205
    amountsToPay,
206
    paymentReferences,
207
    feesToPay,
208
    feeAddressUsed,
209
  };
210
}
211

212
/**
213
 * Get Batch contract Address
214
 * @param request The request to pay
215
 * @param version The version of the batch proxy, which can be different from request pn version
216
 */
217
export function getBatchProxyAddress(request: ClientTypes.IRequestData, version: string): string {
14✔
218
  const pn = getPaymentNetworkExtension(request);
15✔
219
  const pnId = pn?.id;
15✔
220
  if (!pnId) {
15✔
221
    throw new Error('No payment network Id');
×
222
  }
223
  const { network } = request.currencyInfo;
15✔
224
  if (!network) {
15✔
225
    throw new Error('No currency network');
×
226
  }
227
  EvmChains.assertChainSupported(network);
15✔
228

229
  const proxyAddress = batchPaymentsArtifact.getAddress(network, version);
15✔
230

231
  if (!proxyAddress) {
15✔
232
    throw new Error(`No deployment found for network ${pn}, version ${pn?.version}`);
×
233
  }
234
  return proxyAddress;
15✔
235
}
236

237
/**
238
 * ERC20 Batch proxy approvals methods
239
 */
240

241
/**
242
 * Processes the approval transaction of the targeted ERC20 with batch proxy.
243
 * @param request The request to pay
244
 * @param account The account that will be used to pay the request
245
 * @param version The version version of the batch proxy, which can be different from request pn version
246
 * @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum.
247
 * @param overrides optionally, override default transaction values, like gas.
248
 */
249
export async function approveErc20BatchIfNeeded(
14✔
250
  request: ClientTypes.IRequestData,
251
  account: string,
252
  version: string,
253
  signerOrProvider: providers.Provider | Signer = getProvider(),
254
  overrides?: ITransactionOverrides,
255
): Promise<ContractTransaction | void> {
256
  if (!(await hasErc20BatchApproval(request, account, version, signerOrProvider))) {
3✔
257
    return approveErc20Batch(request, version, getSigner(signerOrProvider), overrides);
2✔
258
  }
259
}
260

261
/**
262
 * Checks if the batch proxy has the necessary allowance from a given account
263
 * to pay a given request with ERC20 batch
264
 * @param request The request to pay
265
 * @param account The account that will be used to pay the request
266
 * @param version The version version of the batch proxy, which can be different from request pn version
267
 * @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum.
268
 */
269
export async function hasErc20BatchApproval(
14✔
270
  request: ClientTypes.IRequestData,
271
  account: string,
272
  version: string,
273
  signerOrProvider: providers.Provider | Signer = getProvider(),
274
): Promise<boolean> {
275
  return checkErc20Allowance(
3✔
276
    account,
277
    getBatchProxyAddress(request, version),
278
    signerOrProvider,
279
    request.currencyInfo.value,
280
    request.expectedAmount,
281
  );
282
}
283

284
/**
285
 * Processes the transaction to approve the batch proxy to spend signer's tokens to pay
286
 * the request in its payment currency. Can be used with a Multisig contract.
287
 * @param request The request to pay
288
 * @param version The version version of the batch proxy, which can be different from request pn version
289
 * @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum.
290
 * @param overrides optionally, override default transaction values, like gas.
291
 */
292
export async function approveErc20Batch(
14✔
293
  request: ClientTypes.IRequestData,
294
  version: string,
295
  signerOrProvider: providers.Provider | Signer = getProvider(),
296
  overrides?: ITransactionOverrides,
297
): Promise<ContractTransaction> {
298
  const preparedTx = prepareApproveErc20Batch(request, version, signerOrProvider, overrides);
2✔
299
  const signer = getSigner(signerOrProvider);
2✔
300
  const tx = await signer.sendTransaction(preparedTx);
2✔
301
  return tx;
2✔
302
}
303

304
/**
305
 * Prepare the transaction to approve the proxy to spend signer's tokens to pay
306
 * the request in its payment currency. Can be used with a Multisig contract.
307
 * @param request The request to pay
308
 * @param version The version version of the batch proxy, which can be different from request pn version
309
 * @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum.
310
 * @param overrides optionally, override default transaction values, like gas.
311
 */
312
export function prepareApproveErc20Batch(
14✔
313
  request: ClientTypes.IRequestData,
314
  version: string,
315
  signerOrProvider: providers.Provider | Signer = getProvider(),
316
  overrides?: ITransactionOverrides,
317
): IPreparedTransaction {
318
  const encodedTx = encodeApproveErc20Batch(request, version, signerOrProvider);
2✔
319
  const tokenAddress = request.currencyInfo.value;
2✔
320
  return {
2✔
321
    data: encodedTx,
322
    to: tokenAddress,
323
    value: 0,
324
    ...overrides,
325
  };
326
}
327

328
/**
329
 * Encodes the transaction to approve the batch proxy to spend signer's tokens to pay
330
 * the request in its payment currency. Can be used with a Multisig contract.
331
 * @param request The request to pay
332
 * @param version The version version of the batch proxy, which can be different from request pn version
333
 * @param signerOrProvider the Web3 provider, or signer. Defaults to window.ethereum.
334
 */
335
export function encodeApproveErc20Batch(
14✔
336
  request: ClientTypes.IRequestData,
337
  version: string,
338
  signerOrProvider: providers.Provider | Signer = getProvider(),
339
): string {
340
  const proxyAddress = getBatchProxyAddress(request, version);
2✔
341

342
  return encodeApproveAnyErc20(
2✔
343
    request.currencyInfo.value,
344
    proxyAddress,
345
    getSigner(signerOrProvider),
346
  );
347
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc