ultex-eos 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +8 -0
  3. data/Gemfile.lock +145 -0
  4. data/LICENSE.txt +12 -0
  5. data/README.md +39 -0
  6. data/Rakefile +8 -0
  7. data/bin/console +15 -0
  8. data/bin/setup +8 -0
  9. data/config/blockchains.yml +10 -0
  10. data/config/currencies.yml +43 -0
  11. data/config/wallets.yml +121 -0
  12. data/coverage/.last_run.json +5 -0
  13. data/coverage/.resultset.json +433 -0
  14. data/coverage/.resultset.json.lock +0 -0
  15. data/coverage/assets/0.10.2/application.css +799 -0
  16. data/coverage/assets/0.10.2/application.js +1707 -0
  17. data/coverage/assets/0.10.2/colorbox/border.png +0 -0
  18. data/coverage/assets/0.10.2/colorbox/controls.png +0 -0
  19. data/coverage/assets/0.10.2/colorbox/loading.gif +0 -0
  20. data/coverage/assets/0.10.2/colorbox/loading_background.png +0 -0
  21. data/coverage/assets/0.10.2/favicon_green.png +0 -0
  22. data/coverage/assets/0.10.2/favicon_red.png +0 -0
  23. data/coverage/assets/0.10.2/favicon_yellow.png +0 -0
  24. data/coverage/assets/0.10.2/loading.gif +0 -0
  25. data/coverage/assets/0.10.2/magnify.png +0 -0
  26. data/coverage/assets/0.10.2/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  27. data/coverage/assets/0.10.2/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  28. data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  29. data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  30. data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  31. data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  32. data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  33. data/coverage/assets/0.10.2/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  34. data/coverage/assets/0.10.2/smoothness/images/ui-icons_222222_256x240.png +0 -0
  35. data/coverage/assets/0.10.2/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  36. data/coverage/assets/0.10.2/smoothness/images/ui-icons_454545_256x240.png +0 -0
  37. data/coverage/assets/0.10.2/smoothness/images/ui-icons_888888_256x240.png +0 -0
  38. data/coverage/assets/0.10.2/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  39. data/coverage/index.html +2730 -0
  40. data/docs/deposit.md +5 -0
  41. data/docs/integration.md +31 -0
  42. data/docs/rpc.md +28 -0
  43. data/docs/testnet.md +13 -0
  44. data/docs/tokens.md +6 -0
  45. data/lib/ultex/eos.rb +21 -0
  46. data/lib/ultex/eos/blockchain.rb +107 -0
  47. data/lib/ultex/eos/client.rb +66 -0
  48. data/lib/ultex/eos/hooks.rb +44 -0
  49. data/lib/ultex/eos/railtie.rb +15 -0
  50. data/lib/ultex/eos/transaction_serializer.rb +81 -0
  51. data/lib/ultex/eos/version.rb +7 -0
  52. data/lib/ultex/eos/wallet.rb +99 -0
  53. data/ultex-eos.gemspec +44 -0
  54. metadata +305 -0
