diff --git a/.changeset/tame-mugs-count.md b/.changeset/tame-mugs-count.md new file mode 100644 index 000000000..5a4fff35a --- /dev/null +++ b/.changeset/tame-mugs-count.md @@ -0,0 +1,6 @@ +--- +"@layerzerolabs/devtools-evm": patch +"@layerzerolabs/toolbox-hardhat": patch +--- + +Use getNextNonce when proposing transactions to gnosis diff --git a/packages/devtools-evm/src/signer/sdk.ts b/packages/devtools-evm/src/signer/sdk.ts index db845da5c..34b2d8a8a 100644 --- a/packages/devtools-evm/src/signer/sdk.ts +++ b/packages/devtools-evm/src/signer/sdk.ts @@ -96,13 +96,17 @@ export class GnosisOmniSignerEVM extends async signAndSend(transaction: OmniTransaction): Promise { this.assertTransaction(transaction) const { safeSdk, apiKit } = await this.#initSafe() + const safeAddress = await safeSdk.getAddress() const safeTransaction = await safeSdk.createTransaction({ safeTransactionData: [this.#serializeTransaction(transaction)], + options: { + nonce: await apiKit.getNextNonce(safeAddress), + }, }) const safeTxHash = await safeSdk.getTransactionHash(safeTransaction) const senderSignature = await safeSdk.signTransactionHash(safeTxHash) - const safeAddress = await safeSdk.getAddress() const senderAddress = await this.signer.getAddress() + await apiKit.proposeTransaction({ senderSignature: senderSignature.data, safeAddress, @@ -130,23 +134,20 @@ export class GnosisOmniSignerEVM extends } async #initSafe() { - if (this.safeConfig && (!this.safeSdk || !this.apiKit)) { + if (!this.safeSdk || !this.apiKit) { const ethAdapter = new EthersAdapter({ ethers, signerOrProvider: this.signer, }) this.apiKit = new SafeApiKit({ txServiceUrl: this.safeUrl, ethAdapter }) - const contractNetworks = this.safeConfig.contractNetworks this.safeSdk = await Safe.create({ ethAdapter, safeAddress: this.safeConfig.safeAddress!, - ...(!!contractNetworks && { contractNetworks }), + contractNetworks: this.safeConfig.contractNetworks, }) } - if (!this.safeSdk || !this.apiKit) { - throw new Error('Safe SDK not initialized') - } + return { safeSdk: this.safeSdk, apiKit: this.apiKit } } } diff --git a/packages/devtools-evm/test/signer/sdk.test.ts b/packages/devtools-evm/test/signer/sdk.test.ts index 57f8ed88e..66a023dae 100644 --- a/packages/devtools-evm/test/signer/sdk.test.ts +++ b/packages/devtools-evm/test/signer/sdk.test.ts @@ -1,5 +1,5 @@ import fc from 'fast-check' -import { endpointArbitrary, pointArbitrary } from '@layerzerolabs/test-devtools' +import { endpointArbitrary, evmAddressArbitrary, pointArbitrary } from '@layerzerolabs/test-devtools' import { Signer } from '@ethersproject/abstract-signer' import { GnosisOmniSignerEVM, OmniSignerEVM } from '@/signer' import Safe, { SafeConfig } from '@safe-global/protocol-kit' @@ -108,33 +108,34 @@ describe('signer/ethers', () => { it('should send the transaction using the signer if the eids match', async () => { await fc.assert( fc.asyncProperty( + evmAddressArbitrary, transactionArbitrary, transactionHashArbitrary, - async (transaction, transactionHash) => { + async (safeAddress, transaction, transactionHash) => { const sendTransaction = jest.fn() const getAddress = jest.fn() const signer = { getAddress, sendTransaction } as unknown as Signer - const omniSigner = new GnosisOmniSignerEVM( - transaction.point.eid, - signer, - '', - {} as SafeConfig - ) + const omniSigner = new GnosisOmniSignerEVM(transaction.point.eid, signer, '', { + safeAddress, + }) + // TODO These should be mocked using jest.mock omniSigner['safeSdk'] = { createTransaction: jest.fn().mockResolvedValue({ data: 'transaction' }), getTransactionHash: jest.fn().mockResolvedValue(transactionHash), - getAddress: jest.fn(), + getAddress: jest.fn().mockResolvedValue(safeAddress), signTransactionHash: jest.fn().mockResolvedValue({ data: 'signature' }), } as unknown as Safe const safeService = (omniSigner['apiKit'] = { proposeTransaction: jest.fn(), + getNextNonce: jest.fn(), } as unknown as SafeApiKit) const result = await omniSigner.signAndSend(transaction) expect(result.transactionHash).toEqual(transactionHash) expect(await result.wait()).toEqual({ transactionHash }) + expect(safeService.getNextNonce).toHaveBeenCalledWith(safeAddress) expect(safeService.proposeTransaction).toHaveBeenCalledWith({ - safeAddress: undefined, + safeAddress, safeTransactionData: 'transaction', safeTxHash: transactionHash, senderAddress: undefined,