From 3529b8cbe9dbce3579a5d6d5d61c228f3c2c3bc7 Mon Sep 17 00:00:00 2001 From: RanaBug Date: Wed, 10 Jul 2024 18:10:41 +0100 Subject: [PATCH 1/2] added new modular functions, update getAssets --- CHANGELOG.md | 10 ++ __mocks__/@etherspot/modular-sdk.js | 40 +++++++ __tests__/hooks/useEtherspotModules.test.js | 58 +++++++-- example/src/App.tsx | 126 +++++++++++--------- package-lock.json | 15 +-- package.json | 4 +- src/hooks/useEtherspotAssets.ts | 10 +- src/hooks/useEtherspotModules.ts | 86 ++++++++++++- src/hooks/useEtherspotSwaps.ts | 2 + 9 files changed, 269 insertions(+), 82 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e71b1e3..8a9d565 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [0.14.0] - 2024-07-10 + +### Added Changes +- Version update of `etherspot-modular` +- Added Etherspot Modular SDK `getAllModules` and `isModuleInstalled` to hook `useEtherspotModules` +- Updated `uninstallModule` with Etherspot Modular SDK `generateModuleDeInitData` to hook `useEtherspotModules` which allows to install and uninstall several modules + +### Breaking Changes +- Updated `getAssets` which accepts optional props `chainId` and `name` + ## [0.13.0] - 2024-06-17 ### Added Changes diff --git a/__mocks__/@etherspot/modular-sdk.js b/__mocks__/@etherspot/modular-sdk.js index e9a97b6..a16887b 100644 --- a/__mocks__/@etherspot/modular-sdk.js +++ b/__mocks__/@etherspot/modular-sdk.js @@ -101,6 +101,14 @@ export class ModularSdk { return userOpHash; } + async generateModuleDeInitData() { + const deInitData = ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + ['0x0000000000000000000000000000000000000001', '0x00'] + ); + return deInitData; + } + async installModule(moduleType, module, initData, accountAddress) { if (!accountAddress && !defaultAccountAddressModular) { throw new Error('No account address provided!') @@ -132,6 +140,38 @@ export class ModularSdk { return '0x456'; } + + async getAllModules(pageSize, accountAddress) { + if (!pageSize) { + pageSize = 50 + } + + if (!accountAddress && !defaultAccountAddressModular) { + throw new Error('No account address provided!') + } + + return { + validators: ['0x000', '0x111', '0x222'] + }; + } + + async isModuleInstalled(moduleType, module, accountAddress) { + if (!accountAddress && !defaultAccountAddressModular) { + throw new Error('No account address provided!') + } + + if (!moduleType || !module) { + throw new Error('uninstallModule props missing') + } + + if (module === '0x111') { + return true; + } + + if (module === '0x222') { + return false; + } + } } export const isWalletProvider = EtherspotModular.isWalletProvider; diff --git a/__tests__/hooks/useEtherspotModules.test.js b/__tests__/hooks/useEtherspotModules.test.js index 40c1ce0..806e124 100644 --- a/__tests__/hooks/useEtherspotModules.test.js +++ b/__tests__/hooks/useEtherspotModules.test.js @@ -13,10 +13,7 @@ const initData = ethers.utils.defaultAbiCoder.encode( ["address", "bytes"], ['0x0000000000000000000000000000000000000001', '0x00'] ); - const deInitData = ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - ['0x0000000000000000000000000000000000000001', '0x00'] - ); + const deInitData = '0x00' describe('useEtherspotModules()', () => { it('install one module', async () => { @@ -31,7 +28,6 @@ describe('useEtherspotModules()', () => { wrapper, }); - // wait for balances to be fetched for chain ID 1 await waitFor(() => expect(result.current).not.toBeNull()); @@ -69,7 +65,7 @@ describe('useEtherspotModules()', () => { // wait for balances to be fetched for chain ID 1 await waitFor(() => expect(result.current).not.toBeNull()); - const uninstallModuleNotInstalled = await result.current.uninstallModule(MODULE_TYPE.VALIDATOR, '0x222', deInitData) + const uninstallModuleNotInstalled = await result.current.uninstallModule(MODULE_TYPE.VALIDATOR, '0x222') .catch((e) => { console.error(e); return `${e}` @@ -88,8 +84,56 @@ describe('useEtherspotModules()', () => { expect(uninstallModulePropsMissing).toBe('Error: Failed to uninstall module: Error: uninstallModule props missing'); - const uninstallOneModule = await result.current.uninstallModule(MODULE_TYPE.VALIDATOR, moduleAddress, deInitData); + const uninstallOneModule = await result.current.uninstallModule(MODULE_TYPE.VALIDATOR, moduleAddress); expect(uninstallOneModule).toBe('0x456'); }); + + it('list of modules for one wallet', async () => { + const wrapper = ({ children }) => ( + + {children} + + ); + + const { result } = renderHook(({ chainId }) => useEtherspotModules(chainId), { + initialProps: { chainId: 1 }, + wrapper, + }); + + // wait for balances to be fetched for chain ID 1 + await waitFor(() => expect(result.current).not.toBeNull()); + + const installOneModule = await result.current.installModule(MODULE_TYPE.VALIDATOR, moduleAddress, initData); + expect(installOneModule).toBe('0x123') + + const listOfModules = await result.current.listModules(); + expect(listOfModules.validators).toContain('0x111'); + expect(listOfModules.validators).not.toContain('0x123'); + }); + + it('isModule installed', async () => { + const wrapper = ({ children }) => ( + + {children} + + ); + + const { result } = renderHook(({ chainId }) => useEtherspotModules(chainId), { + initialProps: { chainId: 1 }, + wrapper, + }); + + // wait for balances to be fetched for chain ID 1 + await waitFor(() => expect(result.current).not.toBeNull()); + + const installOneModule = await result.current.installModule(MODULE_TYPE.VALIDATOR, moduleAddress, initData); + expect(installOneModule).toBe('0x123') + + const isModuleOneInstalled = await result.current.isModuleInstalled(MODULE_TYPE.VALIDATOR, moduleAddress); + expect(isModuleOneInstalled).toBe(true); + + const isModuleTowInstalled = await result.current.isModuleInstalled(MODULE_TYPE.VALIDATOR, '0x222'); + expect(isModuleTowInstalled).toBe(false); + }); }) diff --git a/example/src/App.tsx b/example/src/App.tsx index 7a55607..8904fc1 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,10 +1,12 @@ +import { ModuleInfo } from '@etherspot/modular-sdk/dist/sdk/base/EtherspotWalletAPI'; import { MODULE_TYPE } from '@etherspot/modular-sdk/dist/sdk/common'; import { EstimatedBatch, EtherspotBatch, EtherspotBatches, EtherspotTransaction, - IEstimatedBatches, ISentBatches, + IEstimatedBatches, + ISentBatches, SentBatch, useEtherspot, useEtherspotTransactions, @@ -34,9 +36,7 @@ const testModuleSepoliaTestnet = '0x6a00da4DEEf677Ad854B7c14F17Ed9312c2B5fDf'; const CodePreview = ({ code }: { code: string }) => (
-      
-        {code.replaceAll('\n      ', '\n')}
-      
+      {code.replaceAll('\n      ', '\n')}
     
); @@ -56,14 +56,14 @@ const exampleCode = { - ) + ), }, [tabs.MULTIPLE_TRANSACTIONS]: { preview: ` - - + + `, @@ -74,7 +74,7 @@ const exampleCode = { - ) + ), }, }; @@ -83,7 +83,8 @@ const App = () => { const { batches, estimate, send } = useEtherspotTransactions(); // eslint-disable-next-line @typescript-eslint/no-unused-vars const { getDataService, chainId: etherspotChainId } = useEtherspot(); - const { installModule, uninstallModule } = useEtherspotModules(); + const { installModule, uninstallModule, listModules } = useEtherspotModules(); + const etherspotPrimeOrModularAddress = useWalletAddress(); const [balancePerAddress, setBalancePerAddress] = useState({ [walletAddressByName.Alice]: '', [walletAddressByName.Bob]: '', @@ -93,8 +94,7 @@ const App = () => { const [sent, setSent] = useState(null); const [isEstimating, setIsEstimating] = useState(false); const [isSending, setIsSending] = useState(false); - const etherspotPrimeAddress = useWalletAddress(); - + const [modulesList, setModulesList] = useState(); const batchesTreeView = batches.map((batchGroup, id1) => ({ ...batchGroup, @@ -104,7 +104,7 @@ const App = () => { treeNodeId: `batch-${batch.id ?? id2}`, transactions: batch.transactions?.map((transaction, id3) => ({ treeNodeId: `transaction-${transaction.id ?? id3}`, - ...transaction + ...transaction, })), })), })); @@ -160,23 +160,25 @@ const App = () => { }; const deInitData = ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], + ['address', 'bytes'], ['0x0000000000000000000000000000000000000001', '0x00'] ); const onInstallModuleClick = async () => { await installModule(MODULE_TYPE.VALIDATOR, testModuleSepoliaTestnet); - } + await listModules().then((list) => setModulesList(list)); + }; const onUninstallModuleClick = async () => { - await uninstallModule(MODULE_TYPE.VALIDATOR, testModuleSepoliaTestnet, deInitData) - } + await uninstallModule(MODULE_TYPE.VALIDATOR, testModuleSepoliaTestnet, deInitData); + await listModules().then((list) => setModulesList(list)); + }; useEffect(() => { let expired = false; const refreshBalances = async () => { - const dataServices = getDataService() + const dataServices = getDataService(); const updatedBalances = { [walletAddressByName.Alice]: 'N/A', @@ -184,55 +186,61 @@ const App = () => { [walletAddressByName.Christie]: 'N/A', }; - if (etherspotPrimeAddress) { - updatedBalances[etherspotPrimeAddress] = 'N/A'; + if (etherspotPrimeOrModularAddress) { + updatedBalances[etherspotPrimeOrModularAddress] = 'N/A'; } - await Promise.all(Object.keys(updatedBalances).map(async (address) => { - const balances = await dataServices.getAccountBalances({ - account: address, - chainId: +(process.env.REACT_APP_CHAIN_ID as string) - }); - const balance = balances && balances.items.find(({ token }) => token === null); - if (balance) updatedBalances[address] = ethers.utils.formatEther(balance.balance); - })); + await Promise.all( + Object.keys(updatedBalances).map(async (address) => { + const balances = await dataServices.getAccountBalances({ + account: address, + chainId: +(process.env.REACT_APP_CHAIN_ID as string), + }); + const balance = balances && balances.items.find(({ token }) => token === null); + if (balance) updatedBalances[address] = ethers.utils.formatEther(balance.balance); + }) + ); if (expired) return; setBalancePerAddress(updatedBalances); + await listModules().then((list) => setModulesList(list)); }; refreshBalances(); return () => { expired = true; - } + }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [etherspotPrimeAddress]); + }, [etherspotPrimeOrModularAddress]); - const estimationFailed = estimated?.some(( - estimatedGroup, - ) => estimatedGroup.estimatedBatches?.some((estimatedBatch) => estimatedBatch.errorMessage)); + const estimationFailed = estimated?.some((estimatedGroup) => + estimatedGroup.estimatedBatches?.some((estimatedBatch) => estimatedBatch.errorMessage) + ); return ( - Sepolia ETH Faucet + + Sepolia ETH Faucet + {Object.keys(walletAddressByName).map((addressName: string) => { const address = walletAddressByName[addressName as keyof typeof walletAddressByName]; - const balancePart = `balance: ${balancePerAddress[address] ? `${Number(balancePerAddress[address]).toFixed(4)} ETH` : 'Loading...'}`; + const balancePart = `balance: ${ + balancePerAddress[address] ? `${Number(balancePerAddress[address]).toFixed(4)} ETH` : 'Loading...' + }`; const chipLabel = `${addressName} ${balancePart}`; return ( { })} - - Etherspot Smart Wallet Address: - - {!!etherspotPrimeAddress?.length && ( + Etherspot Smart Wallet Address: + {!!etherspotPrimeOrModularAddress?.length && ( - - {etherspotPrimeAddress} - + {etherspotPrimeOrModularAddress} )} - - + + {modulesList && ( +
+ Modules installed: +
    + {modulesList.validators?.map((module, i) => ( +
  • + {module} +
  • + ))} +
+
+ )} + + +
setActiveTab(id)}> @@ -266,12 +284,7 @@ const App = () => { -