Module: EthTool

Defined in:
lib/eth_tool.rb,
lib/eth_tool/version.rb

Constant Summary collapse

VERSION =
"0.1.5"

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.loggerObject

Returns the value of attribute logger.



22
23
24
# File 'lib/eth_tool.rb', line 22

def logger
  @logger
end

Class Method Details

.airdrop(private_key, airdrop_contract_address, token_contract_address, addresses, values, gas_limit, gas_price) ⇒ Object



88
89
90
91
92
93
94
# File 'lib/eth_tool.rb', line 88

def self.airdrop(private_key, airdrop_contract_address, token_contract_address, addresses, values, gas_limit, gas_price)
  data = build_airdrop_data(token_contract_address, addresses, values)

  rawtx = generate_raw_transaction(private_key, 0, data, gas_limit, gas_price, airdrop_contract_address)
  
  @@rpc.eth_send_raw_transaction(rawtx)
end

.build_airdrop_data(token_contract_address, addresses, values) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/eth_tool.rb', line 72

def self.build_airdrop_data(token_contract_address, addresses, values)
  data = "0xad8733ca"
  data = data + padding(token_contract_address)
  data = data + "0000000000000000000000000000000000000000000000000000000000000060"
  data = data + "0000000000000000000000000000000000000000000000000000000000001840"
  data = data + "00000000000000000000000000000000000000000000000000000000000000be"
  addresses.each do |address|
    data = data + padding(address.downcase)
  end
  data = data + "00000000000000000000000000000000000000000000000000000000000000be"
  values.each do |value|
    data = data + padding(value.to_i.to_s(16))
  end
  return data
end

.confirmations(txhash) ⇒ Object



215
216
217
218
219
220
221
222
223
# File 'lib/eth_tool.rb', line 215

def self.confirmations(txhash)
  tx = @@rpc.eth_get_transaction_by_hash(txhash)["result"]

  return 0 unless (tx and (not tx['blockNumber'].nil?))

  block_number = tx['blockNumber'].to_i(16)
  current_block_number = @@rpc.eth_block_number["result"].to_i(16)
  current_block_number - block_number
end

.confirmed?(txhash, number) ⇒ Boolean

Returns:

  • (Boolean)


203
204
205
206
207
208
209
210
211
212
213
# File 'lib/eth_tool.rb', line 203

def self.confirmed?(txhash, number)
  tx = @@rpc.eth_get_transaction_by_hash(txhash)["result"]

  return false unless (tx and (not tx['blockNumber'].nil?))

  block_number = tx['blockNumber'].to_i(16)
  current_block_number = @@rpc.eth_block_number["result"].to_i(16)
  confirmations = current_block_number - block_number

  confirmations >= number
end

.dec_to_hex(value) ⇒ Object

helper methods #########################################################



237
238
239
# File 'lib/eth_tool.rb', line 237

def self.dec_to_hex(value)
  '0x'+value.to_s(16)
end

.estimate_gas(to, data) ⇒ Object

def self.wputs(text)

File.open('/Users/wuminzhe/Projects/eth_tool/scripts/fuck.txt', 'a') { |f| f.puts(text) }

end



68
69
70
# File 'lib/eth_tool.rb', line 68

def self.estimate_gas(to, data)
  @@rpc.eth_estimate_gas({to: to, data: data})["result"].to_i(16)
end

.fee(gas_limit, gas_price) ⇒ Object



17
18
19
# File 'lib/eth_tool.rb', line 17

def self.fee(gas_limit, gas_price)
  BigDecimal(gas_limit) * BigDecimal(gas_price) / 10**18.to_f
end

.fill_eth(private_key, to, amount, gas_limit = 60000, gas_price = 5_000_000_000) ⇒ Object

目标地址上需要填充满这么多eth amount = BigDecimal(gas_limit) * BigDecimal(gas_price) / 10**18



139
140
141
142
143
144
145
146
# File 'lib/eth_tool.rb', line 139

def self.fill_eth(private_key, to, amount, gas_limit=60000, gas_price=5_000_000_000)
  # 目标地址上现有eth
  eth_balance = get_eth_balance(to)

  return unless eth_balance < amount # 如果有足够多的eth,就直接返回
  
  transfer_eth(private_key, (amount-eth_balance), gas_limit, gas_price, to)
