diff --git a/contracts/tokenManager/MysoTokenManager.sol b/contracts/tokenManager/MysoTokenManager.sol index aa408523..ee4e7a81 100644 --- a/contracts/tokenManager/MysoTokenManager.sol +++ b/contracts/tokenManager/MysoTokenManager.sol @@ -33,7 +33,7 @@ contract MysoTokenManager is Ownable2Step, IMysoTokenManager { address public stMysoToken; address public accessSigner; mapping(address => RewardInfo) public rewardInfos; - + mapping(bytes32 => bool) public alreadyClaimed; event RewardInfoSet( address indexed collToken, uint128 collThreshold, @@ -247,13 +247,21 @@ contract MysoTokenManager is Ownable2Step, IMysoTokenManager { if (_signer == address(0)) { return true; } else { - bytes32 payloadHash = keccak256(abi.encode(loan.borrower)); - (bytes32 r, bytes32 vs) = Helpers.splitSignature( - borrowInstructions.mysoTokenManagerData + (bytes memory compactSig, uint256 nonce) = abi.decode( + borrowInstructions.mysoTokenManagerData, + (bytes, uint256) + ); + bytes32 payloadHash = keccak256( + abi.encode(loan.borrower, nonce) ); + if (alreadyClaimed[payloadHash]) { + return false; + } + (bytes32 r, bytes32 vs) = Helpers.splitSignature(compactSig); bytes32 messageHash = ECDSA.toEthSignedMessageHash(payloadHash); address recoveredSigner = messageHash.recover(r, vs); if (recoveredSigner == _signer) { + alreadyClaimed[payloadHash] = true; return true; } return false; diff --git a/test/peer-to-peer/local-tests-tokenmanager.ts b/test/peer-to-peer/local-tests-tokenmanager.ts index 7dbbc0b3..0350cadc 100644 --- a/test/peer-to-peer/local-tests-tokenmanager.ts +++ b/test/peer-to-peer/local-tests-tokenmanager.ts @@ -267,14 +267,19 @@ describe('Peer-to-Peer: Local Tests', function () { } = await setupTest() // create signature for borrower + const nonce = 0 const payload = ethers.utils.defaultAbiCoder.encode( - ['address'], - [borrower1.address] + ['address', 'uint256'], + [borrower1.address, nonce] ) const payloadHash = ethers.utils.keccak256(payload) const signature = await deployer.signMessage(ethers.utils.arrayify(payloadHash)) const sig = ethers.utils.splitSignature(signature) const compactSig = sig.compact + let mysoTokenManagerData = ethers.utils.defaultAbiCoder.encode( + ['bytes', 'uint256'], + [compactSig, nonce] + ) // borrowers approves gateway await usdc.connect(borrower1).approve(borrowerGateway.address, MAX_UINT256) @@ -301,10 +306,10 @@ describe('Peer-to-Peer: Local Tests', function () { // check revert if no sig await expect(borrowerGateway .connect(borrower1) - .borrowWithOnChainQuote(iooVault.address, borrowInstructions, iooOnChainQuote, quoteTupleIdx)).to.be.revertedWith("invalid signature length") + .borrowWithOnChainQuote(iooVault.address, borrowInstructions, iooOnChainQuote, quoteTupleIdx)).to.be.reverted // check pass if valid sig - borrowInstructions.mysoTokenManagerData = compactSig + borrowInstructions.mysoTokenManagerData = mysoTokenManagerData await borrowerGateway .connect(borrower1) .borrowWithOnChainQuote(iooVault.address, borrowInstructions, iooOnChainQuote, quoteTupleIdx) @@ -312,6 +317,11 @@ describe('Peer-to-Peer: Local Tests', function () { let borrower1MytBal = await myt.balanceOf(borrower1.address) expect(borrower1MytBal).to.be.equal(totalMysoLoanAmount1) + // check revert that user can only borrow once with sig + await expect(borrowerGateway + .connect(borrower1) + .borrowWithOnChainQuote(iooVault.address, borrowInstructions, iooOnChainQuote, quoteTupleIdx)).to.be.revertedWithCustomError(mysoTokenManager, "NotAllowed") + // check revert if unauthorized borrower await expect(borrowerGateway .connect(borrower2)