diff --git a/contracts/ERC20SimpleSwap.sol b/contracts/ERC20SimpleSwap.sol index f5ddcd5..0aefc9e 100755 --- a/contracts/ERC20SimpleSwap.sol +++ b/contracts/ERC20SimpleSwap.sol @@ -1,8 +1,12 @@ // SPDX-License-Identifier: BSD-3-Clause -pragma solidity =0.8.19; -import "@openzeppelin/contracts/utils/math/Math.sol"; -import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +pragma solidity =0.7.6; +pragma abicoder v2; +import "@openzeppelin/contracts/math/SafeMath.sol"; +import "@openzeppelin/contracts/math/Math.sol"; +import "@openzeppelin/contracts/cryptography/ECDSA.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/presets/ERC20PresetMinterPauser.sol"; + /** @title Chequebook contract without waivers @@ -13,6 +17,8 @@ Furthermore, solvency can be guaranteed via hardDeposits as a beneficiary, we should always take into account the possibility that a cheque bounces (when no hardDeposits are assigned) */ contract ERC20SimpleSwap { + using SafeMath for uint; + event ChequeCashed( address indexed beneficiary, address indexed recipient, @@ -56,11 +62,15 @@ contract ERC20SimpleSwap { ); // the EIP712 domain this contract uses - function domain() internal view returns (EIP712Domain memory) { + function domain() internal pure returns (EIP712Domain memory) { + uint256 chainId; + assembly { + chainId := chainid() + } return EIP712Domain({ name: "Chequebook", version: "1.0", - chainId: block.chainid + chainId: chainId }); } @@ -75,7 +85,7 @@ contract ERC20SimpleSwap { } // recover a signature with the EIP712 signing scheme - function recoverEIP712(bytes32 hash, bytes memory sig) internal view returns (address) { + function recoverEIP712(bytes32 hash, bytes memory sig) internal pure returns (address) { bytes32 digest = keccak256(abi.encodePacked( "\x19\x01", domainSeparator(domain()), @@ -85,7 +95,7 @@ contract ERC20SimpleSwap { } /* The token against which this chequebook writes cheques */ - IERC20 public token; + ERC20 public token; /* associates every beneficiary with how much has been paid out to them */ mapping (address => uint) public paidOut; /* total amount paid out */ @@ -110,7 +120,7 @@ contract ERC20SimpleSwap { require(_issuer != address(0), "invalid issuer"); require(issuer == address(0), "already initialized"); issuer = _issuer; - token = IERC20(_token); + token = ERC20(_token); defaultHardDepositTimeout = _defaultHardDepositTimeout; } @@ -120,12 +130,12 @@ contract ERC20SimpleSwap { } /// @return the part of the balance that is not covered by hard deposits function liquidBalance() public view returns(uint) { - return balance() - totalHardDeposit; + return balance().sub(totalHardDeposit); } /// @return the part of the balance available for a specific beneficiary function liquidBalanceFor(address beneficiary) public view returns(uint) { - return liquidBalance() + hardDeposits[beneficiary].amount; + return liquidBalance().add(hardDeposits[beneficiary].amount); } /** @dev internal function responsible for checking the issuerSignature, updating hardDeposit balances and doing transfers. @@ -148,7 +158,7 @@ contract ERC20SimpleSwap { "invalid issuer signature"); } /* the requestPayout is the amount requested for payment processing */ - uint requestPayout = cumulativePayout - paidOut[beneficiary]; + uint requestPayout = cumulativePayout.sub(paidOut[beneficiary]); /* calculates acutal payout */ uint totalPayout = Math.min(requestPayout, liquidBalanceFor(beneficiary)); /* calculates hard-deposit usage */ @@ -156,12 +166,13 @@ contract ERC20SimpleSwap { require(totalPayout >= callerPayout, "SimpleSwap: cannot pay caller"); /* if there are some of the hard deposit used, update hardDeposits*/ if (hardDepositUsage != 0) { - hardDeposits[beneficiary].amount = hardDeposits[beneficiary].amount - hardDepositUsage; - totalHardDeposit = totalHardDeposit - hardDepositUsage; + hardDeposits[beneficiary].amount = hardDeposits[beneficiary].amount.sub(hardDepositUsage); + + totalHardDeposit = totalHardDeposit.sub(hardDepositUsage); } /* increase the stored paidOut amount to avoid double payout */ - paidOut[beneficiary] = paidOut[beneficiary] + totalPayout; - totalPaidOut = totalPaidOut + totalPayout; + paidOut[beneficiary] = paidOut[beneficiary].add(totalPayout); + totalPaidOut = totalPaidOut.add(totalPayout); /* let the world know that the issuer has over-promised on outstanding cheques */ if (requestPayout != totalPayout) { @@ -173,7 +184,7 @@ contract ERC20SimpleSwap { /* do a transfer to the caller if specified*/ require(token.transfer(msg.sender, callerPayout), "transfer failed"); /* do the actual payment */ - require(token.transfer(recipient, totalPayout - callerPayout), "transfer failed"); + require(token.transfer(recipient, totalPayout.sub(callerPayout)), "transfer failed"); } else { /* do the actual payment */ require(token.transfer(recipient, totalPayout), "transfer failed"); @@ -250,11 +261,11 @@ contract ERC20SimpleSwap { require(block.timestamp >= hardDeposit.canBeDecreasedAt && hardDeposit.canBeDecreasedAt != 0, "deposit not yet timed out"); /* this throws if decreaseAmount > amount */ //TODO: if there is a cash-out in between prepareDecreaseHardDeposit and decreaseHardDeposit, decreaseHardDeposit will throw and reducing hard-deposits is impossible. - hardDeposit.amount = hardDeposit.amount - hardDeposit.decreaseAmount; + hardDeposit.amount = hardDeposit.amount.sub(hardDeposit.decreaseAmount); /* reset the canBeDecreasedAt to avoid a double decrease */ hardDeposit.canBeDecreasedAt = 0; /* keep totalDeposit in sync */ - totalHardDeposit = totalHardDeposit - hardDeposit.decreaseAmount; + totalHardDeposit = totalHardDeposit.sub(hardDeposit.decreaseAmount); emit HardDepositAmountChanged(beneficiary, hardDeposit.amount); } @@ -266,12 +277,12 @@ contract ERC20SimpleSwap { function increaseHardDeposit(address beneficiary, uint amount) public { require(msg.sender == issuer, "SimpleSwap: not issuer"); /* ensure hard deposits don't exceed the global balance */ - require(totalHardDeposit + amount <= balance(), "hard deposit exceeds balance"); + require(totalHardDeposit.add(amount) <= balance(), "hard deposit exceeds balance"); HardDeposit storage hardDeposit = hardDeposits[beneficiary]; - hardDeposit.amount = hardDeposit.amount + amount; + hardDeposit.amount = hardDeposit.amount.add(amount); // we don't explicitely set hardDepositTimout, as zero means using defaultHardDepositTimeout - totalHardDeposit = totalHardDeposit + amount; + totalHardDeposit = totalHardDeposit.add(amount); /* disable any pending decrease */ hardDeposit.canBeDecreasedAt = 0; emit HardDepositAmountChanged(beneficiary, hardDeposit.amount); @@ -340,5 +351,4 @@ contract ERC20SimpleSwap { decreaseTimeout )); } -} - +} \ No newline at end of file diff --git a/contracts/SimpleSwapFactory.sol b/contracts/SimpleSwapFactory.sol index 07859de..298ba7f 100755 --- a/contracts/SimpleSwapFactory.sol +++ b/contracts/SimpleSwapFactory.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause -pragma solidity =0.8.19; +pragma solidity =0.7.6; +pragma abicoder v2; import "./ERC20SimpleSwap.sol"; import "@openzeppelin/contracts/proxy/Clones.sol"; diff --git a/contracts/TestToken.sol b/contracts/TestToken.sol index 011c459..a437c48 100755 --- a/contracts/TestToken.sol +++ b/contracts/TestToken.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause -pragma solidity =0.8.19; +pragma solidity =0.7.6; -import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol"; +import "@openzeppelin/contracts/presets/ERC20PresetMinterPauser.sol"; contract TestToken is ERC20PresetMinterPauser { diff --git a/hardhat.config.js b/hardhat.config.js index ac5faad..357f086 100755 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -28,7 +28,7 @@ const accounts = module.exports = { defaultNetwork: "hardhat", solidity: { - version: "0.8.19", + version: "0.7.6", settings: { optimizer: { enabled: true, diff --git a/package.json b/package.json index faf8a77..d95c9bb 100755 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "dependencies": { "@nomicfoundation/hardhat-verify": "^1.0.0", "@nomiclabs/hardhat-etherscan": "^3.1.7", - "@openzeppelin/contracts": "^4.4.1", + "@openzeppelin/contracts": "^3.4.1-solc-0.7-2", "dotenv": "^16.0.3" }, "devDependencies": { diff --git a/test/ERC20SimpleSwap.behavior.js b/test/ERC20SimpleSwap.behavior.js index c0ca005..c998e21 100755 --- a/test/ERC20SimpleSwap.behavior.js +++ b/test/ERC20SimpleSwap.behavior.js @@ -303,9 +303,7 @@ function shouldBehaveLikeERC20SimpleSwap([issuer, alice, bob, carol], defaultHar }) context('when the second cumulativePayout is lower than the first cumulativePayout', function () { const secondCumulativePayout = firstCumulativePayout.sub(new BN(1)) - - const revertMessage = 'reverted with panic code 0x11 (Arithmetic operation underflowed or overflowed outside of an unchecked block)'; - + const revertMessage = 'SafeMath: subtraction overflow' const beneficiaryToSign = { cumulativePayout: secondCumulativePayout, recipient, diff --git a/yarn.lock b/yarn.lock index 50cdc46..9c0680d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -934,10 +934,10 @@ find-up "^4.1.0" fs-extra "^8.1.0" -"@openzeppelin/contracts@^4.4.1": - version "4.9.3" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.3.tgz#00d7a8cf35a475b160b3f0293a6403c511099364" - integrity sha512-He3LieZ1pP2TNt5JbkPA4PNT9WC3gOTOlDcFGJW4Le4QKqwmiNJCRt44APfxMxvq7OugU/cqYuPcSBzOw38DAg== +"@openzeppelin/contracts@^3.4.1-solc-0.7-2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.2.tgz#d81f786fda2871d1eb8a8c5a73e455753ba53527" + integrity sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA== "@openzeppelin/test-helpers@^0.5.15": version "0.5.15"