@@ -0,0 +1,5 @@
1
+ # Deposit addresses.
2
+
3
+ For EOS and all others eosio.token deposits we use one EOS account which is defined like deposit wallet address in peatio.
4
+ In Peatio EOS plugin we will define owner of deposit by user unige identifier (UID),
5
+ uid should be sended like memo, so it's very important to always send this memo to get your deposit in peatio
@@ -0,0 +1,31 @@
1
+ # Integration.
2
+
3
+ For Peatio Eos plugin integration you need to do the following steps:
4
+
5
+ ## Image Build.
6
+
7
+ 1. Add peatio-eos gem into your Gemfile.plugin
8
+ ```ruby
9
+ gem 'peatio-eos', '~> 0.1.0'
10
+ ```
11
+
12
+ 2. Run `bundle install` for updating Gemfile.lock
13
+
14
+ 3. Build custom Peatio [docker image with Eos plugin](https://github.com/rubykube/peatio/blob/master/docs/plugins.md#build)
15
+
16
+ 4. Push your image using `docker push`
17
+
18
+ 5. Update your deployment to use image with peatio-eos gem
19
+
20
+ ## Peatio Configuration.
21
+
22
+ 1. Create EOS Blockchain [config example](../config/blockchains.yml).
23
+ * In EOS blockchain blocks goes very fast, so to don't fall behind with blocks from your node we recommend to set step at least to 50 and confirmations at least to 20
24
+
25
+ 2. Create EOS Currency [config example](../config/currencies.yml).
26
+ * No additional steps are needed
27
+
28
+ 3. Create EOS Wallets [config example](../config/wallets.yml)(deposit and hot wallets are required).
29
+ * Be sure that you have eos_token_name option filled even for EOS currency itself
30
+ * We need to communicate with keosd to sign transaction, keosd is running on non default eos port which is 8900 and passed to json_rpc function when we sign
31
+ transaction
@@ -0,0 +1,28 @@
1
+ # RPC Calls
2
+
3
+ The next list of RPC calls where used for plugin development.
4
+ For response examples see spec/resources:
5
+
6
+ * /v1/chain/get_info
7
+ `curl http://127.0.0.1:8888/v1/chain/get_info`
8
+
9
+ * /v1/chain/get_block
10
+ `curl -X POST -d '{"block_num_or_id": <needed_block_num>}' http://127.0.0.1:8888/v1/chain/get_block`
11
+
12
+ * /v1/chain/get_currency_balance
13
+ `curl -X POST -d '{"account": "<your_accont_name>", "code": "eosio.token"}' http://127.0.0.1:8888/v1/chain/get_currency_balance`
14
+
15
+ The next three calls where used to push transaction, for this calls you need to pass json with params which are needed for successfull creation of transaction
16
+ Example of such json's can be found in lib/peatio/eos/transaction_json.rb
17
+
18
+ * /v1/chain/abi_json_to_bin
19
+ `curl -X POST -d @<json_file_with_info_which_you_need_to_encode> http://127.0.0.1:8888/v1/chain/abi_json_to_bin`
20
+
21
+
22
+ **Sign transaction call goes to _/wallet_ route because [keosd](https://developers.eos.io/eosio-nodeos/v1.2.0/docs/keosd-overview) handle everything related to wallets, by default keosd is running on port 8900**
23
+
24
+ * /v1/wallet/sign_transaction
25
+ `curl -X POST -d @<json_file_with_transaction_which_you_need_to_sign> http://127.0.0.1:8900/v1/wallet/sign_transaction`
26
+
27
+ * /v1/chain/push_transaction
28
+ `curl -X POST -d @<json_file_with_signed_transaction_to_push> http://127.0.0.1:8888/v1/chain/push_transaction`
@@ -0,0 +1,13 @@
1
+ # Getting Eos in testnet.
2
+
3
+ ## Faucet
4
+
5
+ * [monitor.jungletestnet.io](https://monitor.jungletestnet.io/#faucet)
6
+
7
+ ## Wallet
8
+
9
+ * [monitor.jungletestnet.io](https://monitor.jungletestnet.io/#accountInfo)
10
+
11
+ ## Explorer
12
+
13
+ * [jungle.bloks.io](https://jungle.bloks.io)
@@ -0,0 +1,6 @@
1
+ # EOS tokens
2
+
3
+ EOS blockchain use eosio.token as a standard for tokens.
4
+ EOS itself is also token.
5
+ Everything that you need to do to integrate some eos token is to add in options eos_token_name option and fill it with the name of token.
6
+ This Option is also not optional even for eos itself, as long as it's also eosio.token
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ultex/eos/version"
4
+ require "active_support/core_ext/object/blank"
5
+ require "active_support/core_ext/enumerable"
6
+ require "peatio"
7
+
8
+ module Peatio
9
+ module Eos
10
+ require "bigdecimal"
11
+ require "bigdecimal/util"
12
+
13
+ require "ultex/eos/blockchain"
14
+ require "ultex/eos/client"
15
+ require "ultex/eos/wallet"
16
+ require "ultex/eos/transaction_serializer"
17
+
18
+ require "ultex/eos/hooks"
19
+ require "ultex/eos/version"
20
+ end
21
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Peatio
4
+ module Eos
5
+ class Blockchain < Peatio::Blockchain::Abstract
6
+ DEFAULT_FEATURES = {case_sensitive: true, cash_addr_format: false}.freeze
7
+
8
+ TOKEN_STANDARD = "eosio.token"
9
+
10
+ class MissingTokenNameError < Peatio::Blockchain::Error; end
11
+ class UndefinedCurrencyError < Peatio::Blockchain::Error; end
12
+
13
+ def initialize(custom_features={})
14
+ @features = DEFAULT_FEATURES.merge(custom_features).slice(*SUPPORTED_FEATURES)
15
+ @settings = {}
16
+ end
17
+
18
+ def configure(settings={})
19
+ # Clean client state during configure.
20
+ @client = nil
21
+
22
+ supported_settings = settings.slice(*SUPPORTED_SETTINGS)
23
+ supported_settings[:currencies]&.each do |c|
24
+ raise MissingTokenNameError, c[:id] if c.dig(:options, :eos_token_name).blank?
25
+ end
26
+ @settings.merge!(supported_settings)
27
+ end
28
+
29
+ def fetch_block!(block_number)
30
+ client.json_rpc("/v1/chain/get_block", "block_num_or_id" => block_number)
31
+ .fetch("transactions").each_with_object([]) do |tx, txs_array|
32
+ txs = build_transaction(tx).map do |ntx|
33
+ Peatio::Transaction.new(ntx.merge(block_number: block_number))
34
+ end
35
+ txs_array.append(*txs)
36
+ end.yield_self {|txs_array| Peatio::Block.new(block_number, txs_array) }
37
+ rescue Peatio::Eos::Client::Error => e
38
+ raise Peatio::Blockchain::ClientError, e
39
+ end
40
+
41
+ def load_balance_of_address!(address, currency_id)
42
+ currency = settings_fetch(:currencies).find {|c| c[:id] == currency_id }
43
+ raise UndefinedCurrencyError unless currency
44
+
45
+ balance = client.json_rpc("/v1/chain/get_currency_balance",
46
+ "account" => address, "code" => TOKEN_STANDARD)
47
+ .find {|b| b.split[1] == currency.dig(:options, :eos_token_name) }
48
+
49
+ # EOS return array with balances for all eosio.token currencies
50
+ balance.blank? ? 0 : normalize_balance(balance, currency)
51
+ rescue Peatio::Eos::Client::Error => e
52
+ raise Peatio::Wallet::ClientError, e
53
+ end
54
+
55
+ def latest_block_number
56
+ client.json_rpc("/v1/chain/get_info").fetch("head_block_num")
57
+ rescue Peatio::Eos::Client::Error => e
58
+ raise Peatio::Blockchain::ClientError, e
59
+ end
60
+
61
+ private
62
+
63
+ def build_transaction(tx)
64
+ return [] if tx["trx"]["id"].blank? # check for deferred transaction
65
+
66
+ tx.dig("trx", "transaction", "actions")
67
+ .each_with_object([]).with_index do |(entry, formatted_txs), i|
68
+ next unless entry["name"] == "transfer" && !entry["data"]["to"].empty?
69
+
70
+ amount, token_name = entry["data"]["quantity"]&.split
71
+ next if token_name.nil? || amount.to_d < 0
72
+
73
+ currencies = settings_fetch(:currencies).select {|c| c.dig(:options, :eos_token_name) == token_name }
74
+ status = tx["status"] == "executed" ? "success" : "failed"
75
+ address = "#{entry['data']['to']}?memo=#{get_memo(entry['data']['memo'])}"
76
+
77
+ # Build transaction for each currency belonging to blockchain.
78
+
79
+ currencies.pluck(:id).each do |currency_id|
80
+ formatted_txs << {hash: tx["trx"]["id"],
81
+ txout: i,
82
+ to_address: address,
83
+ amount: amount.to_d,
84
+ status: status,
85
+ currency_id: currency_id}
86
+ end
87
+ end
88
+ end
89
+
90
+ def normalize_balance(balance, currency)
91
+ balance.chomp(currency.dig(:options, :eos_token_name)).to_d
92
+ end
93
+
94
+ def get_memo(memo)
95
+ memo.match(/\bID[A-Z0-9]{10}\z/) ? memo : ""
96
+ end
97
+
98
+ def client
99
+ @client ||= Peatio::Eos::Client.new(settings_fetch(:server))
100
+ end
101
+
102
+ def settings_fetch(key)
103
+ @settings.fetch(key) { raise Peatio::Blockchain::MissingSettingError, key.to_s }
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "memoist"
4
+ require "faraday"
5
+ require "better-faraday"
6
+
7
+ module Peatio
8
+ module Eos
9
+ class Client
10
+ Error = Class.new(StandardError)
11
+ class ConnectionError < Error; end
12
+
13
+ class ResponseError < StandardError
14
+ def initialize(code, msg)
15
+ super "#{msg} (#{code})"
16
+ end
17
+ end
18
+
19
+ extend Memoist
20
+
21
+ def initialize(endpoint, idle_timeout: 500)
22
+ @rpc_endpoint = URI.parse(endpoint)
23
+ @idle_timeout = idle_timeout
24
+ end
25
+
26
+ def json_rpc(path, params=nil, port=nil)
27
+ # We need to communicate with keosd to sign transaction,
28
+ # keosd is running on non default eos port which is 8900
29
+ # and passed to json_rpc function when we sign transaction
30
+ rpc = URI.parse(@rpc_endpoint.to_s)
31
+ rpc.port = (port.present? ? port : @rpc_endpoint.port)
32
+ response = connection(rpc).post do |req|
33
+ req.url path
34
+
35
+ # To communicate with keosd to sign transaction we need to pass Host param with keosd port
36
+ req.headers["Host"] = "0.0.0.0:#{port}" if port.present?
37
+ req.headers["Content-Type"] = "application/json"
38
+ req.body = params.to_json if params.present?
39
+ req.options.timeout = 120
40
+ end
41
+ response.assert_success!
42
+ response = JSON.parse(response.body)
43
+ return response if response.is_a?(Array) # get balance call return an array
44
+
45
+ response["error"].tap {|error| raise ResponseError.new(error["code"], error["message"]) if error }
46
+ response
47
+ rescue Faraday::Error => e
48
+ raise ConnectionError, e
49
+ rescue StandardError => e
50
+ raise Error, e
51
+ end
52
+
53
+ private
54
+
55
+ def connection(rpc)
56
+ Faraday.new(rpc) do |f|
57
+ f.adapter :net_http_persistent,
58
+ pool_size: 5,
59
+ idle_timeout: @idle_timeout,
60
+ open_timeout: @idle_timeout,
61
+ timeout: @idle_timeout
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Peatio
4
+ module Eos
5
+ module Hooks
6
+ BLOCKCHAIN_VERSION_REQUIREMENT = "~> 1.0.0"
7
+ WALLET_VERSION_REQUIREMENT = "~> 1.0.0"
8
+
9
+ class << self
10
+ def check_compatibility
11
+ unless Gem::Requirement.new(BLOCKCHAIN_VERSION_REQUIREMENT)
12
+ .satisfied_by?(Gem::Version.new(Peatio::Blockchain::VERSION))
13
+ [
14
+ "Eos blockchain version requiremnt was not suttisfied by Peatio::Blockchain.",
15
+ "Eos blockchain requires #{BLOCKCHAIN_VERSION_REQUIREMENT}.",
16
+ "Peatio::Blockchain version is #{Peatio::Blockchain::VERSION}"
17
+ ].join('\n').tap {|s| Kernel.abort s }
18
+ end
19
+
20
+ unless Gem::Requirement.new(WALLET_VERSION_REQUIREMENT)
21
+ .satisfied_by?(Gem::Version.new(Peatio::Wallet::VERSION))
22
+ [
23
+ "Eos wallet version requiremnt was not suttisfied by Peatio::Wallet.",
24
+ "Eos wallet requires #{WALLET_VERSION_REQUIREMENT}.",
25
+ "Peatio::Wallet version is #{Peatio::Wallet::VERSION}"
26
+ ].join('\n').tap {|s| Kernel.abort s }
27
+ end
28
+ end
29
+
30
+ def register
31
+ Peatio::Blockchain.registry[:eos] = Eos::Blockchain.new
32
+ Peatio::Wallet.registry[:eos] = Eos::Wallet.new
33
+ end
34
+ end
35
+
36
+ if defined?(Rails::Railtie)
37
+ require "peatio/eos/railtie"
38
+ else
39
+ check_compatibility
40
+ register
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Peatio
4
+ module Eos
5
+ class Railtie < Rails::Railtie
6
+ config.before_initialize do
7
+ Hooks.check_compatibility
8
+ end
9
+
10
+ config.after_initialize do
11
+ Hooks.register
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Peatio
4
+ module Eos
5
+ class TransactionSerializer
6
+ class << self
7
+ def to_pack_json(address: "default", to_address: "default", amount: "default")
8
+ {
9
+ "code" => "eosio.token",
10
+ "action" => "transfer",
11
+ "args" => {
12
+ "from" => address,
13
+ "to" => to_address,
14
+ "quantity" => amount,
15
+ "memo" => "transfer from peatio"
16
+ }
17
+ }
18
+ end
19
+ end
20
+
21
+ class << self
22
+ def to_sign_json(ref_block_num: 2, block_prefix: 1, expiration: "default",
23
+ address: "default", packed_data: "default", secret: "default", chain_id: "default")
24
+ [
25
+ {
26
+ "ref_block_num" => ref_block_num,
27
+ "ref_block_prefix" => block_prefix,
28
+ "max_cpu_usage_ms" => 0,
29
+ "max_net_usage_words" => 0,
30
+ "expiration" => expiration,
31
+ "region" => "0",
32
+ "actions" => [{
33
+ "account" => "eosio.token",
34
+ "name" => "transfer",
35
+ "authorization" => [{
36
+ "actor" => address,
37
+ "permission" => "active",
38
+ }],
39
+ "data" => packed_data,
40
+ }],
41
+ :signatures => []
42
+ },
43
+ [secret],
44
+ chain_id
45
+ ]
46
+ end
47
+ end
48
+
49
+ class << self
50
+ def to_push_json(address: "default", packed_data: "default", expiration: "default",
51
+ block_num: 2, block_prefix: 1, signature: "default")
52
+ {
53
+ "compression" => "none",
54
+ "transaction" => {
55
+ "actions" => [{
56
+ "account" => "eosio.token",
57
+ "name" => "transfer",
58
+ "authorization" => [{
59
+ "actor" => address,
60
+ "permission" => "active"
61
+ }],
62
+ "data" => packed_data,
63
+ }],
64
+ "expiration" => expiration,
65
+ "max_cpu_usage_ms" => 0,
66
+ "max_net_usage_words" => 0,
67
+ "delay_sec" => 0,
68
+ "ref_block_num" => block_num,
69
+ "ref_block_prefix" => block_prefix,
70
+ "context_free_actions" => [],
71
+ "context_free_data" => [],
72
+ "signatures" => signature,
73
+ "transaction_extensions" => []
74
+ },
75
+ "signatures" => signature
76
+ }
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Peatio
4
+ module Eos
5
+ VERSION = "0.2.1"
6
+ end
7
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Peatio
4
+ module Eos
5
+ class Wallet < Peatio::Wallet::Abstract
6
+ PRECISION = 4
7
+
8
+ ADDRESS_LENGTH = 12
9
+
10
+ TOKEN_STANDARD = "eosio.token"
11
+
12
+ class MissingTokenNameError < Peatio::Blockchain::Error; end
13
+
14
+ def initialize(settings={})
15
+ @settings = settings
16
+ end
17
+
18
+ def configure(settings={})
19
+ @settings.merge!(settings.slice(*SUPPORTED_SETTINGS))
20
+
21
+ @wallet = @settings.fetch(:wallet) do
22
+ raise Peatio::Wallet::MissingSettingError, :wallet
23
+ end.slice(:uri, :address, :secret)
24
+
25
+ @currency = @settings.fetch(:currency) do
26
+ raise Peatio::Wallet::MissingSettingError, :wallet
27
+ end.slice(:id, :base_factor, :options)
28
+ raise MissingTokenNameError if @currency.dig(:options, :eos_token_name).blank?
29
+ end
30
+
31
+ def create_address!(options={})
32
+ # For EOS and all others eosio.token deposits we use one EOS account which is defined like deposit wallet address in peatio.
33
+ # In Peatio EOS plugin we will define owner of deposit by user unige identifier (UID)
34
+ name = "#{@wallet.fetch(:address)}?memo=#{options.fetch(:uid)}"
35
+ {address: name, secret: @wallet.fetch(:secret)}
36
+ rescue Peatio::Eos::Client::Error => e
37
+ raise Peatio::Wallet::ClientError, e
38
+ end
39
+
40
+ def create_transaction!(transaction, _options={})
41
+ tx = transaction
42
+ amount = normalize_amount(tx.amount)
43
+ address = normalize_address(@wallet.fetch(:address))
44
+ # Pack main transaction info into hash
45
+ packed_data = client.json_rpc("/v1/chain/abi_json_to_bin", Peatio::Eos::TransactionSerializer.to_pack_json(address: address,
46
+ to_address: tx.to_address, amount: amount)).fetch("binargs")
47
+ info = client.json_rpc("/v1/chain/get_info")
48
+ # Get block info
49
+ block = client.json_rpc("/v1/chain/get_block", "block_num_or_id" => info.fetch("last_irreversible_block_num"))
50
+ ref_block_num = info.fetch("last_irreversible_block_num") & 0xFFFF
51
+ # Get transaction expiration
52
+ expiration = normalize_expiration(block.fetch("timestamp"))
53
+ # Sign transaction before push
54
+ signed = client.json_rpc("/v1/wallet/sign_transaction", Peatio::Eos::TransactionSerializer.to_sign_json(ref_block_num: ref_block_num,
55
+ block_prefix: block.fetch("ref_block_prefix"), expiration: expiration, address: address,
56
+ packed_data: packed_data, secret: @wallet.fetch(:secret), chain_id: info.fetch("chain_id")), 8900)
57
+ txid = client.json_rpc("/v1/chain/push_transaction", Peatio::Eos::TransactionSerializer.to_push_json(address: address,
58
+ packed_data: packed_data, expiration: signed.fetch("expiration"), block_num: signed.fetch("ref_block_num"),
59
+ block_prefix: signed.fetch("ref_block_prefix"), signature: signed.fetch("signatures"))).fetch("transaction_id")
60
+ tx.hash = txid
61
+ tx
62
+ rescue Peatio::Eos::Client::Error => e
63
+ raise Peatio::Wallet::ClientError, e
64
+ end
65
+
66
+ def load_balance!
67
+ balance = client.json_rpc("/v1/chain/get_currency_balance",
68
+ "account" => @wallet.fetch(:address), "code" => TOKEN_STANDARD)
69
+ .find {|b| b.split[1] == @currency.dig(:options, :eos_token_name) }
70
+ balance.blank? ? 0 : normalize_balance(balance)
71
+ rescue Peatio::Eos::Client::Error => e
72
+ raise Peatio::Wallet::ClientError, e
73
+ end
74
+
75
+ private
76
+
77
+ def normalize_address(address)
78
+ address&.split('?memo=').first
79
+ end
80
+
81
+ def normalize_amount(amount)
82
+ "%.#{PRECISION}f" % amount + " #{@currency.dig(:options, :eos_token_name)}"
83
+ end
84
+
85
+ def normalize_balance(balance)
86
+ balance.chomp(@currency.dig(:options, :eos_token_name)).to_d
87
+ end
88
+
89
+ def normalize_expiration(time)
90
+ (Time.parse(time) + 3600).iso8601(6).split("+").first
91
+ end
92
+
93
+ def client
94
+ uri = @wallet.fetch(:uri) { raise Peatio::Wallet::MissingSettingError, :uri }
95
+ @client ||= Client.new(uri)
96
+ end
97
+ end
98
+ end
99
+ end