diff --git a/.gas-snapshot b/.gas-snapshot index ba5d943..9db71ab 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,31 +1,32 @@ -EscrowFactoryTest:testFuzz_DeployCloneForMaker(bytes32,uint56,uint56) (runs: 256, μ: 317761, ~: 321353) -EscrowFactoryTest:testFuzz_DeployCloneForTaker(bytes32,uint56) (runs: 256, μ: 243976, ~: 247826) -EscrowFactoryTest:test_NoDeploymentForNotResolver() (gas: 112084) -EscrowFactoryTest:test_NoInsufficientBalanceDeploymentForMaker() (gas: 281570) -EscrowFactoryTest:test_NoInsufficientBalanceDeploymentForTaker() (gas: 30637) -EscrowFactoryTest:test_NoInsufficientBalanceNativeDeploymentForMaker() (gas: 241684) -EscrowFactoryTest:test_NoUnsafeDeploymentForTaker() (gas: 38019) -EscrowTest:test_CancelDst() (gas: 230915) -EscrowTest:test_CancelPublicSrc() (gas: 319805) -EscrowTest:test_CancelResolverSrc() (gas: 322942) -EscrowTest:test_NoAnyoneCancelDuringResolverCancelSrc() (gas: 320091) -EscrowTest:test_NoCancelByAnyoneDst() (gas: 238401) -EscrowTest:test_NoCancelDuringPublicWithdrawalDst() (gas: 238345) -EscrowTest:test_NoCancelDuringResolverWithdrawalDst() (gas: 238071) -EscrowTest:test_NoCancelDuringWithdrawalSrc() (gas: 319437) -EscrowTest:test_NoFailedNativeTokenTransferCancelDst() (gas: 249340) -EscrowTest:test_NoFailedNativeTokenTransferCancelSrc() (gas: 335616) -EscrowTest:test_NoFailedNativeTokenTransferWithdrawalDst() (gas: 271094) -EscrowTest:test_NoFailedNativeTokenTransferWithdrawalSrc() (gas: 353692) -EscrowTest:test_NoWithdrawalByAnyone() (gas: 319084) -EscrowTest:test_NoWithdrawalByNonResolverDst() (gas: 238110) -EscrowTest:test_NoWithdrawalDuringFinalityLockDst() (gas: 237218) -EscrowTest:test_NoWithdrawalDuringFinalityLockSrc() (gas: 319954) -EscrowTest:test_NoWithdrawalWithWrongSecretDst() (gas: 239029) -EscrowTest:test_NoWithdrawalWithWrongSecretSrc() (gas: 320533) -EscrowTest:test_WithdrawByAnyoneDst() (gas: 255262) -EscrowTest:test_WithdrawByResolverDst() (gas: 255974) -EscrowTest:test_WithdrawByResolverPublicDst() (gas: 255625) -EscrowTest:test_WithdrawSrc() (gas: 340100) -IntegrationEscrowFactoryTest:testFuzz_DeployCloneForMakerInt(bytes32,uint56,uint56) (runs: 256, μ: 398108, ~: 401576) -IntegrationEscrowFactoryTest:test_NoInsufficientBalanceDeploymentForMakerInt() (gas: 382262) \ No newline at end of file +EscrowFactoryTest:testFuzz_DeployCloneForMaker(bytes32,uint56,uint56) (runs: 256, μ: 234194, ~: 237909) +EscrowFactoryTest:testFuzz_DeployCloneForTaker(bytes32,uint56) (runs: 256, μ: 180412, ~: 183236) +EscrowFactoryTest:test_NoDeploymentForNotResolver() (gas: 103456) +EscrowFactoryTest:test_NoInsufficientBalanceDeploymentForMaker() (gas: 185356) +EscrowFactoryTest:test_NoInsufficientBalanceDeploymentForTaker() (gas: 27715) +EscrowFactoryTest:test_NoInsufficientBalanceNativeDeploymentForMaker() (gas: 151006) +EscrowFactoryTest:test_NoUnsafeDeploymentForTaker() (gas: 34980) +EscrowTest:test_CancelDst() (gas: 165076) +EscrowTest:test_CancelPublicSrc() (gas: 229537) +EscrowTest:test_CancelResolverSrc() (gas: 230646) +EscrowTest:test_NoAnyoneCancelDuringResolverCancelSrc() (gas: 227896) +EscrowTest:test_NoCancelByAnyoneDst() (gas: 172822) +EscrowTest:test_NoCancelDuringPublicWithdrawalDst() (gas: 170625) +EscrowTest:test_NoCancelDuringResolverWithdrawalDst() (gas: 168351) +EscrowTest:test_NoCancelDuringWithdrawalSrc() (gas: 225333) +EscrowTest:test_NoFailedNativeTokenTransferCancelDst() (gas: 183501) +EscrowTest:test_NoFailedNativeTokenTransferCancelSrc() (gas: 245348) +EscrowTest:test_NoFailedNativeTokenTransferWithdrawalDst() (gas: 203410) +EscrowTest:test_NoFailedNativeTokenTransferWithdrawalSrc() (gas: 259494) +EscrowTest:test_NoWithdrawalByAnyone() (gas: 223044) +EscrowTest:test_NoWithdrawalByNonResolverDst() (gas: 168503) +EscrowTest:test_NoWithdrawalDuringFinalityLockDst() (gas: 165563) +EscrowTest:test_NoWithdrawalDuringFinalityLockSrc() (gas: 223873) +EscrowTest:test_NoWithdrawalWithWrongSecretDst() (gas: 169323) +EscrowTest:test_NoWithdrawalWithWrongSecretSrc() (gas: 226329) +EscrowTest:test_WithdrawByAnyoneDst() (gas: 187575) +EscrowTest:test_WithdrawByResolverDst() (gas: 186265) +EscrowTest:test_WithdrawByResolverPublicDst() (gas: 187938) +EscrowTest:test_WithdrawSrc() (gas: 245902) +IntegrationEscrowFactoryTest:testFuzz_DeployCloneForMakerInt(bytes32,uint56,uint56) (runs: 256, μ: 296280, ~: 299872) +IntegrationEscrowFactoryTest:test_NoInsufficientBalanceDeploymentForMakerInt() (gas: 281514) +TimelocksLibTest:test_getStartTimestamps() (gas: 15326) \ No newline at end of file diff --git a/contracts/Escrow.sol b/contracts/Escrow.sol index 91d44f2..ce96fc8 100644 --- a/contracts/Escrow.sol +++ b/contracts/Escrow.sol @@ -7,6 +7,8 @@ import { Clone } from "clones-with-immutable-args/Clone.sol"; import { SafeERC20 } from "solidity-utils/libraries/SafeERC20.sol"; +import { PackedAddresses, PackedAddressesLib } from "./libraries/PackedAddressesLib.sol"; +import { Timelocks, TimelocksLib } from "./libraries/TimelocksLib.sol"; import { IEscrow } from "./interfaces/IEscrow.sol"; /** @@ -14,11 +16,13 @@ import { IEscrow } from "./interfaces/IEscrow.sol"; * @notice Contract to initially lock funds on both chains and then unlock with verification of the secret presented. * @dev Funds are locked in at the time of contract deployment. On both chains this is done by calling `EscrowFactory` * functions. On the source chain Limit Order Protocol calls the `postInteraction` function and on the destination - * chain taker calls the `createEscrow` function. + * chain taker calls the `createEscrowDst` function. * Withdrawal and cancellation functions for the source and destination chains are implemented separately. */ contract Escrow is Clone, IEscrow { using SafeERC20 for IERC20; + using PackedAddressesLib for PackedAddresses; + using TimelocksLib for Timelocks; /** * @notice See {IEscrow-withdrawSrc}. @@ -27,25 +31,28 @@ contract Escrow is Clone, IEscrow { */ function withdrawSrc(bytes32 secret) external { SrcEscrowImmutables calldata escrowImmutables = srcEscrowImmutables(); - if (msg.sender != escrowImmutables.interactionParams.taker) revert InvalidCaller(); + address taker = escrowImmutables.packedAddresses.taker(); + if (msg.sender != taker) revert InvalidCaller(); - uint256 finalisedTimestamp = escrowImmutables.deployedAt + escrowImmutables.extraDataParams.srcTimelocks.finality; - // Check that it's public withdrawal period. + Timelocks timelocks = escrowImmutables.timelocks; + uint256 deployedAt = escrowImmutables.timelocks.deployedAt(); + + // Check that it's a withdrawal period. if ( - block.timestamp < finalisedTimestamp || - block.timestamp >= finalisedTimestamp + escrowImmutables.extraDataParams.srcTimelocks.withdrawal + block.timestamp < timelocks.srcWithdrawalStart(deployedAt) || + block.timestamp >= timelocks.srcCancellationStart(deployedAt) ) revert InvalidWithdrawalTime(); _checkSecretAndTransfer( secret, - escrowImmutables.extraDataParams.hashlock, - escrowImmutables.interactionParams.taker, - escrowImmutables.interactionParams.srcToken, - escrowImmutables.interactionParams.srcAmount + escrowImmutables.hashlock, + taker, + escrowImmutables.packedAddresses.token(), + escrowImmutables.srcAmount ); // Send the safety deposit to the caller. - (bool success, ) = msg.sender.call{value: escrowImmutables.extraDataParams.srcSafetyDeposit}(""); + (bool success, ) = msg.sender.call{value: escrowImmutables.deposits >> 128}(""); if (!success) revert NativeTokenSendingFailure(); } @@ -56,28 +63,29 @@ contract Escrow is Clone, IEscrow { */ function cancelSrc() external { SrcEscrowImmutables calldata escrowImmutables = srcEscrowImmutables(); - uint256 finalisedTimestamp = escrowImmutables.deployedAt + escrowImmutables.extraDataParams.srcTimelocks.finality; - uint256 cancellationTimestamp = finalisedTimestamp + escrowImmutables.extraDataParams.srcTimelocks.withdrawal; - // Check that it's cancellation period. - if (block.timestamp < cancellationTimestamp) { + Timelocks timelocks = escrowImmutables.timelocks; + uint256 deployedAt = escrowImmutables.timelocks.deployedAt(); + + // Check that it's a cancellation period. + if (block.timestamp < timelocks.srcCancellationStart(deployedAt)) { revert InvalidCancellationTime(); } // Check that the caller is a taker if it's the private cancellation period. if ( - block.timestamp < cancellationTimestamp + escrowImmutables.extraDataParams.srcTimelocks.cancel && - msg.sender != escrowImmutables.interactionParams.taker + block.timestamp < timelocks.srcPubCancellationStart(deployedAt) && + msg.sender != escrowImmutables.packedAddresses.taker() ) { revert InvalidCaller(); } - IERC20(escrowImmutables.interactionParams.srcToken).safeTransfer( - escrowImmutables.interactionParams.maker, - escrowImmutables.interactionParams.srcAmount + IERC20(escrowImmutables.packedAddresses.token()).safeTransfer( + escrowImmutables.packedAddresses.maker(), + escrowImmutables.srcAmount ); // Send the safety deposit to the caller. - (bool success, ) = msg.sender.call{value: escrowImmutables.extraDataParams.srcSafetyDeposit}(""); + (bool success, ) = msg.sender.call{value: escrowImmutables.deposits >> 128}(""); if (!success) revert NativeTokenSendingFailure(); } @@ -88,22 +96,25 @@ contract Escrow is Clone, IEscrow { */ function withdrawDst(bytes32 secret) external { DstEscrowImmutables calldata escrowImmutables = dstEscrowImmutables(); - uint256 finalisedTimestamp = escrowImmutables.deployedAt + escrowImmutables.timelocks.finality; - uint256 publicWithdrawalTimestamp = finalisedTimestamp + escrowImmutables.timelocks.withdrawal; - // Check that it's an withdrawal period. + + uint256 deployedAt = escrowImmutables.timelocks.deployedAt(); + // Check that it's a withdrawal period. if ( - block.timestamp < finalisedTimestamp || - block.timestamp >= publicWithdrawalTimestamp + escrowImmutables.timelocks.publicWithdrawal + block.timestamp < escrowImmutables.timelocks.dstWithdrawalStart(deployedAt) || + block.timestamp >= escrowImmutables.timelocks.dstCancellationStart(deployedAt) ) revert InvalidWithdrawalTime(); // Check that the caller is a taker if it's the private withdrawal period. - if (block.timestamp < publicWithdrawalTimestamp && msg.sender != escrowImmutables.taker) revert InvalidCaller(); + if ( + block.timestamp < escrowImmutables.timelocks.dstPubWithdrawalStart(deployedAt) && + msg.sender != escrowImmutables.packedAddresses.taker() + ) revert InvalidCaller(); _checkSecretAndTransfer( secret, escrowImmutables.hashlock, - escrowImmutables.maker, - escrowImmutables.token, + escrowImmutables.packedAddresses.maker(), + escrowImmutables.packedAddresses.token(), escrowImmutables.amount ); @@ -119,19 +130,18 @@ contract Escrow is Clone, IEscrow { */ function cancelDst() external { DstEscrowImmutables calldata escrowImmutables = dstEscrowImmutables(); - if (msg.sender != escrowImmutables.taker) revert InvalidCaller(); + address taker = escrowImmutables.packedAddresses.taker(); + if (msg.sender != taker) revert InvalidCaller(); - uint256 finalisedTimestamp = escrowImmutables.deployedAt + escrowImmutables.timelocks.finality; // Check that it's a cancellation period. if ( - block.timestamp < - finalisedTimestamp + escrowImmutables.timelocks.withdrawal + escrowImmutables.timelocks.publicWithdrawal + block.timestamp < escrowImmutables.timelocks.dstCancellationStart(escrowImmutables.timelocks.deployedAt()) ) { revert InvalidCancellationTime(); } - IERC20(escrowImmutables.token).safeTransfer( - escrowImmutables.taker, + IERC20(escrowImmutables.packedAddresses.token()).safeTransfer( + taker, escrowImmutables.amount ); diff --git a/contracts/EscrowFactory.sol b/contracts/EscrowFactory.sol index 639bbbd..057132f 100644 --- a/contracts/EscrowFactory.sol +++ b/contracts/EscrowFactory.sol @@ -10,10 +10,10 @@ import { Address, AddressLib } from "solidity-utils/libraries/AddressLib.sol"; import { SafeERC20 } from "solidity-utils/libraries/SafeERC20.sol"; import { ClonesWithImmutableArgs } from "clones-with-immutable-args/ClonesWithImmutableArgs.sol"; -import { IEscrow } from "./interfaces/IEscrow.sol"; +import { PackedAddresses, PackedAddressesLib } from "./libraries/PackedAddressesLib.sol"; +import { Timelocks, TimelocksLib } from "./libraries/TimelocksLib.sol"; import { IEscrowFactory } from "./interfaces/IEscrowFactory.sol"; - /** * @title Escrow Factory contract * @notice Contract to create escrow contracts for cross-chain atomic swap. @@ -21,8 +21,12 @@ import { IEscrowFactory } from "./interfaces/IEscrowFactory.sol"; contract EscrowFactory is IEscrowFactory, SimpleSettlementExtension { using AddressLib for Address; using ClonesWithImmutableArgs for address; + using PackedAddressesLib for PackedAddresses; using SafeERC20 for IERC20; + using TimelocksLib for Timelocks; + uint256 internal constant _EXTRA_DATA_PARAMS_OFFSET = 4; + uint256 internal constant _WHITELIST_OFFSET = 228; // Address of the escrow contract implementation to clone. address public immutable IMPLEMENTATION; @@ -37,6 +41,10 @@ contract EscrowFactory is IEscrowFactory, SimpleSettlementExtension { * to a pre-computed deterministic address of the created escrow. * The external postInteraction function call will be made from the Limit Order Protocol * after all funds have been transferred. See {IPostInteraction-postInteraction}. + * `extraData` consists of: + * - 4 bytes for the fee + * - 7 * 32 bytes for hashlock, packedAddresses (2 * 32), dstChainId, dstToken, deposits and timelocks + * - whitelist */ function _postInteraction( IOrderMixin.Order calldata order, @@ -48,93 +56,85 @@ contract EscrowFactory is IEscrowFactory, SimpleSettlementExtension { uint256 /* remainingMakingAmount */, bytes calldata extraData ) internal override { - uint256 resolverFee = _getResolverFee(uint256(uint32(bytes4(extraData[:4]))), order.makingAmount, makingAmount); - extraData = extraData[4:]; + { + bytes calldata whitelist = extraData[_WHITELIST_OFFSET:]; + if (!_isWhitelisted(whitelist, taker)) revert ResolverIsNotWhitelisted(); + } - bytes calldata extraDataParams = extraData[:352]; - bytes calldata whitelist = extraData[352:]; - - if (!_isWhitelisted(whitelist, taker)) revert ResolverIsNotWhitelisted(); + Timelocks timelocks = Timelocks.wrap( + uint256(bytes32(extraData[_WHITELIST_OFFSET-32:_WHITELIST_OFFSET])) + ).setDeployedAt(block.timestamp); // Prepare immutables for the escrow contract. - bytes memory interactionParams = abi.encode( - order.maker, - taker, - block.chainid, // srcChainId - order.makerAsset.get(), // srcToken - makingAmount, // srcAmount - takingAmount // dstAmount - ); - bytes memory data = abi.encodePacked( - block.timestamp, // deployedAt - interactionParams, - extraDataParams - ); - - // Salt is orderHash - address escrow = _createEscrow(data, orderHash, 0); - uint256 safetyDeposit = abi.decode(extraDataParams, (IEscrow.ExtraDataParams)).srcSafetyDeposit; + // 10 * 32 bytes + bytes memory data = new bytes(0x140); + // solhint-disable-next-line no-inline-assembly + assembly("memory-safe") { + mstore(add(data, 0x20), orderHash) + mstore(add(data, 0x40), makingAmount) // srcAmount + mstore(add(data, 0x60), takingAmount) // dstAmount + // Copy hashlock, packedAddresses, dstChainId, dstToken, deposits: 6 * 32 bytes + calldatacopy(add(data, 0x80), add(extraData.offset, _EXTRA_DATA_PARAMS_OFFSET), 0xc0) + mstore(add(data, 0x140), timelocks) + } + + address escrow = _createEscrow(data, 0); + // 4 bytes for a fee + 3 * 32 bytes for hashlock, dstChainId and dstToken + // srcSafetyDeposit is the first 16 bytes in the `deposits` + uint256 safetyDeposit = uint128(bytes16(extraData[100:116])); if ( escrow.balance < safetyDeposit || - IERC20(order.makerAsset.get()).balanceOf(escrow) < makingAmount + IERC20(order.makerAsset.get()).safeBalanceOf(escrow) < makingAmount ) revert InsufficientEscrowBalance(); + uint256 resolverFee = _getResolverFee(uint256(uint32(bytes4(extraData[:4]))), order.makingAmount, makingAmount); _chargeFee(taker, resolverFee); } /** - * @notice See {IEscrowFactory-createEscrow}. + * @notice See {IEscrowFactory-createEscrowDst}. */ - function createEscrow(DstEscrowImmutablesCreation calldata dstEscrowImmutables) external payable { - if (msg.value < dstEscrowImmutables.safetyDeposit) revert InsufficientEscrowBalance(); + function createEscrowDst(DstEscrowImmutablesCreation calldata dstImmutables) external payable { + if (msg.value < dstImmutables.args.safetyDeposit) revert InsufficientEscrowBalance(); // Check that the escrow cancellation will start not later than the cancellation time on the source chain. if ( - block.timestamp + - dstEscrowImmutables.timelocks.finality + - dstEscrowImmutables.timelocks.withdrawal + - dstEscrowImmutables.timelocks.publicWithdrawal > - dstEscrowImmutables.srcCancellationTimestamp + dstImmutables.args.timelocks.dstCancellationStart(block.timestamp) > + dstImmutables.srcCancellationTimestamp ) revert InvalidCreationTime(); - bytes memory data = abi.encode( - block.timestamp, // deployedAt - dstEscrowImmutables.hashlock, - dstEscrowImmutables.maker, - dstEscrowImmutables.taker, - block.chainid, - dstEscrowImmutables.token, - dstEscrowImmutables.amount, - dstEscrowImmutables.safetyDeposit, - dstEscrowImmutables.timelocks.finality, - dstEscrowImmutables.timelocks.withdrawal, - dstEscrowImmutables.timelocks.publicWithdrawal - ); - bytes32 salt = keccak256(abi.encodePacked(data, msg.sender)); - address escrow = _createEscrow(data, salt, msg.value); - IERC20(dstEscrowImmutables.token).safeTransferFrom( - msg.sender, escrow, dstEscrowImmutables.amount + // 7 * 32 bytes for DstEscrowImmutablesCreation + bytes memory data = new bytes(0xe0); + Timelocks timelocks = dstImmutables.args.timelocks.setDeployedAt(block.timestamp); + // solhint-disable-next-line no-inline-assembly + assembly("memory-safe") { + // Copy DstEscrowImmutablesCreation excluding timelocks + calldatacopy(add(data, 0x20), dstImmutables, 0xc0) + mstore(add(data, 0xe0), timelocks) + } + + address escrow = _createEscrow(data, msg.value); + IERC20(dstImmutables.args.packedAddresses.token()).safeTransferFrom( + msg.sender, escrow, dstImmutables.args.amount ); } /** * @notice See {IEscrowFactory-addressOfEscrow}. */ - function addressOfEscrow(bytes32 salt) public view returns (address) { - return address(uint160(ClonesWithImmutableArgs.addressOfClone3(salt))); + function addressOfEscrow(bytes memory data) public view returns (address) { + return ClonesWithImmutableArgs.addressOfClone2(IMPLEMENTATION, data); } /** * @notice Creates a new escrow contract with immutable arguments. - * @dev The escrow contract is a proxy clone created using the create3 pattern. + * @dev The escrow contract is a proxy clone created using the create2 pattern. * @param data Encoded immutable args. - * @param salt The salt that influences the contract address in deterministic deployment. * @return clone The address of the created escrow contract. */ function _createEscrow( bytes memory data, - bytes32 salt, uint256 value ) private returns (address clone) { - clone = address(uint160(IMPLEMENTATION.clone3(data, salt, value))); + clone = IMPLEMENTATION.clone2(data, value); } } diff --git a/contracts/interfaces/IEscrow.sol b/contracts/interfaces/IEscrow.sol index d72e261..e2fc7fe 100644 --- a/contracts/interfaces/IEscrow.sol +++ b/contracts/interfaces/IEscrow.sol @@ -2,76 +2,42 @@ pragma solidity ^0.8.0; -interface IEscrow { - // TODO: is it possible to optimise this? - /** - * Timelocks for the source chain. - * finality: The duration of the chain finality period. - * withdrawal: The duration of the period when only the taker with a secret can withdraw tokens for the taker. - * cancel: The duration of the period when escrow can only be cancelled by the taker. - */ - struct SrcTimelocks { - uint256 finality; - uint256 withdrawal; - uint256 cancel; - } +import { Address } from "solidity-utils/libraries/AddressLib.sol"; - /** - * Timelocks for the destination chain. - * finality: The duration of the chain finality period. - * withdrawal: The duration of the period when only the taker with a secret can withdraw tokens for the maker. - * publicWithdrawal: The duration of the period when anyone with a secret can withdraw tokens for the maker. - */ - struct DstTimelocks { - uint256 finality; - uint256 withdrawal; - uint256 publicWithdrawal; - } +import { PackedAddresses } from "../libraries/PackedAddressesLib.sol"; +import { Timelocks } from "../libraries/TimelocksLib.sol"; - // Data for the immutables from the order post interacton. - struct InteractionParams { - address maker; - address taker; - uint256 srcChainId; - address srcToken; +interface IEscrow { + // Data for the source chain order immutables. + struct SrcEscrowImmutables { + bytes32 orderHash; uint256 srcAmount; uint256 dstAmount; - } - - // Data for the immutables from the order extension. - struct ExtraDataParams { + // --- Extra data --- // Hash of the secret. bytes32 hashlock; + // maker, taker, token in two 32-byte slots + PackedAddresses packedAddresses; uint256 dstChainId; - address dstToken; - uint256 srcSafetyDeposit; - uint256 dstSafetyDeposit; - SrcTimelocks srcTimelocks; - DstTimelocks dstTimelocks; - } - - // Data for the source chain order immutables. - struct SrcEscrowImmutables { - uint256 deployedAt; - InteractionParams interactionParams; - ExtraDataParams extraDataParams; + Address dstToken; + // 16 bytes for srcSafetyDeposit and 16 bytes for dstSafetyDeposit. + uint256 deposits; + Timelocks timelocks; } /** * Data for the destination chain order immutables. - * chainId, token, amount and safetyDeposit relate to the destination chain. + * token, amount and safetyDeposit are related to the destination chain. */ struct DstEscrowImmutables { - uint256 deployedAt; + bytes32 orderHash; // Hash of the secret. bytes32 hashlock; - address maker; - address taker; - uint256 chainId; - address token; + // maker, taker, token in two 32-byte slots + PackedAddresses packedAddresses; uint256 amount; uint256 safetyDeposit; - DstTimelocks timelocks; + Timelocks timelocks; } error InvalidCaller(); diff --git a/contracts/interfaces/IEscrowFactory.sol b/contracts/interfaces/IEscrowFactory.sol index c2bd87d..1054e48 100644 --- a/contracts/interfaces/IEscrowFactory.sol +++ b/contracts/interfaces/IEscrowFactory.sol @@ -9,14 +9,7 @@ interface IEscrowFactory { * token, amount and safetyDeposit are related to the destination chain. */ struct DstEscrowImmutablesCreation { - // Hash of the secret. - bytes32 hashlock; - address maker; - address taker; - address token; - uint256 amount; - uint256 safetyDeposit; - IEscrow.DstTimelocks timelocks; + IEscrow.DstEscrowImmutables args; // Start of the cancellation period for the source chain. uint256 srcCancellationTimestamp; } @@ -30,12 +23,12 @@ interface IEscrowFactory { * and approve the destination token to be transferred to the created escrow. * @param dstEscrowImmutables The immutables of the escrow contract that are used in deployment. */ - function createEscrow(DstEscrowImmutablesCreation calldata dstEscrowImmutables) external payable; + function createEscrowDst(DstEscrowImmutablesCreation calldata dstEscrowImmutables) external payable; /** * @notice Returns the deterministic address of the escrow based on the salt. - * @param salt The salt used to deploy escrow. + * @param data The immutable arguments used to deploy escrow. * @return The computed address of the escrow. */ - function addressOfEscrow(bytes32 salt) external view returns (address); + function addressOfEscrow(bytes memory data) external view returns (address); } diff --git a/contracts/libraries/PackedAddressesLib.sol b/contracts/libraries/PackedAddressesLib.sol new file mode 100644 index 0000000..ca9ef6f --- /dev/null +++ b/contracts/libraries/PackedAddressesLib.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.23; + +struct PackedAddresses { + // 20 most significant bytes of the maker address + 2 empty bytes + 10 bytes of the taker address + uint256 addressesPart1; + // 10 most significant bytes of the taker address + 2 empty bytes + 20 bytes of the token address + uint256 addressesPart2; +} + +/** + * @title Packed Addresses library + * @notice Library to pack 3 addresses into 2 uint256 values. + */ +library PackedAddressesLib { + /** + * @notice Returns the maker address from the packed addresses. + * @param packedAddresses Packed addresses. + * @return The maker address. + */ + function maker(PackedAddresses calldata packedAddresses) internal pure returns (address) { + return _maker(packedAddresses.addressesPart1); + } + + /** + * @notice Returns the taker address from the packed addresses. + * @param packedAddresses Packed addresses. + * @return The taker address. + */ + function taker(PackedAddresses calldata packedAddresses) internal pure returns (address) { + return _taker(packedAddresses.addressesPart1, packedAddresses.addressesPart2); + } + + /** + * @notice Returns the taker address from the packed addresses. + * @param packedAddresses Packed addresses. + * @return The taker address. + */ + function token(PackedAddresses calldata packedAddresses) internal pure returns (address) { + return _token(packedAddresses.addressesPart2); + } + + function _maker(uint256 addressesPart1) internal pure returns (address) { + // 96 = 2 empty bytes + 10 bytes of the taker address + return address(uint160(addressesPart1 >> 96)); + } + + function _taker(uint256 addressesPart1, uint256 addressesPart2) internal pure returns (address) { + // 80 = 10 bytes of the taker address from addressesPart2, 176 = 2 empty bytes + 20 bytes of the token address + return address(uint160((addressesPart1 << 80) | (addressesPart2 >> 176))); + } + + function _token(uint256 addressesPart2) internal pure returns (address) { + return address(uint160(addressesPart2)); + } +} diff --git a/contracts/libraries/TimelocksLib.sol b/contracts/libraries/TimelocksLib.sol new file mode 100644 index 0000000..a9d83c7 --- /dev/null +++ b/contracts/libraries/TimelocksLib.sol @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.23; + +/** + * @dev Timelocks for the source and the destination chains. + * For illustrative purposes, it is possible to describe timelocks by two structures: + * struct SrcTimelocks { + * uint256 finality; + * uint256 withdrawal; + * uint256 cancellation; + * } + * + * struct DstTimelocks { + * uint256 finality; + * uint256 withdrawal; + * uint256 publicWithdrawal; + * } + * + * finality: The duration of the chain finality period. + * withdrawal: The duration of the period when only the taker with a secret can withdraw tokens for taker (source chain) + * or maker (destination chain). + * publicWithdrawal: The duration of the period when anyone with a secret can withdraw tokens for taker (source chain) + * or maker (destination chain). + * cancellation: The duration of the period when escrow can only be cancelled by the taker. + */ +type Timelocks is uint256; + +/** + * @title Timelocks library for compact storage of timelocks in a uint256. + */ +library TimelocksLib { + uint256 internal constant _TIMELOCK_MASK = type(uint32).max; + // 6 variables 32 bits each + uint256 internal constant _SRC_FINALITY_OFFSET = 224; + uint256 internal constant _SRC_WITHDRAWAL_OFFSET = 192; + uint256 internal constant _SRC_CANCELLATION_OFFSET = 160; + uint256 internal constant _DST_FINALITY_OFFSET = 128; + uint256 internal constant _DST_WITHDRAWAL_OFFSET = 96; + uint256 internal constant _DST_PUB_WITHDRAWAL_OFFSET = 64; + + /** + * @notice Returns the Escrow deployment timestamp. + * @param timelocks The timelocks to get the deployment timestamp from. + * @return The Escrow deployment timestamp. + */ + function deployedAt(Timelocks timelocks) internal pure returns (uint256) { + return uint40(Timelocks.unwrap(timelocks)); + } + + /** + * @notice Sets the Escrow deployment timestamp. + * @param timelocks The timelocks to set the deployment timestamp to. + * @param value The new Escrow deployment timestamp. + * @return The timelocks with the deployment timestamp set. + */ + function setDeployedAt(Timelocks timelocks, uint256 value) internal pure returns (Timelocks) { + return Timelocks.wrap((Timelocks.unwrap(timelocks) & ~uint256(type(uint40).max)) | uint40(value)); + } + + // ----- Source chain timelocks ----- // + /** + * @notice Returns the duration of the finality period on the source chain. + * @param timelocks The timelocks to get the finality duration from. + * @return The duration of the finality period. + */ + function srcFinalityDuration(Timelocks timelocks) internal pure returns (uint256) { + return Timelocks.unwrap(timelocks) >> _SRC_FINALITY_OFFSET & _TIMELOCK_MASK ; + } + + /** + * @notice Returns the start of the private withdrawal period on the source chain. + * @param timelocks The timelocks to get the finality duration from. + * @param startTimestamp The timestamp when the counting starts. + * @return The start of the private withdrawal period. + */ + function srcWithdrawalStart(Timelocks timelocks, uint256 startTimestamp) internal pure returns (uint256) { + unchecked { + return startTimestamp + srcFinalityDuration(timelocks); + } + } + + /** + * @notice Returns the duration of the private withdrawal period on the source chain. + * @param timelocks The timelocks to get the private withdrawal duration from. + * @return The duration of the private withdrawal period. + */ + function srcWithdrawalDuration(Timelocks timelocks) internal pure returns (uint256) { + return Timelocks.unwrap(timelocks) >> _SRC_WITHDRAWAL_OFFSET & _TIMELOCK_MASK; + } + + /** + * @notice Returns the start of the private cancellation period on the source chain. + * @param timelocks The timelocks to get the private withdrawal duration from. + * @param startTimestamp The timestamp when the counting starts. + * @return The start of the private cancellation period. + */ + function srcCancellationStart(Timelocks timelocks, uint256 startTimestamp) internal pure returns (uint256) { + unchecked { + return srcWithdrawalStart(timelocks, startTimestamp) + srcWithdrawalDuration(timelocks); + } + } + + /** + * @notice Returns the duration of the private cancellation period on the source chain. + * @param timelocks The timelocks to get the private cancellation duration from. + * @return The duration of the private cancellation period. + */ + function srcCancellationDuration(Timelocks timelocks) internal pure returns (uint256) { + return Timelocks.unwrap(timelocks) >> _SRC_CANCELLATION_OFFSET & _TIMELOCK_MASK; + } + + /** + * @notice Returns the start of the public cancellation period on the source chain. + * @param timelocks The timelocks to get the private cancellation duration from. + * @param startTimestamp The timestamp when the counting starts. + * @return The start of the public cancellation period. + */ + function srcPubCancellationStart(Timelocks timelocks, uint256 startTimestamp) internal pure returns (uint256) { + unchecked { + return srcCancellationStart(timelocks, startTimestamp) + srcCancellationDuration(timelocks); + } + } + + // ----- Destination chain timelocks ----- // + /** + * @notice Returns the duration of the finality period on the destination chain. + * @param timelocks The timelocks to get the finality duration from. + * @return The duration of the finality period. + */ + function dstFinalityDuration(Timelocks timelocks) internal pure returns (uint256) { + return Timelocks.unwrap(timelocks) >> _DST_FINALITY_OFFSET & _TIMELOCK_MASK; + } + + /** + * @notice Returns the start of the private withdrawal period on the destination chain. + * @param timelocks The timelocks to get the finality duration from. + * @param startTimestamp The timestamp when the counting starts. + * @return The start of the private withdrawal period. + */ + function dstWithdrawalStart(Timelocks timelocks, uint256 startTimestamp) internal pure returns (uint256) { + unchecked { + return startTimestamp + dstFinalityDuration(timelocks); + } + } + + /** + * @notice Returns the duration of the private withdrawal period on the destination chain. + * @param timelocks The timelocks to get the private withdrawal duration from. + * @return The duration of the private withdrawal period. + */ + function dstWithdrawalDuration(Timelocks timelocks) internal pure returns (uint256) { + return Timelocks.unwrap(timelocks) >> _DST_WITHDRAWAL_OFFSET & _TIMELOCK_MASK; + } + + /** + * @notice Returns the start of the public withdrawal period on the destination chain. + * @param timelocks The timelocks to get the private withdrawal duration from. + * @param startTimestamp The timestamp when the counting starts. + * @return The start of the public withdrawal period. + */ + function dstPubWithdrawalStart(Timelocks timelocks, uint256 startTimestamp) internal pure returns (uint256) { + unchecked { + return dstWithdrawalStart(timelocks, startTimestamp) + dstWithdrawalDuration(timelocks); + } + } + + /** + * @notice Returns the duration of the public withdrawal period on the destination chain. + * @param timelocks The timelocks to get the public withdrawal duration from. + * @return The duration of the public withdrawal period. + */ + function dstPubWithdrawalDuration(Timelocks timelocks) internal pure returns (uint256) { + return Timelocks.unwrap(timelocks) >> _DST_PUB_WITHDRAWAL_OFFSET & _TIMELOCK_MASK; + } + + /** + * @notice Returns the start of the private cancellation period on the destination chain. + * @param timelocks The timelocks to get the public withdrawal duration from. + * @param startTimestamp The timestamp when the counting starts. + * @return The start of the private cancellation period. + */ + function dstCancellationStart(Timelocks timelocks, uint256 startTimestamp) internal pure returns (uint256) { + unchecked { + return dstPubWithdrawalStart(timelocks, startTimestamp) + dstPubWithdrawalDuration(timelocks); + } + } +} diff --git a/documentation/src/SUMMARY.md b/documentation/src/SUMMARY.md index cb64a27..901911a 100644 --- a/documentation/src/SUMMARY.md +++ b/documentation/src/SUMMARY.md @@ -4,5 +4,8 @@ - [❱ interfaces](contracts/interfaces/README.md) - [IEscrow](contracts/interfaces/IEscrow.sol/interface.IEscrow.md) - [IEscrowFactory](contracts/interfaces/IEscrowFactory.sol/interface.IEscrowFactory.md) + - [❱ libraries](contracts/libraries/README.md) + - [Timelocks](contracts/libraries/TimelocksLib.sol/type.Timelocks.md) + - [TimelocksLib](contracts/libraries/TimelocksLib.sol/library.TimelocksLib.md) - [Escrow](contracts/Escrow.sol/contract.Escrow.md) - [EscrowFactory](contracts/EscrowFactory.sol/contract.EscrowFactory.md) diff --git a/documentation/src/contracts/Escrow.sol/contract.Escrow.md b/documentation/src/contracts/Escrow.sol/contract.Escrow.md index 70f9c3f..7ed4b51 100644 --- a/documentation/src/contracts/Escrow.sol/contract.Escrow.md +++ b/documentation/src/contracts/Escrow.sol/contract.Escrow.md @@ -1,5 +1,5 @@ # Escrow -[Git Source](https://github.com/1inch/cross-chain-swap/blob/f45e33f855d5dd79428a1ba540d9f8df14bbb794/contracts/Escrow.sol) +[Git Source](https://github.com/1inch/cross-chain-swap/blob/4a7a924cfc3cdc40ce87e400e418d193236c06fb/contracts/Escrow.sol) **Inherits:** Clone, [IEscrow](/contracts/interfaces/IEscrow.sol/interface.IEscrow.md) @@ -17,6 +17,9 @@ Withdrawal and cancellation functions for the source and destination chains are See [IEscrow-withdrawSrc](/contracts/interfaces/IEscrow.sol/interface.IEscrow.md#withdrawsrc). +*The function works on the time interval highlighted with capital letters: +---- contract deployed --/-- finality --/-- PRIVATE WITHDRAWAL --/-- private cancel --/-- public cancel ----* + ```solidity function withdrawSrc(bytes32 secret) external; @@ -26,6 +29,9 @@ function withdrawSrc(bytes32 secret) external; See [IEscrow-cancelSrc](/contracts/interfaces/IEscrow.sol/interface.IEscrow.md#cancelsrc). +*The function works on the time intervals highlighted with capital letters: +---- contract deployed --/-- finality --/-- private withdrawal --/-- PRIVATE CANCEL --/-- PUBLIC CANCEL ----* + ```solidity function cancelSrc() external; @@ -35,6 +41,9 @@ function cancelSrc() external; See [IEscrow-withdrawDst](/contracts/interfaces/IEscrow.sol/interface.IEscrow.md#withdrawdst). +*The function works on the time intervals highlighted with capital letters: +---- contract deployed --/-- finality --/-- PRIVATE WITHDRAWAL --/-- PUBLIC WITHDRAWAL --/-- private cancel ----* + ```solidity function withdrawDst(bytes32 secret) external; @@ -44,6 +53,9 @@ function withdrawDst(bytes32 secret) external; See [IEscrow-cancelDst](/contracts/interfaces/IEscrow.sol/interface.IEscrow.md#canceldst). +*The function works on the time interval highlighted with capital letters: +---- contract deployed --/-- finality --/-- private withdrawal --/-- public withdrawal --/-- PRIVATE CANCEL ----* + ```solidity function cancelDst() external; diff --git a/documentation/src/contracts/EscrowFactory.sol/contract.EscrowFactory.md b/documentation/src/contracts/EscrowFactory.sol/contract.EscrowFactory.md index b34a818..e76ef16 100644 --- a/documentation/src/contracts/EscrowFactory.sol/contract.EscrowFactory.md +++ b/documentation/src/contracts/EscrowFactory.sol/contract.EscrowFactory.md @@ -1,5 +1,5 @@ # EscrowFactory -[Git Source](https://github.com/1inch/cross-chain-swap/blob/f45e33f855d5dd79428a1ba540d9f8df14bbb794/contracts/EscrowFactory.sol) +[Git Source](https://github.com/1inch/cross-chain-swap/blob/4a7a924cfc3cdc40ce87e400e418d193236c06fb/contracts/EscrowFactory.sol) **Inherits:** [IEscrowFactory](/contracts/interfaces/IEscrowFactory.sol/interface.IEscrowFactory.md), SimpleSettlementExtension @@ -38,7 +38,7 @@ after all funds have been transferred. See [IPostInteraction-postInteraction](/l function _postInteraction( IOrderMixin.Order calldata order, bytes calldata, - bytes32 orderHash, + bytes32, address taker, uint256 makingAmount, uint256 takingAmount, @@ -62,25 +62,24 @@ See [IEscrowFactory-addressOfEscrow](/contracts/interfaces/IEscrowFactory.sol/in ```solidity -function addressOfEscrow(bytes32 salt) public view returns (address); +function addressOfEscrow(bytes memory data) public view returns (address); ``` ### _createEscrow Creates a new escrow contract with immutable arguments. -*The escrow contract is a proxy clone created using the create3 pattern.* +*The escrow contract is a proxy clone created using the create2 pattern.* ```solidity -function _createEscrow(bytes memory data, bytes32 salt, uint256 value) private returns (address clone); +function _createEscrow(bytes memory data, uint256 value) private returns (address clone); ``` **Parameters** |Name|Type|Description| |----|----|-----------| |`data`|`bytes`|Encoded immutable args.| -|`salt`|`bytes32`|The salt that influences the contract address in deterministic deployment.| |`value`|`uint256`|| **Returns** diff --git a/documentation/src/contracts/README.md b/documentation/src/contracts/README.md index 61863b3..cb60fbc 100644 --- a/documentation/src/contracts/README.md +++ b/documentation/src/contracts/README.md @@ -2,5 +2,6 @@ # Contents - [interfaces](/contracts/interfaces) +- [libraries](/contracts/libraries) - [Escrow](Escrow.sol/contract.Escrow.md) - [EscrowFactory](EscrowFactory.sol/contract.EscrowFactory.md) diff --git a/documentation/src/contracts/interfaces/IEscrow.sol/interface.IEscrow.md b/documentation/src/contracts/interfaces/IEscrow.sol/interface.IEscrow.md index 4dd8bc3..4c00918 100644 --- a/documentation/src/contracts/interfaces/IEscrow.sol/interface.IEscrow.md +++ b/documentation/src/contracts/interfaces/IEscrow.sol/interface.IEscrow.md @@ -1,5 +1,5 @@ # IEscrow -[Git Source](https://github.com/1inch/cross-chain-swap/blob/f45e33f855d5dd79428a1ba540d9f8df14bbb794/contracts/interfaces/IEscrow.sol) +[Git Source](https://github.com/1inch/cross-chain-swap/blob/4a7a924cfc3cdc40ce87e400e418d193236c06fb/contracts/interfaces/IEscrow.sol) ## Functions @@ -7,9 +7,9 @@ Withdraws funds to the taker on the source chain. -*Withdrawal can only be made during the public unlock period and with secret +*Withdrawal can only be made by the taker during the withdrawal period and with secret with hash matches the hashlock. -The safety deposit is sent to the caller.* +The safety deposit is sent to the caller (taker).* ```solidity @@ -39,8 +39,8 @@ function cancelSrc() external; Withdraws funds to the maker on the destination chain. -*Withdrawal can only be made by taker during the private unlock period or by anyone -during the public unlock period. In both cases, a secret with hash matching the hashlock must be provided. +*Withdrawal can only be made by taker during the private withdrawal period or by anyone +during the public withdrawal period. In both cases, a secret with hash matching the hashlock must be provided. The safety deposit is sent to the caller.* @@ -58,8 +58,8 @@ function withdrawDst(bytes32 secret) external; Cancels the escrow on the destination chain and returns tokens to the taker. -*The escrow can only be cancelled during the cancel period. -The safety deposit is sent to the caller.* +*The escrow can only be cancelled by the taker during the cancel period. +The safety deposit is sent to the caller (taker).* ```solidity @@ -134,36 +134,6 @@ error NativeTokenSendingFailure(); ``` ## Structs -### SrcTimelocks -Timelocks for the source chain. -finality: The duration of the chain finality period. -publicUnlock: The duration of the period when anyone with a secret can withdraw tokens for the taker. -cancel: The duration of the period when escrow can only be cancelled by the taker. - - -```solidity -struct SrcTimelocks { - uint256 finality; - uint256 publicUnlock; - uint256 cancel; -} -``` - -### DstTimelocks -Timelocks for the destination chain. -finality: The duration of the chain finality period. -unlock: The duration of the period when only the taker with a secret can withdraw tokens for the maker. -publicUnlock publicUnlock: The duration of the period when anyone with a secret can withdraw tokens for the maker. - - -```solidity -struct DstTimelocks { - uint256 finality; - uint256 unlock; - uint256 publicUnlock; -} -``` - ### InteractionParams ```solidity @@ -184,10 +154,8 @@ struct ExtraDataParams { bytes32 hashlock; uint256 dstChainId; address dstToken; - uint256 srcSafetyDeposit; - uint256 dstSafetyDeposit; - SrcTimelocks srcTimelocks; - DstTimelocks dstTimelocks; + uint256 deposits; + Timelocks timelocks; } ``` @@ -209,14 +177,14 @@ chainId, token, amount and safetyDeposit relate to the destination chain. ```solidity struct DstEscrowImmutables { uint256 deployedAt; + uint256 chainId; bytes32 hashlock; address maker; address taker; - uint256 chainId; address token; uint256 amount; uint256 safetyDeposit; - DstTimelocks timelocks; + Timelocks timelocks; } ``` diff --git a/documentation/src/contracts/interfaces/IEscrowFactory.sol/interface.IEscrowFactory.md b/documentation/src/contracts/interfaces/IEscrowFactory.sol/interface.IEscrowFactory.md index 8f1e24d..b497ba3 100644 --- a/documentation/src/contracts/interfaces/IEscrowFactory.sol/interface.IEscrowFactory.md +++ b/documentation/src/contracts/interfaces/IEscrowFactory.sol/interface.IEscrowFactory.md @@ -1,5 +1,5 @@ # IEscrowFactory -[Git Source](https://github.com/1inch/cross-chain-swap/blob/f45e33f855d5dd79428a1ba540d9f8df14bbb794/contracts/interfaces/IEscrowFactory.sol) +[Git Source](https://github.com/1inch/cross-chain-swap/blob/4a7a924cfc3cdc40ce87e400e418d193236c06fb/contracts/interfaces/IEscrowFactory.sol) ## Functions @@ -27,13 +27,13 @@ Returns the deterministic address of the escrow based on the salt. ```solidity -function addressOfEscrow(bytes32 salt) external view returns (address); +function addressOfEscrow(bytes memory data) external view returns (address); ``` **Parameters** |Name|Type|Description| |----|----|-----------| -|`salt`|`bytes32`|The salt used to deploy escrow.| +|`data`|`bytes`|The immutable arguments used to deploy escrow.| **Returns** @@ -68,7 +68,7 @@ struct DstEscrowImmutablesCreation { address token; uint256 amount; uint256 safetyDeposit; - IEscrow.DstTimelocks timelocks; + Timelocks timelocks; uint256 srcCancellationTimestamp; } ``` diff --git a/documentation/src/contracts/libraries/README.md b/documentation/src/contracts/libraries/README.md new file mode 100644 index 0000000..9a91ade --- /dev/null +++ b/documentation/src/contracts/libraries/README.md @@ -0,0 +1,5 @@ + + +# Contents +- [Timelocks](TimelocksLib.sol/type.Timelocks.md) +- [TimelocksLib](TimelocksLib.sol/library.TimelocksLib.md) diff --git a/documentation/src/contracts/libraries/TimelocksLib.sol/library.TimelocksLib.md b/documentation/src/contracts/libraries/TimelocksLib.sol/library.TimelocksLib.md new file mode 100644 index 0000000..ff9955f --- /dev/null +++ b/documentation/src/contracts/libraries/TimelocksLib.sol/library.TimelocksLib.md @@ -0,0 +1,445 @@ +# TimelocksLib +[Git Source](https://github.com/1inch/cross-chain-swap/blob/4a7a924cfc3cdc40ce87e400e418d193236c06fb/contracts/libraries/TimelocksLib.sol) + + +## State Variables +### _TIMESTAMP_MASK + +```solidity +uint256 private constant _TIMESTAMP_MASK = (1 << 40) - 1; +``` + + +### _SRC_FINALITY_OFFSET + +```solidity +uint256 private constant _SRC_FINALITY_OFFSET = 216; +``` + + +### _SRC_WITHDRAWAL_OFFSET + +```solidity +uint256 private constant _SRC_WITHDRAWAL_OFFSET = 176; +``` + + +### _SRC_CANCEL_OFFSET + +```solidity +uint256 private constant _SRC_CANCEL_OFFSET = 136; +``` + + +### _DST_FINALITY_OFFSET + +```solidity +uint256 private constant _DST_FINALITY_OFFSET = 96; +``` + + +### _DST_WITHDRAWAL_OFFSET + +```solidity +uint256 private constant _DST_WITHDRAWAL_OFFSET = 56; +``` + + +### _DST_PUB_WITHDRAWAL_OFFSET + +```solidity +uint256 private constant _DST_PUB_WITHDRAWAL_OFFSET = 16; +``` + + +## Functions +### getSrcFinalityDuration + +Gets the duration of the finality period on the source chain. + + +```solidity +function getSrcFinalityDuration(Timelocks timelocks) internal pure returns (uint256); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to get the finality duration from.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|The duration of the finality period.| + + +### setSrcFinalityDuration + +Sets the duration of the finality period on the source chain. + + +```solidity +function setSrcFinalityDuration(Timelocks timelocks, uint256 value) internal pure returns (Timelocks); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to set the finality duration to.| +|`value`|`uint256`|The new duration of the finality period.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`Timelocks`|The timelocks with the finality duration set.| + + +### getSrcWithdrawalStart + +Gets the start of the private withdrawal period on the source chain. + + +```solidity +function getSrcWithdrawalStart(Timelocks timelocks, uint256 startTimestamp) internal pure returns (uint256); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to get the finality duration from.| +|`startTimestamp`|`uint256`|The timestamp when the counting starts.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|The start of the private withdrawal period.| + + +### getSrcWithdrawalDuration + +Gets the duration of the private withdrawal period on the source chain. + + +```solidity +function getSrcWithdrawalDuration(Timelocks timelocks) internal pure returns (uint256); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to get the private withdrawal duration from.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|The duration of the private withdrawal period.| + + +### setSrcWithdrawalDuration + +Sets the duration of the private withdrawal period on the source chain. + + +```solidity +function setSrcWithdrawalDuration(Timelocks timelocks, uint256 value) internal pure returns (Timelocks); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to set the private withdrawal duration to.| +|`value`|`uint256`|The new duration of the private withdrawal period.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`Timelocks`|The timelocks with the private withdrawal duration set.| + + +### getSrcCancellationStart + +Gets the start of the private cancellation period on the source chain. + + +```solidity +function getSrcCancellationStart(Timelocks timelocks, uint256 startTimestamp) internal pure returns (uint256); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to get the private withdrawal duration from.| +|`startTimestamp`|`uint256`|The timestamp when the counting starts.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|The start of the private cancellation period.| + + +### getSrcCancellationDuration + +Gets the duration of the private cancellation period on the source chain. + + +```solidity +function getSrcCancellationDuration(Timelocks timelocks) internal pure returns (uint256); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to get the private cancellation duration from.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|The duration of the private cancellation period.| + + +### setSrcCancellationDuration + +Sets the duration of the private cancellation period on the source chain. + + +```solidity +function setSrcCancellationDuration(Timelocks timelocks, uint256 value) internal pure returns (Timelocks); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to set the private cancellation duration to.| +|`value`|`uint256`|The duration of the private cancellation period.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`Timelocks`|The timelocks with the private cancellation duration set.| + + +### getSrcPubCancellationStart + +Gets the start of the public cancellation period on the source chain. + + +```solidity +function getSrcPubCancellationStart(Timelocks timelocks, uint256 startTimestamp) internal pure returns (uint256); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to get the private cancellation duration from.| +|`startTimestamp`|`uint256`|The timestamp when the counting starts.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|The start of the public cancellation period.| + + +### getDstFinalityDuration + +Gets the duration of the finality period on the destination chain. + + +```solidity +function getDstFinalityDuration(Timelocks timelocks) internal pure returns (uint256); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to get the finality duration from.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|The duration of the finality period.| + + +### setDstFinalityDuration + +Sets the duration of the finality period on the destination chain. + + +```solidity +function setDstFinalityDuration(Timelocks timelocks, uint256 value) internal pure returns (Timelocks); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to set the finality duration to.| +|`value`|`uint256`|The duration of the finality period.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`Timelocks`|The timelocks with the finality duration set.| + + +### getDstWithdrawalStart + +Gets the start of the private withdrawal period on the destination chain. + + +```solidity +function getDstWithdrawalStart(Timelocks timelocks, uint256 startTimestamp) internal pure returns (uint256); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to get the finality duration from.| +|`startTimestamp`|`uint256`|The timestamp when the counting starts.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|The start of the private withdrawal period.| + + +### getDstWithdrawalDuration + +Gets the duration of the private withdrawal period on the destination chain. + + +```solidity +function getDstWithdrawalDuration(Timelocks timelocks) internal pure returns (uint256); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to get the private withdrawal duration from.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|The duration of the private withdrawal period.| + + +### setDstWithdrawalDuration + +Sets the duration of the private withdrawal period on the destination chain. + + +```solidity +function setDstWithdrawalDuration(Timelocks timelocks, uint256 value) internal pure returns (Timelocks); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to set the private withdrawal duration to.| +|`value`|`uint256`|The new duration of the private withdrawal period.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`Timelocks`|The timelocks with the private withdrawal duration set.| + + +### getDstPubWithdrawalStart + +Gets the start of the public withdrawal period on the destination chain. + + +```solidity +function getDstPubWithdrawalStart(Timelocks timelocks, uint256 startTimestamp) internal pure returns (uint256); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to get the private withdrawal duration from.| +|`startTimestamp`|`uint256`|The timestamp when the counting starts.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|The start of the public withdrawal period.| + + +### getDstPubWithdrawalDuration + +Gets the duration of the public withdrawal period on the destination chain. + + +```solidity +function getDstPubWithdrawalDuration(Timelocks timelocks) internal pure returns (uint256); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to get the public withdrawal duration from.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|The duration of the public withdrawal period.| + + +### setDstPubWithdrawalDuration + +Sets the duration of the public withdrawal period on the destination chain. + + +```solidity +function setDstPubWithdrawalDuration(Timelocks timelocks, uint256 value) internal pure returns (Timelocks); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to set the public withdrawal duration to.| +|`value`|`uint256`|The new duration of the public withdrawal period.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`Timelocks`|The timelocks with the public withdrawal duration set.| + + +### getDstCancellationStart + +Gets the start of the private cancellation period on the destination chain. + + +```solidity +function getDstCancellationStart(Timelocks timelocks, uint256 startTimestamp) internal pure returns (uint256); +``` +**Parameters** + +|Name|Type|Description| +|----|----|-----------| +|`timelocks`|`Timelocks`|The timelocks to get the public withdrawal duration from.| +|`startTimestamp`|`uint256`|The timestamp when the counting starts.| + +**Returns** + +|Name|Type|Description| +|----|----|-----------| +|``|`uint256`|The start of the private cancellation period.| + + diff --git a/documentation/src/contracts/libraries/TimelocksLib.sol/type.Timelocks.md b/documentation/src/contracts/libraries/TimelocksLib.sol/type.Timelocks.md new file mode 100644 index 0000000..401cff8 --- /dev/null +++ b/documentation/src/contracts/libraries/TimelocksLib.sol/type.Timelocks.md @@ -0,0 +1,27 @@ +# Timelocks +[Git Source](https://github.com/1inch/cross-chain-swap/blob/4a7a924cfc3cdc40ce87e400e418d193236c06fb/contracts/libraries/TimelocksLib.sol) + +*Timelocks for the source and the destination chains. +For illustrative purposes, it is possible to describe timelocks by two structures: +struct SrcTimelocks { +uint256 finality; +uint256 withdrawal; +uint256 cancel; +} +struct DstTimelocks { +uint256 finality; +uint256 withdrawal; +uint256 publicWithdrawal; +} +finality: The duration of the chain finality period. +withdrawal: The duration of the period when only the taker with a secret can withdraw tokens for taker (source chain) +or maker (destination chain). +publicWithdrawal: The duration of the period when anyone with a secret can withdraw tokens for taker (source chain) +or maker (destination chain). +cancel: The duration of the period when escrow can only be cancelled by the taker.* + + +```solidity +type Timelocks is uint256; +``` + diff --git a/test/integration/EscrowFactory.t.sol b/test/integration/EscrowFactory.t.sol index 3798712..58ad8a7 100644 --- a/test/integration/EscrowFactory.t.sol +++ b/test/integration/EscrowFactory.t.sol @@ -3,10 +3,13 @@ pragma solidity 0.8.23; import { IEscrowFactory } from "contracts/EscrowFactory.sol"; import { Escrow, IEscrow } from "contracts/Escrow.sol"; +import { PackedAddressesMemLib } from "../utils/libraries/PackedAddressesMemLib.sol"; -import { BaseSetup, IOrderMixin, TakerTraits } from "../utils/BaseSetup.sol"; +import { Address, AddressLib, BaseSetup, IOrderMixin, TakerTraits } from "../utils/BaseSetup.sol"; contract IntegrationEscrowFactoryTest is BaseSetup { + using AddressLib for Address; + function setUp() public virtual override { BaseSetup.setUp(); } @@ -55,9 +58,9 @@ contract IntegrationEscrowFactoryTest is BaseSetup { assertLt(feeBank.availableCredit(bob.addr), resolverCredit); IEscrow.SrcEscrowImmutables memory returnedImmutables = srcClone.srcEscrowImmutables(); - assertEq(returnedImmutables.extraDataParams.hashlock, keccak256(abi.encodePacked(secret))); - assertEq(returnedImmutables.interactionParams.srcAmount, srcAmount); - assertEq(returnedImmutables.extraDataParams.dstToken, address(dai)); + assertEq(returnedImmutables.hashlock, keccak256(abi.encodePacked(secret))); + assertEq(PackedAddressesMemLib.taker(returnedImmutables.packedAddresses), bob.addr); + assertEq(returnedImmutables.dstToken.get(), address(dai)); } function test_NoInsufficientBalanceDeploymentForMakerInt() public { diff --git a/test/libraries/TimelocksLib.t.sol b/test/libraries/TimelocksLib.t.sol new file mode 100644 index 0000000..3383f57 --- /dev/null +++ b/test/libraries/TimelocksLib.t.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +import { Timelocks, TimelocksLib } from "contracts/libraries/TimelocksLib.sol"; +import { TimelocksSettersLib } from "../utils/libraries/TimelocksSettersLib.sol"; + +import { BaseSetup } from "../utils/BaseSetup.sol"; + +contract TimelocksLibTest is BaseSetup { + using TimelocksLib for Timelocks; + using TimelocksSettersLib for Timelocks; + + function setUp() public virtual override { + BaseSetup.setUp(); + } + + /* solhint-disable func-name-mixedcase */ + + function test_getStartTimestamps() public { + uint256 timestamp = block.timestamp; + Timelocks timelocksTest = TimelocksSettersLib.init( + srcTimelocks.finality, + srcTimelocks.withdrawal, + srcTimelocks.cancel, + dstTimelocks.finality, + dstTimelocks.withdrawal, + dstTimelocks.publicWithdrawal, + timestamp + ); + + assertEq(timelocksTest.srcWithdrawalStart(timestamp), timestamp + srcTimelocks.finality); + assertEq(timelocksTest.srcCancellationStart(timestamp), timestamp + srcTimelocks.finality + srcTimelocks.withdrawal); + assertEq(timelocksTest.srcPubCancellationStart(timestamp), timestamp + srcTimelocks.finality + srcTimelocks.withdrawal + srcTimelocks.cancel); + assertEq(timelocksTest.dstWithdrawalStart(timestamp), timestamp + dstTimelocks.finality); + assertEq(timelocksTest.dstPubWithdrawalStart(timestamp), timestamp + dstTimelocks.finality + dstTimelocks.withdrawal); + assertEq(timelocksTest.dstCancellationStart(timestamp), timestamp + dstTimelocks.finality + dstTimelocks.withdrawal + dstTimelocks.publicWithdrawal); + } + + /* solhint-enable func-name-mixedcase */ +} diff --git a/test/unit/Escrow.t.sol b/test/unit/Escrow.t.sol index 4c30b61..6e9b06c 100644 --- a/test/unit/Escrow.t.sol +++ b/test/unit/Escrow.t.sol @@ -87,7 +87,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.startPrank(bob.addr); - escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); + escrowFactory.createEscrowDst{value: DST_SAFETY_DEPOSIT}(immutables); // withdraw vm.expectRevert(IEscrow.InvalidWithdrawalTime.selector); @@ -142,7 +142,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.startPrank(bob.addr); - escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); + escrowFactory.createEscrowDst{value: DST_SAFETY_DEPOSIT}(immutables); uint256 balanceAlice = dai.balanceOf(alice.addr); uint256 balanceBob = bob.addr.balance; @@ -200,7 +200,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.startPrank(bob.addr); - escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); + escrowFactory.createEscrowDst{value: DST_SAFETY_DEPOSIT}(immutables); // withdraw vm.warp(block.timestamp + dstTimelocks.finality + 100); @@ -217,7 +217,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.prank(bob.addr); - escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); + escrowFactory.createEscrowDst{value: DST_SAFETY_DEPOSIT}(immutables); // withdraw vm.warp(block.timestamp + dstTimelocks.finality + 100); @@ -234,7 +234,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.prank(bob.addr); - escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); + escrowFactory.createEscrowDst{value: DST_SAFETY_DEPOSIT}(immutables); uint256 balanceAlice = dai.balanceOf(alice.addr); uint256 balanceThis = address(this).balance; @@ -260,7 +260,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.startPrank(bob.addr); - escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); + escrowFactory.createEscrowDst{value: DST_SAFETY_DEPOSIT}(immutables); uint256 balanceAlice = dai.balanceOf(alice.addr); uint256 balanceBob = bob.addr.balance; @@ -319,7 +319,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.prank(bob.addr); - escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); + escrowFactory.createEscrowDst{value: DST_SAFETY_DEPOSIT}(immutables); // withdraw vm.warp(block.timestamp + dstTimelocks.finality + dstTimelocks.withdrawal + 10); @@ -480,7 +480,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.startPrank(bob.addr); - escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); + escrowFactory.createEscrowDst{value: DST_SAFETY_DEPOSIT}(immutables); uint256 balanceBob = dai.balanceOf(bob.addr); uint256 balanceBobNative = bob.addr.balance; @@ -506,7 +506,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.prank(bob.addr); - escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); + escrowFactory.createEscrowDst{value: DST_SAFETY_DEPOSIT}(immutables); // cancel vm.warp(block.timestamp + dstTimelocks.finality + dstTimelocks.withdrawal + dstTimelocks.publicWithdrawal + 100); @@ -522,7 +522,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.startPrank(bob.addr); - escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); + escrowFactory.createEscrowDst{value: DST_SAFETY_DEPOSIT}(immutables); // cancel vm.warp(block.timestamp + dstTimelocks.finality + 100); @@ -538,7 +538,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.startPrank(bob.addr); - escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); + escrowFactory.createEscrowDst{value: DST_SAFETY_DEPOSIT}(immutables); // cancel vm.warp(block.timestamp + dstTimelocks.finality + dstTimelocks.withdrawal + 100); @@ -587,7 +587,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.startPrank(bob.addr); - escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); + escrowFactory.createEscrowDst{value: DST_SAFETY_DEPOSIT}(immutables); // cancel vm.warp(block.timestamp + dstTimelocks.finality + dstTimelocks.withdrawal + dstTimelocks.publicWithdrawal + 100); diff --git a/test/unit/EscrowFactory.t.sol b/test/unit/EscrowFactory.t.sol index b2a2bb6..fac8ae7 100644 --- a/test/unit/EscrowFactory.t.sol +++ b/test/unit/EscrowFactory.t.sol @@ -5,10 +5,16 @@ import { SimpleSettlementExtension } from "limit-order-settlement/SimpleSettleme import { Escrow, IEscrow } from "contracts/Escrow.sol"; import { IEscrowFactory } from "contracts/EscrowFactory.sol"; +import { PackedAddresses, PackedAddressesMemLib } from "../utils/libraries/PackedAddressesMemLib.sol"; +import { Timelocks, TimelocksLib } from "contracts/libraries/TimelocksLib.sol"; -import { BaseSetup, IOrderMixin } from "../utils/BaseSetup.sol"; +import { Address, AddressLib, BaseSetup, IOrderMixin } from "../utils/BaseSetup.sol"; contract EscrowFactoryTest is BaseSetup { + using AddressLib for Address; + using PackedAddressesMemLib for PackedAddresses; + using TimelocksLib for Timelocks; + function setUp() public virtual override { BaseSetup.setUp(); } @@ -42,9 +48,19 @@ contract EscrowFactoryTest is BaseSetup { ); IEscrow.SrcEscrowImmutables memory returnedImmutables = srcClone.srcEscrowImmutables(); - assertEq(returnedImmutables.extraDataParams.hashlock, keccak256(abi.encodePacked(secret))); - assertEq(returnedImmutables.interactionParams.srcAmount, srcAmount); - assertEq(returnedImmutables.extraDataParams.dstToken, address(dai)); + assertEq(returnedImmutables.orderHash, orderHash); + assertEq(returnedImmutables.hashlock, keccak256(abi.encodePacked(secret))); + assertEq(returnedImmutables.srcAmount, srcAmount); + assertEq(returnedImmutables.dstToken.get(), address(dai)); + assertEq(returnedImmutables.packedAddresses.maker(), alice.addr); + assertEq(returnedImmutables.packedAddresses.taker(), bob.addr); + assertEq(returnedImmutables.packedAddresses.token(), address(usdc)); + assertEq(returnedImmutables.timelocks.srcFinalityDuration(), srcTimelocks.finality); + assertEq(returnedImmutables.timelocks.srcWithdrawalDuration(), srcTimelocks.withdrawal); + assertEq(returnedImmutables.timelocks.srcCancellationDuration(), srcTimelocks.cancel); + assertEq(returnedImmutables.timelocks.dstFinalityDuration(), dstTimelocks.finality); + assertEq(returnedImmutables.timelocks.dstWithdrawalDuration(), dstTimelocks.withdrawal); + assertEq(returnedImmutables.timelocks.dstPubWithdrawalDuration(), dstTimelocks.publicWithdrawal); } function testFuzz_DeployCloneForTaker(bytes32 secret, uint56 amount) public { @@ -60,16 +76,23 @@ contract EscrowFactoryTest is BaseSetup { uint256 safetyDeposit = uint64(amount) * 10 / 100; // deploy escrow vm.prank(bob.addr); - escrowFactory.createEscrow{value: safetyDeposit}(immutables); + escrowFactory.createEscrowDst{value: safetyDeposit}(immutables); - assertEq(bob.addr.balance, balanceBobNative - immutables.safetyDeposit); + assertEq(bob.addr.balance, balanceBobNative - immutables.args.safetyDeposit); assertEq(dai.balanceOf(bob.addr), balanceBob - amount); assertEq(dai.balanceOf(address(dstClone)), balanceEscrow + amount); assertEq(address(dstClone).balance, balanceEscrowNative + safetyDeposit); IEscrow.DstEscrowImmutables memory returnedImmutables = dstClone.dstEscrowImmutables(); + assertEq(returnedImmutables.orderHash, bytes32(block.timestamp)); assertEq(returnedImmutables.hashlock, keccak256(abi.encodePacked(secret))); assertEq(returnedImmutables.amount, amount); + assertEq(returnedImmutables.packedAddresses.maker(), alice.addr); + assertEq(returnedImmutables.packedAddresses.taker(), bob.addr); + assertEq(returnedImmutables.packedAddresses.token(), address(dai)); + assertEq(returnedImmutables.timelocks.dstFinalityDuration(), dstTimelocks.finality); + assertEq(returnedImmutables.timelocks.dstWithdrawalDuration(), dstTimelocks.withdrawal); + assertEq(returnedImmutables.timelocks.dstPubWithdrawalDuration(), dstTimelocks.publicWithdrawal); } function test_NoInsufficientBalanceNativeDeploymentForMaker() public { @@ -158,7 +181,7 @@ contract EscrowFactoryTest is BaseSetup { // deploy escrow vm.prank(bob.addr); vm.expectRevert(IEscrowFactory.InvalidCreationTime.selector); - escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); + escrowFactory.createEscrowDst{value: DST_SAFETY_DEPOSIT}(immutables); } function test_NoInsufficientBalanceDeploymentForTaker() public { @@ -167,7 +190,7 @@ contract EscrowFactoryTest is BaseSetup { // deploy escrow vm.prank(bob.addr); vm.expectRevert(IEscrowFactory.InsufficientEscrowBalance.selector); - escrowFactory.createEscrow(immutables); + escrowFactory.createEscrowDst(immutables); } /* solhint-enable func-name-mixedcase */ diff --git a/test/utils/BaseSetup.sol b/test/utils/BaseSetup.sol index c5c27b1..eba79fd 100644 --- a/test/utils/BaseSetup.sol +++ b/test/utils/BaseSetup.sol @@ -9,17 +9,47 @@ import { MakerTraits, MakerTraitsLib } from "limit-order-protocol/libraries/Make import { TakerTraits } from "limit-order-protocol/libraries/TakerTraitsLib.sol"; import { WrappedTokenMock } from "limit-order-protocol/mocks/WrappedTokenMock.sol"; import { IFeeBank } from "limit-order-settlement/interfaces/IFeeBank.sol"; -import { Address } from "solidity-utils/libraries/AddressLib.sol"; +import { Address, AddressLib } from "solidity-utils/libraries/AddressLib.sol"; import { TokenCustomDecimalsMock } from "solidity-utils/mocks/TokenCustomDecimalsMock.sol"; import { TokenMock } from "solidity-utils/mocks/TokenMock.sol"; -import { Escrow, IEscrow } from "../../contracts/Escrow.sol"; -import { EscrowFactory, IEscrowFactory } from "../../contracts/EscrowFactory.sol"; +import { Escrow } from "contracts/Escrow.sol"; +import { EscrowFactory, IEscrowFactory } from "contracts/EscrowFactory.sol"; +import { PackedAddresses, PackedAddressesMemLib } from "./libraries/PackedAddressesMemLib.sol"; +import { Timelocks, TimelocksSettersLib } from "./libraries/TimelocksSettersLib.sol"; +import { IEscrow } from "contracts/interfaces/IEscrow.sol"; import { Utils, VmSafe } from "./Utils.sol"; contract BaseSetup is Test { + using AddressLib for Address; using MakerTraitsLib for MakerTraits; + using PackedAddressesMemLib for PackedAddresses; + using TimelocksSettersLib for Timelocks; + + /** + * Timelocks for the source chain. + * finality: The duration of the chain finality period. + * withdrawal: The duration of the period when only the taker with a secret can withdraw tokens for the taker. + * cancel: The duration of the period when escrow can only be cancelled by the taker. + */ + struct SrcTimelocks { + uint256 finality; + uint256 withdrawal; + uint256 cancel; + } + + /** + * Timelocks for the destination chain. + * finality: The duration of the chain finality period. + * withdrawal: The duration of the period when only the taker with a secret can withdraw tokens for the maker. + * publicWithdrawal: The duration of the period when anyone with a secret can withdraw tokens for the maker. + */ + struct DstTimelocks { + uint256 finality; + uint256 withdrawal; + uint256 publicWithdrawal; + } struct InteractionParams { bytes makerAssetSuffix; @@ -70,7 +100,6 @@ contract BaseSetup is Test { uint256 internal constant DST_SAFETY_DEPOSIT = 0.05 ether; uint32 internal constant RESOLVER_FEE = 100; - Utils internal utils; VmSafe.Wallet[] internal users; VmSafe.Wallet internal alice; @@ -86,33 +115,25 @@ contract BaseSetup is Test { Escrow internal escrow; IFeeBank internal feeBank; - IEscrow.SrcTimelocks internal srcTimelocks = IEscrow.SrcTimelocks({ + Timelocks internal timelocks; + Timelocks internal timelocksDst; + + SrcTimelocks internal srcTimelocks = SrcTimelocks({ finality: 120, withdrawal: 900, cancel: 110 }); - IEscrow.DstTimelocks internal dstTimelocks = IEscrow.DstTimelocks({ + DstTimelocks internal dstTimelocks = DstTimelocks({ finality: 300, withdrawal: 240, publicWithdrawal: 360 }); - MakerTraitsParams internal makerTraitsParams = MakerTraitsParams({ - allowedSender: address(0), - shouldCheckEpoch: false, - allowPartialFill: true, - allowMultipleFills: true, - usePermit2: false, - unwrapWeth: false, - expiry: 0, - nonce: 0, - series: 0 - }); /* solhint-enable private-vars-leading-underscore */ receive() external payable {} function setUp() public virtual { - utils = new Utils(); + Utils utils = new Utils(); users = utils.createUsers(2); alice = users[0]; @@ -125,6 +146,8 @@ contract BaseSetup is Test { usdc.mint(alice.addr, 1000 ether); inch.mint(bob.addr, 1000 ether); + _setTimelocks(); + _deployContracts(); vm.startPrank(bob.addr); @@ -148,6 +171,22 @@ contract BaseSetup is Test { vm.label(address(inch), "1INCH"); } + function _setTimelocks() internal { + timelocks = TimelocksSettersLib.init( + srcTimelocks.finality, + srcTimelocks.withdrawal, + srcTimelocks.cancel, + dstTimelocks.finality, + dstTimelocks.withdrawal, + dstTimelocks.publicWithdrawal, + block.timestamp + ); + timelocksDst = timelocks + .setSrcFinalityDuration(0) + .setSrcWithdrawalDuration(0) + .setSrcCancellationDuration(0); + } + function _deployContracts() internal { limitOrderProtocol = new LimitOrderProtocol(IWETH(weth)); @@ -161,6 +200,7 @@ contract BaseSetup is Test { function _buidDynamicData( bytes32 secret, + PackedAddresses memory packedAddresses, uint256 chainId, address token, uint256 srcSafetyDeposit, @@ -170,16 +210,11 @@ contract BaseSetup is Test { return ( abi.encode( hashlock, + packedAddresses, chainId, token, - srcSafetyDeposit, - dstSafetyDeposit, - srcTimelocks.finality, - srcTimelocks.withdrawal, - srcTimelocks.cancel, - dstTimelocks.finality, - dstTimelocks.withdrawal, - dstTimelocks.publicWithdrawal + (srcSafetyDeposit << 128) | dstSafetyDeposit, + timelocks ) ); } @@ -196,14 +231,18 @@ contract BaseSetup is Test { bytes memory extension, Escrow srcClone ) { - uint256 srcSafetyDeposit = srcAmount * 10 / 100; - uint256 dstSafetyDeposit = dstAmount * 10 / 100; + PackedAddresses memory packedAddresses = PackedAddressesMemLib.packAddresses( + alice.addr, + bob.addr, + address(usdc) + ); extraData = _buidDynamicData( secret, + packedAddresses, block.chainid, address(dai), - srcSafetyDeposit, - dstSafetyDeposit + srcAmount * 10 / 100, + dstAmount * 10 / 100 ); bytes memory whitelist = abi.encodePacked( @@ -212,13 +251,6 @@ contract BaseSetup is Test { uint16(0) // time delta ); - bytes memory postInteractionData = abi.encodePacked( - address(escrowFactory), - RESOLVER_FEE, - extraData, - whitelist - ); - if (fakeOrder) { order = IOrderMixin.Order({ salt: 0, @@ -231,6 +263,13 @@ contract BaseSetup is Test { makerTraits: MakerTraits.wrap(0) }); } else { + bytes memory postInteractionData = abi.encodePacked( + address(escrowFactory), + RESOLVER_FEE, + extraData, + whitelist + ); + (order, extension) = _buildOrder( alice.addr, bob.addr, @@ -244,8 +283,17 @@ contract BaseSetup is Test { ); } orderHash = limitOrderProtocol.hashOrder(order); + bytes memory interactionParams = abi.encode( + orderHash, + srcAmount, + dstAmount + ); + bytes memory data = abi.encodePacked( + interactionParams, + extraData + ); - srcClone = Escrow(escrowFactory.addressOfEscrow(orderHash)); + srcClone = Escrow(escrowFactory.addressOfEscrow(data)); extraData = abi.encodePacked( RESOLVER_FEE, extraData, @@ -267,11 +315,7 @@ contract BaseSetup is Test { IEscrowFactory.DstEscrowImmutablesCreation memory escrowImmutables, bytes memory data ) = _buildDstEscrowImmutables(secret, amount, maker, taker, token); - address msgSender = bob.addr; - uint256 deployedAt = block.timestamp; - bytes32 salt = keccak256(abi.encodePacked(deployedAt, data, msgSender)); - Escrow dstClone = Escrow(escrowFactory.addressOfEscrow(salt)); - return (escrowImmutables, dstClone); + return (escrowImmutables, Escrow(escrowFactory.addressOfEscrow(data))); } function _buildDstEscrowImmutables( @@ -287,28 +331,21 @@ contract BaseSetup is Test { bytes32 hashlock = keccak256(abi.encodePacked(secret)); uint256 safetyDeposit = amount * 10 / 100; uint256 srcCancellationTimestamp = block.timestamp + srcTimelocks.finality + srcTimelocks.withdrawal; + PackedAddresses memory packedAddresses = PackedAddressesMemLib.packAddresses(maker, taker, token); + + IEscrow.DstEscrowImmutables memory args = IEscrow.DstEscrowImmutables({ + orderHash: bytes32(block.timestamp), // fake order hash + hashlock: hashlock, + packedAddresses: packedAddresses, + amount: amount, + safetyDeposit: safetyDeposit, + timelocks: timelocksDst + }); immutables = IEscrowFactory.DstEscrowImmutablesCreation( - hashlock, - maker, - taker, - token, - amount, - safetyDeposit, - dstTimelocks, + args, srcCancellationTimestamp ); - data = abi.encode( - hashlock, - alice.addr, - bob.addr, - block.chainid, - address(dai), - amount, - safetyDeposit, - dstTimelocks.finality, - dstTimelocks.withdrawal, - dstTimelocks.publicWithdrawal - ); + data = abi.encode(args); } function _buildMakerTraits(MakerTraitsParams memory params) internal pure returns(MakerTraits) { @@ -336,7 +373,18 @@ contract BaseSetup is Test { MakerTraits makerTraits, InteractionParams memory interactions, bytes memory customData - ) internal view returns(IOrderMixin.Order memory, bytes memory) { + ) internal pure returns(IOrderMixin.Order memory, bytes memory) { + MakerTraitsParams memory makerTraitsParams = MakerTraitsParams({ + allowedSender: address(0), + shouldCheckEpoch: false, + allowPartialFill: true, + allowMultipleFills: true, + usePermit2: false, + unwrapWeth: false, + expiry: 0, + nonce: 0, + series: 0 + }); bytes[8] memory allInteractions = [ interactions.makerAssetSuffix, interactions.takerAssetSuffix, diff --git a/test/utils/libraries/PackedAddressesMemLib.sol b/test/utils/libraries/PackedAddressesMemLib.sol new file mode 100644 index 0000000..f1e7db6 --- /dev/null +++ b/test/utils/libraries/PackedAddressesMemLib.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.23; + +import { PackedAddresses, PackedAddressesLib } from "contracts/libraries/PackedAddressesLib.sol"; + +library PackedAddressesMemLib { + /** + * @notice Packs the addresses into two uint256 values. + * @param makerAddr The maker address. + * @param takerAddr The taker address. + * @param tokenAddr The token address. + * @return The packed addresses. + */ + function packAddresses(address makerAddr, address takerAddr, address tokenAddr) internal pure returns (PackedAddresses memory) { + return PackedAddresses({ + addressesPart1: uint256(uint256(uint160(makerAddr)) << 96 | (uint160(takerAddr) >> 80)), + addressesPart2: uint256(uint256(uint160(takerAddr)) << 176 | uint160(tokenAddr)) + }); + } + + /** + * @notice Returns the maker address from the packed addresses. + * @param packedAddresses Packed addresses. + * @return The maker address. + */ + function maker(PackedAddresses memory packedAddresses) internal pure returns (address) { + return PackedAddressesLib._maker(packedAddresses.addressesPart1); + } + + /** + * @notice Returns the taker address from the packed addresses. + * @param packedAddresses Packed addresses. + * @return The taker address. + */ + function taker(PackedAddresses memory packedAddresses) internal pure returns (address) { + return PackedAddressesLib._taker(packedAddresses.addressesPart1, packedAddresses.addressesPart2); + } + + /** + * @notice Returns the taker address from the packed addresses. + * @param packedAddresses Packed addresses. + * @return The taker address. + */ + function token(PackedAddresses memory packedAddresses) internal pure returns (address) { + return PackedAddressesLib._token(packedAddresses.addressesPart2); + } +} diff --git a/test/utils/libraries/TimelocksSettersLib.sol b/test/utils/libraries/TimelocksSettersLib.sol new file mode 100644 index 0000000..283fb68 --- /dev/null +++ b/test/utils/libraries/TimelocksSettersLib.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.23; + +import { Timelocks, TimelocksLib } from "contracts/libraries/TimelocksLib.sol"; + +/** + * @title Library with setters for Timelocks. + */ +library TimelocksSettersLib { + /** + * @notice Initializes the timelocks. + * @param srcFinality Duration of the finality period on the source chain. + * @param srcWithdrawal Duration of the private withdrawal period on the source chain. + * @param srcCancellation Duration of the private cancellation period on the source chain. + * @param dstFinality Duration of the finality period on the destination chain. + * @param dstWithdrawal Duration of the private withdrawal period on the destination chain. + * @param dstPubWithdrawal Duration of the public withdrawal period on the destination chain. + * @param deployedAtVal Deployment timestamp. + * @return The initialized Timelocks. + */ + function init( + uint256 srcFinality, + uint256 srcWithdrawal, + uint256 srcCancellation, + uint256 dstFinality, + uint256 dstWithdrawal, + uint256 dstPubWithdrawal, + uint256 deployedAtVal + ) internal pure returns (Timelocks) { + return Timelocks.wrap(0 | + ((srcFinality & TimelocksLib._TIMELOCK_MASK) << TimelocksLib._SRC_FINALITY_OFFSET) | + ((srcWithdrawal & TimelocksLib._TIMELOCK_MASK) << TimelocksLib._SRC_WITHDRAWAL_OFFSET) | + ((srcCancellation & TimelocksLib._TIMELOCK_MASK) << TimelocksLib._SRC_CANCELLATION_OFFSET) | + ((dstFinality & TimelocksLib._TIMELOCK_MASK) << TimelocksLib._DST_FINALITY_OFFSET) | + ((dstWithdrawal & TimelocksLib._TIMELOCK_MASK) << TimelocksLib._DST_WITHDRAWAL_OFFSET) | + ((dstPubWithdrawal & TimelocksLib._TIMELOCK_MASK) << TimelocksLib._DST_PUB_WITHDRAWAL_OFFSET) | + uint40(deployedAtVal) + ); + } + + /** + * @notice Sets the duration of the finality period on the source chain. + * @param timelocks The timelocks to set the finality duration to. + * @param value The new duration of the finality period. + * @return The timelocks with the finality duration set. + */ + function setSrcFinalityDuration(Timelocks timelocks, uint256 value) internal pure returns (Timelocks) { + return Timelocks.wrap( + // Clear the finality duration bits and set the new value. + (Timelocks.unwrap(timelocks) & ~(TimelocksLib._TIMELOCK_MASK << TimelocksLib._SRC_FINALITY_OFFSET)) | + ((value & TimelocksLib._TIMELOCK_MASK) << TimelocksLib._SRC_FINALITY_OFFSET) + ); + } + + + /** + * @notice Sets the duration of the private withdrawal period on the source chain. + * @param timelocks The timelocks to set the private withdrawal duration to. + * @param value The new duration of the private withdrawal period. + * @return The timelocks with the private withdrawal duration set. + */ + function setSrcWithdrawalDuration(Timelocks timelocks, uint256 value) internal pure returns (Timelocks) { + return Timelocks.wrap( + // Clear the private withdrawal duration bits and set the new value. + (Timelocks.unwrap(timelocks) & ~(TimelocksLib._TIMELOCK_MASK << TimelocksLib._SRC_WITHDRAWAL_OFFSET)) | + ((value & TimelocksLib._TIMELOCK_MASK) << TimelocksLib._SRC_WITHDRAWAL_OFFSET) + ); + } + + /** + * @notice Sets the duration of the private cancellation period on the source chain. + * @param timelocks The timelocks to set the private cancellation duration to. + * @param value The duration of the private cancellation period. + * @return The timelocks with the private cancellation duration set. + */ + function setSrcCancellationDuration(Timelocks timelocks, uint256 value) internal pure returns (Timelocks) { + return Timelocks.wrap( + // Clear the private cancellation duration bits and set the new value. + (Timelocks.unwrap(timelocks) & ~(TimelocksLib._TIMELOCK_MASK << TimelocksLib._SRC_CANCELLATION_OFFSET)) | + ((value & TimelocksLib._TIMELOCK_MASK) << TimelocksLib._SRC_CANCELLATION_OFFSET) + ); + } + + /** + * @notice Sets the duration of the finality period on the destination chain. + * @param timelocks The timelocks to set the finality duration to. + * @param value The duration of the finality period. + * @return The timelocks with the finality duration set. + */ + function setDstFinalityDuration(Timelocks timelocks, uint256 value) internal pure returns (Timelocks) { + return Timelocks.wrap( + // Clear the finality duration bits and set the new value. + (Timelocks.unwrap(timelocks) & ~(TimelocksLib._TIMELOCK_MASK << TimelocksLib._DST_FINALITY_OFFSET)) | + ((value & TimelocksLib._TIMELOCK_MASK) << TimelocksLib._DST_FINALITY_OFFSET) + ); + } + + /** + * @notice Sets the duration of the private withdrawal period on the destination chain. + * @param timelocks The timelocks to set the private withdrawal duration to. + * @param value The new duration of the private withdrawal period. + * @return The timelocks with the private withdrawal duration set. + */ + function setDstWithdrawalDuration(Timelocks timelocks, uint256 value) internal pure returns (Timelocks) { + return Timelocks.wrap( + // Clear the private withdrawal duration bits and set the new value. + (Timelocks.unwrap(timelocks) & ~(TimelocksLib._TIMELOCK_MASK << TimelocksLib._DST_WITHDRAWAL_OFFSET)) | + ((value & TimelocksLib._TIMELOCK_MASK) << TimelocksLib._DST_WITHDRAWAL_OFFSET) + ); + } + + /** + * @notice Sets the duration of the public withdrawal period on the destination chain. + * @param timelocks The timelocks to set the public withdrawal duration to. + * @param value The new duration of the public withdrawal period. + * @return The timelocks with the public withdrawal duration set. + */ + function setDstPubWithdrawalDuration(Timelocks timelocks, uint256 value) internal pure returns (Timelocks) { + return Timelocks.wrap( + // Clear the public withdrawal duration bits and set the new value. + (Timelocks.unwrap(timelocks) & ~(TimelocksLib._TIMELOCK_MASK << TimelocksLib._DST_PUB_WITHDRAWAL_OFFSET)) | + ((value & TimelocksLib._TIMELOCK_MASK) << TimelocksLib._DST_PUB_WITHDRAWAL_OFFSET) + ); + } +}