peatio 0.4.5 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8cc7f7dae49e5e34e71138ae6f7c5097516309d421fe9a048d3fffc5aa4c4081
4
- data.tar.gz: 209f063cd346d27742b57a12a991904cd7c9c320154aab9fb7d50eb9ce45d736
3
+ metadata.gz: 25f7358d1288e7c007202f1035d577a9dcd33b61b7f23bddf6d5b8c057bb5607
4
+ data.tar.gz: 24fab553870452be347d1fe1fc1067556e485a5856767e4306e5548758135b24
5
5
  SHA512:
6
- metadata.gz: 9b85df8a9f919dadc29dfaff264555cfecefd1b5477e0161ccf517f1370c1a7a4e6f7b663cdc7b8982b70f937c85bf522f94b887ce30d5ec86710385814f2f37
7
- data.tar.gz: 0b2081360b0f23ca898dbef3e0a0d3327f9355ff0e2e5de3c45fb45d30ff0da7fc30d6d2885abbe74210cf87b619578b0dccc1606c4f218bad772882df9cd197
6
+ metadata.gz: ff767337bec9e7b6dfb2993bbe94535138502f9faf761ac70b2e7b30d26bb76df89130aa7cffb1ebab342bad51c512b79034d4f72f42b40a92f7e18240d3eb11
7
+ data.tar.gz: 4b3602e261f5d09480d2c72e6db5c45617bbf257ebbbf7f3637dd20bb39497569f45f0d189868f437e88c2e0c8bdf6cf44408386714f82d3dacde060cb4a6d5a
@@ -2,6 +2,7 @@ PATH
2
2
  remote: .
3
3
  specs:
4
4
  peatio (0.4.5)
5
+ activemodel (~> 5.2.3)
5
6
  amqp
6
7
  bunny
7
8
  clamp
@@ -13,6 +14,13 @@ PATH
13
14
  GEM
14
15
  remote: https://rubygems.org/
15
16
  specs:
17
+ activemodel (5.2.3)
18
+ activesupport (= 5.2.3)
19
+ activesupport (5.2.3)
20
+ concurrent-ruby (~> 1.0, >= 1.0.2)
21
+ i18n (>= 0.7, < 2)
22
+ minitest (~> 5.1)
23
+ tzinfo (~> 1.1)
16
24
  amq-protocol (2.3.0)
17
25
  amqp (1.8.0)
18
26
  amq-protocol (>= 2.2.0)
@@ -24,6 +32,7 @@ GEM
24
32
  bunny-mock (1.7.0)
25
33
  bunny (>= 1.7)
26
34
  clamp (1.3.0)
35
+ concurrent-ruby (1.1.5)
27
36
  diff-lcs (1.3)
28
37
  docile (1.3.1)
29
38
  em-spec (0.2.7)
@@ -36,9 +45,12 @@ GEM
36
45
  websocket
37
46
  eventmachine (1.2.7)
38
47
  http_parser.rb (0.6.0)
48
+ i18n (1.6.0)
49
+ concurrent-ruby (~> 1.0)
39
50
  jaro_winkler (1.5.1)
40
51
  json (2.1.0)
41
52
  jwt (2.1.0)
53
+ minitest (5.11.3)
42
54
  mysql2 (0.5.2)
43
55
  parallel (1.12.1)
44
56
  parser (2.5.1.2)
@@ -80,6 +92,9 @@ GEM
80
92
  simplecov-json (0.2)
81
93
  json
82
94
  simplecov
95
+ thread_safe (0.3.6)
96
+ tzinfo (1.2.5)
97
+ thread_safe (~> 0.1)
83
98
  unicode-display_width (1.4.0)
84
99
  websocket (1.2.8)
85
100
 
@@ -17,4 +17,15 @@ module Peatio
17
17
  require_relative "peatio/injectors/peatio_events"
18
18
  require_relative "peatio/security/key_generator"
19
19
  require_relative "peatio/auth/jwt_authenticator"
