From 305fcf564cd293fc0c7f96b438e4acedf6177b3e Mon Sep 17 00:00:00 2001 From: Ivo Garofalo Date: Thu, 18 Nov 2021 16:46:58 +0100 Subject: [PATCH] add approve function --- .../src/contracts/ERC20EscrowToPay.sol | 46 +++++++++++++------ .../test/contracts/ERC20EscrowToPay.test.ts | 16 ++++++- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/packages/smart-contracts/src/contracts/ERC20EscrowToPay.sol b/packages/smart-contracts/src/contracts/ERC20EscrowToPay.sol index c380430e7d..d499bbb9f0 100644 --- a/packages/smart-contracts/src/contracts/ERC20EscrowToPay.sol +++ b/packages/smart-contracts/src/contracts/ERC20EscrowToPay.sol @@ -109,7 +109,7 @@ contract ERC20EscrowToPay { event RevertedEmergencyClaim(bytes indexed paymentReference); /** - * @notice Emitted when _withdraw "to payee" has been executed. + * @notice Emitted when transaction to and from the escrow has been executed. * @param tokenAddress Address of the ERC20 token smart contract. * @param to Address to the payment issuer, alias payee. * @param amount Amount transfered. @@ -189,9 +189,10 @@ contract ERC20EscrowToPay { } /** - * @notice Locks the request funds for 12 months and cancel any emergency claim. + * @notice Locks the request funds for the payer to recover them + * after 12 months and cancel any emergency claim. * @param _paymentRef Reference of the Invoice related. - * @dev Uses modifiers OnlyPayer and IsNotFrozen. + * @dev Uses modifiers OnlyPayer, IsInEscrow and IsNotFrozen. * @dev unlockDate is set with block.timestamp + twelve months.. */ function freezeRequest(bytes memory _paymentRef) @@ -216,7 +217,7 @@ contract ERC20EscrowToPay { /** * @notice Closes an open escrow and pays the request to payee. * @param _paymentRef Reference of the related Invoice. - * @dev Uses OnlyPayer, modifiers IsInEscrow, IsNOtInEmergencyState and IsNotFrozen. + * @dev Uses OnlyPayer, IsInEscrow, IsNotInEmergencyState and IsNotFrozen. */ function payRequestFromEscrow(bytes memory _paymentRef) external @@ -231,7 +232,7 @@ contract ERC20EscrowToPay { /** * @notice Allows the payee to initiate an emergency claim after a six months lockperiod . * @param _paymentRef Reference of the related Invoice. - * @dev Uses modifiers IsInEscrow, IsNotFrozen. + * @dev Uses modifiers OnlyPayee, IsInEscrow, IsNotInEmergencyState and IsNotFrozen. */ function initiateEmergencyClaim(bytes memory _paymentRef) external @@ -249,7 +250,7 @@ contract ERC20EscrowToPay { /** * @notice Allows the payee claim funds after a six months emergency lockperiod . * @param _paymentRef Reference of the related Invoice. - * @dev Uses modifiers IsInEscrow, IsNotFrozen. + * @dev Uses modifiers OnlyPayee, IsInEscrow and IsNotFrozen. */ function completeEmergencyClaim(bytes memory _paymentRef) external @@ -268,10 +269,10 @@ contract ERC20EscrowToPay { require(_withdraw(_paymentRef, requestMapping[_paymentRef].payee), "Withdraw failed!"); } - /** + /** * @notice Reverts the emergencyState to false and cancels emergencyClaim. * @param _paymentRef Reference of the Invoice related. - * @dev Uses modifiers OnlyPayer and IsNotFrozen. + * @dev Uses modifiers OnlyPayer, IsInEscrow and IsNotFrozen. * @dev Resets emergencyState to false and emergencyClaimDate to zero. */ function revertEmergencyClaim(bytes memory _paymentRef) @@ -291,7 +292,7 @@ contract ERC20EscrowToPay { * @notice Refunds to payer after twelve months and delete the escrow. * @param _paymentRef Reference of the Invoice related. * @dev requires that the request .isFrozen = true and .unlockDate to - * be lower or equal to block.timestamp. + * be lower or equal to block.timestamp. */ function refundFrozenFunds(bytes memory _paymentRef) external @@ -306,7 +307,7 @@ contract ERC20EscrowToPay { require(_withdraw(_paymentRef, requestMapping[_paymentRef].payer), "Withdraw Failed!"); } - /** + /** * @notice Withdraw the funds from the escrow. * @param _paymentRef Reference of the related Invoice. * @param _receiver Receiving address. @@ -324,13 +325,18 @@ contract ERC20EscrowToPay { require(_receiver != address(0), "ZERO adddress"); require(requestMapping[_paymentRef].amount > 0, "ZERO Amount"); + IERC20 requestedToken = IERC20(requestMapping[_paymentRef].tokenAddress); + uint256 _amount = requestMapping[_paymentRef].amount; requestMapping[_paymentRef].amount = 0; - - IERC20(requestMapping[_paymentRef].tokenAddress).approve(address(paymentProxy), _amount); - + + // Checks if the requestedToken is allowed to spend. + if (requestedToken.allowance(address(this),address(paymentProxy)) < _amount) { + approvePaymentProxyToSpend(address(requestedToken)); + } + paymentProxy.transferFromWithReferenceAndFee( - requestMapping[_paymentRef].tokenAddress, + address(requestedToken), _receiver, _amount, _paymentRef, @@ -343,8 +349,18 @@ contract ERC20EscrowToPay { assert(!requestMapping[_paymentRef].emergencyState); delete requestMapping[_paymentRef]; - + return true; } + /** + * @notice Authorizes the proxy to spend a new request currency (ERC20). + * @param _erc20Address Address of an ERC20 used as the request currency. + */ + function approvePaymentProxyToSpend(address _erc20Address) public { + IERC20 erc20 = IERC20(_erc20Address); + uint256 max = 2**256 - 1; + erc20.safeApprove(address(paymentProxy), max); + } + } diff --git a/packages/smart-contracts/test/contracts/ERC20EscrowToPay.test.ts b/packages/smart-contracts/test/contracts/ERC20EscrowToPay.test.ts index 5cc85e2faf..ddefe5bccc 100644 --- a/packages/smart-contracts/test/contracts/ERC20EscrowToPay.test.ts +++ b/packages/smart-contracts/test/contracts/ERC20EscrowToPay.test.ts @@ -22,6 +22,8 @@ describe("Contract: ERC20EscrowToPay", () => { const referenceExample6 = '0xffff'; const referenceExample7 = '0xaabb'; const referenceExample8 = '0xaacc'; + const referenceExample9 = '0xaadd'; + let testERC20: Contract, erc20EscrowToPay: ERC20EscrowToPay, erc20FeeProxy: ERC20FeeProxy; let owner: Signer, payer: Signer, payee: Signer, buidler: Signer; let payerAddress: string, payeeAddress: string, feeAddress: string, erc20EscrowToPayAddress: string; @@ -151,7 +153,7 @@ describe("Contract: ERC20EscrowToPay", () => { it("Should set to frozen w/ lockperiod, cancel emergency claim and emit event.", async () => { await testERC20.connect(payer).approve(erc20EscrowToPayAddress, 2002); await erc20EscrowToPay.connect(payer).payEscrow(testERC20.address, payeeAddress, 2000, referenceExample7, 2, feeAddress); - await erc20EscrowToPay.connect(payee).initiateEmergencyClaim(referenceExample7) + await erc20EscrowToPay.connect(payee).initiateEmergencyClaim(referenceExample7); expect(await erc20EscrowToPay.connect(payer).freezeRequest(referenceExample7)) .to.emit(erc20EscrowToPay, "RequestFrozen") @@ -168,6 +170,18 @@ describe("Contract: ERC20EscrowToPay", () => { expect(erc20EscrowToPay.connect(payer).refundFrozenFunds(referenceExample8)).to.be.reverted; }); + it("Should revert if try to execute EmergencyClaim while request is frozen", async () => { + await testERC20.connect(payer).approve(erc20EscrowToPayAddress, 2002); + await erc20EscrowToPay.connect(payer).payEscrow(testERC20.address, payeeAddress, 2000, referenceExample9, 2, feeAddress); + await erc20EscrowToPay.connect(payer).freezeRequest(referenceExample9); + + const requestMapping = await erc20EscrowToPay.connect(payer).requestMapping(referenceExample9); + + // Make sure the request to be frozen. + expect(requestMapping.isFrozen).to.be.true; + // Expect the call to be reverted. + expect(erc20EscrowToPay.connect(payee).initiateEmergencyClaim(referenceExample9)).to.be.reverted; + }); }); }); \ No newline at end of file