diff --git a/contracts/smart-account/modules/SessionValidationModules/ERC20SessionValidationModule.sol b/contracts/smart-account/modules/SessionValidationModules/ERC20SessionValidationModule.sol index 32a11987..647c4a57 100644 --- a/contracts/smart-account/modules/SessionValidationModules/ERC20SessionValidationModule.sol +++ b/contracts/smart-account/modules/SessionValidationModules/ERC20SessionValidationModule.sol @@ -93,6 +93,13 @@ contract ERC20SessionValidationModule is ISessionValidationModule { ); } + function getUsageCounter( + bytes32 sessionKeyDataHash, + address smartAccount + ) external view returns (uint256) { + return usageCounters[sessionKeyDataHash][smartAccount]; + } + /** * @dev Internal function to validate the session parameters * @param destinationContract address of the contract to be called diff --git a/test/module/SessionValidationModules/ERC20SessionValidation.Module.specs.ts b/test/module/SessionValidationModules/ERC20SessionValidation.Module.specs.ts index 4c5208fb..343dc90d 100644 --- a/test/module/SessionValidationModules/ERC20SessionValidation.Module.specs.ts +++ b/test/module/SessionValidationModules/ERC20SessionValidation.Module.specs.ts @@ -729,6 +729,83 @@ describe("SessionKey: ERC20 Session Validation Module", async () => { }); // SHOULD REVERT IF MAX USAGE EXCEEDED + it("should revert if max usage of the session is exceeded", async () => { + const { + entryPoint, + userSA, + sessionKeyManager, + erc20SessionModule, + mockToken, + sessionKeyData, + leafData, + merkleTree, + } = await setupTests(); + const tokenAmountToTransfer = ethers.utils.parseEther("0.7534"); + + const charlieTokenBalanceBefore = await mockToken.balanceOf( + charlie.address + ); + + const transferUserOp = await makeEcdsaSessionKeySignedUserOp( + "execute_ncC", + [ + mockToken.address, + ethers.utils.parseEther("0"), + encodeTransfer(charlie.address, tokenAmountToTransfer.toString()), + ], + userSA.address, + sessionKey, + entryPoint, + sessionKeyManager.address, + 0, + 0, + erc20SessionModule.address, + sessionKeyData, + merkleTree.getHexProof(ethers.utils.keccak256(leafData)) + ); + + // first successful usage of the session + await entryPoint.handleOps([transferUserOp], alice.address, { + gasLimit: 10000000, + }); + expect(await mockToken.balanceOf(charlie.address)).to.equal( + charlieTokenBalanceBefore.add(tokenAmountToTransfer) + ); + + const sessionKeyDataHash = ethers.utils.keccak256(sessionKeyData); + expect( + await erc20SessionModule.getUsageCounter( + sessionKeyDataHash, + userSA.address + ) + ).to.equal(1); + + const transferUserOp2 = await makeEcdsaSessionKeySignedUserOp( + "execute_ncC", + [ + mockToken.address, + ethers.utils.parseEther("0"), + encodeTransfer(charlie.address, tokenAmountToTransfer.toString()), + ], + userSA.address, + sessionKey, + entryPoint, + sessionKeyManager.address, + 0, + 0, + erc20SessionModule.address, + sessionKeyData, + merkleTree.getHexProof(ethers.utils.keccak256(leafData)) + ); + + await expect( + entryPoint.handleOps([transferUserOp2], alice.address, { + gasLimit: 10000000, + }) + ) + .to.be.revertedWith("FailedOp") + .withArgs(0, "AA23 reverted: ERC20SV Max Usage Exceeded"); + }); describe("validateSessionParams(): ", async () => { it("Should be able to validate valid session params and return the session key address", async () => {