20
+
21
+ require_relative "peatio/blockchain/abstract"
22
+ require_relative "peatio/blockchain/error"
23
+ require_relative "peatio/blockchain/registry"
24
+
25
+ require_relative "peatio/wallet/abstract"
26
+ require_relative "peatio/wallet/error"
27
+ require_relative "peatio/wallet/registry"
28
+
29
+ require_relative "peatio/transaction"
30
+ require_relative "peatio/block"
20
31
  end
@@ -0,0 +1,25 @@
1
+ module Peatio
2
+ class AdapterRegistry
3
+ Error = Class.new(StandardError)
4
+ DuplicatedAdapterError = Class.new(Error)
5
+ NotRegisteredAdapterError = Class.new(Error)
6
+
7
+ def []=(name, instance)
8
+ name = name.to_sym
9
+ raise DuplicatedAdapterError, name if adapters.key?(name)
10
+ adapters[name] = instance
11
+ end
12
+
13
+ def [](name)
14
+ adapters.fetch(name.to_sym) { raise NotRegisteredAdapterError, name }
15
+ end
16
+
17
+ def adapters
18
+ @adapters ||= {}
19
+ end
20
+
21
+ def adapters=(h)
22
+ @adapters = h
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ module Peatio
2
+ class Block
3
+ include Enumerable
4
+
5
+ delegate :each, to: :@transactions
6
+
7
+ attr_reader :number, :transactions
8
+
9
+ def initialize(number, transactions)
10
+ @number = number
11
+ @transactions = transactions
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,157 @@
1
+ module Peatio #:nodoc:
2
+ module Blockchain #:nodoc:
3
+
4
+ # @abstract Represents basic blockchain interface.
5
+ #
6
+ # Subclass and override abstract methods to implement
7
+ # a peatio plugable blockchain.
8
+ # Than you need to register your blockchain implementation.
9
+ #
10
+ # @see Bitcoin::Blockchain Bitcoin as example of Abstract imlementation
11
+ # (inside peatio source https://github.com/rubykube/peatio).
12
+ #
13
+ # @example
14
+ #
15
+ # class MyBlockchain < Peatio::Abstract::Blockchain
16
+ # def fetch_block(block_number)
17
+ # # do something
18
+ # end
19
+ # ...
20
+ # end
21
+ #
22
+ # # Register MyBlockchain as peatio plugable blockchain.
23
+ # Peatio::Blockchain.registry[:my_blockchain] = MyBlockchain.new
24
+ #
25
+ # @author
26
+ # Yaroslav Savchuk <savchukyarpolk@gmail.com> (https://github.com/ysv)
27
+ class Abstract
28
+
29
+ # Hash of features supported by blockchain.
30
+ #
31
+ # @abstract
32
+ #
33
+ # @see Abstract::SUPPORTED_FEATURES for list of features supported by peatio.
34
+ #
35
+ # @!attribute [r] features
36
+ # @return [Hash] list of features supported by blockchain.
37
+ attr_reader :features
38
+
39
+ # List of features supported by peatio.
40
+ #
41
+ # @note Features list:
42
+ #
43
+ # case_sensitive - defines if transactions and addresses of current
44
+ # blockchain are case_sensitive.
45
+ #
46
+ # cash_addr_format - defines if blockchain supports Cash Address format
47
+ # for more info see (https://support.exodus.io/article/664-bitcoin-cash-address-format)
48
+ SUPPORTED_FEATURES = %i[case_sensitive cash_addr_format].freeze
49
+
50
+
51
+ # Current blockchain settings for performing API calls and building blocks.
52
+ #
53
+ # @abstract
54
+ #
55
+ # @see Abstract::SUPPORTED_SETTINGS for list of settings required by blockchain.
56
+ #
57
+ # @!attribute [r] settings
58
+ # @return [Hash] current blockchain settings.
59
+ attr_reader :settings
60
+
61
+ # List of configurable settings.
62
+ #
63
+ # @see #configure
64
+ SUPPORTED_SETTINGS = %i[server currencies].freeze
65
+
66
+
67
+ # Abstract constructor.
68
+ #
69
+ # @abstract
70
+ #
71
+ # @example
72
+ # class MyBlockchain < Peatio::Abstract::Blockchain
73
+ #
74
+ # DEFAULT_FEATURES = {case_sensitive: true, cash_addr_format: false}.freeze
75
+ #
76
+ # # You could override default features by passing them to initializer.
77
+ # def initialize(my_custom_features = {})
78
+ # @features = DEFAULT_FEATURES.merge(my_custom_features)
79
+ # end
80
+ # ...
81
+ # end
82
+ #
83
+ # # Register MyBlockchain as peatio plugable blockchain.
84
+ # custom_features = {cash_addr_format: true}
85
+ # Peatio::Blockchain.registry[:my_blockchain] = MyBlockchain.new(custom_features)
86
+ def initialize(*)
87
+ abstract_method
88
+ end
89
+
90
+ # Merges given configuration parameters with defined during initialization
91
+ # and returns the result.
92
+ #
93
+ # @abstract
94
+ #
95
+ # @param [Hash] settings parameters to use.
96
+ #
97
+ # @option settings [String] :server Public blockchain API endpoint.
98
+ # @option settings [Array<Hash>] :currencies List of currency hashes
99
+ # with :id,:base_factor,:options(deprecated) keys.
100
+ # Custom keys could be added by defining them in Currency #options.
101
+ #
102
+ # @return [Hash] merged settings.
103
+ def configure(settings = {})
104
+ abstract_method
105
+ end
106
+
107
+ # Fetches blockchain block by calling API and builds block object
108
+ # from response payload.
109
+ #
110
+ # @abstract
111
+ #
112
+ # @param block_number [Integer] the block number.
113
+ # @return [Peatio::Block] the block object.
114
+ # @raise [Peatio::Blockchain::ClientError] if error was raised
115
+ # on blockchain API call.
116
+ def fetch_block!(block_number)
117
+ abstract_method
118
+ end
119
+
120
+ # Fetches current blockchain height by calling API and returns it as number.
121
+ #
122
+ # @abstract
123
+ #
124
+ # @return [Integer] the current blockchain height.
125
+ # @raise [Peatio::Blockchain::ClientError] if error was raised
126
+ # on blockchain API call.
127
+ def latest_block_number
128
+ abstract_method
129
+ end
130
+
131
+ # Fetches address balance of specific currency.
132
+ #
133
+ # @note Optional. Don't override this method if your blockchain
134
+ # doesn't provide functionality to get balance by address.
135
+ #
136
+ # @param address [String] the address for requesting balance.
137
+ # @param currency_id [String] which currency balance we need to request.
138
+ # @return [BigDecimal] the current address balance.
139
+ # @raise [Peatio::Blockchain::ClientError,Peatio::Blockchain::UnavailableAddressBalanceError]
140
+ # if error was raised on blockchain API call ClientError is raised.
141
+ # if blockchain API call was successful but we can't detect balance
142
+ # for address Error is raised.
143
+ def load_balance_of_address!(address, currency_id)
144
+ raise Peatio::Blockchain::UnavailableAddressBalanceError
145
+ end
146
+
147
+ private
148
+
149
+ # Method for defining other methods as abstract.
150
+ #
151
+ # @raise [MethodNotImplemented]
152
+ def abstract_method
153
+ method_not_implemented
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,37 @@
1
+ module Peatio
2
+ module Blockchain
3
+ Error = Class.new(StandardError)
4
+
5
+ class ClientError < Error
6
+
7
+ attr_reader :wrapped_ex
8
+
9
+ def initialize(ex_or_string)
10
+ @wrapped_ex = nil
11
+
12
+ if ex_or_string.respond_to?(:backtrace)
13
+ super(ex_or_string.message)
14
+ @wrapped_exception = ex_or_string
15
+ else
16
+ super(ex_or_string.to_s)
17
+ end
18
+ end
19
+ end
20
+
21
+ class MissingSettingError < Error
22
+ def initialize(key)
23
+ super "#{key.capitalize} setting is missing"
24
+ end
25
+ end
26
+
27
+ class UnavailableAddressBalanceError < Error
28
+ def initialize(address)
29
+ @address = address
30
+ end
31
+
32
+ def message
33
+ "Unable to load #{@address} balance"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,16 @@
1
+ require "peatio/adapter_registry"
2
+
3
+ module Peatio
4
+ module Blockchain
5
+
6
+ VERSION = "1.0".freeze
7
+
8
+ class << self
9
+ def registry
10
+ @registry ||= Registry.new
11
+ end
12
+ end
13
+ class Registry < Peatio::AdapterRegistry
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,39 @@
1
+ require 'active_support/concern'
2
+ require 'active_model'
3
+
4
+ module Peatio
5
+ class Transaction
6
+ include ActiveModel::Model
7
+
8
+ STATUSES = %i[success pending fail].freeze
9
+
10
+ attr_accessor :hash, :txout,
11
+ :to_address,
12
+ :amount,
13
+ :block_number,
14
+ :currency_id
15
+
16
+ attr_writer :status
17
+
18
+ validates :hash, :txout,
19
+ :to_address,
20
+ :amount,
21
+ :block_number,
22
+ :currency_id,
23
+ :status,
24
+ presence: true
25
+
26
+ validates :block_number,
27
+ numericality: { greater_than_or_equal_to: 0, only_integer: true }
28
+
29
+ validates :amount,
30
+ numericality: { greater_than_or_equal_to: 0 }
31
+
32
+ validates :status, inclusion: { in: STATUSES }
33
+
34
+ # TODO: rewrite this method
35
+ def status
36
+ @status.to_sym
37
+ end
38
+ end
39
+ end
@@ -1,3 +1,3 @@
1
1
  module Peatio
