From de6e161fce4188d517683fb6784a61711ae6520d Mon Sep 17 00:00:00 2001 From: byshape Date: Thu, 11 Jan 2024 14:37:10 +0000 Subject: [PATCH] Added safety deposit for the source chain --- contracts/Escrow.sol | 37 +++--- contracts/EscrowFactory.sol | 23 +++- contracts/interfaces/IEscrow.sol | 22 +++- contracts/interfaces/IEscrowFactory.sol | 2 +- test/integration/EscrowFactory.t.sol | 10 +- test/unit/Escrow.t.sol | 145 ++++++++++++++++++++---- test/unit/EscrowFactory.t.sol | 19 +++- test/utils/BaseSetup.sol | 25 ++-- 8 files changed, 216 insertions(+), 67 deletions(-) diff --git a/contracts/Escrow.sol b/contracts/Escrow.sol index 1923cbd..451ed29 100644 --- a/contracts/Escrow.sol +++ b/contracts/Escrow.sol @@ -27,19 +27,31 @@ contract Escrow is Clone, IEscrow { escrowImmutables.interactionParams.srcToken, escrowImmutables.interactionParams.srcAmount ); + (bool success, ) = msg.sender.call{value: escrowImmutables.extraDataParams.srcSafetyDeposit}(""); + if (!success) revert NativeTokenSendingFailure(); } function cancelSrc() external { SrcEscrowImmutables calldata escrowImmutables = srcEscrowImmutables(); uint256 finalityTimestamp = escrowImmutables.deployedAt + escrowImmutables.extraDataParams.srcTimelocks.finality; - if (block.timestamp < finalityTimestamp + escrowImmutables.extraDataParams.srcTimelocks.publicUnlock) { + uint256 cancellationTimestamp = finalityTimestamp + escrowImmutables.extraDataParams.srcTimelocks.publicUnlock; + if (block.timestamp < cancellationTimestamp) { revert InvalidCancellationTime(); } + if ( + block.timestamp < cancellationTimestamp + escrowImmutables.extraDataParams.srcTimelocks.cancel && + msg.sender != escrowImmutables.interactionParams.taker + ) { + revert InvalidCaller(); + } + IERC20(escrowImmutables.interactionParams.srcToken).safeTransfer( escrowImmutables.interactionParams.maker, escrowImmutables.interactionParams.srcAmount ); + (bool success, ) = msg.sender.call{value: escrowImmutables.extraDataParams.srcSafetyDeposit}(""); + if (!success) revert NativeTokenSendingFailure(); } function withdrawDst(bytes32 secret) external { @@ -51,9 +63,8 @@ contract Escrow is Clone, IEscrow { block.timestamp > unlockTimestamp + escrowImmutables.timelocks.publicUnlock ) revert InvalidWithdrawalTime(); - if (block.timestamp < unlockTimestamp) { - if (msg.sender != escrowImmutables.taker) revert InvalidCaller(); - } + if (block.timestamp < unlockTimestamp && msg.sender != escrowImmutables.taker) revert InvalidCaller(); + _checkSecretAndTransfer( secret, escrowImmutables.hashlock, @@ -61,10 +72,8 @@ contract Escrow is Clone, IEscrow { escrowImmutables.token, escrowImmutables.amount ); - IERC20(escrowImmutables.token).safeTransfer( - msg.sender, - escrowImmutables.safetyDeposit - ); + (bool success, ) = msg.sender.call{value: escrowImmutables.safetyDeposit}(""); + if (!success) revert NativeTokenSendingFailure(); } function cancelDst() external { @@ -79,10 +88,8 @@ contract Escrow is Clone, IEscrow { escrowImmutables.amount ); - IERC20(escrowImmutables.token).safeTransfer( - msg.sender, - escrowImmutables.safetyDeposit - ); + (bool success, ) = msg.sender.call{value: escrowImmutables.safetyDeposit}(""); + if (!success) revert NativeTokenSendingFailure(); } function srcEscrowImmutables() public pure returns (SrcEscrowImmutables calldata) { @@ -101,11 +108,11 @@ contract Escrow is Clone, IEscrow { return data; } - function _isValidSecret(bytes32 secret, uint256 hashlock) internal pure returns (bool) { - return uint256(keccak256(abi.encode(secret))) == hashlock; + function _isValidSecret(bytes32 secret, bytes32 hashlock) internal pure returns (bool) { + return keccak256(abi.encode(secret)) == hashlock; } - function _checkSecretAndTransfer(bytes32 secret, uint256 hashlock, address recipient, address token, uint256 amount) internal { + function _checkSecretAndTransfer(bytes32 secret, bytes32 hashlock, address recipient, address token, uint256 amount) internal { if (!_isValidSecret(secret, hashlock)) revert InvalidSecret(); IERC20(token).safeTransfer(recipient, amount); } diff --git a/contracts/EscrowFactory.sol b/contracts/EscrowFactory.sol index b9300d9..d5287fc 100644 --- a/contracts/EscrowFactory.sol +++ b/contracts/EscrowFactory.sol @@ -10,6 +10,7 @@ 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 { IEscrowFactory } from "./interfaces/IEscrowFactory.sol"; contract EscrowFactory is IEscrowFactory, ExtensionBase { @@ -51,14 +52,19 @@ contract EscrowFactory is IEscrowFactory, ExtensionBase { ); // Salt is orderHash address escrow = ClonesWithImmutableArgs.addressOfClone3(orderHash); - if (IERC20(order.makerAsset.get()).balanceOf(escrow) < makingAmount) revert InsufficientEscrowBalance(); + uint256 safetyDeposit = abi.decode(extraData, (IEscrow.ExtraDataParams)).srcSafetyDeposit; + if ( + escrow.balance < safetyDeposit || + IERC20(order.makerAsset.get()).balanceOf(escrow) < makingAmount + ) revert InsufficientEscrowBalance(); _createEscrow(data, orderHash); } /** * @dev Creates a new escrow contract for taker. */ - function createEscrow(DstEscrowImmutablesCreation calldata dstEscrowImmutables) external { + function createEscrow(DstEscrowImmutablesCreation calldata dstEscrowImmutables) external payable { + if (msg.value < dstEscrowImmutables.safetyDeposit) revert InsufficientEscrowBalance(); // Check that the escrow cancellation will start not later than the cancellation time on the source chain. if ( block.timestamp + @@ -81,14 +87,19 @@ contract EscrowFactory is IEscrowFactory, ExtensionBase { dstEscrowImmutables.timelocks.publicUnlock ); bytes32 salt = keccak256(abi.encodePacked(data, msg.sender)); - address escrow = _createEscrow(data, salt); + + address escrow = addressOfEscrow(salt); + (bool success, ) = escrow.call{value: dstEscrowImmutables.safetyDeposit}(""); + if (!success) revert IEscrow.NativeTokenSendingFailure(); + + _createEscrow(data, salt); IERC20(dstEscrowImmutables.token).safeTransferFrom( - msg.sender, escrow, dstEscrowImmutables.amount + dstEscrowImmutables.safetyDeposit + msg.sender, escrow, dstEscrowImmutables.amount ); } - function addressOfEscrow(bytes32 salt) external view returns (address) { - return ClonesWithImmutableArgs.addressOfClone3(salt); + function addressOfEscrow(bytes32 salt) public view returns (address) { + return address(uint160(ClonesWithImmutableArgs.addressOfClone3(salt))); } function _createEscrow( diff --git a/contracts/interfaces/IEscrow.sol b/contracts/interfaces/IEscrow.sol index 9fbf740..40ecfc5 100644 --- a/contracts/interfaces/IEscrow.sol +++ b/contracts/interfaces/IEscrow.sol @@ -4,12 +4,24 @@ pragma solidity ^0.8.0; interface IEscrow { // TODO: is it possible to optimise this? - // Timelocks represent the duration of each period, in seconds + /** + * 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. + */ struct SrcTimelocks { uint256 finality; uint256 publicUnlock; + uint256 cancel; } + /** + * 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. + */ struct DstTimelocks { uint256 finality; uint256 unlock; @@ -25,10 +37,11 @@ interface IEscrow { } struct ExtraDataParams { - uint256 hashlock; + bytes32 hashlock; uint256 dstChainId; address dstToken; - uint256 safetyDeposit; + uint256 srcSafetyDeposit; + uint256 dstSafetyDeposit; SrcTimelocks srcTimelocks; DstTimelocks dstTimelocks; } @@ -41,7 +54,7 @@ interface IEscrow { struct DstEscrowImmutables { uint256 deployedAt; - uint256 hashlock; + bytes32 hashlock; address maker; address taker; uint256 chainId; @@ -55,4 +68,5 @@ interface IEscrow { error InvalidCancellationTime(); error InvalidSecret(); error InvalidWithdrawalTime(); + error NativeTokenSendingFailure(); } diff --git a/contracts/interfaces/IEscrowFactory.sol b/contracts/interfaces/IEscrowFactory.sol index 84d00fe..f903cef 100644 --- a/contracts/interfaces/IEscrowFactory.sol +++ b/contracts/interfaces/IEscrowFactory.sol @@ -6,7 +6,7 @@ import { IEscrow } from "./IEscrow.sol"; interface IEscrowFactory { struct DstEscrowImmutablesCreation { - uint256 hashlock; + bytes32 hashlock; address maker; address taker; address token; diff --git a/test/integration/EscrowFactory.t.sol b/test/integration/EscrowFactory.t.sol index fb9348e..1728228 100644 --- a/test/integration/EscrowFactory.t.sol +++ b/test/integration/EscrowFactory.t.sol @@ -37,6 +37,9 @@ contract IntegrationEscrowFactoryTest is BaseSetup { 0 // threshold ); + (bool success,) = address(srcClone).call{value: uint64(srcAmount) * 10 / 100}(""); + assertEq(success, true); + vm.prank(bob.addr); limitOrderProtocol.fillOrderArgs( order, @@ -48,7 +51,7 @@ contract IntegrationEscrowFactoryTest is BaseSetup { ); IEscrow.SrcEscrowImmutables memory returnedImmutables = srcClone.srcEscrowImmutables(); - assertEq(returnedImmutables.extraDataParams.hashlock, uint256(keccak256(abi.encodePacked(secret)))); + assertEq(returnedImmutables.extraDataParams.hashlock, keccak256(abi.encodePacked(secret))); assertEq(returnedImmutables.interactionParams.srcAmount, srcAmount); assertEq(returnedImmutables.extraDataParams.dstToken, address(dai)); } @@ -59,7 +62,7 @@ contract IntegrationEscrowFactoryTest is BaseSetup { bytes32 orderHash, /* bytes memory extraData */, bytes memory extension, - /* Escrow srcClone */ + Escrow srcClone ) = _prepareDataSrc(SECRET, MAKING_AMOUNT, TAKING_AMOUNT, false); (uint8 v, bytes32 r, bytes32 s) = vm.sign(alice, orderHash); @@ -76,6 +79,9 @@ contract IntegrationEscrowFactoryTest is BaseSetup { 0 // threshold ); + (bool success,) = address(srcClone).call{value: SRC_SAFETY_DEPOSIT}(""); + assertEq(success, true); + vm.prank(bob.addr); vm.expectRevert(IEscrowFactory.InsufficientEscrowBalance.selector); limitOrderProtocol.fillOrderArgs( diff --git a/test/unit/Escrow.t.sol b/test/unit/Escrow.t.sol index 2145351..34c1dc5 100644 --- a/test/unit/Escrow.t.sol +++ b/test/unit/Escrow.t.sol @@ -25,6 +25,8 @@ contract EscrowTest is BaseSetup { Escrow srcClone ) = _prepareDataSrc(SECRET, MAKING_AMOUNT, TAKING_AMOUNT, true); + (bool success,) = address(srcClone).call{value: SRC_SAFETY_DEPOSIT}(""); + assertEq(success, true); usdc.transfer(address(srcClone), MAKING_AMOUNT); vm.prank(address(limitOrderProtocol)); @@ -52,7 +54,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.startPrank(bob.addr); - escrowFactory.createEscrow(immutables); + escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); // withdraw vm.expectRevert(IEscrow.InvalidWithdrawalTime.selector); @@ -69,6 +71,8 @@ contract EscrowTest is BaseSetup { Escrow srcClone ) = _prepareDataSrc(SECRET, MAKING_AMOUNT, TAKING_AMOUNT, true); + (bool success,) = address(srcClone).call{value: SRC_SAFETY_DEPOSIT}(""); + assertEq(success, true); usdc.transfer(address(srcClone), MAKING_AMOUNT); vm.prank(address(limitOrderProtocol)); @@ -83,6 +87,7 @@ contract EscrowTest is BaseSetup { extraData ); + uint256 balanceThis = address(this).balance; uint256 balanceBob = usdc.balanceOf(bob.addr); uint256 balanceEscrow = usdc.balanceOf(address(srcClone)); @@ -90,6 +95,7 @@ contract EscrowTest is BaseSetup { vm.warp(block.timestamp + srcTimelocks.finality + 100); srcClone.withdrawSrc(SECRET); + assertEq(address(this).balance, balanceThis + SRC_SAFETY_DEPOSIT); assertEq(usdc.balanceOf(bob.addr), balanceBob + MAKING_AMOUNT); assertEq(usdc.balanceOf(address(srcClone)), balanceEscrow - (MAKING_AMOUNT)); } @@ -102,19 +108,21 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.startPrank(bob.addr); - escrowFactory.createEscrow(immutables); + escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); uint256 balanceAlice = dai.balanceOf(alice.addr); - uint256 balanceBob = dai.balanceOf(bob.addr); + uint256 balanceBob = bob.addr.balance; uint256 balanceEscrow = dai.balanceOf(address(dstClone)); + uint256 balanceEscrowNative = address(dstClone).balance; // withdraw vm.warp(block.timestamp + dstTimelocks.finality + 10); dstClone.withdrawDst(SECRET); assertEq(dai.balanceOf(alice.addr), balanceAlice + TAKING_AMOUNT); - assertEq(dai.balanceOf(bob.addr), balanceBob + SAFETY_DEPOSIT); - assertEq(dai.balanceOf(address(dstClone)), balanceEscrow - (TAKING_AMOUNT + SAFETY_DEPOSIT)); + assertEq(bob.addr.balance, balanceBob + DST_SAFETY_DEPOSIT); + assertEq(dai.balanceOf(address(dstClone)), balanceEscrow - TAKING_AMOUNT); + assertEq(address(dstClone).balance, balanceEscrowNative - DST_SAFETY_DEPOSIT); } function test_NoWithdrawalWithWrongSecretSrc() public { @@ -127,6 +135,8 @@ contract EscrowTest is BaseSetup { Escrow srcClone ) = _prepareDataSrc(SECRET, MAKING_AMOUNT, TAKING_AMOUNT, true); + (bool success,) = address(srcClone).call{value: SRC_SAFETY_DEPOSIT}(""); + assertEq(success, true); usdc.transfer(address(srcClone), MAKING_AMOUNT); vm.prank(address(limitOrderProtocol)); @@ -155,7 +165,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.startPrank(bob.addr); - escrowFactory.createEscrow(immutables); + escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); // withdraw vm.warp(block.timestamp + dstTimelocks.finality + 100); @@ -164,7 +174,7 @@ contract EscrowTest is BaseSetup { } // During non-public unlock period - function test_NoWithdrawalByNonResolvertDst() public { + function test_NoWithdrawalByNonResolverDst() public { ( IEscrowFactory.DstEscrowImmutablesCreation memory immutables, Escrow dstClone @@ -172,7 +182,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.prank(bob.addr); - escrowFactory.createEscrow(immutables); + escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); // withdraw vm.warp(block.timestamp + dstTimelocks.finality + 100); @@ -181,7 +191,7 @@ contract EscrowTest is BaseSetup { } // During public unlock period - function test_WithdrawByAnyonetDst() public { + function test_WithdrawByAnyoneDst() public { ( IEscrowFactory.DstEscrowImmutablesCreation memory immutables, Escrow dstClone @@ -189,19 +199,21 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.prank(bob.addr); - escrowFactory.createEscrow(immutables); + escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); uint256 balanceAlice = dai.balanceOf(alice.addr); - uint256 balanceThis = dai.balanceOf(address(this)); + uint256 balanceThis = address(this).balance; uint256 balanceEscrow = dai.balanceOf(address(dstClone)); + uint256 balanceEscrowNative = address(dstClone).balance; // withdraw vm.warp(block.timestamp + dstTimelocks.finality + dstTimelocks.unlock + 100); dstClone.withdrawDst(SECRET); assertEq(dai.balanceOf(alice.addr), balanceAlice + TAKING_AMOUNT); - assertEq(dai.balanceOf(address(this)), balanceThis + SAFETY_DEPOSIT); - assertEq(dai.balanceOf(address(dstClone)), balanceEscrow - (TAKING_AMOUNT + SAFETY_DEPOSIT)); + assertEq(address(this).balance, balanceThis + DST_SAFETY_DEPOSIT); + assertEq(dai.balanceOf(address(dstClone)), balanceEscrow - TAKING_AMOUNT); + assertEq(address(dstClone).balance, balanceEscrowNative - DST_SAFETY_DEPOSIT); } // During public unlock period @@ -213,22 +225,64 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.startPrank(bob.addr); - escrowFactory.createEscrow(immutables); + escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); uint256 balanceAlice = dai.balanceOf(alice.addr); - uint256 balanceBob = dai.balanceOf(bob.addr); + uint256 balanceBob = bob.addr.balance; uint256 balanceEscrow = dai.balanceOf(address(dstClone)); + uint256 balanceEscrowNative = address(dstClone).balance; // withdraw vm.warp(block.timestamp + dstTimelocks.finality + dstTimelocks.unlock + 100); dstClone.withdrawDst(SECRET); assertEq(dai.balanceOf(alice.addr), balanceAlice + TAKING_AMOUNT); - assertEq(dai.balanceOf(bob.addr), balanceBob + SAFETY_DEPOSIT); - assertEq(dai.balanceOf(address(dstClone)), balanceEscrow - (TAKING_AMOUNT + SAFETY_DEPOSIT)); + assertEq(bob.addr.balance, balanceBob + DST_SAFETY_DEPOSIT); + assertEq(dai.balanceOf(address(dstClone)), balanceEscrow - TAKING_AMOUNT); + assertEq(address(dstClone).balance, balanceEscrowNative - DST_SAFETY_DEPOSIT); + } + + function test_CancelResolverSrc() public { + // deploy escrow + ( + IOrderMixin.Order memory order, + bytes32 orderHash, + bytes memory extraData, + /* bytes memory extension */, + Escrow srcClone + ) = _prepareDataSrc(SECRET, MAKING_AMOUNT, TAKING_AMOUNT, true); + + (bool success,) = address(srcClone).call{value: SRC_SAFETY_DEPOSIT}(""); + assertEq(success, true); + usdc.transfer(address(srcClone), MAKING_AMOUNT); + + vm.prank(address(limitOrderProtocol)); + escrowFactory.postInteraction( + order, + "", // extension + orderHash, + bob.addr, // taker + MAKING_AMOUNT, + TAKING_AMOUNT, + 0, // remainingMakingAmount + extraData + ); + + uint256 balanceBob = bob.addr.balance; + uint256 balanceAlice = usdc.balanceOf(alice.addr); + uint256 balanceEscrow = usdc.balanceOf(address(srcClone)); + + // cancel + vm.warp(block.timestamp + srcTimelocks.finality + srcTimelocks.publicUnlock + 10); + vm.prank(bob.addr); + srcClone.cancelSrc(); + + assertEq(bob.addr.balance, balanceBob + SRC_SAFETY_DEPOSIT); + assertEq(usdc.balanceOf(alice.addr), balanceAlice + MAKING_AMOUNT); + assertEq(usdc.balanceOf(address(srcClone)), balanceEscrow - (MAKING_AMOUNT)); } - function test_CancelSrc() public { + function test_CancelPublicSrc() public { // deploy escrow ( IOrderMixin.Order memory order, @@ -238,6 +292,8 @@ contract EscrowTest is BaseSetup { Escrow srcClone ) = _prepareDataSrc(SECRET, MAKING_AMOUNT, TAKING_AMOUNT, true); + (bool success,) = address(srcClone).call{value: SRC_SAFETY_DEPOSIT}(""); + assertEq(success, true); usdc.transfer(address(srcClone), MAKING_AMOUNT); vm.prank(address(limitOrderProtocol)); @@ -252,13 +308,15 @@ contract EscrowTest is BaseSetup { extraData ); + uint256 balanceThis = address(this).balance; uint256 balanceAlice = usdc.balanceOf(alice.addr); uint256 balanceEscrow = usdc.balanceOf(address(srcClone)); // cancel - vm.warp(block.timestamp + srcTimelocks.finality + srcTimelocks.publicUnlock + 100); + vm.warp(block.timestamp + srcTimelocks.finality + srcTimelocks.publicUnlock + srcTimelocks.cancel + 100); srcClone.cancelSrc(); + assertEq(address(this).balance, balanceThis + SRC_SAFETY_DEPOSIT); assertEq(usdc.balanceOf(alice.addr), balanceAlice + MAKING_AMOUNT); assertEq(usdc.balanceOf(address(srcClone)), balanceEscrow - (MAKING_AMOUNT)); } @@ -273,6 +331,8 @@ contract EscrowTest is BaseSetup { Escrow srcClone ) = _prepareDataSrc(SECRET, MAKING_AMOUNT, TAKING_AMOUNT, true); + (bool success,) = address(srcClone).call{value: SRC_SAFETY_DEPOSIT}(""); + assertEq(success, true); usdc.transfer(address(srcClone), MAKING_AMOUNT); vm.prank(address(limitOrderProtocol)); @@ -293,6 +353,39 @@ contract EscrowTest is BaseSetup { srcClone.cancelSrc(); } + // During non-public cancel period + function test_NoAnyoneCancelDuringResolverCancelSrc() public { + // deploy escrow + ( + IOrderMixin.Order memory order, + bytes32 orderHash, + bytes memory extraData, + /* bytes memory extension */, + Escrow srcClone + ) = _prepareDataSrc(SECRET, MAKING_AMOUNT, TAKING_AMOUNT, true); + + (bool success,) = address(srcClone).call{value: SRC_SAFETY_DEPOSIT}(""); + assertEq(success, true); + usdc.transfer(address(srcClone), MAKING_AMOUNT); + + vm.prank(address(limitOrderProtocol)); + escrowFactory.postInteraction( + order, + "", // extension + orderHash, + bob.addr, // taker + MAKING_AMOUNT, + TAKING_AMOUNT, + 0, // remainingMakingAmount + extraData + ); + + // cancel + vm.warp(block.timestamp + srcTimelocks.finality + srcTimelocks.publicUnlock + 10); + vm.expectRevert(IEscrow.InvalidCaller.selector); + srcClone.cancelSrc(); + } + function test_CancelDst() public { ( IEscrowFactory.DstEscrowImmutablesCreation memory immutables, @@ -301,19 +394,21 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.prank(bob.addr); - escrowFactory.createEscrow(immutables); + escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); - uint256 balanceThis = dai.balanceOf(address(this)); + uint256 balanceThis = address(this).balance; uint256 balanceBob = dai.balanceOf(bob.addr); uint256 balanceEscrow = dai.balanceOf(address(dstClone)); + uint256 balanceEscrowNative = address(dstClone).balance; // cancel vm.warp(block.timestamp + dstTimelocks.finality + dstTimelocks.unlock + dstTimelocks.publicUnlock + 100); dstClone.cancelDst(); - assertEq(dai.balanceOf(address(this)), balanceThis + SAFETY_DEPOSIT); + assertEq(address(this).balance, balanceThis + DST_SAFETY_DEPOSIT); assertEq(dai.balanceOf(bob.addr), balanceBob + TAKING_AMOUNT); - assertEq(dai.balanceOf(address(dstClone)), balanceEscrow - (TAKING_AMOUNT + SAFETY_DEPOSIT)); + assertEq(dai.balanceOf(address(dstClone)), balanceEscrow - TAKING_AMOUNT); + assertEq(address(dstClone).balance, balanceEscrowNative - DST_SAFETY_DEPOSIT); } function test_NoCancelDuringResolverUnlockDst() public { @@ -324,7 +419,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.prank(bob.addr); - escrowFactory.createEscrow(immutables); + escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); // cancel vm.warp(block.timestamp + dstTimelocks.finality + 100); @@ -340,7 +435,7 @@ contract EscrowTest is BaseSetup { // deploy escrow vm.prank(bob.addr); - escrowFactory.createEscrow(immutables); + escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); // cancel vm.warp(block.timestamp + dstTimelocks.finality + dstTimelocks.unlock + 100); diff --git a/test/unit/EscrowFactory.t.sol b/test/unit/EscrowFactory.t.sol index 3330b7b..ffbcc81 100644 --- a/test/unit/EscrowFactory.t.sol +++ b/test/unit/EscrowFactory.t.sol @@ -22,6 +22,8 @@ contract EscrowFactoryTest is BaseSetup { Escrow srcClone ) = _prepareDataSrc(secret, srcAmount, dstAmount, true); + (bool success, ) = address(srcClone).call{value: uint64(srcAmount) * 10 / 100}(""); + assertEq(success, true); usdc.transfer(address(srcClone), srcAmount); vm.prank(address(limitOrderProtocol)); @@ -37,7 +39,7 @@ contract EscrowFactoryTest is BaseSetup { ); IEscrow.SrcEscrowImmutables memory returnedImmutables = srcClone.srcEscrowImmutables(); - assertEq(returnedImmutables.extraDataParams.hashlock, uint256(keccak256(abi.encodePacked(secret)))); + assertEq(returnedImmutables.extraDataParams.hashlock, keccak256(abi.encodePacked(secret))); assertEq(returnedImmutables.interactionParams.srcAmount, srcAmount); assertEq(returnedImmutables.extraDataParams.dstToken, address(dai)); } @@ -47,18 +49,23 @@ contract EscrowFactoryTest is BaseSetup { IEscrowFactory.DstEscrowImmutablesCreation memory immutables, Escrow dstClone ) = _prepareDataDst(secret, amount, alice.addr, bob.addr, address(dai)); + uint256 balanceBobNative = bob.addr.balance; uint256 balanceBob = dai.balanceOf(bob.addr); uint256 balanceEscrow = dai.balanceOf(address(dstClone)); + uint256 balanceEscrowNative = address(dstClone).balance; + uint256 safetyDeposit = uint64(amount) * 10 / 100; // deploy escrow vm.prank(bob.addr); - escrowFactory.createEscrow(immutables); + escrowFactory.createEscrow{value: safetyDeposit}(immutables); - assertEq(dai.balanceOf(bob.addr), balanceBob - (amount + immutables.safetyDeposit)); - assertEq(dai.balanceOf(address(dstClone)), balanceEscrow + amount + immutables.safetyDeposit); + assertEq(bob.addr.balance, balanceBobNative - immutables.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.hashlock, uint256(keccak256(abi.encodePacked(secret)))); + assertEq(returnedImmutables.hashlock, keccak256(abi.encodePacked(secret))); assertEq(returnedImmutables.amount, amount); } @@ -100,7 +107,7 @@ contract EscrowFactoryTest is BaseSetup { // deploy escrow vm.prank(bob.addr); vm.expectRevert(IEscrowFactory.InvalidCreationTime.selector); - escrowFactory.createEscrow(immutables); + escrowFactory.createEscrow{value: DST_SAFETY_DEPOSIT}(immutables); } /* solhint-enable func-name-mixedcase */ diff --git a/test/utils/BaseSetup.sol b/test/utils/BaseSetup.sol index 2d6f2fc..4641184 100644 --- a/test/utils/BaseSetup.sol +++ b/test/utils/BaseSetup.sol @@ -65,7 +65,8 @@ contract BaseSetup is Test { bytes32 internal constant SECRET = keccak256(abi.encodePacked("secret")); uint256 internal constant MAKING_AMOUNT = 0.3 ether; uint256 internal constant TAKING_AMOUNT = 0.5 ether; - uint256 internal constant SAFETY_DEPOSIT = 0.05 ether; + uint256 internal constant SRC_SAFETY_DEPOSIT = 0.03 ether; + uint256 internal constant DST_SAFETY_DEPOSIT = 0.05 ether; Utils internal utils; VmSafe.Wallet[] internal users; @@ -83,7 +84,8 @@ contract BaseSetup is Test { IEscrow.SrcTimelocks internal srcTimelocks = IEscrow.SrcTimelocks({ finality: 120, - publicUnlock: 900 + publicUnlock: 900, + cancel: 110 }); IEscrow.DstTimelocks internal dstTimelocks = IEscrow.DstTimelocks({ finality: 300, @@ -103,6 +105,8 @@ contract BaseSetup is Test { }); /* solhint-enable private-vars-leading-underscore */ + receive() external payable {} + function setUp() public virtual { utils = new Utils(); users = utils.createUsers(2); @@ -147,17 +151,20 @@ contract BaseSetup is Test { bytes32 secret, uint256 chainId, address token, - uint256 safetyDeposit + uint256 srcSafetyDeposit, + uint256 dstSafetyDeposit ) internal view returns (bytes memory) { - uint256 hashlock = uint256(keccak256(abi.encodePacked(secret))); + bytes32 hashlock = keccak256(abi.encodePacked(secret)); return ( abi.encode( hashlock, chainId, token, - safetyDeposit, + srcSafetyDeposit, + dstSafetyDeposit, srcTimelocks.finality, srcTimelocks.publicUnlock, + srcTimelocks.cancel, dstTimelocks.finality, dstTimelocks.unlock, dstTimelocks.publicUnlock @@ -177,12 +184,14 @@ contract BaseSetup is Test { bytes memory extension, Escrow srcClone ) { - uint256 safetyDeposit = dstAmount * 10 / 100; + uint256 srcSafetyDeposit = srcAmount * 10 / 100; + uint256 dstSafetyDeposit = dstAmount * 10 / 100; extraData = _buidDynamicData( secret, block.chainid, address(dai), - safetyDeposit + srcSafetyDeposit, + dstSafetyDeposit ); bytes memory postInteractionData = abi.encodePacked( @@ -250,7 +259,7 @@ contract BaseSetup is Test { IEscrowFactory.DstEscrowImmutablesCreation memory immutables, bytes memory data ) { - uint256 hashlock = uint256(keccak256(abi.encodePacked(secret))); + bytes32 hashlock = keccak256(abi.encodePacked(secret)); uint256 safetyDeposit = amount * 10 / 100; uint256 srcCancellationTimestamp = block.timestamp + srcTimelocks.finality + srcTimelocks.publicUnlock; immutables = IEscrowFactory.DstEscrowImmutablesCreation(