end

.generate_addresses_from_xprv(xprv, amount) ⇒ Object



173
174
175
176
177
178
179
180
181
# File 'lib/eth_tool.rb', line 173

def self.generate_addresses_from_xprv(xprv, amount)
  result = []
  wallet = Bip44::Wallet.from_xprv(xprv)
  amount.times do |i|
    sub_wallet = wallet.sub_wallet("m/#{i}")
    result << sub_wallet.ethereum_address
  end
  result
end

.generate_raw_transaction(private_key, value, data, gas_limit, gas_price = nil, to = nil, nonce = nil) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/eth_tool.rb', line 40

def self.generate_raw_transaction(private_key, value, data, gas_limit, gas_price = nil, to = nil, nonce = nil)
  key = ::Eth::Key.new priv: private_key
  address = key.address

  gas_price_in_dec = gas_price.nil? ? @@rpc.eth_gas_price["result"].to_i(16) : gas_price
  nonce = nonce.nil? ? @@rpc.eth_get_transaction_count(address, 'pending')["result"].to_i(16) : nonce

  args = {
    from: address,
    value: 0,
    data: '0x0',
    nonce: nonce,
    gas_limit: gas_limit,
    gas_price: gas_price_in_dec
  }

  args[:value] = (value * 10**18).to_i if value
  args[:data] = data if data
  args[:to] = to if to
  tx = Eth::Tx.new(args)
  tx.sign key
  tx.hex
end

.get_eth_balance(address) ⇒ Object



35
36
37
38
# File 'lib/eth_tool.rb', line 35

def self.get_eth_balance(address)
  result = @@rpc.eth_get_balance(address)['result'].to_i(16)
  BigDecimal(result) / BigDecimal(10**18)
end

.get_private_key_from_keystore(dir, address, passphrase) ⇒ Object



163
164
165
166
167
168
169
170
171
# File 'lib/eth_tool.rb', line 163

def self.get_private_key_from_keystore(dir, address, passphrase)
  path = File.join(dir, '*')
  Dir[path].each do |file|
    if file.end_with?(address[2..-1])
      key = ::Eth::Key.decrypt(File.read(file), passphrase)
      return key.private_key.to_hex
    end
  end
end

.get_private_key_from_xprv(xprv, index) ⇒ Object



183
184
185
186
187
# File 'lib/eth_tool.rb', line 183

def self.get_private_key_from_xprv(xprv, index)
  wallet = Bip44::Wallet.from_xprv(xprv)
  sub_wallet = wallet.sub_wallet("m/#{index}")
  return sub_wallet.private_key, sub_wallet.ethereum_address
end

.get_token_balance(address, token_contract_address, token_decimals) ⇒ Object



28
29
30
31
32
33
# File 'lib/eth_tool.rb', line 28

def self.get_token_balance(address, token_contract_address, token_decimals)
  data = '0x70a08231' + padding(address) # Ethereum::Function.calc_id('balanceOf(address)') # 70a08231

  result = @@rpc.eth_call({to: token_contract_address, data: data})['result'].to_i(16)
  BigDecimal(result) / BigDecimal(10**token_decimals)
end

.mined?(txhash) ⇒ Boolean

Returns:

  • (Boolean)


198
199
200
201
# File 'lib/eth_tool.rb', line 198

def self.mined?(txhash)
  tx = @@rpc.eth_get_transaction_by_hash(txhash)["result"]
  tx and (not tx['blockNumber'].nil?)
end

.padding(str) ⇒ Object



241
242
243
244
245
246
# File 'lib/eth_tool.rb', line 241

def self.padding(str)
  if str =~ /^0x[a-f0-9]*/
    str = str[2 .. str.length-1]
  end
  str.rjust(64, '0')
end

.rpcObject



13
14
15
# File 'lib/eth_tool.rb', line 13

def self.rpc
  @@rpc
end

.rpc=(url) ⇒ Object



9
10
11
# File 'lib/eth_tool.rb', line 9

def self.rpc=(url)
  @@rpc = Ethereum::HttpClient.new(url)
end

.sweep_eth(private_key, to, gas_limit = 60000, gas_price = 5_000_000_000) ⇒ Object



127
128
129
130
131
132
133
134
135
# File 'lib/eth_tool.rb', line 127