2
- VERSION = "0.4.5"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -0,0 +1,168 @@
1
+ module Peatio
2
+ module Wallet
3
+ # @abstract Represents basic wallet interface.
4
+ #
5
+ # Subclass and override abstract methods to implement
6
+ # a peatio plugable wallet.
7
+ # Than you need to register your wallet implementation.
8
+ #
9
+ # @see Bitcoin::Wallet Bitcoin as example of Abstract imlementation.
10
+ #
11
+ # @example
12
+ #
13
+ # class MyWallet < Peatio::Abstract::Wallet
14
+ # def create_address(options = {})
15
+ # # do something
16
+ # end
17
+ # ...
18
+ # end
19
+ #
20
+ # # Register MyWallet as peatio plugable wallet.
21
+ # Peatio::Wallet.registry[:my_wallet] = MyWallet.new
22
+ #
23
+ # @author
24
+ # Yaroslav Savchuk <savchukyarpolk@gmail.com> (https://github.com/ysv)
25
+ class Abstract
26
+ # Current wallet settings for performing API calls.
27
+ #
28
+ # @abstract
29
+ #
30
+ # @!attribute [r] settings
31
+ # @return [Hash] current wallet settings.
32
+ attr_reader :settings
33
+
34
+ # List of configurable settings.
35
+ #
36
+ # @see #configure
37
+ SUPPORTED_SETTINGS = %i[wallet currency].freeze
38
+
39
+
40
+ # Abstract constructor.
41
+ #
42
+ # @abstract
43
+ #
44
+ # @example
45
+ # class MyWallet< Peatio::Abstract::Wallet
46
+ #
47
+ # # You could customize your wallet by passing features.
48
+ # def initialize(my_custom_features = {})
49
+ # @features = my_custom_features
50
+ # end
51
+ # ...
52
+ # end
53
+ #
54
+ # # Register MyWallet as peatio plugable wallet.
55
+ # custom_features = {cash_addr_format: true}
56
+ # Peatio::Wallet.registry[:my_wallet] = MyWallet.new(custom_features)
57
+ def initialize(*)
58
+ abstract_method
59
+ end
60
+
61
+ # Merges given configuration parameters with defined during initialization
62
+ # and returns the result.
63
+ #
64
+ # @abstract
65
+ #
66
+ # @param [Hash] settings configurations to use.
67
+ # @option settings [Hash] :wallet Wallet settings for performing API calls.
68
+ # With :address required key other settings could be customized
69
+ # using Wallet#settings.
70
+ # @option settings [Array<Hash>] :currencies List of currency hashes
71
+ # with :id,:base_factor,:options(deprecated) keys.
72
+ # Custom keys could be added by defining them in Currency #options.
73
+ #
74
+ # @return [Hash] merged settings.
75
+ def configure(settings = {})
76
+ abstract_method
77
+ end
78
+
79
+ # Performs API call for address creation and returns it.
80
+ #
81
+ # @abstract
82
+ #
83
+ # @param [Hash] options
84
+ # @options options [String] :uid User UID which requested address creation.
85
+ #
86
+ # @return [Hash] newly created blockchain address.
87
+ #
88
+ # @raise [Peatio::Blockchain::ClientError] if error was raised
89
+ # on wallet API call.
90
+ #
91
+ # @example
92
+ # { address: :fake_address,
93
+ # secret: :changeme,
94
+ # details: { uid: account.member.uid } }
95
+ def create_address!(options = {})
96
+ abstract_method
97
+ end
98
+
99
+ # Performs API call for creating transaction and returns updated transaction.
100
+ #
101
+ # @abstract
102
+ #
103
+ # @param [Peatio::Transaction] transaction transaction with defined
104
+ # to_address, amount & currency_id.
105
+ #
106
+ # @param [Hash] options
107
+ # @options options [String] :subtract_fee Defines if you need to subtract
108
+ # fee from amount defined in transaction.
109
+ # It means that you need to deduct fee from amount declared in
110
+ # transaction and send only remaining amount.
111
+ # If transaction amount is 1.0 and estimated fee
112
+ # for sending transaction is 0.01 you need to send 0.09
113
+ # so 1.0 (0.9 + 0.1) will be subtracted from wallet balance
114
+ #
115
+ # @options options [String] custon options for wallet client.
116
+ #
117
+ # @return [Peatio::Transaction] transaction with updated hash.
118
+ #
119
+ # @raise [Peatio::Blockchain::ClientError] if error was raised
120
+ # on wallet API call.
121
+ def create_transaction!(transaction, options = {})
122
+ abstract_method
123
+ end
124
+
125
+ # Fetches address balance of specific currency.
126
+ #
127
+ # @note Optional. Don't override this method if your blockchain
128
+ # doesn't provide functionality to get balance by address.
129
+ #
130
+ # @return [BigDecimal] the current address balance.
131
+ #
132
+ # @raise [Peatio::Blockchain::ClientError,Peatio::Blockchain::UnavailableAddressBalanceError]
133
+ # if error was raised on wallet API call ClientError is raised.
134
+ # if wallet API call was successful but we can't detect balance
135
+ # for address Error is raised.
136
+ def load_balance!
137
+ raise Peatio::Wallet::UnavailableAddressBalanceError
138
+ end
139
+
140
+ # Performs API call(s) for preparing for deposit collection.
141
+ # E.g deposits ETH for collecting ERC20 tokens in case of Ethereum blockchain.
142
+ #
143
+ # @note Optional. Override this method only if you need additional step
144
+ # before deposit collection.
145
+ #
146
+ # @param [Peatio::Transaction] deposit_transaction transaction which
147
+ # describes received deposit.
148
+ #
149
+ # @param [Array<Peatio::Transaction>] spread_transactions result of deposit
150
+ # spread between wallets.
151
+ #
152
+ # @return [Array<Peatio::Transaction>] transaction created for
153
+ # deposit collection preparing.
154
+ # By default return empty [Array]
155
+ def prepare_deposit_collection!(deposit_transaction, spread_transactions, deposit_currency)
156
+ # This method is mostly used for coins which needs additional fees
157
+ # to be deposited before deposit collection.
158
+ []
159
+ end
160
+
161
+ private
162
+
163
+ def abstract_method
164
+ method_not_implemented
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,37 @@
1
+ module Peatio
2
+ module Wallet
3
+ Error = Class.new(StandardError)
4
+
5
+ class ClientError < Error
6
+
7
+ attr_reader :wrapped_ex
8
+
9
+ def initialize(ex_or_string)
10
+ @wrapped_ex = nil
11
+
12
+ if ex_or_string.respond_to?(:backtrace)
13
+ super(ex_or_string.message)
14
+ @wrapped_exception = ex_or_string
15
+ else
16
+ super(ex_or_string.to_s)
17
+ end
18
+ end
19
+ end
20
+
21
+ class MissingSettingError < Error
22
+ def initialize(key)
23
+ super "#{key.capitalize} setting is missing"
24
+ end
25
+ end
26
+
27
+ class UnavailableAddressBalanceError < Error
28
+ def initialize(address)
29
+ @address = address
30
+ end
31
+
32
+ def message
33
+ "Unable to load #{@address} balance"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,16 @@
1
+ require "peatio/adapter_registry"
2
+
3
+ module Peatio
4
+ module Wallet
5
+
6
+ VERSION = "1.0".freeze
7
+
8
+ class << self
9
+ def registry
10
+ @registry ||= Registry.new
11
+ end
12
+ end
13
+ class Registry < Peatio::AdapterRegistry
14
+ end
15
+ end
16
+ end
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ["lib"]
24
24
 
