Skip to content

Commit 0b4b46a

Browse files
committed
chore: ID-4134: update tests.
1 parent 2340e7f commit 0b4b46a

File tree

1 file changed

+68
-12
lines changed

1 file changed

+68
-12
lines changed

tests/ModuleAuthDynamic.spec.ts

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ describe('ModuleAuthDynamic Bootstrap Flow', () => {
293293
})
294294

295295
describe('Bootstrap flow - Negative cases', () => {
296-
it('Should reject first transaction without ImmutableSigner when wallet was not pre-deployed with correct salt', async () => {
296+
it('Should accept first transaction without ImmutableSigner when image hash matches deployment salt', async () => {
297297
// Create a random signer that is NOT the ImmutableSigner
298298
const randomSigner = ethers.Wallet.createRandom().connect(hardhat.provider)
299299

@@ -332,15 +332,63 @@ describe('ModuleAuthDynamic Bootstrap Flow', () => {
332332
false
333333
)
334334

335-
// This should still work because the wallet was deployed with the correct salt
336-
// The signature validation will verify against the deployment address
337-
// (This tests the existing flow, not the bootstrap flow)
335+
// This should work because the signature's image hash matches the deployment salt
336+
// This is the normal first-tx flow (not the bootstrap flow with ImmutableSigner)
338337
await wallet.execute([transaction], nonce, signature)
339338
expect(await wallet.nonce()).to.equal(1)
340339
})
341340

342-
it('Should reject first transaction with wrong image hash even with ImmutableSigner', async () => {
343-
// Deploy wallet with specific signers
341+
it('Should reject first transaction without ImmutableSigner when image hash does not match deployment salt', async () => {
342+
// Create two different random signers
343+
const deploymentSigner = ethers.Wallet.createRandom().connect(hardhat.provider)
344+
const attackerSigner = ethers.Wallet.createRandom().connect(hardhat.provider)
345+
346+
// Deploy wallet with deploymentSigner
347+
const walletSalt = encodeImageHash(1, [
348+
{ weight: 1, address: deploymentSigner.address }
349+
])
350+
const walletAddress = addressOf(factory.address, startupWallet.address, walletSalt)
351+
await factory.connect(walletDeployerEOA).deploy(startupWallet.address, walletSalt)
352+
353+
const wallet = MainModule__factory.connect(walletAddress, relayerEOA)
354+
355+
// Transfer funds
356+
await relayerEOA.sendTransaction({ to: walletAddress, value: 1 })
357+
358+
const transaction = {
359+
delegateCall: false,
360+
revertOnError: true,
361+
gasLimit: 1000000,
362+
target: await randomEOA.getAddress(),
363+
value: 1,
364+
data: []
365+
}
366+
367+
const networkId = (await hardhat.provider.getNetwork()).chainId
368+
const nonce = 0
369+
const data = encodeMetaTransactionsData(wallet.address, [transaction], networkId, nonce)
370+
371+
// Sign with attackerSigner (different from deployment signer, NOT ImmutableSigner)
372+
// This creates a different image hash than what the wallet was deployed with
373+
const signature = await walletMultiSign(
374+
[
375+
{ weight: 1, owner: attackerSigner }
376+
],
377+
1,
378+
data,
379+
false
380+
)
381+
382+
// This should FAIL because:
383+
// 1. Image hash doesn't match deployment salt
384+
// 2. ImmutableSigner is not present to vouch via bootstrap flow
385+
await expect(wallet.execute([transaction], nonce, signature)).to.be.revertedWith(
386+
'ModuleCalls#execute: INVALID_SIGNATURE'
387+
)
388+
})
389+
390+
it('Should accept first transaction with different image hash when ImmutableSigner is present', async () => {
391+
// Deploy wallet with specific signers (threshold 2, userEOA + immutableSigner)
344392
const walletSalt = encodeImageHash(2, [
345393
{ weight: 1, address: await userEOA.getAddress() },
346394
{ weight: 1, address: immutableSigner.address }
@@ -366,21 +414,29 @@ describe('ModuleAuthDynamic Bootstrap Flow', () => {
366414
const nonce = 0
367415
const data = encodeMetaTransactionsData(wallet.address, [transaction], networkId, nonce)
368416

369-
// Sign with ONLY ImmutableSigner (threshold 1, but wallet requires threshold 2)
417+
// Sign with ONLY ImmutableSigner (threshold 1, but wallet was deployed with threshold 2)
370418
// This creates a different image hash than what the wallet was deployed with
419+
// Bootstrap flow allows this because ImmutableSigner can vouch for any first transaction
371420
const signature = await walletMultiSign(
372421
[
373422
{ weight: 1, owner: immutableSigner.address, signature: (await ethSign(immutableEOA as ethers.Wallet, data)) + '03' }
374423
],
375-
1, // Wrong threshold - wallet was deployed with threshold 2
424+
1, // Different threshold than deployment - but allowed via bootstrap
376425
data,
377426
false
378427
)
379428

380-
// This should fail because the image hash doesn't match
381-
await expect(wallet.execute([transaction], nonce, signature)).to.be.revertedWith(
382-
'ModuleCalls#execute: INVALID_SIGNATURE'
383-
)
429+
const originalBalance = await randomEOA.getBalance()
430+
431+
// This should SUCCEED because ImmutableSigner can vouch for any first transaction
432+
// regardless of whether the signature's image hash matches the deployment salt
433+
await wallet.execute([transaction], nonce, signature)
434+
435+
// Verify funds were transferred
436+
expect(await randomEOA.getBalance()).to.equal(originalBalance.add(1))
437+
438+
// Verify nonce incremented
439+
expect(await wallet.nonce()).to.equal(1)
384440
})
385441

386442
it('Should reject second transaction with wrong signers after bootstrap', async () => {

0 commit comments

Comments
 (0)