banano 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ # The <tt>Banano::WalletAccount</tt> class lets you manage
6
+ # your banano accounts, including making and receiving payments.
7
+ #
8
+ module Banano
9
+ class WalletAccount
10
+ extend Forwardable
11
+ # @!method balance(raw: true)
12
+ # (see Banano::Account#balance)
13
+ # @!method block_count
14
+ # (see Banano::Account#block_count)
15
+ # @!method delegators(raw: true)
16
+ # (see Banano::Account#delegators)
17
+ # @!method exists?
18
+ # (see Banano::Account#exists?)
19
+ # @!method id
20
+ # (see Banano::Account#id)
21
+ # @!method info((detailed: false, raw: true)
22
+ # (see Banano::Account#info)
23
+ # @!method last_modified_at
24
+ # (see Banano::Account#last_modified_at)
25
+ # @!method pending(limit: 1000, detailed: false, raw: true)
26
+ # (see Banano::Account#pending)
27
+ # @!method public_key
28
+ # (see Banano::Account#public_key)
29
+ # @!method history(limit: 1000, raw: true)
30
+ # (see Banano::Account#history)
31
+ # @!method representative
32
+ # (see Banano::Account#representative)
33
+ def_delegators :@banano_account_instance,
34
+ :balance,
35
+ :delegators,
36
+ :exists?,
37
+ :id,
38
+ :info,
39
+ :last_modified_at,
40
+ :pending,
41
+ :public_key,
42
+ :history,
43
+ :representative
44
+ alias open? exists?
45
+
46
+ def initialize(node:, wallet:, account: nil)
47
+ @node = node
48
+ @wallet = wallet
49
+ @account = account
50
+ @banano_account_instance = nil
51
+
52
+ unless @account.nil?
53
+ # Wallet must contain the account
54
+ unless Banano::Wallet.new(node: @node, wallet: @wallet).contains?(@account)
55
+ raise ArgumentError, "Account does not exist in wallet. Account: #{@account}, wallet: #{@wallet}"
56
+ end
57
+
58
+ # An object to delegate account methods that don't
59
+ # expect a wallet param in the RPC call, to allow this
60
+ # class to support all methods that can be called on Banano::Account
61
+ @banano_account_instance = Banano::Account.new(node: @node, address: @account)
62
+ end
63
+ end
64
+
65
+ # Creates a new account, or multiple new accounts, in this wallet.
66
+ #
67
+ # ==== Examples:
68
+ #
69
+ # wallet_account.create # => Banano::WalletAccount
70
+ # wallet_account.create(2) # => [Banano::WalletAccount, Banano::WalletAccount]
71
+ #
72
+ # @param count [Integer] number of accounts to create
73
+ #
74
+ # @return [Banano::WalletAccount] returns a single {Banano::WalletAccount}
75
+ # if invoked with no argument
76
+ # @return [Array<Banano::WalletAccount>] returns an Array of {Banano::WalletAccount}
77
+ # if method was called with argument +n+ > 1
78
+ # @raise [ArgumentError] if +n+ is less than 1
79
+ def create(count = 1)
80
+ raise ArgumentError, "number of accounts must be greater than 0" if count < 1
81
+
82
+ if count == 1
83
+ Banano::WalletAccount.new(node: @node,
84
+ wallet: @wallet,
85
+ account: rpc(action: :account_create)[:account])
86
+ else
87
+ Array(rpc(action: :accounts_create, params: {count: count})[:accounts]).map do |account|
88
+ Banano::WalletAccount.new(node: @node,
89
+ wallet: @wallet,
90
+ account: account)
91
+ end
92
+ end
93
+ end
94
+
95
+ # Unlinks the account from the wallet.
96
+ #
97
+ # ==== Example:
98
+ #
99
+ # wallet_account.destroy # => true
100
+ #
101
+ # @return [Boolean] +true+ if action was successful, otherwise +false+
102
+ def destroy
103
+ rpc(action: :account_remove)[:removed] == '1'
104
+ end
105
+
106
+ # Makes a payment from this account to another account
107
+ # on the banano network. Returns a <i>send</i> block hash
108
+ # if successful, or a {Banano::Error} if unsuccessful.
109
+ #
110
+ # Note, there may be a delay in receiving a response due to Proof
111
+ # of Work being done. <i>Proof of Work is precomputed for one transaction
112
+ # in the background. If it has been a while since your last transaction
113
+ # it will send instantly, the next one will need to wait for
114
+ # Proof of Work to be generated.</i>
115
+ #
116
+ # @param to [String] account id of the recipient of your payment
117
+ # @param amount [Integer|Float]
118
+ # @param raw [Boolean] raw or banano units
119
+ # @param id [String] must be unique per payment. It serves an important
120
+ # purpose; it allows you to make the same call multiple times with
121
+ # the same +id+ and be reassured that you will only ever send this
122
+ # nano payment once
123
+ #
124
+ # @return [String] the send block id for the payment
125
+ # @raise [Banano::Error] if unsuccessful
126
+ def pay(to:, amount:, raw: true, id:)
127
+ # Check that to account is a valid address
128
+ response = rpc(action: :validate_account_number, params: {account: to})
129
+ raise ArgumentError, "Account address is invalid: #{to}" unless response[:valid] == '1'
130
+
131
+ raw_amount = raw ? amount : Banano::Unit.ban_to_raw(amount)
132
+ # account is called source, so don't use the normal rpc method
133
+ p = {
134
+ wallet: @wallet,
135
+ source: @account,
136
+ destination: to,
137
+ amount: raw_amount,
138
+ id: id
139
+ }
140
+ response = rpc(action: :send, params: p)
141
+ return Banano::Error.new(response[:error]) if response.key?(:error)
142
+
143
+ response[:block]
144
+ end
145
+
146
+ # Receives a pending payment for this account.
147
+ #
148
+ # When called with no +block+ argument, the latest pending payment
149
+ # for the account will be received.
150
+ #
151
+ # Returns a <i>receive</i> block id
152
+ # if a receive was successful, or +false+ if there were no pending
153
+ # payments to receive.
154
+ #
155
+ # You can receive a specific pending block if you know it by
156
+ # passing the block in as an argument.
157
+ #
158
+ # ==== Examples:
159
+ #
160
+ # account.receive # => "9AE2311..."
161
+ # account.receive("718CC21...") # => "9AE2311..."
162
+ #
163
+ # @param block [String] optional block id of pending payment. If
164
+ # not provided, the latest pending payment will be received
165
+ #
166
+ # @return [String] the receive block id
167
+ # @return [false] if there was no block to receive
168
+ def receive(block = nil)
169
+ if block.nil?
170
+ _receive_without_block
171
+ else
172
+ _receive_with_block(block)
173
+ end
174
+ end
175
+
176
+ # Sets the representative for the account.
177
+ #
178
+ # A representative is an account that will vote on your account's
179
+ # behalf on the nano network if your account is offline and there is
180
+ # a fork of the network that requires voting on.
181
+ #
182
+ # Returns the <em>change block</em> that was
183
+ # broadcast to the nano network. The block contains the information
184
+ # about the representative change for your account.
185
+ #
186
+ # @param [String] representative the id of the representative account
187
+ # to set as this account's representative
188
+ # @return [String] id of the <i>change</i> block created
189
+ # @raise [ArgumentError] if the representative account does not exist
190
+ def change_representative(representative)
191
+ unless Banano::Account.new(node: @node, address: representative).exists?
192
+ raise ArgumentError, "Representative account does not exist: #{representative}"
193
+ end
194
+
195
+ rpc(action: :account_representative_set,
196
+ params: {representative: representative})[:block]
197
+ end
198
+
199
+ private
200
+
201
+ def _receive_without_block
202
+ # Discover the first pending block
203
+ pending_blocks = rpc(action: :pending, params: {account: @account, count: 1})
204
+
205
+ return false if pending_blocks[:blocks].empty?
206
+
207
+ # Then call receive_with_block as normal
208
+ _receive_with_block(pending_blocks[:blocks][0])
209
+ end
210
+
211
+ # Returns block if successful, otherwise false
212
+ def _receive_with_block(block)
213
+ response = rpc(action: :receive, params: {block: block})[:block]
214
+ response.nil? ? false : response
215
+ end
216
+
217
+ def rpc(action:, params: {})
218
+ p = {}
219
+ return p unless @wallet
220
+
221
+ p[:wallet] = @wallet
222
+ p[:account] = @account unless @account.nil?
223
+
224
+ @node.rpc(action: action, params: p.merge(params))
225
+ end
226
+ end
227
+ end
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: banano
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Stoyan Zhekov
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-06-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday_middleware
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '12.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '12.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.9'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.9'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.85'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.85'
97
+ - !ruby/object:Gem::Dependency
98
+ name: webmock
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.8'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.8'
111
+ description: Library for working with Banano currency. Implements parts of the RPC
112
+ protocol for access to Banano node. Convertion between raw and banano units etc.
113
+ email:
114
+ - zh@zhware.net
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - CHANGELOG.md
120
+ - LICENSE.txt
121
+ - README.md
122
+ - bin/console
123
+ - bin/setup
124
+ - lib/banano.rb
125
+ - lib/banano/account.rb
126
+ - lib/banano/client.rb
127
+ - lib/banano/error.rb
128
+ - lib/banano/key.rb
129
+ - lib/banano/node.rb
130
+ - lib/banano/unit.rb
131
+ - lib/banano/util.rb
132
+ - lib/banano/version.rb
133
+ - lib/banano/wallet.rb
134
+ - lib/banano/wallet_account.rb
135
+ homepage: http://github.com/zh/rbanano
136
+ licenses:
137
+ - MIT
138
+ metadata:
139
+ homepage_uri: http://github.com/zh/rbanano
140
+ source_code_uri: http://github.com/zh/rbanano
141
+ changelog_uri: http://github.com/zh/rbanano/CHANGELOG.md
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: 2.3.0
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ requirements: []
157
+ rubygems_version: 3.0.3
158
+ signing_key:
159
+ specification_version: 4
160
+ summary: Library for working with Banano currency.
161
+ test_files: []