25
+ spec.add_dependency "activemodel", "~> 5.2.3"
25
26
  spec.add_dependency "clamp"
26
27
  spec.add_dependency "amqp"
27
28
  spec.add_dependency "eventmachine"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: peatio
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Louis B.
@@ -9,8 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-01-25 00:00:00.000000000 Z
12
+ date: 2019-05-08 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activemodel
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 5.2.3
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 5.2.3
14
28
  - !ruby/object:Gem::Dependency
15
29
  name: clamp
16
30
  requirement: !ruby/object:Gem::Requirement
@@ -286,8 +300,13 @@ files:
286
300
  - bin/peatio
287
301
  - bin/setup
288
302
  - lib/peatio.rb
303
+ - lib/peatio/adapter_registry.rb
289
304
  - lib/peatio/auth/error.rb
290
305
  - lib/peatio/auth/jwt_authenticator.rb
306
+ - lib/peatio/block.rb
307
+ - lib/peatio/blockchain/abstract.rb
308
+ - lib/peatio/blockchain/error.rb
309
+ - lib/peatio/blockchain/registry.rb
291
310
  - lib/peatio/command/amqp.rb
292
311
  - lib/peatio/command/base.rb
293
312
  - lib/peatio/command/db.rb
@@ -305,7 +324,11 @@ files:
305
324
  - lib/peatio/security/key_generator.rb
306
325
  - lib/peatio/sql/client.rb
307
326
  - lib/peatio/sql/schema.rb
327
+ - lib/peatio/transaction.rb
308
328
  - lib/peatio/version.rb
329
+ - lib/peatio/wallet/abstract.rb
330
+ - lib/peatio/wallet/error.rb
331
+ - lib/peatio/wallet/registry.rb
309
332
  - peatio.gemspec
310
333
  homepage: https://www.peatio.tech
311
334
  licenses: []
@@ -325,8 +348,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
325
348
  - !ruby/object:Gem::Version
326
349
  version: '0'
327
350
  requirements: []
328
- rubyforge_project:
329
- rubygems_version: 2.7.7
351
+ rubygems_version: 3.0.3
330
352
  signing_key:
331
353
  specification_version: 4
332
354
  summary: Peatio is a gem for running critical core services