Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ethereum swap watchers #1

Open
wants to merge 11 commits into
base: dev
Choose a base branch
from
202 changes: 192 additions & 10 deletions contracts/EtomicSwap.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
pragma solidity ^0.5.0;
import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract EtomicSwap {
enum PaymentState {
Uninitialized,
PaymentSent,
ReceivedSpent,
ReceiverSpent,
SenderRefunded
}

enum RewardTargetOnSpend {
None,
Contract,
PaymentSender,
PaymentSpender
}

struct Payment {
bytes20 paymentHash;
uint64 lockTime;
Expand All @@ -21,7 +28,7 @@ contract EtomicSwap {
event ReceiverSpent(bytes32 id, bytes32 secret);
event SenderRefunded(bytes32 id);

constructor() public { }
constructor() { }

function ethPayment(
bytes32 _id,
Expand All @@ -48,14 +55,45 @@ contract EtomicSwap {
emit PaymentSent(_id);
}

function ethPaymentReward(
bytes32 _id,
address _receiver,
bytes20 _secretHash,
uint64 _lockTime,
RewardTargetOnSpend _rewardTarget,
bool _sendsContractRewardOnSpend,
uint256 _rewardAmount
) external payable {
require(_receiver != address(0) && msg.value > 0 && payments[_id].state == PaymentState.Uninitialized);

bytes20 paymentHash = ripemd160(abi.encodePacked(
_receiver,
msg.sender,
_secretHash,
address(0),
msg.value,
_rewardTarget,
_sendsContractRewardOnSpend,
_rewardAmount
));

payments[_id] = Payment(
paymentHash,
_lockTime,
PaymentState.PaymentSent
);

emit PaymentSent(_id);
}

function erc20Payment(
bytes32 _id,
uint256 _amount,
address _tokenAddress,
address _receiver,
bytes20 _secretHash,
uint64 _lockTime
) external payable {
) external {
require(_receiver != address(0) && _amount > 0 && payments[_id].state == PaymentState.Uninitialized);

bytes20 paymentHash = ripemd160(abi.encodePacked(
Expand All @@ -77,6 +115,45 @@ contract EtomicSwap {
emit PaymentSent(_id);
}

function erc20PaymentReward(
bytes32 _id,
uint256 _amount,
address _tokenAddress,
address _receiver,
bytes20 _secretHash,
uint64 _lockTime,
RewardTargetOnSpend _rewardTarget,
bool _sendsContractRewardOnSpend,
uint256 _rewardAmount
) external payable {
require(_receiver != address(0) && _amount > 0 && payments[_id].state == PaymentState.Uninitialized);

if (_rewardTarget != RewardTargetOnSpend.None && _rewardTarget != RewardTargetOnSpend.PaymentSpender) {
require(msg.value == _rewardAmount);
}

bytes20 paymentHash = ripemd160(abi.encodePacked(
_receiver,
msg.sender,
_secretHash,
_tokenAddress,
_amount,
_rewardTarget,
_sendsContractRewardOnSpend,
_rewardAmount
));

payments[_id] = Payment(
paymentHash,
_lockTime,
PaymentState.PaymentSent
);

IERC20 token = IERC20(_tokenAddress);
require(token.transferFrom(msg.sender, address(this), _amount));
emit PaymentSent(_id);
}

function receiverSpend(
bytes32 _id,
uint256 _amount,
Expand All @@ -95,9 +172,9 @@ contract EtomicSwap {
));

require(paymentHash == payments[_id].paymentHash);
payments[_id].state = PaymentState.ReceivedSpent;
payments[_id].state = PaymentState.ReceiverSpent;
if (_tokenAddress == address(0)) {
msg.sender.transfer(_amount);
payable(msg.sender).transfer(_amount);
} else {
IERC20 token = IERC20(_tokenAddress);
require(token.transfer(msg.sender, _amount));
Expand All @@ -106,6 +183,61 @@ contract EtomicSwap {
emit ReceiverSpent(_id, _secret);
}

function receiverSpendReward(
bytes32 _id,
uint256 _amount,
bytes32 _secret,
address _tokenAddress,
address _sender,
address _receiver,
RewardTargetOnSpend _rewardTarget,
bool _sendsContractRewardOnSpend,
uint256 _rewardAmount
) external {
require(payments[_id].state == PaymentState.PaymentSent, "Payment was not sent");

bytes20 paymentHash = ripemd160(abi.encodePacked(
_receiver,
_sender,
ripemd160(abi.encodePacked(sha256(abi.encodePacked(_secret)))),
_tokenAddress,
_amount,
_rewardTarget,
_sendsContractRewardOnSpend,
_rewardAmount
));

require(paymentHash == payments[_id].paymentHash, "Invalid payment hash");
payments[_id].state = PaymentState.ReceiverSpent;

if (_tokenAddress == address(0)) {
uint256 transferAmount = _rewardTarget == RewardTargetOnSpend.None ? _amount : _amount - _rewardAmount;
payable(_receiver).transfer(transferAmount);

if (_rewardTarget == RewardTargetOnSpend.PaymentSpender) {
payable(msg.sender).transfer(_rewardAmount);
}
} else {
uint256 transferAmount = _rewardTarget == RewardTargetOnSpend.PaymentSpender ? _amount - _rewardAmount : _amount;
IERC20 token = IERC20(_tokenAddress);
require(token.transfer(_receiver, transferAmount), "Token transfer failed");

if (_rewardTarget == RewardTargetOnSpend.PaymentSpender) {
require(token.transfer(msg.sender, _rewardAmount), "Token transfer failed");
}
}

if (_rewardTarget == RewardTargetOnSpend.PaymentSender) {
payable(_sender).transfer(_rewardAmount);
}

if (_sendsContractRewardOnSpend) {
payable(msg.sender).transfer(_rewardAmount);
}

emit ReceiverSpent(_id, _secret);
}

function senderRefund(
bytes32 _id,
uint256 _amount,
Expand All @@ -117,23 +249,73 @@ contract EtomicSwap {

bytes20 paymentHash = ripemd160(abi.encodePacked(
_receiver,
msg.sender,
msg.sender,
_paymentHash,
_tokenAddress,
_amount
));

require(paymentHash == payments[_id].paymentHash && now >= payments[_id].lockTime);
require(paymentHash == payments[_id].paymentHash && block.timestamp >= payments[_id].lockTime);

payments[_id].state = PaymentState.SenderRefunded;

if (_tokenAddress == address(0)) {
msg.sender.transfer(_amount);
payable(msg.sender).transfer(_amount);
} else {
IERC20 token = IERC20(_tokenAddress);
require(token.transfer(msg.sender, _amount));
}

emit SenderRefunded(_id);
}

function senderRefundReward(
bytes32 _id,
uint256 _amount,
bytes20 _paymentHash,
address _tokenAddress,
address _sender,
address _receiver,
RewardTargetOnSpend _rewardTarget,
bool _sendsReward,
uint256 _rewardAmount
) external {
require(payments[_id].state == PaymentState.PaymentSent);

bytes20 paymentHash = ripemd160(abi.encodePacked(
_receiver,
_sender,
_paymentHash,
_tokenAddress,
_amount,
_rewardTarget,
_sendsReward,
_rewardAmount
));

require(paymentHash == payments[_id].paymentHash && block.timestamp >= payments[_id].lockTime);

payments[_id].state = PaymentState.SenderRefunded;

if (_tokenAddress == address(0)) {
uint256 transferAmount = _rewardTarget == RewardTargetOnSpend.None ? _amount : _amount - _rewardAmount;
payable(_sender).transfer(transferAmount);

if (_rewardTarget != RewardTargetOnSpend.None) {
payable(msg.sender).transfer(_rewardAmount);
}
} else {
uint256 transferAmount = _rewardTarget == RewardTargetOnSpend.PaymentSpender ? _amount - _rewardAmount : _amount;
IERC20 token = IERC20(_tokenAddress);
require(token.transfer(_sender, transferAmount));

if (_rewardTarget == RewardTargetOnSpend.PaymentSpender) {
require(token.transfer(msg.sender, _rewardAmount));
} else if (_rewardTarget != RewardTargetOnSpend.None) {
payable(msg.sender).transfer(_rewardAmount);
}
}

emit SenderRefunded(_id);
}
}
6 changes: 3 additions & 3 deletions contracts/Migrations.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
pragma solidity ^0.5.0;
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/access/Ownable.sol";

contract Migrations is Ownable {
uint public last_completed_migration;

constructor() public { }
constructor() { }

function setCompleted(uint completed) public onlyOwner {
last_completed_migration = completed;
Expand Down
8 changes: 4 additions & 4 deletions contracts/Token.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pragma solidity ^0.5.0;
pragma solidity ^0.8.9;

import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

/**
* @title Standard ERC20 token
Expand Down Expand Up @@ -29,7 +29,7 @@ contract Token is IERC20 {

uint8 public constant decimals = 18;

constructor() public {
constructor() {
_balances[msg.sender] = 1000 ether;
_totalSupply = 1000 ether;
}
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@
"scripts": {
"rpc": "node startRPC.js",
"test": "truffle test",
"clean": "rm -rf merge",
"merge": "node node_modules/sol-merger/bin/sol-merger.js './contracts/*.sol' ./merge"
},
"author": "[email protected]",
"license": "ISC",
"dependencies": {
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"ganache-cli": "^6.2.5",
"openzeppelin-solidity": "https://github.com/OpenZeppelin/openzeppelin-solidity.git#release-v2.1.0-solc-0.5",
"ganache-cli": "^6.7.0",
"@openzeppelin/contracts": "^4.8.1",
"request": "^2.83.0",
"request-promise-native": "^1.0.5",
"ripemd160": "^2.0.1",
Expand Down
8 changes: 6 additions & 2 deletions rpc.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
FROM mhart/alpine-node:11
FROM node:19-bullseye-slim

RUN apk update && apk upgrade && apk add git && apk add python && apk add make && apk add g++
RUN apt-get update && apt-get install -y \
git \
python3 \
make \
g++

ADD . /usr/src/rpc

Expand Down
Loading