Skip to content

Enhancements from Kaleido team #13

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

Merged
merged 17 commits into from
Nov 11, 2019
Merged
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
3 changes: 0 additions & 3 deletions .babelrc

This file was deleted.

3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
abi/
build/
config.js
node_modules/
package-lock.json
93 changes: 76 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,79 @@

- [HashedTimelock.sol](contracts/HashedTimelock.sol) - HTLC for native ETH token
- [HashedTimelockERC20.sol](contracts/HashedTimelockERC20.sol) - HTLC for ERC20 tokens
- [HashedTimelockERC721.sol](contracts/HashedTimelockERC721.sol) - HTLC for ERC721 tokens

Use these contracts for creating HTLCs on the Ethereum side of a cross chain atomic swap (for example the [xcat](https://github.com/chatch/xcat) project).

## Deployment

HashedTimelock:

- Kovan: [0xe196bb1e25483ed771b6691929d47943023c26fe](https://kovan.etherscan.io/address/0xe196bb1e25483ed771b6691929d47943023c26fe)
- Ropsten:
[0x243785f6b65418191ea20b45fde7069ffe4f8cef](https://ropsten.etherscan.io/address/0x243785f6b65418191ea20b45fde7069ffe4f8cef)
- Mainnet: <not deployed yet ...>

HashedTimelockERC20:

- Kovan: [0x763eedd3c04a9a2fca67ac51fc16e394472f29a2](https://kovan.etherscan.io/address/0x763eedd3c04a9a2fca67ac51fc16e394472f29a2)
- Ropsten: [0x16b6fabc530c7bfde69eafd9e271fb610e3fc3f7](https://ropsten.etherscan.io/address/0x16b6fabc530c7bfde69eafd9e271fb610e3fc3f7)
- Mainnet: <not deployed yet ...>
## Run Tests
* Install truffle
* Install ganache [https://truffleframework.com/ganache](https://truffleframework.com/ganache)
* Launch and set the network ID to `4447`

```
$ npm i
$ truffle test
Using network 'test'.

Compiling ./test/helper/ASEANToken.sol...
Compiling ./test/helper/EUToken.sol...


Contract: HashedTimelock
✓ newContract() should create new contract and store correct details (92ms)
✓ newContract() should fail when no ETH sent (84ms)
✓ newContract() should fail with timelocks in the past (78ms)
✓ newContract() should reject a duplicate contract request (159ms)
✓ withdraw() should send receiver funds when given the correct secret preimage (214ms)
✓ withdraw() should fail if preimage does not hash to hashX (111ms)
✓ withdraw() should fail if caller is not the receiver (162ms)
✓ withdraw() should fail after timelock expiry (1243ms)
✓ refund() should pass after timelock expiry (1273ms)
✓ refund() should fail before the timelock expiry (132ms)
✓ getContract() returns empty record when contract doesn't exist (48ms)

Contract: HashedTimelockERC20
✓ newContract() should create new contract and store correct details (214ms)
✓ newContract() should fail when no token transfer approved (107ms)
✓ newContract() should fail when token amount is 0 (166ms)
✓ newContract() should fail when tokens approved for some random account (214ms)
✓ newContract() should fail when the timelock is in the past (136ms)
✓ newContract() should reject a duplicate contract request (282ms)
✓ withdraw() should send receiver funds when given the correct secret preimage (363ms)
✓ withdraw() should fail if preimage does not hash to hashX (227ms)
✓ withdraw() should fail if caller is not the receiver (307ms)
✓ withdraw() should fail after timelock expiry (2257ms)
✓ refund() should pass after timelock expiry (2407ms)
✓ refund() should fail before the timelock expiry (283ms)
✓ getContract() returns empty record when contract doesn't exist (55ms)

Contract: HashedTimelock swap between two ERC20 tokens
✓ Step 1: Alice sets up a swap with Bob in the AliceERC20 contract (233ms)
✓ Step 2: Bob sets up a swap with Alice in the BobERC20 contract (239ms)
✓ Step 3: Alice as the initiator withdraws from the BobERC20 with the secret (97ms)
✓ Step 4: Bob as the counterparty withdraws from the AliceERC20 with the secret learned from Alice's withdrawal (144ms)
Test the refund scenario:
✓ the swap is set up with 5sec timeout on both sides (3613ms)

Contract: HashedTimelock swap between ERC721 token and ERC20 token (Delivery vs. Payment)
✓ Step 1: Alice sets up a swap with Bob to transfer the Commodity token #1 (256ms)
✓ Step 2: Bob sets up a swap with Alice in the payment contract (231ms)
✓ Step 3: Alice as the initiator withdraws from the BobERC721 with the secret (95ms)
✓ Step 4: Bob as the counterparty withdraws from the AliceERC721 with the secret learned from Alice's withdrawal (132ms)
Test the refund scenario:
✓ the swap is set up with 5sec timeout on both sides (3737ms)

Contract: HashedTimelock swap between two ERC721 tokens
✓ Step 1: Alice sets up a swap with Bob in the AliceERC721 contract (225ms)
✓ Step 2: Bob sets up a swap with Alice in the BobERC721 contract (265ms)
✓ Step 3: Alice as the initiator withdraws from the BobERC721 with the secret (131ms)
✓ Step 4: Bob as the counterparty withdraws from the AliceERC721 with the secret learned from Alice's withdrawal (119ms)
Test the refund scenario:
✓ the swap is set up with 5sec timeout on both sides (3635ms)


39 passing (27s)
```

## Protocol - Native ETH

Expand Down Expand Up @@ -62,7 +118,10 @@ See [test/htlc.js](test/htlc.js) for examples of interacting with the contract f

See [test/htlcERC20.js](test/htlcERC20.js) for examples of interacting with the contract from javascript.

## ABI and Bytecode
### HashedTimelockERC721

1. `newContract(receiverAddress, hashlock, timelock, tokenContract, tokenId)` create new HTLC with given receiver, hashlock, expiry, ERC20 token contract address and the token to transfer
2. `withdraw(contractId, preimage)` claim funds revealing the preimage
3. `refund(contractId)` if withdraw was not called the contract creator can get a refund by calling this some time after the time lock has expired.


- [HashedTimelock.json](abi/HashedTimelock.json)
- [HashedTimelockERC20.json](abi/HashedTimelockERC20.json)
11,992 changes: 0 additions & 11,992 deletions abi/HashedTimelock.json

This file was deleted.

14,003 changes: 0 additions & 14,003 deletions abi/HashedTimelockERC20.json

This file was deleted.

15 changes: 0 additions & 15 deletions config.example.js

This file was deleted.

37 changes: 20 additions & 17 deletions contracts/HashedTimelockERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,40 +22,42 @@ import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
* back with this function.
*/
contract HashedTimelockERC20 {
constructor() public {
}

event LogHTLCERC20New(
event HTLCERC20New(
bytes32 indexed contractId,
address indexed sender,
address indexed receiver,
address tokenContract,
uint amount,
uint256 amount,
bytes32 hashlock,
uint timelock
uint256 timelock
);
event LogHTLCERC20Withdraw(bytes32 indexed contractId);
event LogHTLCERC20Refund(bytes32 indexed contractId);
event HTLCERC20Withdraw(bytes32 indexed contractId);
event HTLCERC20Refund(bytes32 indexed contractId);

struct LockContract {
address sender;
address receiver;
address tokenContract;
uint amount;
uint256 amount;
bytes32 hashlock;
uint timelock; // UNIX timestamp seconds - locked UNTIL this time
uint256 timelock; // locked UNTIL this time. Unit depends on consensus algorithm. PoA, PoA and IBFT all use seconds. But Quorum Raft uses nano-seconds
bool withdrawn;
bool refunded;
bytes32 preimage;
}

modifier tokensTransferable(address _token, address _sender, uint _amount) {
modifier tokensTransferable(address _token, address _sender, uint256 _amount) {
require(_amount > 0, "token amount must be > 0");
require(
ERC20(_token).allowance(_sender, address(this)) >= _amount,
"token allowance must be >= amount"
);
_;
}
modifier futureTimelock(uint _time) {
modifier futureTimelock(uint256 _time) {
// only requirement is the timelock time is after the last blocktime (now).
// probably want something a bit further in the future then this.
// but this is still a useful sanity check:
Expand All @@ -76,7 +78,8 @@ contract HashedTimelockERC20 {
modifier withdrawable(bytes32 _contractId) {
require(contracts[_contractId].receiver == msg.sender, "withdrawable: not receiver");
require(contracts[_contractId].withdrawn == false, "withdrawable: already withdrawn");
require(contracts[_contractId].timelock > now, "withdrawable: timelock time must be in the future");
// if we want to disallow claim to be made after the timeout, uncomment the following line
// require(contracts[_contractId].timelock > now, "withdrawable: timelock time must be in the future");
_;
}
modifier refundable(bytes32 _contractId) {
Expand Down Expand Up @@ -108,9 +111,9 @@ contract HashedTimelockERC20 {
function newContract(
address _receiver,
bytes32 _hashlock,
uint _timelock,
uint256 _timelock,
address _tokenContract,
uint _amount
uint256 _amount
)
external
tokensTransferable(_tokenContract, msg.sender, _amount)
Expand Down Expand Up @@ -150,7 +153,7 @@ contract HashedTimelockERC20 {
0x0
);

emit LogHTLCERC20New(
emit HTLCERC20New(
contractId,
msg.sender,
_receiver,
Expand Down Expand Up @@ -180,7 +183,7 @@ contract HashedTimelockERC20 {
c.preimage = _preimage;
c.withdrawn = true;
ERC20(c.tokenContract).transfer(c.receiver, c.amount);
emit LogHTLCERC20Withdraw(_contractId);
emit HTLCERC20Withdraw(_contractId);
return true;
}

Expand All @@ -200,7 +203,7 @@ contract HashedTimelockERC20 {
LockContract storage c = contracts[_contractId];
c.refunded = true;
ERC20(c.tokenContract).transfer(c.sender, c.amount);
emit LogHTLCERC20Refund(_contractId);
emit HTLCERC20Refund(_contractId);
return true;
}

Expand All @@ -216,9 +219,9 @@ contract HashedTimelockERC20 {
address sender,
address receiver,
address tokenContract,
uint amount,
uint256 amount,
bytes32 hashlock,
uint timelock,
uint256 timelock,
bool withdrawn,
bool refunded,
bytes32 preimage
Expand Down
Loading