dpay-ruby 0.1.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.
@@ -0,0 +1,216 @@
1
+ module DPay
2
+ class BaseError < StandardError
3
+ def initialize(error = nil, cause = nil)
4
+ @error = error
5
+ @cause = cause
6
+ end
7
+
8
+ def to_s
9
+ detail = {}
10
+ detail[:error] = @error if !!@error
11
+ detail[:cause] = @cause if !!@cause
12
+
13
+ JSON[detail] rescue detai.to_s
14
+ end
15
+
16
+ def self.build_error(error, context)
17
+ if error.message == 'Unable to acquire database lock'
18
+ raise DPay::RemoteDatabaseLockError, error.message, build_backtrace(error)
19
+ end
20
+
21
+ if error.message.include? 'Internal Error'
22
+ raise DPay::RemoteNodeError.new, error.message, build_backtrace(error)
23
+ end
24
+
25
+ if error.message.include? 'Server error'
26
+ raise DPay::RemoteNodeError.new, error.message, build_backtrace(error)
27
+ end
28
+
29
+ if error.message.include? 'plugin not enabled'
30
+ raise DPay::PluginNotEnabledError, error.message, build_backtrace(error)
31
+ end
32
+
33
+ if error.message.include? 'argument'
34
+ raise DPay::ArgumentError, "#{context}: #{error.message}", build_backtrace(error)
35
+ end
36
+
37
+ if error.message.include? 'Invalid params'
38
+ raise DPay::ArgumentError, "#{context}: #{error.message}", build_backtrace(error)
39
+ end
40
+
41
+ if error.message.start_with? 'Bad Cast:'
42
+ raise DPay::ArgumentError, "#{context}: #{error.message}", build_backtrace(error)
43
+ end
44
+
45
+ if error.message.include? 'prefix_len'
46
+ raise DPay::ArgumentError, "#{context}: #{error.message}", build_backtrace(error)
47
+ end
48
+
49
+ if error.message.include? 'Parse Error'
50
+ raise DPay::ArgumentError, "#{context}: #{error.message}", build_backtrace(error)
51
+ end
52
+
53
+ if error.message.include? 'unknown key'
54
+ raise DPay::ArgumentError, "#{context}: #{error.message} (or content has been deleted)", build_backtrace(error)
55
+ end
56
+
57
+ if error.message.include? 'Comment is not in account\'s comments'
58
+ raise DPay::ArgumentError, "#{context}: #{error.message}", build_backtrace(error)
59
+ end
60
+
61
+ if error.message.include? 'Could not find comment'
62
+ raise DPay::ArgumentError, "#{context}: #{error.message}", build_backtrace(error)
63
+ end
64
+
65
+ if error.message.include? 'unable to convert ISO-formatted string to fc::time_point_sec'
66
+ raise DPay::ArgumentError, "#{context}: #{error.message}", build_backtrace(error)
67
+ end
68
+
69
+ if error.message.include? 'Input data have to treated as object.'
70
+ raise DPay::ArgumentError, "#{context}: #{error.message}", build_backtrace(error)
71
+ end
72
+
73
+ if error.message.include? 'base.amount > share_type(0)'
74
+ raise DPay::ArgumentError, "#{context}: #{error.message}", build_backtrace(error)
75
+ end
76
+
77
+ if error.message.include? 'blk->transactions.size() > itr->trx_in_block'
78
+ raise DPay::VirtualOperationsNotAllowedError, "#{context}: #{error.message}", build_backtrace(error)
79
+ end
80
+
81
+ if error.message.include? 'A transaction must have at least one operation'
82
+ raise DPay::EmptyTransactionError, "#{context}: #{error.message}", build_backtrace(error)
83
+ end
84
+
85
+ if error.message.include? 'transaction expiration exception'
86
+ raise DPay::TransactionExpiredError, "#{context}: #{error.message}", build_backtrace(error)
87
+ end
88
+
89
+ if error.message.include? 'Duplicate transaction check failed'
90
+ raise DPay::DuplicateTransactionError, "#{context}: #{error.message}", build_backtrace(error)
91
+ end
92
+
93
+ if error.message.include? 'signature is not canonical'
94
+ raise DPay::NonCanonicalSignatureError, "#{context}: #{error.message}", build_backtrace(error)
95
+ end
96
+
97
+ if error.message.include? 'attempting to push a block that is too old'
98
+ raise DPay::BlockTooOldError, "#{context}: #{error.message}", build_backtrace(error)
99
+ end
100
+
101
+ if error.message.include? 'irrelevant signature'
102
+ raise DPay::IrrelevantSignatureError, "#{context}: #{error.message}", build_backtrace(error)
103
+ end
104
+
105
+ if error.message.include? 'missing required posting authority'
106
+ raise DPay::MissingPostingAuthorityError, "#{context}: #{error.message}", build_backtrace(error)
107
+ end
108
+
109
+ if error.message.include? 'missing required active authority'
110
+ raise DPay::MissingActiveAuthorityError, "#{context}: #{error.message}", build_backtrace(error)
111
+ end
112
+
113
+ if error.message.include? 'missing required owner authority'
114
+ raise DPay::MissingOwnerAuthorityError, "#{context}: #{error.message}", build_backtrace(error)
115
+ end
116
+
117
+ if error.message.include? 'missing required other authority'
118
+ raise DPay::MissingOtherAuthorityError, "#{context}: #{error.message}", build_backtrace(error)
119
+ end
120
+
121
+ if error.message.include? 'Upstream response error'
122
+ raise DPay::UpstreamResponseError, "#{context}: #{error.message}", build_backtrace(error)
123
+ end
124
+
125
+ if error.message.include? 'Bad or missing upstream response'
126
+ raise DPay::BadOrMissingUpstreamResponseError, "#{context}: #{error.message}", build_backtrace(error)
127
+ end
128
+
129
+ if error.message.include? 'operator has disabled operation indexing by transaction_id'
130
+ raise DPay::TransactionIndexDisabledError, "#{context}: #{error.message}", build_backtrace(error)
131
+ end
132
+
133
+ if error.message.include? 'is_valid_account_name'
134
+ raise DPay::InvalidAccountError, "#{context}: #{error.message}", build_backtrace(error)
135
+ end
136
+
137
+ if error.message.include?('Method') && error.message.include?(' does not exist.')
138
+ raise DPay::UnknownMethodError, "#{context}: #{error.message}", build_backtrace(error)
139
+ end
140
+
141
+ if error.message.include? 'Invalid operation name'
142
+ raise DPay::UnknownOperationError, "#{context}: #{error.message}", build_backtrace(error)
143
+ end
144
+
145
+ if error.message =~ /Invalid object name: .+_operation/
146
+ raise DPay::UnknownOperationError, "#{context}: #{error.message}", build_backtrace(error)
147
+ end
148
+
149
+ if error.message.include? 'Author not found'
150
+ raise DPay::AuthorNotFoundError, "#{context}: #{error.message}", build_backtrace(error)
151
+ end
152
+
153
+ if error.message.include? ' != fc::time_point_sec::maximum()'
154
+ raise DPay::ReachedMaximumTimeError, "#{context}: #{error.message}", build_backtrace(error)
155
+ end
156
+
157
+ if error.message.include? 'Cannot transfer a negative amount (aka: stealing)'
158
+ raise DPay::TheftError, "#{context}: #{error.message}", build_backtrace(error)
159
+ end
160
+
161
+ if error.message.include? 'Must transfer a nonzero amount'
162
+ raise DPay::NonZeroRequiredError, "#{context}: #{error.message}", build_backtrace(error)
163
+ end
164
+
165
+ if error.message.include? 'is_asset_type'
166
+ raise DPay::UnexpectedAssetError, "#{context}: #{error.message}", build_backtrace(error)
167
+ end
168
+
169
+ puts JSON.pretty_generate(error) if ENV['DEBUG']
170
+ raise UnknownError, "#{context}: #{error.message}", build_backtrace(error)
171
+ end
172
+ private
173
+ def self.build_backtrace(error)
174
+ backtrace = Thread.current.backtrace.reject{ |line| line =~ /base_error/ }
175
+ JSON.pretty_generate(error) + "\n" + backtrace.join("\n")
176
+ end
177
+ end
178
+
179
+ class UnsupportedChainError < BaseError; end
180
+ class ArgumentError < BaseError; end
181
+ class TypeError < BaseError; end
182
+ class EmptyTransactionError < ArgumentError; end
183
+ class InvalidAccountError < ArgumentError; end
184
+ class AuthorNotFoundError < ArgumentError; end
185
+ class ReachedMaximumTimeError < ArgumentError; end
186
+ class VirtualOperationsNotAllowedError < ArgumentError; end
187
+ class TheftError < ArgumentError; end
188
+ class NonZeroRequiredError < ArgumentError; end
189
+ class UnexpectedAssetError < ArgumentError; end
190
+ class TransactionExpiredError < BaseError; end
191
+ class DuplicateTransactionError < TransactionExpiredError; end
192
+ class NonCanonicalSignatureError < TransactionExpiredError; end
193
+ class BlockTooOldError < BaseError; end
194
+ class IrrelevantSignatureError < BaseError; end
195
+ class MissingAuthorityError < BaseError; end
196
+ class MissingPostingAuthorityError < MissingAuthorityError; end
197
+ class MissingActiveAuthorityError < MissingAuthorityError; end
198
+ class MissingOwnerAuthorityError < MissingAuthorityError; end
199
+ class MissingOtherAuthorityError < MissingAuthorityError; end
200
+ class IncorrectRequestIdError < BaseError; end
201
+ class IncorrectResponseIdError < BaseError; end
202
+ class RemoteNodeError < BaseError; end
203
+ class UpstreamResponseError < RemoteNodeError; end
204
+ class RemoteDatabaseLockError < UpstreamResponseError; end
205
+ class PluginNotEnabledError < UpstreamResponseError; end
206
+ class BadOrMissingUpstreamResponseError < UpstreamResponseError; end
207
+ class TransactionIndexDisabledError < BaseError; end
208
+ class NotAppBaseError < BaseError; end
209
+ class UnknownApiError < BaseError; end
210
+ class UnknownMethodError < BaseError; end
211
+ class UnknownOperationError < BaseError; end
212
+ class JsonRpcBatchMaximumSizeExceededError < BaseError; end
213
+ class TooManyTimeoutsError < BaseError; end
214
+ class TooManyRetriesError < BaseError; end
215
+ class UnknownError < BaseError; end
216
+ end
@@ -0,0 +1,78 @@
1
+ module DPay
2
+ # {BlockApi} is used to query values related to the block plugin. It can also
3
+ # be used to access a range of multiple blocks by using
4
+ # {http://www.jsonrpc.org/specification#batch JSON-RPC 2.0 batch} requests.
5
+ #
6
+ # Also see: {https://developers.dpays.io/apidefinitions/block-api Block API Definitions}
7
+ class BlockApi < Api
8
+ MAX_RANGE_SIZE = 50
9
+
10
+ def initialize(options = {})
11
+ self.class.api_name = :block_api
12
+ super
13
+ end
14
+
15
+ # Uses a batched requst on a range of block headers.
16
+ #
17
+ # @param options [Hash] The attributes to get a block range with.
18
+ # @option options [Range] :block_range starting on one block number and ending on an higher block number.
19
+ def get_block_headers(options = {block_range: (0..0)}, &block)
20
+ get_block_objects(options.merge(object: :block_header), block)
21
+ end
22
+
23
+ # Uses a batched requst on a range of blocks.
24
+ #
25
+ # @param options [Hash] The attributes to get a block range with.
26
+ # @option options [Range] :block_range starting on one block number and ending on an higher block number.
27
+ def get_blocks(options = {block_range: (0..0)}, &block)
28
+ get_block_objects(options.merge(object: :block), block)
29
+ end
30
+ private
31
+ def get_block_objects(options = {block_range: (0..0)}, block = nil)
32
+ object = options[:object]
33
+ object_method = "get_#{object}".to_sym
34
+ block_range = options[:block_range] || (0..0)
35
+
36
+ if (start = block_range.first) < 1
37
+ raise DPay::ArgumentError, "Invalid starting block: #{start}"
38
+ end
39
+
40
+ chunks = if block_range.size > MAX_RANGE_SIZE
41
+ block_range.each_slice(MAX_RANGE_SIZE)
42
+ else
43
+ [block_range]
44
+ end
45
+
46
+ for sub_range in chunks do
47
+ request_object = []
48
+
49
+ for i in sub_range do
50
+ @rpc_client.put(self.class.api_name, object_method, block_num: i, request_object: request_object)
51
+ end
52
+
53
+ if !!block
54
+ index = 0
55
+ @rpc_client.rpc_batch_execute(request_object: request_object) do |result, error, id|
56
+ block_num = sub_range.to_a[index]
57
+ index = index + 1
58
+
59
+ case object
60
+ when :block_header
61
+ block.call(result.nil? ? nil : result[:header], block_num)
62
+ else
63
+ block.call(result.nil? ? nil : result[object], block_num)
64
+ end
65
+ end
66
+ else
67
+ blocks = []
68
+
69
+ @rpc_client.rpc_batch_execute(request_object: request_object) do |result, error, id|
70
+ blocks << result
71
+ end
72
+ end
73
+ end
74
+
75
+ blocks
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,1171 @@
1
+ require 'bitcoin'
2
+ require 'digest'
3
+ require 'time'
4
+
5
+ module DPay
6
+
7
+ # These class methods make it simple to do things like broacast a {Broadcast#vote}
8
+ # or {Broadcast#comment} operation. They accept all of the fields expected by
9
+ # the blockchain plus the following additional options:
10
+ #
11
+ # * wif
12
+ # * url (optional)
13
+ # * database_api (optional)
14
+ # * block_api (optional)
15
+ # * network_broadcast_api (optional)
16
+ # * pretend (optional)
17
+ #
18
+ # These options are not sent in the broadcast. The `wif` authorities can be
19
+ # posting, active, and owner.
20
+ #
21
+ # Setting `url` will allow you to specify a different node instead of taking
22
+ # the default: ({ChainConfig::NETWORKS_DPAY_DEFAULT_NODE}).
23
+ #
24
+ # Setting `database_api`, `block_api`, and `network_broadcast_api` is
25
+ # optional, doing so will allow you to override the default node and/or the
26
+ # RPC Client.
27
+ #
28
+ # When passing the `pretend` field, if it is set to {::True}, nothing is
29
+ # broadcasted, but the `wif` is checked for the proper authority.
30
+ #
31
+ # For details on what to pass to these methods, check out the {https://developers.dpays.io/apidefinitions/broadcast-ops DPay Developer Portal Broadcast Operations} page.
32
+ class Broadcast
33
+ extend Retriable
34
+
35
+ DEFAULT_MAX_ACCEPTED_PAYOUT = Type::Amount.new(amount: '1000000000', precision: 3, nai: '@@000000013')
36
+
37
+ # This operation is used to cast a vote on a post/comment.
38
+ #
39
+ # options = {
40
+ # wif: wif,
41
+ # params: {
42
+ # voter: voter,
43
+ # author: author,
44
+ # permlink: permlink,
45
+ # weight: weight
46
+ # }
47
+ # }
48
+ #
49
+ # DPay::Broadcast.vote(options) do |result|
50
+ # puts result
51
+ # end
52
+ #
53
+ # @param options [Hash] options
54
+ # @option options [String] :wif Posting wif
55
+ # @option options [Hash] :params
56
+ # * :voter (String)
57
+ # * :author (String)
58
+ # * :permlink (String)
59
+ # * :weight (Number)
60
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
61
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_vote
62
+ def self.vote(options, &block)
63
+ required_fields = %i(voter author permlink weight)
64
+ params = options[:params]
65
+ check_required_fields(params, *required_fields)
66
+
67
+ ops = [[:vote, params]]
68
+
69
+ process(options.merge(ops: ops), &block)
70
+ end
71
+
72
+ # Creates a post/comment. This method simplifies content creation by
73
+ # combining `comment` and `comment_options` into one transaction.
74
+ #
75
+ # options = {
76
+ # wif: wif,
77
+ # params: {
78
+ # author: author,
79
+ # title: 'This is my fancy post title.',
80
+ # body: 'This is my fancy post body.',
81
+ # metadata: {
82
+ # tags: %w(these are my fancy tags)
83
+ # }
84
+ # }
85
+ # }
86
+ #
87
+ # DPay::Broadcast.comment(options)
88
+ #
89
+ # options = {
90
+ # wif: wif,
91
+ # params: {
92
+ # author: author,
93
+ # title: 'This is my fancy post title.',
94
+ # body: 'This is my fancy post body.',
95
+ # metadata: {
96
+ # tags: %w(these are my fancy tags)
97
+ # },
98
+ # beneficiaries: [
99
+ # {account: "david", weight: 500},
100
+ # {account: "erin", weight: 500},
101
+ # {account: "faythe", weight: 1000},
102
+ # {account: "frank", weight: 500}
103
+ # ]
104
+ # }
105
+ # }
106
+ #
107
+ # DPay::Broadcast.comment(options)
108
+ #
109
+ # In addition to the above denormalized `comment_options` fields, the author
110
+ # can also vote for the content in the same transaction by setting `author_vote_weight`:
111
+ #
112
+ # options = {
113
+ # wif: wif,
114
+ # params: {
115
+ # author: author,
116
+ # title: 'This is my fancy post title.',
117
+ # body: 'This is my fancy post body.',
118
+ # metadata: {
119
+ # tags: %w(these are my fancy tags)
120
+ # },
121
+ # author_vote_weight: 10000
122
+ # }
123
+ # }
124
+ #
125
+ # DPay::Broadcast.comment(options)
126
+ #
127
+ # @param options [Hash] options
128
+ # @option options [String] :wif Posting wif
129
+ # @option options [Hash] :params
130
+ # * :author (String)
131
+ # * :title (String) Title of the content.
132
+ # * :body (String) Body of the content.
133
+ # * :metadata (Hash) Metadata of the content, becomes `json_metadata`.
134
+ # * :json_metadata (String) String version of `metadata` (use one or the other).
135
+ # * :permlink (String) (automatic) Permlink of the content, defaults to formatted title.
136
+ # * :parent_permlink (String) (automatic) Parent permlink of the content, defaults to first tag.
137
+ # * :parent_author (String) (optional) Parent author of the content (only used if reply).
138
+ # * :max_accepted_payout (String) (1000000.000 BBD) Maximum accepted payout, set to '0.000 BBD' to deline payout
139
+ # * :percent_dpay_dollars (Numeric) (5000) Percent BEX Dollars is used to set 50/50 or 100% BEX Power
140
+ # * :allow_votes (Numeric) (true) Allow votes for this content.
141
+ # * :allow_curation_rewards (Numeric) (true) Allow curation rewards for this content.
142
+ # * :beneficiaries (Array<Hash>) Sets the beneficiaries of this content.
143
+ # * :author_vote_weight (Number) (optional) Cast a vote by the author in the same transaction.
144
+ # * :pretend (Boolean) Just validate, do not broadcast.
145
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_comment
146
+ def self.comment(options, &block)
147
+ required_fields = %i(author body permlink parent_permlink)
148
+ params = options[:params]
149
+
150
+ if !!params[:metadata] && !!params[:json_metadata]
151
+ raise DPay::ArgumentError, 'Assign either metadata or json_metadata, not both.'
152
+ end
153
+
154
+ metadata = params[:metadata] || {}
155
+ metadata ||= (JSON[params[:json_metadata]] || nil) || {}
156
+ metadata['app'] ||= DPay::AGENT_ID
157
+ tags = metadata['tags'] || []
158
+ params[:parent_permlink] ||= tags.first
159
+
160
+ if !!params[:title]
161
+ params[:permlink] ||= params[:title].downcase.gsub(/[^a-z0-9\-]+/, '-')
162
+ end
163
+
164
+ check_required_fields(params, *required_fields)
165
+
166
+ ops = [[:comment, {
167
+ parent_author: params[:parent_author] || '',
168
+ parent_permlink: params[:parent_permlink],
169
+ author: params[:author],
170
+ permlink: params[:permlink],
171
+ title: params[:title] || '',
172
+ body: params[:body],
173
+ json_metadata: metadata.to_json
174
+ }]]
175
+
176
+ max_accepted_payout = if params.keys.include? :max_accepted_payout
177
+ normalize_amount(options.merge amount: params[:max_accepted_payout])
178
+ else
179
+ normalize_amount(options.merge amount: DEFAULT_MAX_ACCEPTED_PAYOUT)
180
+ end
181
+
182
+ allow_votes = if params.keys.include? :allow_votes
183
+ !!params[:allow_votes]
184
+ else
185
+ true
186
+ end
187
+
188
+ allow_curation_rewards = if params.keys.include? :allow_curation_rewards
189
+ !!params[:allow_curation_rewards]
190
+ else
191
+ true
192
+ end
193
+
194
+ comment_options = {
195
+ author: params[:author],
196
+ permlink: params[:permlink],
197
+ max_accepted_payout: max_accepted_payout,
198
+ percent_dpay_dollars: params[:percent_dpay_dollars] || 10000,
199
+ allow_votes: allow_votes,
200
+ allow_curation_rewards: allow_curation_rewards,
201
+ extensions: []
202
+ }
203
+
204
+ if !!params[:beneficiaries]
205
+ comment_options[:extensions] << [0, {beneficiaries: params[:beneficiaries]}]
206
+ end
207
+
208
+ ops << [:comment_options, comment_options]
209
+
210
+ if !!params[:author_vote_weight]
211
+ author_vote = {
212
+ voter: params[:author],
213
+ author: params[:author],
214
+ permlink: params[:permlink],
215
+ weight: params[:author_vote_weight]
216
+ }
217
+
218
+ ops << [:vote, author_vote]
219
+ end
220
+
221
+ process(options.merge(ops: ops), &block)
222
+ end
223
+
224
+ # Deletes a post/comment.
225
+ #
226
+ # DPay::Broadcast.delete_comment(wif: wif, params: {author: author, permlink: permlink}) do |result|
227
+ # puts result
228
+ # end
229
+ #
230
+ # @param options [Hash] options
231
+ # @option options [String] :wif Posting wif
232
+ # @option options [Hash] :params
233
+ # * :author (String)
234
+ # * :permlink (String)
235
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
236
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_delete_comment
237
+ def self.delete_comment(options, &block)
238
+ required_fields = %i(author permlink)
239
+ params = options[:params]
240
+ check_required_fields(params, *required_fields)
241
+
242
+ ops = [[:delete_comment, params]]
243
+
244
+ process(options.merge(ops: ops), &block)
245
+ end
246
+
247
+ # Transfers asset from one account to another.
248
+ #
249
+ # options = {
250
+ # wif: wif,
251
+ # params: {
252
+ # from: from,
253
+ # to: to,
254
+ # amount: amount,
255
+ # memo: memo
256
+ # }
257
+ # }
258
+ #
259
+ # DPay::Broadcast.transfer(options) do |result|
260
+ # puts result
261
+ # end
262
+ #
263
+ # @param options [Hash] options
264
+ # @option options [String] :wif Active wif
265
+ # @option options [Hash] :params
266
+ # * :from (String)
267
+ # * :to (String)
268
+ # * :amount (String)
269
+ # * :memo (String)
270
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
271
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_transfer
272
+ def self.transfer(options, &block)
273
+ required_fields = %i(from to amount memo)
274
+ params = options[:params]
275
+ check_required_fields(params, *required_fields)
276
+
277
+ params[:amount] = normalize_amount(options.merge amount: params[:amount])
278
+
279
+ ops = [[:transfer, params]]
280
+
281
+ process(options.merge(ops: ops), &block)
282
+ end
283
+
284
+ # This operation converts BEX into VFS (Vesting Fund Shares) at the
285
+ # current exchange rate.
286
+ #
287
+ # options = {
288
+ # wif: wif,
289
+ # params: {
290
+ # from: from,
291
+ # to: to,
292
+ # amount: amount,
293
+ # }
294
+ # }
295
+ #
296
+ # DPay::Broadcast.transfer_to_vesting(options) do |result|
297
+ # puts result
298
+ # end
299
+ #
300
+ # @param options [Hash] options
301
+ # @option options [String] :wif Active wif
302
+ # @option options [Hash] :params
303
+ # * :from (String)
304
+ # * :to (String)
305
+ # * :amount (String)
306
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
307
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_transfer_to_vesting
308
+ def self.transfer_to_vesting(options, &block)
309
+ required_fields = %i(from to amount)
310
+ params = options[:params]
311
+ check_required_fields(params, *required_fields)
312
+
313
+ params[:amount] = normalize_amount(options.merge amount: params[:amount])
314
+
315
+ ops = [[:transfer_to_vesting, params]]
316
+
317
+ process(options.merge(ops: ops), &block)
318
+ end
319
+
320
+ # At any given point in time an account can be withdrawing from their
321
+ # vesting shares.
322
+ #
323
+ # DPay::Broadcast.withdraw_vesting(wif: wif, params: {account: account, vesting_shares: vesting_shares}) do |result|
324
+ # puts result
325
+ # end
326
+ #
327
+ # @param options [Hash] options
328
+ # @option options [String] :wif Active wif
329
+ # @option options [Hash] :params
330
+ # * :account (String)
331
+ # * :vesting_shares (String)
332
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
333
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_withdraw_vesting
334
+ def self.withdraw_vesting(options, &block)
335
+ required_fields = %i(account vesting_shares)
336
+ params = options[:params]
337
+ check_required_fields(params, *required_fields)
338
+
339
+ params[:vesting_shares] = normalize_amount(options.merge amount: params[:vesting_shares])
340
+
341
+ ops = [[:withdraw_vesting, params]]
342
+
343
+ process(options.merge(ops: ops), &block)
344
+ end
345
+
346
+ # This operation creates a limit order and matches it against existing open
347
+ # orders.
348
+ #
349
+ # @param options [Hash] options
350
+ # @option options [String] :wif Active wif
351
+ # @option options [Hash] :params
352
+ # * :owner (String)
353
+ # * :orderid (String)
354
+ # * :amount_to_sell (String)
355
+ # * :min_to_receive (String)
356
+ # * :fill_or_kill (Boolean)
357
+ # * :expiration (String)
358
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
359
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_limit_order_create
360
+ def self.limit_order_create(options, &block)
361
+ required_fields = %i(owner orderid amount_to_sell min_to_receive
362
+ fill_or_kill)
363
+ params = options[:params]
364
+ check_required_fields(params, *required_fields)
365
+
366
+ params[:amount_to_sell] = normalize_amount(options.merge amount: params[:amount_to_sell])
367
+ params[:min_to_receive] = normalize_amount(options.merge amount: params[:min_to_receive])
368
+
369
+ if !!params[:expiration]
370
+ params[:expiration] = Time.parse(params[:expiration].to_s)
371
+ params[:expiration] = params[:expiration].strftime('%Y-%m-%dT%H:%M:%S')
372
+ end
373
+
374
+ ops = [[:limit_order_create, params]]
375
+
376
+ process(options.merge(ops: ops), &block)
377
+ end
378
+
379
+ # Cancels an order and returns the balance to owner.
380
+ #
381
+ # @param options [Hash] options
382
+ # @option options [String] :wif Active wif
383
+ # @option options [Hash] :params
384
+ # * :owner (String)
385
+ # * :orderid (String)
386
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
387
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_limit_order_cancel
388
+ def self.limit_order_cancel(options, &block)
389
+ required_fields = %i(owner orderid)
390
+ params = options[:params]
391
+ check_required_fields(params, *required_fields)
392
+
393
+ ops = [[:limit_order_cancel, params]]
394
+
395
+ process(options.merge(ops: ops), &block)
396
+ end
397
+
398
+ # Feeds can only be published by the top N witnesses which are included in
399
+ # every round and are used to define the exchange rate between dpay and the
400
+ # dollar.
401
+ #
402
+ # @param options [Hash] options
403
+ # @option options [String] :wif Active wif
404
+ # @option options [Hash] :params
405
+ # * :publisher (String)
406
+ # * :exchange_rate (Hash)
407
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
408
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_feed_publish
409
+ def self.feed_publish(options, &block)
410
+ required_fields = %i(publisher exchange_rate)
411
+ params = options[:params]
412
+ check_required_fields(params, *required_fields)
413
+
414
+ exchange_rate = params[:exchange_rate] rescue nil || {}
415
+ base = exchange_rate[:base]
416
+ quote = exchange_rate[:quote]
417
+ params[:exchange_rate][:base] = normalize_amount(options.merge amount: base)
418
+ params[:exchange_rate][:quote] = normalize_amount(options.merge amount: quote)
419
+
420
+ ops = [[:feed_publish, params]]
421
+
422
+ process(options.merge(ops: ops), &block)
423
+ end
424
+
425
+ # This operation instructs the blockchain to start a conversion between
426
+ # BEX and BBD, the funds are deposited after 3.5 days.
427
+ #
428
+ # @param options [Hash] options
429
+ # @option options [String] :wif Active wif
430
+ # @option options [Hash] :params
431
+ # * :owner (String)
432
+ # * :requestid (String)
433
+ # * :amount (String)
434
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
435
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_convert
436
+ def self.convert(options, &block)
437
+ required_fields = %i(owner requestid amount)
438
+ params = options[:params]
439
+ check_required_fields(params, *required_fields)
440
+
441
+ params[:amount] = normalize_amount(options.merge amount: params[:amount])
442
+
443
+ ops = [[:convert, params]]
444
+
445
+ process(options.merge(ops: ops), &block)
446
+ end
447
+
448
+ # Create an account.
449
+ # options = {
450
+ # wif: wif,
451
+ # params: {
452
+ # fee: '1.000 BEX',
453
+ # creator: creator_account_name,
454
+ # new_account_name: new_account_name,
455
+ # owner: {
456
+ # weight_threshold: 1,
457
+ # account_auths: [],
458
+ # key_auths: [[owner_public_key, 1]],
459
+ # },
460
+ # active: {
461
+ # weight_threshold: 1,
462
+ # account_auths: [],
463
+ # key_auths: [[active_public_key, 1]],
464
+ # },
465
+ # posting: {
466
+ # weight_threshold: 1,
467
+ # account_auths: [],
468
+ # key_auths: [[posting_public_key, 1]],
469
+ # },
470
+ # memo_key: memo_public_key,
471
+ # json_metadata: '{}'
472
+ # }
473
+ # }
474
+ #
475
+ # DPay::Broadcast.account_create(options)
476
+ #
477
+ # @param options [Hash] options
478
+ # @option options [String] :wif Active wif
479
+ # @option options [Hash] :params
480
+ # * :fee (String)
481
+ # * :creator (String)
482
+ # * :new_account_name (String)
483
+ # * :owner (Hash)
484
+ # * :active (Hash)
485
+ # * :posting (Hash)
486
+ # * :memo_key (String)
487
+ # * :metadata (Hash) Metadata of the account, becomes `json_metadata`.
488
+ # * :json_metadata (String) String version of `metadata` (use one or the other).
489
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
490
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_account_create
491
+ def self.account_create(options, &block)
492
+ required_fields = %i(fee creator new_account_name owner active posting memo_key json_metadata)
493
+ params = options[:params]
494
+
495
+ if !!params[:metadata] && !!params[:json_metadata]
496
+ raise DPay::ArgumentError, 'Assign either metadata or json_metadata, not both.'
497
+ end
498
+
499
+ metadata = params.delete(:metadata) || {}
500
+ metadata ||= (JSON[params[:json_metadata]] || nil) || {}
501
+ params[:json_metadata] = metadata.to_json
502
+
503
+ check_required_fields(params, *required_fields)
504
+
505
+ params[:fee] = normalize_amount(options.merge amount: params[:fee])
506
+
507
+ ops = [[:account_create, params]]
508
+
509
+ process(options.merge(ops: ops), &block)
510
+ end
511
+
512
+ # Update an account.
513
+ # options = {
514
+ # wif: wif,
515
+ # params: {
516
+ # account: new_account_name,
517
+ # owner: {
518
+ # weight_threshold: 1,
519
+ # account_auths: [],
520
+ # key_auths: [[owner_public_key, 1]],
521
+ # },
522
+ # active: {
523
+ # weight_threshold: 1,
524
+ # account_auths: [],
525
+ # key_auths: [[active_public_key, 1]],
526
+ # },
527
+ # posting: {
528
+ # weight_threshold: 1,
529
+ # account_auths: [],
530
+ # key_auths: [[posting_public_key, 1]],
531
+ # },
532
+ # memo_key: memo_public_key,
533
+ # json_metadata: '{}'
534
+ # }
535
+ # }
536
+ #
537
+ # DPay::Broadcast.account_update(options)
538
+ #
539
+ # @param options [Hash] options
540
+ # @option options [String] :wif Active wif
541
+ # @option options [Hash] :params
542
+ # * :account (String)
543
+ # * :owner (Hash) (optional)
544
+ # * :active (Hash) (optional)
545
+ # * :posting (Hash) (optional)
546
+ # * :memo_key (String) (optional)
547
+ # * :metadata (Hash) Metadata of the account, becomes `json_metadata`.
548
+ # * :json_metadata (String) String version of `metadata` (use one or the other).
549
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
550
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_account_update
551
+ def self.account_update(options, &block)
552
+ required_fields = %i(account)
553
+ params = options[:params]
554
+
555
+ if !!params[:metadata] && !!params[:json_metadata]
556
+ raise DPay::ArgumentError, 'Assign either metadata or json_metadata, not both.'
557
+ end
558
+
559
+ metadata = params.delete(:metadata) || {}
560
+ metadata ||= (JSON[params[:json_metadata]] || nil) || {}
561
+ params[:json_metadata] = metadata.to_json
562
+
563
+ check_required_fields(params, *required_fields)
564
+
565
+ ops = [[:account_update, params]]
566
+
567
+ process(options.merge(ops: ops), &block)
568
+ end
569
+
570
+ # Users who wish to become a witness must pay a fee acceptable to the
571
+ # current witnesses to apply for the position and allow voting to begin.
572
+ #
573
+ # options = {
574
+ # wif: wif,
575
+ # params: {
576
+ # owner: witness_account_name,
577
+ # url: '',
578
+ # block_signing_key: 'STM8ZSyzjPm48GmUuMSRufkVYkwYbZzbxeMysAVp7KFQwbTf98TcG',
579
+ # props: {
580
+ # account_creation_fee: '0.000 BEX',
581
+ # maximum_block_size: 131072,
582
+ # bbd_interest_rate:1000
583
+ # },
584
+ # fee: '0.000 BEX',
585
+ # }
586
+ # }
587
+ #
588
+ # DPay::Broadcast.witness_update(options)
589
+ #
590
+ # @param options [Hash] options
591
+ # @option options [String] :wif Active wif
592
+ # @option options [Hash] :params
593
+ # * :owner (String)
594
+ # * :url (String) (optional)
595
+ # * :block_signing_key (String)
596
+ # * :props (String)
597
+ # * :fee (String)
598
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
599
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_witness_update
600
+ def self.witness_update(options, &block)
601
+ required_fields = %i(owner block_signing_key props fee)
602
+ params = options[:params]
603
+ check_required_fields(params, *required_fields)
604
+
605
+ account_creation_fee = params[:props][:account_creation_fee] rescue nil
606
+ params[:props][:account_creation_fee] = normalize_amount(options.merge amount: account_creation_fee)
607
+ params[:fee] = normalize_amount(options.merge amount: params[:fee])
608
+
609
+ ops = [[:witness_update, params]]
610
+
611
+ process(options.merge(ops: ops), &block)
612
+ end
613
+
614
+ # All accounts with a VFS (Vesting Fund Shares) can vote for or against any
615
+ # witness.
616
+ #
617
+ # @param options [Hash] options
618
+ # @option options [String] :wif Active wif
619
+ # @option options [Hash] :params
620
+ # * :account (String)
621
+ # * :witness (String)
622
+ # * :approve (Boolean)
623
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
624
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_account_witness_vote
625
+ def self.account_witness_vote(options, &block)
626
+ required_fields = %i(account witness approve)
627
+ params = options[:params]
628
+ check_required_fields(params, *required_fields)
629
+
630
+ ops = [[:account_witness_vote, params]]
631
+
632
+ process(options.merge(ops: ops), &block)
633
+ end
634
+
635
+ # @param options [Hash] options
636
+ # @option options [String] :wif Active wif
637
+ # @option options [Hash] :params
638
+ # * :account (String)
639
+ # * :proxy (String)
640
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
641
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_account_witness_proxy
642
+ def self.account_witness_proxy(options, &block)
643
+ required_fields = %i(account proxy)
644
+ params = options[:params]
645
+ check_required_fields(params, *required_fields)
646
+
647
+ ops = [[:account_witness_proxy, params]]
648
+
649
+ process(options.merge(ops: ops), &block)
650
+ end
651
+
652
+ # Provides a generic way to add higher level protocols on top of witness
653
+ # consensus.
654
+ #
655
+ # @param options [Hash] options
656
+ # @option options [String] :wif Active wif
657
+ # @option options [Hash] :params
658
+ # * :required_auths (Array<String>)
659
+ # * :id (String)
660
+ # * :data (String)
661
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
662
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_custom
663
+ def self.custom(options, &block)
664
+ required_fields = %i(required_auths id data)
665
+ params = options[:params]
666
+ check_required_fields(params, *required_fields)
667
+
668
+ ops = [[:custom, params]]
669
+
670
+ process(options.merge(ops: ops), &block)
671
+ end
672
+
673
+ # The semmantics for this operation are the same as the {Broadcast#custom_json}
674
+ # operation, but with a binary payload.
675
+ #
676
+ # @param options [Hash] options
677
+ # @option options [String] :wif Posting wif
678
+ # @option options [Hash] :params
679
+ # * :id (String)
680
+ # * :data (String)
681
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
682
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_custom_binary
683
+ def self.custom_binary(options, &block)
684
+ required_fields = %i(id data)
685
+ params = options[:params]
686
+ check_required_fields(params, *required_fields)
687
+
688
+ ops = [[:custom_binary, params]]
689
+
690
+ process(options.merge(ops: ops), &block)
691
+ end
692
+
693
+ # Serves the same purpose as {Broadcast#custom} but also supports required
694
+ # posting authorities.
695
+ #
696
+ # @param options [Hash] options
697
+ # @option options [String] :wif Posting wif
698
+ # @option options [Hash] :params
699
+ # * :required_auths (Array<String>)
700
+ # * :required_posting_auths (Arrat<String>)
701
+ # * :id (String)
702
+ # * :data (Hash) Data of the custom json, becomes `json`.
703
+ # * :json (String) String version of `data` (use one or the other).
704
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
705
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_custom_json
706
+ def self.custom_json(options, &block)
707
+ required_fields = %i(id)
708
+ params = options[:params]
709
+
710
+ if !!params[:data] && !!params[:json]
711
+ raise DPay::ArgumentError, 'Assign either data or json, not both.'
712
+ end
713
+
714
+ data = params.delete(:data) || {}
715
+ data ||= (JSON[params[:json]] || nil) || {}
716
+ params[:json] = data.to_json
717
+
718
+ check_required_fields(params, *required_fields)
719
+
720
+ params[:required_auths] ||= []
721
+ params[:required_posting_auths] ||= []
722
+ ops = [[:custom_json, params]]
723
+
724
+ process(options.merge(ops: ops), &block)
725
+ end
726
+
727
+ # Allows an account to setup a vesting withdraw but with the additional
728
+ # request for the funds to be transferred directly to another account’s
729
+ # balance rather than the withdrawing account.
730
+ #
731
+ # @param options [Hash] options
732
+ # @option options [String] :wif Active wif
733
+ # @option options [Hash] :params
734
+ # * :from_account (String)
735
+ # * :to_account (String)
736
+ # * :percent (Numeric)
737
+ # * :auto_vest (Boolean)
738
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
739
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_set_withdraw_vesting_route
740
+ def self.set_withdraw_vesting_route(options, &block)
741
+ required_fields = %i(from_account to_account percent auto_vest)
742
+ params = options[:params]
743
+ check_required_fields(params, *required_fields)
744
+
745
+ ops = [[:set_withdraw_vesting_route, params]]
746
+
747
+ process(options.merge(ops: ops), &block)
748
+ end
749
+
750
+ # All account recovery requests come from a listed recovery account.
751
+ #
752
+ # @param options [Hash] options
753
+ # @option options [String] :wif Active wif
754
+ # @option options [Hash] :params
755
+ # * :recovery_account (String)
756
+ # * :account_to_recover (String)
757
+ # * :new_owner_authority (Hash)
758
+ # * :extensions (Array) (optional)
759
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
760
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_request_account_recovery
761
+ def self.request_account_recovery(options, &block)
762
+ required_fields = %i(recovery_account account_to_recover new_owner_authority)
763
+ params = options[:params]
764
+ check_required_fields(params, *required_fields)
765
+
766
+ params[:extensions] ||= []
767
+ ops = [[:request_account_recovery, params]]
768
+
769
+ process(options.merge(ops: ops), &block)
770
+ end
771
+
772
+ # @param options [Hash] options
773
+ # @option options [String] :wif Active wif
774
+ # @option options [Hash] :params
775
+ # * :account_to_recover (String)
776
+ # * :new_owner_authority (Hash)
777
+ # * :recent_owner_authority (Hash)
778
+ # * :extensions (Array) (optional)
779
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
780
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_recover_account
781
+ def self.recover_account(options, &block)
782
+ required_fields = %i(account_to_recover new_owner_authority recent_owner_authority)
783
+ params = options[:params]
784
+ check_required_fields(params, *required_fields)
785
+
786
+ params[:extensions] ||= []
787
+ ops = [[:recover_account, params]]
788
+
789
+ process(options.merge(ops: ops), &block)
790
+ end
791
+
792
+ # Each account lists another account as their recovery account.
793
+ #
794
+ # @param options [Hash] options
795
+ # @option options [String] :wif Posting wif
796
+ # @option options [Hash] :params
797
+ # * :account_to_recover (String)
798
+ # * :new_recovery_account (String)
799
+ # * :extensions (Array) (optional)
800
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
801
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_change_recovery_account
802
+ def self.change_recovery_account(options, &block)
803
+ required_fields = %i(account_to_recover)
804
+ params = options[:params]
805
+ check_required_fields(params, *required_fields)
806
+
807
+ params[:new_recovery_account] ||= ''
808
+ params[:extensions] ||= []
809
+ ops = [[:change_recovery_account, params]]
810
+
811
+ process(options.merge(ops: ops), &block)
812
+ end
813
+
814
+ # The purpose of this operation is to enable someone to send money
815
+ # contingently to another individual.
816
+ #
817
+ # @param options [Hash] options
818
+ # @option options [String] :wif Active wif
819
+ # @option options [Hash] :params
820
+ # * :from (String)
821
+ # * :to (String)
822
+ # * :agent (String)
823
+ # * :escrow_id (String)
824
+ # * :bbd_amount (String)
825
+ # * :dpay_amount (String)
826
+ # * :fee (String)
827
+ # * :ratification_deadline (String)
828
+ # * :escrow_expiration (String)
829
+ # * :meta (Hash) Meta of the escrow transfer, becomes `json_meta`.
830
+ # * :json_meta (String) String version of `metadata` (use one or the other).
831
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
832
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_escrow_transfer
833
+ def self.escrow_transfer(options, &block)
834
+ required_fields = %i(from to agent escrow_id fee ratification_deadline)
835
+ params = options[:params]
836
+
837
+ if !!params[:meta] && !!params[:json_meta]
838
+ raise DPay::ArgumentError, 'Assign either meta or json_meta, not both.'
839
+ end
840
+
841
+ meta = params.delete(:meta) || {}
842
+ meta ||= (JSON[params[:json_meta]] || nil) || {}
843
+ params[:json_meta] = meta.to_json
844
+
845
+ check_required_fields(params, *required_fields)
846
+
847
+ params[:bbd_amount] = normalize_amount(options.merge amount: params[:bbd_amount])
848
+ params[:dpay_amount] = normalize_amount(options.merge amount: params[:dpay_amount])
849
+ params[:fee] = normalize_amount(options.merge amount: params[:fee])
850
+
851
+ params[:ratification_deadline] = Time.parse(params[:ratification_deadline].to_s)
852
+ params[:ratification_deadline] = params[:ratification_deadline].strftime('%Y-%m-%dT%H:%M:%S')
853
+
854
+ if !!params[:escrow_expiration]
855
+ params[:escrow_expiration] = Time.parse(params[:escrow_expiration].to_s)
856
+ params[:escrow_expiration] = params[:escrow_expiration].strftime('%Y-%m-%dT%H:%M:%S')
857
+ end
858
+
859
+ ops = [[:escrow_transfer, params]]
860
+
861
+ process(options.merge(ops: ops), &block)
862
+ end
863
+
864
+ # If either the sender or receiver of an escrow payment has an issue, they
865
+ # can raise it for dispute.
866
+ #
867
+ # @param options [Hash] options
868
+ # @option options [String] :wif Active wif
869
+ # @option options [Hash] :params
870
+ # * :from (String)
871
+ # * :to (String)
872
+ # * :agent (String)
873
+ # * :who (String)
874
+ # * :escrow_id (String)
875
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
876
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_escrow_dispute
877
+ def self.escrow_dispute(options, &block)
878
+ required_fields = %i(from to agent who escrow_id)
879
+ params = options[:params]
880
+ check_required_fields(params, *required_fields)
881
+
882
+ ops = [[:escrow_dispute, params]]
883
+
884
+ process(options.merge(ops: ops), &block)
885
+ end
886
+
887
+ # This operation can be used by anyone associated with the escrow transfer
888
+ # to release funds if they have permission.
889
+ #
890
+ # @param options [Hash] options
891
+ # @option options [String] :wif Active wif
892
+ # @option options [Hash] :params
893
+ # * :from (String)
894
+ # * :to (String)
895
+ # * :agent (String)
896
+ # * :who (String)
897
+ # * :receiver (String)
898
+ # * :escrow_id (String)
899
+ # * :bbd_amount (String)
900
+ # * :dpay_amount (String)
901
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
902
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_escrow_release
903
+ def self.escrow_release(options, &block)
904
+ required_fields = %i(from to agent who receiver escrow_id)
905
+ params = options[:params]
906
+ check_required_fields(params, *required_fields)
907
+
908
+ params[:bbd_amount] = normalize_amount(options.merge amount: params[:bbd_amount])
909
+ params[:dpay_amount] = normalize_amount(options.merge amount: params[:dpay_amount])
910
+
911
+ ops = [[:escrow_release, params]]
912
+
913
+ process(options.merge(ops: ops), &block)
914
+ end
915
+
916
+ # The agent and to accounts must approve an escrow transaction for it to be
917
+ # valid on the blockchain.
918
+ #
919
+ # @param options [Hash] options
920
+ # @option options [String] :wif Active wif
921
+ # @option options [Hash] :params
922
+ # * :from (String)
923
+ # * :to (String)
924
+ # * :agent (String)
925
+ # * :who (String)
926
+ # * :escrow_id (String)
927
+ # * :approve (String)
928
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
929
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_escrow_approve
930
+ def self.escrow_approve(options, &block)
931
+ required_fields = %i(from to agent who escrow_id approve)
932
+ params = options[:params]
933
+ check_required_fields(params, *required_fields)
934
+
935
+ ops = [[:escrow_approve, params]]
936
+
937
+ process(options.merge(ops: ops), &block)
938
+ end
939
+
940
+ # For time locked savings accounts.
941
+ #
942
+ # @param options [Hash] options
943
+ # @option options [String] :wif Active wif
944
+ # @option options [Hash] :params
945
+ # * :from (String)
946
+ # * :to (String)
947
+ # * :amount (String)
948
+ # * :memo (String) (optional)
949
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
950
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_transfer_to_savings
951
+ def self.transfer_to_savings(options, &block)
952
+ required_fields = %i(from to amount)
953
+ params = options[:params]
954
+ check_required_fields(params, *required_fields)
955
+
956
+ params[:memo] ||= ''
957
+ params[:amount] = normalize_amount(options.merge amount: params[:amount])
958
+
959
+ ops = [[:transfer_to_savings, params]]
960
+
961
+ process(options.merge(ops: ops), &block)
962
+ end
963
+
964
+ # @param options [Hash] options
965
+ # @option options [String] :wif Active wif
966
+ # @option options [Hash] :params
967
+ # * :from (String)
968
+ # * :request_id (String)
969
+ # * :to (String)
970
+ # * :amount (String)
971
+ # * :memo (String) (optional)
972
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
973
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_transfer_from_savings
974
+ def self.transfer_from_savings(options, &block)
975
+ required_fields = %i(from request_id to amount)
976
+ params = options[:params]
977
+ check_required_fields(params, *required_fields)
978
+
979
+ params[:memo] ||= ''
980
+ params[:amount] = normalize_amount(options.merge amount: params[:amount])
981
+
982
+ ops = [[:transfer_from_savings, params]]
983
+
984
+ process(options.merge(ops: ops), &block)
985
+ end
986
+
987
+ # @param options [Hash] options
988
+ # @option options [String] :wif Active wif
989
+ # @option options [Hash] :params
990
+ # * :from (String)
991
+ # * :request_id (String)
992
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
993
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_cancel_transfer_from_savings
994
+ def self.cancel_transfer_from_savings(options, &block)
995
+ required_fields = %i(from request_id)
996
+ params = options[:params]
997
+ check_required_fields(params, *required_fields)
998
+
999
+ ops = [[:cancel_transfer_from_savings, params]]
1000
+
1001
+ process(options.merge(ops: ops), &block)
1002
+ end
1003
+
1004
+ # An account can chose to decline their voting rights after a 30 day delay.
1005
+ # This includes voting on content and witnesses. **The voting rights cannot
1006
+ # be acquired again once they have been declined.** This is only to
1007
+ # formalize a smart contract between certain accounts and the community that
1008
+ # currently only exists as a social contract.
1009
+ #
1010
+ # @param options [Hash] options
1011
+ # @option options [String] :wif Owner wif
1012
+ # @option options [Hash] :params
1013
+ # * :account (String)
1014
+ # * :decline (String)
1015
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
1016
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_decline_voting_rights
1017
+ def self.decline_voting_rights(options, &block)
1018
+ required_fields = %i(account decline)
1019
+ params = options[:params]
1020
+ check_required_fields(params, *required_fields)
1021
+
1022
+ ops = [[:decline_voting_rights, params]]
1023
+
1024
+ process(options.merge(ops: ops), &block)
1025
+ end
1026
+
1027
+ # Delegate vesting shares from one account to the other.
1028
+ #
1029
+ # @param options [Hash] options
1030
+ # @option options [String] :wif Active wif
1031
+ # @option options [Hash] :params
1032
+ # * :delegator (String)
1033
+ # * :delegatee (String)
1034
+ # * :vesting_shares (String)
1035
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
1036
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_delegate_vesting_shares
1037
+ def self.delegate_vesting_shares(options, &block)
1038
+ required_fields = %i(delegator delegatee vesting_shares)
1039
+ params = options[:params]
1040
+ check_required_fields(params, *required_fields)
1041
+
1042
+ params[:vesting_shares] = normalize_amount(options.merge amount: params[:vesting_shares])
1043
+ ops = [[:delegate_vesting_shares, params]]
1044
+
1045
+ process(options.merge(ops: ops), &block)
1046
+ end
1047
+
1048
+ # @param options [Hash] options
1049
+ # @option options [String] :wif Active wif
1050
+ # @option options [Hash] :params
1051
+ # * :fee (String)
1052
+ # * :delegation (String)
1053
+ # * :creator (String)
1054
+ # * :new_account_name (String)
1055
+ # * :owner (String)
1056
+ # * :active (String)
1057
+ # * :posting (String)
1058
+ # * :memo_key (String)
1059
+ # * :metadata (Hash) Metadata of the account, becomes `json_metadata`.
1060
+ # * :json_metadata (String) String version of `metadata` (use one or the other).
1061
+ # * :extensions (Array)
1062
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
1063
+ # @see https://developers.dpays.io/apidefinitions/broadcast-ops#broadcast_ops_account_create_with_delegation
1064
+ def self.account_create_with_delegation(options, &block)
1065
+ required_fields = %i(fee delegation creator new_account_name owner active posting memo_key)
1066
+ params = options[:params]
1067
+
1068
+ if !!params[:metadata] && !!params[:json_metadata]
1069
+ raise DPay::ArgumentError, 'Assign either metadata or json_metadata, not both.'
1070
+ end
1071
+
1072
+ metadata = params.delete(:metadata) || {}
1073
+ metadata ||= (JSON[params[:json_metadata]] || nil) || {}
1074
+ params[:json_metadata] = metadata.to_json
1075
+
1076
+ check_required_fields(params, *required_fields)
1077
+
1078
+ params[:fee] = normalize_amount(options.merge amount: params[:fee])
1079
+ params[:delegation] = normalize_amount(options.merge amount: params[:delegation])
1080
+ params[:extensions] ||= []
1081
+
1082
+ ops = [[:account_create_with_delegation, params]]
1083
+
1084
+ process(options.merge(ops: ops), &block)
1085
+ end
1086
+
1087
+ # @param options [Hash] options
1088
+ # @option options [Array<Array<Hash>] :ops Operations to process.
1089
+ # @option options [Boolean] :pretend Just validate, do not broadcast.
1090
+ def self.process(options, &block)
1091
+ ops = options[:ops]
1092
+ tx = TransactionBuilder.new(options)
1093
+ response = nil
1094
+
1095
+ loop do; begin
1096
+ tx.operations = ops
1097
+ trx = tx.transaction
1098
+
1099
+ response = if !!options[:pretend]
1100
+ if !!options[:app_base]
1101
+ database_api(options).verify_authority(trx: trx)
1102
+ else
1103
+ database_api(options).verify_authority(trx)
1104
+ end
1105
+ else
1106
+ if !!options[:app_base]
1107
+ network_broadcast_api(options).broadcast_transaction(trx: trx)
1108
+ else
1109
+ network_broadcast_api(options).broadcast_transaction_synchronous(trx)
1110
+ end
1111
+ end
1112
+
1113
+ break
1114
+ rescue => e
1115
+ if can_retry? e
1116
+ tx.expiration = nil
1117
+ redo
1118
+ end
1119
+
1120
+ raise e
1121
+ end; end
1122
+
1123
+ if !!block
1124
+ block.call response.result
1125
+ else
1126
+ return response.result
1127
+ end
1128
+ end
1129
+ private
1130
+ # @private
1131
+ def self.normalize_amount(options)
1132
+ if !!options[:app_base]
1133
+ Type::Amount.to_h(options[:amount])
1134
+ else
1135
+ Type::Amount.to_s(options[:amount])
1136
+ end
1137
+ end
1138
+
1139
+ # @private
1140
+ def self.database_api(options)
1141
+ options[:database_api] ||= if !!options[:app_base]
1142
+ DPay::DatabaseApi.new(options)
1143
+ else
1144
+ DPay::CondenserApi.new(options)
1145
+ end
1146
+ end
1147
+
1148
+ # @private
1149
+ def self.network_broadcast_api(options)
1150
+ options[:network_broadcast_api] ||= if !!options[:app_base]
1151
+ DPay::NetworkBroadcaseApi.new(options)
1152
+ else
1153
+ DPay::CondenserApi.new(options)
1154
+ end
1155
+ end
1156
+
1157
+ # @private
1158
+ def self.check_required_fields(hash, *fields)
1159
+ fields.each do |field|
1160
+ value = hash[field]
1161
+
1162
+ raise DPay::ArgumentError, "#{field}: required" if value.nil?
1163
+
1164
+ case value
1165
+ when String, Array, Hash
1166
+ raise DPay::ArgumentError, "#{field}: required" if value.empty?
1167
+ end
1168
+ end
1169
+ end
1170
+ end
1171
+ end