diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2567def..747f388 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## [0.6.2] - 2023-10-24
+
+### Added Changes
+- Added missing `useEtherspotTransactions` hook tests for `estimate` method
+- Fixed `skip` prop to ignore batch group estimations
+- Fixed batching for same chain ID SDK instance
+
## [0.6.1] - 2023-10-24
### Added Changes
diff --git a/__mocks__/@etherspot/prime-sdk.js b/__mocks__/@etherspot/prime-sdk.js
index b60feff..164713f 100644
--- a/__mocks__/@etherspot/prime-sdk.js
+++ b/__mocks__/@etherspot/prime-sdk.js
@@ -6,6 +6,7 @@ const otherAccountAddress = '0xAb4C67d8D7B248B2fA6B638C645466065fE8F1F1'
export class PrimeSdk {
sdkChainId;
+ userOps = [];
constructor (provider, config) {
this.sdkChainId = config.chainId;
@@ -177,6 +178,62 @@ export class PrimeSdk {
return { items: prices }
}
+
+ async clearUserOpsFromBatch() {
+ this.userOps = [];
+ }
+
+ async addUserOpsToBatch(userOp) {
+ this.userOps.push(userOp);
+ }
+
+ async estimate(paymaster) {
+ let maxFeePerGas = ethers.utils.parseUnits('1', 'gwei');
+ let maxPriorityFeePerGas = ethers.utils.parseUnits('1', 'gwei');
+ let callGasLimit = ethers.BigNumber.from('50000');
+
+ if (paymaster?.url === 'someUrl') {
+ maxFeePerGas = ethers.utils.parseUnits('2', 'gwei');
+ maxPriorityFeePerGas = ethers.utils.parseUnits('3', 'gwei');
+ callGasLimit = ethers.BigNumber.from('75000');
+ }
+
+ let finalGasLimit = ethers.BigNumber.from(callGasLimit);
+
+ if (this.sdkChainId === 420) {
+ throw new Error('Transaction reverted: chain too high');
+ }
+
+ this.userOps.forEach((userOp) => {
+ if (userOp.to === '0xDEADBEEF') {
+ throw new Error('Transaction reverted: invalid address');
+ }
+ finalGasLimit = finalGasLimit.add(callGasLimit);
+ if (userOp.data
+ && userOp.data !== '0x0'
+ && userOp.data !== '0xFFF') {
+ finalGasLimit = finalGasLimit.add(callGasLimit);
+ }
+ });
+
+ return {
+ sender: defaultAccountAddress,
+ nonce: ethers.BigNumber.from(1),
+ initCode: '0x001',
+ callData: '0x002',
+ callGasLimit: finalGasLimit,
+ verificationGasLimit: ethers.BigNumber.from('25000'),
+ preVerificationGas: ethers.BigNumber.from('75000'),
+ maxFeePerGas,
+ maxPriorityFeePerGas,
+ paymasterAndData: '0x003',
+ signature: '0x004',
+ }
+ }
+
+ totalGasEstimated({ callGasLimit, verificationGasLimit, preVerificationGas }) {
+ return callGasLimit.add(verificationGasLimit).add(preVerificationGas);
+ }
}
export const isWalletProvider = EtherspotPrime.isWalletProvider;
diff --git a/__tests__/hooks/useEtherspotTransactions.test.js b/__tests__/hooks/useEtherspotTransactions.test.js
index 1fb4273..c246925 100644
--- a/__tests__/hooks/useEtherspotTransactions.test.js
+++ b/__tests__/hooks/useEtherspotTransactions.test.js
@@ -167,4 +167,422 @@ describe('useEtherspotTransactions()', () => {
))
.toThrow('No parent ');
});
+
+ it('estimates single grouped batches', async () => {
+ const wrapper = ({ children }) => (
+
+
+ test
+
+
+
+
+
+
+
+
+
+
+
+ {children}
+
+ );
+
+ const { result } = renderHook(() => useEtherspotTransactions(), { wrapper });
+
+ const estimated = await result.current.estimate();
+ expect(estimated[0].estimatedBatches[0].cost.toString()).toBe('350000');
+ });
+
+ it('estimates multiple grouped batches with skipped and with no batches', async () => {
+ const wrapper = ({ children }) => (
+
+
+ test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ test
+
+
+
+
+
+
+
+ test
+
+
+ {children}
+
+ );
+
+ const { result } = renderHook(() => useEtherspotTransactions(), { wrapper });
+
+ const estimated = await result.current.estimate();
+ expect(estimated[0].estimatedBatches[0].cost.toString()).toBe('350000');
+ expect(estimated[0].estimatedBatches[1].cost.toString()).toBe('200000');
+ expect(estimated[2].estimatedBatches[0].cost.toString()).toBe('250000');
+ });
+
+ it('estimates multiple grouped batches with paymaster', async () => {
+ const wrapper = ({ children }) => (
+
+
+ test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {children}
+
+ );
+
+ const { result } = renderHook(() => useEtherspotTransactions(), { wrapper });
+
+ const estimated = await result.current.estimate();
+ expect(estimated[0].estimatedBatches[0].cost.toString()).toBe('350000');
+ expect(estimated[1].estimatedBatches[0].cost.toString()).toBe('325000');
+ });
+
+ it('estimates multiple grouped batches with matching chain IDs', async () => {
+ const wrapper = ({ children }) => (
+
+
+ test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {children}
+
+ );
+
+ const { result } = renderHook(() => useEtherspotTransactions(), { wrapper });
+
+ const estimated = await result.current.estimate();
+ expect(estimated[0].estimatedBatches[0].cost.toString()).toBe('350000');
+ expect(estimated[0].estimatedBatches[1].cost.toString()).toBe('200000');
+ expect(estimated[1].estimatedBatches[0].cost.toString()).toBe('325000');
+ expect(estimated[2].estimatedBatches[0].cost.toString()).toBe('325000');
+ });
+
+ it('estimates and calls onEstimated for each batch group', async () => {
+ const onEstimated1 = jest.fn((estimated) => estimated);
+ const onEstimated2 = jest.fn((estimated) => estimated);
+
+ const wrapper = ({ children }) => (
+
+
+ test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {children}
+
+ );
+
+ const { result } = renderHook(() => useEtherspotTransactions(), { wrapper });
+
+ const estimated = await result.current.estimate();
+
+ expect(onEstimated1).toBeCalledTimes(1);
+ expect(onEstimated2).toBeCalledTimes(1);
+ expect(onEstimated1.mock.calls[0][0]).toStrictEqual(estimated[0].estimatedBatches);
+ expect(onEstimated2.mock.calls[0][0]).toStrictEqual(estimated[1].estimatedBatches);
+ });
+
+ it('estimates and returns error messages for each batch group', async () => {
+ const onEstimated1 = jest.fn((estimated) => estimated);
+ const onEstimated2 = jest.fn((estimated) => estimated);
+
+ const wrapper = ({ children }) => (
+
+
+ test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {children}
+
+ );
+
+ const { result } = renderHook(() => useEtherspotTransactions(), { wrapper });
+
+ const estimated = await result.current.estimate();
+
+ expect(estimated[0].estimatedBatches[0].errorMessage).toBe('Transaction reverted: chain too high');
+ expect(estimated[1].estimatedBatches[0].errorMessage).toBe('Transaction reverted: invalid address');
+ expect(onEstimated1.mock.calls[0][0]).toStrictEqual(estimated[0].estimatedBatches);
+ expect(onEstimated2.mock.calls[0][0]).toStrictEqual(estimated[1].estimatedBatches);
+ });
+
+ it('estimates and returns error messages for batch group by ID', async () => {
+ const onEstimated1 = jest.fn((estimated) => estimated);
+ const onEstimated2 = jest.fn((estimated) => estimated);
+ const onEstimated3 = jest.fn((estimated) => estimated);
+
+ const wrapper = ({ children }) => (
+
+
+ test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {children}
+
+ );
+
+ const { result } = renderHook(() => useEtherspotTransactions(), { wrapper });
+
+ const estimated1 = await result.current.estimate(['test-id-1']);
+ expect(estimated1.length).toBe(1);
+ expect(estimated1[0].estimatedBatches[0].errorMessage).toBe('Transaction reverted: chain too high');
+ expect(onEstimated1.mock.calls[0][0]).toStrictEqual(estimated1[0].estimatedBatches);
+
+ const estimated2 = await result.current.estimate(['test-id-2', 'test-id-3']);
+ expect(estimated2.length).toBe(2);
+ expect(estimated2[0].estimatedBatches[0].cost.toString()).toBe('325000');
+ expect(estimated2[1].estimatedBatches[0].cost.toString()).toBe('200000');
+ expect(onEstimated2.mock.calls[0][0]).toStrictEqual(estimated2[0].estimatedBatches);
+ expect(onEstimated3.mock.calls[0][0]).toStrictEqual(estimated2[1].estimatedBatches);
+ });
})
diff --git a/package-lock.json b/package-lock.json
index 286fe1f..6bdb7a7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@etherspot/transaction-kit",
- "version": "0.6.0",
+ "version": "0.6.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@etherspot/transaction-kit",
- "version": "0.6.0",
+ "version": "0.6.2",
"license": "MIT",
"dependencies": {
"@etherspot/eip1271-verification-util": "^0.1.2",
diff --git a/package.json b/package.json
index e7faa41..f70abc1 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "@etherspot/transaction-kit",
"description": "React Etherspot Transaction Kit",
- "version": "0.6.1",
+ "version": "0.6.2",
"main": "dist/cjs/index.js",
"scripts": {
"rollup:build": "NODE_OPTIONS=--max-old-space-size=8192 rollup -c",
diff --git a/src/providers/EtherspotTransactionKitContextProvider.tsx b/src/providers/EtherspotTransactionKitContextProvider.tsx
index 7f1a466..2a298d0 100644
--- a/src/providers/EtherspotTransactionKitContextProvider.tsx
+++ b/src/providers/EtherspotTransactionKitContextProvider.tsx
@@ -31,6 +31,8 @@ const EtherspotTransactionKitContextProvider = ({ children }: EtherspotTransacti
return Promise.all(groupedBatchesToEstimate.map(async (groupedBatch): Promise => {
const batches = (groupedBatch.batches ?? []) as IBatch[];
+ if (groupedBatch.skip) return { ...groupedBatch, estimatedBatches: [] };
+
const estimatedBatches: EstimatedBatch[] = [];
// push estimations in same order
@@ -47,7 +49,8 @@ const EtherspotTransactionKitContextProvider = ({ children }: EtherspotTransacti
continue;
}
- const etherspotPrimeSdk = await getSdk(batchChainId);
+ // force new instance for each batch to not mix up user ops added to SDK state batch
+ const etherspotPrimeSdk = await getSdk(batchChainId, true);
try {
if (!forSending) await etherspotPrimeSdk.clearUserOpsFromBatch();