From f8842c6bd00e5968d3d8e70597a9538e11b401e8 Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Wed, 20 May 2026 13:00:43 +0200 Subject: [PATCH 01/19] feat: tron batch payment contracts and tests --- package.json | 11 +- .../test/tron/BatchPayments.test.js | 942 ++++++++++++++++++ .../test/tron/ERC20BatchPayments.test.js | 772 ++++++++++++++ packages/smart-contracts/test/tron/helpers.js | 155 +++ .../tron/contracts/BatchPayments.sol | 1 + .../tron/contracts/ERC20BatchPayments.sol | 179 ++++ .../contracts/interfaces/ERC20FeeProxy.sol | 1 + .../contracts/interfaces/EthereumFeeProxy.sol | 1 + .../tron/contracts/lib/SafeERC20.sol | 1 + 9 files changed, 2060 insertions(+), 3 deletions(-) create mode 100644 packages/smart-contracts/test/tron/BatchPayments.test.js create mode 100644 packages/smart-contracts/test/tron/ERC20BatchPayments.test.js create mode 100644 packages/smart-contracts/test/tron/helpers.js create mode 120000 packages/smart-contracts/tron/contracts/BatchPayments.sol create mode 100644 packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol create mode 120000 packages/smart-contracts/tron/contracts/interfaces/ERC20FeeProxy.sol create mode 120000 packages/smart-contracts/tron/contracts/interfaces/EthereumFeeProxy.sol create mode 120000 packages/smart-contracts/tron/contracts/lib/SafeERC20.sol diff --git a/package.json b/package.json index 9034320d17..59e5642682 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,14 @@ "node": ">=22.0.0" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e", - "workspaces": [ - "packages/*" - ], + "workspaces": { + "packages": [ + "packages/*" + ], + "nohoist": [ + "@requestnetwork/smart-contracts/@openzeppelin/**" + ] + }, "repository": { "type": "git", "url": "git+https://github.com/RequestNetwork/requestNetwork.git" diff --git a/packages/smart-contracts/test/tron/BatchPayments.test.js b/packages/smart-contracts/test/tron/BatchPayments.test.js new file mode 100644 index 0000000000..a024230ad8 --- /dev/null +++ b/packages/smart-contracts/test/tron/BatchPayments.test.js @@ -0,0 +1,942 @@ +const BatchPayments = artifacts.require('BatchPayments'); +const { + REF_A, + REF_B, + REF_C, + waitForConfirmation, + balanceOf, + diff, + deployBaseSetup, + makeTokenApproval, + deployTokenWithSupply, + expectRevertOrNoBalanceChange, + assertBatchTokenBalancesZero, + expectNonOwnerReverts, + deployBadTRC20, + sumStrings, + mulString, + computeBatchFee, + getApprovalAmount, + trxBalance, + ONE_TRX_SUN, + TRON_ZERO_ADDRESS, +} = require('./helpers'); + +contract('BatchPayments Tron Test Suite', (accounts) => { + const payer = accounts[0]; + const payee1 = accounts[1] || 'TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE'; + const payee2 = accounts[2] || 'TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs'; + const payee3 = accounts[3] || 'TFwt56qg984vEmk2UoDqUDeZhWEFSDaTmk'; + const feeAddress = accounts[4] || 'TNPGB28MjVCnEhTfpW51C2Ap3ZNnqGDXLB'; + + const BATCH_FEE_BPS = 10; + + let batch; + let token1; + let token2; + let token3; + + before(async () => { + const setup = await deployBaseSetup({ + accounts, + batchDeployFn: (erc20FeeProxy, owner, ethProxy) => + BatchPayments.new(erc20FeeProxy.address, ethProxy, owner), + batchFee: BATCH_FEE_BPS, + }); + batch = setup.batch; + [token1, token2, token3] = setup.tokens; + + console.log('\n=== BatchPayments (main) Test Setup ==='); + console.log('Batch:', batch.address); + console.log('Token1:', token1.address); + await waitForConfirmation(3000); + }); + + beforeEach(async () => { + await waitForConfirmation(2000); + }); + + describe('Happy Path Payment Scenarios', () => { + describe('batchERC20PaymentsWithReference', () => { + it('should pay 3 ERC20 payments', async () => { + const amount1 = '2000'; + const amount2 = '300'; + const amount3 = '400'; + const fee1 = '200'; + const fee2 = '20'; + const fee3 = '30'; + + const batchFee = computeBatchFee(sumStrings([amount1, amount2, amount3]), BATCH_FEE_BPS); + const totalPaymentAndFees = sumStrings([ + amount1, + amount2, + amount3, + fee1, + fee2, + fee3, + batchFee, + ]); + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3], batchFee), + ); + + const payee1Before = await balanceOf(token1, payee1); + const payee2Before = await balanceOf(token1, payee2); + const payerBefore = await balanceOf(token1, payer); + const feeBefore = await balanceOf(token1, feeAddress); + + await batch.batchERC20PaymentsWithReference( + token1.address, + [payee1, payee2, payee2], + [amount1, amount2, amount3], + [REF_A, REF_B, REF_C], + [fee1, fee2, fee3], + feeAddress, + { from: payer }, + ); + await waitForConfirmation(3000); + + const payee1After = await balanceOf(token1, payee1); + const payee2After = await balanceOf(token1, payee2); + const payerAfter = await balanceOf(token1, payer); + const feeAfter = await balanceOf(token1, feeAddress); + + assert.equal(diff(payee1After, payee1Before).toString(), amount1); + assert.equal(diff(payee2After, payee2Before).toString(), sumStrings([amount2, amount3])); + assert.equal( + diff(feeAfter, feeBefore).toString(), + sumStrings([fee1, fee2, fee3, batchFee]), + ); + assert( + diff(payerBefore, payerAfter) >= BigInt(totalPaymentAndFees), + 'payer should pay amounts, fees, and batch fee', + ); + }); + + it('should pay 10 ERC20 payments', async () => { + const amount = '200'; + const feeAmount = '100'; + const nbTxs = 10; + + const batchFeeTotal = computeBatchFee(mulString(amount, nbTxs), BATCH_FEE_BPS); + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount(Array(nbTxs).fill(amount), Array(nbTxs).fill(feeAmount), batchFeeTotal), + ); + + const payee1Before = await balanceOf(token1, payee1); + const feeBefore = await balanceOf(token1, feeAddress); + + await batch.batchERC20PaymentsWithReference( + token1.address, + Array(nbTxs).fill(payee1), + Array(nbTxs).fill(amount), + Array(nbTxs).fill(REF_A), + Array(nbTxs).fill(feeAmount), + feeAddress, + { from: payer }, + ); + await waitForConfirmation(3000); + + assert.equal( + diff(await balanceOf(token1, payee1), payee1Before).toString(), + mulString(amount, nbTxs), + ); + assert.equal( + diff(await balanceOf(token1, feeAddress), feeBefore).toString(), + sumStrings([mulString(feeAmount, nbTxs), batchFeeTotal]), + ); + }); + + it('should leave no token balance on the batch contract after a successful payment', async () => { + const amount1 = '100'; + const fee1 = '10'; + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1], [fee1], computeBatchFee(amount1, BATCH_FEE_BPS)), + ); + + await batch.batchERC20PaymentsWithReference( + token1.address, + [payee1], + [amount1], + [REF_A], + [fee1], + feeAddress, + { from: payer }, + ); + await waitForConfirmation(3000); + + await assertBatchTokenBalancesZero(batch, [token1]); + }); + + it('should apply an updated batch fee on the next payment', async () => { + const newBatchFeeBps = 50; + await batch.setBatchFee(newBatchFeeBps, { from: payer }); + + const amount1 = '1000'; + const fee1 = '10'; + const batchFee = computeBatchFee(amount1, newBatchFeeBps); + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1], [fee1], batchFee), + ); + + const feeBefore = await balanceOf(token1, feeAddress); + + await batch.batchERC20PaymentsWithReference( + token1.address, + [payee1], + [amount1], + [REF_A], + [fee1], + feeAddress, + { from: payer }, + ); + await waitForConfirmation(3000); + + assert.equal( + diff(await balanceOf(token1, feeAddress), feeBefore).toString(), + sumStrings([fee1, batchFee]), + ); + + await batch.setBatchFee(BATCH_FEE_BPS, { from: payer }); + }); + + it('should pay ERC20 payments with no batch fee when batch fee is zero', async () => { + await batch.setBatchFee(0, { from: payer }); + + const amount1 = '500'; + const fee1 = '25'; + + await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); + + const feeBefore = await balanceOf(token1, feeAddress); + + await batch.batchERC20PaymentsWithReference( + token1.address, + [payee1], + [amount1], + [REF_A], + [fee1], + feeAddress, + { from: payer }, + ); + await waitForConfirmation(3000); + + assert.equal(diff(await balanceOf(token1, feeAddress), feeBefore).toString(), fee1); + + await batch.setBatchFee(BATCH_FEE_BPS, { from: payer }); + }); + }); + + describe('batchERC20PaymentsMultiTokensWithReference', () => { + it('should pay 3 ERC20 payments in three different tokens', async () => { + const amount1 = '5000'; + const amount2 = '3000'; + const amount3 = '4000'; + const fee1 = '600'; + const fee2 = '200'; + const fee3 = '300'; + + const batchFee1 = computeBatchFee(amount1, BATCH_FEE_BPS); + const batchFee2 = computeBatchFee(amount2, BATCH_FEE_BPS); + const batchFee3 = computeBatchFee(amount3, BATCH_FEE_BPS); + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1], [fee1], batchFee1), + ); + await makeTokenApproval( + token2, + payer, + batch.address, + getApprovalAmount([amount2], [fee2], batchFee2), + ); + await makeTokenApproval( + token3, + payer, + batch.address, + getApprovalAmount([amount3], [fee3], batchFee3), + ); + + const payee1Before = await balanceOf(token1, payee1); + const payee2Token2Before = await balanceOf(token2, payee2); + const payee2Token3Before = await balanceOf(token3, payee2); + const feeToken1Before = await balanceOf(token1, feeAddress); + const feeToken2Before = await balanceOf(token2, feeAddress); + const feeToken3Before = await balanceOf(token3, feeAddress); + const payerToken1Before = await balanceOf(token1, payer); + const payerToken2Before = await balanceOf(token2, payer); + const payerToken3Before = await balanceOf(token3, payer); + + await batch.batchERC20PaymentsMultiTokensWithReference( + [token1.address, token2.address, token3.address], + [payee1, payee2, payee2], + [amount1, amount2, amount3], + [REF_A, REF_B, REF_C], + [fee1, fee2, fee3], + feeAddress, + { from: payer }, + ); + await waitForConfirmation(3000); + + assert.equal(diff(await balanceOf(token1, payee1), payee1Before).toString(), amount1); + assert.equal(diff(await balanceOf(token2, payee2), payee2Token2Before).toString(), amount2); + assert.equal(diff(await balanceOf(token3, payee2), payee2Token3Before).toString(), amount3); + assert.equal( + diff(await balanceOf(token1, feeAddress), feeToken1Before).toString(), + sumStrings([fee1, batchFee1]), + ); + assert.equal( + diff(await balanceOf(token2, feeAddress), feeToken2Before).toString(), + sumStrings([fee2, batchFee2]), + ); + assert.equal( + diff(await balanceOf(token3, feeAddress), feeToken3Before).toString(), + sumStrings([fee3, batchFee3]), + ); + + const total1 = sumStrings([amount1, fee1, batchFee1]); + const total2 = sumStrings([amount2, fee2, batchFee2]); + const total3 = sumStrings([amount3, fee3, batchFee3]); + assert( + diff(payerToken1Before, await balanceOf(token1, payer)) >= BigInt(total1), + 'payer should pay token1 amounts, fees, and batch fee', + ); + assert( + diff(payerToken2Before, await balanceOf(token2, payer)) >= BigInt(total2), + 'payer should pay token2 amounts, fees, and batch fee', + ); + assert( + diff(payerToken3Before, await balanceOf(token3, payer)) >= BigInt(total3), + 'payer should pay token3 amounts, fees, and batch fee', + ); + }); + + it('should pay 3 ERC20 payments in three different tokens with a zero amount payment', async () => { + const amount1 = '5000'; + const amount2 = '0'; + const amount3 = '4000'; + const fee1 = '600'; + const fee2 = '0'; + const fee3 = '300'; + + const batchFee1 = computeBatchFee(amount1, BATCH_FEE_BPS); + const batchFee3 = computeBatchFee(amount3, BATCH_FEE_BPS); + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1], [fee1], batchFee1), + ); + await makeTokenApproval(token2, payer, batch.address, getApprovalAmount([amount2], [fee2])); + await makeTokenApproval( + token3, + payer, + batch.address, + getApprovalAmount([amount3], [fee3], batchFee3), + ); + + const payee1Before = await balanceOf(token1, payee1); + const payee2Token2Before = await balanceOf(token2, payee2); + const payee2Token3Before = await balanceOf(token3, payee2); + + await batch.batchERC20PaymentsMultiTokensWithReference( + [token1.address, token2.address, token3.address], + [payee1, payee2, payee2], + [amount1, amount2, amount3], + [REF_A, REF_B, REF_C], + [fee1, fee2, fee3], + feeAddress, + { from: payer }, + ); + await waitForConfirmation(3000); + + assert.equal(diff(await balanceOf(token1, payee1), payee1Before).toString(), amount1); + assert.equal(diff(await balanceOf(token2, payee2), payee2Token2Before).toString(), amount2); + assert.equal(diff(await balanceOf(token3, payee2), payee2Token3Before).toString(), amount3); + }); + + it('should pay 4 ERC20 payments in two different tokens', async () => { + const amount1 = '200'; + const amount2 = '200'; + const amount3 = '200'; + const amount4 = '200'; + const fee1 = '10'; + const fee2 = '10'; + const fee3 = '10'; + const fee4 = '10'; + + const batchFee1 = computeBatchFee(sumStrings([amount1, amount2]), BATCH_FEE_BPS); + const batchFee2 = computeBatchFee(sumStrings([amount3, amount4]), BATCH_FEE_BPS); + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1, amount2], [fee1, fee2], batchFee1), + ); + await makeTokenApproval( + token2, + payer, + batch.address, + getApprovalAmount([amount3, amount4], [fee3, fee4], batchFee2), + ); + + const payee2Token1Before = await balanceOf(token1, payee2); + const payee2Token2Before = await balanceOf(token2, payee2); + + await batch.batchERC20PaymentsMultiTokensWithReference( + [token1.address, token1.address, token2.address, token2.address], + [payee2, payee2, payee2, payee2], + [amount1, amount2, amount3, amount4], + [REF_A, REF_A, REF_A, REF_A], + [fee1, fee2, fee3, fee4], + feeAddress, + { from: payer }, + ); + await waitForConfirmation(3000); + + assert.equal( + diff(await balanceOf(token1, payee2), payee2Token1Before).toString(), + sumStrings([amount1, amount2]), + ); + assert.equal( + diff(await balanceOf(token2, payee2), payee2Token2Before).toString(), + sumStrings([amount3, amount4]), + ); + }); + + it('should pay 10 ERC20 payments in two different tokens', async () => { + const amount = '20'; + const feeAmount = '10'; + const nbPaymentsPerToken = 5; + + const batchFee1 = computeBatchFee(mulString(amount, nbPaymentsPerToken), BATCH_FEE_BPS); + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount( + Array(nbPaymentsPerToken).fill(amount), + Array(nbPaymentsPerToken).fill(feeAmount), + batchFee1, + ), + ); + await makeTokenApproval( + token2, + payer, + batch.address, + getApprovalAmount( + Array(nbPaymentsPerToken).fill(amount), + Array(nbPaymentsPerToken).fill(feeAmount), + batchFee1, + ), + ); + + const payee1Token1Before = await balanceOf(token1, payee1); + const payee1Token2Before = await balanceOf(token2, payee1); + + await batch.batchERC20PaymentsMultiTokensWithReference( + [ + ...Array(nbPaymentsPerToken).fill(token1.address), + ...Array(nbPaymentsPerToken).fill(token2.address), + ], + Array(nbPaymentsPerToken * 2).fill(payee1), + Array(nbPaymentsPerToken * 2).fill(amount), + Array(nbPaymentsPerToken * 2).fill(REF_A), + Array(nbPaymentsPerToken * 2).fill(feeAmount), + feeAddress, + { from: payer }, + ); + await waitForConfirmation(3000); + + assert.equal( + diff(await balanceOf(token1, payee1), payee1Token1Before).toString(), + mulString(amount, nbPaymentsPerToken), + ); + assert.equal( + diff(await balanceOf(token2, payee1), payee1Token2Before).toString(), + mulString(amount, nbPaymentsPerToken), + ); + }); + + it('should leave no token balance on the batch contract after a successful payment', async () => { + const amount1 = '100'; + const amount2 = '200'; + const fee1 = '10'; + const fee2 = '20'; + + const batchFee1 = computeBatchFee(amount1, BATCH_FEE_BPS); + const batchFee2 = computeBatchFee(amount2, BATCH_FEE_BPS); + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1], [fee1], batchFee1), + ); + await makeTokenApproval( + token2, + payer, + batch.address, + getApprovalAmount([amount2], [fee2], batchFee2), + ); + + await batch.batchERC20PaymentsMultiTokensWithReference( + [token1.address, token2.address], + [payee1, payee2], + [amount1, amount2], + [REF_A, REF_B], + [fee1, fee2], + feeAddress, + { from: payer }, + ); + await waitForConfirmation(3000); + + await assertBatchTokenBalancesZero(batch, [token1, token2]); + }); + + it('should pay a multi-token ERC20 payment with BadTRC20', async () => { + const badToken = await deployBadTRC20(payer); + const paymentAmount = '100'; + const feeAmount = '10'; + const amount1 = '50'; + const fee1 = '5'; + + try { + await badToken.approve(batch.address, getApprovalAmount([paymentAmount], [feeAmount]), { + from: payer, + }); + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1], [fee1], computeBatchFee(amount1, BATCH_FEE_BPS)), + ); + await waitForConfirmation(3000); + + const badPayeeBefore = await balanceOf(badToken, payee1); + const payee1Before = await balanceOf(token1, payee2); + + await batch.batchERC20PaymentsMultiTokensWithReference( + [badToken.address, token1.address], + [payee1, payee2], + [paymentAmount, amount1], + [REF_A, REF_B], + [feeAmount, fee1], + feeAddress, + { from: payer }, + ); + await waitForConfirmation(3000); + + const badPayeeAfter = await balanceOf(badToken, payee1); + const payee1After = await balanceOf(token1, payee2); + assert( + badPayeeAfter > badPayeeBefore || payee1After > payee1Before, + 'BadTRC20 multi-token: at least one payee balance should increase when batch succeeds', + ); + } catch (_error) { + console.log( + 'BadTRC20 multi-token batch payment rejected by Tron (acceptable for non-standard tokens)', + ); + } + }); + }); + }); + + describe('Error cases scenarios', () => { + describe('batchERC20PaymentsWithReference', () => { + it('should revert when the payer does not have enough funds to pay', async () => { + const amount1 = '5'; + const amount2 = '30'; + const amount3 = '400'; + const fee1 = '1'; + const fee2 = '2'; + const fee3 = '3'; + + const lowToken = await deployTokenWithSupply('100', payer); + await makeTokenApproval( + lowToken, + payer, + batch.address, + getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3]), + ); + + const payee3Before = await balanceOf(lowToken, payee3); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsWithReference( + lowToken.address, + [payee1, payee2, payee3], + [amount1, amount2, amount3], + [REF_A, REF_B, REF_C], + [fee1, fee2, fee3], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(lowToken, payee3)], + ); + + assert(unchanged, 'should not transfer when funds insufficient'); + assert.equal((await balanceOf(lowToken, payee3)).toString(), payee3Before.toString()); + }); + + it('should revert when the payer does not have enough funds to pay the batch fee', async () => { + const amount1 = '100'; + const amount2 = '200'; + const fee1 = '1'; + const fee2 = '2'; + const paymentTotal = sumStrings([amount1, amount2, fee1, fee2]); + + const lowToken = await deployTokenWithSupply(paymentTotal, payer); + await makeTokenApproval(lowToken, payer, batch.address, paymentTotal); + + const payee1Before = await balanceOf(lowToken, payee1); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsWithReference( + lowToken.address, + [payee1, payee2], + [amount1, amount2], + [REF_A, REF_B], + [fee1, fee2], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(lowToken, payee1)], + ); + + assert(unchanged, 'should not transfer when batch fee cannot be paid'); + assert.equal((await balanceOf(lowToken, payee1)).toString(), payee1Before.toString()); + }); + + it('should revert when the payer did not approve the batch contract to spend the tokens', async () => { + const amount1 = '20'; + const amount2 = '30'; + const amount3 = '40'; + const fee1 = '1'; + const fee2 = '2'; + const fee3 = '3'; + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3]), + ); + await token1.approve(batch.address, '10', { from: payer }); + await waitForConfirmation(2000); + + const payee1Before = await balanceOf(token1, payee1); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsWithReference( + token1.address, + [payee1, payee2, payee3], + [amount1, amount2, amount3], + [REF_A, REF_B, REF_C], + [fee1, fee2, fee3], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(token1, payee1)], + ); + + assert(unchanged, 'should not transfer without allowance'); + assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); + }); + + it('should revert when input arrays have different lengths', async () => { + const amount1 = '100'; + const fee1 = '1'; + const fee2 = '2'; + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1], [fee1, fee2]), + ); + + const payee1Before = await balanceOf(token1, payee1); + const payee2Before = await balanceOf(token1, payee2); + + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsWithReference( + token1.address, + [payee1, payee2], + [amount1], + [REF_A, REF_B], + [fee1, fee2], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(token1, payee1), await balanceOf(token1, payee2)], + ); + + assert(unchanged, 'should not transfer when array lengths mismatch'); + assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); + assert.equal((await balanceOf(token1, payee2)).toString(), payee2Before.toString()); + }); + }); + + describe('batchERC20PaymentsMultiTokensWithReference', () => { + it('should revert when the payer does not have enough funds to pay in at least one of the tokens', async () => { + const amount1 = '5'; + const amount2 = '30'; + const amount3 = '400'; + const fee1 = '1'; + const fee2 = '2'; + const fee3 = '3'; + + const lowToken = await deployTokenWithSupply('400', payer); + await makeTokenApproval( + lowToken, + payer, + batch.address, + getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3]), + ); + + const payee3Before = await balanceOf(lowToken, payee3); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsMultiTokensWithReference( + [lowToken.address, lowToken.address, lowToken.address], + [payee1, payee2, payee3], + [amount1, amount2, amount3], + [REF_A, REF_B, REF_C], + [fee1, fee2, fee3], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(lowToken, payee3)], + ); + + assert(unchanged, 'multi-token batch should not transfer when funds insufficient'); + assert.equal((await balanceOf(lowToken, payee3)).toString(), payee3Before.toString()); + }); + + it('should revert when the payer does not have enough funds to pay the batch fee in at least one of the tokens', async () => { + const amount1 = '100'; + const amount2 = '200'; + const amount3 = '300'; + const fee1 = '1'; + const fee2 = '2'; + const fee3 = '3'; + + const lowToken = await deployTokenWithSupply('607', payer); + await makeTokenApproval( + lowToken, + payer, + batch.address, + getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3]), + ); + + const payee2Before = await balanceOf(lowToken, payee2); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsMultiTokensWithReference( + [lowToken.address, lowToken.address, lowToken.address], + [payee1, payee2, payee2], + [amount1, amount2, amount3], + [REF_A, REF_B, REF_C], + [fee1, fee2, fee3], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(lowToken, payee2)], + ); + + assert(unchanged, 'multi-token batch should not transfer when batch fee cannot be paid'); + assert.equal((await balanceOf(lowToken, payee2)).toString(), payee2Before.toString()); + }); + + it('should revert when the payer did not approve the batch contract to spend the tokens in at least one of the tokens', async () => { + const amount1 = '100'; + const amount2 = '200'; + const amount3 = '300'; + const fee1 = '1'; + const fee2 = '2'; + const fee3 = '3'; + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3]), + ); + await token1.approve(batch.address, '10', { from: payer }); + await waitForConfirmation(2000); + + const payee1Before = await balanceOf(token1, payee1); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsMultiTokensWithReference( + [token1.address, token1.address, token1.address], + [payee1, payee2, payee3], + [amount1, amount2, amount3], + [REF_A, REF_B, REF_C], + [fee1, fee2, fee3], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(token1, payee1)], + ); + + assert(unchanged, 'multi-token batch should not transfer without allowance'); + assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); + }); + + it('should revert when the payer did not approve the batch contract for one of the tokens', async () => { + const amount1 = '100'; + const amount2 = '200'; + const fee1 = '1'; + const fee2 = '2'; + + await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); + + const payee2Token2Before = await balanceOf(token2, payee2); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsMultiTokensWithReference( + [token1.address, token2.address], + [payee1, payee2], + [amount1, amount2], + [REF_A, REF_B], + [fee1, fee2], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(token2, payee2)], + ); + + assert(unchanged, 'should not transfer when one token lacks approval'); + assert.equal((await balanceOf(token2, payee2)).toString(), payee2Token2Before.toString()); + }); + + it('should revert when input arrays have different lengths', async () => { + const amount1 = '100'; + const fee1 = '1'; + const fee2 = '2'; + + await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); + await makeTokenApproval(token2, payer, batch.address, getApprovalAmount([amount1], [fee2])); + + const payee1Before = await balanceOf(token1, payee1); + const payee2Before = await balanceOf(token2, payee2); + + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsMultiTokensWithReference( + [token1.address, token2.address], + [payee1, payee2], + [amount1], + [REF_A, REF_B], + [fee1, fee2], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(token1, payee1), await balanceOf(token2, payee2)], + ); + + assert(unchanged, 'should not transfer when array lengths mismatch'); + assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); + assert.equal((await balanceOf(token2, payee2)).toString(), payee2Before.toString()); + }); + }); + + describe('batchEthPaymentsWithReference', () => { + it('should revert when calling batchEthPaymentsWithReference when EthFeeProxy is not set', async () => { + assert.equal(await batch.paymentEthFeeProxy(), TRON_ZERO_ADDRESS); + + const paymentAmount = String(10 * ONE_TRX_SUN); + const feeAmount = '0'; + const payeeBefore = await trxBalance(payee1); + const payerBefore = await trxBalance(payer); + + try { + await batch.batchEthPaymentsWithReference( + [payee1], + [paymentAmount], + [REF_A], + [feeAmount], + feeAddress, + { from: payer, callValue: Number(paymentAmount) }, + ); + } catch (_error) {} + await waitForConfirmation(2000); + + assert.equal((await trxBalance(payee1)).toString(), payeeBefore.toString()); + + const payerSpent = payerBefore - (await trxBalance(payer)); + assert( + payerSpent < BigInt(paymentAmount), + `payer should only spend tx fees, not ${paymentAmount} sun (spent ${payerSpent})`, + ); + }); + }); + }); + + describe('Admin', () => { + describe('setPaymentErc20FeeProxy', () => { + it('should allow owner to update proxy addresses', async () => { + const ERC20FeeProxy = artifacts.require('ERC20FeeProxy'); + const newProxy = await ERC20FeeProxy.new(); + await batch.setPaymentErc20FeeProxy(newProxy.address, { from: payer }); + assert.equal(await batch.paymentErc20FeeProxy(), newProxy.address); + }); + + it('should revert when a non-owner tries to update proxy addresses', async () => { + await expectNonOwnerReverts( + () => batch.setPaymentErc20FeeProxy(payee1, { from: payee1 }), + async () => await batch.paymentErc20FeeProxy(), + ); + }); + }); + + describe('setBatchFee', () => { + it('should allow owner to update the batch fee', async () => { + const newBatchFee = 50; + await batch.setBatchFee(newBatchFee, { from: payer }); + assert.equal((await batch.batchFee()).toString(), String(newBatchFee)); + await batch.setBatchFee(BATCH_FEE_BPS, { from: payer }); + }); + + it('should revert when a non-owner tries to set the batch fee', async () => { + await expectNonOwnerReverts( + () => batch.setBatchFee(99, { from: payee1 }), + async () => (await batch.batchFee()).toString(), + ); + }); + }); + + describe('setPaymentEthFeeProxy', () => { + it('should revert when a non-owner tries to set the EthFeeProxy address', async () => { + await expectNonOwnerReverts( + () => batch.setPaymentEthFeeProxy(TRON_ZERO_ADDRESS, { from: payee1 }), + async () => await batch.paymentEthFeeProxy(), + ); + }); + }); + }); +}); diff --git a/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js b/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js new file mode 100644 index 0000000000..0c1d8b900e --- /dev/null +++ b/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js @@ -0,0 +1,772 @@ +const BatchPaymentsTronSimplified = artifacts.require('BatchPaymentsTronSimplified'); +const { + REF_A, + REF_B, + REF_C, + TRON_ZERO_ADDRESS, + waitForConfirmation, + balanceOf, + diff, + deployBaseSetup, + makeTokenApproval, + deployTokenWithSupply, + expectRevertOrNoBalanceChange, + assertBatchTokenBalancesZero, + expectNonOwnerReverts, + deployBadTRC20, + sumStrings, + mulString, + getApprovalAmount, +} = require('./helpers'); + +contract('BatchPaymentsTronSimplified Tron Test Suite', (accounts) => { + const payer = accounts[0]; + const payee1 = accounts[1] || 'TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE'; + const payee2 = accounts[2] || 'TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs'; + const payee3 = accounts[3] || 'TFwt56qg984vEmk2UoDqUDeZhWEFSDaTmk'; + const feeAddress = accounts[4] || 'TNPGB28MjVCnEhTfpW51C2Ap3ZNnqGDXLB'; + + let batch; + let token1; + let token2; + let token3; + + before(async () => { + const setup = await deployBaseSetup({ + accounts, + batchDeployFn: (erc20FeeProxy, owner) => + BatchPaymentsTronSimplified.new(erc20FeeProxy.address, owner), + }); + batch = setup.batch; + [token1, token2, token3] = setup.tokens; + + console.log('\n=== BatchPaymentsTronSimplified Test Setup ==='); + console.log('Batch:', batch.address); + await waitForConfirmation(3000); + }); + + beforeEach(async () => { + await waitForConfirmation(2000); + }); + + describe('Happy Path Payment Scenarios', () => { + describe('batchERC20PaymentsWithReference', () => { + it('should pay 3 ERC20 payments', async () => { + const amount1 = '2000'; + const amount2 = '300'; + const amount3 = '400'; + const fee1 = '200'; + const fee2 = '20'; + const fee3 = '30'; + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3]), + ); + + const payee1Before = await balanceOf(token1, payee1); + const payee2Before = await balanceOf(token1, payee2); + const feeBefore = await balanceOf(token1, feeAddress); + + await batch.batchERC20PaymentsWithReference( + token1.address, + [payee1, payee2, payee2], + [amount1, amount2, amount3], + [REF_A, REF_B, REF_C], + [fee1, fee2, fee3], + feeAddress, + { from: payer }, + ); + + assert.equal(diff(await balanceOf(token1, payee1), payee1Before).toString(), amount1); + assert.equal( + diff(await balanceOf(token1, payee2), payee2Before).toString(), + sumStrings([amount2, amount3]), + ); + assert.equal( + diff(await balanceOf(token1, feeAddress), feeBefore).toString(), + sumStrings([fee1, fee2, fee3]), + ); + }); + + it('should pay 10 ERC20 payments', async () => { + const amount = '200'; + const feeAmount = '100'; + const nbTxs = 10; + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount(Array(nbTxs).fill(amount), Array(nbTxs).fill(feeAmount)), + ); + + const payee1Before = await balanceOf(token1, payee1); + const feeBefore = await balanceOf(token1, feeAddress); + + await batch.batchERC20PaymentsWithReference( + token1.address, + Array(nbTxs).fill(payee1), + Array(nbTxs).fill(amount), + Array(nbTxs).fill(REF_A), + Array(nbTxs).fill(feeAmount), + feeAddress, + { from: payer }, + ); + + assert.equal( + diff(await balanceOf(token1, payee1), payee1Before).toString(), + mulString(amount, nbTxs), + ); + assert.equal( + diff(await balanceOf(token1, feeAddress), feeBefore).toString(), + mulString(feeAmount, nbTxs), + ); + }); + + it('should leave no token balance on the batch contract after a successful payment', async () => { + const amount1 = '100'; + const fee1 = '10'; + + await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); + + await batch.batchERC20PaymentsWithReference( + token1.address, + [payee1], + [amount1], + [REF_A], + [fee1], + feeAddress, + { from: payer }, + ); + await waitForConfirmation(3000); + + await assertBatchTokenBalancesZero(batch, [token1]); + }); + + it('should pay a single ERC20 payment with BadTRC20', async () => { + const badToken = await deployBadTRC20(payer); + const paymentAmount = '100'; + const feeAmount = '10'; + + try { + await badToken.approve(batch.address, getApprovalAmount([paymentAmount], [feeAmount]), { + from: payer, + }); + await waitForConfirmation(3000); + + const payeeBefore = await balanceOf(badToken, payee1); + + await batch.batchERC20PaymentsWithReference( + badToken.address, + [payee1], + [paymentAmount], + [REF_A], + [feeAmount], + feeAddress, + { from: payer }, + ); + await waitForConfirmation(3000); + + const payeeAfter = await balanceOf(badToken, payee1); + assert( + payeeAfter > payeeBefore, + 'BadTRC20: payee balance should increase when batch payment succeeds', + ); + } catch (_error) { + console.log( + 'BadTRC20 batch payment rejected by Tron (acceptable for non-standard tokens)', + ); + } + }); + }); + + describe('batchERC20PaymentsMultiTokensWithReference', () => { + it('should pay 3 ERC20 payments in three different tokens', async () => { + const amount1 = '5000'; + const amount2 = '3000'; + const amount3 = '4000'; + const fee1 = '600'; + const fee2 = '200'; + const fee3 = '300'; + + await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); + await makeTokenApproval(token2, payer, batch.address, getApprovalAmount([amount2], [fee2])); + await makeTokenApproval(token3, payer, batch.address, getApprovalAmount([amount3], [fee3])); + + const payee1Before = await balanceOf(token1, payee1); + const payee2Token2Before = await balanceOf(token2, payee2); + const payee2Token3Before = await balanceOf(token3, payee2); + const feeToken1Before = await balanceOf(token1, feeAddress); + const feeToken2Before = await balanceOf(token2, feeAddress); + const feeToken3Before = await balanceOf(token3, feeAddress); + + await batch.batchERC20PaymentsMultiTokensWithReference( + [token1.address, token2.address, token3.address], + [payee1, payee2, payee2], + [amount1, amount2, amount3], + [REF_A, REF_B, REF_C], + [fee1, fee2, fee3], + feeAddress, + { from: payer }, + ); + + assert.equal(diff(await balanceOf(token1, payee1), payee1Before).toString(), amount1); + assert.equal(diff(await balanceOf(token2, payee2), payee2Token2Before).toString(), amount2); + assert.equal(diff(await balanceOf(token3, payee2), payee2Token3Before).toString(), amount3); + assert.equal(diff(await balanceOf(token1, feeAddress), feeToken1Before).toString(), fee1); + assert.equal(diff(await balanceOf(token2, feeAddress), feeToken2Before).toString(), fee2); + assert.equal(diff(await balanceOf(token3, feeAddress), feeToken3Before).toString(), fee3); + }); + + it('should pay 3 ERC20 payments in three different tokens with a zero amount payment', async () => { + const amount1 = '5000'; + const amount2 = '0'; + const amount3 = '4000'; + const fee1 = '600'; + const fee2 = '0'; + const fee3 = '300'; + + await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); + await makeTokenApproval(token2, payer, batch.address, getApprovalAmount([amount2], [fee2])); + await makeTokenApproval(token3, payer, batch.address, getApprovalAmount([amount3], [fee3])); + + const payee1Before = await balanceOf(token1, payee1); + const payee2Token2Before = await balanceOf(token2, payee2); + const payee2Token3Before = await balanceOf(token3, payee2); + + await batch.batchERC20PaymentsMultiTokensWithReference( + [token1.address, token2.address, token3.address], + [payee1, payee2, payee2], + [amount1, amount2, amount3], + [REF_A, REF_B, REF_C], + [fee1, fee2, fee3], + feeAddress, + { from: payer }, + ); + + assert.equal(diff(await balanceOf(token1, payee1), payee1Before).toString(), amount1); + assert.equal(diff(await balanceOf(token2, payee2), payee2Token2Before).toString(), amount2); + assert.equal(diff(await balanceOf(token3, payee2), payee2Token3Before).toString(), amount3); + }); + + it('should pay 4 ERC20 payments in two different tokens', async () => { + const amount1 = '200'; + const amount2 = '200'; + const amount3 = '200'; + const amount4 = '200'; + const fee1 = '10'; + const fee2 = '10'; + const fee3 = '10'; + const fee4 = '10'; + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1, amount2], [fee1, fee2]), + ); + await makeTokenApproval( + token2, + payer, + batch.address, + getApprovalAmount([amount3, amount4], [fee3, fee4]), + ); + + const payee2Token1Before = await balanceOf(token1, payee2); + const payee2Token2Before = await balanceOf(token2, payee2); + + await batch.batchERC20PaymentsMultiTokensWithReference( + [token1.address, token1.address, token2.address, token2.address], + [payee2, payee2, payee2, payee2], + [amount1, amount2, amount3, amount4], + [REF_A, REF_A, REF_A, REF_A], + [fee1, fee2, fee3, fee4], + feeAddress, + { from: payer }, + ); + + assert.equal( + diff(await balanceOf(token1, payee2), payee2Token1Before).toString(), + sumStrings([amount1, amount2]), + ); + assert.equal( + diff(await balanceOf(token2, payee2), payee2Token2Before).toString(), + sumStrings([amount3, amount4]), + ); + }); + + it('should pay 10 ERC20 payments in two different tokens', async () => { + const amount = '20'; + const feeAmount = '10'; + const nbPaymentsPerToken = 5; + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount( + Array(nbPaymentsPerToken).fill(amount), + Array(nbPaymentsPerToken).fill(feeAmount), + ), + ); + await makeTokenApproval( + token2, + payer, + batch.address, + getApprovalAmount( + Array(nbPaymentsPerToken).fill(amount), + Array(nbPaymentsPerToken).fill(feeAmount), + ), + ); + + const payee1Token1Before = await balanceOf(token1, payee1); + const payee1Token2Before = await balanceOf(token2, payee1); + + await batch.batchERC20PaymentsMultiTokensWithReference( + [ + ...Array(nbPaymentsPerToken).fill(token1.address), + ...Array(nbPaymentsPerToken).fill(token2.address), + ], + Array(nbPaymentsPerToken * 2).fill(payee1), + Array(nbPaymentsPerToken * 2).fill(amount), + Array(nbPaymentsPerToken * 2).fill(REF_A), + Array(nbPaymentsPerToken * 2).fill(feeAmount), + feeAddress, + { from: payer }, + ); + + assert.equal( + diff(await balanceOf(token1, payee1), payee1Token1Before).toString(), + mulString(amount, nbPaymentsPerToken), + ); + assert.equal( + diff(await balanceOf(token2, payee1), payee1Token2Before).toString(), + mulString(amount, nbPaymentsPerToken), + ); + }); + + it('should pay 10 ERC20 payments in two different tokens without fees', async () => { + const amount = '20'; + const feeAmount = '0'; + const nbPaymentsPerToken = 5; + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount( + Array(nbPaymentsPerToken).fill(amount), + Array(nbPaymentsPerToken).fill(feeAmount), + ), + ); + await makeTokenApproval( + token2, + payer, + batch.address, + getApprovalAmount( + Array(nbPaymentsPerToken).fill(amount), + Array(nbPaymentsPerToken).fill(feeAmount), + ), + ); + + const payee1Token1Before = await balanceOf(token1, payee1); + const payee1Token2Before = await balanceOf(token2, payee1); + const feeToken1Before = await balanceOf(token1, feeAddress); + const feeToken2Before = await balanceOf(token2, feeAddress); + + await batch.batchERC20PaymentsMultiTokensWithReference( + [ + ...Array(nbPaymentsPerToken).fill(token1.address), + ...Array(nbPaymentsPerToken).fill(token2.address), + ], + Array(nbPaymentsPerToken * 2).fill(payee1), + Array(nbPaymentsPerToken * 2).fill(amount), + Array(nbPaymentsPerToken * 2).fill(REF_A), + Array(nbPaymentsPerToken * 2).fill(feeAmount), + TRON_ZERO_ADDRESS, + { from: payer }, + ); + await waitForConfirmation(3000); + + assert.equal( + diff(await balanceOf(token1, payee1), payee1Token1Before).toString(), + mulString(amount, nbPaymentsPerToken), + ); + assert.equal( + diff(await balanceOf(token2, payee1), payee1Token2Before).toString(), + mulString(amount, nbPaymentsPerToken), + ); + assert.equal( + diff(await balanceOf(token1, feeAddress), feeToken1Before).toString(), + feeAmount, + ); + assert.equal( + diff(await balanceOf(token2, feeAddress), feeToken2Before).toString(), + feeAmount, + ); + }); + + it('should leave no token balance on the batch contract after a successful payment', async () => { + const amount1 = '100'; + const amount2 = '200'; + const fee1 = '10'; + const fee2 = '20'; + + await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); + await makeTokenApproval(token2, payer, batch.address, getApprovalAmount([amount2], [fee2])); + + await batch.batchERC20PaymentsMultiTokensWithReference( + [token1.address, token2.address], + [payee1, payee2], + [amount1, amount2], + [REF_A, REF_B], + [fee1, fee2], + feeAddress, + { from: payer }, + ); + await waitForConfirmation(3000); + + await assertBatchTokenBalancesZero(batch, [token1, token2]); + }); + + it('should pay a multi-token ERC20 payment with BadTRC20', async () => { + const badToken = await deployBadTRC20(payer); + const paymentAmount = '100'; + const feeAmount = '10'; + const amount1 = '50'; + const fee1 = '5'; + + try { + await badToken.approve(batch.address, getApprovalAmount([paymentAmount], [feeAmount]), { + from: payer, + }); + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1], [fee1]), + ); + await waitForConfirmation(3000); + + const badPayeeBefore = await balanceOf(badToken, payee1); + const payee1Before = await balanceOf(token1, payee2); + + await batch.batchERC20PaymentsMultiTokensWithReference( + [badToken.address, token1.address], + [payee1, payee2], + [paymentAmount, amount1], + [REF_A, REF_B], + [feeAmount, fee1], + feeAddress, + { from: payer }, + ); + await waitForConfirmation(3000); + + const badPayeeAfter = await balanceOf(badToken, payee1); + const payee1After = await balanceOf(token1, payee2); + assert( + badPayeeAfter > badPayeeBefore || payee1After > payee1Before, + 'BadTRC20 multi-token: at least one payee balance should increase when batch succeeds', + ); + } catch (_error) { + console.log( + 'BadTRC20 multi-token batch payment rejected by Tron (acceptable for non-standard tokens)', + ); + } + }); + }); + }); + + describe('Error cases scenarios', () => { + describe('batchERC20PaymentsWithReference', () => { + it('should revert when the payer does not have enough funds to pay', async () => { + const amount1 = '5'; + const amount2 = '30'; + const amount3 = '400'; + const fee1 = '1'; + const fee2 = '2'; + const fee3 = '3'; + + const lowToken = await deployTokenWithSupply('100', payer); + await makeTokenApproval( + lowToken, + payer, + batch.address, + getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3]), + ); + + const payee3Before = await balanceOf(lowToken, payee3); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsWithReference( + lowToken.address, + [payee1, payee2, payee3], + [amount1, amount2, amount3], + [REF_A, REF_B, REF_C], + [fee1, fee2, fee3], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(lowToken, payee3)], + ); + + assert(unchanged, 'should not transfer when funds insufficient'); + assert.equal((await balanceOf(lowToken, payee3)).toString(), payee3Before.toString()); + }); + + it('should revert when the payer does not have enough funds to pay the fees', async () => { + const amount1 = '100'; + const amount2 = '200'; + const fee1 = '50'; + const fee2 = '50'; + + const lowToken = await deployTokenWithSupply('300', payer); + await makeTokenApproval( + lowToken, + payer, + batch.address, + getApprovalAmount([amount1, amount2], [fee1, fee2]), + ); + + const payee1Before = await balanceOf(lowToken, payee1); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsWithReference( + lowToken.address, + [payee1, payee2], + [amount1, amount2], + [REF_A, REF_B], + [fee1, fee2], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(lowToken, payee1)], + ); + + assert(unchanged, 'should not transfer when fees cannot be paid'); + assert.equal((await balanceOf(lowToken, payee1)).toString(), payee1Before.toString()); + }); + + it('should revert when the payer did not approve the batch contract to spend the tokens', async () => { + const amount1 = '20'; + const amount2 = '30'; + const amount3 = '40'; + const fee1 = '1'; + const fee2 = '2'; + const fee3 = '3'; + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3]), + ); + await token1.approve(batch.address, '10', { from: payer }); + await waitForConfirmation(2000); + + const payee1Before = await balanceOf(token1, payee1); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsWithReference( + token1.address, + [payee1, payee2, payee3], + [amount1, amount2, amount3], + [REF_A, REF_B, REF_C], + [fee1, fee2, fee3], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(token1, payee1)], + ); + + assert(unchanged, 'should not transfer without allowance'); + assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); + }); + + it('should revert when input arrays have different lengths', async () => { + const amount1 = '100'; + const fee1 = '1'; + const fee2 = '2'; + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1], [fee1, fee2]), + ); + + const payee1Before = await balanceOf(token1, payee1); + const payee2Before = await balanceOf(token1, payee2); + + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsWithReference( + token1.address, + [payee1, payee2], + [amount1], + [REF_A, REF_B], + [fee1, fee2], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(token1, payee1), await balanceOf(token1, payee2)], + ); + + assert(unchanged, 'should not transfer when array lengths mismatch'); + assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); + assert.equal((await balanceOf(token1, payee2)).toString(), payee2Before.toString()); + }); + }); + + describe('batchERC20PaymentsMultiTokensWithReference', () => { + it('should revert when the payer does not have enough funds to pay in at least one of the tokens', async () => { + const amount1 = '5'; + const amount2 = '30'; + const amount3 = '400'; + const fee1 = '1'; + const fee2 = '2'; + const fee3 = '3'; + + const lowToken = await deployTokenWithSupply('400', payer); + await makeTokenApproval( + lowToken, + payer, + batch.address, + getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3]), + ); + + const payee3Before = await balanceOf(lowToken, payee3); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsMultiTokensWithReference( + [lowToken.address, lowToken.address, lowToken.address], + [payee1, payee2, payee3], + [amount1, amount2, amount3], + [REF_A, REF_B, REF_C], + [fee1, fee2, fee3], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(lowToken, payee3)], + ); + + assert(unchanged, 'multi-token batch should not transfer when funds insufficient'); + assert.equal((await balanceOf(lowToken, payee3)).toString(), payee3Before.toString()); + }); + + it('should revert when the payer did not approve the batch contract to spend the tokens in at least one of the tokens', async () => { + const amount1 = '100'; + const amount2 = '200'; + const amount3 = '300'; + const fee1 = '1'; + const fee2 = '2'; + const fee3 = '3'; + + await makeTokenApproval( + token1, + payer, + batch.address, + getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3]), + ); + await token1.approve(batch.address, '10', { from: payer }); + await waitForConfirmation(2000); + + const payee1Before = await balanceOf(token1, payee1); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsMultiTokensWithReference( + [token1.address, token1.address, token1.address], + [payee1, payee2, payee3], + [amount1, amount2, amount3], + [REF_A, REF_B, REF_C], + [fee1, fee2, fee3], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(token1, payee1)], + ); + + assert(unchanged, 'multi-token batch should not transfer without allowance'); + assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); + }); + + it('should revert when the payer did not approve the batch contract for one of the tokens', async () => { + const amount1 = '100'; + const amount2 = '200'; + const fee1 = '1'; + const fee2 = '2'; + + await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); + + const payee2Token2Before = await balanceOf(token2, payee2); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsMultiTokensWithReference( + [token1.address, token2.address], + [payee1, payee2], + [amount1, amount2], + [REF_A, REF_B], + [fee1, fee2], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(token2, payee2)], + ); + + assert(unchanged, 'should not transfer when one token lacks approval'); + assert.equal((await balanceOf(token2, payee2)).toString(), payee2Token2Before.toString()); + }); + + it('should revert when input arrays have different lengths', async () => { + const amount1 = '100'; + const fee1 = '1'; + const fee2 = '2'; + + await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); + await makeTokenApproval(token2, payer, batch.address, getApprovalAmount([amount1], [fee2])); + + const payee1Before = await balanceOf(token1, payee1); + const payee2Before = await balanceOf(token2, payee2); + + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsMultiTokensWithReference( + [token1.address, token2.address], + [payee1, payee2], + [amount1], + [REF_A, REF_B], + [fee1, fee2], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(token1, payee1), await balanceOf(token2, payee2)], + ); + + assert(unchanged, 'should not transfer when array lengths mismatch'); + assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); + assert.equal((await balanceOf(token2, payee2)).toString(), payee2Before.toString()); + }); + }); + }); + + describe('Admin', () => { + describe('setPaymentErc20FeeProxy', () => { + it('should allow owner to update proxy addresses', async () => { + const ERC20FeeProxy = artifacts.require('ERC20FeeProxy'); + const newProxy = await ERC20FeeProxy.new(); + await batch.setPaymentErc20FeeProxy(newProxy.address, { from: payer }); + assert.equal(await batch.paymentErc20FeeProxy(), newProxy.address); + }); + + it('should revert when a non-owner tries to update proxy addresses', async () => { + await expectNonOwnerReverts( + () => batch.setPaymentErc20FeeProxy(payee1, { from: payee1 }), + async () => await batch.paymentErc20FeeProxy(), + ); + }); + }); + }); +}); diff --git a/packages/smart-contracts/test/tron/helpers.js b/packages/smart-contracts/test/tron/helpers.js new file mode 100644 index 0000000000..9efc5959e2 --- /dev/null +++ b/packages/smart-contracts/test/tron/helpers.js @@ -0,0 +1,155 @@ +const INITIAL_SUPPLY = '10000000000'; + +const REF_A = '0xaaaa'; +const REF_B = '0xbbbb'; +const REF_C = '0xcccc'; + +/** Tron base58 zero address (unset EthFeeProxy on Tron deployments). */ +const TRON_ZERO_ADDRESS = '410000000000000000000000000000000000000000'; + +/** 1 TRX = 1_000_000 sun on Tron. */ +const ONE_TRX_SUN = 1_000_000; + +const waitForConfirmation = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + +const balanceOf = async (token, account) => { + const value = await token.balanceOf(account); + return BigInt(value.toString()); +}; + +const trxBalance = async (address) => { + const balance = await tronWeb.trx.getBalance(address); + return BigInt(balance); +}; + +const diff = (after, before) => after - before; + +const sumStrings = (values) => values.reduce((acc, value) => acc + BigInt(value), 0n).toString(); + +const mulString = (value, count) => (BigInt(value) * BigInt(count)).toString(); + +const computeBatchFee = (totalPaymentAmount, bps) => + ((BigInt(totalPaymentAmount) * BigInt(bps)) / 1000n).toString(); + +const getApprovalAmount = (amountList, feeList, batchFee = '0') => + sumStrings([...amountList, ...feeList, batchFee]); + +/** + * Deploy ERC20FeeProxy, optional batch contract, and one or more TestTRC20 tokens. + */ +const deployBaseSetup = async ({ accounts, batchDeployFn, batchFee, tokenCount = 3 }) => { + const ERC20FeeProxy = artifacts.require('ERC20FeeProxy'); + const TestTRC20 = artifacts.require('TestTRC20'); + + const owner = accounts[0]; + const erc20FeeProxy = await ERC20FeeProxy.new(); + const dummyEthProxy = TRON_ZERO_ADDRESS; + + let batch = null; + if (batchDeployFn) { + batch = await batchDeployFn(erc20FeeProxy, owner, dummyEthProxy); + if (batchFee !== undefined && batch.setBatchFee) { + await batch.setBatchFee(batchFee, { from: owner }); + } + } + + const tokens = []; + for (let i = 0; i < tokenCount; i++) { + const token = await TestTRC20.new(INITIAL_SUPPLY, `Test TRC20 ${i + 1}`, `TT${i + 1}`, 18); + tokens.push(token); + } + + return { erc20FeeProxy, batch, tokens, dummyEthProxy }; +}; + +/** + * Approve contract to spend payer tokens. + */ +const makeTokenApproval = async (token, payer, batchAddress, amount) => { + await token.approve(batchAddress, amount, { from: payer }); + await waitForConfirmation(2000); +}; + +/** + * Deploy a TestTRC20 with a specific initial supply assigned to payer. + */ +const deployTokenWithSupply = async (supply, payer) => { + const TestTRC20 = artifacts.require('TestTRC20'); + return TestTRC20.new(supply, 'Test TRC20', 'TTRC', 18, { from: payer }); +}; + +/** + * Runs fn and asserts tracked balances are unchanged (source of truth Tron when Tron tx reverts). + */ +const expectRevertOrNoBalanceChange = async (fn, getBalances) => { + const before = await getBalances(); + try { + await fn(); + } catch (_error) {} + await waitForConfirmation(2000); + const after = await getBalances(); + const unchanged = before.every((value, index) => value === after[index]); + return { unchanged }; +}; + +/** + * Asserts the batch contract holds zero balance for each token. + */ +const assertBatchTokenBalancesZero = async (batch, tokens) => { + for (const token of tokens) { + const bal = await balanceOf(token, batch.address); + assert.equal(bal.toString(), '0', `batch should have zero token balance for ${token.address}`); + } +}; + +/** + * Expects fn to revert; optionally asserts getState() is unchanged. + */ +const expectNonOwnerReverts = async (fn, getState) => { + const before = await getState(); + let threw = false; + try { + await fn(); + } catch (_error) { + threw = true; + } + await waitForConfirmation(2000); + assert(threw, 'expected non-owner call to revert'); + if (getState) { + const after = await getState(); + assert.equal(after, before, 'state should be unchanged after failed non-owner call'); + } + return { reverted: threw }; +}; + +/** + * Deploy BadTRC20 with migration-style constructor args. + */ +const deployBadTRC20 = async (payer) => { + const BadTRC20 = artifacts.require('BadTRC20'); + return BadTRC20.new('1000000000000', 'BadTRC20', 'BAD', 8, { from: payer }); +}; + +module.exports = { + INITIAL_SUPPLY, + REF_A, + REF_B, + REF_C, + TRON_ZERO_ADDRESS, + ONE_TRX_SUN, + waitForConfirmation, + balanceOf, + trxBalance, + diff, + sumStrings, + mulString, + computeBatchFee, + getApprovalAmount, + deployBaseSetup, + makeTokenApproval, + deployTokenWithSupply, + expectRevertOrNoBalanceChange, + assertBatchTokenBalancesZero, + expectNonOwnerReverts, + deployBadTRC20, +}; diff --git a/packages/smart-contracts/tron/contracts/BatchPayments.sol b/packages/smart-contracts/tron/contracts/BatchPayments.sol new file mode 120000 index 0000000000..5f16a2c12b --- /dev/null +++ b/packages/smart-contracts/tron/contracts/BatchPayments.sol @@ -0,0 +1 @@ +../../src/contracts/BatchPayments.sol \ No newline at end of file diff --git a/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol b/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol new file mode 100644 index 0000000000..e8275cc2f5 --- /dev/null +++ b/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import '@openzeppelin/contracts/access/Ownable.sol'; +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import './interfaces/ERC20FeeProxy.sol'; +import './lib/SafeERC20.sol'; + +/** + * @title BatchPaymentsTronSimplified + * @notice Tron-only batch contract that routes each payment through ERC20FeeProxy. + * If one payment fails, the whole batch reverts. + * @dev Uses ERC20FeeProxy to pay an invoice and fees, with a payment reference. + * Make sure this contract has allowance to spend the payer's tokens. + * Make sure the payer has enough tokens to pay the amounts and fees. + */ +contract BatchPaymentsTronSimplified is Ownable { + using SafeERC20 for IERC20; + + IERC20FeeProxy public paymentErc20FeeProxy; + + struct Token { + address tokenAddress; + uint256 amountAndFee; + } + + /** + * @param _paymentErc20FeeProxy The address of the ERC20FeeProxy to use. + * @param _owner Owner of the contract. + */ + constructor(address _paymentErc20FeeProxy, address _owner) { + paymentErc20FeeProxy = IERC20FeeProxy(_paymentErc20FeeProxy); + transferOwnership(_owner); + } + + /** + * @notice Send a batch of ERC20 payments with fees and payment references to multiple accounts. + * @param _tokenAddress Token to transact with. + * @param _recipients List of recipient accounts. + * @param _amounts List of amounts, corresponding to recipients[]. + * @param _paymentReferences List of payment references, corresponding to recipients[]. + * @param _feeAmounts List of fee amounts, corresponding to recipients[]. + * @param _feeAddress The fee recipient. + */ + function batchERC20PaymentsWithReference( + address _tokenAddress, + address[] calldata _recipients, + uint256[] calldata _amounts, + bytes[] calldata _paymentReferences, + uint256[] calldata _feeAmounts, + address _feeAddress + ) external { + require( + _recipients.length == _amounts.length && + _recipients.length == _paymentReferences.length && + _recipients.length == _feeAmounts.length, + 'the input arrays must have the same length' + ); + + uint256 amountAndFee = 0; + for (uint256 i = 0; i < _recipients.length; i++) { + amountAndFee += _amounts[i] + _feeAmounts[i]; + } + + _transferToContractAndApproveProxy(IERC20(_tokenAddress), amountAndFee); + + for (uint256 i = 0; i < _recipients.length; i++) { + paymentErc20FeeProxy.transferFromWithReferenceAndFee( + _tokenAddress, + _recipients[i], + _amounts[i], + _paymentReferences[i], + _feeAmounts[i], + _feeAddress + ); + } + } + + /** + * @notice Send a batch of ERC20 payments on multiple tokens with fees and payment references. + * @param _tokenAddresses List of tokens to transact with. + * @param _recipients List of recipient accounts. + * @param _amounts List of amounts, corresponding to recipients[]. + * @param _paymentReferences List of payment references, corresponding to recipients[]. + * @param _feeAmounts List of fee amounts, corresponding to recipients[]. + * @param _feeAddress The fee recipient. + */ + function batchERC20PaymentsMultiTokensWithReference( + address[] calldata _tokenAddresses, + address[] calldata _recipients, + uint256[] calldata _amounts, + bytes[] calldata _paymentReferences, + uint256[] calldata _feeAmounts, + address _feeAddress + ) external { + require( + _tokenAddresses.length == _recipients.length && + _tokenAddresses.length == _amounts.length && + _tokenAddresses.length == _paymentReferences.length && + _tokenAddresses.length == _feeAmounts.length, + 'the input arrays must have the same length' + ); + + Token[] memory uniqueTokens = new Token[](_tokenAddresses.length); + for (uint256 i = 0; i < _tokenAddresses.length; i++) { + for (uint256 j = 0; j < _tokenAddresses.length; j++) { + if (uniqueTokens[j].tokenAddress == _tokenAddresses[i]) { + uniqueTokens[j].amountAndFee += _amounts[i] + _feeAmounts[i]; + break; + } + if (uniqueTokens[j].amountAndFee == 0 && (_amounts[i] + _feeAmounts[i]) > 0) { + uniqueTokens[j].tokenAddress = _tokenAddresses[i]; + uniqueTokens[j].amountAndFee = _amounts[i] + _feeAmounts[i]; + break; + } + } + } + + for (uint256 i = 0; i < uniqueTokens.length && uniqueTokens[i].amountAndFee > 0; i++) { + _transferToContractAndApproveProxy( + IERC20(uniqueTokens[i].tokenAddress), + uniqueTokens[i].amountAndFee + ); + } + + for (uint256 i = 0; i < _recipients.length; i++) { + paymentErc20FeeProxy.transferFromWithReferenceAndFee( + _tokenAddresses[i], + _recipients[i], + _amounts[i], + _paymentReferences[i], + _feeAmounts[i], + _feeAddress + ); + } + } + + /** + * @notice Authorizes the proxy to spend a request currency (ERC20). + * @param _erc20Address Address of an ERC20 used as the request currency. + */ + function approvePaymentProxyToSpend(address _erc20Address) public { + IERC20 erc20 = IERC20(_erc20Address); + uint256 max = type(uint256).max; + require(erc20.safeApprove(address(paymentErc20FeeProxy), max), 'approve() failed'); + } + + /** + * @notice Updates the ERC20FeeProxy address. + * @param _paymentErc20FeeProxy The address of the ERC20FeeProxy to use. + */ + function setPaymentErc20FeeProxy(address _paymentErc20FeeProxy) public onlyOwner { + paymentErc20FeeProxy = IERC20FeeProxy(_paymentErc20FeeProxy); + } + + /** + * @notice Pulls tokens from the payer to this contract and approves the proxy to spend them. + * @param requestedToken The token to pay. + * @param amountAndFee The sum of payment amounts and fees for this token. + */ + function _transferToContractAndApproveProxy( + IERC20 requestedToken, + uint256 amountAndFee + ) internal { + require( + requestedToken.allowance(msg.sender, address(this)) >= amountAndFee, + 'Not sufficient allowance for batch to pay' + ); + require(requestedToken.balanceOf(msg.sender) >= amountAndFee, 'not enough funds'); + require( + requestedToken.safeTransferFrom(msg.sender, address(this), amountAndFee), + 'payment transferFrom() failed' + ); + + if (requestedToken.allowance(address(this), address(paymentErc20FeeProxy)) < amountAndFee) { + approvePaymentProxyToSpend(address(requestedToken)); + } + } +} diff --git a/packages/smart-contracts/tron/contracts/interfaces/ERC20FeeProxy.sol b/packages/smart-contracts/tron/contracts/interfaces/ERC20FeeProxy.sol new file mode 120000 index 0000000000..88ec30138c --- /dev/null +++ b/packages/smart-contracts/tron/contracts/interfaces/ERC20FeeProxy.sol @@ -0,0 +1 @@ +../../../src/contracts/interfaces/ERC20FeeProxy.sol \ No newline at end of file diff --git a/packages/smart-contracts/tron/contracts/interfaces/EthereumFeeProxy.sol b/packages/smart-contracts/tron/contracts/interfaces/EthereumFeeProxy.sol new file mode 120000 index 0000000000..2ba444a117 --- /dev/null +++ b/packages/smart-contracts/tron/contracts/interfaces/EthereumFeeProxy.sol @@ -0,0 +1 @@ +../../../src/contracts/interfaces/EthereumFeeProxy.sol \ No newline at end of file diff --git a/packages/smart-contracts/tron/contracts/lib/SafeERC20.sol b/packages/smart-contracts/tron/contracts/lib/SafeERC20.sol new file mode 120000 index 0000000000..4003968ec2 --- /dev/null +++ b/packages/smart-contracts/tron/contracts/lib/SafeERC20.sol @@ -0,0 +1 @@ +../../../src/contracts/lib/SafeERC20.sol \ No newline at end of file From d356b3c34b4ec3fad7fadba24e6c7bc330a8e354 Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Wed, 20 May 2026 13:02:52 +0200 Subject: [PATCH 02/19] fix: contract name --- packages/smart-contracts/test/tron/ERC20BatchPayments.test.js | 2 +- .../smart-contracts/tron/contracts/ERC20BatchPayments.sol | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js b/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js index 0c1d8b900e..43c3709698 100644 --- a/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js +++ b/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js @@ -19,7 +19,7 @@ const { getApprovalAmount, } = require('./helpers'); -contract('BatchPaymentsTronSimplified Tron Test Suite', (accounts) => { +contract('ERC20BatchPayments Tron Test Suite', (accounts) => { const payer = accounts[0]; const payee1 = accounts[1] || 'TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE'; const payee2 = accounts[2] || 'TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs'; diff --git a/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol b/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol index e8275cc2f5..0d1b85fcba 100644 --- a/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol +++ b/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol @@ -7,14 +7,14 @@ import './interfaces/ERC20FeeProxy.sol'; import './lib/SafeERC20.sol'; /** - * @title BatchPaymentsTronSimplified + * @title ERC20BatchPayments * @notice Tron-only batch contract that routes each payment through ERC20FeeProxy. * If one payment fails, the whole batch reverts. * @dev Uses ERC20FeeProxy to pay an invoice and fees, with a payment reference. * Make sure this contract has allowance to spend the payer's tokens. * Make sure the payer has enough tokens to pay the amounts and fees. */ -contract BatchPaymentsTronSimplified is Ownable { +contract ERC20BatchPayments is Ownable { using SafeERC20 for IERC20; IERC20FeeProxy public paymentErc20FeeProxy; From 2d355772a2315b1c0cf42fcb3b7d6739b11b09f6 Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Wed, 20 May 2026 13:36:30 +0200 Subject: [PATCH 03/19] fix: imports & naming --- .../smart-contracts/test/tron/ERC20BatchPayments.test.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js b/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js index 43c3709698..866a6d20a3 100644 --- a/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js +++ b/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js @@ -1,4 +1,4 @@ -const BatchPaymentsTronSimplified = artifacts.require('BatchPaymentsTronSimplified'); +const ERC20BatchPayments = artifacts.require('ERC20BatchPayments'); const { REF_A, REF_B, @@ -34,13 +34,12 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { before(async () => { const setup = await deployBaseSetup({ accounts, - batchDeployFn: (erc20FeeProxy, owner) => - BatchPaymentsTronSimplified.new(erc20FeeProxy.address, owner), + batchDeployFn: (erc20FeeProxy, owner) => ERC20BatchPayments.new(erc20FeeProxy.address, owner), }); batch = setup.batch; [token1, token2, token3] = setup.tokens; - console.log('\n=== BatchPaymentsTronSimplified Test Setup ==='); + console.log('\n=== ERC20BatchPayments Test Setup ==='); console.log('Batch:', batch.address); await waitForConfirmation(3000); }); From 93e29c9710c08c1b5e031b89fb51c157c3fae1aa Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Wed, 20 May 2026 14:06:49 +0200 Subject: [PATCH 04/19] fix: expectNonOwnerReverts --- packages/smart-contracts/test/tron/helpers.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/smart-contracts/test/tron/helpers.js b/packages/smart-contracts/test/tron/helpers.js index 9efc5959e2..2ba73b6920 100644 --- a/packages/smart-contracts/test/tron/helpers.js +++ b/packages/smart-contracts/test/tron/helpers.js @@ -107,14 +107,11 @@ const assertBatchTokenBalancesZero = async (batch, tokens) => { */ const expectNonOwnerReverts = async (fn, getState) => { const before = await getState(); - let threw = false; try { await fn(); - } catch (_error) { - threw = true; - } + } catch (_error) {} await waitForConfirmation(2000); - assert(threw, 'expected non-owner call to revert'); + if (getState) { const after = await getState(); assert.equal(after, before, 'state should be unchanged after failed non-owner call'); From 92b7657438c30d76a0fe77320687b06577eb5348 Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Wed, 20 May 2026 14:43:33 +0200 Subject: [PATCH 05/19] fix: remove return statement --- packages/smart-contracts/test/tron/helpers.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/smart-contracts/test/tron/helpers.js b/packages/smart-contracts/test/tron/helpers.js index 2ba73b6920..3fd05d4242 100644 --- a/packages/smart-contracts/test/tron/helpers.js +++ b/packages/smart-contracts/test/tron/helpers.js @@ -112,11 +112,8 @@ const expectNonOwnerReverts = async (fn, getState) => { } catch (_error) {} await waitForConfirmation(2000); - if (getState) { - const after = await getState(); - assert.equal(after, before, 'state should be unchanged after failed non-owner call'); - } - return { reverted: threw }; + const after = await getState(); + assert.equal(after, before, 'state should be unchanged after failed non-owner call'); }; /** From b91110487e047dd64677aaa6d341f7ac59930f37 Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Thu, 21 May 2026 20:37:09 +0200 Subject: [PATCH 06/19] chore: remove contract ownership --- .../test/tron/ERC20BatchPayments.test.js | 21 +------------------ .../tron/contracts/ERC20BatchPayments.sol | 15 ++----------- 2 files changed, 3 insertions(+), 33 deletions(-) diff --git a/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js b/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js index 866a6d20a3..a57ab68f68 100644 --- a/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js +++ b/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js @@ -12,7 +12,6 @@ const { deployTokenWithSupply, expectRevertOrNoBalanceChange, assertBatchTokenBalancesZero, - expectNonOwnerReverts, deployBadTRC20, sumStrings, mulString, @@ -34,7 +33,7 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { before(async () => { const setup = await deployBaseSetup({ accounts, - batchDeployFn: (erc20FeeProxy, owner) => ERC20BatchPayments.new(erc20FeeProxy.address, owner), + batchDeployFn: (erc20FeeProxy) => ERC20BatchPayments.new(erc20FeeProxy.address), }); batch = setup.batch; [token1, token2, token3] = setup.tokens; @@ -750,22 +749,4 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { }); }); }); - - describe('Admin', () => { - describe('setPaymentErc20FeeProxy', () => { - it('should allow owner to update proxy addresses', async () => { - const ERC20FeeProxy = artifacts.require('ERC20FeeProxy'); - const newProxy = await ERC20FeeProxy.new(); - await batch.setPaymentErc20FeeProxy(newProxy.address, { from: payer }); - assert.equal(await batch.paymentErc20FeeProxy(), newProxy.address); - }); - - it('should revert when a non-owner tries to update proxy addresses', async () => { - await expectNonOwnerReverts( - () => batch.setPaymentErc20FeeProxy(payee1, { from: payee1 }), - async () => await batch.paymentErc20FeeProxy(), - ); - }); - }); - }); }); diff --git a/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol b/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol index 0d1b85fcba..68e788d9a8 100644 --- a/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol +++ b/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import '@openzeppelin/contracts/access/Ownable.sol'; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import './interfaces/ERC20FeeProxy.sol'; import './lib/SafeERC20.sol'; @@ -14,7 +13,7 @@ import './lib/SafeERC20.sol'; * Make sure this contract has allowance to spend the payer's tokens. * Make sure the payer has enough tokens to pay the amounts and fees. */ -contract ERC20BatchPayments is Ownable { +contract ERC20BatchPayments { using SafeERC20 for IERC20; IERC20FeeProxy public paymentErc20FeeProxy; @@ -26,11 +25,9 @@ contract ERC20BatchPayments is Ownable { /** * @param _paymentErc20FeeProxy The address of the ERC20FeeProxy to use. - * @param _owner Owner of the contract. */ - constructor(address _paymentErc20FeeProxy, address _owner) { + constructor(address _paymentErc20FeeProxy) { paymentErc20FeeProxy = IERC20FeeProxy(_paymentErc20FeeProxy); - transferOwnership(_owner); } /** @@ -145,14 +142,6 @@ contract ERC20BatchPayments is Ownable { require(erc20.safeApprove(address(paymentErc20FeeProxy), max), 'approve() failed'); } - /** - * @notice Updates the ERC20FeeProxy address. - * @param _paymentErc20FeeProxy The address of the ERC20FeeProxy to use. - */ - function setPaymentErc20FeeProxy(address _paymentErc20FeeProxy) public onlyOwner { - paymentErc20FeeProxy = IERC20FeeProxy(_paymentErc20FeeProxy); - } - /** * @notice Pulls tokens from the payer to this contract and approves the proxy to spend them. * @param requestedToken The token to pay. From 9822c99e8432c5e2f55457678dfbe81ca9b87d5f Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Tue, 26 May 2026 19:28:13 +0200 Subject: [PATCH 07/19] chore: remove BatchPayments contract --- .../test/tron/BatchPayments.test.js | 942 ------------------ .../tron/contracts/BatchPayments.sol | 1 - 2 files changed, 943 deletions(-) delete mode 100644 packages/smart-contracts/test/tron/BatchPayments.test.js delete mode 120000 packages/smart-contracts/tron/contracts/BatchPayments.sol diff --git a/packages/smart-contracts/test/tron/BatchPayments.test.js b/packages/smart-contracts/test/tron/BatchPayments.test.js deleted file mode 100644 index a024230ad8..0000000000 --- a/packages/smart-contracts/test/tron/BatchPayments.test.js +++ /dev/null @@ -1,942 +0,0 @@ -const BatchPayments = artifacts.require('BatchPayments'); -const { - REF_A, - REF_B, - REF_C, - waitForConfirmation, - balanceOf, - diff, - deployBaseSetup, - makeTokenApproval, - deployTokenWithSupply, - expectRevertOrNoBalanceChange, - assertBatchTokenBalancesZero, - expectNonOwnerReverts, - deployBadTRC20, - sumStrings, - mulString, - computeBatchFee, - getApprovalAmount, - trxBalance, - ONE_TRX_SUN, - TRON_ZERO_ADDRESS, -} = require('./helpers'); - -contract('BatchPayments Tron Test Suite', (accounts) => { - const payer = accounts[0]; - const payee1 = accounts[1] || 'TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE'; - const payee2 = accounts[2] || 'TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs'; - const payee3 = accounts[3] || 'TFwt56qg984vEmk2UoDqUDeZhWEFSDaTmk'; - const feeAddress = accounts[4] || 'TNPGB28MjVCnEhTfpW51C2Ap3ZNnqGDXLB'; - - const BATCH_FEE_BPS = 10; - - let batch; - let token1; - let token2; - let token3; - - before(async () => { - const setup = await deployBaseSetup({ - accounts, - batchDeployFn: (erc20FeeProxy, owner, ethProxy) => - BatchPayments.new(erc20FeeProxy.address, ethProxy, owner), - batchFee: BATCH_FEE_BPS, - }); - batch = setup.batch; - [token1, token2, token3] = setup.tokens; - - console.log('\n=== BatchPayments (main) Test Setup ==='); - console.log('Batch:', batch.address); - console.log('Token1:', token1.address); - await waitForConfirmation(3000); - }); - - beforeEach(async () => { - await waitForConfirmation(2000); - }); - - describe('Happy Path Payment Scenarios', () => { - describe('batchERC20PaymentsWithReference', () => { - it('should pay 3 ERC20 payments', async () => { - const amount1 = '2000'; - const amount2 = '300'; - const amount3 = '400'; - const fee1 = '200'; - const fee2 = '20'; - const fee3 = '30'; - - const batchFee = computeBatchFee(sumStrings([amount1, amount2, amount3]), BATCH_FEE_BPS); - const totalPaymentAndFees = sumStrings([ - amount1, - amount2, - amount3, - fee1, - fee2, - fee3, - batchFee, - ]); - - await makeTokenApproval( - token1, - payer, - batch.address, - getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3], batchFee), - ); - - const payee1Before = await balanceOf(token1, payee1); - const payee2Before = await balanceOf(token1, payee2); - const payerBefore = await balanceOf(token1, payer); - const feeBefore = await balanceOf(token1, feeAddress); - - await batch.batchERC20PaymentsWithReference( - token1.address, - [payee1, payee2, payee2], - [amount1, amount2, amount3], - [REF_A, REF_B, REF_C], - [fee1, fee2, fee3], - feeAddress, - { from: payer }, - ); - await waitForConfirmation(3000); - - const payee1After = await balanceOf(token1, payee1); - const payee2After = await balanceOf(token1, payee2); - const payerAfter = await balanceOf(token1, payer); - const feeAfter = await balanceOf(token1, feeAddress); - - assert.equal(diff(payee1After, payee1Before).toString(), amount1); - assert.equal(diff(payee2After, payee2Before).toString(), sumStrings([amount2, amount3])); - assert.equal( - diff(feeAfter, feeBefore).toString(), - sumStrings([fee1, fee2, fee3, batchFee]), - ); - assert( - diff(payerBefore, payerAfter) >= BigInt(totalPaymentAndFees), - 'payer should pay amounts, fees, and batch fee', - ); - }); - - it('should pay 10 ERC20 payments', async () => { - const amount = '200'; - const feeAmount = '100'; - const nbTxs = 10; - - const batchFeeTotal = computeBatchFee(mulString(amount, nbTxs), BATCH_FEE_BPS); - - await makeTokenApproval( - token1, - payer, - batch.address, - getApprovalAmount(Array(nbTxs).fill(amount), Array(nbTxs).fill(feeAmount), batchFeeTotal), - ); - - const payee1Before = await balanceOf(token1, payee1); - const feeBefore = await balanceOf(token1, feeAddress); - - await batch.batchERC20PaymentsWithReference( - token1.address, - Array(nbTxs).fill(payee1), - Array(nbTxs).fill(amount), - Array(nbTxs).fill(REF_A), - Array(nbTxs).fill(feeAmount), - feeAddress, - { from: payer }, - ); - await waitForConfirmation(3000); - - assert.equal( - diff(await balanceOf(token1, payee1), payee1Before).toString(), - mulString(amount, nbTxs), - ); - assert.equal( - diff(await balanceOf(token1, feeAddress), feeBefore).toString(), - sumStrings([mulString(feeAmount, nbTxs), batchFeeTotal]), - ); - }); - - it('should leave no token balance on the batch contract after a successful payment', async () => { - const amount1 = '100'; - const fee1 = '10'; - - await makeTokenApproval( - token1, - payer, - batch.address, - getApprovalAmount([amount1], [fee1], computeBatchFee(amount1, BATCH_FEE_BPS)), - ); - - await batch.batchERC20PaymentsWithReference( - token1.address, - [payee1], - [amount1], - [REF_A], - [fee1], - feeAddress, - { from: payer }, - ); - await waitForConfirmation(3000); - - await assertBatchTokenBalancesZero(batch, [token1]); - }); - - it('should apply an updated batch fee on the next payment', async () => { - const newBatchFeeBps = 50; - await batch.setBatchFee(newBatchFeeBps, { from: payer }); - - const amount1 = '1000'; - const fee1 = '10'; - const batchFee = computeBatchFee(amount1, newBatchFeeBps); - - await makeTokenApproval( - token1, - payer, - batch.address, - getApprovalAmount([amount1], [fee1], batchFee), - ); - - const feeBefore = await balanceOf(token1, feeAddress); - - await batch.batchERC20PaymentsWithReference( - token1.address, - [payee1], - [amount1], - [REF_A], - [fee1], - feeAddress, - { from: payer }, - ); - await waitForConfirmation(3000); - - assert.equal( - diff(await balanceOf(token1, feeAddress), feeBefore).toString(), - sumStrings([fee1, batchFee]), - ); - - await batch.setBatchFee(BATCH_FEE_BPS, { from: payer }); - }); - - it('should pay ERC20 payments with no batch fee when batch fee is zero', async () => { - await batch.setBatchFee(0, { from: payer }); - - const amount1 = '500'; - const fee1 = '25'; - - await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); - - const feeBefore = await balanceOf(token1, feeAddress); - - await batch.batchERC20PaymentsWithReference( - token1.address, - [payee1], - [amount1], - [REF_A], - [fee1], - feeAddress, - { from: payer }, - ); - await waitForConfirmation(3000); - - assert.equal(diff(await balanceOf(token1, feeAddress), feeBefore).toString(), fee1); - - await batch.setBatchFee(BATCH_FEE_BPS, { from: payer }); - }); - }); - - describe('batchERC20PaymentsMultiTokensWithReference', () => { - it('should pay 3 ERC20 payments in three different tokens', async () => { - const amount1 = '5000'; - const amount2 = '3000'; - const amount3 = '4000'; - const fee1 = '600'; - const fee2 = '200'; - const fee3 = '300'; - - const batchFee1 = computeBatchFee(amount1, BATCH_FEE_BPS); - const batchFee2 = computeBatchFee(amount2, BATCH_FEE_BPS); - const batchFee3 = computeBatchFee(amount3, BATCH_FEE_BPS); - - await makeTokenApproval( - token1, - payer, - batch.address, - getApprovalAmount([amount1], [fee1], batchFee1), - ); - await makeTokenApproval( - token2, - payer, - batch.address, - getApprovalAmount([amount2], [fee2], batchFee2), - ); - await makeTokenApproval( - token3, - payer, - batch.address, - getApprovalAmount([amount3], [fee3], batchFee3), - ); - - const payee1Before = await balanceOf(token1, payee1); - const payee2Token2Before = await balanceOf(token2, payee2); - const payee2Token3Before = await balanceOf(token3, payee2); - const feeToken1Before = await balanceOf(token1, feeAddress); - const feeToken2Before = await balanceOf(token2, feeAddress); - const feeToken3Before = await balanceOf(token3, feeAddress); - const payerToken1Before = await balanceOf(token1, payer); - const payerToken2Before = await balanceOf(token2, payer); - const payerToken3Before = await balanceOf(token3, payer); - - await batch.batchERC20PaymentsMultiTokensWithReference( - [token1.address, token2.address, token3.address], - [payee1, payee2, payee2], - [amount1, amount2, amount3], - [REF_A, REF_B, REF_C], - [fee1, fee2, fee3], - feeAddress, - { from: payer }, - ); - await waitForConfirmation(3000); - - assert.equal(diff(await balanceOf(token1, payee1), payee1Before).toString(), amount1); - assert.equal(diff(await balanceOf(token2, payee2), payee2Token2Before).toString(), amount2); - assert.equal(diff(await balanceOf(token3, payee2), payee2Token3Before).toString(), amount3); - assert.equal( - diff(await balanceOf(token1, feeAddress), feeToken1Before).toString(), - sumStrings([fee1, batchFee1]), - ); - assert.equal( - diff(await balanceOf(token2, feeAddress), feeToken2Before).toString(), - sumStrings([fee2, batchFee2]), - ); - assert.equal( - diff(await balanceOf(token3, feeAddress), feeToken3Before).toString(), - sumStrings([fee3, batchFee3]), - ); - - const total1 = sumStrings([amount1, fee1, batchFee1]); - const total2 = sumStrings([amount2, fee2, batchFee2]); - const total3 = sumStrings([amount3, fee3, batchFee3]); - assert( - diff(payerToken1Before, await balanceOf(token1, payer)) >= BigInt(total1), - 'payer should pay token1 amounts, fees, and batch fee', - ); - assert( - diff(payerToken2Before, await balanceOf(token2, payer)) >= BigInt(total2), - 'payer should pay token2 amounts, fees, and batch fee', - ); - assert( - diff(payerToken3Before, await balanceOf(token3, payer)) >= BigInt(total3), - 'payer should pay token3 amounts, fees, and batch fee', - ); - }); - - it('should pay 3 ERC20 payments in three different tokens with a zero amount payment', async () => { - const amount1 = '5000'; - const amount2 = '0'; - const amount3 = '4000'; - const fee1 = '600'; - const fee2 = '0'; - const fee3 = '300'; - - const batchFee1 = computeBatchFee(amount1, BATCH_FEE_BPS); - const batchFee3 = computeBatchFee(amount3, BATCH_FEE_BPS); - - await makeTokenApproval( - token1, - payer, - batch.address, - getApprovalAmount([amount1], [fee1], batchFee1), - ); - await makeTokenApproval(token2, payer, batch.address, getApprovalAmount([amount2], [fee2])); - await makeTokenApproval( - token3, - payer, - batch.address, - getApprovalAmount([amount3], [fee3], batchFee3), - ); - - const payee1Before = await balanceOf(token1, payee1); - const payee2Token2Before = await balanceOf(token2, payee2); - const payee2Token3Before = await balanceOf(token3, payee2); - - await batch.batchERC20PaymentsMultiTokensWithReference( - [token1.address, token2.address, token3.address], - [payee1, payee2, payee2], - [amount1, amount2, amount3], - [REF_A, REF_B, REF_C], - [fee1, fee2, fee3], - feeAddress, - { from: payer }, - ); - await waitForConfirmation(3000); - - assert.equal(diff(await balanceOf(token1, payee1), payee1Before).toString(), amount1); - assert.equal(diff(await balanceOf(token2, payee2), payee2Token2Before).toString(), amount2); - assert.equal(diff(await balanceOf(token3, payee2), payee2Token3Before).toString(), amount3); - }); - - it('should pay 4 ERC20 payments in two different tokens', async () => { - const amount1 = '200'; - const amount2 = '200'; - const amount3 = '200'; - const amount4 = '200'; - const fee1 = '10'; - const fee2 = '10'; - const fee3 = '10'; - const fee4 = '10'; - - const batchFee1 = computeBatchFee(sumStrings([amount1, amount2]), BATCH_FEE_BPS); - const batchFee2 = computeBatchFee(sumStrings([amount3, amount4]), BATCH_FEE_BPS); - - await makeTokenApproval( - token1, - payer, - batch.address, - getApprovalAmount([amount1, amount2], [fee1, fee2], batchFee1), - ); - await makeTokenApproval( - token2, - payer, - batch.address, - getApprovalAmount([amount3, amount4], [fee3, fee4], batchFee2), - ); - - const payee2Token1Before = await balanceOf(token1, payee2); - const payee2Token2Before = await balanceOf(token2, payee2); - - await batch.batchERC20PaymentsMultiTokensWithReference( - [token1.address, token1.address, token2.address, token2.address], - [payee2, payee2, payee2, payee2], - [amount1, amount2, amount3, amount4], - [REF_A, REF_A, REF_A, REF_A], - [fee1, fee2, fee3, fee4], - feeAddress, - { from: payer }, - ); - await waitForConfirmation(3000); - - assert.equal( - diff(await balanceOf(token1, payee2), payee2Token1Before).toString(), - sumStrings([amount1, amount2]), - ); - assert.equal( - diff(await balanceOf(token2, payee2), payee2Token2Before).toString(), - sumStrings([amount3, amount4]), - ); - }); - - it('should pay 10 ERC20 payments in two different tokens', async () => { - const amount = '20'; - const feeAmount = '10'; - const nbPaymentsPerToken = 5; - - const batchFee1 = computeBatchFee(mulString(amount, nbPaymentsPerToken), BATCH_FEE_BPS); - - await makeTokenApproval( - token1, - payer, - batch.address, - getApprovalAmount( - Array(nbPaymentsPerToken).fill(amount), - Array(nbPaymentsPerToken).fill(feeAmount), - batchFee1, - ), - ); - await makeTokenApproval( - token2, - payer, - batch.address, - getApprovalAmount( - Array(nbPaymentsPerToken).fill(amount), - Array(nbPaymentsPerToken).fill(feeAmount), - batchFee1, - ), - ); - - const payee1Token1Before = await balanceOf(token1, payee1); - const payee1Token2Before = await balanceOf(token2, payee1); - - await batch.batchERC20PaymentsMultiTokensWithReference( - [ - ...Array(nbPaymentsPerToken).fill(token1.address), - ...Array(nbPaymentsPerToken).fill(token2.address), - ], - Array(nbPaymentsPerToken * 2).fill(payee1), - Array(nbPaymentsPerToken * 2).fill(amount), - Array(nbPaymentsPerToken * 2).fill(REF_A), - Array(nbPaymentsPerToken * 2).fill(feeAmount), - feeAddress, - { from: payer }, - ); - await waitForConfirmation(3000); - - assert.equal( - diff(await balanceOf(token1, payee1), payee1Token1Before).toString(), - mulString(amount, nbPaymentsPerToken), - ); - assert.equal( - diff(await balanceOf(token2, payee1), payee1Token2Before).toString(), - mulString(amount, nbPaymentsPerToken), - ); - }); - - it('should leave no token balance on the batch contract after a successful payment', async () => { - const amount1 = '100'; - const amount2 = '200'; - const fee1 = '10'; - const fee2 = '20'; - - const batchFee1 = computeBatchFee(amount1, BATCH_FEE_BPS); - const batchFee2 = computeBatchFee(amount2, BATCH_FEE_BPS); - - await makeTokenApproval( - token1, - payer, - batch.address, - getApprovalAmount([amount1], [fee1], batchFee1), - ); - await makeTokenApproval( - token2, - payer, - batch.address, - getApprovalAmount([amount2], [fee2], batchFee2), - ); - - await batch.batchERC20PaymentsMultiTokensWithReference( - [token1.address, token2.address], - [payee1, payee2], - [amount1, amount2], - [REF_A, REF_B], - [fee1, fee2], - feeAddress, - { from: payer }, - ); - await waitForConfirmation(3000); - - await assertBatchTokenBalancesZero(batch, [token1, token2]); - }); - - it('should pay a multi-token ERC20 payment with BadTRC20', async () => { - const badToken = await deployBadTRC20(payer); - const paymentAmount = '100'; - const feeAmount = '10'; - const amount1 = '50'; - const fee1 = '5'; - - try { - await badToken.approve(batch.address, getApprovalAmount([paymentAmount], [feeAmount]), { - from: payer, - }); - await makeTokenApproval( - token1, - payer, - batch.address, - getApprovalAmount([amount1], [fee1], computeBatchFee(amount1, BATCH_FEE_BPS)), - ); - await waitForConfirmation(3000); - - const badPayeeBefore = await balanceOf(badToken, payee1); - const payee1Before = await balanceOf(token1, payee2); - - await batch.batchERC20PaymentsMultiTokensWithReference( - [badToken.address, token1.address], - [payee1, payee2], - [paymentAmount, amount1], - [REF_A, REF_B], - [feeAmount, fee1], - feeAddress, - { from: payer }, - ); - await waitForConfirmation(3000); - - const badPayeeAfter = await balanceOf(badToken, payee1); - const payee1After = await balanceOf(token1, payee2); - assert( - badPayeeAfter > badPayeeBefore || payee1After > payee1Before, - 'BadTRC20 multi-token: at least one payee balance should increase when batch succeeds', - ); - } catch (_error) { - console.log( - 'BadTRC20 multi-token batch payment rejected by Tron (acceptable for non-standard tokens)', - ); - } - }); - }); - }); - - describe('Error cases scenarios', () => { - describe('batchERC20PaymentsWithReference', () => { - it('should revert when the payer does not have enough funds to pay', async () => { - const amount1 = '5'; - const amount2 = '30'; - const amount3 = '400'; - const fee1 = '1'; - const fee2 = '2'; - const fee3 = '3'; - - const lowToken = await deployTokenWithSupply('100', payer); - await makeTokenApproval( - lowToken, - payer, - batch.address, - getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3]), - ); - - const payee3Before = await balanceOf(lowToken, payee3); - const { unchanged } = await expectRevertOrNoBalanceChange( - () => - batch.batchERC20PaymentsWithReference( - lowToken.address, - [payee1, payee2, payee3], - [amount1, amount2, amount3], - [REF_A, REF_B, REF_C], - [fee1, fee2, fee3], - feeAddress, - { from: payer }, - ), - async () => [await balanceOf(lowToken, payee3)], - ); - - assert(unchanged, 'should not transfer when funds insufficient'); - assert.equal((await balanceOf(lowToken, payee3)).toString(), payee3Before.toString()); - }); - - it('should revert when the payer does not have enough funds to pay the batch fee', async () => { - const amount1 = '100'; - const amount2 = '200'; - const fee1 = '1'; - const fee2 = '2'; - const paymentTotal = sumStrings([amount1, amount2, fee1, fee2]); - - const lowToken = await deployTokenWithSupply(paymentTotal, payer); - await makeTokenApproval(lowToken, payer, batch.address, paymentTotal); - - const payee1Before = await balanceOf(lowToken, payee1); - const { unchanged } = await expectRevertOrNoBalanceChange( - () => - batch.batchERC20PaymentsWithReference( - lowToken.address, - [payee1, payee2], - [amount1, amount2], - [REF_A, REF_B], - [fee1, fee2], - feeAddress, - { from: payer }, - ), - async () => [await balanceOf(lowToken, payee1)], - ); - - assert(unchanged, 'should not transfer when batch fee cannot be paid'); - assert.equal((await balanceOf(lowToken, payee1)).toString(), payee1Before.toString()); - }); - - it('should revert when the payer did not approve the batch contract to spend the tokens', async () => { - const amount1 = '20'; - const amount2 = '30'; - const amount3 = '40'; - const fee1 = '1'; - const fee2 = '2'; - const fee3 = '3'; - - await makeTokenApproval( - token1, - payer, - batch.address, - getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3]), - ); - await token1.approve(batch.address, '10', { from: payer }); - await waitForConfirmation(2000); - - const payee1Before = await balanceOf(token1, payee1); - const { unchanged } = await expectRevertOrNoBalanceChange( - () => - batch.batchERC20PaymentsWithReference( - token1.address, - [payee1, payee2, payee3], - [amount1, amount2, amount3], - [REF_A, REF_B, REF_C], - [fee1, fee2, fee3], - feeAddress, - { from: payer }, - ), - async () => [await balanceOf(token1, payee1)], - ); - - assert(unchanged, 'should not transfer without allowance'); - assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); - }); - - it('should revert when input arrays have different lengths', async () => { - const amount1 = '100'; - const fee1 = '1'; - const fee2 = '2'; - - await makeTokenApproval( - token1, - payer, - batch.address, - getApprovalAmount([amount1], [fee1, fee2]), - ); - - const payee1Before = await balanceOf(token1, payee1); - const payee2Before = await balanceOf(token1, payee2); - - const { unchanged } = await expectRevertOrNoBalanceChange( - () => - batch.batchERC20PaymentsWithReference( - token1.address, - [payee1, payee2], - [amount1], - [REF_A, REF_B], - [fee1, fee2], - feeAddress, - { from: payer }, - ), - async () => [await balanceOf(token1, payee1), await balanceOf(token1, payee2)], - ); - - assert(unchanged, 'should not transfer when array lengths mismatch'); - assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); - assert.equal((await balanceOf(token1, payee2)).toString(), payee2Before.toString()); - }); - }); - - describe('batchERC20PaymentsMultiTokensWithReference', () => { - it('should revert when the payer does not have enough funds to pay in at least one of the tokens', async () => { - const amount1 = '5'; - const amount2 = '30'; - const amount3 = '400'; - const fee1 = '1'; - const fee2 = '2'; - const fee3 = '3'; - - const lowToken = await deployTokenWithSupply('400', payer); - await makeTokenApproval( - lowToken, - payer, - batch.address, - getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3]), - ); - - const payee3Before = await balanceOf(lowToken, payee3); - const { unchanged } = await expectRevertOrNoBalanceChange( - () => - batch.batchERC20PaymentsMultiTokensWithReference( - [lowToken.address, lowToken.address, lowToken.address], - [payee1, payee2, payee3], - [amount1, amount2, amount3], - [REF_A, REF_B, REF_C], - [fee1, fee2, fee3], - feeAddress, - { from: payer }, - ), - async () => [await balanceOf(lowToken, payee3)], - ); - - assert(unchanged, 'multi-token batch should not transfer when funds insufficient'); - assert.equal((await balanceOf(lowToken, payee3)).toString(), payee3Before.toString()); - }); - - it('should revert when the payer does not have enough funds to pay the batch fee in at least one of the tokens', async () => { - const amount1 = '100'; - const amount2 = '200'; - const amount3 = '300'; - const fee1 = '1'; - const fee2 = '2'; - const fee3 = '3'; - - const lowToken = await deployTokenWithSupply('607', payer); - await makeTokenApproval( - lowToken, - payer, - batch.address, - getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3]), - ); - - const payee2Before = await balanceOf(lowToken, payee2); - const { unchanged } = await expectRevertOrNoBalanceChange( - () => - batch.batchERC20PaymentsMultiTokensWithReference( - [lowToken.address, lowToken.address, lowToken.address], - [payee1, payee2, payee2], - [amount1, amount2, amount3], - [REF_A, REF_B, REF_C], - [fee1, fee2, fee3], - feeAddress, - { from: payer }, - ), - async () => [await balanceOf(lowToken, payee2)], - ); - - assert(unchanged, 'multi-token batch should not transfer when batch fee cannot be paid'); - assert.equal((await balanceOf(lowToken, payee2)).toString(), payee2Before.toString()); - }); - - it('should revert when the payer did not approve the batch contract to spend the tokens in at least one of the tokens', async () => { - const amount1 = '100'; - const amount2 = '200'; - const amount3 = '300'; - const fee1 = '1'; - const fee2 = '2'; - const fee3 = '3'; - - await makeTokenApproval( - token1, - payer, - batch.address, - getApprovalAmount([amount1, amount2, amount3], [fee1, fee2, fee3]), - ); - await token1.approve(batch.address, '10', { from: payer }); - await waitForConfirmation(2000); - - const payee1Before = await balanceOf(token1, payee1); - const { unchanged } = await expectRevertOrNoBalanceChange( - () => - batch.batchERC20PaymentsMultiTokensWithReference( - [token1.address, token1.address, token1.address], - [payee1, payee2, payee3], - [amount1, amount2, amount3], - [REF_A, REF_B, REF_C], - [fee1, fee2, fee3], - feeAddress, - { from: payer }, - ), - async () => [await balanceOf(token1, payee1)], - ); - - assert(unchanged, 'multi-token batch should not transfer without allowance'); - assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); - }); - - it('should revert when the payer did not approve the batch contract for one of the tokens', async () => { - const amount1 = '100'; - const amount2 = '200'; - const fee1 = '1'; - const fee2 = '2'; - - await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); - - const payee2Token2Before = await balanceOf(token2, payee2); - const { unchanged } = await expectRevertOrNoBalanceChange( - () => - batch.batchERC20PaymentsMultiTokensWithReference( - [token1.address, token2.address], - [payee1, payee2], - [amount1, amount2], - [REF_A, REF_B], - [fee1, fee2], - feeAddress, - { from: payer }, - ), - async () => [await balanceOf(token2, payee2)], - ); - - assert(unchanged, 'should not transfer when one token lacks approval'); - assert.equal((await balanceOf(token2, payee2)).toString(), payee2Token2Before.toString()); - }); - - it('should revert when input arrays have different lengths', async () => { - const amount1 = '100'; - const fee1 = '1'; - const fee2 = '2'; - - await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); - await makeTokenApproval(token2, payer, batch.address, getApprovalAmount([amount1], [fee2])); - - const payee1Before = await balanceOf(token1, payee1); - const payee2Before = await balanceOf(token2, payee2); - - const { unchanged } = await expectRevertOrNoBalanceChange( - () => - batch.batchERC20PaymentsMultiTokensWithReference( - [token1.address, token2.address], - [payee1, payee2], - [amount1], - [REF_A, REF_B], - [fee1, fee2], - feeAddress, - { from: payer }, - ), - async () => [await balanceOf(token1, payee1), await balanceOf(token2, payee2)], - ); - - assert(unchanged, 'should not transfer when array lengths mismatch'); - assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); - assert.equal((await balanceOf(token2, payee2)).toString(), payee2Before.toString()); - }); - }); - - describe('batchEthPaymentsWithReference', () => { - it('should revert when calling batchEthPaymentsWithReference when EthFeeProxy is not set', async () => { - assert.equal(await batch.paymentEthFeeProxy(), TRON_ZERO_ADDRESS); - - const paymentAmount = String(10 * ONE_TRX_SUN); - const feeAmount = '0'; - const payeeBefore = await trxBalance(payee1); - const payerBefore = await trxBalance(payer); - - try { - await batch.batchEthPaymentsWithReference( - [payee1], - [paymentAmount], - [REF_A], - [feeAmount], - feeAddress, - { from: payer, callValue: Number(paymentAmount) }, - ); - } catch (_error) {} - await waitForConfirmation(2000); - - assert.equal((await trxBalance(payee1)).toString(), payeeBefore.toString()); - - const payerSpent = payerBefore - (await trxBalance(payer)); - assert( - payerSpent < BigInt(paymentAmount), - `payer should only spend tx fees, not ${paymentAmount} sun (spent ${payerSpent})`, - ); - }); - }); - }); - - describe('Admin', () => { - describe('setPaymentErc20FeeProxy', () => { - it('should allow owner to update proxy addresses', async () => { - const ERC20FeeProxy = artifacts.require('ERC20FeeProxy'); - const newProxy = await ERC20FeeProxy.new(); - await batch.setPaymentErc20FeeProxy(newProxy.address, { from: payer }); - assert.equal(await batch.paymentErc20FeeProxy(), newProxy.address); - }); - - it('should revert when a non-owner tries to update proxy addresses', async () => { - await expectNonOwnerReverts( - () => batch.setPaymentErc20FeeProxy(payee1, { from: payee1 }), - async () => await batch.paymentErc20FeeProxy(), - ); - }); - }); - - describe('setBatchFee', () => { - it('should allow owner to update the batch fee', async () => { - const newBatchFee = 50; - await batch.setBatchFee(newBatchFee, { from: payer }); - assert.equal((await batch.batchFee()).toString(), String(newBatchFee)); - await batch.setBatchFee(BATCH_FEE_BPS, { from: payer }); - }); - - it('should revert when a non-owner tries to set the batch fee', async () => { - await expectNonOwnerReverts( - () => batch.setBatchFee(99, { from: payee1 }), - async () => (await batch.batchFee()).toString(), - ); - }); - }); - - describe('setPaymentEthFeeProxy', () => { - it('should revert when a non-owner tries to set the EthFeeProxy address', async () => { - await expectNonOwnerReverts( - () => batch.setPaymentEthFeeProxy(TRON_ZERO_ADDRESS, { from: payee1 }), - async () => await batch.paymentEthFeeProxy(), - ); - }); - }); - }); -}); diff --git a/packages/smart-contracts/tron/contracts/BatchPayments.sol b/packages/smart-contracts/tron/contracts/BatchPayments.sol deleted file mode 120000 index 5f16a2c12b..0000000000 --- a/packages/smart-contracts/tron/contracts/BatchPayments.sol +++ /dev/null @@ -1 +0,0 @@ -../../src/contracts/BatchPayments.sol \ No newline at end of file From bc5e65e20d4c4472badd13f174809706437456a8 Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Tue, 26 May 2026 19:28:59 +0200 Subject: [PATCH 08/19] feat: extend tron deployment script to support ERC20BatchPayments --- .../scripts/tron/deploy-mainnet.js | 122 ++++++++++++++---- .../scripts/tron/deploy-nile.js | 12 ++ 2 files changed, 106 insertions(+), 28 deletions(-) diff --git a/packages/smart-contracts/scripts/tron/deploy-mainnet.js b/packages/smart-contracts/scripts/tron/deploy-mainnet.js index 873cca51c2..d3f9ec34dd 100644 --- a/packages/smart-contracts/scripts/tron/deploy-mainnet.js +++ b/packages/smart-contracts/scripts/tron/deploy-mainnet.js @@ -2,7 +2,7 @@ /** * Tron Mainnet Deployment Script * - * This script deploys the ERC20FeeProxy to Tron mainnet. + * This script deploys the ERC20FeeProxy and ERC20BatchPayments to Tron mainnet. * * ⚠️ WARNING: This deploys to MAINNET with real TRX! * @@ -28,6 +28,18 @@ const PRIVATE_KEY = process.env.TRON_PRIVATE_KEY; // Safety check const CONFIRM_MAINNET = process.env.CONFIRM_MAINNET_DEPLOY === 'true'; +const MAINNET_DEPLOYMENT_PATH = path.join(__dirname, '../../deployments/tron/mainnet.json'); + +/** + * Contracts to deploy + * + * Comment out the contracts you don't want to deploy. + */ +const CONTRACTS_TO_DEPLOY = [ + //'ERC20FeeProxy', + 'ERC20BatchPayments', +]; + if (!PRIVATE_KEY) { console.error('Error: TRON_PRIVATE_KEY environment variable is required'); process.exit(1); @@ -49,6 +61,13 @@ async function loadArtifact(contractName) { return JSON.parse(fs.readFileSync(artifactPath, 'utf8')); } +function loadExistingMainnetDeployment() { + if (!fs.existsSync(MAINNET_DEPLOYMENT_PATH)) { + return null; + } + return JSON.parse(fs.readFileSync(MAINNET_DEPLOYMENT_PATH, 'utf8')); +} + async function confirmDeployment() { if (CONFIRM_MAINNET) { return true; @@ -92,6 +111,24 @@ async function deployContract(contractName, constructorArgs = []) { }; } +async function deployContractWrapper({ + contractName, + deployments, + blockNumbers, + constructorArgs = [], +}) { + const contract = await deployContract(contractName, constructorArgs); + deployments[contractName] = { + address: contract.address, + hexAddress: contract.hexAddress, + }; + + // Get block number + const block = await tronWeb.trx.getCurrentBlock(); + const blockNumber = block.block_header.raw_data.number; + blockNumbers[contractName] = blockNumber; +} + async function main() { console.log('╔══════════════════════════════════════════════════════════╗'); console.log('║ TRON MAINNET DEPLOYMENT ║'); @@ -123,50 +160,80 @@ async function main() { console.log('\n🚀 Starting mainnet deployment...\n'); const deployments = {}; + const blockNumbers = {}; const startTime = Date.now(); try { - // Deploy ERC20FeeProxy only (no test tokens on mainnet) - const erc20FeeProxy = await deployContract('ERC20FeeProxy'); - deployments.ERC20FeeProxy = { - address: erc20FeeProxy.address, - hexAddress: erc20FeeProxy.hexAddress, - }; + const existingDeployment = loadExistingMainnetDeployment(); + + // Deploy ERC20FeeProxy + if (CONTRACTS_TO_DEPLOY.includes('ERC20FeeProxy')) { + await deployContractWrapper({ contractName: 'ERC20FeeProxy', deployments, blockNumbers }); + await new Promise((resolve) => setTimeout(resolve, 5000)); + } + + // Deploy ERC20BatchPayments + if (CONTRACTS_TO_DEPLOY.includes('ERC20BatchPayments')) { + const erc20FeeProxyAddress = deployments.ERC20FeeProxy + ? deployments.ERC20FeeProxy.address + : existingDeployment.contracts.ERC20FeeProxy.address; - // Get block number - const block = await tronWeb.trx.getCurrentBlock(); - const blockNumber = block.block_header.raw_data.number; + if (!erc20FeeProxyAddress) { + console.error( + 'ERC20FeeProxy address not found in deployments/tron/mainnet.json; cannot deploy ERC20BatchPayments', + ); + process.exit(1); + } + + console.log('Using ERC20FeeProxy at:', erc20FeeProxyAddress); + await deployContractWrapper({ + contractName: 'ERC20BatchPayments', + deployments, + blockNumbers, + constructorArgs: [erc20FeeProxyAddress], + }); + } // Print summary console.log('\n╔══════════════════════════════════════════════════════════╗'); console.log('║ MAINNET DEPLOYMENT SUMMARY ║'); console.log('╚══════════════════════════════════════════════════════════╝\n'); - console.log('ERC20FeeProxy:'); - console.log(` Address: ${deployments.ERC20FeeProxy.address}`); - console.log(` Block: ${blockNumber}`); - console.log( - ` Tronscan: https://tronscan.org/#/contract/${deployments.ERC20FeeProxy.address}`, - ); + for (const contractName of Object.keys(deployments)) { + console.log(`${contractName}:`); + console.log(` Address: ${deployments[contractName].address}`); + console.log(` Block: ${blockNumbers[contractName]}`); + console.log( + ` Tronscan: https://tronscan.org/#/contract/${deployments[contractName].address}`, + ); + } + + const newContracts = Object.entries(deployments).reduce((acc, [contractName, contract]) => { + acc[contractName] = { + ...contract, + creationBlockNumber: blockNumbers[contractName], + }; + return acc; + }, {}); + + const contracts = { + ...(existingDeployment.contracts || {}), + ...newContracts, + }; - // Save deployment info + // Save deployment info (merge with existing mainnet.json) const deploymentInfo = { network: 'mainnet', chainId: '1', timestamp: new Date().toISOString(), deployer: deployerAddress, deploymentDuration: `${(Date.now() - startTime) / 1000}s`, - contracts: { - ERC20FeeProxy: { - ...deployments.ERC20FeeProxy, - creationBlockNumber: blockNumber, - }, - }, + contracts, }; - const outputPath = path.join(__dirname, '../../deployments/tron/mainnet.json'); - fs.writeFileSync(outputPath, JSON.stringify(deploymentInfo, null, 2)); - console.log(`\nDeployment info saved to: ${outputPath}`); + fs.mkdirSync(path.dirname(MAINNET_DEPLOYMENT_PATH), { recursive: true }); + fs.writeFileSync(MAINNET_DEPLOYMENT_PATH, JSON.stringify(deploymentInfo, null, 2)); + console.log(`\nDeployment info saved to: ${MAINNET_DEPLOYMENT_PATH}`); // Next steps console.log('\n╔══════════════════════════════════════════════════════════╗'); @@ -174,8 +241,7 @@ async function main() { console.log('╚══════════════════════════════════════════════════════════╝\n'); console.log('1. Verify contract on Tronscan'); console.log('2. Run verification script: yarn tron:verify:mainnet'); - console.log('3. Update artifact registry in:'); - console.log(' packages/smart-contracts/src/lib/artifacts/ERC20FeeProxy/index.ts'); + console.log('3. Update artifact registry with new deployment addresses'); console.log('4. Test with a real TRC20 token payment'); } catch (error) { console.error('\n❌ Deployment failed:', error.message); diff --git a/packages/smart-contracts/scripts/tron/deploy-nile.js b/packages/smart-contracts/scripts/tron/deploy-nile.js index 9e5d21d8b8..5e83afeec2 100644 --- a/packages/smart-contracts/scripts/tron/deploy-nile.js +++ b/packages/smart-contracts/scripts/tron/deploy-nile.js @@ -91,6 +91,15 @@ async function main() { hexAddress: erc20FeeProxy.address, }; + // 2. Deploy ERC20BatchPayments + const erc20BatchPayments = await deployContract('ERC20BatchPayments', [ + deployments.ERC20FeeProxy.address, + ]); + deployments.ERC20BatchPayments = { + address: tronWeb.address.fromHex(erc20BatchPayments.address), + hexAddress: erc20BatchPayments.address, + }; + // 2. Deploy TestTRC20 for testing const testToken = await deployContract('TestTRC20', [ '1000000000000000000000000000', // 1 billion tokens @@ -141,6 +150,9 @@ async function main() { console.log('╚══════════════════════════════════════════════════════════╝\n'); console.log('1. Verify contracts on Nile Tronscan:'); console.log(' https://nile.tronscan.org/#/contract/' + deployments.ERC20FeeProxy.address); + console.log( + ' https://nile.tronscan.org/#/contract/' + deployments.ERC20BatchPayments.address, + ); console.log('\n2. Run tests against deployed contracts:'); console.log(' TRON_PRIVATE_KEY=... yarn tron:test:nile'); console.log('\n3. Update artifact registry with deployment addresses'); From 0ad5f2453a632d77ec73faa6ca9062ef55d2cc08 Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Tue, 26 May 2026 19:29:25 +0200 Subject: [PATCH 09/19] add deployment information for ERC20BatchPayments --- .../deployments/tron/mainnet.json | 12 +- .../deployments/tron/nile.json | 5 + .../artifacts/ERC20BatchPayments/0.1.0.json | 117 ++++++++++++++++++ .../lib/artifacts/ERC20BatchPayments/index.ts | 24 ++++ 4 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/0.1.0.json create mode 100644 packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts diff --git a/packages/smart-contracts/deployments/tron/mainnet.json b/packages/smart-contracts/deployments/tron/mainnet.json index f1755f5186..7d7fdf8ff3 100644 --- a/packages/smart-contracts/deployments/tron/mainnet.json +++ b/packages/smart-contracts/deployments/tron/mainnet.json @@ -1,14 +1,20 @@ { "network": "mainnet", "chainId": "1", - "timestamp": "2024-01-01T00:00:00.000Z", - "deployer": "TO_BE_FILLED_ON_DEPLOYMENT", - "note": "Existing deployment from handover document", + "timestamp": "2026-05-26T17:02:20.157Z", + "deployer": "TR7EydtGnsxriSieLfEuspTAqhQRmoscWC", + "deploymentDuration": "3.244s", + "note": "Deployment of ERC20BatchPayments", "contracts": { "ERC20FeeProxy": { "address": "TCUDPYnS9dH3WvFEaE7wN7vnDa51J4R4fd", "hexAddress": "411b6ca35d39842cf8fbe49000653a1505412da659", "creationBlockNumber": 79216121 + }, + "ERC20BatchPayments": { + "address": "TUdcGd29QpV65MkbqgBLWJKbTG3UL7PuQB", + "hexAddress": "41ccb57e7bbb729b1c8c94294a61b658a6f2304281", + "creationBlockNumber": 83048743 } } } diff --git a/packages/smart-contracts/deployments/tron/nile.json b/packages/smart-contracts/deployments/tron/nile.json index 77257b8bc6..699e324f4e 100644 --- a/packages/smart-contracts/deployments/tron/nile.json +++ b/packages/smart-contracts/deployments/tron/nile.json @@ -9,6 +9,11 @@ "address": "THK5rNmrvCujhmrXa5DB1dASepwXTr9cJs", "hexAddress": "41508b3b4059c40bb3aac5da5ac006ccdd9c4dc957", "creationBlockNumber": 63208782 + }, + "ERC20BatchPayments": { + "address": "TDnU5eY8Et3QdZRWMSTvoXQnxQeMxF7CE4", + "hexAddress": "4129d883d52bf19f97ef0a0c2edb99a679b0d5e12e", + "creationBlockNumber": 67775288 } } } diff --git a/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/0.1.0.json b/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/0.1.0.json new file mode 100644 index 0000000000..62f2440ff3 --- /dev/null +++ b/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/0.1.0.json @@ -0,0 +1,117 @@ +{ + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_paymentErc20FeeProxy", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_erc20Address", + "type": "address" + } + ], + "name": "approvePaymentProxyToSpend", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_tokenAddresses", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "_recipients", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_amounts", + "type": "uint256[]" + }, + { + "internalType": "bytes[]", + "name": "_paymentReferences", + "type": "bytes[]" + }, + { + "internalType": "uint256[]", + "name": "_feeAmounts", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "_feeAddress", + "type": "address" + } + ], + "name": "batchERC20PaymentsMultiTokensWithReference", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_tokenAddress", + "type": "address" + }, + { + "internalType": "address[]", + "name": "_recipients", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "_amounts", + "type": "uint256[]" + }, + { + "internalType": "bytes[]", + "name": "_paymentReferences", + "type": "bytes[]" + }, + { + "internalType": "uint256[]", + "name": "_feeAmounts", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "_feeAddress", + "type": "address" + } + ], + "name": "batchERC20PaymentsWithReference", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paymentErc20FeeProxy", + "outputs": [ + { + "internalType": "contract IERC20FeeProxy", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ] +} diff --git a/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts b/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts new file mode 100644 index 0000000000..2826468f7a --- /dev/null +++ b/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts @@ -0,0 +1,24 @@ +import { ContractArtifact } from '../../ContractArtifact'; + +import { abi as ABI_0_1_0 } from './0.1.0.json'; +// @ts-ignore Cannot find module +import type { ERC20BatchPayments } from '../../../types/tron'; + +export const erc20BatchPaymentsArtifact = new ContractArtifact( + { + tron: { + abi: ABI_0_1_0, + deployment: { + nile: { + address: 'THK5rNmrvCujhmrXa5DB1dASepwXTr9cJs', + creationBlockNumber: 63208782, + }, + tron: { + address: 'TUdcGd29QpV65MkbqgBLWJKbTG3UL7PuQB', + creationBlockNumber: 83048743, + }, + }, + }, + }, + '0.1.0', +); From 663ef2f6200529ac2fbba7cf576391c52a9a2d49 Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Tue, 26 May 2026 20:05:38 +0200 Subject: [PATCH 10/19] update nile ERC20BatchPAyments deployment info --- .../src/lib/artifacts/ERC20BatchPayments/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts b/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts index 2826468f7a..9e0c603c73 100644 --- a/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts +++ b/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts @@ -10,8 +10,8 @@ export const erc20BatchPaymentsArtifact = new ContractArtifact Date: Wed, 27 May 2026 11:30:20 +0200 Subject: [PATCH 11/19] contract update based on comments --- .../deployments/tron/mainnet.json | 12 +++++----- .../deployments/tron/nile.json | 6 ++--- .../lib/artifacts/ERC20BatchPayments/index.ts | 8 +++---- .../test/tron/ERC20BatchPayments.test.js | 22 +++++++++++++++++++ .../tron/contracts/ERC20BatchPayments.sol | 5 +++-- 5 files changed, 38 insertions(+), 15 deletions(-) diff --git a/packages/smart-contracts/deployments/tron/mainnet.json b/packages/smart-contracts/deployments/tron/mainnet.json index 7d7fdf8ff3..ae0df3ed79 100644 --- a/packages/smart-contracts/deployments/tron/mainnet.json +++ b/packages/smart-contracts/deployments/tron/mainnet.json @@ -1,10 +1,10 @@ { "network": "mainnet", "chainId": "1", - "timestamp": "2026-05-26T17:02:20.157Z", + "timestamp": "2026-05-27T09:23:48.624Z", "deployer": "TR7EydtGnsxriSieLfEuspTAqhQRmoscWC", - "deploymentDuration": "3.244s", - "note": "Deployment of ERC20BatchPayments", + "deploymentDuration": "3.227s", + "note": "Deployment of ERC20BatchPayments to Mainnet", "contracts": { "ERC20FeeProxy": { "address": "TCUDPYnS9dH3WvFEaE7wN7vnDa51J4R4fd", @@ -12,9 +12,9 @@ "creationBlockNumber": 79216121 }, "ERC20BatchPayments": { - "address": "TUdcGd29QpV65MkbqgBLWJKbTG3UL7PuQB", - "hexAddress": "41ccb57e7bbb729b1c8c94294a61b658a6f2304281", - "creationBlockNumber": 83048743 + "address": "THm8vX6GNfRFZ15mRqdgvj56wjB6575S7C", + "hexAddress": "4155789c40d8ba55166296217cc244ca2dd3499f89", + "creationBlockNumber": 83068367 } } } diff --git a/packages/smart-contracts/deployments/tron/nile.json b/packages/smart-contracts/deployments/tron/nile.json index 699e324f4e..818ebdd22a 100644 --- a/packages/smart-contracts/deployments/tron/nile.json +++ b/packages/smart-contracts/deployments/tron/nile.json @@ -11,9 +11,9 @@ "creationBlockNumber": 63208782 }, "ERC20BatchPayments": { - "address": "TDnU5eY8Et3QdZRWMSTvoXQnxQeMxF7CE4", - "hexAddress": "4129d883d52bf19f97ef0a0c2edb99a679b0d5e12e", - "creationBlockNumber": 67775288 + "address": "TC6nD547PRDVWuX8hBMREU7vVvSZNCAZot", + "hexAddress": "41175ee218bb15fc25224ab5937b0844f4f70a2b97", + "creationBlockNumber": 67794373 } } } diff --git a/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts b/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts index 9e0c603c73..534029b8c6 100644 --- a/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts +++ b/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts @@ -10,12 +10,12 @@ export const erc20BatchPaymentsArtifact = new ContractArtifact { }); }); }); + +contract('ERC20BatchPayments constructor', () => { + const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; + + it('should revert when paymentErc20FeeProxy is the zero address', async () => { + let reverted = false; + let errorMessage = ''; + + try { + await ERC20BatchPayments.new(ZERO_ADDRESS); + } catch (error) { + reverted = true; + errorMessage = error.message || String(error); + } + + assert(reverted, 'deployment should revert when paymentErc20FeeProxy is address(0)'); + assert( + errorMessage.includes('paymentErc20FeeProxy cannot be 0x'), + `expected zero-address revert, got: ${errorMessage}`, + ); + }); +}); diff --git a/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol b/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol index 68e788d9a8..4a190b0d60 100644 --- a/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol +++ b/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol @@ -16,7 +16,7 @@ import './lib/SafeERC20.sol'; contract ERC20BatchPayments { using SafeERC20 for IERC20; - IERC20FeeProxy public paymentErc20FeeProxy; + IERC20FeeProxy public immutable paymentErc20FeeProxy; struct Token { address tokenAddress; @@ -27,6 +27,7 @@ contract ERC20BatchPayments { * @param _paymentErc20FeeProxy The address of the ERC20FeeProxy to use. */ constructor(address _paymentErc20FeeProxy) { + require(_paymentErc20FeeProxy != address(0), 'ERC20BatchPayments: paymentErc20FeeProxy cannot be 0x'); paymentErc20FeeProxy = IERC20FeeProxy(_paymentErc20FeeProxy); } @@ -136,7 +137,7 @@ contract ERC20BatchPayments { * @notice Authorizes the proxy to spend a request currency (ERC20). * @param _erc20Address Address of an ERC20 used as the request currency. */ - function approvePaymentProxyToSpend(address _erc20Address) public { + function approvePaymentProxyToSpend(address _erc20Address) internal { IERC20 erc20 = IERC20(_erc20Address); uint256 max = type(uint256).max; require(erc20.safeApprove(address(paymentErc20FeeProxy), max), 'approve() failed'); From 9c07ed22679239935bf227497f3eedc89e6fd7ac Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Wed, 27 May 2026 13:32:39 +0200 Subject: [PATCH 12/19] fix: update ABI --- .../src/lib/artifacts/ERC20BatchPayments/0.1.0.json | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/0.1.0.json b/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/0.1.0.json index 62f2440ff3..e3bdc7a3cc 100644 --- a/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/0.1.0.json +++ b/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/0.1.0.json @@ -11,19 +11,6 @@ "stateMutability": "nonpayable", "type": "constructor" }, - { - "inputs": [ - { - "internalType": "address", - "name": "_erc20Address", - "type": "address" - } - ], - "name": "approvePaymentProxyToSpend", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { From 0a0d00ebf6a9b799f9411e91c1baeb2baac36986 Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Thu, 28 May 2026 11:57:21 +0200 Subject: [PATCH 13/19] fix: after review update --- packages/smart-contracts/package.json | 6 +- .../scripts/tron/deploy-nile.js | 46 +++--- .../test/tron/ERC20BatchPayments.test.js | 156 +++++++++++++++++- packages/smart-contracts/test/tron/helpers.js | 18 +- .../tron/contracts/ERC20BatchPayments.sol | 89 +++++++--- .../contracts/interfaces/EthereumFeeProxy.sol | 1 - 6 files changed, 251 insertions(+), 65 deletions(-) delete mode 120000 packages/smart-contracts/tron/contracts/interfaces/EthereumFeeProxy.sol diff --git a/packages/smart-contracts/package.json b/packages/smart-contracts/package.json index ae00d400cf..65a729817e 100644 --- a/packages/smart-contracts/package.json +++ b/packages/smart-contracts/package.json @@ -35,7 +35,8 @@ "build:lib": "tsc -b tsconfig.build.json && cp src/types/*.d.ts dist/src/types && cp -r dist/src/types types", "prebuild:sol": "yarn workspace @requestnetwork/types build && yarn workspace @requestnetwork/utils build && yarn workspace @requestnetwork/currency build", "build:sol": "yarn hardhat compile", - "build": "yarn build:sol && yarn build:lib", + "build:tron": "yarn tron:compile && typechain --target ethers-v5 --out-dir src/types/tron 'build/tron/*.json'", + "build": "yarn build:sol && yarn build:tron && yarn build:lib", "clean:types": "rm -rf types && rm -rf src/types", "clean:lib": "rm -rf dist tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo", "clean:hardhat": "rm -rf cache && rm -rf build", @@ -106,9 +107,10 @@ "ganache-cli": "6.12.0", "hardhat": "2.26.5", "solhint": "3.3.6", + "tronbox": "4.7.0", "tronweb": "5.3.2", "typechain": "8.3.2", - "typescript": "4.8.4", + "typescript": "5.8.3", "web3": "1.7.3", "zksync-web3": "0.14.3" } diff --git a/packages/smart-contracts/scripts/tron/deploy-nile.js b/packages/smart-contracts/scripts/tron/deploy-nile.js index 5e83afeec2..5858affcf0 100644 --- a/packages/smart-contracts/scripts/tron/deploy-nile.js +++ b/packages/smart-contracts/scripts/tron/deploy-nile.js @@ -56,10 +56,19 @@ async function deployContract(contractName, constructorArgs = []) { parameters: constructorArgs, }); - console.log(`${contractName} deployed at: ${contract.address}`); - console.log(`Base58 address: ${tronWeb.address.fromHex(contract.address)}`); + const base58Address = tronWeb.address.fromHex(contract.address); + const block = await tronWeb.trx.getCurrentBlock(); + const creationBlockNumber = block.block_header.raw_data.number; - return contract; + console.log(`${contractName} deployed at: ${contract.address}`); + console.log(`Base58 address: ${base58Address}`); + console.log(`Block: ${creationBlockNumber}`); + + return { + address: base58Address, + hexAddress: contract.address, + creationBlockNumber, + }; } async function main() { @@ -85,39 +94,25 @@ async function main() { try { // 1. Deploy ERC20FeeProxy - const erc20FeeProxy = await deployContract('ERC20FeeProxy'); - deployments.ERC20FeeProxy = { - address: tronWeb.address.fromHex(erc20FeeProxy.address), - hexAddress: erc20FeeProxy.address, - }; + deployments.ERC20FeeProxy = await deployContract('ERC20FeeProxy'); // 2. Deploy ERC20BatchPayments - const erc20BatchPayments = await deployContract('ERC20BatchPayments', [ + deployments.ERC20BatchPayments = await deployContract('ERC20BatchPayments', [ deployments.ERC20FeeProxy.address, ]); - deployments.ERC20BatchPayments = { - address: tronWeb.address.fromHex(erc20BatchPayments.address), - hexAddress: erc20BatchPayments.address, - }; - // 2. Deploy TestTRC20 for testing - const testToken = await deployContract('TestTRC20', [ + // 3. Deploy TestTRC20 for testing + deployments.TestTRC20 = await deployContract('TestTRC20', [ '1000000000000000000000000000', // 1 billion tokens 'Nile Test TRC20', 'NTRC20', 18, ]); - deployments.TestTRC20 = { - address: tronWeb.address.fromHex(testToken.address), - hexAddress: testToken.address, - }; - // 3. Deploy test token variants - const trc20NoReturn = await deployContract('TRC20NoReturn', ['1000000000000000000000000000']); - deployments.TRC20NoReturn = { - address: tronWeb.address.fromHex(trc20NoReturn.address), - hexAddress: trc20NoReturn.address, - }; + // 4. Deploy test token variants + deployments.TRC20NoReturn = await deployContract('TRC20NoReturn', [ + '1000000000000000000000000000', + ]); // Print summary console.log('\n╔══════════════════════════════════════════════════════════╗'); @@ -128,6 +123,7 @@ async function main() { console.log(`${name}:`); console.log(` Base58: ${info.address}`); console.log(` Hex: ${info.hexAddress}`); + console.log(` Block: ${info.creationBlockNumber}`); } // Save deployment info diff --git a/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js b/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js index 9cad9b4eba..ff858a3ec4 100644 --- a/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js +++ b/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js @@ -4,6 +4,7 @@ const { REF_B, REF_C, TRON_ZERO_ADDRESS, + ZERO_ADDRESS, waitForConfirmation, balanceOf, diff, @@ -617,6 +618,81 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); assert.equal((await balanceOf(token1, payee2)).toString(), payee2Before.toString()); }); + + it('should revert when token address is zero', async () => { + const amount1 = '100'; + const fee1 = '1'; + + await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); + + const payee1Before = await balanceOf(token1, payee1); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsWithReference( + ZERO_ADDRESS, + [payee1], + [amount1], + [REF_A], + [fee1], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(token1, payee1)], + ); + + assert(unchanged, 'should not transfer when token address is zero'); + assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); + }); + + it('should revert when recipient is zero', async () => { + const amount1 = '100'; + const fee1 = '1'; + + await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); + + const payee1Before = await balanceOf(token1, payee1); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsWithReference( + token1.address, + [ZERO_ADDRESS], + [amount1], + [REF_A], + [fee1], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(token1, payee1)], + ); + + assert(unchanged, 'should not transfer when recipient is zero'); + assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); + }); + + it('should revert when feeAddress is zero and fee is non-zero', async () => { + const amount1 = '100'; + const fee1 = '1'; + + await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); + + const payee1Before = await balanceOf(token1, payee1); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsWithReference( + token1.address, + [payee1], + [amount1], + [REF_A], + [fee1], + ZERO_ADDRESS, + { from: payer }, + ), + async () => [await balanceOf(token1, payee1)], + ); + + assert(unchanged, 'should not transfer when feeAddress is zero and fee is non-zero'); + assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); + }); }); describe('batchERC20PaymentsMultiTokensWithReference', () => { @@ -747,13 +823,89 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); assert.equal((await balanceOf(token2, payee2)).toString(), payee2Before.toString()); }); + + it('should revert when token address is zero', async () => { + const amount1 = '100'; + const fee1 = '1'; + + await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); + + const payee1Before = await balanceOf(token1, payee1); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsMultiTokensWithReference( + [ZERO_ADDRESS], + [payee1], + [amount1], + [REF_A], + [fee1], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(token1, payee1)], + ); + + assert(unchanged, 'multi-token batch should not transfer when token address is zero'); + assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); + }); + + it('should revert when recipient is zero', async () => { + const amount1 = '100'; + const fee1 = '1'; + + await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); + + const payee1Before = await balanceOf(token1, payee1); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsMultiTokensWithReference( + [token1.address], + [ZERO_ADDRESS], + [amount1], + [REF_A], + [fee1], + feeAddress, + { from: payer }, + ), + async () => [await balanceOf(token1, payee1)], + ); + + assert(unchanged, 'multi-token batch should not transfer when recipient is zero'); + assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); + }); + + it('should revert when feeAddress is zero and fee is non-zero', async () => { + const amount1 = '100'; + const fee1 = '1'; + + await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); + + const payee1Before = await balanceOf(token1, payee1); + const { unchanged } = await expectRevertOrNoBalanceChange( + () => + batch.batchERC20PaymentsMultiTokensWithReference( + [token1.address], + [payee1], + [amount1], + [REF_A], + [fee1], + ZERO_ADDRESS, + { from: payer }, + ), + async () => [await balanceOf(token1, payee1)], + ); + + assert( + unchanged, + 'multi-token batch should not transfer when feeAddress is zero and fee is non-zero', + ); + assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); + }); }); }); }); contract('ERC20BatchPayments constructor', () => { - const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; - it('should revert when paymentErc20FeeProxy is the zero address', async () => { let reverted = false; let errorMessage = ''; diff --git a/packages/smart-contracts/test/tron/helpers.js b/packages/smart-contracts/test/tron/helpers.js index 3fd05d4242..d9e2ff8de2 100644 --- a/packages/smart-contracts/test/tron/helpers.js +++ b/packages/smart-contracts/test/tron/helpers.js @@ -7,6 +7,9 @@ const REF_C = '0xcccc'; /** Tron base58 zero address (unset EthFeeProxy on Tron deployments). */ const TRON_ZERO_ADDRESS = '410000000000000000000000000000000000000000'; +/** EVM-style zero address for revert tests. */ +const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; + /** 1 TRX = 1_000_000 sun on Tron. */ const ONE_TRX_SUN = 1_000_000; @@ -28,16 +31,12 @@ const sumStrings = (values) => values.reduce((acc, value) => acc + BigInt(value) const mulString = (value, count) => (BigInt(value) * BigInt(count)).toString(); -const computeBatchFee = (totalPaymentAmount, bps) => - ((BigInt(totalPaymentAmount) * BigInt(bps)) / 1000n).toString(); - -const getApprovalAmount = (amountList, feeList, batchFee = '0') => - sumStrings([...amountList, ...feeList, batchFee]); +const getApprovalAmount = (amountList, feeList) => sumStrings([...amountList, ...feeList]); /** * Deploy ERC20FeeProxy, optional batch contract, and one or more TestTRC20 tokens. */ -const deployBaseSetup = async ({ accounts, batchDeployFn, batchFee, tokenCount = 3 }) => { +const deployBaseSetup = async ({ accounts, batchDeployFn, tokenCount = 3 }) => { const ERC20FeeProxy = artifacts.require('ERC20FeeProxy'); const TestTRC20 = artifacts.require('TestTRC20'); @@ -48,9 +47,6 @@ const deployBaseSetup = async ({ accounts, batchDeployFn, batchFee, tokenCount = let batch = null; if (batchDeployFn) { batch = await batchDeployFn(erc20FeeProxy, owner, dummyEthProxy); - if (batchFee !== undefined && batch.setBatchFee) { - await batch.setBatchFee(batchFee, { from: owner }); - } } const tokens = []; @@ -79,7 +75,7 @@ const deployTokenWithSupply = async (supply, payer) => { }; /** - * Runs fn and asserts tracked balances are unchanged (source of truth Tron when Tron tx reverts). + * Runs fn and asserts tracked balances are unchanged (source of truth on Tron when tx reverts). */ const expectRevertOrNoBalanceChange = async (fn, getBalances) => { const before = await getBalances(); @@ -130,6 +126,7 @@ module.exports = { REF_B, REF_C, TRON_ZERO_ADDRESS, + ZERO_ADDRESS, ONE_TRX_SUN, waitForConfirmation, balanceOf, @@ -137,7 +134,6 @@ module.exports = { diff, sumStrings, mulString, - computeBatchFee, getApprovalAmount, deployBaseSetup, makeTokenApproval, diff --git a/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol b/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol index 4a190b0d60..a77c7cc719 100644 --- a/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol +++ b/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol @@ -12,12 +12,18 @@ import './lib/SafeERC20.sol'; * @dev Uses ERC20FeeProxy to pay an invoice and fees, with a payment reference. * Make sure this contract has allowance to spend the payer's tokens. * Make sure the payer has enough tokens to pay the amounts and fees. + * This contract emits no events. Index TransferWithReferenceAndFee on ERC20FeeProxy + * with msg.sender == address(this batch contract). + * The proxy receives a one-time max allowance per token. */ contract ERC20BatchPayments { using SafeERC20 for IERC20; IERC20FeeProxy public immutable paymentErc20FeeProxy; + /// @dev True after unlimited proxy approval was set for a token (avoids repeated approve calls). + mapping(address => bool) private _proxyApproved; + struct Token { address tokenAddress; uint256 amountAndFee; @@ -54,15 +60,32 @@ contract ERC20BatchPayments { _recipients.length == _feeAmounts.length, 'the input arrays must have the same length' ); + require(_tokenAddress != address(0), 'ERC20BatchPayments: token cannot be 0x'); uint256 amountAndFee = 0; - for (uint256 i = 0; i < _recipients.length; i++) { + for (uint256 i = 0; i < _recipients.length; ) { amountAndFee += _amounts[i] + _feeAmounts[i]; + unchecked { + ++i; + } } - _transferToContractAndApproveProxy(IERC20(_tokenAddress), amountAndFee); + if (amountAndFee > 0) { + _transferToContractAndApproveProxy(IERC20(_tokenAddress), amountAndFee); + } - for (uint256 i = 0; i < _recipients.length; i++) { + for (uint256 i = 0; i < _recipients.length; ) { + uint256 paymentSum = _amounts[i] + _feeAmounts[i]; + if (paymentSum == 0) { + unchecked { + ++i; + } + continue; + } + require(_recipients[i] != address(0), 'ERC20BatchPayments: recipient cannot be 0x'); + if (_feeAmounts[i] > 0) { + require(_feeAddress != address(0), 'ERC20BatchPayments: feeAddress cannot be 0x when fee > 0'); + } paymentErc20FeeProxy.transferFromWithReferenceAndFee( _tokenAddress, _recipients[i], @@ -71,6 +94,9 @@ contract ERC20BatchPayments { _feeAmounts[i], _feeAddress ); + unchecked { + ++i; + } } } @@ -100,8 +126,9 @@ contract ERC20BatchPayments { ); Token[] memory uniqueTokens = new Token[](_tokenAddresses.length); - for (uint256 i = 0; i < _tokenAddresses.length; i++) { - for (uint256 j = 0; j < _tokenAddresses.length; j++) { + for (uint256 i = 0; i < _tokenAddresses.length; ) { + require(_tokenAddresses[i] != address(0), 'ERC20BatchPayments: token cannot be 0x'); + for (uint256 j = 0; j < _tokenAddresses.length; ) { if (uniqueTokens[j].tokenAddress == _tokenAddresses[i]) { uniqueTokens[j].amountAndFee += _amounts[i] + _feeAmounts[i]; break; @@ -111,17 +138,37 @@ contract ERC20BatchPayments { uniqueTokens[j].amountAndFee = _amounts[i] + _feeAmounts[i]; break; } + unchecked { + ++j; + } + } + unchecked { + ++i; } } - for (uint256 i = 0; i < uniqueTokens.length && uniqueTokens[i].amountAndFee > 0; i++) { + for (uint256 i = 0; i < uniqueTokens.length && uniqueTokens[i].amountAndFee > 0; ) { _transferToContractAndApproveProxy( IERC20(uniqueTokens[i].tokenAddress), uniqueTokens[i].amountAndFee ); + unchecked { + ++i; + } } - for (uint256 i = 0; i < _recipients.length; i++) { + for (uint256 i = 0; i < _recipients.length; ) { + uint256 paymentSum = _amounts[i] + _feeAmounts[i]; + if (paymentSum == 0) { + unchecked { + ++i; + } + continue; + } + require(_recipients[i] != address(0), 'ERC20BatchPayments: recipient cannot be 0x'); + if (_feeAmounts[i] > 0) { + require(_feeAddress != address(0), 'ERC20BatchPayments: feeAddress cannot be 0x when fee > 0'); + } paymentErc20FeeProxy.transferFromWithReferenceAndFee( _tokenAddresses[i], _recipients[i], @@ -130,21 +177,15 @@ contract ERC20BatchPayments { _feeAmounts[i], _feeAddress ); + unchecked { + ++i; + } } } - /** - * @notice Authorizes the proxy to spend a request currency (ERC20). - * @param _erc20Address Address of an ERC20 used as the request currency. - */ - function approvePaymentProxyToSpend(address _erc20Address) internal { - IERC20 erc20 = IERC20(_erc20Address); - uint256 max = type(uint256).max; - require(erc20.safeApprove(address(paymentErc20FeeProxy), max), 'approve() failed'); - } - /** * @notice Pulls tokens from the payer to this contract and approves the proxy to spend them. + * @dev Approves the proxy once per token with max allowance; later batches skip approve. * @param requestedToken The token to pay. * @param amountAndFee The sum of payment amounts and fees for this token. */ @@ -152,18 +193,18 @@ contract ERC20BatchPayments { IERC20 requestedToken, uint256 amountAndFee ) internal { - require( - requestedToken.allowance(msg.sender, address(this)) >= amountAndFee, - 'Not sufficient allowance for batch to pay' - ); - require(requestedToken.balanceOf(msg.sender) >= amountAndFee, 'not enough funds'); require( requestedToken.safeTransferFrom(msg.sender, address(this), amountAndFee), 'payment transferFrom() failed' ); - if (requestedToken.allowance(address(this), address(paymentErc20FeeProxy)) < amountAndFee) { - approvePaymentProxyToSpend(address(requestedToken)); + address token = address(requestedToken); + if (!_proxyApproved[token]) { + require( + requestedToken.safeApprove(address(paymentErc20FeeProxy), type(uint256).max), + 'approve() failed' + ); + _proxyApproved[token] = true; } } } diff --git a/packages/smart-contracts/tron/contracts/interfaces/EthereumFeeProxy.sol b/packages/smart-contracts/tron/contracts/interfaces/EthereumFeeProxy.sol deleted file mode 120000 index 2ba444a117..0000000000 --- a/packages/smart-contracts/tron/contracts/interfaces/EthereumFeeProxy.sol +++ /dev/null @@ -1 +0,0 @@ -../../../src/contracts/interfaces/EthereumFeeProxy.sol \ No newline at end of file From 53043a09ce60783b58d4b65bbc3dc5bd3b040ab5 Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Thu, 28 May 2026 12:50:30 +0200 Subject: [PATCH 14/19] fix: make helper method not swallow errors --- packages/smart-contracts/package.json | 2 +- .../test/tron/ERC20BatchPayments.test.js | 79 +-- packages/smart-contracts/test/tron/helpers.js | 20 +- yarn.lock | 574 +++++++++++++++--- 4 files changed, 522 insertions(+), 153 deletions(-) diff --git a/packages/smart-contracts/package.json b/packages/smart-contracts/package.json index 65a729817e..0de3e68d0e 100644 --- a/packages/smart-contracts/package.json +++ b/packages/smart-contracts/package.json @@ -107,7 +107,7 @@ "ganache-cli": "6.12.0", "hardhat": "2.26.5", "solhint": "3.3.6", - "tronbox": "4.7.0", + "tronbox": "4.7.1", "tronweb": "5.3.2", "typechain": "8.3.2", "typescript": "5.8.3", diff --git a/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js b/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js index ff858a3ec4..453292c083 100644 --- a/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js +++ b/packages/smart-contracts/test/tron/ERC20BatchPayments.test.js @@ -11,7 +11,7 @@ const { deployBaseSetup, makeTokenApproval, deployTokenWithSupply, - expectRevertOrNoBalanceChange, + assertBalancesUnchanged, assertBatchTokenBalancesZero, deployBadTRC20, sumStrings, @@ -498,7 +498,7 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { ); const payee3Before = await balanceOf(lowToken, payee3); - const { unchanged } = await expectRevertOrNoBalanceChange( + await assertBalancesUnchanged( () => batch.batchERC20PaymentsWithReference( lowToken.address, @@ -510,9 +510,8 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { { from: payer }, ), async () => [await balanceOf(lowToken, payee3)], + 'should not transfer when funds insufficient', ); - - assert(unchanged, 'should not transfer when funds insufficient'); assert.equal((await balanceOf(lowToken, payee3)).toString(), payee3Before.toString()); }); @@ -531,7 +530,7 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { ); const payee1Before = await balanceOf(lowToken, payee1); - const { unchanged } = await expectRevertOrNoBalanceChange( + await assertBalancesUnchanged( () => batch.batchERC20PaymentsWithReference( lowToken.address, @@ -543,9 +542,8 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { { from: payer }, ), async () => [await balanceOf(lowToken, payee1)], + 'should not transfer when fees cannot be paid', ); - - assert(unchanged, 'should not transfer when fees cannot be paid'); assert.equal((await balanceOf(lowToken, payee1)).toString(), payee1Before.toString()); }); @@ -567,7 +565,7 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { await waitForConfirmation(2000); const payee1Before = await balanceOf(token1, payee1); - const { unchanged } = await expectRevertOrNoBalanceChange( + await assertBalancesUnchanged( () => batch.batchERC20PaymentsWithReference( token1.address, @@ -579,9 +577,8 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { { from: payer }, ), async () => [await balanceOf(token1, payee1)], + 'should not transfer without allowance', ); - - assert(unchanged, 'should not transfer without allowance'); assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); }); @@ -600,7 +597,7 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { const payee1Before = await balanceOf(token1, payee1); const payee2Before = await balanceOf(token1, payee2); - const { unchanged } = await expectRevertOrNoBalanceChange( + await assertBalancesUnchanged( () => batch.batchERC20PaymentsWithReference( token1.address, @@ -612,9 +609,8 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { { from: payer }, ), async () => [await balanceOf(token1, payee1), await balanceOf(token1, payee2)], + 'should not transfer when array lengths mismatch', ); - - assert(unchanged, 'should not transfer when array lengths mismatch'); assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); assert.equal((await balanceOf(token1, payee2)).toString(), payee2Before.toString()); }); @@ -626,7 +622,7 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); const payee1Before = await balanceOf(token1, payee1); - const { unchanged } = await expectRevertOrNoBalanceChange( + await assertBalancesUnchanged( () => batch.batchERC20PaymentsWithReference( ZERO_ADDRESS, @@ -638,9 +634,8 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { { from: payer }, ), async () => [await balanceOf(token1, payee1)], + 'should not transfer when token address is zero', ); - - assert(unchanged, 'should not transfer when token address is zero'); assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); }); @@ -651,7 +646,7 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); const payee1Before = await balanceOf(token1, payee1); - const { unchanged } = await expectRevertOrNoBalanceChange( + await assertBalancesUnchanged( () => batch.batchERC20PaymentsWithReference( token1.address, @@ -663,9 +658,8 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { { from: payer }, ), async () => [await balanceOf(token1, payee1)], + 'should not transfer when recipient is zero', ); - - assert(unchanged, 'should not transfer when recipient is zero'); assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); }); @@ -676,7 +670,7 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); const payee1Before = await balanceOf(token1, payee1); - const { unchanged } = await expectRevertOrNoBalanceChange( + await assertBalancesUnchanged( () => batch.batchERC20PaymentsWithReference( token1.address, @@ -688,9 +682,8 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { { from: payer }, ), async () => [await balanceOf(token1, payee1)], + 'should not transfer when feeAddress is zero and fee is non-zero', ); - - assert(unchanged, 'should not transfer when feeAddress is zero and fee is non-zero'); assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); }); }); @@ -713,7 +706,7 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { ); const payee3Before = await balanceOf(lowToken, payee3); - const { unchanged } = await expectRevertOrNoBalanceChange( + await assertBalancesUnchanged( () => batch.batchERC20PaymentsMultiTokensWithReference( [lowToken.address, lowToken.address, lowToken.address], @@ -725,9 +718,8 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { { from: payer }, ), async () => [await balanceOf(lowToken, payee3)], + 'multi-token batch should not transfer when funds insufficient', ); - - assert(unchanged, 'multi-token batch should not transfer when funds insufficient'); assert.equal((await balanceOf(lowToken, payee3)).toString(), payee3Before.toString()); }); @@ -749,7 +741,7 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { await waitForConfirmation(2000); const payee1Before = await balanceOf(token1, payee1); - const { unchanged } = await expectRevertOrNoBalanceChange( + await assertBalancesUnchanged( () => batch.batchERC20PaymentsMultiTokensWithReference( [token1.address, token1.address, token1.address], @@ -761,9 +753,8 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { { from: payer }, ), async () => [await balanceOf(token1, payee1)], + 'multi-token batch should not transfer without allowance', ); - - assert(unchanged, 'multi-token batch should not transfer without allowance'); assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); }); @@ -776,7 +767,7 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); const payee2Token2Before = await balanceOf(token2, payee2); - const { unchanged } = await expectRevertOrNoBalanceChange( + await assertBalancesUnchanged( () => batch.batchERC20PaymentsMultiTokensWithReference( [token1.address, token2.address], @@ -788,9 +779,8 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { { from: payer }, ), async () => [await balanceOf(token2, payee2)], + 'should not transfer when one token lacks approval', ); - - assert(unchanged, 'should not transfer when one token lacks approval'); assert.equal((await balanceOf(token2, payee2)).toString(), payee2Token2Before.toString()); }); @@ -805,7 +795,7 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { const payee1Before = await balanceOf(token1, payee1); const payee2Before = await balanceOf(token2, payee2); - const { unchanged } = await expectRevertOrNoBalanceChange( + await assertBalancesUnchanged( () => batch.batchERC20PaymentsMultiTokensWithReference( [token1.address, token2.address], @@ -817,9 +807,8 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { { from: payer }, ), async () => [await balanceOf(token1, payee1), await balanceOf(token2, payee2)], + 'should not transfer when array lengths mismatch', ); - - assert(unchanged, 'should not transfer when array lengths mismatch'); assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); assert.equal((await balanceOf(token2, payee2)).toString(), payee2Before.toString()); }); @@ -831,7 +820,7 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); const payee1Before = await balanceOf(token1, payee1); - const { unchanged } = await expectRevertOrNoBalanceChange( + await assertBalancesUnchanged( () => batch.batchERC20PaymentsMultiTokensWithReference( [ZERO_ADDRESS], @@ -843,9 +832,8 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { { from: payer }, ), async () => [await balanceOf(token1, payee1)], + 'multi-token batch should not transfer when token address is zero', ); - - assert(unchanged, 'multi-token batch should not transfer when token address is zero'); assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); }); @@ -856,7 +844,7 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); const payee1Before = await balanceOf(token1, payee1); - const { unchanged } = await expectRevertOrNoBalanceChange( + await assertBalancesUnchanged( () => batch.batchERC20PaymentsMultiTokensWithReference( [token1.address], @@ -868,9 +856,8 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { { from: payer }, ), async () => [await balanceOf(token1, payee1)], + 'multi-token batch should not transfer when recipient is zero', ); - - assert(unchanged, 'multi-token batch should not transfer when recipient is zero'); assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); }); @@ -881,7 +868,7 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { await makeTokenApproval(token1, payer, batch.address, getApprovalAmount([amount1], [fee1])); const payee1Before = await balanceOf(token1, payee1); - const { unchanged } = await expectRevertOrNoBalanceChange( + await assertBalancesUnchanged( () => batch.batchERC20PaymentsMultiTokensWithReference( [token1.address], @@ -893,10 +880,6 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { { from: payer }, ), async () => [await balanceOf(token1, payee1)], - ); - - assert( - unchanged, 'multi-token batch should not transfer when feeAddress is zero and fee is non-zero', ); assert.equal((await balanceOf(token1, payee1)).toString(), payee1Before.toString()); @@ -908,19 +891,13 @@ contract('ERC20BatchPayments Tron Test Suite', (accounts) => { contract('ERC20BatchPayments constructor', () => { it('should revert when paymentErc20FeeProxy is the zero address', async () => { let reverted = false; - let errorMessage = ''; try { await ERC20BatchPayments.new(ZERO_ADDRESS); } catch (error) { reverted = true; - errorMessage = error.message || String(error); } assert(reverted, 'deployment should revert when paymentErc20FeeProxy is address(0)'); - assert( - errorMessage.includes('paymentErc20FeeProxy cannot be 0x'), - `expected zero-address revert, got: ${errorMessage}`, - ); }); }); diff --git a/packages/smart-contracts/test/tron/helpers.js b/packages/smart-contracts/test/tron/helpers.js index d9e2ff8de2..ba15ead179 100644 --- a/packages/smart-contracts/test/tron/helpers.js +++ b/packages/smart-contracts/test/tron/helpers.js @@ -75,17 +75,21 @@ const deployTokenWithSupply = async (supply, payer) => { }; /** - * Runs fn and asserts tracked balances are unchanged (source of truth on Tron when tx reverts). + * Runs fn (errors propagate) and asserts tracked balances are unchanged. */ -const expectRevertOrNoBalanceChange = async (fn, getBalances) => { +const assertBalancesUnchanged = async ( + fn, + getBalances, + message = 'balances should be unchanged', +) => { const before = await getBalances(); - try { - await fn(); - } catch (_error) {} + await fn(); await waitForConfirmation(2000); const after = await getBalances(); - const unchanged = before.every((value, index) => value === after[index]); - return { unchanged }; + assert( + before.every((value, index) => value === after[index]), + message, + ); }; /** @@ -138,7 +142,7 @@ module.exports = { deployBaseSetup, makeTokenApproval, deployTokenWithSupply, - expectRevertOrNoBalanceChange, + assertBalancesUnchanged, assertBatchTokenBalancesZero, expectNonOwnerReverts, deployBadTRC20, diff --git a/yarn.lock b/yarn.lock index 1808ce4151..9118b2f639 100644 --- a/yarn.lock +++ b/yarn.lock @@ -773,6 +773,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/runtime@7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.10.tgz#a07b4d8fa27af131a633d7b3524db803eb4764c2" + integrity sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.0.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4": version "7.27.0" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz" @@ -5484,6 +5491,11 @@ dependencies: "@sinonjs/commons" "^3.0.0" +"@solidity-parser/parser@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.20.2.tgz#e07053488ed60dae1b54f6fe37bb6d2c5fe146a7" + integrity sha512-rbu0bzwNvMcwAjH86hiEAcOeRI2EeK8zCkHDrFykh/Al8mvJeFmjy3UrE7GYQjNwOgbGUUtCn5/k8CB8zIu7QA== + "@solidity-parser/parser@^0.13.2": version "0.13.2" resolved "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.13.2.tgz" @@ -7143,6 +7155,16 @@ ajv-keywords@^3.5.2: resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== +ajv@6.14.0: + version "6.14.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.14.0.tgz#fd067713e228210636ebb08c60bd3765d6dbe73a" + integrity sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + ajv@8.17.1, ajv@^8.0.0, ajv@^8.0.1, ajv@^8.12.0: version "8.17.1" resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz" @@ -7633,7 +7655,7 @@ assert@^1.4.0: object-assign "^4.1.1" util "0.10.3" -assertion-error@^1.1.0: +assertion-error@^1.0.1, assertion-error@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== @@ -7703,18 +7725,18 @@ async@2.6.2: dependencies: lodash "^4.17.11" -async@^1.4.2: - version "1.5.2" - resolved "https://registry.npmjs.org/async/-/async-1.5.2.tgz" - integrity sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w== - -async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0, async@^2.6.1: +async@2.6.4, async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0, async@^2.6.1: version "2.6.4" resolved "https://registry.npmjs.org/async/-/async-2.6.4.tgz" integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== dependencies: lodash "^4.17.14" +async@^1.4.2: + version "1.5.2" + resolved "https://registry.npmjs.org/async/-/async-1.5.2.tgz" + integrity sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w== + async@^3.2.3: version "3.2.5" resolved "https://registry.npmjs.org/async/-/async-3.2.5.tgz" @@ -7774,6 +7796,25 @@ aws4@^1.8.0: resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +axios@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.15.0.tgz#0fcee91ef03d386514474904b27863b2c683bf4f" + integrity sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q== + dependencies: + follow-redirects "^1.15.11" + form-data "^4.0.5" + proxy-from-env "^2.1.0" + +axios@1.16.1: + version "1.16.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.16.1.tgz#517e29291d19d6e8cf919ff264f4fe157261ba12" + integrity sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A== + dependencies: + follow-redirects "^1.16.0" + form-data "^4.0.5" + https-proxy-agent "^5.0.1" + proxy-from-env "^2.1.0" + axios@^1.0.0, axios@^1.4.0, axios@^1.6.8: version "1.8.4" resolved "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz" @@ -8433,6 +8474,11 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +balanced-match@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" + integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== + base-x@^2.0.1: version "2.0.6" resolved "https://registry.npmjs.org/base-x/-/base-x-2.0.6.tgz" @@ -8552,11 +8598,16 @@ bignumber.js@*: resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz" integrity sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw== -bignumber.js@^7.2.1: +bignumber.js@7.2.1, bignumber.js@^7.2.1: version "7.2.1" resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz" integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== +bignumber.js@9.1.2: + version "9.1.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" + integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== + bignumber.js@^9.0.0, bignumber.js@^9.0.1: version "9.0.1" resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz" @@ -8712,6 +8763,20 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" +brace-expansion@^2.0.2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.1.tgz#c68b1c4111c76aae3a6fba55d496cee10c39dad8" + integrity sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA== + dependencies: + balanced-match "^1.0.0" + +brace-expansion@^5.0.5: + version "5.0.6" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.6.tgz#ec68fe0a641a29d8711579caf641d05bae1f2285" + integrity sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g== + dependencies: + balanced-match "^4.0.2" + braces@^1.8.2: version "1.8.5" resolved "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz" @@ -8797,7 +8862,7 @@ browser-stdout@1.3.0: resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz" integrity sha1-81HTKWnTL6XXpVZxVCY9korjvR8= -browser-stdout@1.3.1: +browser-stdout@1.3.1, browser-stdout@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== @@ -8999,6 +9064,11 @@ buffer-alloc@^1.2.0: buffer-alloc-unsafe "^1.1.0" buffer-fill "^1.0.0" +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + buffer-equal@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz" @@ -9458,6 +9528,18 @@ cbor@^8.1.0: dependencies: nofilter "^3.1.0" +chai@4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.1.2.tgz#0f64584ba642f0f2ace2806279f4f06ca23ad73c" + integrity sha512-YTHf80rJ8M5/cJoFKEV1y3PnexbGs0vSHjouRRU8gLM05Nc3Mqq9zor/P4SCqB/sgvKRLvya7wHLC1XQ9pTjgQ== + dependencies: + assertion-error "^1.0.1" + check-error "^1.0.1" + deep-eql "^3.0.0" + get-func-name "^2.0.0" + pathval "^1.0.0" + type-detect "^4.0.0" + chai@4.3.4: version "4.3.4" resolved "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz" @@ -9470,6 +9552,15 @@ chai@4.3.4: pathval "^1.1.1" type-detect "^4.0.5" +chalk@2.4.2, chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@4.1.0, chalk@^4.0.0, chalk@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz" @@ -9497,15 +9588,6 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - change-case-all@1.0.14: version "1.0.14" resolved "https://registry.npmjs.org/change-case-all/-/change-case-all-1.0.14.tgz" @@ -9590,7 +9672,7 @@ chardet@^0.7.0: resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -check-error@^1.0.2: +check-error@^1.0.1, check-error@^1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz" integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== @@ -9744,6 +9826,13 @@ chokidar@^4.0.0: dependencies: readdirp "^4.0.1" +chokidar@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== + dependencies: + readdirp "^4.0.1" + chownr@^1.0.1, chownr@^1.1.1, chownr@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz" @@ -10851,6 +10940,13 @@ debug@3.2.6: dependencies: ms "^2.1.1" +debug@3.2.7, debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" @@ -10858,13 +10954,6 @@ debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, de dependencies: ms "2.1.2" -debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: - version "3.2.7" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - debug@^4.3.5: version "4.3.7" resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz" @@ -10926,7 +11015,7 @@ dedent@^1.0.0: resolved "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz" integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== -deep-eql@^3.0.1: +deep-eql@^3.0.0, deep-eql@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz" integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== @@ -11228,6 +11317,11 @@ diff@^4.0.1: resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +diff@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-7.0.0.tgz#3fb34d387cd76d803f6eebea67b921dab0182a9a" + integrity sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz" @@ -11636,6 +11730,14 @@ enhanced-resolve@^5.17.1: graceful-fs "^4.2.4" tapable "^2.2.0" +enquirer@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" + integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== + dependencies: + ansi-colors "^4.1.1" + strip-ansi "^6.0.1" + enquirer@^2.3.0, enquirer@^2.3.5, enquirer@^2.3.6, enquirer@~2.3.6: version "2.3.6" resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz" @@ -11966,7 +12068,7 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escape-string-regexp@4.0.0: +escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== @@ -12429,6 +12531,16 @@ ethereum-common@^0.0.18: resolved "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.18.tgz" integrity sha512-EoltVQTRNg2Uy4o84qpa2aXymXDJhxm7eos/ACOg0DG4baAbMjhbdAEsx9GeE8sC3XCxnYvrrzZDH8D8MtA2iQ== +ethereum-cryptography@2.2.1, ethereum-cryptography@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz#58f2810f8e020aecb97de8c8c76147600b0b8ccf" + integrity sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg== + dependencies: + "@noble/curves" "1.4.2" + "@noble/hashes" "1.4.0" + "@scure/bip32" "1.4.0" + "@scure/bip39" "1.3.0" + ethereum-cryptography@^0.1.3: version "0.1.3" resolved "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz" @@ -12470,16 +12582,6 @@ ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2: "@scure/bip32" "1.3.1" "@scure/bip39" "1.2.1" -ethereum-cryptography@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz#58f2810f8e020aecb97de8c8c76147600b0b8ccf" - integrity sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg== - dependencies: - "@noble/curves" "1.4.2" - "@noble/hashes" "1.4.0" - "@scure/bip32" "1.4.0" - "@scure/bip39" "1.3.0" - ethereum-waffle@3.4.4: version "3.4.4" resolved "https://registry.npmjs.org/ethereum-waffle/-/ethereum-waffle-3.4.4.tgz" @@ -12767,6 +12869,32 @@ ethers@5.7.2, ethers@^5.0.1, ethers@^5.0.2, ethers@^5.5.2, ethers@^5.7.0, ethers "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" +ethers@6.13.5: + version "6.13.5" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.5.tgz#8c1d6ac988ac08abc3c1d8fabbd4b8b602851ac4" + integrity sha512-+knKNieu5EKRThQJWwqaJ10a6HE9sSehGeqWN65//wE7j47ZpFhKAnHB/JJFibwwg61I/koxaPsXbXpD/skNOQ== + dependencies: + "@adraffy/ens-normalize" "1.10.1" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" + "@types/node" "22.7.5" + aes-js "4.0.0-beta.5" + tslib "2.7.0" + ws "8.17.1" + +ethers@6.15.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.15.0.tgz#2980f2a3baf0509749b7e21f8692fa8a8349c0e3" + integrity sha512-Kf/3ZW54L4UT0pZtsY/rf+EkBU7Qi5nnhonjUb8yTXcxH3cdcWrV2cRyk0Xk/4jK6OoHhxxZHriyhje20If2hQ== + dependencies: + "@adraffy/ens-normalize" "1.10.1" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" + "@types/node" "22.7.5" + aes-js "4.0.0-beta.5" + tslib "2.7.0" + ws "8.17.1" + ethers@^4.0.32: version "4.0.48" resolved "https://registry.npmjs.org/ethers/-/ethers-4.0.48.tgz" @@ -13548,6 +13676,14 @@ find-root@^1.0.0: resolved "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz" integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== +find-up@4.1.0, find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + find-up@5.0.0, find-up@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" @@ -13578,14 +13714,6 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@^4.0.0, find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - find-yarn-workspace-root@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz" @@ -13658,6 +13786,11 @@ follow-redirects@^1.12.1, follow-redirects@^1.15.6: resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz" integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== +follow-redirects@^1.15.11, follow-redirects@^1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.16.0.tgz#28474a159d3b9d11ef62050a14ed60e4df6d61bc" + integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw== + fontkit@^1.8.0: version "1.8.1" resolved "https://registry.npmjs.org/fontkit/-/fontkit-1.8.1.tgz" @@ -13744,7 +13877,7 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@^4.0.4: +form-data@^4.0.4, form-data@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== @@ -13811,6 +13944,15 @@ fs-constants@^1.0.0: resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs-extra@8.1.0, fs-extra@^8.0.1: + version "8.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@9.1.0, fs-extra@^9.0.0, fs-extra@^9.0.1, fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" @@ -13886,15 +14028,6 @@ fs-extra@^7.0.0, fs-extra@^7.0.1: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^8.0.1: - version "8.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-minipass@^1.2.7: version "1.2.7" resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz" @@ -14331,6 +14464,15 @@ glob-to-regexp@^0.4.1: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== +glob@13.0.6: + version "13.0.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-13.0.6.tgz#078666566a425147ccacfbd2e332deb66a2be71d" + integrity sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw== + dependencies: + minimatch "^10.2.2" + minipass "^7.1.3" + path-scurry "^2.0.2" + glob@7.1.2: version "7.1.2" resolved "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz" @@ -14390,6 +14532,18 @@ glob@^10.2.2: minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-scurry "^1.10.1" +glob@^10.4.5: + version "10.5.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" + integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^8.0.1: version "8.1.0" resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" @@ -14547,6 +14701,11 @@ globby@^8.0.1: pify "^3.0.0" slash "^1.0.0" +google-protobuf@3.21.4: + version "3.21.4" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.21.4.tgz#2f933e8b6e5e9f8edde66b7be0024b68f77da6c9" + integrity sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ== + gopd@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" @@ -14635,6 +14794,13 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6 resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +graphlib@2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da" + integrity sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A== + dependencies: + lodash "^4.17.15" + graphql-config@^5.0.2: version "5.0.3" resolved "https://registry.npmjs.org/graphql-config/-/graphql-config-5.0.3.tgz" @@ -15079,6 +15245,11 @@ homedir-polyfill@^1.0.1: dependencies: parse-passwd "^1.0.0" +homedir@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/homedir/-/homedir-0.6.0.tgz#2b21db66bf08a6db38249a3eff52d7d18706af1e" + integrity sha512-KZFBHenkVuyyG4uaqRSXqWJr3HTxcaPguM7rU1BlH/mtbDlzaXNSXTa9AhV+fXEjrNemHu9vtLRIaM8/8OW0xA== + hoopy@^0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz" @@ -15297,6 +15468,14 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +https-proxy-agent@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + https-proxy-agent@^7.0.0: version "7.0.2" resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz" @@ -16134,7 +16313,7 @@ is-path-cwd@^2.2.0: resolved "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz" integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== -is-path-inside@^3.0.2: +is-path-inside@^3.0.2, is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== @@ -16499,6 +16678,15 @@ jackspeak@^2.3.5: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jake@^10.8.5: version "10.8.7" resolved "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz" @@ -17857,6 +18045,11 @@ lodash@4.17.20: resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== +lodash@4.18.1: + version "4.18.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c" + integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q== + lodash@^4.14.2, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@~4.17.0: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" @@ -17973,11 +18166,16 @@ lru-cache@5.1.1, lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -lru-cache@^10.4.3: +lru-cache@^10.2.0, lru-cache@^10.4.3: version "10.4.3" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== +lru-cache@^11.0.0: + version "11.5.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.5.1.tgz#f3daa3540847b9737ebc02499ddb36765e54db4a" + integrity sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A== + lru-cache@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz" @@ -18545,6 +18743,13 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" +minimatch@^10.2.2: + version "10.2.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1" + integrity sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg== + dependencies: + brace-expansion "^5.0.5" + minimatch@^3.0.0, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" @@ -18552,6 +18757,13 @@ minimatch@^3.0.0, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimatch@^3.0.2: + version "3.1.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e" + integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== + dependencies: + brace-expansion "^1.1.7" + minimatch@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/minimatch/-/minimatch-4.2.3.tgz" @@ -18594,6 +18806,13 @@ minimatch@^9.0.0, minimatch@^9.0.1: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.4, minimatch@^9.0.5: + version "9.0.9" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.9.tgz#9b0cb9fcb78087f6fd7eababe2511c4d3d60574e" + integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg== + dependencies: + brace-expansion "^2.0.2" + minimist-options@4.1.0, minimist-options@^4.0.2: version "4.1.0" resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz" @@ -18703,6 +18922,11 @@ minipass@^5.0.0: resolved "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz" integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== +minipass@^7.1.2, minipass@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b" + integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== + minizlib@^1.3.3: version "1.3.3" resolved "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz" @@ -18759,6 +18983,13 @@ mkdirp@0.5.1: dependencies: minimist "0.0.8" +mkdirp@0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + mkdirp@^0.5.1, mkdirp@^0.5.5: version "0.5.5" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" @@ -18788,6 +19019,33 @@ mnemonist@^0.38.0: dependencies: obliterator "^1.6.1" +mocha@11.7.4: + version "11.7.4" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.7.4.tgz#f161b17aeccb0762484b33bdb3f7ab9410ba5c82" + integrity sha512-1jYAaY8x0kAZ0XszLWu14pzsf4KV740Gld4HXkhNTXwcHx4AUEDkPzgEHg9CM5dVcW+zv036tjpsEbLraPJj4w== + dependencies: + browser-stdout "^1.3.1" + chokidar "^4.0.1" + debug "^4.3.5" + diff "^7.0.0" + escape-string-regexp "^4.0.0" + find-up "^5.0.0" + glob "^10.4.5" + he "^1.2.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + log-symbols "^4.1.0" + minimatch "^9.0.5" + ms "^2.1.3" + picocolors "^1.1.1" + serialize-javascript "^6.0.2" + strip-json-comments "^3.1.1" + supports-color "^8.1.1" + workerpool "^9.2.0" + yargs "^17.7.2" + yargs-parser "^21.1.1" + yargs-unparser "^2.0.0" + mocha@^10.0.0: version "10.2.0" resolved "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz" @@ -19198,6 +19456,13 @@ node-dijkstra@2.5.0: resolved "https://registry.npmjs.org/node-dijkstra/-/node-dijkstra-2.5.0.tgz" integrity sha1-D+t2xaBfNbVueG3m300zZK8o1Og= +node-dir@0.1.17: + version "0.1.17" + resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" + integrity sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg== + dependencies: + minimatch "^3.0.2" + node-fetch-native@^1.6.4: version "1.6.4" resolved "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.4.tgz" @@ -19926,6 +20191,11 @@ ora@^5.4.1: strip-ansi "^6.0.0" wcwidth "^1.0.1" +original-require@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/original-require/-/original-require-1.0.1.tgz#0f130471584cd33511c5ec38c8d59213f9ac5e20" + integrity sha512-5vdKMbE58WaE61uVD+PKyh8xdM398UnjPBLotW2sjG5MzHARwta/+NtMBCBA0t2WQblGYBvq5vsiZpWokwno+A== + os-browserify@~0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz" @@ -20132,6 +20402,11 @@ p-waterfall@2.1.1: dependencies: p-reduce "^2.0.0" +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + pacote@15.1.1: version "15.1.1" resolved "https://registry.npmjs.org/pacote/-/pacote-15.1.1.tgz" @@ -20495,6 +20770,22 @@ path-scurry@^1.10.1, path-scurry@^1.6.1: lru-cache "^9.1.1 || ^10.0.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-scurry@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.2.tgz#6be0d0ee02a10d9e0de7a98bae65e182c9061f85" + integrity sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg== + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + path-to-regexp@0.1.10: version "0.1.10" resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz" @@ -20538,7 +20829,7 @@ pathe@^1.1.1, pathe@^1.1.2: resolved "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz" integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== -pathval@^1.1.1: +pathval@^1.0.0, pathval@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== @@ -20588,12 +20879,17 @@ pegjs@^0.10.0: resolved "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz" integrity sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0= +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picocolors@^1.0.0, picocolors@^1.1.0: +picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== @@ -20963,6 +21259,11 @@ proxy-from-env@^1.1.0: resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +proxy-from-env@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz#a7487568adad577cfaaa7e88c49cab3ab3081aba" + integrity sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA== + proxy-middleware@latest: version "0.15.0" resolved "https://registry.yarnpkg.com/proxy-middleware/-/proxy-middleware-0.15.0.tgz#a3fdf1befb730f951965872ac2f6074c61477a56" @@ -22140,7 +22441,7 @@ semver-compare@^1.0.0: resolved "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz" integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= -"semver@2 || 3 || 4 || 5", semver@7.3.8, semver@7.5.4, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^6.0.0, semver@^6.1.0, semver@^6.3.0, semver@^6.3.1, semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.1, semver@^7.5.3, semver@^7.5.4, semver@^7.7.1, semver@~5.4.1: +"semver@2 || 3 || 4 || 5", semver@7.3.8, semver@7.5.4, semver@7.7.1, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^6.0.0, semver@^6.1.0, semver@^6.3.0, semver@^6.3.1, semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.1, semver@^7.5.3, semver@^7.5.4, semver@^7.7.1, semver@~5.4.1: version "7.5.4" resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -22228,6 +22529,13 @@ serialize-javascript@^6.0.1: dependencies: randombytes "^2.1.0" +serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + serve-index@^1.9.1: version "1.9.1" resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz" @@ -22658,6 +22966,19 @@ solc@0.8.26: semver "^5.5.0" tmp "0.0.33" +solc@0.8.34: + version "0.8.34" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.34.tgz#be5fc555a9863f3f1b2d49d3f5f9897c48e433e7" + integrity sha512-qf8HajA1sHhXRV0hMSDXLjVbc4v3Q+SQbL9zok+1WmgVj7Z4oMjMHxaysCzfGtFVqjZdfDDJWyZI+tcx5bO7Dw== + dependencies: + command-exists "^1.2.8" + commander "^8.1.0" + follow-redirects "^1.12.1" + js-sha3 "0.8.0" + memorystream "^0.3.1" + semver "^5.5.0" + tmp "0.0.33" + solc@^0.4.20: version "0.4.26" resolved "https://registry.npmjs.org/solc/-/solc-0.4.26.tgz" @@ -22792,14 +23113,7 @@ source-map-support@0.5.19: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.4.15: - version "0.4.18" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz" - integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== - dependencies: - source-map "^0.5.6" - -source-map-support@^0.5.12, source-map-support@^0.5.13, source-map-support@^0.5.17, source-map-support@~0.5.12, source-map-support@~0.5.20: +source-map-support@0.5.21, source-map-support@^0.5.12, source-map-support@^0.5.13, source-map-support@^0.5.17, source-map-support@~0.5.12, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -22807,6 +23121,13 @@ source-map-support@^0.5.12, source-map-support@^0.5.13, source-map-support@^0.5. buffer-from "^1.0.0" source-map "^0.6.0" +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz" + integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== + dependencies: + source-map "^0.5.6" + source-map-url@^0.4.0: version "0.4.1" resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz" @@ -23483,7 +23804,7 @@ supports-color@4.4.0: dependencies: has-flag "^2.0.0" -supports-color@8.1.1, supports-color@^8.0.0: +supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== @@ -24082,6 +24403,40 @@ trim-right@^1.0.1: resolved "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz" integrity sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw== +tronbox@4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/tronbox/-/tronbox-4.7.1.tgz#7d00e2c6552a6d916c671db4377e03cf7824aa0c" + integrity sha512-BND9KGI45REKZBRiDsfjUSxlLeTM2H46rH4VJbbkwKTX1sS59/hgywiev383fXi2M9oS3M30c3l6ZzQqV2YysQ== + dependencies: + "@solidity-parser/parser" "0.20.2" + ajv "6.14.0" + async "2.6.4" + axios "1.16.1" + bignumber.js "7.2.1" + chai "4.1.2" + chalk "2.4.2" + colors "1.4.0" + debug "3.2.7" + enquirer "2.4.1" + ethers "6.15.0" + find-up "4.1.0" + fs-extra "8.1.0" + glob "13.0.6" + graphlib "2.1.8" + homedir "0.6.0" + lodash "4.18.1" + mkdirp "0.5.6" + mocha "11.7.4" + node-dir "0.1.17" + original-require "1.0.1" + solc "0.8.34" + source-map-support "0.5.21" + tmp "0.0.33" + tronweb "6.3.0" + vcsurl "0.1.1" + yargs "15.4.1" + yauzl "3.2.1" + tronweb@5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/tronweb/-/tronweb-5.3.2.tgz#393b0fa0290e2c5aa7a3b3b82956f53ca65a764f" @@ -24101,6 +24456,21 @@ tronweb@5.3.2: semver "^5.6.0" validator "^13.7.0" +tronweb@6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/tronweb/-/tronweb-6.3.0.tgz#66275aa792efa8378fadd6af5ad0b4fbfe0e4bc9" + integrity sha512-5CAjDO4/KfymgjKFgnXgfKKQp0xgOn8otCBYjgYAEIpLZDKNAk14Z0dDeg0UqYuceCiyMHjW7a19Rsz8EmhAOw== + dependencies: + "@babel/runtime" "7.26.10" + axios "1.15.0" + bignumber.js "9.1.2" + ethereum-cryptography "2.2.1" + ethers "6.13.5" + eventemitter3 "5.0.1" + google-protobuf "3.21.4" + semver "7.7.1" + validator "13.15.23" + try-to-catch@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/try-to-catch/-/try-to-catch-3.0.0.tgz" @@ -24650,11 +25020,6 @@ typescript@2.9.1: resolved "https://registry.npmjs.org/typescript/-/typescript-2.9.1.tgz" integrity sha512-h6pM2f/GDchCFlldnriOhs1QHuwbnmj6/v7499eMHqPeW4V2G0elua2eIc2nu8v2NdHV0Gm+tzX83Hr6nUFjQA== -typescript@4.8.4: - version "4.8.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" - integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== - typescript@5.8.3: version "5.8.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" @@ -25242,6 +25607,11 @@ validate-npm-package-name@^5.0.0: dependencies: builtins "^5.0.0" +validator@13.15.23: + version "13.15.23" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.23.tgz#59a874f84e4594588e3409ab1edbe64e96d0c62d" + integrity sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw== + validator@^13.7.0: version "13.15.26" resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.26.tgz#36c3deeab30e97806a658728a155c66fcaa5b944" @@ -25270,6 +25640,11 @@ vary@^1, vary@~1.1.2: resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= +vcsurl@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vcsurl/-/vcsurl-0.1.1.tgz#5e00a109e7381b55b5d45b892533c8ec35c9320c" + integrity sha512-UXJ+Vd9rBt7cIbqikL76aO+fwNM/lpliY2SkNrKFKenLoVSkKPOld9DWCDsHEi7o51yWrn3dH1+NuzmTFhmdQg== + verror@1.10.0: version "1.10.0" resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz" @@ -26631,6 +27006,11 @@ workerpool@6.2.1: resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== +workerpool@^9.2.0: + version "9.3.4" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-9.3.4.tgz#f6c92395b2141afd78e2a889e80cb338fe9fca41" + integrity sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg== + "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" @@ -26953,7 +27333,7 @@ yargs-parser@^8.1.0: dependencies: camelcase "^4.1.0" -yargs-unparser@2.0.0: +yargs-unparser@2.0.0, yargs-unparser@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz" integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== @@ -26980,6 +27360,23 @@ yargs@13.2.4: y18n "^4.0.0" yargs-parser "^13.1.0" +yargs@15.4.1, yargs@^15.3.1: + version "15.4.1" + resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + yargs@16.2.0, yargs@^16.2.0: version "16.2.0" resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" @@ -27040,23 +27437,6 @@ yargs@^13.3.2: y18n "^4.0.0" yargs-parser "^13.1.2" -yargs@^15.3.1: - version "15.4.1" - resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" - yargs@^17.0.0: version "17.0.1" resolved "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz" @@ -27103,6 +27483,14 @@ yargs@^4.7.1: y18n "^3.2.1" yargs-parser "^2.4.1" +yauzl@3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-3.2.1.tgz#d35befb9a0fdd328da41926be895ade2de14dbe7" + integrity sha512-k1isifdbpNSFEHFJ1ZY4YDewv0IH9FR61lDetaRMD3j2ae3bIXGV+7c+LHCqtQGofSd8PIyV4X6+dHMAnSr60A== + dependencies: + buffer-crc32 "~0.2.3" + pend "~1.2.0" + yn@3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" From ff40067eb7e12c6ba29ba0895139685891982b05 Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Thu, 28 May 2026 13:13:15 +0200 Subject: [PATCH 15/19] fix: conflicting contract build --- .github/workflows/tron-smart-contracts.yml | 8 ++++---- .gitignore | 1 + packages/smart-contracts/TRON_DEPLOYMENT.md | 2 +- packages/smart-contracts/package.json | 4 ++-- packages/smart-contracts/scripts/tron/deploy-mainnet.js | 2 +- packages/smart-contracts/scripts/tron/deploy-nile.js | 2 +- .../smart-contracts/scripts/tron/deploy-test-token.js | 2 +- .../smart-contracts/scripts/tron/verify-deployment.js | 2 +- packages/smart-contracts/tronbox-config.js | 5 ++--- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/tron-smart-contracts.yml b/.github/workflows/tron-smart-contracts.yml index a7cd610cff..01105ecc2a 100644 --- a/.github/workflows/tron-smart-contracts.yml +++ b/.github/workflows/tron-smart-contracts.yml @@ -61,11 +61,11 @@ jobs: working-directory: packages/smart-contracts run: | echo "Checking build artifacts..." - ls -la build/tron/ + ls -la build-tron/ # Verify key contracts were compiled for contract in ERC20FeeProxy TestTRC20 BadTRC20 TRC20True TRC20NoReturn TRC20False TRC20Revert; do - if [ ! -f "build/tron/${contract}.json" ]; then + if [ ! -f "build-tron/${contract}.json" ]; then echo "ERROR: ${contract}.json not found!" exit 1 fi @@ -81,7 +81,7 @@ jobs: # Check that the compiled contract has the expected functions for func in transferFromWithReferenceAndFee; do - if ! grep -q "$func" build/tron/ERC20FeeProxy.json; then + if ! grep -q "$func" build-tron/ERC20FeeProxy.json; then echo "ERROR: ERC20FeeProxy missing $func function!" exit 1 fi @@ -90,7 +90,7 @@ jobs: # Verify TestTRC20 has standard ERC20 functions for func in transfer approve transferFrom balanceOf allowance; do - if ! grep -q "$func" build/tron/TestTRC20.json; then + if ! grep -q "$func" build-tron/TestTRC20.json; then echo "ERROR: TestTRC20 missing $func function!" exit 1 fi diff --git a/.gitignore b/.gitignore index e767737c1b..ea539e70a0 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ tsconfig.build.tsbuildinfo /packages/smart-contracts/types/ /packages/smart-contracts/src/types/ /packages/smart-contracts/build-zk/ +/packages/smart-contracts/build-tron/ /packages/smart-contracts/cache-zk/ # security testing artifacts diff --git a/packages/smart-contracts/TRON_DEPLOYMENT.md b/packages/smart-contracts/TRON_DEPLOYMENT.md index 0cd43e8405..bc2d78cf38 100644 --- a/packages/smart-contracts/TRON_DEPLOYMENT.md +++ b/packages/smart-contracts/TRON_DEPLOYMENT.md @@ -33,7 +33,7 @@ Compile contracts for Tron: yarn tron:compile ``` -This creates artifacts in `build/tron/` directory. +This creates artifacts in `build-tron/` directory (outside Hardhat's `build/` to avoid artifact name collisions). ## Testing diff --git a/packages/smart-contracts/package.json b/packages/smart-contracts/package.json index 0de3e68d0e..1641c7d43e 100644 --- a/packages/smart-contracts/package.json +++ b/packages/smart-contracts/package.json @@ -35,11 +35,11 @@ "build:lib": "tsc -b tsconfig.build.json && cp src/types/*.d.ts dist/src/types && cp -r dist/src/types types", "prebuild:sol": "yarn workspace @requestnetwork/types build && yarn workspace @requestnetwork/utils build && yarn workspace @requestnetwork/currency build", "build:sol": "yarn hardhat compile", - "build:tron": "yarn tron:compile && typechain --target ethers-v5 --out-dir src/types/tron 'build/tron/*.json'", + "build:tron": "yarn tron:compile && typechain --target ethers-v5 --out-dir src/types/tron 'build-tron/*.json'", "build": "yarn build:sol && yarn build:tron && yarn build:lib", "clean:types": "rm -rf types && rm -rf src/types", "clean:lib": "rm -rf dist tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo", - "clean:hardhat": "rm -rf cache && rm -rf build", + "clean:hardhat": "rm -rf cache build build-tron", "clean": "yarn clean:lib && yarn clean:types && yarn clean:hardhat", "lint:lib": "eslint . --fix", "lint:lib:check": "eslint .", diff --git a/packages/smart-contracts/scripts/tron/deploy-mainnet.js b/packages/smart-contracts/scripts/tron/deploy-mainnet.js index d3f9ec34dd..e54e9fc24a 100644 --- a/packages/smart-contracts/scripts/tron/deploy-mainnet.js +++ b/packages/smart-contracts/scripts/tron/deploy-mainnet.js @@ -51,7 +51,7 @@ const tronWeb = new TronWeb({ privateKey: PRIVATE_KEY, }); -const ARTIFACTS_DIR = path.join(__dirname, '../../build/tron'); +const ARTIFACTS_DIR = path.join(__dirname, '../../build-tron'); async function loadArtifact(contractName) { const artifactPath = path.join(ARTIFACTS_DIR, `${contractName}.json`); diff --git a/packages/smart-contracts/scripts/tron/deploy-nile.js b/packages/smart-contracts/scripts/tron/deploy-nile.js index 5858affcf0..09eedde64b 100644 --- a/packages/smart-contracts/scripts/tron/deploy-nile.js +++ b/packages/smart-contracts/scripts/tron/deploy-nile.js @@ -32,7 +32,7 @@ const tronWeb = new TronWeb({ }); // Contract artifacts paths -const ARTIFACTS_DIR = path.join(__dirname, '../../build/tron'); +const ARTIFACTS_DIR = path.join(__dirname, '../../build-tron'); async function loadArtifact(contractName) { const artifactPath = path.join(ARTIFACTS_DIR, `${contractName}.json`); diff --git a/packages/smart-contracts/scripts/tron/deploy-test-token.js b/packages/smart-contracts/scripts/tron/deploy-test-token.js index 8422473f4a..77119f51bc 100644 --- a/packages/smart-contracts/scripts/tron/deploy-test-token.js +++ b/packages/smart-contracts/scripts/tron/deploy-test-token.js @@ -44,7 +44,7 @@ async function main() { } // Load compiled contract - const buildPath = path.join(__dirname, '../../build/tron/TestTRC20.json'); + const buildPath = path.join(__dirname, '../../build-tron/TestTRC20.json'); if (!fs.existsSync(buildPath)) { console.error('❌ Contract not compiled. Run: yarn tron:compile'); diff --git a/packages/smart-contracts/scripts/tron/verify-deployment.js b/packages/smart-contracts/scripts/tron/verify-deployment.js index 3bacc14daa..b3fa29a62e 100644 --- a/packages/smart-contracts/scripts/tron/verify-deployment.js +++ b/packages/smart-contracts/scripts/tron/verify-deployment.js @@ -41,7 +41,7 @@ async function loadDeployment(network) { } async function loadArtifact(contractName) { - const artifactPath = path.join(__dirname, `../../build/tron/${contractName}.json`); + const artifactPath = path.join(__dirname, `../../build-tron/${contractName}.json`); return JSON.parse(fs.readFileSync(artifactPath, 'utf8')); } diff --git a/packages/smart-contracts/tronbox-config.js b/packages/smart-contracts/tronbox-config.js index 65259723bc..bd9a5e7dcd 100644 --- a/packages/smart-contracts/tronbox-config.js +++ b/packages/smart-contracts/tronbox-config.js @@ -60,10 +60,9 @@ module.exports = { }, }, - // Contract build directory - Tron builds go under build/tron alongside Hardhat builds - // Tron-specific contracts are in tron/contracts/ (outside Hardhat sources to avoid conflicts) + // Tron artifacts must not live under Hardhat's `build/` (paths.artifacts) or HH701 duplicate names occur. contracts_directory: './tron/contracts', - contracts_build_directory: './build/tron', + contracts_build_directory: './build-tron', migrations_directory: './migrations/tron', test_directory: './test/tron', From 3efe115dec672ba0ecf12035da641d559b6ad154 Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Thu, 28 May 2026 17:34:45 +0200 Subject: [PATCH 16/19] feat: update contract deployment info --- .../smart-contracts/deployments/tron/mainnet.json | 13 ++++++------- packages/smart-contracts/deployments/tron/nile.json | 8 ++++---- .../src/lib/artifacts/ERC20BatchPayments/index.ts | 8 ++++---- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/smart-contracts/deployments/tron/mainnet.json b/packages/smart-contracts/deployments/tron/mainnet.json index ae0df3ed79..e48ed6eb68 100644 --- a/packages/smart-contracts/deployments/tron/mainnet.json +++ b/packages/smart-contracts/deployments/tron/mainnet.json @@ -1,10 +1,9 @@ { "network": "mainnet", "chainId": "1", - "timestamp": "2026-05-27T09:23:48.624Z", - "deployer": "TR7EydtGnsxriSieLfEuspTAqhQRmoscWC", - "deploymentDuration": "3.227s", - "note": "Deployment of ERC20BatchPayments to Mainnet", + "timestamp": "2024-01-01T00:00:00.000Z", + "deployer": "TO_BE_FILLED_ON_DEPLOYMENT", + "note": "Existing deployment from handover document", "contracts": { "ERC20FeeProxy": { "address": "TCUDPYnS9dH3WvFEaE7wN7vnDa51J4R4fd", @@ -12,9 +11,9 @@ "creationBlockNumber": 79216121 }, "ERC20BatchPayments": { - "address": "THm8vX6GNfRFZ15mRqdgvj56wjB6575S7C", - "hexAddress": "4155789c40d8ba55166296217cc244ca2dd3499f89", - "creationBlockNumber": 83068367 + "address": "TRZbXXuLd3HW5utzVysA3rpLgU7sVBrd1D", + "hexAddress": "41ab0ad52e1d1615ee6bc20ab8f2a4c498fb89fd10", + "creationBlockNumber": 83104290 } } } diff --git a/packages/smart-contracts/deployments/tron/nile.json b/packages/smart-contracts/deployments/tron/nile.json index 818ebdd22a..f42f30de64 100644 --- a/packages/smart-contracts/deployments/tron/nile.json +++ b/packages/smart-contracts/deployments/tron/nile.json @@ -3,7 +3,7 @@ "chainId": "3", "timestamp": "2024-01-01T00:00:00.000Z", "deployer": "TO_BE_FILLED_ON_DEPLOYMENT", - "note": "Existing deployment from handover document. Run 'yarn tron:deploy:nile' to redeploy.", + "note": "Existing deployment from handover document", "contracts": { "ERC20FeeProxy": { "address": "THK5rNmrvCujhmrXa5DB1dASepwXTr9cJs", @@ -11,9 +11,9 @@ "creationBlockNumber": 63208782 }, "ERC20BatchPayments": { - "address": "TC6nD547PRDVWuX8hBMREU7vVvSZNCAZot", - "hexAddress": "41175ee218bb15fc25224ab5937b0844f4f70a2b97", - "creationBlockNumber": 67794373 + "address": "TBAtFt46T7LUW5Sya6PNjw7MQrKkzKEFMx", + "hexAddress": "410d2d78623480a4caf18ea157badd9a8a7311b746", + "creationBlockNumber": 67830042 } } } diff --git a/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts b/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts index 534029b8c6..06f7a195f3 100644 --- a/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts +++ b/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts @@ -10,12 +10,12 @@ export const erc20BatchPaymentsArtifact = new ContractArtifact Date: Thu, 28 May 2026 18:40:18 +0200 Subject: [PATCH 17/19] fix: make contract evm-based and use symlink --- packages/smart-contracts/TRON_DEPLOYMENT.md | 2 +- packages/smart-contracts/package.json | 6 +- .../scripts/tron/deploy-mainnet.js | 2 +- .../scripts/tron/deploy-nile.js | 2 +- .../scripts/tron/deploy-test-token.js | 2 +- .../scripts/tron/verify-deployment.js | 2 +- .../src/contracts/ERC20BatchPayments.sol | 210 +++++++++++++++++ .../lib/artifacts/ERC20BatchPayments/index.ts | 2 +- .../src/lib/artifacts/index.ts | 1 + .../tron/contracts/ERC20BatchPayments.sol | 211 +----------------- packages/smart-contracts/tronbox-config.js | 5 +- 11 files changed, 223 insertions(+), 222 deletions(-) create mode 100644 packages/smart-contracts/src/contracts/ERC20BatchPayments.sol mode change 100644 => 120000 packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol diff --git a/packages/smart-contracts/TRON_DEPLOYMENT.md b/packages/smart-contracts/TRON_DEPLOYMENT.md index bc2d78cf38..0cd43e8405 100644 --- a/packages/smart-contracts/TRON_DEPLOYMENT.md +++ b/packages/smart-contracts/TRON_DEPLOYMENT.md @@ -33,7 +33,7 @@ Compile contracts for Tron: yarn tron:compile ``` -This creates artifacts in `build-tron/` directory (outside Hardhat's `build/` to avoid artifact name collisions). +This creates artifacts in `build/tron/` directory. ## Testing diff --git a/packages/smart-contracts/package.json b/packages/smart-contracts/package.json index 7973ce64cd..72ac895d0a 100644 --- a/packages/smart-contracts/package.json +++ b/packages/smart-contracts/package.json @@ -35,11 +35,10 @@ "build:lib": "tsc -b tsconfig.build.json && cp src/types/*.d.ts dist/src/types && cp -r dist/src/types types", "prebuild:sol": "yarn workspace @requestnetwork/types build && yarn workspace @requestnetwork/utils build && yarn workspace @requestnetwork/currency build", "build:sol": "yarn hardhat compile", - "build:tron": "yarn tron:compile && typechain --target ethers-v5 --out-dir src/types/tron 'build-tron/*.json'", - "build": "yarn build:sol && yarn build:tron && yarn build:lib", + "build": "yarn build:sol && yarn build:lib", "clean:types": "rm -rf types && rm -rf src/types", "clean:lib": "rm -rf dist tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo", - "clean:hardhat": "rm -rf cache build build-tron", + "clean:hardhat": "rm -rf cache && rm -rf build", "clean": "yarn clean:lib && yarn clean:types && yarn clean:hardhat", "lint:lib": "eslint . --fix", "lint:lib:check": "eslint .", @@ -107,7 +106,6 @@ "ganache-cli": "6.12.0", "hardhat": "2.26.5", "solhint": "3.3.6", - "tronbox": "4.7.1", "tronweb": "5.3.2", "typechain": "8.3.2", "typescript": "5.8.3", diff --git a/packages/smart-contracts/scripts/tron/deploy-mainnet.js b/packages/smart-contracts/scripts/tron/deploy-mainnet.js index e54e9fc24a..d3f9ec34dd 100644 --- a/packages/smart-contracts/scripts/tron/deploy-mainnet.js +++ b/packages/smart-contracts/scripts/tron/deploy-mainnet.js @@ -51,7 +51,7 @@ const tronWeb = new TronWeb({ privateKey: PRIVATE_KEY, }); -const ARTIFACTS_DIR = path.join(__dirname, '../../build-tron'); +const ARTIFACTS_DIR = path.join(__dirname, '../../build/tron'); async function loadArtifact(contractName) { const artifactPath = path.join(ARTIFACTS_DIR, `${contractName}.json`); diff --git a/packages/smart-contracts/scripts/tron/deploy-nile.js b/packages/smart-contracts/scripts/tron/deploy-nile.js index 09eedde64b..5858affcf0 100644 --- a/packages/smart-contracts/scripts/tron/deploy-nile.js +++ b/packages/smart-contracts/scripts/tron/deploy-nile.js @@ -32,7 +32,7 @@ const tronWeb = new TronWeb({ }); // Contract artifacts paths -const ARTIFACTS_DIR = path.join(__dirname, '../../build-tron'); +const ARTIFACTS_DIR = path.join(__dirname, '../../build/tron'); async function loadArtifact(contractName) { const artifactPath = path.join(ARTIFACTS_DIR, `${contractName}.json`); diff --git a/packages/smart-contracts/scripts/tron/deploy-test-token.js b/packages/smart-contracts/scripts/tron/deploy-test-token.js index 77119f51bc..8422473f4a 100644 --- a/packages/smart-contracts/scripts/tron/deploy-test-token.js +++ b/packages/smart-contracts/scripts/tron/deploy-test-token.js @@ -44,7 +44,7 @@ async function main() { } // Load compiled contract - const buildPath = path.join(__dirname, '../../build-tron/TestTRC20.json'); + const buildPath = path.join(__dirname, '../../build/tron/TestTRC20.json'); if (!fs.existsSync(buildPath)) { console.error('❌ Contract not compiled. Run: yarn tron:compile'); diff --git a/packages/smart-contracts/scripts/tron/verify-deployment.js b/packages/smart-contracts/scripts/tron/verify-deployment.js index b3fa29a62e..3bacc14daa 100644 --- a/packages/smart-contracts/scripts/tron/verify-deployment.js +++ b/packages/smart-contracts/scripts/tron/verify-deployment.js @@ -41,7 +41,7 @@ async function loadDeployment(network) { } async function loadArtifact(contractName) { - const artifactPath = path.join(__dirname, `../../build-tron/${contractName}.json`); + const artifactPath = path.join(__dirname, `../../build/tron/${contractName}.json`); return JSON.parse(fs.readFileSync(artifactPath, 'utf8')); } diff --git a/packages/smart-contracts/src/contracts/ERC20BatchPayments.sol b/packages/smart-contracts/src/contracts/ERC20BatchPayments.sol new file mode 100644 index 0000000000..a77c7cc719 --- /dev/null +++ b/packages/smart-contracts/src/contracts/ERC20BatchPayments.sol @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import './interfaces/ERC20FeeProxy.sol'; +import './lib/SafeERC20.sol'; + +/** + * @title ERC20BatchPayments + * @notice Tron-only batch contract that routes each payment through ERC20FeeProxy. + * If one payment fails, the whole batch reverts. + * @dev Uses ERC20FeeProxy to pay an invoice and fees, with a payment reference. + * Make sure this contract has allowance to spend the payer's tokens. + * Make sure the payer has enough tokens to pay the amounts and fees. + * This contract emits no events. Index TransferWithReferenceAndFee on ERC20FeeProxy + * with msg.sender == address(this batch contract). + * The proxy receives a one-time max allowance per token. + */ +contract ERC20BatchPayments { + using SafeERC20 for IERC20; + + IERC20FeeProxy public immutable paymentErc20FeeProxy; + + /// @dev True after unlimited proxy approval was set for a token (avoids repeated approve calls). + mapping(address => bool) private _proxyApproved; + + struct Token { + address tokenAddress; + uint256 amountAndFee; + } + + /** + * @param _paymentErc20FeeProxy The address of the ERC20FeeProxy to use. + */ + constructor(address _paymentErc20FeeProxy) { + require(_paymentErc20FeeProxy != address(0), 'ERC20BatchPayments: paymentErc20FeeProxy cannot be 0x'); + paymentErc20FeeProxy = IERC20FeeProxy(_paymentErc20FeeProxy); + } + + /** + * @notice Send a batch of ERC20 payments with fees and payment references to multiple accounts. + * @param _tokenAddress Token to transact with. + * @param _recipients List of recipient accounts. + * @param _amounts List of amounts, corresponding to recipients[]. + * @param _paymentReferences List of payment references, corresponding to recipients[]. + * @param _feeAmounts List of fee amounts, corresponding to recipients[]. + * @param _feeAddress The fee recipient. + */ + function batchERC20PaymentsWithReference( + address _tokenAddress, + address[] calldata _recipients, + uint256[] calldata _amounts, + bytes[] calldata _paymentReferences, + uint256[] calldata _feeAmounts, + address _feeAddress + ) external { + require( + _recipients.length == _amounts.length && + _recipients.length == _paymentReferences.length && + _recipients.length == _feeAmounts.length, + 'the input arrays must have the same length' + ); + require(_tokenAddress != address(0), 'ERC20BatchPayments: token cannot be 0x'); + + uint256 amountAndFee = 0; + for (uint256 i = 0; i < _recipients.length; ) { + amountAndFee += _amounts[i] + _feeAmounts[i]; + unchecked { + ++i; + } + } + + if (amountAndFee > 0) { + _transferToContractAndApproveProxy(IERC20(_tokenAddress), amountAndFee); + } + + for (uint256 i = 0; i < _recipients.length; ) { + uint256 paymentSum = _amounts[i] + _feeAmounts[i]; + if (paymentSum == 0) { + unchecked { + ++i; + } + continue; + } + require(_recipients[i] != address(0), 'ERC20BatchPayments: recipient cannot be 0x'); + if (_feeAmounts[i] > 0) { + require(_feeAddress != address(0), 'ERC20BatchPayments: feeAddress cannot be 0x when fee > 0'); + } + paymentErc20FeeProxy.transferFromWithReferenceAndFee( + _tokenAddress, + _recipients[i], + _amounts[i], + _paymentReferences[i], + _feeAmounts[i], + _feeAddress + ); + unchecked { + ++i; + } + } + } + + /** + * @notice Send a batch of ERC20 payments on multiple tokens with fees and payment references. + * @param _tokenAddresses List of tokens to transact with. + * @param _recipients List of recipient accounts. + * @param _amounts List of amounts, corresponding to recipients[]. + * @param _paymentReferences List of payment references, corresponding to recipients[]. + * @param _feeAmounts List of fee amounts, corresponding to recipients[]. + * @param _feeAddress The fee recipient. + */ + function batchERC20PaymentsMultiTokensWithReference( + address[] calldata _tokenAddresses, + address[] calldata _recipients, + uint256[] calldata _amounts, + bytes[] calldata _paymentReferences, + uint256[] calldata _feeAmounts, + address _feeAddress + ) external { + require( + _tokenAddresses.length == _recipients.length && + _tokenAddresses.length == _amounts.length && + _tokenAddresses.length == _paymentReferences.length && + _tokenAddresses.length == _feeAmounts.length, + 'the input arrays must have the same length' + ); + + Token[] memory uniqueTokens = new Token[](_tokenAddresses.length); + for (uint256 i = 0; i < _tokenAddresses.length; ) { + require(_tokenAddresses[i] != address(0), 'ERC20BatchPayments: token cannot be 0x'); + for (uint256 j = 0; j < _tokenAddresses.length; ) { + if (uniqueTokens[j].tokenAddress == _tokenAddresses[i]) { + uniqueTokens[j].amountAndFee += _amounts[i] + _feeAmounts[i]; + break; + } + if (uniqueTokens[j].amountAndFee == 0 && (_amounts[i] + _feeAmounts[i]) > 0) { + uniqueTokens[j].tokenAddress = _tokenAddresses[i]; + uniqueTokens[j].amountAndFee = _amounts[i] + _feeAmounts[i]; + break; + } + unchecked { + ++j; + } + } + unchecked { + ++i; + } + } + + for (uint256 i = 0; i < uniqueTokens.length && uniqueTokens[i].amountAndFee > 0; ) { + _transferToContractAndApproveProxy( + IERC20(uniqueTokens[i].tokenAddress), + uniqueTokens[i].amountAndFee + ); + unchecked { + ++i; + } + } + + for (uint256 i = 0; i < _recipients.length; ) { + uint256 paymentSum = _amounts[i] + _feeAmounts[i]; + if (paymentSum == 0) { + unchecked { + ++i; + } + continue; + } + require(_recipients[i] != address(0), 'ERC20BatchPayments: recipient cannot be 0x'); + if (_feeAmounts[i] > 0) { + require(_feeAddress != address(0), 'ERC20BatchPayments: feeAddress cannot be 0x when fee > 0'); + } + paymentErc20FeeProxy.transferFromWithReferenceAndFee( + _tokenAddresses[i], + _recipients[i], + _amounts[i], + _paymentReferences[i], + _feeAmounts[i], + _feeAddress + ); + unchecked { + ++i; + } + } + } + + /** + * @notice Pulls tokens from the payer to this contract and approves the proxy to spend them. + * @dev Approves the proxy once per token with max allowance; later batches skip approve. + * @param requestedToken The token to pay. + * @param amountAndFee The sum of payment amounts and fees for this token. + */ + function _transferToContractAndApproveProxy( + IERC20 requestedToken, + uint256 amountAndFee + ) internal { + require( + requestedToken.safeTransferFrom(msg.sender, address(this), amountAndFee), + 'payment transferFrom() failed' + ); + + address token = address(requestedToken); + if (!_proxyApproved[token]) { + require( + requestedToken.safeApprove(address(paymentErc20FeeProxy), type(uint256).max), + 'approve() failed' + ); + _proxyApproved[token] = true; + } + } +} diff --git a/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts b/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts index 06f7a195f3..a2834de59b 100644 --- a/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts +++ b/packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/index.ts @@ -2,7 +2,7 @@ import { ContractArtifact } from '../../ContractArtifact'; import { abi as ABI_0_1_0 } from './0.1.0.json'; // @ts-ignore Cannot find module -import type { ERC20BatchPayments } from '../../../types/tron'; +import type { ERC20BatchPayments } from '../../../types'; export const erc20BatchPaymentsArtifact = new ContractArtifact( { diff --git a/packages/smart-contracts/src/lib/artifacts/index.ts b/packages/smart-contracts/src/lib/artifacts/index.ts index 03aa84d523..d0e4d541df 100644 --- a/packages/smart-contracts/src/lib/artifacts/index.ts +++ b/packages/smart-contracts/src/lib/artifacts/index.ts @@ -4,6 +4,7 @@ export * from './ChainlinkConversionPath'; export * from './Erc20ConversionProxy'; export * from './ERC20FeeProxy'; +export * from './ERC20BatchPayments'; export * from './ERC20Proxy'; export * from './ERC20SwapToPay'; export * from './Erc20SwapConversion'; diff --git a/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol b/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol deleted file mode 100644 index a77c7cc719..0000000000 --- a/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol +++ /dev/null @@ -1,210 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; -import './interfaces/ERC20FeeProxy.sol'; -import './lib/SafeERC20.sol'; - -/** - * @title ERC20BatchPayments - * @notice Tron-only batch contract that routes each payment through ERC20FeeProxy. - * If one payment fails, the whole batch reverts. - * @dev Uses ERC20FeeProxy to pay an invoice and fees, with a payment reference. - * Make sure this contract has allowance to spend the payer's tokens. - * Make sure the payer has enough tokens to pay the amounts and fees. - * This contract emits no events. Index TransferWithReferenceAndFee on ERC20FeeProxy - * with msg.sender == address(this batch contract). - * The proxy receives a one-time max allowance per token. - */ -contract ERC20BatchPayments { - using SafeERC20 for IERC20; - - IERC20FeeProxy public immutable paymentErc20FeeProxy; - - /// @dev True after unlimited proxy approval was set for a token (avoids repeated approve calls). - mapping(address => bool) private _proxyApproved; - - struct Token { - address tokenAddress; - uint256 amountAndFee; - } - - /** - * @param _paymentErc20FeeProxy The address of the ERC20FeeProxy to use. - */ - constructor(address _paymentErc20FeeProxy) { - require(_paymentErc20FeeProxy != address(0), 'ERC20BatchPayments: paymentErc20FeeProxy cannot be 0x'); - paymentErc20FeeProxy = IERC20FeeProxy(_paymentErc20FeeProxy); - } - - /** - * @notice Send a batch of ERC20 payments with fees and payment references to multiple accounts. - * @param _tokenAddress Token to transact with. - * @param _recipients List of recipient accounts. - * @param _amounts List of amounts, corresponding to recipients[]. - * @param _paymentReferences List of payment references, corresponding to recipients[]. - * @param _feeAmounts List of fee amounts, corresponding to recipients[]. - * @param _feeAddress The fee recipient. - */ - function batchERC20PaymentsWithReference( - address _tokenAddress, - address[] calldata _recipients, - uint256[] calldata _amounts, - bytes[] calldata _paymentReferences, - uint256[] calldata _feeAmounts, - address _feeAddress - ) external { - require( - _recipients.length == _amounts.length && - _recipients.length == _paymentReferences.length && - _recipients.length == _feeAmounts.length, - 'the input arrays must have the same length' - ); - require(_tokenAddress != address(0), 'ERC20BatchPayments: token cannot be 0x'); - - uint256 amountAndFee = 0; - for (uint256 i = 0; i < _recipients.length; ) { - amountAndFee += _amounts[i] + _feeAmounts[i]; - unchecked { - ++i; - } - } - - if (amountAndFee > 0) { - _transferToContractAndApproveProxy(IERC20(_tokenAddress), amountAndFee); - } - - for (uint256 i = 0; i < _recipients.length; ) { - uint256 paymentSum = _amounts[i] + _feeAmounts[i]; - if (paymentSum == 0) { - unchecked { - ++i; - } - continue; - } - require(_recipients[i] != address(0), 'ERC20BatchPayments: recipient cannot be 0x'); - if (_feeAmounts[i] > 0) { - require(_feeAddress != address(0), 'ERC20BatchPayments: feeAddress cannot be 0x when fee > 0'); - } - paymentErc20FeeProxy.transferFromWithReferenceAndFee( - _tokenAddress, - _recipients[i], - _amounts[i], - _paymentReferences[i], - _feeAmounts[i], - _feeAddress - ); - unchecked { - ++i; - } - } - } - - /** - * @notice Send a batch of ERC20 payments on multiple tokens with fees and payment references. - * @param _tokenAddresses List of tokens to transact with. - * @param _recipients List of recipient accounts. - * @param _amounts List of amounts, corresponding to recipients[]. - * @param _paymentReferences List of payment references, corresponding to recipients[]. - * @param _feeAmounts List of fee amounts, corresponding to recipients[]. - * @param _feeAddress The fee recipient. - */ - function batchERC20PaymentsMultiTokensWithReference( - address[] calldata _tokenAddresses, - address[] calldata _recipients, - uint256[] calldata _amounts, - bytes[] calldata _paymentReferences, - uint256[] calldata _feeAmounts, - address _feeAddress - ) external { - require( - _tokenAddresses.length == _recipients.length && - _tokenAddresses.length == _amounts.length && - _tokenAddresses.length == _paymentReferences.length && - _tokenAddresses.length == _feeAmounts.length, - 'the input arrays must have the same length' - ); - - Token[] memory uniqueTokens = new Token[](_tokenAddresses.length); - for (uint256 i = 0; i < _tokenAddresses.length; ) { - require(_tokenAddresses[i] != address(0), 'ERC20BatchPayments: token cannot be 0x'); - for (uint256 j = 0; j < _tokenAddresses.length; ) { - if (uniqueTokens[j].tokenAddress == _tokenAddresses[i]) { - uniqueTokens[j].amountAndFee += _amounts[i] + _feeAmounts[i]; - break; - } - if (uniqueTokens[j].amountAndFee == 0 && (_amounts[i] + _feeAmounts[i]) > 0) { - uniqueTokens[j].tokenAddress = _tokenAddresses[i]; - uniqueTokens[j].amountAndFee = _amounts[i] + _feeAmounts[i]; - break; - } - unchecked { - ++j; - } - } - unchecked { - ++i; - } - } - - for (uint256 i = 0; i < uniqueTokens.length && uniqueTokens[i].amountAndFee > 0; ) { - _transferToContractAndApproveProxy( - IERC20(uniqueTokens[i].tokenAddress), - uniqueTokens[i].amountAndFee - ); - unchecked { - ++i; - } - } - - for (uint256 i = 0; i < _recipients.length; ) { - uint256 paymentSum = _amounts[i] + _feeAmounts[i]; - if (paymentSum == 0) { - unchecked { - ++i; - } - continue; - } - require(_recipients[i] != address(0), 'ERC20BatchPayments: recipient cannot be 0x'); - if (_feeAmounts[i] > 0) { - require(_feeAddress != address(0), 'ERC20BatchPayments: feeAddress cannot be 0x when fee > 0'); - } - paymentErc20FeeProxy.transferFromWithReferenceAndFee( - _tokenAddresses[i], - _recipients[i], - _amounts[i], - _paymentReferences[i], - _feeAmounts[i], - _feeAddress - ); - unchecked { - ++i; - } - } - } - - /** - * @notice Pulls tokens from the payer to this contract and approves the proxy to spend them. - * @dev Approves the proxy once per token with max allowance; later batches skip approve. - * @param requestedToken The token to pay. - * @param amountAndFee The sum of payment amounts and fees for this token. - */ - function _transferToContractAndApproveProxy( - IERC20 requestedToken, - uint256 amountAndFee - ) internal { - require( - requestedToken.safeTransferFrom(msg.sender, address(this), amountAndFee), - 'payment transferFrom() failed' - ); - - address token = address(requestedToken); - if (!_proxyApproved[token]) { - require( - requestedToken.safeApprove(address(paymentErc20FeeProxy), type(uint256).max), - 'approve() failed' - ); - _proxyApproved[token] = true; - } - } -} diff --git a/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol b/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol new file mode 120000 index 0000000000..7ce208dc94 --- /dev/null +++ b/packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol @@ -0,0 +1 @@ +../../src/contracts/ERC20BatchPayments.sol \ No newline at end of file diff --git a/packages/smart-contracts/tronbox-config.js b/packages/smart-contracts/tronbox-config.js index bd9a5e7dcd..65259723bc 100644 --- a/packages/smart-contracts/tronbox-config.js +++ b/packages/smart-contracts/tronbox-config.js @@ -60,9 +60,10 @@ module.exports = { }, }, - // Tron artifacts must not live under Hardhat's `build/` (paths.artifacts) or HH701 duplicate names occur. + // Contract build directory - Tron builds go under build/tron alongside Hardhat builds + // Tron-specific contracts are in tron/contracts/ (outside Hardhat sources to avoid conflicts) contracts_directory: './tron/contracts', - contracts_build_directory: './build-tron', + contracts_build_directory: './build/tron', migrations_directory: './migrations/tron', test_directory: './test/tron', From 8fe81ec93fe17f203d305b500f55361400e3dfc9 Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Thu, 28 May 2026 18:53:53 +0200 Subject: [PATCH 18/19] update global files --- .github/workflows/tron-smart-contracts.yml | 25 +- .gitignore | 1 - yarn.lock | 569 ++++----------------- 3 files changed, 108 insertions(+), 487 deletions(-) diff --git a/.github/workflows/tron-smart-contracts.yml b/.github/workflows/tron-smart-contracts.yml index c9363999ba..7ee4dcbcfe 100644 --- a/.github/workflows/tron-smart-contracts.yml +++ b/.github/workflows/tron-smart-contracts.yml @@ -12,6 +12,9 @@ on: - 'packages/smart-contracts/test/tron/**' - 'packages/smart-contracts/tronbox-config.js' - 'packages/smart-contracts/src/lib/artifacts/ERC20FeeProxy/**' + - 'packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/**' + - 'packages/smart-contracts/src/contracts/ERC20FeeProxy.sol' + - 'packages/smart-contracts/src/contracts/ERC20BatchPayments.sol' - 'packages/payment-processor/src/payment/*tron*' - 'packages/payment-processor/test/payment/*tron*' - 'packages/currency/src/chains/tron/**' @@ -27,6 +30,9 @@ on: - 'packages/smart-contracts/test/tron/**' - 'packages/smart-contracts/tronbox-config.js' - 'packages/smart-contracts/src/lib/artifacts/ERC20FeeProxy/**' + - 'packages/smart-contracts/src/lib/artifacts/ERC20BatchPayments/**' + - 'packages/smart-contracts/src/contracts/ERC20FeeProxy.sol' + - 'packages/smart-contracts/src/contracts/ERC20BatchPayments.sol' - 'packages/payment-processor/src/payment/*tron*' - 'packages/payment-processor/test/payment/*tron*' - 'packages/currency/src/chains/tron/**' @@ -73,11 +79,11 @@ jobs: working-directory: packages/smart-contracts run: | echo "Checking build artifacts..." - ls -la build-tron/ + ls -la build/tron/ # Verify key contracts were compiled - for contract in ERC20FeeProxy TestTRC20 BadTRC20 TRC20True TRC20NoReturn TRC20False TRC20Revert; do - if [ ! -f "build-tron/${contract}.json" ]; then + for contract in ERC20FeeProxy ERC20BatchPayments TestTRC20 BadTRC20 TRC20True TRC20NoReturn TRC20False TRC20Revert; do + if [ ! -f "build/tron/${contract}.json" ]; then echo "ERROR: ${contract}.json not found!" exit 1 fi @@ -93,7 +99,7 @@ jobs: # Check that the compiled contract has the expected functions for func in transferFromWithReferenceAndFee; do - if ! grep -q "$func" build-tron/ERC20FeeProxy.json; then + if ! grep -q "$func" build/tron/ERC20FeeProxy.json; then echo "ERROR: ERC20FeeProxy missing $func function!" exit 1 fi @@ -102,13 +108,22 @@ jobs: # Verify TestTRC20 has standard ERC20 functions for func in transfer approve transferFrom balanceOf allowance; do - if ! grep -q "$func" build-tron/TestTRC20.json; then + if ! grep -q "$func" build/tron/TestTRC20.json; then echo "ERROR: TestTRC20 missing $func function!" exit 1 fi echo "✓ TestTRC20 has $func" done + echo "Verifying ERC20BatchPayments ABI..." + for func in batchERC20PaymentsWithReference batchERC20PaymentsMultiTokensWithReference paymentErc20FeeProxy; do + if ! grep -q "$func" build/tron/ERC20BatchPayments.json; then + echo "ERROR: ERC20BatchPayments missing $func!" + exit 1 + fi + echo "✓ ERC20BatchPayments has $func" + done + echo "✅ Contract ABI structure verified" - name: Verify deployment files are valid JSON diff --git a/.gitignore b/.gitignore index ea539e70a0..e767737c1b 100644 --- a/.gitignore +++ b/.gitignore @@ -41,7 +41,6 @@ tsconfig.build.tsbuildinfo /packages/smart-contracts/types/ /packages/smart-contracts/src/types/ /packages/smart-contracts/build-zk/ -/packages/smart-contracts/build-tron/ /packages/smart-contracts/cache-zk/ # security testing artifacts diff --git a/yarn.lock b/yarn.lock index 9118b2f639..74f68ebf64 100644 --- a/yarn.lock +++ b/yarn.lock @@ -773,13 +773,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/runtime@7.26.10": - version "7.26.10" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.10.tgz#a07b4d8fa27af131a633d7b3524db803eb4764c2" - integrity sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw== - dependencies: - regenerator-runtime "^0.14.0" - "@babel/runtime@^7.0.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4": version "7.27.0" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz" @@ -5491,11 +5484,6 @@ dependencies: "@sinonjs/commons" "^3.0.0" -"@solidity-parser/parser@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.20.2.tgz#e07053488ed60dae1b54f6fe37bb6d2c5fe146a7" - integrity sha512-rbu0bzwNvMcwAjH86hiEAcOeRI2EeK8zCkHDrFykh/Al8mvJeFmjy3UrE7GYQjNwOgbGUUtCn5/k8CB8zIu7QA== - "@solidity-parser/parser@^0.13.2": version "0.13.2" resolved "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.13.2.tgz" @@ -7155,16 +7143,6 @@ ajv-keywords@^3.5.2: resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@6.14.0: - version "6.14.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.14.0.tgz#fd067713e228210636ebb08c60bd3765d6dbe73a" - integrity sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - ajv@8.17.1, ajv@^8.0.0, ajv@^8.0.1, ajv@^8.12.0: version "8.17.1" resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz" @@ -7655,7 +7633,7 @@ assert@^1.4.0: object-assign "^4.1.1" util "0.10.3" -assertion-error@^1.0.1, assertion-error@^1.1.0: +assertion-error@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== @@ -7725,18 +7703,18 @@ async@2.6.2: dependencies: lodash "^4.17.11" -async@2.6.4, async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0, async@^2.6.1: +async@^1.4.2: + version "1.5.2" + resolved "https://registry.npmjs.org/async/-/async-1.5.2.tgz" + integrity sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w== + +async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0, async@^2.6.1: version "2.6.4" resolved "https://registry.npmjs.org/async/-/async-2.6.4.tgz" integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== dependencies: lodash "^4.17.14" -async@^1.4.2: - version "1.5.2" - resolved "https://registry.npmjs.org/async/-/async-1.5.2.tgz" - integrity sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w== - async@^3.2.3: version "3.2.5" resolved "https://registry.npmjs.org/async/-/async-3.2.5.tgz" @@ -7796,25 +7774,6 @@ aws4@^1.8.0: resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== -axios@1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.15.0.tgz#0fcee91ef03d386514474904b27863b2c683bf4f" - integrity sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q== - dependencies: - follow-redirects "^1.15.11" - form-data "^4.0.5" - proxy-from-env "^2.1.0" - -axios@1.16.1: - version "1.16.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.16.1.tgz#517e29291d19d6e8cf919ff264f4fe157261ba12" - integrity sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A== - dependencies: - follow-redirects "^1.16.0" - form-data "^4.0.5" - https-proxy-agent "^5.0.1" - proxy-from-env "^2.1.0" - axios@^1.0.0, axios@^1.4.0, axios@^1.6.8: version "1.8.4" resolved "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz" @@ -8474,11 +8433,6 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -balanced-match@^4.0.2: - version "4.0.4" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" - integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== - base-x@^2.0.1: version "2.0.6" resolved "https://registry.npmjs.org/base-x/-/base-x-2.0.6.tgz" @@ -8598,16 +8552,11 @@ bignumber.js@*: resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz" integrity sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw== -bignumber.js@7.2.1, bignumber.js@^7.2.1: +bignumber.js@^7.2.1: version "7.2.1" resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz" integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== -bignumber.js@9.1.2: - version "9.1.2" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" - integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== - bignumber.js@^9.0.0, bignumber.js@^9.0.1: version "9.0.1" resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz" @@ -8763,20 +8712,6 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -brace-expansion@^2.0.2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.1.tgz#c68b1c4111c76aae3a6fba55d496cee10c39dad8" - integrity sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA== - dependencies: - balanced-match "^1.0.0" - -brace-expansion@^5.0.5: - version "5.0.6" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.6.tgz#ec68fe0a641a29d8711579caf641d05bae1f2285" - integrity sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g== - dependencies: - balanced-match "^4.0.2" - braces@^1.8.2: version "1.8.5" resolved "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz" @@ -8862,7 +8797,7 @@ browser-stdout@1.3.0: resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz" integrity sha1-81HTKWnTL6XXpVZxVCY9korjvR8= -browser-stdout@1.3.1, browser-stdout@^1.3.1: +browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== @@ -9064,11 +8999,6 @@ buffer-alloc@^1.2.0: buffer-alloc-unsafe "^1.1.0" buffer-fill "^1.0.0" -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== - buffer-equal@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz" @@ -9528,18 +9458,6 @@ cbor@^8.1.0: dependencies: nofilter "^3.1.0" -chai@4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.1.2.tgz#0f64584ba642f0f2ace2806279f4f06ca23ad73c" - integrity sha512-YTHf80rJ8M5/cJoFKEV1y3PnexbGs0vSHjouRRU8gLM05Nc3Mqq9zor/P4SCqB/sgvKRLvya7wHLC1XQ9pTjgQ== - dependencies: - assertion-error "^1.0.1" - check-error "^1.0.1" - deep-eql "^3.0.0" - get-func-name "^2.0.0" - pathval "^1.0.0" - type-detect "^4.0.0" - chai@4.3.4: version "4.3.4" resolved "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz" @@ -9552,15 +9470,6 @@ chai@4.3.4: pathval "^1.1.1" type-detect "^4.0.5" -chalk@2.4.2, chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chalk@4.1.0, chalk@^4.0.0, chalk@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz" @@ -9588,6 +9497,15 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + change-case-all@1.0.14: version "1.0.14" resolved "https://registry.npmjs.org/change-case-all/-/change-case-all-1.0.14.tgz" @@ -9672,7 +9590,7 @@ chardet@^0.7.0: resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -check-error@^1.0.1, check-error@^1.0.2: +check-error@^1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz" integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== @@ -9826,13 +9744,6 @@ chokidar@^4.0.0: dependencies: readdirp "^4.0.1" -chokidar@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" - integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== - dependencies: - readdirp "^4.0.1" - chownr@^1.0.1, chownr@^1.1.1, chownr@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz" @@ -10940,13 +10851,6 @@ debug@3.2.6: dependencies: ms "^2.1.1" -debug@3.2.7, debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: - version "3.2.7" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" @@ -10954,6 +10858,13 @@ debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, de dependencies: ms "2.1.2" +debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + debug@^4.3.5: version "4.3.7" resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz" @@ -11015,7 +10926,7 @@ dedent@^1.0.0: resolved "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz" integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== -deep-eql@^3.0.0, deep-eql@^3.0.1: +deep-eql@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz" integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== @@ -11317,11 +11228,6 @@ diff@^4.0.1: resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -diff@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-7.0.0.tgz#3fb34d387cd76d803f6eebea67b921dab0182a9a" - integrity sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw== - diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz" @@ -11730,14 +11636,6 @@ enhanced-resolve@^5.17.1: graceful-fs "^4.2.4" tapable "^2.2.0" -enquirer@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" - integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== - dependencies: - ansi-colors "^4.1.1" - strip-ansi "^6.0.1" - enquirer@^2.3.0, enquirer@^2.3.5, enquirer@^2.3.6, enquirer@~2.3.6: version "2.3.6" resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz" @@ -12068,7 +11966,7 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: +escape-string-regexp@4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== @@ -12531,16 +12429,6 @@ ethereum-common@^0.0.18: resolved "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.18.tgz" integrity sha512-EoltVQTRNg2Uy4o84qpa2aXymXDJhxm7eos/ACOg0DG4baAbMjhbdAEsx9GeE8sC3XCxnYvrrzZDH8D8MtA2iQ== -ethereum-cryptography@2.2.1, ethereum-cryptography@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz#58f2810f8e020aecb97de8c8c76147600b0b8ccf" - integrity sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg== - dependencies: - "@noble/curves" "1.4.2" - "@noble/hashes" "1.4.0" - "@scure/bip32" "1.4.0" - "@scure/bip39" "1.3.0" - ethereum-cryptography@^0.1.3: version "0.1.3" resolved "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz" @@ -12582,6 +12470,16 @@ ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2: "@scure/bip32" "1.3.1" "@scure/bip39" "1.2.1" +ethereum-cryptography@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz#58f2810f8e020aecb97de8c8c76147600b0b8ccf" + integrity sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg== + dependencies: + "@noble/curves" "1.4.2" + "@noble/hashes" "1.4.0" + "@scure/bip32" "1.4.0" + "@scure/bip39" "1.3.0" + ethereum-waffle@3.4.4: version "3.4.4" resolved "https://registry.npmjs.org/ethereum-waffle/-/ethereum-waffle-3.4.4.tgz" @@ -12869,32 +12767,6 @@ ethers@5.7.2, ethers@^5.0.1, ethers@^5.0.2, ethers@^5.5.2, ethers@^5.7.0, ethers "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" -ethers@6.13.5: - version "6.13.5" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.5.tgz#8c1d6ac988ac08abc3c1d8fabbd4b8b602851ac4" - integrity sha512-+knKNieu5EKRThQJWwqaJ10a6HE9sSehGeqWN65//wE7j47ZpFhKAnHB/JJFibwwg61I/koxaPsXbXpD/skNOQ== - dependencies: - "@adraffy/ens-normalize" "1.10.1" - "@noble/curves" "1.2.0" - "@noble/hashes" "1.3.2" - "@types/node" "22.7.5" - aes-js "4.0.0-beta.5" - tslib "2.7.0" - ws "8.17.1" - -ethers@6.15.0: - version "6.15.0" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.15.0.tgz#2980f2a3baf0509749b7e21f8692fa8a8349c0e3" - integrity sha512-Kf/3ZW54L4UT0pZtsY/rf+EkBU7Qi5nnhonjUb8yTXcxH3cdcWrV2cRyk0Xk/4jK6OoHhxxZHriyhje20If2hQ== - dependencies: - "@adraffy/ens-normalize" "1.10.1" - "@noble/curves" "1.2.0" - "@noble/hashes" "1.3.2" - "@types/node" "22.7.5" - aes-js "4.0.0-beta.5" - tslib "2.7.0" - ws "8.17.1" - ethers@^4.0.32: version "4.0.48" resolved "https://registry.npmjs.org/ethers/-/ethers-4.0.48.tgz" @@ -13676,14 +13548,6 @@ find-root@^1.0.0: resolved "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz" integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== -find-up@4.1.0, find-up@^4.0.0, find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - find-up@5.0.0, find-up@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" @@ -13714,6 +13578,14 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + find-yarn-workspace-root@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz" @@ -13786,11 +13658,6 @@ follow-redirects@^1.12.1, follow-redirects@^1.15.6: resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz" integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== -follow-redirects@^1.15.11, follow-redirects@^1.16.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.16.0.tgz#28474a159d3b9d11ef62050a14ed60e4df6d61bc" - integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw== - fontkit@^1.8.0: version "1.8.1" resolved "https://registry.npmjs.org/fontkit/-/fontkit-1.8.1.tgz" @@ -13877,7 +13744,7 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@^4.0.4, form-data@^4.0.5: +form-data@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== @@ -13944,15 +13811,6 @@ fs-constants@^1.0.0: resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@8.1.0, fs-extra@^8.0.1: - version "8.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@9.1.0, fs-extra@^9.0.0, fs-extra@^9.0.1, fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" @@ -14028,6 +13886,15 @@ fs-extra@^7.0.0, fs-extra@^7.0.1: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@^8.0.1: + version "8.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-minipass@^1.2.7: version "1.2.7" resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz" @@ -14464,15 +14331,6 @@ glob-to-regexp@^0.4.1: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@13.0.6: - version "13.0.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-13.0.6.tgz#078666566a425147ccacfbd2e332deb66a2be71d" - integrity sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw== - dependencies: - minimatch "^10.2.2" - minipass "^7.1.3" - path-scurry "^2.0.2" - glob@7.1.2: version "7.1.2" resolved "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz" @@ -14532,18 +14390,6 @@ glob@^10.2.2: minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-scurry "^1.10.1" -glob@^10.4.5: - version "10.5.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" - integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== - dependencies: - foreground-child "^3.1.0" - jackspeak "^3.1.2" - minimatch "^9.0.4" - minipass "^7.1.2" - package-json-from-dist "^1.0.0" - path-scurry "^1.11.1" - glob@^8.0.1: version "8.1.0" resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" @@ -14701,11 +14547,6 @@ globby@^8.0.1: pify "^3.0.0" slash "^1.0.0" -google-protobuf@3.21.4: - version "3.21.4" - resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.21.4.tgz#2f933e8b6e5e9f8edde66b7be0024b68f77da6c9" - integrity sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ== - gopd@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" @@ -14794,13 +14635,6 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6 resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -graphlib@2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da" - integrity sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A== - dependencies: - lodash "^4.17.15" - graphql-config@^5.0.2: version "5.0.3" resolved "https://registry.npmjs.org/graphql-config/-/graphql-config-5.0.3.tgz" @@ -15245,11 +15079,6 @@ homedir-polyfill@^1.0.1: dependencies: parse-passwd "^1.0.0" -homedir@0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/homedir/-/homedir-0.6.0.tgz#2b21db66bf08a6db38249a3eff52d7d18706af1e" - integrity sha512-KZFBHenkVuyyG4uaqRSXqWJr3HTxcaPguM7rU1BlH/mtbDlzaXNSXTa9AhV+fXEjrNemHu9vtLRIaM8/8OW0xA== - hoopy@^0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz" @@ -15468,14 +15297,6 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -https-proxy-agent@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== - dependencies: - agent-base "6" - debug "4" - https-proxy-agent@^7.0.0: version "7.0.2" resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz" @@ -16313,7 +16134,7 @@ is-path-cwd@^2.2.0: resolved "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz" integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== -is-path-inside@^3.0.2, is-path-inside@^3.0.3: +is-path-inside@^3.0.2: version "3.0.3" resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== @@ -16678,15 +16499,6 @@ jackspeak@^2.3.5: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" -jackspeak@^3.1.2: - version "3.4.3" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" - integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" - jake@^10.8.5: version "10.8.7" resolved "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz" @@ -18045,11 +17857,6 @@ lodash@4.17.20: resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== -lodash@4.18.1: - version "4.18.1" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c" - integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q== - lodash@^4.14.2, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@~4.17.0: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" @@ -18166,16 +17973,11 @@ lru-cache@5.1.1, lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -lru-cache@^10.2.0, lru-cache@^10.4.3: +lru-cache@^10.4.3: version "10.4.3" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== -lru-cache@^11.0.0: - version "11.5.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.5.1.tgz#f3daa3540847b9737ebc02499ddb36765e54db4a" - integrity sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A== - lru-cache@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz" @@ -18743,13 +18545,6 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^10.2.2: - version "10.2.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1" - integrity sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg== - dependencies: - brace-expansion "^5.0.5" - minimatch@^3.0.0, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" @@ -18757,13 +18552,6 @@ minimatch@^3.0.0, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimatch@^3.0.2: - version "3.1.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e" - integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== - dependencies: - brace-expansion "^1.1.7" - minimatch@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/minimatch/-/minimatch-4.2.3.tgz" @@ -18806,13 +18594,6 @@ minimatch@^9.0.0, minimatch@^9.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.4, minimatch@^9.0.5: - version "9.0.9" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.9.tgz#9b0cb9fcb78087f6fd7eababe2511c4d3d60574e" - integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg== - dependencies: - brace-expansion "^2.0.2" - minimist-options@4.1.0, minimist-options@^4.0.2: version "4.1.0" resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz" @@ -18922,11 +18703,6 @@ minipass@^5.0.0: resolved "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz" integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== -minipass@^7.1.2, minipass@^7.1.3: - version "7.1.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b" - integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== - minizlib@^1.3.3: version "1.3.3" resolved "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz" @@ -18983,13 +18759,6 @@ mkdirp@0.5.1: dependencies: minimist "0.0.8" -mkdirp@0.5.6: - version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== - dependencies: - minimist "^1.2.6" - mkdirp@^0.5.1, mkdirp@^0.5.5: version "0.5.5" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" @@ -19019,33 +18788,6 @@ mnemonist@^0.38.0: dependencies: obliterator "^1.6.1" -mocha@11.7.4: - version "11.7.4" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.7.4.tgz#f161b17aeccb0762484b33bdb3f7ab9410ba5c82" - integrity sha512-1jYAaY8x0kAZ0XszLWu14pzsf4KV740Gld4HXkhNTXwcHx4AUEDkPzgEHg9CM5dVcW+zv036tjpsEbLraPJj4w== - dependencies: - browser-stdout "^1.3.1" - chokidar "^4.0.1" - debug "^4.3.5" - diff "^7.0.0" - escape-string-regexp "^4.0.0" - find-up "^5.0.0" - glob "^10.4.5" - he "^1.2.0" - is-path-inside "^3.0.3" - js-yaml "^4.1.0" - log-symbols "^4.1.0" - minimatch "^9.0.5" - ms "^2.1.3" - picocolors "^1.1.1" - serialize-javascript "^6.0.2" - strip-json-comments "^3.1.1" - supports-color "^8.1.1" - workerpool "^9.2.0" - yargs "^17.7.2" - yargs-parser "^21.1.1" - yargs-unparser "^2.0.0" - mocha@^10.0.0: version "10.2.0" resolved "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz" @@ -19456,13 +19198,6 @@ node-dijkstra@2.5.0: resolved "https://registry.npmjs.org/node-dijkstra/-/node-dijkstra-2.5.0.tgz" integrity sha1-D+t2xaBfNbVueG3m300zZK8o1Og= -node-dir@0.1.17: - version "0.1.17" - resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" - integrity sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg== - dependencies: - minimatch "^3.0.2" - node-fetch-native@^1.6.4: version "1.6.4" resolved "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.4.tgz" @@ -20191,11 +19926,6 @@ ora@^5.4.1: strip-ansi "^6.0.0" wcwidth "^1.0.1" -original-require@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/original-require/-/original-require-1.0.1.tgz#0f130471584cd33511c5ec38c8d59213f9ac5e20" - integrity sha512-5vdKMbE58WaE61uVD+PKyh8xdM398UnjPBLotW2sjG5MzHARwta/+NtMBCBA0t2WQblGYBvq5vsiZpWokwno+A== - os-browserify@~0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz" @@ -20402,11 +20132,6 @@ p-waterfall@2.1.1: dependencies: p-reduce "^2.0.0" -package-json-from-dist@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" - integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== - pacote@15.1.1: version "15.1.1" resolved "https://registry.npmjs.org/pacote/-/pacote-15.1.1.tgz" @@ -20770,22 +20495,6 @@ path-scurry@^1.10.1, path-scurry@^1.6.1: lru-cache "^9.1.1 || ^10.0.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" -path-scurry@^1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" - integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== - dependencies: - lru-cache "^10.2.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - -path-scurry@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.2.tgz#6be0d0ee02a10d9e0de7a98bae65e182c9061f85" - integrity sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg== - dependencies: - lru-cache "^11.0.0" - minipass "^7.1.2" - path-to-regexp@0.1.10: version "0.1.10" resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz" @@ -20829,7 +20538,7 @@ pathe@^1.1.1, pathe@^1.1.2: resolved "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz" integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== -pathval@^1.0.0, pathval@^1.1.1: +pathval@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== @@ -20879,17 +20588,12 @@ pegjs@^0.10.0: resolved "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz" integrity sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0= -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== - performance-now@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1: +picocolors@^1.0.0, picocolors@^1.1.0: version "1.1.1" resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== @@ -21259,11 +20963,6 @@ proxy-from-env@^1.1.0: resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -proxy-from-env@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz#a7487568adad577cfaaa7e88c49cab3ab3081aba" - integrity sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA== - proxy-middleware@latest: version "0.15.0" resolved "https://registry.yarnpkg.com/proxy-middleware/-/proxy-middleware-0.15.0.tgz#a3fdf1befb730f951965872ac2f6074c61477a56" @@ -22441,7 +22140,7 @@ semver-compare@^1.0.0: resolved "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz" integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= -"semver@2 || 3 || 4 || 5", semver@7.3.8, semver@7.5.4, semver@7.7.1, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^6.0.0, semver@^6.1.0, semver@^6.3.0, semver@^6.3.1, semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.1, semver@^7.5.3, semver@^7.5.4, semver@^7.7.1, semver@~5.4.1: +"semver@2 || 3 || 4 || 5", semver@7.3.8, semver@7.5.4, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^6.0.0, semver@^6.1.0, semver@^6.3.0, semver@^6.3.1, semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.1, semver@^7.5.3, semver@^7.5.4, semver@^7.7.1, semver@~5.4.1: version "7.5.4" resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -22529,13 +22228,6 @@ serialize-javascript@^6.0.1: dependencies: randombytes "^2.1.0" -serialize-javascript@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" - integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== - dependencies: - randombytes "^2.1.0" - serve-index@^1.9.1: version "1.9.1" resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz" @@ -22966,19 +22658,6 @@ solc@0.8.26: semver "^5.5.0" tmp "0.0.33" -solc@0.8.34: - version "0.8.34" - resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.34.tgz#be5fc555a9863f3f1b2d49d3f5f9897c48e433e7" - integrity sha512-qf8HajA1sHhXRV0hMSDXLjVbc4v3Q+SQbL9zok+1WmgVj7Z4oMjMHxaysCzfGtFVqjZdfDDJWyZI+tcx5bO7Dw== - dependencies: - command-exists "^1.2.8" - commander "^8.1.0" - follow-redirects "^1.12.1" - js-sha3 "0.8.0" - memorystream "^0.3.1" - semver "^5.5.0" - tmp "0.0.33" - solc@^0.4.20: version "0.4.26" resolved "https://registry.npmjs.org/solc/-/solc-0.4.26.tgz" @@ -23113,14 +22792,6 @@ source-map-support@0.5.19: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@0.5.21, source-map-support@^0.5.12, source-map-support@^0.5.13, source-map-support@^0.5.17, source-map-support@~0.5.12, source-map-support@~0.5.20: - version "0.5.21" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-support@^0.4.15: version "0.4.18" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz" @@ -23128,6 +22799,14 @@ source-map-support@^0.4.15: dependencies: source-map "^0.5.6" +source-map-support@^0.5.12, source-map-support@^0.5.13, source-map-support@^0.5.17, source-map-support@~0.5.12, source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-url@^0.4.0: version "0.4.1" resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz" @@ -23804,7 +23483,7 @@ supports-color@4.4.0: dependencies: has-flag "^2.0.0" -supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.1: +supports-color@8.1.1, supports-color@^8.0.0: version "8.1.1" resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== @@ -24403,40 +24082,6 @@ trim-right@^1.0.1: resolved "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz" integrity sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw== -tronbox@4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/tronbox/-/tronbox-4.7.1.tgz#7d00e2c6552a6d916c671db4377e03cf7824aa0c" - integrity sha512-BND9KGI45REKZBRiDsfjUSxlLeTM2H46rH4VJbbkwKTX1sS59/hgywiev383fXi2M9oS3M30c3l6ZzQqV2YysQ== - dependencies: - "@solidity-parser/parser" "0.20.2" - ajv "6.14.0" - async "2.6.4" - axios "1.16.1" - bignumber.js "7.2.1" - chai "4.1.2" - chalk "2.4.2" - colors "1.4.0" - debug "3.2.7" - enquirer "2.4.1" - ethers "6.15.0" - find-up "4.1.0" - fs-extra "8.1.0" - glob "13.0.6" - graphlib "2.1.8" - homedir "0.6.0" - lodash "4.18.1" - mkdirp "0.5.6" - mocha "11.7.4" - node-dir "0.1.17" - original-require "1.0.1" - solc "0.8.34" - source-map-support "0.5.21" - tmp "0.0.33" - tronweb "6.3.0" - vcsurl "0.1.1" - yargs "15.4.1" - yauzl "3.2.1" - tronweb@5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/tronweb/-/tronweb-5.3.2.tgz#393b0fa0290e2c5aa7a3b3b82956f53ca65a764f" @@ -24456,21 +24101,6 @@ tronweb@5.3.2: semver "^5.6.0" validator "^13.7.0" -tronweb@6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/tronweb/-/tronweb-6.3.0.tgz#66275aa792efa8378fadd6af5ad0b4fbfe0e4bc9" - integrity sha512-5CAjDO4/KfymgjKFgnXgfKKQp0xgOn8otCBYjgYAEIpLZDKNAk14Z0dDeg0UqYuceCiyMHjW7a19Rsz8EmhAOw== - dependencies: - "@babel/runtime" "7.26.10" - axios "1.15.0" - bignumber.js "9.1.2" - ethereum-cryptography "2.2.1" - ethers "6.13.5" - eventemitter3 "5.0.1" - google-protobuf "3.21.4" - semver "7.7.1" - validator "13.15.23" - try-to-catch@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/try-to-catch/-/try-to-catch-3.0.0.tgz" @@ -25607,11 +25237,6 @@ validate-npm-package-name@^5.0.0: dependencies: builtins "^5.0.0" -validator@13.15.23: - version "13.15.23" - resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.23.tgz#59a874f84e4594588e3409ab1edbe64e96d0c62d" - integrity sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw== - validator@^13.7.0: version "13.15.26" resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.26.tgz#36c3deeab30e97806a658728a155c66fcaa5b944" @@ -25640,11 +25265,6 @@ vary@^1, vary@~1.1.2: resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= -vcsurl@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/vcsurl/-/vcsurl-0.1.1.tgz#5e00a109e7381b55b5d45b892533c8ec35c9320c" - integrity sha512-UXJ+Vd9rBt7cIbqikL76aO+fwNM/lpliY2SkNrKFKenLoVSkKPOld9DWCDsHEi7o51yWrn3dH1+NuzmTFhmdQg== - verror@1.10.0: version "1.10.0" resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz" @@ -27006,11 +26626,6 @@ workerpool@6.2.1: resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -workerpool@^9.2.0: - version "9.3.4" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-9.3.4.tgz#f6c92395b2141afd78e2a889e80cb338fe9fca41" - integrity sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg== - "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" @@ -27333,7 +26948,7 @@ yargs-parser@^8.1.0: dependencies: camelcase "^4.1.0" -yargs-unparser@2.0.0, yargs-unparser@^2.0.0: +yargs-unparser@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz" integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== @@ -27360,23 +26975,6 @@ yargs@13.2.4: y18n "^4.0.0" yargs-parser "^13.1.0" -yargs@15.4.1, yargs@^15.3.1: - version "15.4.1" - resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" - yargs@16.2.0, yargs@^16.2.0: version "16.2.0" resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" @@ -27437,6 +27035,23 @@ yargs@^13.3.2: y18n "^4.0.0" yargs-parser "^13.1.2" +yargs@^15.3.1: + version "15.4.1" + resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + yargs@^17.0.0: version "17.0.1" resolved "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz" @@ -27483,14 +27098,6 @@ yargs@^4.7.1: y18n "^3.2.1" yargs-parser "^2.4.1" -yauzl@3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-3.2.1.tgz#d35befb9a0fdd328da41926be895ade2de14dbe7" - integrity sha512-k1isifdbpNSFEHFJ1ZY4YDewv0IH9FR61lDetaRMD3j2ae3bIXGV+7c+LHCqtQGofSd8PIyV4X6+dHMAnSr60A== - dependencies: - buffer-crc32 "~0.2.3" - pend "~1.2.0" - yn@3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" From e2cc869e2c970833a761fb74fbe60dd96b9ece4f Mon Sep 17 00:00:00 2001 From: LeoSlrRf Date: Thu, 28 May 2026 19:22:58 +0200 Subject: [PATCH 19/19] revert ts change --- packages/smart-contracts/package.json | 2 +- yarn.lock | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/smart-contracts/package.json b/packages/smart-contracts/package.json index 72ac895d0a..1b71e39bc2 100644 --- a/packages/smart-contracts/package.json +++ b/packages/smart-contracts/package.json @@ -108,7 +108,7 @@ "solhint": "3.3.6", "tronweb": "5.3.2", "typechain": "8.3.2", - "typescript": "5.8.3", + "typescript": "4.8.4", "web3": "1.7.3", "zksync-web3": "0.14.3" } diff --git a/yarn.lock b/yarn.lock index 74f68ebf64..1808ce4151 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24650,6 +24650,11 @@ typescript@2.9.1: resolved "https://registry.npmjs.org/typescript/-/typescript-2.9.1.tgz" integrity sha512-h6pM2f/GDchCFlldnriOhs1QHuwbnmj6/v7499eMHqPeW4V2G0elua2eIc2nu8v2NdHV0Gm+tzX83Hr6nUFjQA== +typescript@4.8.4: + version "4.8.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" + integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== + typescript@5.8.3: version "5.8.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e"