Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ out/
!out/EscrowNative.sol/EscrowNative.json
!out/EscrowERC20.sol/
!out/EscrowERC20.sol/EscrowERC20.json
!out/EscrowFactory.sol/
!out/EscrowFactory.sol/EscrowFactory.json

# Ignores development broadcast logs
!/broadcast
Expand Down
2 changes: 1 addition & 1 deletion artifacts/erc20_deployment.hex

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion artifacts/erc20_runtime.hex

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion artifacts/native_deployment.hex

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion artifacts/native_runtime.hex

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion out/EscrowERC20.sol/EscrowERC20.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion out/EscrowNative.sol/EscrowNative.json

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions src/EscrowBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ abstract contract EscrowBase {
error CancellationRequested();
error ExecutorAlreadyBonded();
error InsufficientBond();
error ZeroDeployer();

// The following variables are set up in the constructor.
address immutable deployerAddress;
address public immutable deployerAddress;
uint256 public currentRewardAmount;
uint256 public currentPaymentAmount;
uint256 public originalRewardAmount;
Expand All @@ -39,10 +40,11 @@ abstract contract EscrowBase {
bool public cancellationRequest;
bool public funded; // marks if the contract has funds to pay out the executors or not (if it doesn't have funds, no executor should be accepted)

constructor(address _expectedRecipient, uint256 _expectedAmount) {
constructor(address _deployer, address _expectedRecipient, uint256 _expectedAmount) {
if (_deployer == address(0)) revert ZeroDeployer();
deployerAddress = _deployer;
expectedRecipient = _expectedRecipient;
expectedAmount = _expectedAmount;
deployerAddress = msg.sender;
}

// only deployer can call this. will set the cancellation request to true.
Expand Down
3 changes: 2 additions & 1 deletion src/EscrowERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ contract EscrowERC20 is EscrowBase {
}

constructor(
address _deployer,
address _tokenContract,
address _expectedRecipient,
uint256 _expectedAmount,
uint256 _currentRewardAmount,
uint256 _currentPaymentAmount
) EscrowBase(_expectedRecipient, _expectedAmount) {
) EscrowBase(_deployer, _expectedRecipient, _expectedAmount) {
if (_tokenContract == address(0)) revert ZeroAddress();
tokenContract = _tokenContract;

Expand Down
81 changes: 81 additions & 0 deletions src/EscrowFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.30;

import "./EscrowERC20.sol";
import "./EscrowNative.sol";

contract EscrowFactory {
event EscrowCreated(address indexed deployer, address escrow);

function createEscrowERC20(uint256 nonce, address tokenContract, address expectedRecipient, uint256 expectedAmount)
external
returns (address)
{
bytes32 salt = _salt(msg.sender, nonce);
EscrowERC20 escrow =
new EscrowERC20{salt: salt}(msg.sender, tokenContract, expectedRecipient, expectedAmount, 0, 0);
emit EscrowCreated(msg.sender, address(escrow));
return address(escrow);
}

function createEscrowNative(uint256 nonce, address expectedRecipient, uint256 expectedAmount)
external
returns (address)
{
bytes32 salt = _salt(msg.sender, nonce);
EscrowNative escrow = new EscrowNative{salt: salt}(msg.sender, expectedRecipient, expectedAmount, 0, 0);
emit EscrowCreated(msg.sender, address(escrow));
return address(escrow);
}

function predictEscrowERC20Address(
address deployer,
uint256 nonce,
address tokenContract,
address expectedRecipient,
uint256 expectedAmount
) external view returns (address) {
bytes32 salt = _salt(deployer, nonce);
bytes32 hash = keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
salt,
keccak256(
abi.encodePacked(
type(EscrowERC20).creationCode,
abi.encode(deployer, tokenContract, expectedRecipient, expectedAmount, uint256(0), uint256(0))
)
)
)
);
return address(uint160(uint256(hash)));
}

function predictEscrowNativeAddress(
address deployer,
uint256 nonce,
address expectedRecipient,
uint256 expectedAmount
) external view returns (address) {
bytes32 salt = _salt(deployer, nonce);
bytes32 hash = keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
salt,
keccak256(
abi.encodePacked(
type(EscrowNative).creationCode,
abi.encode(deployer, expectedRecipient, expectedAmount, uint256(0), uint256(0))
)
)
)
);
return address(uint160(uint256(hash)));
}

function _salt(address deployer, uint256 nonce) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(deployer, nonce));
}
}
3 changes: 2 additions & 1 deletion src/EscrowNative.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ contract EscrowNative is EscrowBase {
}

