/
ethereum.js
147 lines (130 loc) · 4.34 KB
/
ethereum.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import Promise from 'bluebird'
import Web3 from 'web3'
import Web3Utils from 'web3/lib/utils/utils'
import truffleContract from 'truffle-contract'
import has from 'lodash/has'
const txContractId = txReceipt => txReceipt.logs[0].args.contractId
const ethToWei = eth => Web3Utils.toWei(eth, 'ether')
const contractArrToObj = c => {
return {
sender: c[0],
receiver: c[1],
amount: c[2],
hashlock: c[3],
timelock: c[4],
withdrawn: c[5],
refunded: c[6],
preimage: c[7],
}
}
const validateSetup = (web3, htlcContractObj, htlcContractAddr) => {
if (!Web3Utils.isAddress(htlcContractAddr))
throw new Error(
`HashedTimelock deployment address [${htlcContractAddr}] ` +
`is not a valid contract address`
)
const htlcBytecode = web3.eth.getCode(htlcContractAddr)
if (htlcBytecode === '0x0')
throw new Error(
`No code deployed at HashedTimelock deployment address ` +
`[${htlcContractAddr}] `
)
if (htlcBytecode !== htlcContractObj.deployedBytecode)
throw new Error(
`Wrong code deployed at HashedTimelock deployment address. The ` +
`deployed contract should match the bytecode in the ethereum-htlc ` +
`module abi file. ` +
`[${htlcContractAddr}]\n\nExpected:[\n` +
`[${htlcContractObj.deployedBytecode}]\n\nGot:[\n${htlcBytecode}]\n`
)
}
class Ethereum {
/**
* Setup web3 and a handle to the HashedTimelock contract
*
* @param rpcAddr Address of Ethereum client RPC
* @param htlcContractObj Truffle contract generated object containing
* details of the contract including the abi.
* @param htlcContractAddr Address of the deployed HTLC contract
*/
constructor(rpcAddr, htlcContractObj, htlcContractAddr) {
this.web3 = new Web3(new Web3.providers.HttpProvider(rpcAddr))
validateSetup(this.web3, htlcContractObj, htlcContractAddr)
// htlc contract handle (truffle-contract)
const HTLC = truffleContract(htlcContractObj)
HTLC.setProvider(this.web3.currentProvider)
this.htlc = HTLC.at(htlcContractAddr)
// htlc contract handle (web3) - for events/logs only
const HTLCWeb3 = this.web3.eth.contract(htlcContractObj.abi)
this.htlcWeb3 = HTLCWeb3.at(htlcContractAddr)
// TODO: promisify web3 async handles and move async code out of contructor (for metamask consumption)
// this.web3.eth.getCodeAsync = Promise.promisify(this.web3.eth.getCode)
}
/**
* Set up a new hashed timelock contract.
*
* @return Promise with contractId of the new HTLC
*/
createHashedTimelockContract(hashX, sellerAddr, buyerAddr, amount, locktime) {
const ethHashX = hashX.startsWith('0x') ? hashX : '0x' + hashX
const amountWei = ethToWei(amount)
return this.htlc
.newContract(buyerAddr, ethHashX, locktime, {
from: sellerAddr,
value: amountWei,
gas: 200000,
})
.then(txContractId)
}
/**
* Buyer withdraws the funds revealing the preimage for hash(x)
*
* @return Promise with tx receipt
*/
buyerWithdraw(contractId, preimage, buyerAddr) {
return this.htlc.withdraw(contractId, preimage, {from: buyerAddr})
}
/**
* Seller claims refund assuming the transfer did not complete.
* Seller can not call this before the timelock has expired.
*
* @return Promise with tx receipt
*/
sellerRefund(contractId, sellerAddr) {
return this.htlc.refund(contractId, {from: sellerAddr})
}
/**
* Fetch HTLC contract details
*
* @return Promise with contract details - see contractArrToObj for prop names
*/
getContract(contractId) {
return this.htlc.getContract.call(contractId).then(c => contractArrToObj(c))
}
/**
* Try find a trade contract given contrace details. Looks up LogHTLCNew
* events to discover a matching contract.
*
* @return Promise with contractId of matching contract
*/
findContract(depositor, withdrawer, amount, hashlock, timelock) {
const filter = {
receiver: withdrawer,
sender: depositor,
amount: amount,
hashlock: hashlock,
timelock: timelock,
}
const event = this.htlcWeb3.LogHTLCNew(filter)
event.get = Promise.promisify(event.get)
return event
.get()
.then(
res =>
res.length > 0 && has(res[0], 'args')
? res[0].args.contractId
: undefined
)
}
}
export default Ethereum