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.
- checksums.yaml +7 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +145 -0
- data/LICENSE.txt +12 -0
- data/README.md +39 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/config/blockchains.yml +10 -0
- data/config/currencies.yml +43 -0
- data/config/wallets.yml +121 -0
- data/coverage/.last_run.json +5 -0
- data/coverage/.resultset.json +433 -0
- data/coverage/.resultset.json.lock +0 -0
- data/coverage/assets/0.10.2/application.css +799 -0
- data/coverage/assets/0.10.2/application.js +1707 -0
- data/coverage/assets/0.10.2/colorbox/border.png +0 -0
- data/coverage/assets/0.10.2/colorbox/controls.png +0 -0
- data/coverage/assets/0.10.2/colorbox/loading.gif +0 -0
- data/coverage/assets/0.10.2/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.10.2/favicon_green.png +0 -0
- data/coverage/assets/0.10.2/favicon_red.png +0 -0
- data/coverage/assets/0.10.2/favicon_yellow.png +0 -0
- data/coverage/assets/0.10.2/loading.gif +0 -0
- data/coverage/assets/0.10.2/magnify.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.10.2/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/index.html +2730 -0
- data/docs/deposit.md +5 -0
- data/docs/integration.md +31 -0
- data/docs/rpc.md +28 -0
- data/docs/testnet.md +13 -0
- data/docs/tokens.md +6 -0
- data/lib/ultex/eos.rb +21 -0
- data/lib/ultex/eos/blockchain.rb +107 -0
- data/lib/ultex/eos/client.rb +66 -0
- data/lib/ultex/eos/hooks.rb +44 -0
- data/lib/ultex/eos/railtie.rb +15 -0
- data/lib/ultex/eos/transaction_serializer.rb +81 -0
- data/lib/ultex/eos/version.rb +7 -0
- data/lib/ultex/eos/wallet.rb +99 -0
- data/ultex-eos.gemspec +44 -0
- metadata +305 -0
data/docs/deposit.md
ADDED
|
@@ -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
|
data/docs/integration.md
ADDED
|
@@ -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
|
data/docs/rpc.md
ADDED
|
@@ -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`
|
data/docs/testnet.md
ADDED
|
@@ -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)
|
data/docs/tokens.md
ADDED
|
@@ -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
|
data/lib/ultex/eos.rb
ADDED
|
@@ -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,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,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
|