constructor(
address _deployer,
address _expectedRecipient,
uint256 _expectedAmount,
uint256 _currentRewardAmount,
uint256 _currentPaymentAmount
) payable EscrowBase(_expectedRecipient, _expectedAmount) {
) payable EscrowBase(_deployer, _expectedRecipient, _expectedAmount) {
if (_currentRewardAmount > 0 && _currentPaymentAmount > 0) {
if (msg.value != _currentRewardAmount + _currentPaymentAmount) revert IncorrectETHAmount();
currentRewardAmount = _currentRewardAmount;
Expand Down
12 changes: 6 additions & 6 deletions test/EscrowERC20.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ contract EscrowERC20Test is Test {
address futureEscrow = vm.computeCreateAddress(deployer, vm.getNonce(deployer));
token.approve(futureEscrow, REWARD_AMOUNT + PAYMENT_AMOUNT);

escrow = new EscrowERC20(address(token), recipient, EXPECTED_AMOUNT, REWARD_AMOUNT, PAYMENT_AMOUNT);
escrow = new EscrowERC20(deployer, address(token), recipient, EXPECTED_AMOUNT, REWARD_AMOUNT, PAYMENT_AMOUNT);
vm.stopPrank();

token.mint(executor, 10000e18);
Expand All @@ -86,7 +86,7 @@ contract EscrowERC20Test is Test {
address futureEscrow2 = vm.computeCreateAddress(deployer, vm.getNonce(deployer));
token.approve(futureEscrow2, REWARD_AMOUNT + PAYMENT_AMOUNT);

EscrowERC20 escrow2 = new EscrowERC20(address(token), recipient, EXPECTED_AMOUNT, 0, 0);
EscrowERC20 escrow2 = new EscrowERC20(deployer, address(token), recipient, EXPECTED_AMOUNT, 0, 0);

token.approve(address(escrow2), REWARD_AMOUNT + PAYMENT_AMOUNT);
escrow2.fund(REWARD_AMOUNT, PAYMENT_AMOUNT);
Expand All @@ -101,7 +101,7 @@ contract EscrowERC20Test is Test {

function testFundZeroReward() public {
vm.startPrank(deployer);
EscrowERC20 unfundedEscrow = new EscrowERC20(address(token), recipient, EXPECTED_AMOUNT, 0, 0);
EscrowERC20 unfundedEscrow = new EscrowERC20(deployer, address(token), recipient, EXPECTED_AMOUNT, 0, 0);

token.approve(address(unfundedEscrow), PAYMENT_AMOUNT);
vm.expectRevert(EscrowERC20.ZeroRewardAmount.selector);
Expand Down Expand Up @@ -141,7 +141,7 @@ contract EscrowERC20Test is Test {
function testBondNotFunded() public {
// Create an unfunded escrow
vm.startPrank(deployer);
EscrowERC20 unfundedEscrow = new EscrowERC20(address(token), recipient, EXPECTED_AMOUNT, 0, 0);
EscrowERC20 unfundedEscrow = new EscrowERC20(deployer, address(token), recipient, EXPECTED_AMOUNT, 0, 0);
vm.stopPrank();

vm.startPrank(executor);
Expand Down Expand Up @@ -259,7 +259,7 @@ contract EscrowERC20Test is Test {

function testCollectNotFunded() public {
vm.prank(deployer);
EscrowERC20 unfundedEscrow = new EscrowERC20(address(token), recipient, EXPECTED_AMOUNT, 0, 0);
EscrowERC20 unfundedEscrow = new EscrowERC20(deployer, address(token), recipient, EXPECTED_AMOUNT, 0, 0);

EscrowERC20.ReceiptProof memory dummyProof = EscrowERC20.ReceiptProof({
blockHeader: hex"", receiptRlp: hex"", proofNodes: hex"", receiptPath: hex"", logIndex: 0
Expand Down Expand Up @@ -374,7 +374,7 @@ contract EscrowERC20Test is Test {

function testCancelAndWithdrawNotFunded() public {
vm.prank(deployer);
EscrowERC20 unfundedEscrow = new EscrowERC20(address(token), recipient, EXPECTED_AMOUNT, 0, 0);
EscrowERC20 unfundedEscrow = new EscrowERC20(deployer, address(token), recipient, EXPECTED_AMOUNT, 0, 0);

vm.prank(deployer);
vm.expectRevert(EscrowBase.NotFunded.selector);
Expand Down
Loading
Loading