From 2f845a695dfd36a7ee777521b7b02b1994d09213 Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Fri, 6 Oct 2023 18:51:25 +0400 Subject: [PATCH 1/6] assembly _isWhitelisted --- contracts/SettlementExtension.sol | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/contracts/SettlementExtension.sol b/contracts/SettlementExtension.sol index e08ac8b4..21a9fceb 100644 --- a/contracts/SettlementExtension.sol +++ b/contracts/SettlementExtension.sol @@ -159,21 +159,21 @@ contract SettlementExtension is IPostInteraction, IAmountGetter, FeeBankCharger /// (bytes10,bytes2)[N] resolversAddressesAndTimeDeltas; /// } - function _isWhitelisted(bytes calldata whitelist, address resolver) private view returns (bool) { - uint256 allowedTime = uint32(bytes4(whitelist[0:4])); // initially set to auction start time - whitelist = whitelist[4:]; - uint256 whitelistSize = whitelist.length / 12; - uint80 maskedResolverAddress = uint80(uint160(resolver) & _RESOLVER_ADDRESS_MASK); - 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; + function _isWhitelisted(bytes calldata whitelist, address resolver) private view returns (bool result) { + // solhint-disable-next-line no-inline-assembly + assembly ("memory-safe") { + let allowedTime := shr(224, calldataload(whitelist.offset)) + let maskedResolverAddress := and(resolver, _RESOLVER_ADDRESS_MASK) + let cdEnd := add(whitelist.offset, whitelist.length) + for { let cdPtr := add(whitelist.offset, 4) } lt(cdPtr, cdEnd) { cdPtr := add(cdPtr, 12) } { + let data := calldataload(cdPtr) + let whitelistedAddress := shr(176, data) + allowedTime := add(allowedTime, and(shr(160, data), 0xffff)) + if eq(maskedResolverAddress, whitelistedAddress) { + result := sub(1, gt(allowedTime, timestamp())) + break + } } - whitelist = whitelist[12:]; } - return false; } } From 4475de50f5f2fad741e90c1b5cc8ec97c9ff3ac8 Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Fri, 6 Oct 2023 19:37:41 +0400 Subject: [PATCH 2/6] assembly _parseFeeData --- contracts/SettlementExtension.sol | 35 +++++++++++++++++++------------ 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/contracts/SettlementExtension.sol b/contracts/SettlementExtension.sol index 21a9fceb..c4c33534 100644 --- a/contracts/SettlementExtension.sol +++ b/contracts/SettlementExtension.sol @@ -138,20 +138,29 @@ contract SettlementExtension is IPostInteraction, IAmountGetter, FeeBankCharger uint256 actualMakingAmount, uint256 actualTakingAmount ) private pure returns (uint256 resolverFee, address integrator, uint256 integrationFee, bytes calldata whitelist) { - bytes1 feeType = extraData[0]; - extraData = extraData[1:]; - if (feeType & 0x01 == 0x01) { - // resolverFee enabled - resolverFee = uint256(uint32(bytes4(extraData[:4]))) * _ORDER_FEE_BASE_POINTS * actualMakingAmount / orderMakingAmount; - extraData = extraData[4:]; - } - if (feeType & 0x02 == 0x02) { - // integratorFee enabled - integrator = address(bytes20(extraData[:20])); - integrationFee = actualTakingAmount * uint256(uint32(bytes4(extraData[20:24]))) / _TAKING_FEE_BASE; - extraData = extraData[24:]; + // solhint-disable-next-line no-inline-assembly + assembly ("memory-safe") { + let firstWord := calldataload(extraData.offset) + let feeType := shr(248, firstWord) + firstWord := shl(8, firstWord) + // let extraDataEnd := add(extraData.offset, extraData.length) + // extraData.offset := add(extraData.offset, 1) + whitelist.offset := add(extraData.offset, 1) + if and(feeType, 0x01) { + // resolverFee enabled + resolverFee := div(mul(mul(shr(224, firstWord), _ORDER_FEE_BASE_POINTS), actualMakingAmount), orderMakingAmount) + firstWord := shl(32, firstWord) + whitelist.offset := add(whitelist.offset, 4) + } + if and(feeType, 0x02) { + // integratorFee enabled + integrator := shr(96, firstWord) + firstWord := shl(160, firstWord) + integrationFee := div(mul(actualTakingAmount, shr(224, firstWord)), _TAKING_FEE_BASE) + whitelist.offset := add(whitelist.offset, 24) + } + whitelist.length := sub(extraData.length, sub(whitelist.offset, extraData.offset)) } - whitelist = extraData; } /// struct WhitelistDetails { From b3826386828f06bbc8e69f4a4272a4ab84de48c8 Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Fri, 6 Oct 2023 19:38:01 +0400 Subject: [PATCH 3/6] assembly _getRateBump --- contracts/SettlementExtension.sol | 77 ++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/contracts/SettlementExtension.sol b/contracts/SettlementExtension.sol index c4c33534..0f0fe66b 100644 --- a/contracts/SettlementExtension.sol +++ b/contracts/SettlementExtension.sol @@ -77,33 +77,64 @@ contract SettlementExtension is IPostInteraction, IAmountGetter, FeeBankCharger return Math.ceilDiv(order.takingAmount * makingAmount * (_BASE_POINTS + rateBump), _BASE_POINTS * order.makingAmount); } - function _getRateBump(bytes calldata auctionDetails) private view returns (uint256) { - uint256 auctionStartTime = uint32(bytes4(auctionDetails[0:4])); - uint256 auctionFinishTime = auctionStartTime + uint24(bytes3(auctionDetails[4:7])); - uint256 initialRateBump = uint24(bytes3(auctionDetails[7:10])); - - if (block.timestamp <= auctionStartTime) { - return initialRateBump; - } else if (block.timestamp >= auctionFinishTime) { - return 0; // Means 0% bump - } + function _getRateBump(bytes calldata auctionDetails) private view returns (uint256 result) { + // solhint-disable-next-line no-inline-assembly + assembly ("memory-safe") { + function timeWeightedAvg(t1, v1, t2, v2) -> avg { + avg := div(add(mul(sub(timestamp(), t1), v2), mul(sub(t2, timestamp()), v1)), sub(t2, t1)) + } - auctionDetails = auctionDetails[10:]; - uint256 pointsSize = auctionDetails.length / 5; - uint256 currentPointTime = auctionStartTime; - uint256 currentRateBump = initialRateBump; + let firstWord := calldataload(auctionDetails.offset) + let auctionStartTime := shr(224, firstWord) + firstWord := shl(32, firstWord) + let auctionFinishTime := add(auctionStartTime, shr(232, firstWord)) + firstWord := shl(24, firstWord) + let initialRateBump := shr(232, firstWord) - for (uint256 i = 0; i < pointsSize; i++) { - uint256 nextRateBump = uint24(bytes3(auctionDetails[:3])); - uint256 nextPointTime = currentPointTime + uint16(bytes2(auctionDetails[3:5])); - if (block.timestamp <= nextPointTime) { - return ((block.timestamp - currentPointTime) * nextRateBump + (nextPointTime - block.timestamp) * currentRateBump) / (nextPointTime - currentPointTime); + switch gt(timestamp(), auctionStartTime) + case 0 { + result := initialRateBump + } + default { + switch lt(timestamp(), auctionFinishTime) + case 0 { + result := 0 + } + default { + let cdPtr := add(auctionDetails.offset, 10) + let cdEnd := add(auctionDetails.offset, auctionDetails.length) + let currentPointTime := auctionStartTime + let currentRateBump := initialRateBump + for { } lt(cdPtr, cdEnd) { cdPtr := add(cdPtr, 5) } { + let data := calldataload(cdPtr) + let nextRateBump := shr(232, data) + data := shl(32, data) + let nextPointTime := add(currentPointTime, shr(240, data)) + switch gt(timestamp(), nextPointTime) + case 0 { + result := div( + add( + mul(sub(timestamp(), currentPointTime), nextRateBump), + mul(sub(nextPointTime, timestamp()), currentRateBump) + ), + sub(nextPointTime, currentPointTime) + ) + break + } + default { + currentPointTime := nextPointTime + currentRateBump := nextRateBump + } + } + if eq(cdPtr, cdEnd) { + result := div( + mul(sub(auctionFinishTime, timestamp()), currentRateBump), + sub(auctionFinishTime, currentPointTime) + ) + } + } } - currentRateBump = nextRateBump; - currentPointTime = nextPointTime; } - - return (auctionFinishTime - block.timestamp) * currentRateBump / (auctionFinishTime - currentPointTime); } function postInteraction( From a17e744e38e55fead733714cee48270bf83dd728 Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Wed, 1 Nov 2023 15:17:04 +0400 Subject: [PATCH 4/6] move comment to proper place --- contracts/SettlementExtension.sol | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/contracts/SettlementExtension.sol b/contracts/SettlementExtension.sol index 0f0fe66b..95e6b68e 100644 --- a/contracts/SettlementExtension.sol +++ b/contracts/SettlementExtension.sol @@ -44,13 +44,6 @@ contract SettlementExtension is IPostInteraction, IAmountGetter, FeeBankCharger _limitOrderProtocol = limitOrderProtocol; } - /// struct AuctionDetails { - /// bytes4 auctionStartTime; - /// bytes3 auctionDuration; - /// bytes3 initialRateBump; - /// (bytes3,bytes2)[N] pointsAndTimeDeltas; - /// } - function getMakingAmount( IOrderMixin.Order calldata order, bytes calldata /* extension */, @@ -77,6 +70,13 @@ contract SettlementExtension is IPostInteraction, IAmountGetter, FeeBankCharger return Math.ceilDiv(order.takingAmount * makingAmount * (_BASE_POINTS + rateBump), _BASE_POINTS * order.makingAmount); } + /// struct AuctionDetails { + /// bytes4 auctionStartTime; + /// bytes3 auctionDuration; + /// bytes3 initialRateBump; + /// (bytes3,bytes2)[N] pointsAndTimeDeltas; + /// } + function _getRateBump(bytes calldata auctionDetails) private view returns (uint256 result) { // solhint-disable-next-line no-inline-assembly assembly ("memory-safe") { From 18eff8317cbd1a2efcc0f32473935dda348ccc1d Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Wed, 1 Nov 2023 16:37:52 +0400 Subject: [PATCH 5/6] fix --- contracts/SettlementExtension.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/SettlementExtension.sol b/contracts/SettlementExtension.sol index 2bb2badf..fefbbac1 100644 --- a/contracts/SettlementExtension.sol +++ b/contracts/SettlementExtension.sol @@ -108,7 +108,7 @@ contract SettlementExtension is IPostInteraction, IAmountGetter, FeeBankCharger for { } lt(cdPtr, cdEnd) { cdPtr := add(cdPtr, 5) } { let data := calldataload(cdPtr) let nextRateBump := shr(232, data) - data := shl(32, data) + data := shl(24, data) let nextPointTime := add(currentPointTime, shr(240, data)) switch gt(timestamp(), nextPointTime) case 0 { From a2d6d0eee758711af1437437c6be92e6b1f25864 Mon Sep 17 00:00:00 2001 From: Mikhail Melnik Date: Wed, 1 Nov 2023 17:54:51 +0400 Subject: [PATCH 6/6] add extension gas check --- test/MeasureGas.js | 65 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/test/MeasureGas.js b/test/MeasureGas.js index 662bf32b..e17bd597 100644 --- a/test/MeasureGas.js +++ b/test/MeasureGas.js @@ -3,8 +3,9 @@ const hre = require('hardhat'); const path = require('path'); const { ethers } = hre; const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { time, expect, ether, trim0x, deployContract } = require('@1inch/solidity-utils'); +const { constants, time, expect, ether, trim0x, deployContract } = require('@1inch/solidity-utils'); const { deploySwapTokens, getChainId } = require('./helpers/fixtures'); +const { buildAuctionDetails } = require('./helpers/fusionUtils'); const { buildOrder, signOrder, buildTakerTraits, buildMakerTraits } = require('@1inch/limit-order-protocol-contract/test/helpers/orderUtils'); const settlementV1Utils = require('@1inch/limit-order-settlement-v1/test/helpers/orderUtils'); @@ -224,6 +225,68 @@ describe('MeasureGas', function () { }); }); + describe('Extension check', function () { + it('post interaction', async function () { + const { contracts: { dai, weth }, accounts: { owner } } = await loadFixture(initContractsAndApproves); + const settlementExtension = await deployContract('SettlementExtension', [owner.address, weth.address]); + const currentTime = (await time.latest()) - time.duration.minutes(1); + + const postInteractionData = ethers.utils.solidityPack( + ['uint8', 'uint32', 'bytes10', 'uint16', 'bytes10', 'uint16', 'bytes10', 'uint16', 'bytes10', 'uint16', 'bytes10', 'uint16'], + [ + 0, currentTime, + '0x' + weth.address.substring(22), 0, + '0x' + weth.address.substring(22), 0, + '0x' + weth.address.substring(22), 0, + '0x' + owner.address.substring(22), 0, + '0x' + weth.address.substring(22), 0, + ], + ); + + const order = buildOrder({ + maker: owner.address, + makerAsset: dai.address, + takerAsset: weth.address, + makingAmount: ether('10'), + takingAmount: ether('1'), + makerTraits: buildMakerTraits(), + }, { + postInteraction: settlementExtension.address + trim0x(postInteractionData), + }); + + await settlementExtension.postInteraction(order, '0x', constants.ZERO_BYTES32, owner.address, ether('10'), ether('1'), ether('10'), postInteractionData); + }); + + it('making getter', async function () { + const { contracts: { dai, weth, settlementExtension }, accounts: { owner } } = await loadFixture(initContractsAndApproves); + + const currentTime = await time.latest(); + const { details } = await buildAuctionDetails({ + startTime: currentTime - time.duration.minutes(5), + initialRateBump: 900000, + points: [[800000, 100], [700000, 100], [600000, 100], [500000, 100], [400000, 100]], + }); + + const order = buildOrder({ + maker: owner.address, + makerAsset: dai.address, + takerAsset: weth.address, + makingAmount: ether('10'), + takingAmount: ether('1'), + makerTraits: buildMakerTraits(), + }, { + makingAmountData: settlementExtension.address + trim0x(details), + takingAmountData: settlementExtension.address + trim0x(details), + postInteraction: settlementExtension.address + trim0x(ethers.utils.solidityPack( + ['uint8', 'uint32', 'bytes10', 'uint16'], [0, currentTime, '0x' + owner.address.substring(22), 0], + )), + }); + + const txn = await settlementExtension.populateTransaction.getMakingAmount(order, '0x', constants.ZERO_BYTES32, constants.ZERO_ADDRESS, ether('1'), ether('10'), details); + await owner.sendTransaction(txn); + }); + }); + 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);