def self.sweep_eth(private_key, to, gas_limit=60000, gas_price=5_000_000_000)
  address = ::Eth::Key.new(priv: private_key).address
  eth_balance = get_eth_balance(address)
  # 转eth不能都转,要留一点作为gas费用
  keep = BigDecimal(gas_limit) * BigDecimal(gas_price) / 10**18
  amount = eth_balance - keep
  return if amount <= 0
  transfer_eth(private_key, amount, gas_limit, gas_price, to)
end

.sweep_token(private_key, token_contract_address, token_decimals, to, gas_limit = 60000, gas_price = 5_000_000_000) ⇒ Object

sweep



120
121
122
123
124
125
# File 'lib/eth_tool.rb', line 120

def self.sweep_token(private_key, token_contract_address, token_decimals, to, gas_limit=60000, gas_price=5_000_000_000)
  address = ::Eth::Key.new(priv: private_key).address
  token_balance = get_token_balance(address, token_contract_address, token_decimals)
  return if token_balance == 0
  transfer_token(private_key, token_contract_address, token_decimals, token_balance, gas_limit, gas_price, to)
end

.token_transfer_success?(txhash) ⇒ Boolean

Returns:

  • (Boolean)


225
226
227
228
229
230
231
232
233
# File 'lib/eth_tool.rb', line 225

def self.token_transfer_success?(txhash)
  tx = @@rpc.eth_get_transaction_by_hash(txhash)["result"]
  return false unless (tx and (not tx['blockNumber'].nil?))

  receipt = @@rpc.eth_get_transaction_receipt(txhash)["result"]
  return false unless receipt

  return false if (receipt['status'] && receipt['status'] == '0x0')
end

.transfer_eth(private_key, amount, gas_limit, gas_price, to) ⇒ Object



110
111
112
113
114
115
116
117
# File 'lib/eth_tool.rb', line 110

def self.transfer_eth(private_key, amount, gas_limit, gas_price, to)
  if amount < (1.0/10**18)
    logger.info "转账金额不能小于以太坊最小单位"
    return 
  end
  rawtx = generate_raw_transaction(private_key, amount, nil, gas_limit, gas_price, to)
  @@rpc.eth_send_raw_transaction(rawtx)
end

.transfer_token(private_key, token_contract_address, token_decimals, amount, gas_limit, gas_price, to, nonce = nil) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/eth_tool.rb', line 96

def self.transfer_token(private_key, token_contract_address, token_decimals, amount, gas_limit, gas_price, to, nonce=nil)
  if amount < (1.0/10**token_decimals)
    logger.info "转账金额不能小于精度最小单位(#{token_contract_address}, #{token_decimals})"
    return
  end

  # 生成raw transaction
  amount_in_wei = (amount * (10**token_decimals)).to_i
  data = '0xa9059cbb' + padding(to) + padding(dec_to_hex(amount_in_wei)) # Ethereum::Function.calc_id('transfer(address,uint256)') # a9059cbb
  rawtx = generate_raw_transaction(private_key, 0, data, gas_limit, gas_price, token_contract_address, nonce)
  
  @@rpc.eth_send_raw_transaction(rawtx)
end

.tx_replace(private_key, txhash, gas_price) ⇒ Object



148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/eth_tool.rb', line 148

def self.tx_replace(private_key, txhash, gas_price)
  tx = @@rpc.eth_get_transaction_by_hash(txhash)["result"]
  return if tx.nil?

  value = tx["value"].to_i(16)
  data = tx["input"]
  gas_limit = tx["gas"].to_i(16)
  gas_price = gas_price
  to = tx["to"]
  nonce = tx["nonce"].to_i(16)

  rawtx = generate_raw_transaction(private_key, value, data, gas_limit, gas_price, to, nonce)
  @@rpc.eth_send_raw_transaction(rawtx)
end

.wait_for_miner(txhash, timeout: 1200, step: 20) ⇒ Object



189
190
191
192
193
194
195
196
# File 'lib/eth_tool.rb', line 189

def self.wait_for_miner(txhash, timeout: 1200, step: 20)
  start_time = Time.now
  loop do
    raise Timeout::Error if ((Time.now - start_time) > timeout)
    return true if mined?(txhash)
    sleep step
  end
end