module EthTool

Constants

VERSION

Attributes

logger[RW]

Public Class Methods

airdrop(private_key, airdrop_contract_address, token_contract_address, addresses, values, gas_limit, gas_price) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source

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

# File lib/eth_tool.rb, line 237
def self.dec_to_hex(value)
  '0x'+value.to_s(16)
end
estimate_gas(to, data) click to toggle source

def self.wputs(text)

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

end

# 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) click to toggle source
# 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) click to toggle source

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

# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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
rpc() click to toggle source
# File lib/eth_tool.rb, line 13
def self.rpc
  @@rpc
end
rpc=(url) click to toggle source
# 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) click to toggle source
# 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) click to toggle source

sweep

# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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