Skip to content

Commit 5ef63fe

Browse files
committed
Extend wrappers and add first test
1 parent dc01e90 commit 5ef63fe

File tree

6 files changed

+206
-43
lines changed

6 files changed

+206
-43
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ethereum-htlc",
3-
"version": "0.0.10",
3+
"version": "0.0.11",
44
"description": "Hashed Timelock Contracts for Ethereum",
55
"main": "truffle.js",
66
"license": "GPL-3.0",
@@ -16,7 +16,7 @@
1616
"ganache-start": "ganache-cli --networkId 4447 --port 7545",
1717
"lint": "solium -d contracts/",
1818
"test": "truffle test",
19-
"ethereum-htlc": "TODO"
19+
"ethereum-htlc": "truffle compile"
2020
},
2121
"keywords": [
2222
"ethereum",

test/wrapper/htlc-wrapper-test.js

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
const HtlcWrapper = require('../../wrapper/htlc-wrapper')
2+
3+
const {assertEqualBN} = require('../helper/assert')
4+
const {
5+
getBalance,
6+
htlcArrayToObj,
7+
isSha256Hash,
8+
newSecretHashPair,
9+
nowSeconds,
10+
txContractId,
11+
txGas,
12+
txLoggedArgs,
13+
} = require('../helper/utils')
14+
15+
const HashedTimelock = artifacts.require('./HashedTimelock.sol')
16+
17+
const hourSeconds = 3600
18+
const timeLock1Hour = nowSeconds() + hourSeconds
19+
const oneFinney = web3.utils.toWei(web3.utils.toBN(1), 'finney')
20+
21+
contract('HashedTimelockWrapper', accounts => {
22+
const sender = accounts[1]
23+
const receiver = accounts[2]
24+
const provider = new web3.providers.HttpProvider("http://localhost:7545");
25+
26+
it('newContract() and getContract() in wrapper should create new contract and store correct details', async () => {
27+
const hashPair = newSecretHashPair()
28+
const htlcWrapper = new HtlcWrapper(HashedTimelock, provider, null, false, null, null, null)
29+
const txReceipt = await htlcWrapper.newContract(
30+
receiver,
31+
hashPair.hash,
32+
timeLock1Hour,
33+
sender,
34+
oneFinney
35+
)
36+
const logArgs = txLoggedArgs(txReceipt)
37+
38+
const contractId = logArgs.contractId
39+
assert(isSha256Hash(contractId))
40+
41+
assert.equal(logArgs.sender, sender)
42+
assert.equal(logArgs.receiver, receiver)
43+
assertEqualBN(logArgs.amount, oneFinney)
44+
assert.equal(logArgs.hashlock, hashPair.hash)
45+
assert.equal(logArgs.timelock, timeLock1Hour)
46+
47+
const contractArr = await htlcWrapper.getContract(contractId)
48+
const contract = htlcArrayToObj(contractArr)
49+
assert.equal(contract.sender, sender)
50+
assert.equal(contract.receiver, receiver)
51+
assertEqualBN(contract.amount, oneFinney)
52+
assert.equal(contract.hashlock, hashPair.hash)
53+
assert.equal(contract.timelock.toNumber(), timeLock1Hour)
54+
assert.isFalse(contract.withdrawn)
55+
assert.isFalse(contract.refunded)
56+
assert.equal(
57+
contract.preimage,
58+
'0x0000000000000000000000000000000000000000000000000000000000000000'
59+
)
60+
})
61+
62+
it('withdraw() contract in wrapper should withdraw amount correctly', async () => {
63+
const hashPair = newSecretHashPair()
64+
const htlcWrapper = new HtlcWrapper(HashedTimelock, provider, null)
65+
const newContractTx = await htlcWrapper.newContract(
66+
receiver,
67+
hashPair.hash,
68+
timeLock1Hour,
69+
sender,
70+
oneFinney
71+
)
72+
73+
const contractId = txContractId(newContractTx)
74+
const receiverBalBefore = await getBalance(receiver)
75+
76+
// receiver calls withdraw with the secret to get the funds
77+
const withdrawTx = await htlcWrapper.withdraw(contractId, hashPair.secret, receiver)
78+
const tx = await web3.eth.getTransaction(withdrawTx.tx)
79+
80+
// Check contract funds are now at the receiver address
81+
const expectedBal = receiverBalBefore
82+
.add(oneFinney)
83+
.sub(txGas(withdrawTx, tx.gasPrice))
84+
assertEqualBN(
85+
await getBalance(receiver),
86+
expectedBal,
87+
"receiver balance doesn't match"
88+
)
89+
const contractArr = await htlcWrapper.getContract(contractId)
90+
const contract = htlcArrayToObj(contractArr)
91+
assert.isTrue(contract.withdrawn) // withdrawn set
92+
assert.isFalse(contract.refunded) // refunded still false
93+
assert.equal(contract.preimage, hashPair.secret)
94+
})
95+
96+
it('refund() in wrapper should pass after timelock expiry', async () => {
97+
const hashPair = newSecretHashPair()
98+
const htlcWrapper = new HtlcWrapper(HashedTimelock, provider, null)
99+
const timelock1Second = nowSeconds() + 1
100+
101+
const newContractTx = await htlcWrapper.newContract(
102+
receiver,
103+
hashPair.hash,
104+
timelock1Second,
105+
sender,
106+
oneFinney
107+
)
108+
const contractId = txContractId(newContractTx)
109+
110+
// wait one second so we move past the timelock time
111+
return new Promise((resolve, reject) =>
112+
setTimeout(async () => {
113+
try {
114+
const balBefore = await getBalance(sender)
115+
const refundTx = await htlcWrapper.refund(contractId, sender)
116+
const tx = await web3.eth.getTransaction(refundTx.tx)
117+
// Check contract funds are now at the senders address
118+
const expectedBal = balBefore.add(oneFinney).sub(txGas(refundTx, tx.gasPrice))
119+
assertEqualBN(
120+
await getBalance(sender),
121+
expectedBal,
122+
"sender balance doesn't match"
123+
)
124+
const contract = await htlcWrapper.getContract(contractId)
125+
assert.isTrue(contract[6]) // refunded set
126+
assert.isFalse(contract[5]) // withdrawn still false
127+
resolve()
128+
} catch (err) {
129+
reject(err)
130+
}
131+
}, 1000)
132+
)
133+
})
134+
135+
it('retrieve contract from address in wrapper', async () => {
136+
const hashPair = newSecretHashPair()
137+
const htlcWrapper = new HtlcWrapper(HashedTimelock, provider, null)
138+
139+
const address = await HtlcWrapper.deployContract(HashedTimelock, null, null)
140+
htlcWrapper.setAddress(address.address)
141+
142+
const timelock1Second = nowSeconds() + 1
143+
144+
await htlcWrapper.newContract(
145+
receiver,
146+
hashPair.hash,
147+
timelock1Second,
148+
sender,
149+
oneFinney
150+
)
151+
})
152+
})

wrapper/base-wrapper.js

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,38 @@
1-
export class BaseWrapper {
2-
contract = require("@truffle/contract")
3-
hashedTimelockContract
4-
address
1+
const contract = require("@truffle/contract")
52

3+
class BaseWrapper {
64
/**
75
* For additional information concerning the constructor parameters,
86
* @see https://www.npmjs.com/package/@truffle/contract
97
* Necessary parameters for the constructor are @param contractJson, @param provider, and @param shouldDeploy.
8+
*
109
*/
11-
constructor(contractJson, provider, optionalAddress, shouldDeploy, deployCallback, argArray, txParams) {
12-
this.hashedTimelockContract = this.contract(contractJson)
13-
this.hashedTimelockContract.setProvider(provider)
14-
this.address = optionalAddress
15-
if (shouldDeploy) {
16-
this.hashedTimelockContract.new(argArray, txParams).then((instance) => {
17-
// should call setAddress here
18-
deployCallback(instance)
19-
})
10+
constructor(contractJson, provider, optionalAddress) {
11+
this.hashedTimelockContract = contract(contractJson)
12+
if (provider !== null) {
13+
this.hashedTimelockContract.setProvider(provider)
2014
}
15+
this.address = optionalAddress
2116
}
2217

2318
/**
2419
* @param contractId bytes32
2520
* @param preimage bytes32
21+
* @param receiver address
2622
*/
27-
withdraw(contractId, preimage) {
23+
withdraw(contractId, preimage, receiver) {
2824
return this.getContractInstance().then((instance) => {
29-
return instance.withdraw(contractId, preimage)
25+
return instance.withdraw(contractId, preimage, {from: receiver})
3026
})
3127
}
3228

3329
/**
3430
* @param contractId bytes 32
31+
* @param sender address
3532
*/
36-
refund(contractId) {
33+
refund(contractId, sender) {
3734
return this.getContractInstance().then((instance) => {
38-
return instance.refund(contractId)
35+
return instance.refund(contractId, {from: sender})
3936
})
4037
}
4138

@@ -49,17 +46,8 @@ export class BaseWrapper {
4946
})
5047
}
5148

52-
/**
53-
* @param contractId bytes 32
54-
*/
55-
haveContract(contractId) {
56-
return this.getContractInstance().then((instance) => {
57-
return instance.haveContract(contractId)
58-
})
59-
}
60-
6149
getContractInstance() {
62-
if (this.address !== undefined) {
50+
if (this.address !== undefined && this.address !== null) {
6351
return this.hashedTimelockContract.at(this.address)
6452
}
6553
return this.hashedTimelockContract.deployed()
@@ -68,4 +56,10 @@ export class BaseWrapper {
6856
setAddress(address) {
6957
this.address = address
7058
}
59+
60+
static deployContract(contractJson, argArray, txParams) {
61+
return contractJson.new(argArray, txParams);
62+
}
7163
}
64+
65+
module.exports = BaseWrapper

wrapper/htlc-wrapper-erc20.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
1-
import {BaseWrapper} from "./base-wrapper"
1+
const BaseWrapper = require('./base-wrapper')
22

33
/**
44
* This wrapper can be used for already deployed contracts sharing the main interfaces of HTLCs.
55
*/
6-
export class HtlcErc20Wrapper extends BaseWrapper {
6+
class HtlcErc20Wrapper extends BaseWrapper {
77
/**
88
* Returns the contract ID.
99
* @param receiverAddress address
1010
* @param hashlock bytes 32
1111
* @param timelock uint
1212
* @param tokenContract address
1313
* @param amount uint
14+
* @param sender address
1415
*/
15-
newContract(receiverAddress, hashlock, timelock, tokenContract, amount) {
16+
newContract(receiverAddress, hashlock, timelock, tokenContract, amount, sender) {
1617
return this.getContractInstance().then((instance) => {
17-
return instance.newContract(receiverAddress, hashlock, timelock, tokenContract, amount)
18+
return instance.newContract(receiverAddress, hashlock, timelock, tokenContract, {
19+
from: sender
20+
})
1821
})
1922
}
20-
}
23+
}
24+
25+
module.exports = HtlcErc20Wrapper

wrapper/htlc-wrapper-erc721.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
1-
import {BaseWrapper} from "./base-wrapper"
1+
const BaseWrapper = require('./base-wrapper')
22

33
/**
44
* This wrapper can be used for already deployed contracts sharing the main interfaces of HTLCs.
55
*/
6-
export class HtlcErc721Wrapper extends BaseWrapper {
6+
class HtlcErc721Wrapper extends BaseWrapper {
77
/**
88
* @param receiverAddress address
99
* @param hashlock bytes 32
1010
* @param timelock uint
1111
* @param tokenContract address
1212
* @param tokenId uint
13+
* @param sender address
1314
*/
14-
newContract(receiverAddress, hashlock, timelock, tokenContract, tokenId) {
15+
newContract(receiverAddress, hashlock, timelock, tokenContract, tokenId, sender) {
1516
return this.getContractInstance().then((instance) => {
16-
return instance.newContract(receiverAddress, hashlock, timelock, tokenContract, tokenId)
17+
return instance.newContract(receiverAddress, hashlock, timelock, tokenContract, tokenId, {
18+
from: sender,
19+
})
1720
})
1821
}
19-
}
22+
}
23+
24+
module.exports = HtlcErc721Wrapper

wrapper/htlc-wrapper.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
1-
import {BaseWrapper} from "./base-wrapper"
1+
const BaseWrapper = require('./base-wrapper')
22

33
/**
44
* This wrapper can be used for already deployed contracts sharing the main interfaces of HTLCs.
55
*/
6-
export class HtlcWrapper extends BaseWrapper {
6+
class HtlcWrapper extends BaseWrapper {
77
/**
88
* Returns the contract ID.
99
* @param receiverAddress address
1010
* @param hashlock bytes 32
1111
* @param timelock uint
12+
* @param sender address
13+
* @param amount uint
1214
*/
13-
newContract(receiverAddress, hashlock, timelock) {
15+
newContract(receiverAddress, hashlock, timelock, sender, amount) {
1416
return this.getContractInstance().then((instance) => {
15-
return instance.newContract(receiverAddress, hashlock, timelock)
17+
return instance.newContract(receiverAddress, hashlock, timelock, {
18+
from: sender,
19+
value: amount,
20+
})
1621
})
1722
}
1823
}
24+
25+
module.exports = HtlcWrapper

0 commit comments

Comments
 (0)