From 339b0ed2f0e3841d15287b8cda20fbfee0729e44 Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Tue, 19 Nov 2024 17:58:23 +0400 Subject: [PATCH 01/11] (wip) upgrade to new fee flow --- contracts/FeeBank.sol | 133 ----- contracts/FeeBankCharger.sol | 76 --- contracts/Settlement.sol | 4 +- contracts/SimpleSettlement.sol | 153 +++++- contracts/extensions/BaseExtension.sol | 194 -------- contracts/extensions/ExtensionLib.sol | 50 -- .../extensions/IntegratorFeeExtension.sol | 111 ----- .../ResolverValidationExtension.sol | 119 ----- contracts/mocks/GasBumpChecker.sol | 7 +- contracts/mocks/SettlementMock.sol | 16 - package.json | 2 +- yarn.lock | 466 +----------------- 12 files changed, 162 insertions(+), 1169 deletions(-) delete mode 100644 contracts/FeeBank.sol delete mode 100644 contracts/FeeBankCharger.sol delete mode 100644 contracts/extensions/BaseExtension.sol delete mode 100644 contracts/extensions/ExtensionLib.sol delete mode 100644 contracts/extensions/IntegratorFeeExtension.sol delete mode 100644 contracts/extensions/ResolverValidationExtension.sol delete mode 100644 contracts/mocks/SettlementMock.sol diff --git a/contracts/FeeBank.sol b/contracts/FeeBank.sol deleted file mode 100644 index 2617398..0000000 --- a/contracts/FeeBank.sol +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.23; - -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { SafeERC20 } from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol"; -import { UniERC20 } from "@1inch/solidity-utils/contracts/libraries/UniERC20.sol"; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; -import { IFeeBankCharger } from "./interfaces/IFeeBankCharger.sol"; -import { IFeeBank } from "./interfaces/IFeeBank.sol"; - -/** - * @title FeeBank - * @notice FeeBank contract introduces a credit system for paying fees. - * A user can deposit tokens to the FeeBank contract, obtain credits and then use them to pay fees. - * @dev FeeBank is coupled with FeeBankCharger to actually charge fees. - */ -contract FeeBank is IFeeBank, Ownable { - using SafeERC20 for IERC20; - using UniERC20 for IERC20; - - error ZeroAddress(); - - IERC20 private immutable _FEE_TOKEN; - IFeeBankCharger private immutable _CHARGER; - - mapping(address account => uint256 availableCredit) private _accountDeposits; - - constructor(IFeeBankCharger charger, IERC20 feeToken, address owner) Ownable(owner) { - if (address(feeToken) == address(0)) revert ZeroAddress(); - _CHARGER = charger; - _FEE_TOKEN = feeToken; - } - - /** - * @notice See {IFeeBank-availableCredit}. - */ - function availableCredit(address account) external view returns (uint256) { - return _CHARGER.availableCredit(account); - } - - /** - * @notice See {IFeeBank-deposit}. - */ - function deposit(uint256 amount) external returns (uint256) { - return _depositFor(msg.sender, amount); - } - - /** - * @notice See {IFeeBank-depositFor}. - */ - function depositFor(address account, uint256 amount) external returns (uint256) { - return _depositFor(account, amount); - } - - /** - * @notice See {IFeeBank-depositWithPermit}. - */ - function depositWithPermit(uint256 amount, bytes calldata permit) external returns (uint256) { - return depositForWithPermit(msg.sender, amount, permit); - } - - /** - * @notice See {IFeeBank-depositForWithPermit}. - */ - function depositForWithPermit( - address account, - uint256 amount, - bytes calldata permit - ) public returns (uint256) { - _FEE_TOKEN.safePermit(permit); - return _depositFor(account, amount); - } - - /** - * @notice See {IFeeBank-withdraw}. - */ - function withdraw(uint256 amount) external returns (uint256) { - return _withdrawTo(msg.sender, amount); - } - - /** - * @notice See {IFeeBank-withdrawTo}. - */ - function withdrawTo(address account, uint256 amount) external returns (uint256) { - return _withdrawTo(account, amount); - } - - /** - * @notice Admin method returns commissions spent by users. - * @param accounts Accounts whose commissions are being withdrawn. - * @return totalAccountFees The total amount of accounts commissions. - */ - function gatherFees(address[] calldata accounts) external onlyOwner returns (uint256 totalAccountFees) { - uint256 accountsLength = accounts.length; - unchecked { - for (uint256 i = 0; i < accountsLength; ++i) { - address account = accounts[i]; - uint256 accountDeposit = _accountDeposits[account]; - uint256 availableCredit_ = _CHARGER.availableCredit(account); - _accountDeposits[account] = availableCredit_; - totalAccountFees += accountDeposit - availableCredit_; // overflow is impossible due to checks in FeeBankCharger - } - } - _FEE_TOKEN.safeTransfer(msg.sender, totalAccountFees); - } - - function _depositFor(address account, uint256 amount) internal returns (uint256 totalAvailableCredit) { - if (account == address(0)) revert ZeroAddress(); - _FEE_TOKEN.safeTransferFrom(msg.sender, address(this), amount); - unchecked { - _accountDeposits[account] += amount; // overflow is impossible due to limited _FEE_TOKEN supply - } - totalAvailableCredit = _CHARGER.increaseAvailableCredit(account, amount); - } - - function _withdrawTo(address account, uint256 amount) internal returns (uint256 totalAvailableCredit) { - totalAvailableCredit = _CHARGER.decreaseAvailableCredit(msg.sender, amount); - unchecked { - _accountDeposits[msg.sender] -= amount; // underflow is impossible due to checks in FeeBankCharger - } - _FEE_TOKEN.safeTransfer(account, amount); - } - - /** - * @notice Retrieves funds accidently sent directly to the contract address - * @param token ERC20 token to retrieve - * @param amount amount to retrieve - */ - function rescueFunds(IERC20 token, uint256 amount) external onlyOwner { - token.uniTransfer(payable(msg.sender), amount); - } -} diff --git a/contracts/FeeBankCharger.sol b/contracts/FeeBankCharger.sol deleted file mode 100644 index f849fe2..0000000 --- a/contracts/FeeBankCharger.sol +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.23; - -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IFeeBank } from "./interfaces/IFeeBank.sol"; -import { IFeeBankCharger } from "./interfaces/IFeeBankCharger.sol"; -import { FeeBank } from "./FeeBank.sol"; - -/** - * @title FeeBankCharger - * @notice FeeBankCharger contract implements logic to increase or decrease users' credits in FeeBank. - */ -contract FeeBankCharger is IFeeBankCharger { - error OnlyFeeBankAccess(); - error NotEnoughCredit(); - - /** - * @notice See {IFeeBankCharger-feeBank}. - */ - IFeeBank public immutable FEE_BANK; - mapping(address => uint256) private _creditAllowance; - - /** - * @dev Modifier to check if the sender is a FEE_BANK contract. - */ - modifier onlyFeeBank() { - if (msg.sender != address(FEE_BANK)) revert OnlyFeeBankAccess(); - _; - } - - constructor(IERC20 feeToken, address owner) { - FEE_BANK = new FeeBank(this, feeToken, owner); - } - - /** - * @notice See {IFeeBankCharger-availableCredit}. - */ - function availableCredit(address account) external view returns (uint256) { - return _creditAllowance[account]; - } - - /** - * @notice See {IFeeBankCharger-increaseAvailableCredit}. - */ - function increaseAvailableCredit(address account, uint256 amount) external onlyFeeBank returns (uint256 allowance) { - allowance = _creditAllowance[account]; - unchecked { - allowance += amount; // overflow is impossible due to limited _token supply - } - _creditAllowance[account] = allowance; - } - - /** - * @notice See {IFeeBankCharger-decreaseAvailableCredit}. - */ - function decreaseAvailableCredit(address account, uint256 amount) external onlyFeeBank returns (uint256 allowance) { - return _creditAllowance[account] -= amount; // checked math is needed to prevent underflow - } - - /** - * @notice Internal function that charges a specified fee from a given account's credit allowance. - * @dev Reverts with 'NotEnoughCredit' if the account's credit allowance is insufficient to cover the fee. - * @param account The address of the account from which the fee is being charged. - * @param fee The amount of fee to be charged from the account. - */ - function _chargeFee(address account, uint256 fee) internal virtual { - if (fee > 0) { - uint256 currentAllowance = _creditAllowance[account]; - if (currentAllowance < fee) revert NotEnoughCredit(); - unchecked { - _creditAllowance[account] = currentAllowance - fee; - } - } - } -} diff --git a/contracts/Settlement.sol b/contracts/Settlement.sol index 21b5eaf..ab8c1f0 100644 --- a/contracts/Settlement.sol +++ b/contracts/Settlement.sol @@ -14,8 +14,8 @@ import { SimpleSettlement } from "./SimpleSettlement.sol"; contract Settlement is SimpleSettlement { error InvalidPriorityFee(); - constructor(address limitOrderProtocol, IERC20 feeToken, IERC20 accessToken, address weth, address owner) - SimpleSettlement(limitOrderProtocol, feeToken, accessToken, weth, owner) + constructor(address limitOrderProtocol, IERC20 accessToken, address weth, address owner) + SimpleSettlement(limitOrderProtocol, accessToken, weth, owner) {} function _postInteraction( diff --git a/contracts/SimpleSettlement.sol b/contracts/SimpleSettlement.sol index 8ea0bb3..882ead0 100644 --- a/contracts/SimpleSettlement.sol +++ b/contracts/SimpleSettlement.sol @@ -3,42 +3,165 @@ pragma solidity 0.8.23; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol"; -import { BaseExtension } from "./extensions/BaseExtension.sol"; -import { IntegratorFeeExtension } from "./extensions/IntegratorFeeExtension.sol"; -import { ResolverValidationExtension } from "./extensions/ResolverValidationExtension.sol"; +import { FeeTaker } from "@1inch/limit-order-protocol-contract/contracts/extensions/FeeTaker.sol"; /** * @title Simple Settlement contract * @notice Contract to execute limit orders settlement, created by Fusion mode. */ -contract SimpleSettlement is ResolverValidationExtension, IntegratorFeeExtension { +contract SimpleSettlement is FeeTaker { + uint256 private constant _BASE_POINTS = 10_000_000; // 100% + uint256 private constant _GAS_PRICE_BASE = 1_000_000; // 1000 means 1 Gwei + + error AllowedTimeViolation(); + /** * @notice Initializes the contract. * @param limitOrderProtocol The limit order protocol contract. - * @param feeToken The token to charge protocol fees in. * @param accessToken Contract address whose tokens allow filling limit orders with a fee for resolvers that are outside the whitelist. * @param weth The WETH address. * @param owner The owner of the contract. */ - constructor(address limitOrderProtocol, IERC20 feeToken, IERC20 accessToken, address weth, address owner) - BaseExtension(limitOrderProtocol) - ResolverValidationExtension(feeToken, accessToken, owner) - IntegratorFeeExtension(weth) - Ownable(owner) + constructor(address limitOrderProtocol, IERC20 accessToken, address weth, address owner) + FeeTaker(limitOrderProtocol, accessToken, weth, owner) {} - function _postInteraction( + /** + * @dev Adds dutch auction capabilities to the getter + */ + function _getMakingAmount( IOrderMixin.Order calldata order, bytes calldata extension, bytes32 orderHash, address taker, - uint256 makingAmount, uint256 takingAmount, uint256 remainingMakingAmount, bytes calldata extraData - ) internal virtual override(ResolverValidationExtension, IntegratorFeeExtension) { - super._postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData); + ) internal view override returns (uint256) { + uint256 rateBump = _getRateBump(extraData); + return Math.mulDiv( + super._getMakingAmount(order, extension, orderHash, taker, takingAmount, remainingMakingAmount, extraData), + takingAmount * _BASE_POINTS, + order.takingAmount * (_BASE_POINTS + rateBump) + ); + } + + /** + * @dev Adds dutch auction capabilities to the getter + */ + function _getTakingAmount( + IOrderMixin.Order calldata order, + bytes calldata extension, + bytes32 orderHash, + address taker, + uint256 makingAmount, + uint256 remainingMakingAmount, + bytes calldata extraData + ) internal view override returns (uint256) { + uint256 rateBump = _getRateBump(extraData); + return Math.mulDiv( + super._getTakingAmount(order, extension, orderHash, taker, makingAmount, remainingMakingAmount, extraData), + makingAmount * (_BASE_POINTS + rateBump), + order.makingAmount * _BASE_POINTS, + Math.Rounding.Ceil + ); + } + + /** + * @dev Parses auction rate bump data from the `auctionDetails` field. + * `gasBumpEstimate` and `gasPriceEstimate` are used to estimate the transaction costs + * which are then offset from the auction rate bump. + * @param auctionDetails AuctionDetails is a tightly packed struct of the following format: + * ``` + * struct AuctionDetails { + * bytes3 gasBumpEstimate; + * bytes4 gasPriceEstimate; + * bytes4 auctionStartTime; + * bytes3 auctionDuration; + * bytes3 initialRateBump; + * (bytes3,bytes2)[N] pointsAndTimeDeltas; + * } + * ``` + * @return rateBump The rate bump. + */ + function _getRateBump(bytes calldata auctionDetails) private view returns (uint256) { + unchecked { + uint256 gasBumpEstimate = uint24(bytes3(auctionDetails[0:3])); + uint256 gasPriceEstimate = uint32(bytes4(auctionDetails[3:7])); + uint256 gasBump = gasBumpEstimate == 0 || gasPriceEstimate == 0 ? 0 : gasBumpEstimate * block.basefee / gasPriceEstimate / _GAS_PRICE_BASE; + uint256 auctionStartTime = uint32(bytes4(auctionDetails[7:11])); + uint256 auctionFinishTime = auctionStartTime + uint24(bytes3(auctionDetails[11:14])); + uint256 initialRateBump = uint24(bytes3(auctionDetails[14:17])); + uint256 auctionBump = _getAuctionBump(auctionStartTime, auctionFinishTime, initialRateBump, auctionDetails[17:]); + return auctionBump > gasBump ? auctionBump - gasBump : 0; + } + } + + /** + * @dev Calculates auction price bump. Auction is represented as a piecewise linear function with `N` points. + * Each point is represented as a pair of `(rateBump, timeDelta)`, where `rateBump` is the + * rate bump in basis points and `timeDelta` is the time delta in seconds. + * The rate bump is interpolated linearly between the points. + * The last point is assumed to be `(0, auctionDuration)`. + * @param auctionStartTime The time when the auction starts. + * @param auctionFinishTime The time when the auction finishes. + * @param initialRateBump The initial rate bump. + * @param pointsAndTimeDeltas The points and time deltas structure. + * @return The rate bump at the current time. + */ + function _getAuctionBump(uint256 auctionStartTime, uint256 auctionFinishTime, uint256 initialRateBump, bytes calldata pointsAndTimeDeltas) private view returns (uint256) { + unchecked { + if (block.timestamp <= auctionStartTime) { + return initialRateBump; + } else if (block.timestamp >= auctionFinishTime) { + return 0; + } + + uint256 currentPointTime = auctionStartTime; + uint256 currentRateBump = initialRateBump; + + while (pointsAndTimeDeltas.length > 0) { + uint256 nextRateBump = uint24(bytes3(pointsAndTimeDeltas[:3])); + uint256 nextPointTime = currentPointTime + uint16(bytes2(pointsAndTimeDeltas[3:5])); + if (block.timestamp <= nextPointTime) { + return ((block.timestamp - currentPointTime) * nextRateBump + (nextPointTime - block.timestamp) * currentRateBump) / (nextPointTime - currentPointTime); + } + currentRateBump = nextRateBump; + currentPointTime = nextPointTime; + pointsAndTimeDeltas = pointsAndTimeDeltas[5:]; + } + return (auctionFinishTime - block.timestamp) * currentRateBump / (auctionFinishTime - currentPointTime); + } + } + + /** + * @notice See {IPostInteraction-postInteraction}. + * @dev Takes the fee in taking tokens and transfers the rest to the maker. + * `whitelistData` consists of: + * 4 bytes - allowed time + * 1 byte - whitelist size + * (bytes12)[N] — taker whitelist + */ + function _isWhitelistedPostInteractionImpl(bytes calldata whitelistData, address taker) internal view override returns (bool isWhitelisted, bytes calldata tail) { + unchecked { + uint80 maskedTakerAddress = uint80(uint160(taker)); + uint256 allowedTime = uint32(bytes4(whitelistData)); + uint256 size = uint8(whitelistData[4]); + bytes calldata whitelist = whitelistData[5:5 + 12 * size]; + tail = whitelistData[5 + 12 * size:]; + + for (uint256 i = 0; i < size; i++) { + uint80 whitelistedAddress = uint80(bytes10(whitelist)); + if (block.timestamp < allowedTime) { + revert AllowedTimeViolation(); + } else if (maskedTakerAddress == whitelistedAddress) { + return (true, tail); + } + allowedTime += uint16(bytes2(whitelist[10:])); // add next time delta + whitelist = whitelist[12:]; + } + } } } diff --git a/contracts/extensions/BaseExtension.sol b/contracts/extensions/BaseExtension.sol deleted file mode 100644 index 9918dbf..0000000 --- a/contracts/extensions/BaseExtension.sol +++ /dev/null @@ -1,194 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.23; - -import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; -import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol"; -import { IPostInteraction } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IPostInteraction.sol"; -import { IPreInteraction } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IPreInteraction.sol"; -import { IAmountGetter } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IAmountGetter.sol"; - -/** - * @title Base Extension contract - * @notice Contract to define the basic functionality for the limit orders settlement. - */ -contract BaseExtension is IPreInteraction, IPostInteraction, IAmountGetter { - error OnlyLimitOrderProtocol(); - - uint256 private constant _BASE_POINTS = 10_000_000; // 100% - uint256 private constant _GAS_PRICE_BASE = 1_000_000; // 1000 means 1 Gwei - - address private immutable _LIMIT_ORDER_PROTOCOL; - - /// @dev Modifier to check if the caller is the limit order protocol contract. - modifier onlyLimitOrderProtocol { - if (msg.sender != _LIMIT_ORDER_PROTOCOL) revert OnlyLimitOrderProtocol(); - _; - } - - /** - * @notice Initializes the contract. - * @param limitOrderProtocol The limit order protocol contract. - */ - constructor(address limitOrderProtocol) { - _LIMIT_ORDER_PROTOCOL = limitOrderProtocol; - } - - /** - * See {IAmountGetter-getMakingAmount} - */ - function getMakingAmount( - IOrderMixin.Order calldata order, - bytes calldata /* extension */, - bytes32 /* orderHash */, - address /* taker */, - uint256 takingAmount, - uint256 /* remainingMakingAmount */, - bytes calldata extraData - ) external view returns (uint256) { - uint256 rateBump = _getRateBump(extraData); - return Math.mulDiv(order.makingAmount, takingAmount * _BASE_POINTS, order.takingAmount * (_BASE_POINTS + rateBump)); - } - - /** - * See {IAmountGetter-getTakingAmount} - */ - function getTakingAmount( - IOrderMixin.Order calldata order, - bytes calldata /* extension */, - bytes32 /* orderHash */, - address /* taker */, - uint256 makingAmount, - uint256 /* remainingMakingAmount */, - bytes calldata extraData - ) external view returns (uint256) { - uint256 rateBump = _getRateBump(extraData); - return Math.mulDiv(order.takingAmount, makingAmount * (_BASE_POINTS + rateBump), order.makingAmount * _BASE_POINTS, Math.Rounding.Ceil); - } - - /** - * See {IPreInteraction-preInteraction} - */ - function preInteraction( - IOrderMixin.Order calldata order, - bytes calldata extension, - bytes32 orderHash, - address taker, - uint256 makingAmount, - uint256 takingAmount, - uint256 remainingMakingAmount, - bytes calldata extraData - ) external onlyLimitOrderProtocol { - _preInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData); - } - - /** - * See {IPostInteraction-postInteraction} - */ - function postInteraction( - IOrderMixin.Order calldata order, - bytes calldata extension, - bytes32 orderHash, - address taker, - uint256 makingAmount, - uint256 takingAmount, - uint256 remainingMakingAmount, - bytes calldata extraData - ) external onlyLimitOrderProtocol { - _postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData); - } - - function _preInteraction( - IOrderMixin.Order calldata order, - bytes calldata extension, - bytes32 orderHash, - address taker, - uint256 makingAmount, - uint256 takingAmount, - uint256 remainingMakingAmount, - bytes calldata extraData - ) internal virtual {} - - function _postInteraction( - IOrderMixin.Order calldata order, - bytes calldata extension, - bytes32 orderHash, - address taker, - uint256 makingAmount, - uint256 takingAmount, - uint256 remainingMakingAmount, - bytes calldata extraData - ) internal virtual { - // Allows to add custom postInteractions - if (extraData.length > 20) { - IPostInteraction(address(bytes20(extraData))).postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData[20 : extraData.length - 1]); - } - } - - /** - * @dev Parses auction rate bump data from the `auctionDetails` field. - * `gasBumpEstimate` and `gasPriceEstimate` are used to estimate the transaction costs - * which are then offset from the auction rate bump. - * @param auctionDetails AuctionDetails is a tightly packed struct of the following format: - * ``` - * struct AuctionDetails { - * bytes3 gasBumpEstimate; - * bytes4 gasPriceEstimate; - * bytes4 auctionStartTime; - * bytes3 auctionDuration; - * bytes3 initialRateBump; - * (bytes3,bytes2)[N] pointsAndTimeDeltas; - * } - * ``` - * @return rateBump The rate bump. - */ - function _getRateBump(bytes calldata auctionDetails) private view returns (uint256) { - unchecked { - uint256 gasBumpEstimate = uint24(bytes3(auctionDetails[0:3])); - uint256 gasPriceEstimate = uint32(bytes4(auctionDetails[3:7])); - uint256 gasBump = gasBumpEstimate == 0 || gasPriceEstimate == 0 ? 0 : gasBumpEstimate * block.basefee / gasPriceEstimate / _GAS_PRICE_BASE; - uint256 auctionStartTime = uint32(bytes4(auctionDetails[7:11])); - uint256 auctionFinishTime = auctionStartTime + uint24(bytes3(auctionDetails[11:14])); - uint256 initialRateBump = uint24(bytes3(auctionDetails[14:17])); - uint256 auctionBump = _getAuctionBump(auctionStartTime, auctionFinishTime, initialRateBump, auctionDetails[17:]); - return auctionBump > gasBump ? auctionBump - gasBump : 0; - } - } - - /** - * @dev Calculates auction price bump. Auction is represented as a piecewise linear function with `N` points. - * Each point is represented as a pair of `(rateBump, timeDelta)`, where `rateBump` is the - * rate bump in basis points and `timeDelta` is the time delta in seconds. - * The rate bump is interpolated linearly between the points. - * The last point is assumed to be `(0, auctionDuration)`. - * @param auctionStartTime The time when the auction starts. - * @param auctionFinishTime The time when the auction finishes. - * @param initialRateBump The initial rate bump. - * @param pointsAndTimeDeltas The points and time deltas structure. - * @return The rate bump at the current time. - */ - function _getAuctionBump(uint256 auctionStartTime, uint256 auctionFinishTime, uint256 initialRateBump, bytes calldata pointsAndTimeDeltas) private view returns (uint256) { - unchecked { - if (block.timestamp <= auctionStartTime) { - return initialRateBump; - } else if (block.timestamp >= auctionFinishTime) { - return 0; - } - - uint256 currentPointTime = auctionStartTime; - uint256 currentRateBump = initialRateBump; - - while (pointsAndTimeDeltas.length > 0) { - uint256 nextRateBump = uint24(bytes3(pointsAndTimeDeltas[:3])); - uint256 nextPointTime = currentPointTime + uint16(bytes2(pointsAndTimeDeltas[3:5])); - if (block.timestamp <= nextPointTime) { - return ((block.timestamp - currentPointTime) * nextRateBump + (nextPointTime - block.timestamp) * currentRateBump) / (nextPointTime - currentPointTime); - } - currentRateBump = nextRateBump; - currentPointTime = nextPointTime; - pointsAndTimeDeltas = pointsAndTimeDeltas[5:]; - } - return (auctionFinishTime - block.timestamp) * currentRateBump / (auctionFinishTime - currentPointTime); - } - } -} diff --git a/contracts/extensions/ExtensionLib.sol b/contracts/extensions/ExtensionLib.sol deleted file mode 100644 index a804de9..0000000 --- a/contracts/extensions/ExtensionLib.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -/** - * @title Extension Library - * @notice Library to retrieve data from the bitmap. - */ -library ExtensionLib { - bytes1 private constant _RESOLVER_FEE_FLAG = 0x01; - bytes1 private constant _INTEGRATOR_FEE_FLAG = 0x02; - bytes1 private constant _CUSTOM_RECEIVER_FLAG = 0x04; - uint256 private constant _WHITELIST_SHIFT = 3; - - /** - * @notice Checks if the resolver fee is enabled - * @param extraData Data to be processed in the extension - * @return True if the resolver fee is enabled - */ - function resolverFeeEnabled(bytes calldata extraData) internal pure returns (bool) { - return extraData[extraData.length - 1] & _RESOLVER_FEE_FLAG == _RESOLVER_FEE_FLAG; - } - - /** - * @notice Checks if the integrator fee is enabled - * @param extraData Data to be processed in the extension - * @return True if the integrator fee is enabled - */ - function integratorFeeEnabled(bytes calldata extraData) internal pure returns (bool) { - return extraData[extraData.length - 1] & _INTEGRATOR_FEE_FLAG == _INTEGRATOR_FEE_FLAG; - } - - /** - * @notice Checks if the custom receiver is enabled - * @param extraData Data to be processed in the extension - * @return True if the custom receiver is specified - */ - function hasCustomReceiver(bytes calldata extraData) internal pure returns (bool) { - return extraData[extraData.length - 1] & _CUSTOM_RECEIVER_FLAG == _CUSTOM_RECEIVER_FLAG; - } - - /** - * @notice Gets the number of resolvers in the whitelist - * @param extraData Data to be processed in the extension - * @return The number of resolvers in the whitelist - */ - function resolversCount(bytes calldata extraData) internal pure returns (uint256) { - return uint8(extraData[extraData.length - 1]) >> _WHITELIST_SHIFT; - } -} diff --git a/contracts/extensions/IntegratorFeeExtension.sol b/contracts/extensions/IntegratorFeeExtension.sol deleted file mode 100644 index 4a4a816..0000000 --- a/contracts/extensions/IntegratorFeeExtension.sol +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.23; - -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; -import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol"; -import { MakerTraits, MakerTraitsLib } from "@1inch/limit-order-protocol-contract/contracts/libraries/MakerTraitsLib.sol"; -import { SafeERC20 } from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol"; -import { Address, AddressLib } from "@1inch/solidity-utils/contracts/libraries/AddressLib.sol"; -import { UniERC20 } from "@1inch/solidity-utils/contracts/libraries/UniERC20.sol"; -import { BaseExtension } from "./BaseExtension.sol"; -import { ExtensionLib } from "./ExtensionLib.sol"; - -/** - * @title Integrator Fee Extension - * @notice Abstract contract designed to integrate fee processing within the post-interaction phase of order execution. - */ -abstract contract IntegratorFeeExtension is BaseExtension, Ownable { - using SafeERC20 for IERC20; - using AddressLib for Address; - using ExtensionLib for bytes; - using MakerTraitsLib for MakerTraits; - using UniERC20 for IERC20; - - /** - * @dev Eth transfer failed. The target fallback may have reverted. - */ - error EthTransferFailed(); - - /// @dev Allows fees in range [1e-5, 0.65535] - uint256 private constant _FEE_BASE = 1e5; - - address private immutable _WETH; - - constructor(address weth) { - _WETH = weth; - } - - /** - * @notice Fallback function to receive ETH. - */ - receive() external payable {} - - /** - * @param extraData Structured data of length n bytes, segmented as follows: - * [0:2] - Fee percentage in basis points. - * [2:22] - Integrator address. - * [22:42] - Custom receiver address. - * [42:n] - ExtraData for other extensions, not utilized by this integration fee extension. - * [n] - Bitmap indicating usage flags, where `xxxx xx1x` signifies integration fee usage. Other bits in this bitmap are not used by this extension. - */ - function _postInteraction( - IOrderMixin.Order calldata order, - bytes calldata extension, - bytes32 orderHash, - address taker, - uint256 makingAmount, - uint256 takingAmount, - uint256 remainingMakingAmount, - bytes calldata extraData - ) internal virtual override { - if (extraData.integratorFeeEnabled()) { - uint256 fee = takingAmount * uint256(uint16(bytes2(extraData))) / _FEE_BASE; - address feeRecipient = address(bytes20(extraData[2:22])); - extraData = extraData[22:]; - - address receiver = order.maker.get(); - if (extraData.hasCustomReceiver()) { - receiver = address(bytes20(extraData)); - extraData = extraData[20:]; - } - - bool isEth = order.takerAsset.get() == address(_WETH) && order.makerTraits.unwrapWeth(); - - if (isEth) { - if (fee > 0) { - _sendEth(feeRecipient, fee); - } - unchecked { - _sendEth(receiver, takingAmount - fee); - } - } else { - if (fee > 0) { - IERC20(order.takerAsset.get()).safeTransfer(feeRecipient, fee); - } - unchecked { - IERC20(order.takerAsset.get()).safeTransfer(receiver, takingAmount - fee); - } - } - } - - super._postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData); - } - - /** - * @notice Retrieves funds accidently sent directly to the contract address - * @param token ERC20 token to retrieve - * @param amount amount to retrieve - */ - function rescueFunds(IERC20 token, uint256 amount) external onlyOwner { - token.uniTransfer(payable(msg.sender), amount); - } - - function _sendEth(address target, uint256 amount) private { - (bool success, ) = target.call{value: amount}(""); - if (!success) { - revert EthTransferFailed(); - } - } -} diff --git a/contracts/extensions/ResolverValidationExtension.sol b/contracts/extensions/ResolverValidationExtension.sol deleted file mode 100644 index 8d231b8..0000000 --- a/contracts/extensions/ResolverValidationExtension.sol +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.23; - -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol"; -import { FeeBankCharger } from "../FeeBankCharger.sol"; -import { BaseExtension } from "./BaseExtension.sol"; -import { ExtensionLib } from "./ExtensionLib.sol"; - -/** - * @title Resolver Validation Extension - * @notice This abstract contract combines functionalities to enhance security and compliance in the order execution process. - * Ensures that only transactions from whitelisted resolvers or resolvers who own specific accessToken are processed within the post-interaction phase of order execution. - * Additionally, it allows charging a fee to resolvers in the `postInteraction` method, providing a mechanism for resolver fee management. - */ -abstract contract ResolverValidationExtension is BaseExtension, FeeBankCharger { - using ExtensionLib for bytes; - - error ResolverCanNotFillOrder(); - - uint256 private constant _ORDER_FEE_BASE_POINTS = 1e15; - /// @notice Contract address whose tokens allow filling limit orders with a fee for resolvers that are outside the whitelist - IERC20 private immutable _ACCESS_TOKEN; - - constructor(IERC20 feeToken, IERC20 accessToken, address owner) FeeBankCharger(feeToken, owner) { - _ACCESS_TOKEN = accessToken; - } - - /** - * @dev Validates whether the resolver is whitelisted. - * @param allowedTime The time after which interaction with the order is allowed. - * @param whitelist Whitelist is tightly packed struct of the following format: - * ``` - * (bytes10,bytes2)[N] resolversAddressesAndTimeDeltas; - * ``` - * Resolvers in the list are sorted in ascending order by the time when they are allowed to interact with the order. - * Time deltas represent the time in seconds between the adjacent resolvers. - * Only 10 lowest bytes of the resolver address are used for comparison. - * @param whitelistSize The amount of resolvers in the whitelist. - * @param resolver The resolver to check. - * @return Whether the resolver is whitelisted. - */ - function _isWhitelisted(uint256 allowedTime, bytes calldata whitelist, uint256 whitelistSize, address resolver) internal view virtual returns (bool) { - unchecked { - uint80 maskedResolverAddress = uint80(uint160(resolver)); - for (uint256 i = 0; i < whitelistSize; i++) { - uint80 whitelistedAddress = uint80(bytes10(whitelist[:10])); - allowedTime += uint16(bytes2(whitelist[10:12])); // add next time delta - if (maskedResolverAddress == whitelistedAddress) { - return allowedTime <= block.timestamp; - } else if (allowedTime > block.timestamp) { - return false; - } - whitelist = whitelist[12:]; - } - return false; - } - } - - /** - * @dev Calculates the resolver fee. - * @param fee Scaled resolver fee. - * @param orderMakingAmount Making amount from the order. - * @param actualMakingAmount Making amount that was actually filled. - * @return resolverFee Calculated resolver fee. - */ - function _getResolverFee( - uint256 fee, - uint256 orderMakingAmount, - uint256 actualMakingAmount - ) internal pure virtual returns(uint256) { - return fee * _ORDER_FEE_BASE_POINTS * actualMakingAmount / orderMakingAmount; - } - - /** - * @param extraData Structured data of length n bytes, segmented as follows: - * [0:4] - Resolver fee information. - * [4:8] - The time after which interaction with the order is allowed. - * [8:k] - Data as defined by the `whitelist` parameter for the `_isWhitelisted` method, - * where k depends on the amount of resolvers in the whitelist, as indicated by the bitmap in the last byte. - * [k:n] - ExtraData for other extensions, not utilized by this validation extension. - * [n] - Bitmap indicating various usage flags and values. - * The bitmask xxxx xxx1 signifies resolver fee usage. - * The bitmask VVVV Vxxx represents the number of resolvers in the whitelist, where the V bits denote the count of resolvers. - * The remaining bits in this bitmap are not used by this extension. - */ - function _postInteraction( - IOrderMixin.Order calldata order, - bytes calldata extension, - bytes32 orderHash, - address taker, - uint256 makingAmount, - uint256 takingAmount, - uint256 remainingMakingAmount, - bytes calldata extraData - ) internal virtual override { - bool feeEnabled = extraData.resolverFeeEnabled(); - uint256 resolversCount = extraData.resolversCount(); - unchecked { - uint256 resolverFee; - if (feeEnabled) { - resolverFee = _getResolverFee(uint256(uint32(bytes4(extraData[:4]))), order.makingAmount, makingAmount); - extraData = extraData[4:]; - } - - uint256 allowedTime = uint32(bytes4(extraData[0:4])); - extraData = extraData[4:]; - uint256 whitelistSize = resolversCount * 12; - if (!_isWhitelisted(allowedTime, extraData[:whitelistSize], resolversCount, taker)) { // resolversCount always > 0 on prod - if (allowedTime > block.timestamp || _ACCESS_TOKEN.balanceOf(taker) == 0) revert ResolverCanNotFillOrder(); - if (feeEnabled) { - _chargeFee(taker, resolverFee); - } - } - super._postInteraction(order, extension, orderHash, taker, makingAmount, takingAmount, remainingMakingAmount, extraData[whitelistSize:]); - } - } -} diff --git a/contracts/mocks/GasBumpChecker.sol b/contracts/mocks/GasBumpChecker.sol index 3507f4e..72c70b4 100644 --- a/contracts/mocks/GasBumpChecker.sol +++ b/contracts/mocks/GasBumpChecker.sol @@ -2,13 +2,14 @@ pragma solidity 0.8.23; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IOrderMixin } from "@1inch/limit-order-protocol-contract/contracts/interfaces/IOrderMixin.sol"; -import { BaseExtension } from "../extensions/BaseExtension.sol"; +import { SimpleSettlement } from "../SimpleSettlement.sol"; -contract GasBumpChecker is BaseExtension { +contract GasBumpChecker is SimpleSettlement { error InvalidResult(uint256 actual, uint256 expected); - constructor() BaseExtension(address(this)) {} + constructor(IERC20 accessToken, address weth, address owner) SimpleSettlement(address(this), accessToken, weth, owner) {} function testGetTakingAmount( IOrderMixin.Order calldata order, diff --git a/contracts/mocks/SettlementMock.sol b/contracts/mocks/SettlementMock.sol deleted file mode 100644 index fffac3b..0000000 --- a/contracts/mocks/SettlementMock.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.23; - -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { Settlement } from "../Settlement.sol"; - -contract SettlementMock is Settlement { - constructor(address limitOrderProtocol, IERC20 token, IERC20 accessToken, address weth) - Settlement(limitOrderProtocol, token, accessToken, weth, msg.sender) - {} - - function decreaseAvailableCreditMock(address account, uint256 amount) external { - _chargeFee(account, amount); - } -} diff --git a/package.json b/package.json index ea39da1..e6f63aa 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "@1inch/delegating": "1.1.0", - "@1inch/limit-order-protocol-contract": "4.0.3", + "@1inch/limit-order-protocol-contract": "https://github.com/1inch/limit-order-protocol.git#fec2379", "@1inch/solidity-utils": "6.0.0", "@1inch/st1inch": "2.2.0", "@openzeppelin/contracts": "5.1.0" diff --git a/yarn.lock b/yarn.lock index 0ca0612..b641a0c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21,14 +21,14 @@ "@1inch/token-plugins" "1.3.0" "@openzeppelin/contracts" "5.0.1" -"@1inch/limit-order-protocol-contract@4.0.3": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@1inch/limit-order-protocol-contract/-/limit-order-protocol-contract-4.0.3.tgz#0a6a82a90b2502f1665764e2ff7f8dcb735ffea5" - integrity sha512-h3ZpH0/WJF5nl3ikbwsPNuEt02mNRHXqJ9DT8yn2ImLxpuHSUglp5SOVZA8qf6PJwntgmbJ7+u08e7lVZAx1jQ== +"@1inch/limit-order-protocol-contract@https://github.com/1inch/limit-order-protocol.git#fec2379": + version "4.2.0" + resolved "https://github.com/1inch/limit-order-protocol.git#fec23799007523be80eabc2341a0c3159b479323" dependencies: - "@1inch/solidity-utils" "4.2.1" + "@1inch/solidity-utils" "6.0.0" "@chainlink/contracts" "0.8.0" - "@openzeppelin/contracts" "5.0.1" + "@gnosis.pm/safe-contracts" "1.3.0" + "@openzeppelin/contracts" "5.1.0" "@1inch/solidity-utils@3.5.5": version "3.5.5" @@ -48,26 +48,6 @@ hardhat "2.19.2" hardhat-deploy "0.11.44" -"@1inch/solidity-utils@4.2.1": - version "4.2.1" - resolved "https://registry.yarnpkg.com/@1inch/solidity-utils/-/solidity-utils-4.2.1.tgz#1feb6887ebc6ecc9ec2c1a1cde4963a2704cdc06" - integrity sha512-Xx7KXzBRAxVukNHIcki0yQkbY+dK9vZRnxSTfA/SLwH+zotMArqOohtmbRsuBxZQQ1c9XNqBOuDhufOVoyZYew== - dependencies: - "@metamask/eth-sig-util" "7.0.1" - "@nomicfoundation/hardhat-ethers" "3.0.5" - "@nomicfoundation/hardhat-network-helpers" "1.0.10" - "@nomicfoundation/hardhat-verify" "2.0.5" - "@openzeppelin/contracts" "5.0.2" - "@uniswap/permit2-sdk" "1.2.0" - chai "4.4.0" - dotenv "16.4.5" - ethereumjs-util "7.1.5" - ethers "6.11.1" - hardhat "2.22.2" - hardhat-deploy "0.12.2" - mocha-chai-jest-snapshot "1.1.4" - node-fetch "2.7.0" - "@1inch/solidity-utils@6.0.0": version "6.0.0" resolved "https://registry.yarnpkg.com/@1inch/solidity-utils/-/solidity-utils-6.0.0.tgz#a04cba02078ce83239cc4f6e4f3824bf7fc32a46" @@ -361,7 +341,7 @@ "@babel/parser" "^7.25.9" "@babel/types" "^7.25.9" -"@babel/traverse@^7.25.9", "@babel/traverse@^7.7.2": +"@babel/traverse@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.9.tgz#a50f8fe49e7f69f53de5bea7e413cd35c5e13c84" integrity sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw== @@ -374,7 +354,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.20.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.3.3": +"@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.3.3": version "7.26.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff" integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA== @@ -875,6 +855,11 @@ resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== +"@gnosis.pm/safe-contracts@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-contracts/-/safe-contracts-1.3.0.tgz#316741a7690d8751a1f701538cfc9ec80866eedc" + integrity sha512-1p+1HwGvxGUVzVkFjNzglwHrLNA67U/axP0Ct85FzzH8yhGJb4t9jDjPYocVMzLorDoWAfKicGy1akPY9jXRVw== + "@humanwhocodes/config-array@^0.13.0": version "0.13.0" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" @@ -922,18 +907,6 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.3.tgz#2030606ec03a18c31803b8a36382762e447655df" - integrity sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw== - dependencies: - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - slash "^3.0.0" - "@jest/console@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" @@ -946,13 +919,6 @@ jest-util "^29.7.0" slash "^3.0.0" -"@jest/expect-utils@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" - integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA== - dependencies: - jest-get-type "^28.0.2" - "@jest/expect-utils@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" @@ -960,13 +926,6 @@ dependencies: jest-get-type "^29.6.3" -"@jest/schemas@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" - integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg== - dependencies: - "@sinclair/typebox" "^0.24.1" - "@jest/schemas@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" @@ -974,16 +933,6 @@ dependencies: "@sinclair/typebox" "^0.27.8" -"@jest/test-result@^28.1.1": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.3.tgz#5eae945fd9f4b8fcfce74d239e6f725b6bf076c5" - integrity sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg== - dependencies: - "@jest/console" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - "@jest/test-result@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" @@ -994,27 +943,6 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/transform@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.3.tgz#59d8098e50ab07950e0f2fc0fc7ec462371281b0" - integrity sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA== - dependencies: - "@babel/core" "^7.11.6" - "@jest/types" "^28.1.3" - "@jridgewell/trace-mapping" "^0.3.13" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.3" - jest-regex-util "^28.0.2" - jest-util "^28.1.3" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - write-file-atomic "^4.0.1" - "@jest/transform@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" @@ -1036,18 +964,6 @@ slash "^3.0.0" write-file-atomic "^4.0.2" -"@jest/types@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" - integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ== - dependencies: - "@jest/schemas" "^28.1.3" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - "@jest/types@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" @@ -1084,7 +1000,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== -"@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": +"@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -1280,89 +1196,41 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nomicfoundation/edr-darwin-arm64@0.3.8": - version "0.3.8" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.3.8.tgz#09de1f03c0336670fce959f376f0fe9137545836" - integrity sha512-eB0leCexS8sQEmfyD72cdvLj9djkBzQGP4wSQw6SNf2I4Sw4Cnzb3d45caG2FqFFjbvfqL0t+badUUIceqQuMw== - "@nomicfoundation/edr-darwin-arm64@0.6.4": version "0.6.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.6.4.tgz#6eaa64a6ea5201e4c92b121f2b7fd197b26e450a" integrity sha512-QNQErISLgssV9+qia8sIjRANqtbW8snSDvjspixT/kSQ5ZSGxxctTg7x72wPSrcu8+EBEveIe5uqENIp5GH8HQ== -"@nomicfoundation/edr-darwin-x64@0.3.8": - version "0.3.8" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.3.8.tgz#c3ca237c74ed3b6fb800fd7f1de7174f4ad24f72" - integrity sha512-JksVCS1N5ClwVF14EvO25HCQ+Laljh/KRfHERMVAC9ZwPbTuAd/9BtKvToCBi29uCHWqsXMI4lxCApYQv2nznw== - "@nomicfoundation/edr-darwin-x64@0.6.4": version "0.6.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.6.4.tgz#d15ca89e9deef7d0a710cf90e79f3cc270a5a999" integrity sha512-cjVmREiwByyc9+oGfvAh49IAw+oVJHF9WWYRD+Tm/ZlSpnEVWxrGNBak2bd/JSYjn+mZE7gmWS4SMRi4nKaLUg== -"@nomicfoundation/edr-linux-arm64-gnu@0.3.8": - version "0.3.8" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.3.8.tgz#08bd367789e745f4e78a8a87368fc470eea8a7de" - integrity sha512-raCE+fOeNXhVBLUo87cgsHSGvYYRB6arih4eG6B9KGACWK5Veebtm9xtKeiD8YCsdUlUfat6F7ibpeNm91fpsA== - "@nomicfoundation/edr-linux-arm64-gnu@0.6.4": version "0.6.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.6.4.tgz#e73c41ca015dfddb5f4cb6cd3d9b2cbe5cc28989" integrity sha512-96o9kRIVD6W5VkgKvUOGpWyUGInVQ5BRlME2Fa36YoNsRQMaKtmYJEU0ACosYES6ZTpYC8U5sjMulvPtVoEfOA== -"@nomicfoundation/edr-linux-arm64-musl@0.3.8": - version "0.3.8" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.3.8.tgz#9cab5cbec0052cb5812c6c66c463d28a756cd916" - integrity sha512-PwiDp4wBZWMCIy29eKkv8moTKRrpiSDlrc+GQMSZLhOAm8T33JKKXPwD/2EbplbhCygJDGXZdtEKl9x9PaH66A== - "@nomicfoundation/edr-linux-arm64-musl@0.6.4": version "0.6.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.6.4.tgz#90906f733e4ad26657baeb22d28855d934ab7541" integrity sha512-+JVEW9e5plHrUfQlSgkEj/UONrIU6rADTEk+Yp9pbe+mzNkJdfJYhs5JYiLQRP4OjxH4QOrXI97bKU6FcEbt5Q== -"@nomicfoundation/edr-linux-x64-gnu@0.3.8": - version "0.3.8" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.3.8.tgz#d4a11b6ebcd1b29d7431d185c6df3e65a2cd4bde" - integrity sha512-6AcvA/XKoipGap5jJmQ9Y6yT7Uf39D9lu2hBcDCXnXbMcXaDGw4mn1/L4R63D+9VGZyu1PqlcJixCUZlGGIWlg== - "@nomicfoundation/edr-linux-x64-gnu@0.6.4": version "0.6.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.6.4.tgz#11b8bd73df145a192e5a08199e5e81995fcde502" integrity sha512-nzYWW+fO3EZItOeP4CrdMgDXfaGBIBkKg0Y/7ySpUxLqzut40O4Mb0/+quqLAFkacUSWMlFp8nsmypJfOH5zoA== -"@nomicfoundation/edr-linux-x64-musl@0.3.8": - version "0.3.8" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.3.8.tgz#b8eef960d06380a365866ddd1e97ecb7fbf6bd70" - integrity sha512-cxb0sEmZjlwhYWO28sPsV64VDx31ekskhC1IsDXU1p9ntjHSJRmW4KEIqJ2O3QwJap/kLKfMS6TckvY10gjc6w== - "@nomicfoundation/edr-linux-x64-musl@0.6.4": version "0.6.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.6.4.tgz#a34b9a2c9e34853207824dc81622668a069ca642" integrity sha512-QFRoE9qSQ2boRrVeQ1HdzU+XN7NUgwZ1SIy5DQt4d7jCP+5qTNsq8LBNcqhRBOATgO63nsweNUhxX/Suj5r1Sw== -"@nomicfoundation/edr-win32-x64-msvc@0.3.8": - version "0.3.8" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.3.8.tgz#ac7061aeb07cc847c429513080b76bb05297a869" - integrity sha512-yVuVPqRRNLZk7TbBMkKw7lzCvI8XO8fNTPTYxymGadjr9rEGRuNTU1yBXjfJ59I1jJU/X2TSkRk1OFX0P5tpZQ== - "@nomicfoundation/edr-win32-x64-msvc@0.6.4": version "0.6.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.6.4.tgz#ca035c6f66ae9f88fa3ef123a1f3a2099cce7a5a" integrity sha512-2yopjelNkkCvIjUgBGhrn153IBPLwnsDeNiq6oA0WkeM8tGmQi4td+PGi9jAriUDAkc59Yoi2q9hYA6efiY7Zw== -"@nomicfoundation/edr@^0.3.1": - version "0.3.8" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.3.8.tgz#28fe7ae4f462ae74a16cd1a714ff7b1cd9c22b4c" - integrity sha512-u2UJ5QpznSHVkZRh6ePWoeVb6kmPrrqh08gCnZ9FHlJV9CITqlrTQHJkacd+INH31jx88pTAJnxePE4XAiH5qg== - dependencies: - "@nomicfoundation/edr-darwin-arm64" "0.3.8" - "@nomicfoundation/edr-darwin-x64" "0.3.8" - "@nomicfoundation/edr-linux-arm64-gnu" "0.3.8" - "@nomicfoundation/edr-linux-arm64-musl" "0.3.8" - "@nomicfoundation/edr-linux-x64-gnu" "0.3.8" - "@nomicfoundation/edr-linux-x64-musl" "0.3.8" - "@nomicfoundation/edr-win32-x64-msvc" "0.3.8" - "@nomicfoundation/edr@^0.6.4": version "0.6.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.6.4.tgz#1cd336c46a60f5af774e6cf0f1943f49f63dded6" @@ -1610,21 +1478,6 @@ table "^6.8.0" undici "^5.14.0" -"@nomicfoundation/hardhat-verify@2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.5.tgz#dcc2cb5e5c55a39704c7d492436f80f05a4ca5a3" - integrity sha512-Tg4zu8RkWpyADSFIgF4FlJIUEI4VkxcvELsmbJn2OokbvH2SnUrqKmw0BBfDrtvP0hhmx8wsnrRKP5DV/oTyTA== - dependencies: - "@ethersproject/abi" "^5.1.2" - "@ethersproject/address" "^5.0.2" - cbor "^8.1.0" - chalk "^2.4.2" - debug "^4.1.1" - lodash.clonedeep "^4.5.0" - semver "^6.3.0" - table "^6.8.0" - undici "^5.14.0" - "@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.2": version "0.1.2" resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.2.tgz#3a9c3b20d51360b20affb8f753e756d553d49557" @@ -1697,11 +1550,6 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.1.tgz#93da90fc209a0a4ff09c1deb037fbb35e4020890" integrity sha512-yQJaT5HDp9hYOOp4jTYxMsR02gdFZFXhewX5HW9Jo4fsqSVqqyIO/xTHdWDaKX5a3pv1txmf076Lziz+sO7L1w== -"@openzeppelin/contracts@5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.2.tgz#b1d03075e49290d06570b2fd42154d76c2a5d210" - integrity sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA== - "@openzeppelin/contracts@5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.1.0.tgz#4e61162f2a2bf414c4e10c45eca98ce5f1aadbd4" @@ -1850,11 +1698,6 @@ "@sentry/types" "5.30.0" tslib "^1.9.3" -"@sinclair/typebox@^0.24.1": - version "0.24.51" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" - integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== - "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -1934,13 +1777,6 @@ mkdirp "^3.0.1" path-browserify "^1.0.1" -"@types/babel__traverse@^7.0.6": - version "7.20.6" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7" - integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== - dependencies: - "@babel/types" "^7.20.7" - "@types/bn.js@^4.11.3": version "4.11.6" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" @@ -2090,11 +1926,6 @@ dependencies: "@types/node" "*" -"@types/prettier@^2.1.5": - version "2.7.3" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" - integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== - "@types/qs@^6.2.31", "@types/qs@^6.9.7": version "6.9.17" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.17.tgz#fc560f60946d0aeff2f914eb41679659d3310e1a" @@ -2839,19 +2670,6 @@ chai@4.3.10: pathval "^1.1.1" type-detect "^4.0.8" -chai@4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.0.tgz#f9ac79f26726a867ac9d90a9b382120479d5f55b" - integrity sha512-x9cHNq1uvkCdU+5xTkNh5WtgD4e4yDFCsp9jVc7N7qVeKeftv3gO/ZrviX5d+3ZfxdYnZXZYujjRInu1RogU6A== - dependencies: - assertion-error "^1.1.0" - check-error "^1.0.3" - deep-eql "^4.1.3" - get-func-name "^2.0.2" - loupe "^2.3.6" - pathval "^1.1.1" - type-detect "^4.0.8" - chai@4.5.0, chai@^4.3.4: version "4.5.0" resolved "https://registry.yarnpkg.com/chai/-/chai-4.5.0.tgz#707e49923afdd9b13a8b0b47d33d732d13812fd8" @@ -3077,11 +2895,6 @@ config-chain@^1.1.11: ini "^1.3.4" proto-list "~1.2.1" -convert-source-map@^1.4.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" - integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== - convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" @@ -3265,11 +3078,6 @@ depd@2.0.0: resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -diff-sequences@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" - integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== - diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -3855,19 +3663,6 @@ ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1: ethjs-util "0.1.6" rlp "^2.2.3" -ethers@6.11.1: - version "6.11.1" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.11.1.tgz#96aae00b627c2e35f9b0a4d65c7ab658259ee6af" - integrity sha512-mxTAE6wqJQAbp5QAe/+o+rXOID7Nw91OZXvgpjDa1r4fAbq2Nu314oEZSbjoRLacuCzs7kUC3clEvkCQowffGg== - dependencies: - "@adraffy/ens-normalize" "1.10.1" - "@noble/curves" "1.2.0" - "@noble/hashes" "1.3.2" - "@types/node" "18.15.13" - aes-js "4.0.0-beta.5" - tslib "2.4.0" - ws "8.5.0" - ethers@6.13.4: version "6.13.4" resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.4.tgz#bd3e1c3dc1e7dc8ce10f9ffb4ee40967a651b53c" @@ -3954,17 +3749,6 @@ evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -expect@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec" - integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g== - dependencies: - "@jest/expect-utils" "^28.1.3" - jest-get-type "^28.0.2" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - expect@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" @@ -4518,36 +4302,6 @@ hardhat-deploy@0.11.44: qs "^6.9.4" zksync-web3 "^0.14.3" -hardhat-deploy@0.12.2: - version "0.12.2" - resolved "https://registry.yarnpkg.com/hardhat-deploy/-/hardhat-deploy-0.12.2.tgz#1067bd0af61a65287bcf2a0ba7711ca1b54be2f6" - integrity sha512-Xp/4Lb5lC/j3kvitaWW5IZN5Meqv5D3kTIifc3ZwBoQtFLN26/fDfRV6MWAAcRO9gH64hZVokvtcDdl/fd7w3A== - dependencies: - "@ethersproject/abi" "^5.7.0" - "@ethersproject/abstract-signer" "^5.7.0" - "@ethersproject/address" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/contracts" "^5.7.0" - "@ethersproject/providers" "^5.7.2" - "@ethersproject/solidity" "^5.7.0" - "@ethersproject/transactions" "^5.7.0" - "@ethersproject/wallet" "^5.7.0" - "@types/qs" "^6.9.7" - axios "^0.21.1" - chalk "^4.1.2" - chokidar "^3.5.2" - debug "^4.3.2" - enquirer "^2.3.6" - ethers "^5.7.0" - form-data "^4.0.0" - fs-extra "^10.0.0" - match-all "^1.2.6" - murmur-128 "^0.2.1" - qs "^6.9.4" - zksync-ethers "^5.0.0" - hardhat-deploy@0.12.4: version "0.12.4" resolved "https://registry.yarnpkg.com/hardhat-deploy/-/hardhat-deploy-0.12.4.tgz#5ebef37f1004f52a74987213b0465ad7c9433fb2" @@ -4785,55 +4539,6 @@ hardhat@2.22.15: uuid "^8.3.2" ws "^7.4.6" -hardhat@2.22.2: - version "2.22.2" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.22.2.tgz#0cadd7ec93bf39bab09f81603e75bc5e92acea3d" - integrity sha512-0xZ7MdCZ5sJem4MrvpQWLR3R3zGDoHw5lsR+pBFimqwagimIOn3bWuZv69KA+veXClwI1s/zpqgwPwiFrd4Dxw== - dependencies: - "@ethersproject/abi" "^5.1.2" - "@metamask/eth-sig-util" "^4.0.0" - "@nomicfoundation/edr" "^0.3.1" - "@nomicfoundation/ethereumjs-common" "4.0.4" - "@nomicfoundation/ethereumjs-tx" "5.0.4" - "@nomicfoundation/ethereumjs-util" "9.0.4" - "@nomicfoundation/solidity-analyzer" "^0.1.0" - "@sentry/node" "^5.18.1" - "@types/bn.js" "^5.1.0" - "@types/lru-cache" "^5.1.0" - adm-zip "^0.4.16" - aggregate-error "^3.0.0" - ansi-escapes "^4.3.0" - boxen "^5.1.2" - chalk "^2.4.2" - chokidar "^3.4.0" - ci-info "^2.0.0" - debug "^4.1.1" - enquirer "^2.3.0" - env-paths "^2.2.0" - ethereum-cryptography "^1.0.3" - ethereumjs-abi "^0.6.8" - find-up "^2.1.0" - fp-ts "1.19.3" - fs-extra "^7.0.1" - glob "7.2.0" - immutable "^4.0.0-rc.12" - io-ts "1.10.4" - keccak "^3.0.2" - lodash "^4.17.11" - mnemonist "^0.38.0" - mocha "^10.0.0" - p-map "^4.0.0" - raw-body "^2.4.1" - resolve "1.17.0" - semver "^6.3.0" - solc "0.7.3" - source-map-support "^0.5.13" - stacktrace-parser "^0.1.10" - tsort "0.0.1" - undici "^5.14.0" - uuid "^8.3.2" - ws "^7.4.6" - has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" @@ -5267,16 +4972,6 @@ jackspeak@^3.1.2: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" -jest-diff@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f" - integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw== - dependencies: - chalk "^4.0.0" - diff-sequences "^28.1.1" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" - jest-diff@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" @@ -5287,35 +4982,11 @@ jest-diff@^29.7.0: jest-get-type "^29.6.3" pretty-format "^29.7.0" -jest-get-type@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" - integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== - jest-get-type@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== -jest-haste-map@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.3.tgz#abd5451129a38d9841049644f34b034308944e2b" - integrity sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA== - dependencies: - "@jest/types" "^28.1.3" - "@types/graceful-fs" "^4.1.3" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^28.0.2" - jest-util "^28.1.3" - jest-worker "^28.1.3" - micromatch "^4.0.4" - walker "^1.0.8" - optionalDependencies: - fsevents "^2.3.2" - jest-haste-map@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" @@ -5335,16 +5006,6 @@ jest-haste-map@^29.7.0: optionalDependencies: fsevents "^2.3.2" -jest-matcher-utils@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" - integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw== - dependencies: - chalk "^4.0.0" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" - jest-matcher-utils@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" @@ -5355,21 +5016,6 @@ jest-matcher-utils@^29.7.0: jest-get-type "^29.6.3" pretty-format "^29.7.0" -jest-message-util@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" - integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^28.1.3" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^28.1.3" - slash "^3.0.0" - stack-utils "^2.0.3" - jest-message-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" @@ -5385,45 +5031,11 @@ jest-message-util@^29.7.0: slash "^3.0.0" stack-utils "^2.0.3" -jest-regex-util@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" - integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== - jest-regex-util@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== -jest-snapshot@^28.1.1: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.3.tgz#17467b3ab8ddb81e2f605db05583d69388fc0668" - integrity sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^28.1.3" - "@jest/transform" "^28.1.3" - "@jest/types" "^28.1.3" - "@types/babel__traverse" "^7.0.6" - "@types/prettier" "^2.1.5" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^28.1.3" - graceful-fs "^4.2.9" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - jest-haste-map "^28.1.3" - jest-matcher-utils "^28.1.3" - jest-message-util "^28.1.3" - jest-util "^28.1.3" - natural-compare "^1.4.0" - pretty-format "^28.1.3" - semver "^7.3.5" - jest-snapshot@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" @@ -5450,18 +5062,6 @@ jest-snapshot@^29.7.0: pretty-format "^29.7.0" semver "^7.5.3" -jest-util@^28.1.1, jest-util@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" - integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== - dependencies: - "@jest/types" "^28.1.3" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - jest-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" @@ -5474,15 +5074,6 @@ jest-util@^29.7.0: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-worker@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98" - integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^8.0.0" - jest-worker@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" @@ -5930,19 +5521,6 @@ mnemonist@^0.38.0: dependencies: obliterator "^2.0.0" -mocha-chai-jest-snapshot@1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/mocha-chai-jest-snapshot/-/mocha-chai-jest-snapshot-1.1.4.tgz#0c8e15530968074b08d1b7fbf28c2ed8b92f4380" - integrity sha512-ybwtS10P8BXDJQn9B3QyQA8Lxr/CcYxtuyWKk1PxD9vJorH8VL3edB7re4GcG9dRAdDPE/B0BsfwmCo6W43O7w== - dependencies: - "@jest/test-result" "^28.1.1" - chalk "^4.1.2" - find-package-json "^1.2.0" - jest-snapshot "^28.1.1" - jest-util "^28.1.1" - slash "^3.0.0" - yargs "^17.5.1" - mocha-chai-jest-snapshot@1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/mocha-chai-jest-snapshot/-/mocha-chai-jest-snapshot-1.1.6.tgz#cba844aeec6e19ef830992eb71817745c75e2e09" @@ -6434,16 +6012,6 @@ prettier@^2.8.3: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== -pretty-format@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" - integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== - dependencies: - "@jest/schemas" "^28.1.3" - ansi-regex "^5.0.1" - ansi-styles "^5.0.0" - react-is "^18.0.0" - pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" @@ -6849,7 +6417,7 @@ semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.2, semver@^7.6.3: +semver@^7.3.4, semver@^7.3.7, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.2, semver@^7.6.3: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -7815,7 +7383,7 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -write-file-atomic@^4.0.1, write-file-atomic@^4.0.2: +write-file-atomic@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== @@ -7891,7 +7459,7 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.5.1, yargs@^17.7.2: +yargs@^17.7.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== From 43a774db00e10e408d5f77b2ce31a687cd377c65 Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Thu, 21 Nov 2024 14:59:40 +0400 Subject: [PATCH 02/11] fix tests --- contracts/SimpleSettlement.sol | 48 ++-- contracts/mocks/GasBumpChecker.sol | 2 +- package.json | 2 +- test/FeeBank.js | 205 ----------------- test/FeeBankCharger.js | 68 ------ test/GasBump.js | 54 +++-- test/MeasureGas.js | 249 +++++++++----------- test/PriorityFeeLimiter.js | 39 ++-- test/Settlement.js | 353 +++++++---------------------- test/WhitelistChecker.js | 10 +- test/helpers/fixtures.js | 20 +- test/helpers/fusionUtils.js | 83 +++---- yarn.lock | 4 +- 13 files changed, 312 insertions(+), 825 deletions(-) delete mode 100644 test/FeeBank.js delete mode 100644 test/FeeBankCharger.js diff --git a/contracts/SimpleSettlement.sol b/contracts/SimpleSettlement.sol index 882ead0..faf1de9 100644 --- a/contracts/SimpleSettlement.sol +++ b/contracts/SimpleSettlement.sol @@ -40,11 +40,11 @@ contract SimpleSettlement is FeeTaker { uint256 remainingMakingAmount, bytes calldata extraData ) internal view override returns (uint256) { - uint256 rateBump = _getRateBump(extraData); + (uint256 rateBump, bytes calldata tail) = _getRateBump(extraData); return Math.mulDiv( - super._getMakingAmount(order, extension, orderHash, taker, takingAmount, remainingMakingAmount, extraData), - takingAmount * _BASE_POINTS, - order.takingAmount * (_BASE_POINTS + rateBump) + super._getMakingAmount(order, extension, orderHash, taker, takingAmount, remainingMakingAmount, tail), + _BASE_POINTS, + _BASE_POINTS + rateBump ); } @@ -60,11 +60,11 @@ contract SimpleSettlement is FeeTaker { uint256 remainingMakingAmount, bytes calldata extraData ) internal view override returns (uint256) { - uint256 rateBump = _getRateBump(extraData); + (uint256 rateBump, bytes calldata tail) = _getRateBump(extraData); return Math.mulDiv( - super._getTakingAmount(order, extension, orderHash, taker, makingAmount, remainingMakingAmount, extraData), - makingAmount * (_BASE_POINTS + rateBump), - order.makingAmount * _BASE_POINTS, + super._getTakingAmount(order, extension, orderHash, taker, makingAmount, remainingMakingAmount, tail), + _BASE_POINTS + rateBump, + _BASE_POINTS, Math.Rounding.Ceil ); } @@ -86,7 +86,7 @@ contract SimpleSettlement is FeeTaker { * ``` * @return rateBump The rate bump. */ - function _getRateBump(bytes calldata auctionDetails) private view returns (uint256) { + function _getRateBump(bytes calldata auctionDetails) private view returns (uint256, bytes calldata) { unchecked { uint256 gasBumpEstimate = uint24(bytes3(auctionDetails[0:3])); uint256 gasPriceEstimate = uint32(bytes4(auctionDetails[3:7])); @@ -94,8 +94,8 @@ contract SimpleSettlement is FeeTaker { uint256 auctionStartTime = uint32(bytes4(auctionDetails[7:11])); uint256 auctionFinishTime = auctionStartTime + uint24(bytes3(auctionDetails[11:14])); uint256 initialRateBump = uint24(bytes3(auctionDetails[14:17])); - uint256 auctionBump = _getAuctionBump(auctionStartTime, auctionFinishTime, initialRateBump, auctionDetails[17:]); - return auctionBump > gasBump ? auctionBump - gasBump : 0; + (uint256 auctionBump, bytes calldata tail) = _getAuctionBump(auctionStartTime, auctionFinishTime, initialRateBump, auctionDetails[17:]); + return (auctionBump > gasBump ? auctionBump - gasBump : 0, tail); } } @@ -111,28 +111,33 @@ contract SimpleSettlement is FeeTaker { * @param pointsAndTimeDeltas The points and time deltas structure. * @return The rate bump at the current time. */ - function _getAuctionBump(uint256 auctionStartTime, uint256 auctionFinishTime, uint256 initialRateBump, bytes calldata pointsAndTimeDeltas) private view returns (uint256) { + function _getAuctionBump( + uint256 auctionStartTime, uint256 auctionFinishTime, uint256 initialRateBump, bytes calldata pointsAndTimeDeltas + ) private view returns (uint256, bytes calldata) { unchecked { + uint256 currentPointTime = auctionStartTime; + uint256 currentRateBump = initialRateBump; + uint256 pointsCount = uint8(pointsAndTimeDeltas[0]); + pointsAndTimeDeltas = pointsAndTimeDeltas[1:]; + bytes calldata tail = pointsAndTimeDeltas[5 * pointsCount:]; + if (block.timestamp <= auctionStartTime) { - return initialRateBump; + return (initialRateBump, tail); } else if (block.timestamp >= auctionFinishTime) { - return 0; + return (0, tail); } - uint256 currentPointTime = auctionStartTime; - uint256 currentRateBump = initialRateBump; - - while (pointsAndTimeDeltas.length > 0) { + for (uint256 i = 0; i < pointsCount; i++) { uint256 nextRateBump = uint24(bytes3(pointsAndTimeDeltas[:3])); uint256 nextPointTime = currentPointTime + uint16(bytes2(pointsAndTimeDeltas[3:5])); if (block.timestamp <= nextPointTime) { - return ((block.timestamp - currentPointTime) * nextRateBump + (nextPointTime - block.timestamp) * currentRateBump) / (nextPointTime - currentPointTime); + return (((block.timestamp - currentPointTime) * nextRateBump + (nextPointTime - block.timestamp) * currentRateBump) / (nextPointTime - currentPointTime), tail); } currentRateBump = nextRateBump; currentPointTime = nextPointTime; pointsAndTimeDeltas = pointsAndTimeDeltas[5:]; } - return (auctionFinishTime - block.timestamp) * currentRateBump / (auctionFinishTime - currentPointTime); + return ((auctionFinishTime - block.timestamp) * currentRateBump / (auctionFinishTime - currentPointTime), tail); } } @@ -162,6 +167,9 @@ contract SimpleSettlement is FeeTaker { allowedTime += uint16(bytes2(whitelist[10:])); // add next time delta whitelist = whitelist[12:]; } + if (block.timestamp < allowedTime) { + revert AllowedTimeViolation(); + } } } } diff --git a/contracts/mocks/GasBumpChecker.sol b/contracts/mocks/GasBumpChecker.sol index 72c70b4..7798fd4 100644 --- a/contracts/mocks/GasBumpChecker.sol +++ b/contracts/mocks/GasBumpChecker.sol @@ -20,7 +20,7 @@ contract GasBumpChecker is SimpleSettlement { uint256 remainingMakingAmount, bytes calldata extraData, uint256 expectedResult - ) external payable { + ) external { uint256 res = this.getTakingAmount( order, extension, diff --git a/package.json b/package.json index e6f63aa..23a65c9 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "@1inch/delegating": "1.1.0", - "@1inch/limit-order-protocol-contract": "https://github.com/1inch/limit-order-protocol.git#fec2379", + "@1inch/limit-order-protocol-contract": "https://github.com/1inch/limit-order-protocol.git#a27230", "@1inch/solidity-utils": "6.0.0", "@1inch/st1inch": "2.2.0", "@openzeppelin/contracts": "5.1.0" diff --git a/test/FeeBank.js b/test/FeeBank.js deleted file mode 100644 index 923b0ea..0000000 --- a/test/FeeBank.js +++ /dev/null @@ -1,205 +0,0 @@ -const { expect, ether, getPermit, deployContract, constants } = require('@1inch/solidity-utils'); -const { ethers } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); -const { deploySwapTokens, getChainId } = require('./helpers/fixtures'); - -describe('FeeBank', function () { - async function initContracts() { - const chainId = await getChainId(); - const [owner, alice] = await ethers.getSigners(); - - const inch = await deployContract('ERC20PermitMock', ['1INCH', '1INCH', owner, ether('1000')]); - const { lopv4 } = await deploySwapTokens(); - const matcher = await deployContract('SettlementMock', [lopv4, inch, constants.ZERO_ADDRESS, constants.ZERO_ADDRESS]); - - const FeeBank = await ethers.getContractFactory('FeeBank'); - const feeBank = FeeBank.attach(await matcher.FEE_BANK()); - - await inch.transfer(alice, ether('100')); - await inch.approve(feeBank, ether('1000')); - await inch.connect(alice).approve(feeBank, ether('1000')); - - return { - contracts: { inch, feeBank, matcher }, - accounts: { owner, alice }, - others: { chainId }, - }; - } - - describe('deposits', function () { - it('should increase accountDeposits and availableCredit with deposit()', async function () { - const { contracts: { inch, feeBank }, accounts: { owner, alice } } = await loadFixture(initContracts); - const ownerAmount = ether('1'); - const aliceAmount = ether('10'); - const ownerBalanceBefore = await inch.balanceOf(owner); - const aliceBalanceBefore = await inch.balanceOf(alice); - - await feeBank.deposit(ownerAmount); - await feeBank.connect(alice).deposit(aliceAmount); - - expect(await feeBank.availableCredit(owner)).to.equal(ownerAmount); - expect(await feeBank.availableCredit(alice)).to.equal(aliceAmount); - expect(await inch.balanceOf(owner)).to.equal(ownerBalanceBefore - ownerAmount); - expect(await inch.balanceOf(alice)).to.equal(aliceBalanceBefore - aliceAmount); - }); - - it('should increase accountDeposits and availableCredit with depositFor()', async function () { - const { contracts: { inch, feeBank }, accounts: { owner, alice } } = await loadFixture(initContracts); - const ownerAmount = ether('1'); - const aliceAmount = ether('10'); - const ownerBalanceBefore = await inch.balanceOf(owner); - const aliceBalanceBefore = await inch.balanceOf(alice); - - await feeBank.connect(alice).depositFor(owner, ownerAmount); - await feeBank.depositFor(alice, aliceAmount); - - expect(await feeBank.availableCredit(owner)).to.equal(ownerAmount); - expect(await feeBank.availableCredit(alice)).to.equal(aliceAmount); - expect(await inch.balanceOf(owner)).to.equal(ownerBalanceBefore - aliceAmount); - expect(await inch.balanceOf(alice)).to.equal(aliceBalanceBefore - ownerAmount); - }); - - it('should increase accountDeposits and availableCredit without approve with depositWithPermit()', async function () { - const { contracts: { inch, feeBank }, accounts: { owner }, others: { chainId } } = await loadFixture(initContracts); - const ownerAmount = ether('1'); - await inch.approve(feeBank, '0'); - const permit = await getPermit(owner, inch, '1', chainId, await feeBank.getAddress(), ownerAmount); - const ownerBalanceBefore = await inch.balanceOf(owner); - - await feeBank.depositWithPermit(ownerAmount, permit); - - expect(await feeBank.availableCredit(owner)).to.equal(ownerAmount); - expect(await inch.balanceOf(owner)).to.equal(ownerBalanceBefore - ownerAmount); - }); - - it('should increase accountDeposits and availableCredit without approve with depositForWithPermit()', async function () { - const { contracts: { inch, feeBank }, accounts: { owner, alice }, others: { chainId } } = await loadFixture(initContracts); - const ownerAmount = ether('1'); - await inch.approve(feeBank, '0'); - const permit = await getPermit(owner, inch, '1', chainId, await feeBank.getAddress(), ownerAmount); - const ownerBalanceBefore = await inch.balanceOf(owner); - - await feeBank.depositForWithPermit(alice, ownerAmount, permit); - - expect(await feeBank.availableCredit(alice)).to.equal(ownerAmount); - expect(await inch.balanceOf(owner)).to.equal(ownerBalanceBefore - ownerAmount); - }); - }); - - describe('withdrawals', function () { - async function initContratsAndDeposit() { - const data = await initContracts(); - const { contracts: { feeBank } } = data; - const totalDepositAmount = ether('100'); - await feeBank.deposit(totalDepositAmount); - return { ...data, others: { ...data.others, totalDepositAmount } }; - } - - it('should decrease accountDeposits and availableCredit with withdraw()', async function () { - const { contracts: { inch, feeBank }, accounts: { owner }, others: { totalDepositAmount } } = await loadFixture(initContratsAndDeposit); - const amount = ether('10'); - const ownerBalanceBefore = await inch.balanceOf(owner); - - await feeBank.withdraw(amount); - - expect(await feeBank.availableCredit(owner)).to.equal(totalDepositAmount - amount); - expect(await inch.balanceOf(owner)).to.equal(ownerBalanceBefore + amount); - }); - - it('should decrease accountDeposits and availableCredit with withdrawTo()', async function () { - const { contracts: { inch, feeBank }, accounts: { owner, alice }, others: { totalDepositAmount } } = await loadFixture(initContratsAndDeposit); - const amount = ether('10'); - const aliceBalanceBefore = await inch.balanceOf(alice); - - await feeBank.withdrawTo(alice, amount); - - expect(await feeBank.availableCredit(owner)).to.equal(totalDepositAmount - amount); - expect(await inch.balanceOf(alice)).to.equal(aliceBalanceBefore + amount); - }); - - it('should not withdrawal more than account have', async function () { - const { contracts: { feeBank }, others: { totalDepositAmount } } = await loadFixture(initContratsAndDeposit); - await expect(feeBank.withdraw(totalDepositAmount + 1n)).to.be.revertedWithPanic(PANIC_CODES.UNDERFLOW); - }); - }); - - describe('gatherFees', function () { - it('should correct withdrawal fee for 1 account', async function () { - const { contracts: { inch, feeBank, matcher }, accounts: { owner, alice } } = await loadFixture(initContracts); - const amount = ether('10'); - const subCreditAmount = ether('2'); - await feeBank.connect(alice).deposit(amount); - await matcher.decreaseAvailableCreditMock(alice, subCreditAmount); - - const balanceBefore = await inch.balanceOf(owner); - expect(await feeBank.availableCredit(alice)).to.equal(amount - subCreditAmount); - await feeBank.gatherFees([alice]); - - expect(await feeBank.availableCredit(alice)).to.equal(amount - subCreditAmount); - expect(await inch.balanceOf(owner)).to.equal(balanceBefore + subCreditAmount); - }); - - it('should correct withdrawal fee for 2 account', async function () { - const { contracts: { inch, feeBank, matcher }, accounts: { owner, alice } } = await loadFixture(initContracts); - const ownerAmount = ether('10'); - const aliceAmount = ether('25'); - const subCreditownerAmount = ether('2'); - const subCreditaliceAmount = ether('11'); - await feeBank.deposit(ownerAmount); - await feeBank.connect(alice).deposit(aliceAmount); - await matcher.decreaseAvailableCreditMock(owner, subCreditownerAmount); - await matcher.decreaseAvailableCreditMock(alice, subCreditaliceAmount); - - const balanceBefore = await inch.balanceOf(owner); - expect(await feeBank.availableCredit(owner)).to.equal(ownerAmount - subCreditownerAmount); - expect(await feeBank.availableCredit(alice)).to.equal(aliceAmount - subCreditaliceAmount); - await feeBank.gatherFees([await owner.getAddress(), await alice.getAddress()]); - - expect(await feeBank.availableCredit(owner)).to.equal(ownerAmount - subCreditownerAmount); - expect(await feeBank.availableCredit(alice)).to.equal(aliceAmount - subCreditaliceAmount); - expect(await inch.balanceOf(owner)).to.equal( - balanceBefore + subCreditownerAmount + subCreditaliceAmount, - ); - }); - - it('should correct withdrawal fee for several account', async function () { - const { contracts: { inch, feeBank, matcher }, accounts: { owner } } = await loadFixture(initContracts); - const accounts = []; - const wallets = await ethers.getSigners(); - for (const wallet of wallets) { - accounts.push(wallet.address); - } - const amounts = []; - const subCreditAmounts = []; - let totalSubCreditAmounts = ether('0'); - for (let i = 1; i < accounts.length; i++) { - amounts[i] = BigInt('0x' + Array.from(ethers.randomBytes(8)).map(b => b.toString(16).padStart(2, '0')).join('')); - subCreditAmounts[i] = BigInt('0x' + Array.from(ethers.randomBytes(2)).map(b => b.toString(16).padStart(2, '0')).join('')); - totalSubCreditAmounts = totalSubCreditAmounts + subCreditAmounts[i]; - await feeBank.depositFor(accounts[i], amounts[i]); - } - for (let i = 1; i < accounts.length; i++) { - await matcher.decreaseAvailableCreditMock(accounts[i], subCreditAmounts[i]); - } - - const balanceBefore = await inch.balanceOf(owner.address); - for (let i = 1; i < accounts.length; i++) { - expect(await feeBank.availableCredit(accounts[i])).to.equal(amounts[i] - subCreditAmounts[i]); - } - - await feeBank.gatherFees(accounts); - for (let i = 1; i < accounts.length; i++) { - expect(await feeBank.availableCredit(accounts[i])).to.equal(amounts[i] - subCreditAmounts[i]); - } - expect(await inch.balanceOf(owner.address)).to.equal(balanceBefore + totalSubCreditAmounts); - }); - - it('should not work by non-owner', async function () { - const { contracts: { feeBank }, accounts: { owner, alice } } = await loadFixture(initContracts); - await expect(feeBank.connect(alice).gatherFees([owner.address, alice.address])).to.be.revertedWithCustomError( - feeBank, 'OwnableUnauthorizedAccount', - ); - }); - }); -}); diff --git a/test/FeeBankCharger.js b/test/FeeBankCharger.js deleted file mode 100644 index 826d527..0000000 --- a/test/FeeBankCharger.js +++ /dev/null @@ -1,68 +0,0 @@ -const { expect, ether, deployContract } = require('@1inch/solidity-utils'); -const { ethers } = require('hardhat'); -const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); - -describe('FeeBankCharger', function () { - async function initContracts() { - const [owner, alice] = await ethers.getSigners(); - const inch = await deployContract('ERC20PermitMock', ['1INCH', '1INCH', owner, ether('1000')]); - - const charger = await deployContract('FeeBankCharger', [inch, owner]); - - const FeeBank = await ethers.getContractFactory('FeeBank'); - const feeBank = FeeBank.attach(await charger.FEE_BANK()); - - await inch.transfer(alice, ether('100')); - await inch.approve(feeBank, ether('1000')); - await inch.connect(alice).approve(feeBank, ether('1000')); - - return { - contracts: { inch, charger, feeBank }, - accounts: { owner, alice }, - }; - } - - describe('increaseAvailableCredit', function () { - it('should increase credit', async function () { - const { contracts: { charger, feeBank }, accounts: { alice } } = await loadFixture(initContracts); - const amount = ether('100'); - expect(await charger.availableCredit(alice)).to.equal('0'); - await feeBank.depositFor(alice, amount); - expect(await charger.availableCredit(alice)).to.equal(amount); - }); - - it('should not increase credit by non-feeBank address', async function () { - const { contracts: { charger }, accounts: { alice } } = await loadFixture(initContracts); - await expect(charger.increaseAvailableCredit(alice, ether('100'))).to.be.revertedWithCustomError( - charger, - 'OnlyFeeBankAccess', - ); - }); - }); - - describe('decreaseAvailableCredit', function () { - async function initContractsAndAllowance() { - const data = await initContracts(); - const { contracts: { feeBank } } = data; - const creditAmount = ether('100'); - await feeBank.deposit(creditAmount); - return { ...data, others: { creditAmount } }; - } - - it('should decrease credit', async function () { - const { contracts: { charger, feeBank }, accounts: { owner, alice }, others: { creditAmount } } = await loadFixture(initContractsAndAllowance); - const amount = ether('10'); - expect(await charger.availableCredit(owner)).to.equal(creditAmount); - await feeBank.withdrawTo(alice, amount); - expect(await charger.availableCredit(owner)).to.equal(creditAmount - amount); - }); - - it('should not deccrease credit by non-feeBank address', async function () { - const { contracts: { charger }, accounts: { alice } } = await loadFixture(initContractsAndAllowance); - await expect(charger.decreaseAvailableCredit(alice, ether('10'))).to.be.revertedWithCustomError( - charger, - 'OnlyFeeBankAccess', - ); - }); - }); -}); diff --git a/test/GasBump.js b/test/GasBump.js index a587dfe..d71cf50 100644 --- a/test/GasBump.js +++ b/test/GasBump.js @@ -1,10 +1,10 @@ -const { deployContract, time, ether, constants } = require('@1inch/solidity-utils'); +const { time, ether, constants } = require('@1inch/solidity-utils'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { buildOrder, buildMakerTraits } = require('@1inch/limit-order-protocol-contract/test/helpers/orderUtils'); +const { buildOrder, buildMakerTraits, buildFeeTakerExtensions } = require('@1inch/limit-order-protocol-contract/test/helpers/orderUtils'); const { initContractsForSettlement } = require('./helpers/fixtures'); const { buildAuctionDetails } = require('./helpers/fusionUtils'); const hre = require('hardhat'); -const { network } = hre; +const { network, ethers } = hre; describe('GasBump', function () { before(async function () { @@ -16,8 +16,9 @@ describe('GasBump', function () { }); async function prepare() { - const { contracts: { dai, weth }, accounts: { owner } } = await initContractsForSettlement(); - const checker = await deployContract('GasBumpChecker'); + const { contracts: { dai, weth, accessToken }, accounts: { owner } } = await initContractsForSettlement(); + const GasBumpChecker = await ethers.getContractFactory('GasBumpChecker'); + const checker = await GasBumpChecker.deploy(accessToken, weth, owner); const currentTime = (await time.latest()) - time.duration.minutes(1) + 1; const { details: auctionDetails } = await buildAuctionDetails({ gasBumpEstimate: 10000, // 0.1% of taking amount @@ -27,42 +28,49 @@ describe('GasBump', function () { points: [[500000, 60]], }); - const order = buildOrder({ - maker: owner.address, - makerAsset: await dai.getAddress(), - takerAsset: await weth.getAddress(), - makingAmount: ether('10'), - takingAmount: ether('1'), - makerTraits: buildMakerTraits(), + const extensions = buildFeeTakerExtensions({ + feeTaker: await checker.getAddress(), + getterExtraPrefix: auctionDetails, }); - return { order, owner, auctionDetails, checker }; + const order = buildOrder( + { + maker: owner.address, + makerAsset: await dai.getAddress(), + takerAsset: await weth.getAddress(), + makingAmount: ether('10'), + takingAmount: ether('1'), + makerTraits: buildMakerTraits(), + }, + ); + + return { order, owner, extensions, checker }; } - async function testGetTakingAmount(checker, order, owner, auctionDetails, basefee, result) { + async function testGetTakingAmount(checker, order, extensions, basefee, result) { await network.provider.send('hardhat_setNextBlockBaseFeePerGas', ['0x' + basefee.toString(16)]); await checker.testGetTakingAmount( - order, '0x', constants.ZERO_BYTES32, owner.address, ether('10'), ether('10'), auctionDetails, result, { gasPrice: basefee }, + order, '0x', constants.ZERO_BYTES32, constants.ZERO_ADDRESS, ether('10'), ether('10'), '0x' + extensions.takingAmountData.substring(42), result, { gasPrice: basefee }, ); } it('0 gwei = no gas fee', async function () { - const { order, owner, auctionDetails, checker } = await loadFixture(prepare); - await testGetTakingAmount(checker, order, owner, auctionDetails, 0, ether('1.05')); + const { order, extensions, checker } = await loadFixture(prepare); + await testGetTakingAmount(checker, order, extensions, 0, ether('1.05')); }); it('0.1 gwei = 0.01% gas fee', async function () { - const { order, owner, auctionDetails, checker } = await loadFixture(prepare); - await testGetTakingAmount(checker, order, owner, auctionDetails, 1e8, ether('1.0499')); + const { order, extensions, checker } = await loadFixture(prepare); + await testGetTakingAmount(checker, order, extensions, 1e8, ether('1.0499')); }); it('15 gwei = 1.5% gas fee', async function () { - const { order, owner, auctionDetails, checker } = await loadFixture(prepare); - await testGetTakingAmount(checker, order, owner, auctionDetails, 15e9, ether('1.035')); + const { order, extensions, checker } = await loadFixture(prepare); + await testGetTakingAmount(checker, order, extensions, 15e9, ether('1.035')); }); it('100 gwei = 10% gas fee, should be capped with takingAmount', async function () { - const { order, owner, auctionDetails, checker } = await loadFixture(prepare); - await testGetTakingAmount(checker, order, owner, auctionDetails, 100e9, ether('1')); + const { order, extensions, checker } = await loadFixture(prepare); + await testGetTakingAmount(checker, order, extensions, 100e9, ether('1')); }); }); diff --git a/test/MeasureGas.js b/test/MeasureGas.js index 0316178..4dd8d81 100644 --- a/test/MeasureGas.js +++ b/test/MeasureGas.js @@ -4,10 +4,10 @@ const path = require('path'); const { ethers } = hre; const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { constants, time, expect, ether, trim0x, deployContract } = require('@1inch/solidity-utils'); -const { deploySwapTokens, getChainId } = require('./helpers/fixtures'); -const { buildAuctionDetails, buildExtensionsBitmapData } = require('./helpers/fusionUtils'); +const { deploySwapTokens, getChainId, initContractsForSettlement } = require('./helpers/fixtures'); +const { buildAuctionDetails, buildCalldataForOrder } = require('./helpers/fusionUtils'); const { buildOrder: buildOrderV1, buildSalt: buildSaltV1, signOrder: signOrderV1 } = require('./helpers/orderUtilsV1'); -const { buildOrder, signOrder, buildTakerTraits, buildMakerTraits } = require('@1inch/limit-order-protocol-contract/test/helpers/orderUtils'); +const { buildOrder, signOrder, buildTakerTraits, buildMakerTraits, buildFeeTakerExtensions } = require('@1inch/limit-order-protocol-contract/test/helpers/orderUtils'); const RESOLVERS_NUMBER = 10; @@ -20,18 +20,18 @@ describe('MeasureGas', function () { const [owner, alice] = await ethers.getSigners(); const chainId = await getChainId(); const abiCoder = ethers.AbiCoder.defaultAbiCoder(); - const { dai, weth, inch, accessToken, lopv3, lopv4 } = await deploySwapTokens(); + const { dai, weth, accessToken, lopv3, lopv4 } = await deploySwapTokens(); await dai.transfer(alice, ether('100')); - await inch.mint(owner, ether('100')); + // await inch.mint(owner, ether('100')); await weth.deposit({ value: ether('1') }); await weth.connect(alice).deposit({ value: ether('1') }); - const settlementExtension = await deployContract('Settlement', [lopv4, inch, accessToken, weth, owner]); + const settlementExtension = await deployContract('Settlement', [lopv4, accessToken, weth, owner]); const SettlementV1 = JSON.parse(fs.readFileSync(path.join(__dirname, '../artifacts-v1/SettlementV1.json'), 'utf8')); // const settlement = await deployContract(SettlementV1.abi, [lopv3.address, inch.address]); const ContractFactory = await ethers.getContractFactory(SettlementV1.abi, SettlementV1.bytecode); - const settlement = await ContractFactory.deploy(lopv3, inch); + const settlement = await ContractFactory.deploy(lopv3, weth); const resolversV1 = []; const ResolverV1Mock = JSON.parse(fs.readFileSync(path.join(__dirname, '../artifacts-v1/ResolverV1Mock.json'), 'utf8')); @@ -39,18 +39,13 @@ describe('MeasureGas', function () { const ResolverMockFactory = await ethers.getContractFactory(ResolverV1Mock.abi, ResolverV1Mock.bytecode); resolversV1[i] = await ResolverMockFactory.deploy(settlement); } - const FeeBank = await ethers.getContractFactory('FeeBank'); - const feeBank = FeeBank.attach(await settlement.feeBank()); - await inch.approve(feeBank, ether('100')); - await feeBank.depositFor(resolversV1[0], ether('100')); - const ResolverMock = await ethers.getContractFactory('ResolverMock'); const resolver = await ResolverMock.deploy(settlement, lopv4); await resolver.approve(dai, lopv4); await resolver.approve(weth, lopv4); return { - contracts: { dai, weth, accessToken, lopv3, lopv4, settlement, settlementExtension, feeBank, resolversV1, resolver }, + contracts: { dai, weth, accessToken, lopv3, lopv4, settlement, settlementExtension, resolversV1, resolver }, accounts: { owner, alice }, others: { chainId, abiCoder }, }; @@ -229,33 +224,26 @@ describe('MeasureGas', function () { it('post interaction', async function () { const { contracts: { dai, weth, accessToken }, accounts: { owner } } = await loadFixture(initContractsAndApproves); const settlementExtension = await deployContract('Settlement', [owner, weth, accessToken, weth, owner]); - const currentTime = (await time.latest()) - time.duration.minutes(1); + const auction = await buildAuctionDetails(); + const extensions = buildFeeTakerExtensions({ + feeTaker: await settlementExtension.getAddress(), + getterExtraPrefix: auction.details, + whitelistPostInteraction: '0x0000000000', + }); - const postInteractionData = ethers.solidityPacked( - ['uint32', 'bytes10', 'uint16', 'bytes10', 'uint16', 'bytes10', 'uint16', 'bytes10', 'uint16', 'bytes10', 'uint16', 'bytes1'], - [ - currentTime, - '0x' + weth.target.substring(22), 0, - '0x' + weth.target.substring(22), 0, - '0x' + weth.target.substring(22), 0, - '0x' + owner.address.substring(22), 0, - '0x' + weth.target.substring(22), 0, - buildExtensionsBitmapData({ resolvers: 5 }), - ], + const order = buildOrder( + { + maker: owner.address, + makerAsset: await dai.getAddress(), + takerAsset: await weth.getAddress(), + makingAmount: ether('10'), + takingAmount: ether('1'), + makerTraits: buildMakerTraits(), + }, + extensions, ); - const order = buildOrder({ - maker: owner.address, - makerAsset: await dai.getAddress(), - takerAsset: await weth.getAddress(), - makingAmount: ether('10'), - takingAmount: ether('1'), - makerTraits: buildMakerTraits(), - }, { - postInteraction: await settlementExtension.getAddress() + trim0x(postInteractionData), - }); - - await settlementExtension.postInteraction(order, '0x', constants.ZERO_BYTES32, owner.address, ether('10'), ether('1'), ether('10'), postInteractionData); + await settlementExtension.postInteraction(order, '0x', constants.ZERO_BYTES32, owner.address, ether('10'), ether('1'), ether('10'), '0x' + extensions.postInteraction.substring(42)); }); it('making getter', async function () { @@ -267,55 +255,65 @@ describe('MeasureGas', function () { initialRateBump: 900000, points: [[800000, 100], [700000, 100], [600000, 100], [500000, 100], [400000, 100]], }); - - const order = buildOrder({ - maker: owner.address, - makerAsset: await dai.getAddress(), - takerAsset: await weth.getAddress(), - makingAmount: ether('10'), - takingAmount: ether('1'), - makerTraits: buildMakerTraits(), - }, { - makingAmountData: await settlementExtension.getAddress() + trim0x(details), - takingAmountData: await settlementExtension.getAddress() + trim0x(details), + const extensions = buildFeeTakerExtensions({ + feeTaker: await settlementExtension.getAddress(), + getterExtraPrefix: details, + whitelistPostInteraction: '0x0000000000', }); - const txn = await settlementExtension.getMakingAmount.populateTransaction(order, '0x', constants.ZERO_BYTES32, constants.ZERO_ADDRESS, ether('1'), ether('10'), details); - await owner.sendTransaction(txn); + const order = buildOrder( + { + maker: owner.address, + makerAsset: await dai.getAddress(), + takerAsset: await weth.getAddress(), + makingAmount: ether('10'), + takingAmount: ether('1'), + makerTraits: buildMakerTraits(), + }, + extensions, + ); + + await settlementExtension.getMakingAmount(order, '0x', constants.ZERO_BYTES32, constants.ZERO_ADDRESS, ether('1'), ether('10'), '0x' + extensions.makingAmountData.substring(42)); }); }); describe('SettlementExtension', function () { it('extension 1 fill for 1 order', async function () { - const { contracts: { dai, weth, lopv4, settlementExtension }, accounts: { owner, alice }, others: { chainId } } = await loadFixture(initContractsAndApproves); - - const auctionStartTime = await time.latest(); - const { details: auctionDetails } = await buildAuctionDetails({ startTime: auctionStartTime, duration: time.duration.hours(1) }); - - const order = buildOrder({ + const { + contracts: { dai, weth, lopv4, settlement }, + accounts: { alice, owner }, + others: { chainId }, + } = await loadFixture(initContractsForSettlement); + const auction = await buildAuctionDetails(); + + const orderData = { maker: alice.address, makerAsset: await dai.getAddress(), takerAsset: await weth.getAddress(), makingAmount: ether('100'), takingAmount: ether('0.1'), makerTraits: buildMakerTraits(), - }, { - makingAmountData: await settlementExtension.getAddress() + trim0x(auctionDetails), - takingAmountData: await settlementExtension.getAddress() + trim0x(auctionDetails), - postInteraction: await settlementExtension.getAddress() + trim0x(ethers.solidityPacked( - ['uint32', 'bytes10', 'uint16', 'bytes1'], [auctionStartTime, '0x' + owner.address.substring(22), 0, buildExtensionsBitmapData()], - )), - }); + }; + + const order = buildOrder( + orderData, + buildFeeTakerExtensions({ + feeTaker: await settlement.getAddress(), + getterExtraPrefix: auction.details, + whitelistPostInteraction: '0x0000000000', + }), + ); const { r, yParityAndS: vs } = ethers.Signature.from(await signOrder(order, chainId, await lopv4.getAddress(), alice)); - await weth.approve(lopv4, ether('0.1')); const takerTraits = buildTakerTraits({ makingAmount: true, - minReturn: ether('0.1'), + threshold: ether('0.1'), extension: order.extension, }); + await weth.approve(lopv4, ether('0.1')); + const tx = await lopv4.fillOrderArgs( order, r, @@ -324,62 +322,55 @@ describe('MeasureGas', function () { takerTraits.traits, takerTraits.args, ); + console.log(`1 fill for 1 order gasUsed: ${(await tx.wait()).gasUsed}`); + await expect(tx).to.changeTokenBalances(dai, [owner, alice], [ether('100'), ether('-100')]); await expect(tx).to.changeTokenBalances(weth, [owner, alice], [ether('-0.1'), ether('0.1')]); }); it('extension 1 fill for 1 order via resolver with funds', async function () { - const { contracts: { dai, weth, lopv4, settlementExtension, resolver }, accounts: { alice }, others: { chainId } } = await loadFixture(initContractsAndApproves); - - const auctionStartTime = await time.latest(); - const { details: auctionDetails } = await buildAuctionDetails({ startTime: auctionStartTime, duration: time.duration.hours(1) }); - - const order = buildOrder({ - maker: alice.address, - makerAsset: await dai.getAddress(), - takerAsset: await weth.getAddress(), - makingAmount: ether('100'), - takingAmount: ether('0.1'), - makerTraits: buildMakerTraits(), - }, { - makingAmountData: await settlementExtension.getAddress() + trim0x(auctionDetails), - takingAmountData: await settlementExtension.getAddress() + trim0x(auctionDetails), - postInteraction: await settlementExtension.getAddress() + trim0x(ethers.solidityPacked( - ['uint32', 'bytes10', 'uint16', 'bytes1'], [auctionStartTime, '0x' + resolver.target.substring(22), 0, buildExtensionsBitmapData()], - )), - }); - - const { r, yParityAndS: vs } = ethers.Signature.from(await signOrder(order, chainId, await lopv4.getAddress(), alice)); - - const takerTraits = buildTakerTraits({ - makingAmount: true, - minReturn: ether('100'), - extension: order.extension, + const dataFormFixture = await loadFixture(initContractsForSettlement); + const auction = await buildAuctionDetails(); + const setupData = { ...dataFormFixture, auction }; + const { + contracts: { dai, weth, resolver }, + accounts: { alice }, + } = setupData; + + const fillOrderToData = await buildCalldataForOrder({ + orderData: { + maker: alice.address, + makerAsset: await dai.getAddress(), + takerAsset: await weth.getAddress(), + makingAmount: ether('100'), + takingAmount: ether('0.1'), + makerTraits: buildMakerTraits(), + }, + orderSigner: alice, + setupData, + threshold: ether('0.1'), + isInnermostOrder: true, }); await weth.transfer(resolver, ether('0.1')); - const tx = await resolver.settleOrders( - lopv4.interface.encodeFunctionData('fillOrderArgs', [ - order, - r, - vs, - ether('100'), - takerTraits.traits, - takerTraits.args, - ]), - ); + const tx = await resolver.settleOrders(fillOrderToData); + console.log(`1 fill for 1 order via resolver with funds gasUsed: ${(await tx.wait()).gasUsed}`); await expect(tx).to.changeTokenBalances(dai, [resolver, alice], [ether('100'), ether('-100')]); await expect(tx).to.changeTokenBalances(weth, [resolver, alice], [ether('-0.1'), ether('0.1')]); }); it('extension 1 fill for 1 order via resolver without funds', async function () { - const { contracts: { dai, weth, lopv4, settlementExtension, resolver }, accounts: { owner, alice }, others: { chainId, abiCoder } } = await loadFixture(initContractsAndApproves); - - const auctionStartTime = await time.latest(); - const { details: auctionDetails } = await buildAuctionDetails({ startTime: auctionStartTime, duration: time.duration.hours(1) }); + const dataFormFixture = await loadFixture(initContractsForSettlement); + const auction = await buildAuctionDetails(); + const setupData = { ...dataFormFixture, auction }; + const { + contracts: { dai, weth, resolver }, + accounts: { owner, alice }, + others: { abiCoder }, + } = setupData; const resolverArgs = abiCoder.encode( ['address[]', 'bytes[]'], @@ -395,42 +386,26 @@ describe('MeasureGas', function () { ], ); - const order = buildOrder({ - maker: alice.address, - makerAsset: await dai.getAddress(), - takerAsset: await weth.getAddress(), - makingAmount: ether('100'), - takingAmount: ether('0.1'), - makerTraits: buildMakerTraits(), - }, { - makingAmountData: await settlementExtension.getAddress() + trim0x(auctionDetails), - takingAmountData: await settlementExtension.getAddress() + trim0x(auctionDetails), - postInteraction: await settlementExtension.getAddress() + trim0x(ethers.solidityPacked( - ['uint32', 'bytes10', 'uint16', 'bytes1'], [auctionStartTime, '0x' + resolver.target.substring(22), 0, buildExtensionsBitmapData()], - )), - }); - - const { r, yParityAndS: vs } = ethers.Signature.from(await signOrder(order, chainId, await lopv4.getAddress(), alice)); - - const takerTraits = buildTakerTraits({ - makingAmount: true, - minReturn: ether('100'), - extension: order.extension, - interaction: await resolver.getAddress() + '01' + trim0x(resolverArgs), + const fillOrderToData = await buildCalldataForOrder({ + orderData: { + maker: alice.address, + makerAsset: await dai.getAddress(), + takerAsset: await weth.getAddress(), + makingAmount: ether('100'), + takingAmount: ether('0.1'), + makerTraits: buildMakerTraits(), + }, + orderSigner: alice, + setupData, + threshold: ether('0.1'), + additionalDataForSettlement: resolverArgs, + isInnermostOrder: true, }); await weth.approve(resolver, ether('0.1')); - const tx = await resolver.settleOrders( - lopv4.interface.encodeFunctionData('fillOrderArgs', [ - order, - r, - vs, - ether('100'), - takerTraits.traits, - takerTraits.args, - ]), - ); + const tx = await resolver.settleOrders(fillOrderToData); + console.log(`1 fill for 1 order via resolver without money gasUsed: ${(await tx.wait()).gasUsed}`); await expect(tx).to.changeTokenBalances(dai, [resolver, alice], [ether('100'), ether('-100')]); await expect(tx).to.changeTokenBalances(weth, [owner, alice], [ether('-0.1'), ether('0.1')]); diff --git a/test/PriorityFeeLimiter.js b/test/PriorityFeeLimiter.js index 05c242b..cb53c36 100644 --- a/test/PriorityFeeLimiter.js +++ b/test/PriorityFeeLimiter.js @@ -1,10 +1,9 @@ -const { expect, deployContract, time, ether, trim0x, constants } = require('@1inch/solidity-utils'); +const { expect, deployContract, ether, constants } = require('@1inch/solidity-utils'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { buildOrder, buildMakerTraits } = require('@1inch/limit-order-protocol-contract/test/helpers/orderUtils'); +const { buildOrder, buildMakerTraits, buildFeeTakerExtensions } = require('@1inch/limit-order-protocol-contract/test/helpers/orderUtils'); const { initContractsForSettlement } = require('./helpers/fixtures'); const hre = require('hardhat'); -const { buildExtensionsBitmapData } = require('./helpers/fusionUtils'); -const { ethers, network } = hre; +const { network } = hre; describe('PriorityFeeLimiter', function () { before(async function () { @@ -18,25 +17,25 @@ describe('PriorityFeeLimiter', function () { async function prepare() { const { contracts: { dai, weth, accessToken }, accounts: { owner } } = await initContractsForSettlement(); const settlementExtension = await deployContract('Settlement', [owner, weth, accessToken, weth, owner]); - const currentTime = (await time.latest()) - time.duration.minutes(1); - const postInteractionData = ethers.solidityPacked( - ['uint32', 'bytes1'], - [currentTime, buildExtensionsBitmapData({ resolvers: 0 })], - ); - - const order = buildOrder({ - maker: owner.address, - makerAsset: await dai.getAddress(), - takerAsset: await weth.getAddress(), - makingAmount: ether('10'), - takingAmount: ether('1'), - makerTraits: buildMakerTraits(), - }, { - postInteraction: await settlementExtension.getAddress() + trim0x(postInteractionData), + const extensions = buildFeeTakerExtensions({ + feeTaker: await settlementExtension.getAddress(), + whitelistPostInteraction: '0x0000000000', }); - return { order, owner, postInteractionData, settlementExtension }; + const order = buildOrder( + { + maker: owner.address, + makerAsset: await dai.getAddress(), + takerAsset: await weth.getAddress(), + makingAmount: ether('10'), + takingAmount: ether('1'), + makerTraits: buildMakerTraits(), + }, + extensions, + ); + + return { order, owner, postInteractionData: '0x' + extensions.postInteraction.substring(42), settlementExtension }; } function sendPostInteractionTxn(settlementExtension, order, owner, postInteractionData, maxPriorityFeePerGas) { diff --git a/test/Settlement.js b/test/Settlement.js index 6d41afd..850ce40 100644 --- a/test/Settlement.js +++ b/test/Settlement.js @@ -1,13 +1,13 @@ const { ethers } = require('hardhat'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { time, expect, ether, trim0x, timeIncreaseTo, getPermit, getPermit2, compressPermit, permit2Contract, constants, deployContract } = require('@1inch/solidity-utils'); +const { time, expect, ether, trim0x, timeIncreaseTo, getPermit, getPermit2, compressPermit, permit2Contract, deployContract } = require('@1inch/solidity-utils'); const { buildMakerTraits } = require('@1inch/limit-order-protocol-contract/test/helpers/orderUtils'); const { initContractsForSettlement } = require('./helpers/fixtures'); const { buildAuctionDetails, buildCalldataForOrder } = require('./helpers/fusionUtils'); const ORDER_FEE = 100n; const BACK_ORDER_FEE = 125n; -const BASE_POINTS = ether('0.001'); // 1e15 +const FEE_BASE = 100000n; describe('Settlement', function () { it('opposite direction recursive swap', async function () { @@ -30,7 +30,7 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('100'), + threshold: ether('100'), isInnermostOrder: true, }); @@ -45,7 +45,7 @@ describe('Settlement', function () { }, orderSigner: owner, setupData, - minReturn: ether('0.1'), + threshold: ether('0.1'), additionalDataForSettlement: fillOrderToData1, }); @@ -75,7 +75,7 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('100'), + threshold: ether('100'), isInnermostOrder: true, }); @@ -90,7 +90,7 @@ describe('Settlement', function () { }, orderSigner: owner, setupData, - minReturn: ether('0.1'), + threshold: ether('0.1'), additionalDataForSettlement: fillOrderToData1, }); @@ -125,7 +125,7 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('100'), + threshold: ether('100'), isInnermostOrder: true, }); @@ -140,7 +140,7 @@ describe('Settlement', function () { }, orderSigner: owner, setupData, - minReturn: ether('0.1'), + threshold: ether('0.1'), additionalDataForSettlement: fillOrderToData1, }); @@ -178,7 +178,7 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('100'), + threshold: ether('101'), isInnermostOrder: true, integrator: bob.address, integratorFee: 100, @@ -189,21 +189,21 @@ describe('Settlement', function () { maker: owner.address, makerAsset: await dai.getAddress(), takerAsset: await weth.getAddress(), - makingAmount: ether('100'), + makingAmount: ether('100.1'), takingAmount: ether('0.1'), makerTraits: buildMakerTraits(), }, orderSigner: owner, setupData, - minReturn: ether('0.1'), + threshold: ether('0.11'), additionalDataForSettlement: fillOrderToData1, integrator: bob.address, integratorFee: 100, }); const txn = await resolver.settleOrders(fillOrderToData0); - await expect(txn).to.changeTokenBalances(dai, [owner, alice, bob], [ether('-100'), ether('99.9'), ether('0.1')]); - await expect(txn).to.changeTokenBalances(weth, [owner, alice, bob], [ether('0.0999'), ether('-0.11'), ether('0.0001')]); + await expect(txn).to.changeTokenBalances(dai, [owner, alice, bob], [ether('-100.1'), ether('100'), ether('0.1')]); + await expect(txn).to.changeTokenBalances(weth, [owner, alice, bob, resolver], [ether('0.1'), ether('-0.11'), ether('0.0001'), ether('0.0099')]); }); it('unidirectional recursive swap', async function () { @@ -241,7 +241,7 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('15'), + threshold: ether('15'), additionalDataForSettlement: resolverArgs, isInnermostOrder: true, isMakingAmount: false, @@ -258,7 +258,7 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('10'), + threshold: ether('10'), additionalDataForSettlement: fillOrderToData1, isMakingAmount: false, }); @@ -275,7 +275,7 @@ describe('Settlement', function () { const auction = await buildAuctionDetails(); const setupData = { ...dataFormFixture, auction }; const { - contracts: { dai, weth, resolver, settlement }, + contracts: { dai, weth, resolver }, accounts: { owner, alice, bob }, } = setupData; @@ -291,12 +291,11 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('100'), + threshold: ether('101'), isInnermostOrder: true, resolverFee: BACK_ORDER_FEE, integrator: bob.address, integratorFee: INTEGRATOR_FEE, - whitelistData: '0x' + constants.ZERO_ADDRESS.substring(22), }); const fillOrderToData0 = await buildCalldataForOrder({ @@ -304,27 +303,22 @@ describe('Settlement', function () { maker: owner.address, makerAsset: await dai.getAddress(), takerAsset: await weth.getAddress(), - makingAmount: ether('100'), + makingAmount: ether('100.24'), takingAmount: ether('0.1'), makerTraits: buildMakerTraits(), }, orderSigner: owner, setupData, - minReturn: ether('0.1'), + threshold: ether('0.11'), additionalDataForSettlement: fillOrderToData1, resolverFee: ORDER_FEE, - whitelistData: '0x' + constants.ZERO_ADDRESS.substring(22), + integrator: bob.address, }); - const availableCreditBefore = await settlement.availableCredit(resolver); - const tx = await resolver.settleOrders(fillOrderToData0); - // Check resolverFee - expect(await settlement.availableCredit(resolver)).to.equal( - availableCreditBefore - BASE_POINTS * (ORDER_FEE + BACK_ORDER_FEE), - ); - // Check integratorFee - expect(tx).changeTokenBalance(dai, bob, ether('100') * INTEGRATOR_FEE / BigInt(1e9)); + + await expect(tx).to.changeTokenBalances(dai, [owner, alice, bob], [ether('-100.24'), ether('100'), ether('0.24')]); + await expect(tx).to.changeTokenBalances(weth, [owner, alice, bob, resolver], [ether('0.1'), ether('-0.11'), ether('0.0001'), ether('0.0099')]); }); it('opposite direction recursive swap with resolverFee and integratorFee and custom receiver', async function () { @@ -332,7 +326,7 @@ describe('Settlement', function () { const auction = await buildAuctionDetails(); const setupData = { ...dataFormFixture, auction }; const { - contracts: { dai, weth, resolver, settlement }, + contracts: { dai, weth, resolver }, accounts: { owner, alice, bob }, } = setupData; @@ -351,12 +345,11 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('100'), + threshold: ether('101'), isInnermostOrder: true, resolverFee: BACK_ORDER_FEE, integrator: bob.address, integratorFee: INTEGRATOR_FEE, - whitelistData: '0x' + constants.ZERO_ADDRESS.substring(22), }); const fillOrderToData0 = await buildCalldataForOrder({ @@ -365,27 +358,22 @@ describe('Settlement', function () { receiver: ownerReciever.address, makerAsset: await dai.getAddress(), takerAsset: await weth.getAddress(), - makingAmount: ether('100'), + makingAmount: ether('100.24'), takingAmount: ether('0.1'), makerTraits: buildMakerTraits(), }, orderSigner: owner, setupData, - minReturn: ether('0.1'), + threshold: ether('0.11'), additionalDataForSettlement: fillOrderToData1, resolverFee: ORDER_FEE, - whitelistData: '0x' + constants.ZERO_ADDRESS.substring(22), + integrator: bob.address, }); - const availableCreditBefore = await settlement.availableCredit(resolver); - const tx = await resolver.settleOrders(fillOrderToData0); - // Check resolverFee - expect(await settlement.availableCredit(resolver)).to.equal( - availableCreditBefore - BASE_POINTS * (ORDER_FEE + BACK_ORDER_FEE), - ); - await expect(tx).to.changeTokenBalances(dai, [owner, aliceReciever, bob], [ether('-100'), ether('99.885'), ether('0.115')]); - await expect(tx).to.changeTokenBalances(weth, [alice, ownerReciever, bob], [ether('-0.11'), ether('0.1'), 0]); + + await expect(tx).to.changeTokenBalances(dai, [owner, aliceReciever, bob], [ether('-100.24'), ether('100'), ether('0.24')]); + await expect(tx).to.changeTokenBalances(weth, [alice, ownerReciever, bob, resolver], [ether('-0.11'), ether('0.1'), ether('0.0001'), ether('0.0099')]); }); it('opposite direction recursive swap with resolverFee and integratorFee and custom receiver and weth unwrapping', async function () { @@ -393,7 +381,7 @@ describe('Settlement', function () { const auction = await buildAuctionDetails(); const setupData = { ...dataFormFixture, auction }; const { - contracts: { dai, weth, resolver, settlement }, + contracts: { dai, weth, resolver }, accounts: { owner, alice, bob }, } = setupData; @@ -412,10 +400,10 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('100'), + threshold: ether('101'), isInnermostOrder: true, resolverFee: BACK_ORDER_FEE, - whitelistData: '0x' + constants.ZERO_ADDRESS.substring(22), + integrator: bob.address, }); const fillOrderToData0 = await buildCalldataForOrder({ @@ -424,30 +412,24 @@ describe('Settlement', function () { receiver: ownerReciever.address, makerAsset: await dai.getAddress(), takerAsset: await weth.getAddress(), - makingAmount: ether('100'), + makingAmount: ether('100.125'), takingAmount: ether('0.1'), makerTraits: buildMakerTraits({ unwrapWeth: true }), }, orderSigner: owner, setupData, - minReturn: ether('0.1'), + threshold: ether('0.11'), additionalDataForSettlement: fillOrderToData1, resolverFee: ORDER_FEE, integrator: bob.address, integratorFee: INTEGRATOR_FEE, - whitelistData: '0x' + constants.ZERO_ADDRESS.substring(22), }); - const availableCreditBefore = await settlement.availableCredit(resolver); - const tx = await resolver.settleOrders(fillOrderToData0); - // Check resolverFee - expect(await settlement.availableCredit(resolver)).to.equal( - availableCreditBefore - BASE_POINTS * (ORDER_FEE + BACK_ORDER_FEE), - ); - await expect(tx).to.changeTokenBalances(dai, [owner, aliceReciever, bob], [ether('-100'), ether('100'), 0]); - await expect(tx).to.changeTokenBalances(weth, [alice, ownerReciever, bob], [ether('-0.11'), 0, 0]); - await expect(tx).to.changeEtherBalances([alice, ownerReciever, bob], [0, ether('0.099885'), ether('0.000115')]); + + await expect(tx).to.changeTokenBalances(dai, [owner, aliceReciever, bob], [ether('-100.125'), ether('100'), ether('0.125')]); + await expect(tx).to.changeTokenBalances(weth, [alice, ownerReciever, bob, resolver], [ether('-0.11'), 0, 0, ether('0.009785')]); + await expect(tx).to.changeEtherBalances([alice, ownerReciever, bob], [0, ether('0.1'), ether('0.000215')]); }); it('triple recursive swap', async function () { @@ -470,7 +452,7 @@ describe('Settlement', function () { }, orderSigner: owner, setupData, - minReturn: ether('0.025'), + threshold: ether('0.025'), isInnermostOrder: true, isMakingAmount: false, }); @@ -486,7 +468,7 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('15'), + threshold: ether('15'), additionalDataForSettlement: fillOrderToData2, isMakingAmount: false, }); @@ -502,7 +484,7 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('10'), + threshold: ether('10'), additionalDataForSettlement: fillOrderToData1, isMakingAmount: false, }); @@ -565,7 +547,7 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('100'), + threshold: ether('100'), additionalDataForSettlement: resolverCalldata, isInnermostOrder: true, isMakingAmount: false, @@ -692,10 +674,10 @@ describe('Settlement', function () { await expect(txn).to.changeTokenBalances(weth, [owner, alice], [ether('-0.12'), ether('0.12')]); }); - it('set auctionDuration', async function () { + it.skip('set auctionDuration', async function () { const dataFormFixture = await loadFixture(initContractsForSettlement); - const auction = await buildAuctionDetails({ startTime: (await time.latest()) - (450 - 4), duration: 900, initialRateBump: 1000000n }); + const auction = await buildAuctionDetails({ startTime: (await time.latest()), duration: 900, initialRateBump: 1000000n }); const setupData = { ...dataFormFixture, auction }; const { contracts: { dai, weth, resolver }, @@ -713,65 +695,13 @@ describe('Settlement', function () { }); }); - it('should change availableCredit with non-zero fee', async function () { - const dataFormFixture = await loadFixture(initContractsForSettlement); - const auction = await buildAuctionDetails(); - const setupData = { ...dataFormFixture, auction }; - const { - contracts: { dai, weth, settlement, resolver }, - accounts: { owner, alice }, - } = setupData; - - const fillOrderToData1 = await buildCalldataForOrder({ - orderData: { - maker: alice.address, - makerAsset: await weth.getAddress(), - takerAsset: await dai.getAddress(), - makingAmount: ether('0.1'), - takingAmount: ether('100'), - makerTraits: buildMakerTraits(), - }, - orderSigner: alice, - setupData, - minReturn: ether('0.1'), - isInnermostOrder: true, - isMakingAmount: false, - resolverFee: BACK_ORDER_FEE, - whitelistData: '0x' + constants.ZERO_ADDRESS.substring(22), - }); - - const fillOrderToData0 = await buildCalldataForOrder({ - orderData: { - maker: owner.address, - makerAsset: await dai.getAddress(), - takerAsset: await weth.getAddress(), - makingAmount: ether('100'), - takingAmount: ether('0.1'), - makerTraits: buildMakerTraits(), - }, - orderSigner: owner, - setupData, - minReturn: ether('100'), - additionalDataForSettlement: fillOrderToData1, - isMakingAmount: false, - resolverFee: ORDER_FEE, - whitelistData: '0x' + constants.ZERO_ADDRESS.substring(22), - }); - const availableCreditBefore = await settlement.availableCredit(resolver); - - await resolver.settleOrders(fillOrderToData0); - expect(await settlement.availableCredit(resolver)).to.equal( - availableCreditBefore - BASE_POINTS * (ORDER_FEE + BACK_ORDER_FEE), - ); - }); - it('partial fill with taking fee', async function () { const dataFormFixture = await loadFixture(initContractsForSettlement); const auction = await buildAuctionDetails(); const setupData = { ...dataFormFixture, auction }; const { - contracts: { dai, weth, settlement, resolver }, - accounts: { owner, alice }, + contracts: { dai, weth, resolver }, + accounts: { owner, alice, bob }, others: { abiCoder }, } = setupData; @@ -786,7 +716,7 @@ describe('Settlement', function () { weth.interface.encodeFunctionData('transferFrom', [ owner.address, await resolver.getAddress(), - ether('0.01') * partialModifier / points, + ether('0.01') * partialModifier / points * (ORDER_FEE + FEE_BASE) / FEE_BASE, ]), ], ], @@ -803,129 +733,19 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('0.01') * partialModifier / points, + threshold: ether('0.011') * partialModifier / points, additionalDataForSettlement: resolverArgs, isInnermostOrder: true, fillingAmount: ether('10') * partialModifier / points, resolverFee: ORDER_FEE, - whitelistData: '0x' + constants.ZERO_ADDRESS.substring(22), + integrator: bob.address, }); await weth.approve(resolver, ether('0.01')); - const availableCreditBefore = await settlement.availableCredit(resolver); const txn = await resolver.settleOrders(fillOrderToData0); await expect(txn).to.changeTokenBalances(dai, [resolver, alice], [ether('10') * partialModifier / points, ether('-10') * partialModifier / points]); - await expect(txn).to.changeTokenBalances(weth, [owner, alice], [ether('-0.01') * partialModifier / points, ether('0.01') * partialModifier / points]); - expect(await settlement.availableCredit(resolver)).to.equal( - availableCreditBefore - (ORDER_FEE * partialModifier / points) * BASE_POINTS, - ); - }); - - it('resolver should pay minimal 1 wei fee', async function () { - const dataFormFixture = await loadFixture(initContractsForSettlement); - const auction = await buildAuctionDetails(); - const setupData = { ...dataFormFixture, auction }; - const { - contracts: { dai, weth, settlement, resolver }, - accounts: { owner, alice }, - others: { abiCoder }, - } = setupData; - - const minimalPartialModifier = 1n; - const points = ether('0.01'); - const minimalOrderFee = 10n; - - const resolverArgs = abiCoder.encode( - ['address[]', 'bytes[]'], - [ - [await weth.getAddress()], - [ - weth.interface.encodeFunctionData('transferFrom', [ - owner.address, - await resolver.getAddress(), - ether('0.01') * minimalPartialModifier / points, - ]), - ], - ], - ); - - const fillOrderToData0 = await buildCalldataForOrder({ - orderData: { - maker: alice.address, - makerAsset: await dai.getAddress(), - takerAsset: await weth.getAddress(), - makingAmount: ether('10'), - takingAmount: ether('0.01'), - makerTraits: buildMakerTraits(), - }, - orderSigner: alice, - setupData, - minReturn: ether('0.01'), - additionalDataForSettlement: resolverArgs, - isInnermostOrder: true, - fillingAmount: ether('10') * minimalPartialModifier / points, - resolverFee: minimalOrderFee, - whitelistData: '0x' + constants.ZERO_ADDRESS.substring(22), - }); - - await weth.approve(resolver, ether('0.01')); - const availableCreditBefore = await settlement.availableCredit(resolver); - - const txn = await resolver.settleOrders(fillOrderToData0); - await expect(txn).to.changeTokenBalances(dai, [resolver, alice], [ether('10') * minimalPartialModifier / points, ether('-10') * minimalPartialModifier / points]); - await expect(txn).to.changeTokenBalances(weth, [owner, alice], [ether('-0.01') * minimalPartialModifier / points, ether('0.01') * minimalPartialModifier / points]); - expect(await settlement.availableCredit(resolver)).to.equal( - availableCreditBefore - 1n, - ); - }); - - it('should not change when availableCredit is not enough', async function () { - const dataFormFixture = await loadFixture(initContractsForSettlement); - const auction = await buildAuctionDetails(); - const setupData = { ...dataFormFixture, auction }; - const { - contracts: { dai, weth, resolver }, - accounts: { owner, alice }, - } = setupData; - - const fillOrderToData1 = await buildCalldataForOrder({ - orderData: { - maker: alice.address, - makerAsset: await weth.getAddress(), - takerAsset: await dai.getAddress(), - makingAmount: ether('0.1'), - takingAmount: ether('100'), - makerTraits: buildMakerTraits(), - }, - orderSigner: alice, - setupData, - minReturn: ether('100'), - isInnermostOrder: true, - resolverFee: BACK_ORDER_FEE, - whitelistData: '0x' + constants.ZERO_ADDRESS.substring(22), - }); - - const fillOrderToData0 = await buildCalldataForOrder({ - orderData: { - maker: owner.address, - makerAsset: await dai.getAddress(), - takerAsset: await weth.getAddress(), - makingAmount: ether('100'), - takingAmount: ether('0.1'), - makerTraits: buildMakerTraits(), - }, - orderSigner: owner, - setupData, - minReturn: ether('0.1'), - additionalDataForSettlement: fillOrderToData1, - resolverFee: '1000000', - whitelistData: '0x' + constants.ZERO_ADDRESS.substring(22), - }); - - await expect(resolver.settleOrders(fillOrderToData0)).to.be.revertedWithCustomError( - dataFormFixture.contracts.settlement, 'NotEnoughCredit', - ); + await expect(txn).to.changeTokenBalances(weth, [owner, alice], [ether('-0.01') * partialModifier / points * (ORDER_FEE + FEE_BASE) / FEE_BASE, ether('0.01') * partialModifier / points]); }); it('should not pay resolver fee when whitelisted address and it has accessToken', async function () { @@ -933,7 +753,7 @@ describe('Settlement', function () { const auction = await buildAuctionDetails(); const setupData = { ...dataFormFixture, auction }; const { - contracts: { dai, weth, resolver, settlement }, + contracts: { dai, weth, resolver }, accounts: { alice }, } = setupData; @@ -950,18 +770,15 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('0.1'), + threshold: ether('0.1'), isInnermostOrder: true, resolverFee: ORDER_FEE, whitelistResolvers: ['0x' + resolver.target.substring(22)], }); - const availableCreditBefore = await settlement.availableCredit(resolver); const txn = await resolver.settleOrders(fillOrderToData); await expect(txn).to.changeTokenBalances(dai, [alice, resolver], [ether('-100'), ether('100')]); await expect(txn).to.changeTokenBalances(weth, [alice, resolver], [ether('0.1'), ether('-0.1')]); - // Check resolverFee - expect(await settlement.availableCredit(resolver)).to.equal(availableCreditBefore); }); it('should not pay resolver fee when whitelisted address and it has not accessToken', async function () { @@ -969,7 +786,7 @@ describe('Settlement', function () { const auction = await buildAuctionDetails(); const setupData = { ...dataFormFixture, auction }; const { - contracts: { dai, weth, accessToken, resolver, settlement }, + contracts: { dai, weth, accessToken, resolver }, accounts: { alice }, } = setupData; @@ -986,7 +803,7 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('0.1'), + threshold: ether('0.1'), isInnermostOrder: true, resolverFee: ORDER_FEE, whitelistResolvers: ['0x' + resolver.target.substring(22)], @@ -994,12 +811,9 @@ describe('Settlement', function () { await accessToken.burn(resolver, 1); - const availableCreditBefore = await settlement.availableCredit(resolver); const txn = await resolver.settleOrders(fillOrderToData); await expect(txn).to.changeTokenBalances(dai, [alice, resolver], [ether('-100'), ether('100')]); await expect(txn).to.changeTokenBalances(weth, [alice, resolver], [ether('0.1'), ether('-0.1')]); - // Check resolverFee - expect(await settlement.availableCredit(resolver)).to.equal(availableCreditBefore); }); it('should pay resolver fee when non-whitelisted address and it has accessToken', async function () { @@ -1007,11 +821,11 @@ describe('Settlement', function () { const auction = await buildAuctionDetails(); const setupData = { ...dataFormFixture, auction }; const { - contracts: { dai, weth, resolver, settlement }, - accounts: { alice }, + contracts: { dai, weth, resolver }, + accounts: { alice, bob }, } = setupData; - weth.transfer(resolver, ether('0.1')); + weth.transfer(resolver, ether('0.1001')); const fillOrderToData = await buildCalldataForOrder({ orderData: { @@ -1024,19 +838,15 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('0.1'), + threshold: ether('0.11'), isInnermostOrder: true, resolverFee: ORDER_FEE, + integrator: bob.address, }); - const availableCreditBefore = await settlement.availableCredit(resolver); const txn = await resolver.settleOrders(fillOrderToData); await expect(txn).to.changeTokenBalances(dai, [alice, resolver], [ether('-100'), ether('100')]); - await expect(txn).to.changeTokenBalances(weth, [alice, resolver], [ether('0.1'), ether('-0.1')]); - // Check resolverFee - expect(await settlement.availableCredit(resolver)).to.equal( - availableCreditBefore - BASE_POINTS * ORDER_FEE, - ); + await expect(txn).to.changeTokenBalances(weth, [alice, resolver, bob], [ether('0.1'), ether('-0.1001'), ether('0.0001')]); }); it('should revert when non-whitelisted address and it has not accessToken', async function () { @@ -1044,11 +854,11 @@ describe('Settlement', function () { const auction = await buildAuctionDetails(); const setupData = { ...dataFormFixture, auction }; const { - contracts: { dai, weth, accessToken, resolver }, + contracts: { dai, weth, accessToken, resolver, settlement }, accounts: { alice }, } = setupData; - weth.transfer(resolver, ether('0.1')); + weth.transfer(resolver, ether('0.1001')); const fillOrderToData = await buildCalldataForOrder({ orderData: { @@ -1061,16 +871,13 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('0.1'), + threshold: ether('0.11'), isInnermostOrder: true, resolverFee: ORDER_FEE, - whitelistData: '0x' + constants.ZERO_ADDRESS.substring(22), }); await accessToken.burn(resolver, 1); - await expect(resolver.settleOrders(fillOrderToData)).to.be.revertedWithCustomError( - dataFormFixture.contracts.settlement, 'ResolverCanNotFillOrder', - ); + await expect(resolver.settleOrders(fillOrderToData)).to.be.revertedWithCustomError(settlement, 'OnlyWhitelistOrAccessToken'); }); describe('whitelist lock period', async function () { @@ -1079,7 +886,7 @@ describe('Settlement', function () { const auction = await buildAuctionDetails({ startTime: await time.latest() + time.duration.hours('3') }); const setupData = { ...dataFormFixture, auction }; const { - contracts: { dai, weth, accessToken, resolver }, + contracts: { dai, weth, accessToken, resolver, settlement }, accounts: { owner, alice }, } = setupData; @@ -1094,7 +901,7 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('100'), + threshold: ether('100'), isInnermostOrder: true, whitelistResolvers: ['0x' + resolver.target.substring(22)], }); @@ -1110,19 +917,17 @@ describe('Settlement', function () { }, orderSigner: owner, setupData, - minReturn: ether('0.1'), + threshold: ether('0.1'), additionalDataForSettlement: fillOrderToData1, whitelistResolvers: ['0x' + resolver.target.substring(22)], }); - await accessToken.burn(resolver, 1); - - await expect(resolver.settleOrders(fillOrderToData0)).to.be.revertedWithCustomError( - setupData.contracts.settlement, 'ResolverCanNotFillOrder', - ); + await expect(resolver.settleOrders(fillOrderToData0)).to.be.revertedWithCustomError(settlement, 'AllowedTimeViolation'); await timeIncreaseTo(setupData.auction.startTime + 1); + await accessToken.burn(resolver, 1); + await resolver.settleOrders(fillOrderToData0); }); @@ -1131,7 +936,7 @@ describe('Settlement', function () { const auction = await buildAuctionDetails({ startTime: await time.latest() + time.duration.hours('3') }); const setupData = { ...dataFormFixture, auction }; const { - contracts: { dai, weth, resolver }, + contracts: { dai, weth, resolver, settlement }, accounts: { owner, alice }, } = setupData; @@ -1146,7 +951,7 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('100'), + threshold: ether('100'), isInnermostOrder: true, }); @@ -1161,13 +966,11 @@ describe('Settlement', function () { }, orderSigner: owner, setupData, - minReturn: ether('0.1'), + threshold: ether('0.1'), additionalDataForSettlement: fillOrderToData1, }); - await expect(resolver.settleOrders(fillOrderToData0)).to.be.revertedWithCustomError( - setupData.contracts.settlement, 'ResolverCanNotFillOrder', - ); + await expect(resolver.settleOrders(fillOrderToData0)).to.be.revertedWithCustomError(settlement, 'AllowedTimeViolation'); await timeIncreaseTo(setupData.auction.startTime + 1); @@ -1198,7 +1001,7 @@ describe('Settlement', function () { }, orderSigner: alice, setupData, - minReturn: ether('100'), + threshold: ether('100'), isInnermostOrder: true, customPostInteraction: await customExtension.getAddress() + trim0x(customPostInteractionData), }); @@ -1214,7 +1017,7 @@ describe('Settlement', function () { }, orderSigner: owner, setupData, - minReturn: ether('0.1'), + threshold: ether('0.1'), additionalDataForSettlement: fillOrderToData1, }); diff --git a/test/WhitelistChecker.js b/test/WhitelistChecker.js index a2cd37b..bac383e 100644 --- a/test/WhitelistChecker.js +++ b/test/WhitelistChecker.js @@ -12,7 +12,7 @@ describe('WhitelistChecker', function () { const auction = await buildAuctionDetails(); const setupData = { ...dataFormFixture, auction }; const { - contracts: { dai, weth, accessToken, resolver }, + contracts: { dai, weth, accessToken, resolver, settlement }, accounts: { alice }, } = setupData; @@ -34,9 +34,7 @@ describe('WhitelistChecker', function () { }); await accessToken.burn(resolver, 1); - await expect(resolver.settleOrders(fillOrderToData)).to.be.revertedWithCustomError( - dataFormFixture.contracts.settlement, 'ResolverCanNotFillOrder', - ); + await expect(resolver.settleOrders(fillOrderToData)).to.be.revertedWithCustomError(settlement, 'OnlyWhitelistOrAccessToken'); }); it('only resolver can use takerInteraction method', async function () { @@ -155,7 +153,7 @@ describe('WhitelistChecker', function () { }); await accessToken.burn(resolver, 1); - await expect(resolver.settleOrders(fillOrderToData)).to.be.revertedWithCustomError(settlement, 'ResolverCanNotFillOrder'); + await expect(resolver.settleOrders(fillOrderToData)).to.be.revertedWithCustomError(settlement, 'AllowedTimeViolation'); }); }); @@ -219,7 +217,7 @@ describe('WhitelistChecker', function () { isInnermostOrder: true, }); - await expect(resolver.settleOrders(fillOrderToData)).to.be.revertedWithCustomError(settlement, 'ResolverCanNotFillOrder'); + await expect(resolver.settleOrders(fillOrderToData)).to.be.revertedWithCustomError(settlement, 'AllowedTimeViolation'); }); }); }); diff --git a/test/helpers/fixtures.js b/test/helpers/fixtures.js index a2d080e..2ec0b7d 100644 --- a/test/helpers/fixtures.js +++ b/test/helpers/fixtures.js @@ -11,7 +11,6 @@ async function deploySwapTokens() { const [account] = await ethers.getSigners(); const dai = await deployContract('ERC20PermitMock', ['DAI', 'DAI', account, ether('1000')]); const weth = await deployContract('WrappedTokenMock', ['WETH', 'WETH']); - const inch = await deployContract('TokenMock', ['1INCH', '1INCH']); const accessToken = await deployContract('TokenMock', ['NFT', 'NFT']); const lopv4 = await deployContract('LimitOrderProtocol', [weth]); @@ -19,7 +18,7 @@ async function deploySwapTokens() { const ContractFactory = await ethers.getContractFactory(LimitOrderProtocolV3.abi, LimitOrderProtocolV3.bytecode); const lopv3 = await ContractFactory.deploy(weth); - return { dai, weth, inch, accessToken, lopv3, lopv4 }; + return { dai, weth, accessToken, lopv3, lopv4 }; } async function initContractsForSettlement() { @@ -27,26 +26,19 @@ async function initContractsForSettlement() { const chainId = await getChainId(); const [owner, alice, bob] = await ethers.getSigners(); - const { dai, weth, inch, accessToken, lopv4 } = await deploySwapTokens(); + const { dai, weth, accessToken, lopv4 } = await deploySwapTokens(); await dai.transfer(alice, ether('101')); - await inch.mint(owner, ether('100')); await weth.deposit({ value: ether('1') }); await weth.connect(alice).deposit({ value: ether('1') }); - const settlement = await deployContract('SettlementMock', [lopv4, inch, accessToken, weth]); - - const FeeBank = await ethers.getContractFactory('FeeBank'); - const feeBank = FeeBank.attach(await settlement.FEE_BANK()); + const settlement = await deployContract('SimpleSettlement', [lopv4, accessToken, weth, owner]); const ResolverMock = await ethers.getContractFactory('ResolverMock'); const resolver = await ResolverMock.deploy(settlement, lopv4); - await inch.approve(feeBank, ether('100')); - await feeBank.depositFor(resolver, ether('100')); - - await dai.approve(lopv4, ether('100')); - await dai.connect(alice).approve(lopv4, ether('100')); + await dai.approve(lopv4, ether('101')); + await dai.connect(alice).approve(lopv4, ether('101')); await weth.approve(lopv4, ether('1')); await weth.connect(alice).approve(lopv4, ether('1')); @@ -57,7 +49,7 @@ async function initContractsForSettlement() { await accessToken.mint(owner, 1); return { - contracts: { dai, weth, accessToken, lopv4, settlement, feeBank, resolver }, + contracts: { dai, weth, accessToken, lopv4, settlement, resolver }, accounts: { owner, alice, bob }, others: { chainId, abiCoder }, }; diff --git a/test/helpers/fusionUtils.js b/test/helpers/fusionUtils.js index 6fc644f..121c04a 100644 --- a/test/helpers/fusionUtils.js +++ b/test/helpers/fusionUtils.js @@ -1,31 +1,15 @@ const { time, trim0x, constants } = require('@1inch/solidity-utils'); const { ethers } = require('hardhat'); -const { buildOrder, buildTakerTraits, signOrder } = require('@1inch/limit-order-protocol-contract/test/helpers/orderUtils'); +const { buildOrder, buildTakerTraits, signOrder, buildFeeTakerExtensions } = require('@1inch/limit-order-protocol-contract/test/helpers/orderUtils'); -const expBase = 999999952502977513n; // 0.05^(1/(2 years)) means 95% value loss over 2 years - -function buildExtensionsBitmapData({ - resolvers = 1, - feeType = 0, -} = {}) { - const WHITELIST_BITMAP_OFFSET = 3; // Bitmap: VVVVVxxx - const FEE_RESOLVER_FLAG = 1; - const FEE_INTEGRATOR_FLAG = 2; - const CUSTOM_RECEIVER_FLAG = 4; - return ethers.toBeHex( - (resolvers << WHITELIST_BITMAP_OFFSET) | - feeType & FEE_RESOLVER_FLAG | - feeType & FEE_INTEGRATOR_FLAG | - feeType & CUSTOM_RECEIVER_FLAG, - ); -} +const expBase = 999999952502977513n; // 0.05^(1/(2 years)) means 95% value loss over 2 years} async function buildCalldataForOrder({ orderData, orderSigner, - minReturn, + threshold, setupData, - additionalDataForSettlement = '', + additionalDataForSettlement = '0x', isInnermostOrder = false, isMakingAmount = true, fillingAmount = isMakingAmount ? orderData.makingAmount : orderData.takingAmount, @@ -42,47 +26,40 @@ async function buildCalldataForOrder({ auction: { startTime: auctionStartTime, details: auctionDetails }, } = setupData; - let postInteractionResolverFee = ''; - let postInteractionIntegratorFee = ''; - let feeType = 0; - if (resolverFee > 0) { - feeType += 1; - postInteractionResolverFee = trim0x(ethers.solidityPacked(['bytes4'], ['0x' + resolverFee.toString(16).padStart(8, '0')])); - } - if (integratorFee > 0) { - feeType += 2; - postInteractionIntegratorFee = trim0x(ethers.solidityPacked(['bytes2', 'bytes20'], ['0x' + integratorFee.toString(16).padStart(4, '0'), integrator])); - if (orderData.receiver && orderData.receiver !== constants.ZERO_ADDRESS) { - feeType += 4; - postInteractionIntegratorFee += trim0x(orderData.receiver); - } - orderData.receiver = setupData.contracts.settlement.target; + let whitelist = ethers.solidityPacked(['uint8'], [whitelistResolvers.length]); + let whitelistPostInteraction = ethers.solidityPacked(['uint32', 'uint8'], [auctionStartTime, whitelistResolvers.length]); + for (let i = 0; i < whitelistResolvers.length; i++) { + whitelistPostInteraction += trim0x(ethers.solidityPacked(['bytes10', 'uint16'], [whitelistResolvers[i], resolversAllowedTime[i] || 0])); + whitelist += trim0x(whitelistResolvers[i]); } - let whitelistData = ''; - for (let i = 0; i < whitelistResolvers.length; i++) { - whitelistData += trim0x(ethers.solidityPacked(['bytes10', 'uint16'], [whitelistResolvers[i], resolversAllowedTime[i] || 0])); + let makerReceiver; + if (resolverFee > 0 || integratorFee > 0) { + makerReceiver = orderData.receiver; + orderData.receiver = await settlement.getAddress(); } - const order = buildOrder(orderData, { - makingAmountData: await settlement.getAddress() + trim0x(auctionDetails), - takingAmountData: await settlement.getAddress() + trim0x(auctionDetails), - postInteraction: await settlement.getAddress() + - postInteractionIntegratorFee + - postInteractionResolverFee + - trim0x(ethers.solidityPacked(['uint32'], [auctionStartTime])) + - whitelistData + - trim0x(customPostInteraction) + - trim0x(ethers.solidityPacked(['bytes1'], [buildExtensionsBitmapData({ resolvers: whitelistResolvers.length, feeType })])), - }); + const order = buildOrder( + orderData, + buildFeeTakerExtensions({ + feeTaker: await settlement.getAddress(), + getterExtraPrefix: auctionDetails, + feeRecipient: integrator, + makerReceiver: (makerReceiver && makerReceiver !== constants.ZERO_ADDRESS) ? makerReceiver : undefined, + integratorFee, + resolverFee, + whitelistDiscount: 0, + whitelist, + whitelistPostInteraction, + customPostInteraction, + }), + ); const { r, yParityAndS: vs } = ethers.Signature.from(await signOrder(order, chainId, await lopv4.getAddress(), orderSigner)); - await resolver.approve(order.takerAsset, lopv4); - const takerTraits = buildTakerTraits({ makingAmount: isMakingAmount, - minReturn, + threshold, extension: order.extension, interaction: await resolver.getAddress() + (isInnermostOrder ? '01' : '00') + trim0x(additionalDataForSettlement), target: await resolver.getAddress(), @@ -111,6 +88,7 @@ async function buildAuctionDetails({ let details = ethers.solidityPacked( ['uint24', 'uint32', 'uint32', 'uint24', 'uint24'], [gasBumpEstimate, gasPriceEstimate, startTime + delay, duration, initialRateBump], ); + details += trim0x(ethers.solidityPacked(['uint8'], [points.length])); for (let i = 0; i < points.length; i++) { details += trim0x(ethers.solidityPacked(['uint24', 'uint16'], [points[i][0], points[i][1]])); } @@ -121,5 +99,4 @@ module.exports = { expBase, buildAuctionDetails, buildCalldataForOrder, - buildExtensionsBitmapData, }; diff --git a/yarn.lock b/yarn.lock index b641a0c..a875105 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21,9 +21,9 @@ "@1inch/token-plugins" "1.3.0" "@openzeppelin/contracts" "5.0.1" -"@1inch/limit-order-protocol-contract@https://github.com/1inch/limit-order-protocol.git#fec2379": +"@1inch/limit-order-protocol-contract@https://github.com/1inch/limit-order-protocol.git#a27230": version "4.2.0" - resolved "https://github.com/1inch/limit-order-protocol.git#fec23799007523be80eabc2341a0c3159b479323" + resolved "https://github.com/1inch/limit-order-protocol.git#a27230667f09252a8e37c6c0b4f479919e26e607" dependencies: "@1inch/solidity-utils" "6.0.0" "@chainlink/contracts" "0.8.0" From 131ac48afed2c90bd5d304c7ccc5cc5257de55cc Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Thu, 21 Nov 2024 16:13:32 +0400 Subject: [PATCH 03/11] fix last test --- test/Settlement.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Settlement.js b/test/Settlement.js index 850ce40..5a95570 100644 --- a/test/Settlement.js +++ b/test/Settlement.js @@ -674,19 +674,19 @@ describe('Settlement', function () { await expect(txn).to.changeTokenBalances(weth, [owner, alice], [ether('-0.12'), ether('0.12')]); }); - it.skip('set auctionDuration', async function () { + it('set auctionDuration', async function () { const dataFormFixture = await loadFixture(initContractsForSettlement); - const auction = await buildAuctionDetails({ startTime: (await time.latest()), duration: 900, initialRateBump: 1000000n }); + const auction = await buildAuctionDetails({ startTime: (await time.latest()) - 448, duration: 900, initialRateBump: 1000000n }); const setupData = { ...dataFormFixture, auction }; const { contracts: { dai, weth, resolver }, accounts: { owner, alice }, } = setupData; - await time.increaseTo(await time.latest() + 1); const fillOrderToData = await prepareSingleOrder({ setupData, + targetTakingAmount: ether('0.105'), }); const txn = await resolver.settleOrders(fillOrderToData); From 0b3e2c2d12e2801fd6757068a038d73a9f7b1182 Mon Sep 17 00:00:00 2001 From: SevenSwen Date: Thu, 21 Nov 2024 19:21:56 +0700 Subject: [PATCH 04/11] remove excess calculation --- test/Settlement.js | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/test/Settlement.js b/test/Settlement.js index 5a95570..2b6e6ab 100644 --- a/test/Settlement.js +++ b/test/Settlement.js @@ -503,25 +503,8 @@ describe('Settlement', function () { contracts: { dai, weth, resolver }, accounts: { owner, alice }, others: { abiCoder }, - auction: { startTime, delay, duration, initialRateBump }, } = setupData; - let actualTakingAmount = targetTakingAmount; - if (actualTakingAmount === 0n) { - actualTakingAmount = ether('0.1'); - const ts = await time.latest(); - // TODO: avoid this shit (as well as any other computations in tests) - if (ts < startTime + delay + duration) { - // actualTakingAmount = actualTakingAmount * ( - // _BASE_POINTS + initialRateBump * (startTime + delay + duration - currentTimestamp) / duration - // ) / _BASE_POINTS - const minDuration = startTime + delay + duration - ts > duration ? duration : startTime + delay + duration - ts - 3; - actualTakingAmount = - (actualTakingAmount * (10000000n + BigInt(initialRateBump) * BigInt(minDuration) / BigInt(duration))) / - 10000000n; - } - } - const resolverCalldata = abiCoder.encode( ['address[]', 'bytes[]'], [ @@ -530,7 +513,7 @@ describe('Settlement', function () { weth.interface.encodeFunctionData('transferFrom', [ owner.address, await resolver.getAddress(), - actualTakingAmount, + targetTakingAmount, ]), ], ], @@ -551,10 +534,10 @@ describe('Settlement', function () { additionalDataForSettlement: resolverCalldata, isInnermostOrder: true, isMakingAmount: false, - fillingAmount: actualTakingAmount, + fillingAmount: targetTakingAmount, }); - await weth.approve(resolver, actualTakingAmount); + await weth.approve(resolver, targetTakingAmount); return fillOrderToData; }; @@ -567,7 +550,10 @@ describe('Settlement', function () { accounts: { owner, alice }, } = setupData; - const fillOrderToData = await prepareSingleOrder({ setupData }); + const fillOrderToData = await prepareSingleOrder({ + setupData, + targetTakingAmount: ether('0.11'), + }); const txn = await resolver.settleOrders(fillOrderToData); await expect(txn).to.changeTokenBalances(dai, [resolver, alice], [ether('100'), ether('-100')]); @@ -667,7 +653,10 @@ describe('Settlement', function () { accounts: { owner, alice }, } = setupData; - const fillOrderToData = await prepareSingleOrder({ setupData }); + const fillOrderToData = await prepareSingleOrder({ + setupData, + targetTakingAmount: ether('0.12'), + }); const txn = await resolver.settleOrders(fillOrderToData); await expect(txn).to.changeTokenBalances(dai, [resolver, alice], [ether('100'), ether('-100')]); From f12ad7063e70c870df59d121e7e857bc8b33cf87 Mon Sep 17 00:00:00 2001 From: SevenSwen Date: Thu, 21 Nov 2024 20:44:20 +0700 Subject: [PATCH 05/11] use setNextBlockTimestamp in time-dependent tests --- test/Settlement.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/test/Settlement.js b/test/Settlement.js index 2b6e6ab..c95c06c 100644 --- a/test/Settlement.js +++ b/test/Settlement.js @@ -541,9 +541,10 @@ describe('Settlement', function () { return fillOrderToData; }; - it('matching order before orderTime has maximal rate bump', async function () { + it('matching order at first second has maximal rate bump', async function () { const dataFormFixture = await loadFixture(initContractsForSettlement); - const auction = await buildAuctionDetails({ delay: 60, initialRateBump: 1000000n }); + const now = await time.latest() + 10; + const auction = await buildAuctionDetails({ startTime: now, delay: 60, initialRateBump: 1000000n }); const setupData = { ...dataFormFixture, auction }; const { contracts: { dai, weth, resolver }, @@ -555,6 +556,7 @@ describe('Settlement', function () { targetTakingAmount: ether('0.11'), }); + await time.setNextBlockTimestamp(now); const txn = await resolver.settleOrders(fillOrderToData); await expect(txn).to.changeTokenBalances(dai, [resolver, alice], [ether('100'), ether('-100')]); await expect(txn).to.changeTokenBalances(weth, [owner, alice], [ether('-0.11'), ether('0.11')]); @@ -575,8 +577,7 @@ describe('Settlement', function () { setupData, }); - await timeIncreaseTo(setupData.auction.startTime + 239); - + await time.setNextBlockTimestamp(setupData.auction.startTime + 240); const txn = await resolver.settleOrders(fillOrderToData); await expect(txn).to.changeTokenBalances(dai, [resolver, alice], [ether('100'), ether('-100')]); await expect(txn).to.changeTokenBalances(weth, [owner, alice], [ether('-0.109'), ether('0.109')]); @@ -596,8 +597,7 @@ describe('Settlement', function () { setupData, }); - await timeIncreaseTo(setupData.auction.startTime + 240 / 2 - 1); - + await time.setNextBlockTimestamp(setupData.auction.startTime + 240 / 2); const txn = await resolver.settleOrders(fillOrderToData); await expect(txn).to.changeTokenBalances(dai, [resolver, alice], [ether('100'), ether('-100')]); await expect(txn).to.changeTokenBalances(weth, [owner, alice], [ether('-0.1095'), ether('0.1095')]); @@ -616,8 +616,8 @@ describe('Settlement', function () { targetTakingAmount: ether('0.106'), setupData, }); - await timeIncreaseTo(setupData.auction.startTime + 759); + await time.setNextBlockTimestamp(setupData.auction.startTime + 760); const txn = await resolver.settleOrders(fillOrderToData); await expect(txn).to.changeTokenBalances(dai, [resolver, alice], [ether('100'), ether('-100')]); await expect(txn).to.changeTokenBalances(weth, [owner, alice], [ether('-0.106'), ether('0.106')]); @@ -636,8 +636,8 @@ describe('Settlement', function () { targetTakingAmount: ether('0.103'), setupData, }); - await timeIncreaseTo(setupData.auction.startTime + 859); + await time.setNextBlockTimestamp(setupData.auction.startTime + 860); const txn = await resolver.settleOrders(fillOrderToData); await expect(txn).to.changeTokenBalances(dai, [resolver, alice], [ether('100'), ether('-100')]); await expect(txn).to.changeTokenBalances(weth, [owner, alice], [ether('-0.103'), ether('0.103')]); @@ -646,7 +646,8 @@ describe('Settlement', function () { it('set initial rate', async function () { const dataFormFixture = await loadFixture(initContractsForSettlement); - const auction = await buildAuctionDetails({ delay: 60, initialRateBump: 2000000n }); + const now = await time.latest() + 10; + const auction = await buildAuctionDetails({ startTime: now, delay: 60, initialRateBump: 2000000n }); const setupData = { ...dataFormFixture, auction }; const { contracts: { dai, weth, resolver }, @@ -658,6 +659,7 @@ describe('Settlement', function () { targetTakingAmount: ether('0.12'), }); + await time.setNextBlockTimestamp(now); const txn = await resolver.settleOrders(fillOrderToData); await expect(txn).to.changeTokenBalances(dai, [resolver, alice], [ether('100'), ether('-100')]); await expect(txn).to.changeTokenBalances(weth, [owner, alice], [ether('-0.12'), ether('0.12')]); @@ -665,8 +667,8 @@ describe('Settlement', function () { it('set auctionDuration', async function () { const dataFormFixture = await loadFixture(initContractsForSettlement); - - const auction = await buildAuctionDetails({ startTime: (await time.latest()) - 448, duration: 900, initialRateBump: 1000000n }); + const now = await time.latest() + 10; + const auction = await buildAuctionDetails({ startTime: now - 450, duration: 900, initialRateBump: 1000000n }); const setupData = { ...dataFormFixture, auction }; const { contracts: { dai, weth, resolver }, @@ -678,6 +680,7 @@ describe('Settlement', function () { targetTakingAmount: ether('0.105'), }); + await time.setNextBlockTimestamp(now); const txn = await resolver.settleOrders(fillOrderToData); await expect(txn).to.changeTokenBalances(dai, [resolver, alice], [ether('100'), ether('-100')]); await expect(txn).to.changeTokenBalances(weth, [owner, alice], [ether('-0.105'), ether('0.105')]); From 82f0a25c969170f710825ce6aa6920062adbde88 Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Tue, 26 Nov 2024 13:25:25 +0400 Subject: [PATCH 06/11] fix natspec --- contracts/SimpleSettlement.sol | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/contracts/SimpleSettlement.sol b/contracts/SimpleSettlement.sol index faf1de9..7a78aaa 100644 --- a/contracts/SimpleSettlement.sol +++ b/contracts/SimpleSettlement.sol @@ -142,12 +142,17 @@ contract SimpleSettlement is FeeTaker { } /** - * @notice See {IPostInteraction-postInteraction}. - * @dev Takes the fee in taking tokens and transfers the rest to the maker. - * `whitelistData` consists of: + * @dev Validates whether the taker is whitelisted. + * @param whitelistData Whitelist data is a tightly packed struct of the following format: + * ``` * 4 bytes - allowed time - * 1 byte - whitelist size + * 1 byte - size of the whitelist * (bytes12)[N] — taker whitelist + * ``` + * Only 10 lowest bytes of the address are used for comparison. + * @param taker The taker address to check. + * @return isWhitelisted Whether the taker is whitelisted. + * @return tail Remaining calldata. */ function _isWhitelistedPostInteractionImpl(bytes calldata whitelistData, address taker) internal view override returns (bool isWhitelisted, bytes calldata tail) { unchecked { From 3cfbbdbaa2c5e2cc26aded0bcc4d78cdd012e98e Mon Sep 17 00:00:00 2001 From: Gleb Alekseev Date: Tue, 3 Dec 2024 18:46:24 +0700 Subject: [PATCH 07/11] Update test/MeasureGas.js Co-authored-by: Xenia <94478708+byshape@users.noreply.github.com> --- test/MeasureGas.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/MeasureGas.js b/test/MeasureGas.js index 4dd8d81..ba11523 100644 --- a/test/MeasureGas.js +++ b/test/MeasureGas.js @@ -23,7 +23,6 @@ describe('MeasureGas', function () { const { dai, weth, accessToken, lopv3, lopv4 } = await deploySwapTokens(); await dai.transfer(alice, ether('100')); - // await inch.mint(owner, ether('100')); await weth.deposit({ value: ether('1') }); await weth.connect(alice).deposit({ value: ether('1') }); From b69b5a3eabb8b716d09d60d8d9ecd9f2eb5f840b Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Thu, 12 Dec 2024 00:31:40 +0400 Subject: [PATCH 08/11] bump version --- package.json | 2 +- yarn.lock | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 23a65c9..c20f763 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "@1inch/delegating": "1.1.0", - "@1inch/limit-order-protocol-contract": "https://github.com/1inch/limit-order-protocol.git#a27230", + "@1inch/limit-order-protocol-contract": "4.2.1", "@1inch/solidity-utils": "6.0.0", "@1inch/st1inch": "2.2.0", "@openzeppelin/contracts": "5.1.0" diff --git a/yarn.lock b/yarn.lock index a875105..825fadd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21,9 +21,10 @@ "@1inch/token-plugins" "1.3.0" "@openzeppelin/contracts" "5.0.1" -"@1inch/limit-order-protocol-contract@https://github.com/1inch/limit-order-protocol.git#a27230": - version "4.2.0" - resolved "https://github.com/1inch/limit-order-protocol.git#a27230667f09252a8e37c6c0b4f479919e26e607" +"@1inch/limit-order-protocol-contract@4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@1inch/limit-order-protocol-contract/-/limit-order-protocol-contract-4.2.1.tgz#255a4bebf84e537b7c862a578748e48e1fbd3461" + integrity sha512-a8ST22sjxMiA9uCHWc+2OAdlTqWps2nJMHhgFi4e2VfwqcOf4JoIJGxwEHFU1fFuOq3xMyxSZONinmyFsINYWg== dependencies: "@1inch/solidity-utils" "6.0.0" "@chainlink/contracts" "0.8.0" From e5aae6e5936bf82fcbff5e84e061c3c441730ced Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Thu, 12 Dec 2024 00:37:43 +0400 Subject: [PATCH 09/11] fix modifier --- contracts/mocks/GasBumpChecker.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/mocks/GasBumpChecker.sol b/contracts/mocks/GasBumpChecker.sol index 7798fd4..4012113 100644 --- a/contracts/mocks/GasBumpChecker.sol +++ b/contracts/mocks/GasBumpChecker.sol @@ -20,7 +20,7 @@ contract GasBumpChecker is SimpleSettlement { uint256 remainingMakingAmount, bytes calldata extraData, uint256 expectedResult - ) external { + ) external view { uint256 res = this.getTakingAmount( order, extension, From 955f3a9ea2829058c43f15f583245a61256505d3 Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Thu, 12 Dec 2024 00:49:11 +0400 Subject: [PATCH 10/11] fix --- test/GasBump.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/GasBump.js b/test/GasBump.js index d71cf50..2b18f50 100644 --- a/test/GasBump.js +++ b/test/GasBump.js @@ -49,7 +49,7 @@ describe('GasBump', function () { async function testGetTakingAmount(checker, order, extensions, basefee, result) { await network.provider.send('hardhat_setNextBlockBaseFeePerGas', ['0x' + basefee.toString(16)]); - await checker.testGetTakingAmount( + await checker.testGetTakingAmount.send( order, '0x', constants.ZERO_BYTES32, constants.ZERO_ADDRESS, ether('10'), ether('10'), '0x' + extensions.takingAmountData.substring(42), result, { gasPrice: basefee }, ); } From 5f11426929316373454d8a8819400b848cf54945 Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Thu, 12 Dec 2024 00:50:01 +0400 Subject: [PATCH 11/11] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f9bae33..f4cce1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@1inch/limit-order-settlement", - "version": "2.1.1", + "version": "3.0.0", "description": "1inch Limit Order Settlement", "repository": { "type": "git",