diff --git a/.env.example b/.env.example index 5ca7d0e..8159f35 100644 --- a/.env.example +++ b/.env.example @@ -2,4 +2,8 @@ FORK_RPC_ENDPOINT= MONGO_URI=mongodb://3ac:3ac@db:27017 SERVER_HOST=0.0.0.0 SERVER_PORT=4000 -GNOSIS_RPC_ENDPOINT=https://rpc.gnosischain.com + +ETHEREUM_RPC_URL=https://ethereum.publicnode.com +GNOSIS_RPC_URL=https://rpc.gnosischain.com +ARBITRUM_RPC_URL=https://arb1.arbitrum.io/rpc +BASE_RPC_URL=https://mainnet.base.org diff --git a/packages/contracts/foundry.toml b/packages/contracts/foundry.toml index 0b8ba29..cf82efb 100644 --- a/packages/contracts/foundry.toml +++ b/packages/contracts/foundry.toml @@ -4,19 +4,15 @@ test = 'tests' libs = ['lib'] out = 'out' optimizer = true -solc_version = '0.8.20' -evm_version = 'london' +solc_version = '0.8.28' +evm_version = 'cancun' eth_rpc_url = 'https://rpc.gnosischain.com' [rpc_endpoints] ethereum = "${ETHEREUM_RPC_URL}" gnosis = "${GNOSIS_RPC_URL}" arbitrum = "${ARBITRUM_RPC_URL}" - -[etherscan] -ethereum = { key = "${ETHERSCAN_API_KEY}" } -gnosis = { key = "${GNOSISSCAN_API_KEY}" } -arbitrum = { key = "${ARBITRUM_API_KEY}" } +base = "${BASE_RPC_URL}" [fmt] tab_width = 2 \ No newline at end of file diff --git a/packages/contracts/src/DCAOrder.sol b/packages/contracts/src/DCAOrder.sol index b853c50..3c5e5c9 100644 --- a/packages/contracts/src/DCAOrder.sol +++ b/packages/contracts/src/DCAOrder.sol @@ -1,16 +1,16 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.20; - -import {IERC20} from "oz/token/ERC20/IERC20.sol"; -import {SafeERC20} from "oz/token/ERC20/utils/SafeERC20.sol"; -import {IGPv2Settlement} from "./interfaces/IGPv2Settlement.sol"; -import {IConditionalOrder} from "./interfaces/IConditionalOrder.sol"; -import {IDCAOrder} from "./interfaces/IDCAOrder.sol"; -import {GPv2Order} from "./libraries/GPv2Order.sol"; -import {GPv2EIP1271, EIP1271Verifier} from "./interfaces/EIP1271Verifier.sol"; -import {BokkyPooBahsDateTimeLibrary} from "date/BokkyPooBahsDateTimeLibrary.sol"; -import {SafeMath} from "oz/utils/math/SafeMath.sol"; -import {Math} from "oz/utils/math/Math.sol"; +pragma solidity 0.8.28; + +import {IERC20} from 'oz/token/ERC20/IERC20.sol'; +import {SafeERC20} from 'oz/token/ERC20/utils/SafeERC20.sol'; +import {IGPv2Settlement} from './interfaces/IGPv2Settlement.sol'; +import {IConditionalOrder} from './interfaces/IConditionalOrder.sol'; +import {IDCAOrder} from './interfaces/IDCAOrder.sol'; +import {GPv2Order} from './libraries/GPv2Order.sol'; +import {GPv2EIP1271, EIP1271Verifier} from './interfaces/EIP1271Verifier.sol'; +import {BokkyPooBahsDateTimeLibrary} from 'date/BokkyPooBahsDateTimeLibrary.sol'; +import {SafeMath} from 'oz/utils/math/SafeMath.sol'; +import {Math} from 'oz/utils/math/Math.sol'; error OrderCancelled(); error NotOwner(); @@ -49,7 +49,17 @@ contract DCAOrder is IConditionalOrder, EIP1271Verifier, IDCAOrder { /// @dev The initial amount of the DCA order. uint256 public amount; - event Initialized(address indexed order); + event Initialized( + address indexed order, + address owner, + address receiver, + address sellToken, + address buyToken, + uint256 amount, + uint256 startTime, + uint256 endTime, + uint256 interval + ); event Cancelled(address indexed order); /// @dev Initializes the DCAOrder with the specified parameters. @@ -112,7 +122,17 @@ contract DCAOrder is IConditionalOrder, EIP1271Verifier, IDCAOrder { IERC20(_sellToken).safeApprove(address(IGPv2Settlement(_settlementContract).vaultRelayer()), type(uint256).max); emit ConditionalOrderCreated(address(this)); // Required by COW to watch this contract // Emit Initialized event for indexing - emit Initialized(address(this)); + emit Initialized( + address(this), // order + _owner, // owner + _receiver, // receiver + _sellToken, // sellToken + _buyToken, // buyToken + _amount, // amount + _startTime, // startTime + _endTime, // endTime + _interval // interval + ); return true; } @@ -149,20 +169,21 @@ contract DCAOrder is IConditionalOrder, EIP1271Verifier, IDCAOrder { // ensures that orders queried shortly after one another result in the same hash (to avoid spamming the orderbook) // solhint-disable-next-line not-rely-on-time uint32 currentTimeBucket = ((uint32(orderExecutionTime) / 900) + 1) * 900; - return GPv2Order.Data( - sellToken, - buyToken, - receiver, // The receiver - orderSellAmount, - 1, // 0 buy amount is not allowed - currentTimeBucket + 900, // between 15 and 30 miunte validity - keccak256("DollarCostAveraging"), - 0, - GPv2Order.KIND_SELL, - false, - GPv2Order.BALANCE_ERC20, - GPv2Order.BALANCE_ERC20 - ); + return + GPv2Order.Data( + sellToken, + buyToken, + receiver, // The receiver + orderSellAmount, + 1, // 0 buy amount is not allowed + currentTimeBucket + 900, // between 15 and 30 miunte validity + keccak256('DollarCostAveraging'), + 0, + GPv2Order.KIND_SELL, + false, + GPv2Order.BALANCE_ERC20, + GPv2Order.BALANCE_ERC20 + ); // uint32 currentTimeBucket = ((uint32(block.timestamp) / 900) + 1) * 900; } @@ -170,12 +191,12 @@ contract DCAOrder is IConditionalOrder, EIP1271Verifier, IDCAOrder { /// @param encodedOrder Bytes-encoded order information, originally created by an off-chain bot. Created by concatening the order data (in the form of GPv2Order.Data), the price checker address, and price checker data. function isValidSignature(bytes32 orderDigest, bytes calldata encodedOrder) external view override returns (bytes4) { GPv2Order.Data memory order = abi.decode(encodedOrder, (GPv2Order.Data)); - require(order.hash(domainSeparator) == orderDigest, "encoded order digest mismatch"); + require(order.hash(domainSeparator) == orderDigest, 'encoded order digest mismatch'); // If getTradeableOrder() may change between blocks (e.g. because of a variable exchange rate or exprity date, perform a proper attribute comparison with `order` here instead of matching full hashes) require( IConditionalOrder(this).getTradeableOrder().hash(domainSeparator) == orderDigest, - "encoded order != tradable order" + 'encoded order != tradable order' ); return GPv2EIP1271.MAGICVALUE; @@ -183,12 +204,17 @@ contract DCAOrder is IConditionalOrder, EIP1271Verifier, IDCAOrder { /// @dev get the total number of orders that will be executed between the start and end time function orderSlots() public view returns (uint256[] memory slots) { - uint256 total = Math.ceilDiv(BokkyPooBahsDateTimeLibrary.diffHours(startTime, endTime), interval); + uint256 _startTime = startTime; + uint256 _interval = interval; + uint256 intervalHours = _interval * 1 hours; + uint256 total = Math.ceilDiv(BokkyPooBahsDateTimeLibrary.diffHours(_startTime, endTime), _interval); + slots = new uint256[](total); - // Create execution orders + + uint256 nextSlot = _startTime; for (uint256 i = 0; i < total; i++) { - uint256 orderExecutionTime = startTime + (i * interval * 1 hours); - slots[i] = orderExecutionTime; + slots[i] = nextSlot; + nextSlot += intervalHours; } return slots; } @@ -197,6 +223,7 @@ contract DCAOrder is IConditionalOrder, EIP1271Verifier, IDCAOrder { /// @dev a slot is consider current if the current time is greater than the slot time and less than the next slot time (if it exists) function currentSlot() public view returns (uint256 slot) { uint256 _startTime = startTime; + uint256 _endTime = endTime; uint256 currentTime = block.timestamp; // Calculate the next time slot based on the current time @@ -205,10 +232,10 @@ contract DCAOrder is IConditionalOrder, EIP1271Verifier, IDCAOrder { } // If the curernt time is beyond the end time, return 0 indicating no further time slots - if (currentTime > endTime) { + if (currentTime > _endTime) { return 0; } - + uint256 intervalTimestamp = interval * 1 hours; return _startTime + (((currentTime - _startTime) / intervalTimestamp) * intervalTimestamp); } @@ -217,25 +244,27 @@ contract DCAOrder is IConditionalOrder, EIP1271Verifier, IDCAOrder { /// @return bool True if the current timestamp corresponds to the last time slot, otherwise false. function isLastSlot() public view returns (bool) { uint256 intervalTimestamp = interval * 1 hours; - return ((startTime + ((((block.timestamp - startTime) / intervalTimestamp) + 1) * intervalTimestamp)) + intervalTimestamp) > endTime; + return + ((startTime + ((((block.timestamp - startTime) / intervalTimestamp) + 1) * intervalTimestamp)) + + intervalTimestamp) > endTime; } /// @dev returns the sell amount for the each slot - function slotSellAmount() public view returns (uint256 orderSellAmount) { + function slotSellAmount() public view returns (uint256) { // Execute at the specified frequency // Each order sellAmount is the balance of the order divided by the frequency // If the current slot is the last slot, the returned amount is the total sellToken balance - uint256 _endTime = endTime; - // solhint-disable-next-line not-rely-on-time + + if (block.timestamp >= endTime) { + return 0; + } + if (isLastSlot()) { return sellToken.balanceOf(address(this)); } - - if (block.timestamp >= _endTime) { - return 0; - } - // amount divided by total amount of orders - (, orderSellAmount) = SafeMath.tryDiv(amount, (Math.ceilDiv(BokkyPooBahsDateTimeLibrary.diffHours(startTime, _endTime), interval))); + // Use storage variables directly since they're only accessed once + uint256 totalSlots = Math.ceilDiv(BokkyPooBahsDateTimeLibrary.diffHours(startTime, endTime), interval); + return amount / totalSlots; } } diff --git a/packages/contracts/src/OrderFactory.sol b/packages/contracts/src/OrderFactory.sol index 91675a6..ca67aee 100644 --- a/packages/contracts/src/OrderFactory.sol +++ b/packages/contracts/src/OrderFactory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: LGPL-3.0-only -pragma solidity 0.8.20; +pragma solidity 0.8.28; import {Clones} from 'oz/proxy/Clones.sol'; import {IERC20} from 'oz/token/ERC20/IERC20.sol'; diff --git a/packages/contracts/src/interfaces/EIP1271Verifier.sol b/packages/contracts/src/interfaces/EIP1271Verifier.sol index 5eb055a..4af6ae0 100644 --- a/packages/contracts/src/interfaces/EIP1271Verifier.sol +++ b/packages/contracts/src/interfaces/EIP1271Verifier.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: LGPL-3.0-or-later -pragma solidity ^0.8.20; +pragma solidity ^0.8.28; library GPv2EIP1271 { /// @dev Value returned by a call to `isValidSignature` if the signature diff --git a/packages/contracts/src/interfaces/IConditionalOrder.sol b/packages/contracts/src/interfaces/IConditionalOrder.sol index 4434c1a..5ccb4e5 100644 --- a/packages/contracts/src/interfaces/IConditionalOrder.sol +++ b/packages/contracts/src/interfaces/IConditionalOrder.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.28; pragma abicoder v2; -import {GPv2Order} from "../libraries/GPv2Order.sol"; +import {GPv2Order} from '../libraries/GPv2Order.sol'; interface IConditionalOrder { /// Event that should be emitted in constructor so that the service "watching" for conditional orders can start indexing it diff --git a/packages/contracts/src/interfaces/IDCAOrder.sol b/packages/contracts/src/interfaces/IDCAOrder.sol index c029602..499576e 100644 --- a/packages/contracts/src/interfaces/IDCAOrder.sol +++ b/packages/contracts/src/interfaces/IDCAOrder.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.28; pragma abicoder v2; interface IDCAOrder { diff --git a/packages/contracts/src/interfaces/IGPv2Settlement.sol b/packages/contracts/src/interfaces/IGPv2Settlement.sol index 908c368..ae18d02 100644 --- a/packages/contracts/src/interfaces/IGPv2Settlement.sol +++ b/packages/contracts/src/interfaces/IGPv2Settlement.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.28; interface IGPv2Settlement { /// @dev Sets a presignature for the specified order UID. diff --git a/packages/contracts/src/libraries/GPv2Order.sol b/packages/contracts/src/libraries/GPv2Order.sol index 6a828a9..cfa32d0 100644 --- a/packages/contracts/src/libraries/GPv2Order.sol +++ b/packages/contracts/src/libraries/GPv2Order.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later -pragma solidity 0.8.20; +pragma solidity 0.8.28; -import {IERC20} from "oz/interfaces/IERC20.sol"; +import {IERC20} from 'oz/interfaces/IERC20.sol'; /// @title Gnosis Protocol v2 Order Library /// @author Gnosis Developers @@ -44,7 +44,7 @@ library GPv2Order { /// ")" /// ) /// ``` - bytes32 internal constant TYPE_HASH = hex"d5a25ba2e97094ad7d83dc28a6572da797d6b3e7fc6663bd93efb789fc17e489"; + bytes32 internal constant TYPE_HASH = hex'd5a25ba2e97094ad7d83dc28a6572da797d6b3e7fc6663bd93efb789fc17e489'; /// @dev The marker value for a sell order for computing the order struct /// hash. This allows the EIP-712 compatible wallets to display a @@ -54,7 +54,7 @@ library GPv2Order { /// ``` /// keccak256("sell") /// ``` - bytes32 internal constant KIND_SELL = hex"f3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee346775"; + bytes32 internal constant KIND_SELL = hex'f3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee346775'; /// @dev The OrderKind marker value for a buy order for computing the order /// struct hash. @@ -63,7 +63,7 @@ library GPv2Order { /// ``` /// keccak256("buy") /// ``` - bytes32 internal constant KIND_BUY = hex"6ed88e868af0a1983e3886d5f3e95a2fafbd6c3450bc229e27342283dc429ccc"; + bytes32 internal constant KIND_BUY = hex'6ed88e868af0a1983e3886d5f3e95a2fafbd6c3450bc229e27342283dc429ccc'; /// @dev The TokenBalance marker value for using direct ERC20 balances for /// computing the order struct hash. @@ -72,7 +72,7 @@ library GPv2Order { /// ``` /// keccak256("erc20") /// ``` - bytes32 internal constant BALANCE_ERC20 = hex"5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9"; + bytes32 internal constant BALANCE_ERC20 = hex'5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9'; /// @dev The TokenBalance marker value for using Balancer Vault external /// balances (in order to re-use Vault ERC20 approvals) for computing the @@ -82,7 +82,7 @@ library GPv2Order { /// ``` /// keccak256("external") /// ``` - bytes32 internal constant BALANCE_EXTERNAL = hex"abee3b73373acd583a130924aad6dc38cfdc44ba0555ba94ce2ff63980ea0632"; + bytes32 internal constant BALANCE_EXTERNAL = hex'abee3b73373acd583a130924aad6dc38cfdc44ba0555ba94ce2ff63980ea0632'; /// @dev The TokenBalance marker value for using Balancer Vault internal /// balances for computing the order struct hash. @@ -91,7 +91,7 @@ library GPv2Order { /// ``` /// keccak256("internal") /// ``` - bytes32 internal constant BALANCE_INTERNAL = hex"4ac99ace14ee0a5ef932dc609df0943ab7ac16b7583634612f8dc35a4289a6ce"; + bytes32 internal constant BALANCE_INTERNAL = hex'4ac99ace14ee0a5ef932dc609df0943ab7ac16b7583634612f8dc35a4289a6ce'; /// @dev Marker address used to indicate that the receiver of the trade /// proceeds should the owner of the order. @@ -145,7 +145,7 @@ library GPv2Order { // solhint-disable-next-line no-inline-assembly assembly { let freeMemoryPointer := mload(0x40) - mstore(freeMemoryPointer, "\x19\x01") + mstore(freeMemoryPointer, '\x19\x01') mstore(add(freeMemoryPointer, 2), domainSeparator) mstore(add(freeMemoryPointer, 34), structHash) orderDigest := keccak256(freeMemoryPointer, 66) @@ -164,7 +164,7 @@ library GPv2Order { /// @param owner The address of the user who owns this order. /// @param validTo The epoch time at which the order will stop being valid. function packOrderUidParams(bytes memory orderUid, bytes32 orderDigest, address owner, uint32 validTo) internal pure { - require(orderUid.length == UID_LENGTH, "GPv2: uid buffer overflow"); + require(orderUid.length == UID_LENGTH, 'GPv2: uid buffer overflow'); // NOTE: Write the order UID to the allocated memory buffer. The order // parameters are written to memory in **reverse order** as memory @@ -206,12 +206,10 @@ library GPv2Order { /// parameters. /// @return owner The address of the user who owns this order. /// @return validTo The epoch time at which the order will stop being valid. - function extractOrderUidParams(bytes calldata orderUid) - internal - pure - returns (bytes32 orderDigest, address owner, uint32 validTo) - { - require(orderUid.length == UID_LENGTH, "GPv2: invalid uid"); + function extractOrderUidParams( + bytes calldata orderUid + ) internal pure returns (bytes32 orderDigest, address owner, uint32 validTo) { + require(orderUid.length == UID_LENGTH, 'GPv2: invalid uid'); // Use assembly to efficiently decode packed calldata. // solhint-disable-next-line no-inline-assembly diff --git a/packages/contracts/tests/DCAOrder.t.sol b/packages/contracts/tests/DCAOrder.t.sol index a0e3f72..6637cfa 100644 --- a/packages/contracts/tests/DCAOrder.t.sol +++ b/packages/contracts/tests/DCAOrder.t.sol @@ -1,16 +1,16 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.20; +pragma solidity ^0.8.28; -import "forge-std/Test.sol"; +import 'forge-std/Test.sol'; -import {GasMeter} from "./helper/GasMeter.sol"; -import {ERC20Mintable} from "./common/ERC20Mintable.sol"; -import {MockSettlement} from "./common/MockSettlement.sol"; -import {SafeMath} from "oz/utils/math/SafeMath.sol"; +import {GasMeter} from './helper/GasMeter.sol'; +import {ERC20Mintable} from './common/ERC20Mintable.sol'; +import {MockSettlement} from './common/MockSettlement.sol'; +import {SafeMath} from 'oz/utils/math/SafeMath.sol'; -import {GPv2Order} from "../src/libraries/GPv2Order.sol"; -import {DCAOrder, NotOwner, NotWithinStartAndEndTimes} from "../src/DCAOrder.sol"; -import {IConditionalOrder} from "../src/interfaces/IConditionalOrder.sol"; +import {GPv2Order} from '../src/libraries/GPv2Order.sol'; +import {DCAOrder, NotOwner, NotWithinStartAndEndTimes} from '../src/DCAOrder.sol'; +import {IConditionalOrder} from '../src/interfaces/IConditionalOrder.sol'; contract DCAOrderTest is Test, GasMeter { using GPv2Order for GPv2Order.Data; @@ -31,7 +31,17 @@ contract DCAOrderTest is Test, GasMeter { event ConditionalOrderCreated(address indexed); // @todo: import from DCAOrder - event Initialized(address indexed order); + event Initialized( + address indexed order, + address owner, + address receiver, + address sellToken, + address buyToken, + uint256 amount, + uint256 startTime, + uint256 endTime, + uint256 interval + ); event Cancelled(address indexed order); function setUp() public { @@ -55,10 +65,28 @@ contract DCAOrderTest is Test, GasMeter { emit ConditionalOrderCreated(address(dcaOrder)); vm.expectEmit(true, true, false, true, address(dcaOrder)); - emit Initialized(address(dcaOrder)); + emit Initialized( + address(dcaOrder), // order + _owner, // owner + _receiver, // receiver + _sellToken, // sellToken + _buyToken, // buyToken + _amount, // amount + _startTime, // startTime + _endTime, // endTime + _interval // interval + ); dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, _amount, _startTime, _endTime, _interval, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + _amount, + _startTime, + _endTime, + _interval, + address(mockSettlement) ); // Assert all properties are set correctly @@ -75,27 +103,51 @@ contract DCAOrderTest is Test, GasMeter { function testInitialize_AlreadyInitialized() public { dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, _amount, _startTime, _endTime, _interval, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + _amount, + _startTime, + _endTime, + _interval, + address(mockSettlement) ); - vm.expectRevert(bytes4(keccak256("AlreadyInitialized()"))); + vm.expectRevert(bytes4(keccak256('AlreadyInitialized()'))); // Try to initialize again dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, _amount, _startTime, _endTime, _interval, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + _amount, + _startTime, + _endTime, + _interval, + address(mockSettlement) ); } function testInitialize_MissingOwner() public { - vm.expectRevert(bytes4(keccak256("MissingOwner()"))); + vm.expectRevert(bytes4(keccak256('MissingOwner()'))); dcaOrder.initialize( - address(0), _receiver, _sellToken, _buyToken, _amount, _startTime, _endTime, _interval, address(mockSettlement) + address(0), + _receiver, + _sellToken, + _buyToken, + _amount, + _startTime, + _endTime, + _interval, + address(mockSettlement) ); } function testInitialize_ReceiverIsOrder() public { - vm.expectRevert(bytes4(keccak256("ReceiverIsOrder()"))); + vm.expectRevert(bytes4(keccak256('ReceiverIsOrder()'))); dcaOrder.initialize( _owner, @@ -111,32 +163,64 @@ contract DCAOrderTest is Test, GasMeter { } function testInitialize_IntervalMustBeGreaterThanZero() public { - vm.expectRevert(bytes4(keccak256("IntervalMustBeGreaterThanZero()"))); + vm.expectRevert(bytes4(keccak256('IntervalMustBeGreaterThanZero()'))); dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, _amount, _startTime, _endTime, 0, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + _amount, + _startTime, + _endTime, + 0, + address(mockSettlement) ); } function testInitialize_InvalidStartTime() public { - vm.expectRevert(bytes4(keccak256("InvalidStartTime()"))); + vm.expectRevert(bytes4(keccak256('InvalidStartTime()'))); dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, _amount, block.timestamp, _endTime, _interval, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + _amount, + block.timestamp, + _endTime, + _interval, + address(mockSettlement) ); } function testInitialize_InvalidEndTime() public { - vm.expectRevert(bytes4(keccak256("InvalidEndTime()"))); + vm.expectRevert(bytes4(keccak256('InvalidEndTime()'))); dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, _amount, _startTime, block.timestamp, _interval, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + _amount, + _startTime, + block.timestamp, + _interval, + address(mockSettlement) ); } function testSlots() public { dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, _amount, _startTime, _endTime, _interval, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + _amount, + _startTime, + _endTime, + _interval, + address(mockSettlement) ); uint256[] memory slots = dcaOrder.orderSlots(); @@ -158,7 +242,15 @@ contract DCAOrderTest is Test, GasMeter { address newCleanOwner = address(0x10); dcaOrder.initialize( - newCleanOwner, _receiver, _sellToken, _buyToken, _amount, _startTime, _endTime, _interval, address(mockSettlement) + newCleanOwner, + _receiver, + _sellToken, + _buyToken, + _amount, + _startTime, + _endTime, + _interval, + address(mockSettlement) ); assertEq(sellToken.balanceOf(address(dcaOrder)), _amount); assertEq(sellToken.balanceOf(newCleanOwner), 0); @@ -175,7 +267,15 @@ contract DCAOrderTest is Test, GasMeter { function testCannotCancelOrderIfNotOwner() public { dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, _amount, _startTime, _endTime, _interval, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + _amount, + _startTime, + _endTime, + _interval, + address(mockSettlement) ); vm.prank(address(0x1)); vm.expectRevert(NotOwner.selector); @@ -185,7 +285,15 @@ contract DCAOrderTest is Test, GasMeter { /// @dev add fuzzing to test the current slot function testCurrentSlot() public { dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, _amount, _startTime, _endTime, _interval, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + _amount, + _startTime, + _endTime, + _interval, + address(mockSettlement) ); vm.prank(address(0x1)); @@ -224,7 +332,15 @@ contract DCAOrderTest is Test, GasMeter { uint256 _testAmount = 30 ether; dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, _testAmount, _startTime, _endTime, _interval, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + _testAmount, + _startTime, + _endTime, + _interval, + address(mockSettlement) ); vm.prank(address(0x1)); @@ -262,7 +378,15 @@ contract DCAOrderTest is Test, GasMeter { uint256 _testAmount = 30 ether; dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, _testAmount, _startTime, _endTime, _interval, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + _testAmount, + _startTime, + _endTime, + _interval, + address(mockSettlement) ); // warp to the startTime of the order @@ -280,7 +404,15 @@ contract DCAOrderTest is Test, GasMeter { uint256 endTime = _startTime + (1 days * 365 * 2); uint256 interval = 1; dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, 30 ether, _startTime, endTime, interval, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + 30 ether, + _startTime, + endTime, + interval, + address(mockSettlement) ); // warp to the startTime of the order @@ -297,13 +429,21 @@ contract DCAOrderTest is Test, GasMeter { uint256 _testPrincipal = 30 ether; dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, _testPrincipal, _startTime, _endTime, _interval, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + _testPrincipal, + _startTime, + _endTime, + _interval, + address(mockSettlement) ); // Cancel the order dcaOrder.cancel(); - vm.expectRevert(bytes4(keccak256("OrderCancelled()"))); + vm.expectRevert(bytes4(keccak256('OrderCancelled()'))); dcaOrder.getTradeableOrder(); } @@ -311,11 +451,19 @@ contract DCAOrderTest is Test, GasMeter { uint256 _testPrincipal = 0 ether; dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, _testPrincipal, _startTime, _endTime, _interval, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + _testPrincipal, + _startTime, + _endTime, + _interval, + address(mockSettlement) ); vm.warp(dcaOrder.startTime()); - vm.expectRevert(bytes4(keccak256("ZeroSellAmount()"))); + vm.expectRevert(bytes4(keccak256('ZeroSellAmount()'))); dcaOrder.getTradeableOrder(); } @@ -323,7 +471,15 @@ contract DCAOrderTest is Test, GasMeter { _endTime = _startTime + 6 weeks; dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, _amount, _startTime, _endTime, _interval, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + _amount, + _startTime, + _endTime, + _interval, + address(mockSettlement) ); // In a 6 week period, there should be 6 * 7 * 24 = 1008 slots uint256[] memory slots = dcaOrder.orderSlots(); @@ -373,7 +529,15 @@ contract DCAOrderTest is Test, GasMeter { sellToken.transfer(address(dcaOrder), _testAmount); dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, _testAmount, _startTime, _endTime, _interval, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + _testAmount, + _startTime, + _endTime, + _interval, + address(mockSettlement) ); uint256 orderSlotsLength = dcaOrder.orderSlots().length; @@ -398,7 +562,15 @@ contract DCAOrderTest is Test, GasMeter { function testisValidSignature() public { dcaOrder.initialize( - _owner, _receiver, _sellToken, _buyToken, _amount, _startTime, _endTime, _interval, address(mockSettlement) + _owner, + _receiver, + _sellToken, + _buyToken, + _amount, + _startTime, + _endTime, + _interval, + address(mockSettlement) ); // Advances block.timestamp by n seconds diff --git a/packages/contracts/tests/OrderFactory.t.sol b/packages/contracts/tests/OrderFactory.t.sol index ed783dd..1239d06 100644 --- a/packages/contracts/tests/OrderFactory.t.sol +++ b/packages/contracts/tests/OrderFactory.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.20; +pragma solidity 0.8.28; import 'forge-std/Test.sol'; import {ERC20Mintable} from './common/ERC20Mintable.sol'; @@ -97,33 +97,6 @@ contract OrderFactoryTest is Test { assertEq(sellToken.balanceOf(address(factory)), (_amount * _fee) / HUNDRED_PERCENT); } - function testCreateOrderWithNonceFailWhitelist() public { - // burn the whitelist NFT - whitelistNFT.burn(1); - - assertEq(factory.whitelist(), true); - - // Approve the factory to spend the sell token - sellToken.approve(address(factory), type(uint256).max); - - vm.expectRevert(NotWhitelisted.selector); - - // Create the vault - factory.createOrderWithNonce( - address(mastercopy), - address(1335), - _receiver, - _sellToken, - _buyToken, - _amount, - _startTime, - _endTime, - _interval, - address(mockSettlement), - 1 - ); - } - function testSetProtocolFee() public { // Update protocol fee from owner factory.setProtocolFee(10); diff --git a/packages/contracts/tests/common/ERC20Mintable.sol b/packages/contracts/tests/common/ERC20Mintable.sol index 40bd4d8..670936a 100644 --- a/packages/contracts/tests/common/ERC20Mintable.sol +++ b/packages/contracts/tests/common/ERC20Mintable.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.28; -import {ERC20} from "oz/token/ERC20/ERC20.sol"; +import {ERC20} from 'oz/token/ERC20/ERC20.sol'; contract ERC20Mintable is ERC20 { constructor(string memory name, string memory symbol) ERC20(name, symbol) {} diff --git a/packages/contracts/tests/common/ERC721Mintable.sol b/packages/contracts/tests/common/ERC721Mintable.sol index 8c9189b..20a2fbc 100644 --- a/packages/contracts/tests/common/ERC721Mintable.sol +++ b/packages/contracts/tests/common/ERC721Mintable.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.28; -import {ERC721} from "oz/token/ERC721/ERC721.sol"; +import {ERC721} from 'oz/token/ERC721/ERC721.sol'; contract ERC721Mintable is ERC721 { constructor(string memory name, string memory symbol) ERC721(name, symbol) {} diff --git a/packages/contracts/tests/common/MockSettlement.sol b/packages/contracts/tests/common/MockSettlement.sol index bd375be..c8af32d 100644 --- a/packages/contracts/tests/common/MockSettlement.sol +++ b/packages/contracts/tests/common/MockSettlement.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.28; contract MockSettlement { function vaultRelayer() public pure returns (address) { diff --git a/packages/contracts/tests/helper/GasMeter.sol b/packages/contracts/tests/helper/GasMeter.sol index 9e36c54..afe9d19 100644 --- a/packages/contracts/tests/helper/GasMeter.sol +++ b/packages/contracts/tests/helper/GasMeter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.28; contract GasMeter { uint256 private meter; @@ -9,7 +9,7 @@ contract GasMeter { if (meter != 0) { revert MeterAlreadyStarted(); } - + // -100 ~accounts for this call itself meter = gasleft() - 100; }