Hashed Timelock Contracts for ETH, ERC20 and ERC721 on Ethereum
hashed-timelock-contract-ethereum
Hashed Timelock Contracts (HTLCs) for Ethereum:
- HashedTimelock.sol - HTLC for native ETH token
- HashedTimelockERC20.sol - HTLC for ERC20 tokens
- HashedTimelockERC721.sol - HTLC for ERC721 tokens
Run Tests
$ npm install
$ npm run ganache-start
$ npm run 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
Main flow

Timelock expires

Protocol - ERC20
Main flow

Timelock expires

Interface
HashedTimelock
-
newContract(receiverAddress, hashlock, timelock)create new HTLC with given receiver, hashlock and expiry; returns contractId bytes32 -
withdraw(contractId, preimage)claim funds revealing the preimage -
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.
HashedTimelockERC20
-
newContract(receiverAddress, hashlock, timelock, tokenContract, amount)create new HTLC with given receiver, hashlock, expiry, ERC20 token contract address and amount of tokens -
withdraw(contractId, preimage)claim funds revealing the preimage -
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.
HashedTimelockERC721
-
newContract(receiverAddress, hashlock, timelock, tokenContract, tokenId)create new HTLC with given receiver, hashlock, expiry, ERC20 token contract address and the token to transfer -
withdraw(contractId, preimage)claim funds revealing the preimage -
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.