glueby 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +3 -4
  3. data/README.md +3 -3
  4. data/glueby.gemspec +2 -1
  5. data/lib/generators/glueby/contract/templates/initializer.rb.erb +8 -8
  6. data/lib/generators/glueby/contract/templates/key_table.rb.erb +16 -16
  7. data/lib/generators/glueby/contract/templates/timestamp_table.rb.erb +2 -0
  8. data/lib/generators/glueby/contract/templates/token_metadata_table.rb.erb +13 -0
  9. data/lib/generators/glueby/contract/templates/utxo_table.rb.erb +1 -0
  10. data/lib/generators/glueby/contract/{reissuable_token_generator.rb → token_generator.rb} +12 -1
  11. data/lib/glueby/block_syncer.rb +97 -97
  12. data/lib/glueby/configuration.rb +2 -0
  13. data/lib/glueby/contract/active_record/timestamp.rb +9 -2
  14. data/lib/glueby/contract/active_record/token_metadata.rb +8 -0
  15. data/lib/glueby/contract/active_record.rb +1 -0
  16. data/lib/glueby/contract/fee_estimator/auto.rb +9 -1
  17. data/lib/glueby/contract/fee_estimator.rb +12 -5
  18. data/lib/glueby/contract/timestamp/syncer.rb +1 -1
  19. data/lib/glueby/contract/token.rb +161 -80
  20. data/lib/glueby/contract/tx_builder.rb +104 -67
  21. data/lib/glueby/fee_provider/tasks.rb +4 -1
  22. data/lib/glueby/internal/wallet/abstract_wallet_adapter.rb +26 -0
  23. data/lib/glueby/internal/wallet/active_record/key.rb +2 -2
  24. data/lib/glueby/internal/wallet/active_record/utxo.rb +9 -0
  25. data/lib/glueby/internal/wallet/active_record_wallet_adapter.rb +22 -9
  26. data/lib/glueby/internal/wallet/errors.rb +1 -0
  27. data/lib/glueby/internal/wallet/mysql_wallet_adapter.rb +17 -0
  28. data/lib/glueby/internal/wallet/tapyrus_core_wallet_adapter.rb +1 -1
  29. data/lib/glueby/internal/wallet.rb +16 -0
  30. data/lib/glueby/util/digest.rb +23 -0
  31. data/lib/glueby/utxo_provider/tasks.rb +3 -1
  32. data/lib/glueby/utxo_provider.rb +56 -9
  33. data/lib/glueby/version.rb +1 -1
  34. data/lib/tasks/glueby/block_syncer.rake +7 -0
  35. metadata +23 -5
@@ -43,6 +43,10 @@ module Glueby
43
43
  # token.amount(wallet: alice)
44
44
  # => 100
45
45
  #
46
+ # Issue with metadata / Get metadata
47
+ # token = Token.issue!(issuer: alice, amount: 100, metadata: 'metadata')
48
+ # token.metadata
49
+ # => "metadata"
46
50
  class Token
47
51
  include Glueby::Contract::TxBuilder
48
52
  extend Glueby::Contract::TxBuilder
@@ -57,22 +61,24 @@ module Glueby
57
61
  # @param token_type [TokenTypes]
58
62
  # @param amount [Integer]
59
63
  # @param split [Integer] The tx outputs should be split by specified number.
64
+ # @param fee_estimator [Glueby::Contract::FeeEstimator]
65
+ # @param metadata [String] The data to be stored in blockchain.
60
66
  # @return [Array<token, Array<tx>>] Tuple of tx array and token object
61
67
  # @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
62
68
  # @raise [InvalidAmount] if amount is not positive integer.
63
69
  # @raise [InvalidSplit] if split is greater than 1 for NFT token.
64
- # @raise [UnspportedTokenType] if token is not supported.
65
- def issue!(issuer:, token_type: Tapyrus::Color::TokenTypes::REISSUABLE, amount: 1, split: 1)
70
+ # @raise [UnsupportedTokenType] if token is not supported.
71
+ def issue!(issuer:, token_type: Tapyrus::Color::TokenTypes::REISSUABLE, amount: 1, split: 1, fee_estimator: FeeEstimator::Fixed.new, metadata: nil)
66
72
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
67
73
  raise Glueby::Contract::Errors::InvalidSplit if token_type == Tapyrus::Color::TokenTypes::NFT && split > 1
68
74
 
69
75
  txs, color_id = case token_type
70
76
  when Tapyrus::Color::TokenTypes::REISSUABLE
71
- issue_reissuable_token(issuer: issuer, amount: amount, split: split)
77
+ issue_reissuable_token(issuer: issuer, amount: amount, split: split, fee_estimator: fee_estimator, metadata: metadata)
72
78
  when Tapyrus::Color::TokenTypes::NON_REISSUABLE
73
- issue_non_reissuable_token(issuer: issuer, amount: amount, split: split)
79
+ issue_non_reissuable_token(issuer: issuer, amount: amount, split: split, fee_estimator: fee_estimator, metadata: metadata)
74
80
  when Tapyrus::Color::TokenTypes::NFT
75
- issue_nft_token(issuer: issuer)
81
+ issue_nft_token(issuer: issuer, metadata: metadata)
76
82
  else
77
83
  raise Glueby::Contract::Errors::UnsupportedTokenType
78
84
  end
@@ -84,10 +90,35 @@ module Glueby
84
90
  Glueby::AR::SystemInformation.use_only_finalized_utxo?
85
91
  end
86
92
 
93
+ # Sign to pay-to-contract output.
94
+ #
95
+ # @param issuer [Glueby::Walelt] Issuer of the token
96
+ # @param tx [Tapyrus::Tx] The transaction to be signed with metadata
97
+ # @param funding_tx [Tapyrus::Tx] The funding transaction that has pay-to-contract output in its first output
98
+ # @param payment_base [String] The public key used to generate pay to contract public key
99
+ # @param metadata [String] Data that represents token metadata
100
+ # @return [Tapyrus::Tx] signed tx
101
+ def sign_to_p2c_output(issuer, tx, funding_tx, payment_base, metadata)
102
+ utxo = { txid: funding_tx.txid, vout: 0, script_pubkey: funding_tx.outputs[0].script_pubkey.to_hex }
103
+ issuer.internal_wallet.sign_to_pay_to_contract_address(tx, utxo, payment_base, metadata)
104
+ end
105
+
87
106
  private
88
107
 
89
- def issue_reissuable_token(issuer:, amount:, split: 1)
90
- funding_tx = create_funding_tx(wallet: issuer, only_finalized: only_finalized?)
108
+ def create_p2c_address(wallet, metadata)
109
+ p2c_address, payment_base = wallet.internal_wallet.create_pay_to_contract_address(metadata)
110
+ script = Tapyrus::Script.parse_from_addr(p2c_address)
111
+ [script, p2c_address, payment_base]
112
+ end
113
+
114
+ def issue_reissuable_token(issuer:, amount:, split: 1, fee_estimator:, metadata: nil)
115
+ script, p2c_address, payment_base = create_p2c_address(issuer, metadata) if metadata
116
+
117
+ # For reissuable token, we need funding transaction for every issuance.
118
+ # To make it easier for API users to understand whether a transaction is a new issue or a reissue,
119
+ # when Token.issue! is executed, a new address is created and tpc is sent to it to ensure that it is a new issue,
120
+ # and a transaction is created using that UTXO as input to create a new color_id.
121
+ funding_tx = create_funding_tx(wallet: issuer, script: script, only_finalized: only_finalized?)
91
122
  script_pubkey = funding_tx.outputs.first.script_pubkey
92
123
  color_id = Tapyrus::Color::ColorIdentifier.reissuable(script_pubkey)
93
124
 
@@ -99,41 +130,82 @@ module Glueby
99
130
  # Store the script_pubkey for reissue the token.
100
131
  Glueby::Contract::AR::ReissuableToken.create!(color_id: color_id.to_hex, script_pubkey: script_pubkey.to_hex)
101
132
 
102
- tx = create_issue_tx_for_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount, split: split)
133
+ tx = create_issue_tx_for_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount, split: split, fee_estimator: fee_estimator)
134
+ if metadata
135
+ Glueby::Contract::AR::TokenMetadata.create!(
136
+ color_id: color_id.to_hex,
137
+ metadata: metadata,
138
+ p2c_address: p2c_address,
139
+ payment_base: payment_base
140
+ )
141
+ tx = sign_to_p2c_output(issuer, tx, funding_tx, payment_base, metadata)
142
+ end
103
143
  tx = issuer.internal_wallet.broadcast(tx)
104
144
  [[funding_tx, tx], color_id]
105
145
  end
106
146
  end
107
147
 
108
- def issue_non_reissuable_token(issuer:, amount:, split: 1)
109
- funding_tx = create_funding_tx(wallet: issuer, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider?
110
- funding_tx = issuer.internal_wallet.broadcast(funding_tx) if funding_tx
148
+ def issue_non_reissuable_token(issuer:, amount:, split: 1, fee_estimator:, metadata: nil)
149
+ script, p2c_address, payment_base = create_p2c_address(issuer, metadata) if metadata
150
+ funding_tx = create_funding_tx(wallet: issuer, script: script, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider? || script
151
+ if funding_tx
152
+ ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
153
+ funding_tx = issuer.internal_wallet.broadcast(funding_tx)
154
+ end
155
+ end
111
156
 
112
- tx = create_issue_tx_for_non_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount, split: split)
113
- tx = issuer.internal_wallet.broadcast(tx)
157
+ ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
158
+ tx = create_issue_tx_for_non_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount, split: split, fee_estimator: fee_estimator)
159
+ out_point = tx.inputs.first.out_point
160
+ color_id = Tapyrus::Color::ColorIdentifier.non_reissuable(out_point)
161
+ if metadata
162
+ Glueby::Contract::AR::TokenMetadata.create!(
163
+ color_id: color_id.to_hex,
164
+ metadata: metadata,
165
+ p2c_address: p2c_address,
166
+ payment_base: payment_base
167
+ )
168
+ tx = sign_to_p2c_output(issuer, tx, funding_tx, payment_base, metadata)
169
+ end
170
+ tx = issuer.internal_wallet.broadcast(tx)
114
171
 
115
- out_point = tx.inputs.first.out_point
116
- color_id = Tapyrus::Color::ColorIdentifier.non_reissuable(out_point)
117
- if funding_tx
118
- [[funding_tx, tx], color_id]
119
- else
120
- [[tx], color_id]
172
+ if funding_tx
173
+ [[funding_tx, tx], color_id]
174
+ else
175
+ [[tx], color_id]
176
+ end
121
177
  end
122
178
  end
123
179
 
124
- def issue_nft_token(issuer:)
125
- funding_tx = create_funding_tx(wallet: issuer, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider?
126
- funding_tx = issuer.internal_wallet.broadcast(funding_tx) if funding_tx
180
+ def issue_nft_token(issuer:, metadata: nil)
181
+ script, p2c_address, payment_base = create_p2c_address(issuer, metadata) if metadata
182
+ funding_tx = create_funding_tx(wallet: issuer, script: script, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider? || script
183
+ if funding_tx
184
+ ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
185
+ funding_tx = issuer.internal_wallet.broadcast(funding_tx)
186
+ end
187
+ end
127
188
 
128
- tx = create_issue_tx_for_nft_token(funding_tx: funding_tx, issuer: issuer)
129
- tx = issuer.internal_wallet.broadcast(tx)
189
+ ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
190
+ tx = create_issue_tx_for_nft_token(funding_tx: funding_tx, issuer: issuer)
191
+ out_point = tx.inputs.first.out_point
192
+ color_id = Tapyrus::Color::ColorIdentifier.nft(out_point)
193
+ if metadata
194
+ Glueby::Contract::AR::TokenMetadata.create!(
195
+ color_id: color_id.to_hex,
196
+ metadata: metadata,
197
+ p2c_address: p2c_address,
198
+ payment_base: payment_base
199
+ )
200
+ tx = sign_to_p2c_output(issuer, tx, funding_tx, payment_base, metadata)
201
+ end
202
+ tx = issuer.internal_wallet.broadcast(tx)
130
203
 
131
- out_point = tx.inputs.first.out_point
132
- color_id = Tapyrus::Color::ColorIdentifier.nft(out_point)
133
- if funding_tx
134
- [[funding_tx, tx], color_id]
135
- else
136
- [[tx], color_id]
204
+ if funding_tx
205
+ [[funding_tx, tx], color_id]
206
+ else
207
+ [[tx], color_id]
208
+ end
137
209
  end
138
210
  end
139
211
  end
@@ -145,25 +217,28 @@ module Glueby
145
217
  # @param issuer [Glueby::Wallet]
146
218
  # @param amount [Integer]
147
219
  # @param split [Integer]
220
+ # @param fee_estimator [Glueby::Contract::FeeEstimator]
148
221
  # @return [Array<String, tx>] Tuple of color_id and tx object
149
222
  # @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
150
223
  # @raise [InvalidAmount] if amount is not positive integer.
151
224
  # @raise [InvalidTokenType] if token is not reissuable.
152
225
  # @raise [UnknownScriptPubkey] when token is reissuable but it doesn't know script pubkey to issue token.
153
- def reissue!(issuer:, amount:, split: 1)
226
+ def reissue!(issuer:, amount:, split: 1, fee_estimator: FeeEstimator::Fixed.new)
154
227
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
155
228
  raise Glueby::Contract::Errors::InvalidTokenType unless token_type == Tapyrus::Color::TokenTypes::REISSUABLE
156
229
 
157
- if validate_reissuer(wallet: issuer)
158
- funding_tx = create_funding_tx(wallet: issuer, script: @script_pubkey, only_finalized: only_finalized?)
159
- funding_tx = issuer.internal_wallet.broadcast(funding_tx)
160
- tx = create_reissue_tx(funding_tx: funding_tx, issuer: issuer, amount: amount, color_id: color_id, split: split)
161
- tx = issuer.internal_wallet.broadcast(tx)
230
+ token_metadata = Glueby::Contract::AR::TokenMetadata.find_by(color_id: color_id.to_hex)
231
+ raise Glueby::Contract::Errors::UnknownScriptPubkey unless valid_reissuer?(wallet: issuer, token_metadata: token_metadata)
162
232
 
163
- [color_id, tx]
164
- else
165
- raise Glueby::Contract::Errors::UnknownScriptPubkey
233
+ funding_tx = create_funding_tx(wallet: issuer, script: @script_pubkey, only_finalized: only_finalized?)
234
+ funding_tx = issuer.internal_wallet.broadcast(funding_tx)
235
+ tx = create_reissue_tx(funding_tx: funding_tx, issuer: issuer, amount: amount, color_id: color_id, split: split, fee_estimator: fee_estimator)
236
+ if token_metadata
237
+ tx = Token.sign_to_p2c_output(issuer, tx, funding_tx, token_metadata.payment_base, token_metadata.metadata)
166
238
  end
239
+ tx = issuer.internal_wallet.broadcast(tx)
240
+
241
+ [color_id, tx]
167
242
  end
168
243
 
169
244
  # Send the token to other wallet
@@ -171,23 +246,21 @@ module Glueby
171
246
  # @param sender [Glueby::Wallet] wallet to send this token
172
247
  # @param receiver_address [String] address to receive this token
173
248
  # @param amount [Integer]
249
+ # @param fee_estimator [Glueby::Contract::FeeEstimator]
174
250
  # @return [Array<String, tx>] Tuple of color_id and tx object
175
251
  # @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
176
252
  # @raise [InsufficientTokens] if wallet does not have enough token to send.
177
253
  # @raise [InvalidAmount] if amount is not positive integer.
178
- def transfer!(sender:, receiver_address:, amount: 1)
254
+ def transfer!(sender:, receiver_address:, amount: 1, fee_estimator: FeeEstimator::Fixed.new)
179
255
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
180
256
 
181
- funding_tx = create_funding_tx(wallet: sender, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider?
182
- funding_tx = sender.internal_wallet.broadcast(funding_tx) if funding_tx
183
-
184
257
  tx = create_transfer_tx(
185
- funding_tx: funding_tx,
186
258
  color_id: color_id,
187
259
  sender: sender,
188
260
  receiver_address: receiver_address,
189
261
  amount: amount,
190
- only_finalized: only_finalized?
262
+ only_finalized: only_finalized?,
263
+ fee_estimator: fee_estimator
191
264
  )
192
265
  sender.internal_wallet.broadcast(tx)
193
266
  [color_id, tx]
@@ -197,23 +270,22 @@ module Glueby
197
270
  #
198
271
  # @param sender [Glueby::Wallet] wallet to send this token
199
272
  # @param receivers [Array<Hash>] array of hash, which keys are :address and :amount
273
+ # @param fee_estimator [Glueby::Contract::FeeEstimator]
200
274
  # @return [Array<String, tx>] Tuple of color_id and tx object
201
275
  # @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
202
276
  # @raise [InsufficientTokens] if wallet does not have enough token to send.
203
277
  # @raise [InvalidAmount] if amount is not positive integer.
204
- def multi_transfer!(sender:, receivers:)
278
+ def multi_transfer!(sender:, receivers:, fee_estimator: FeeEstimator::Fixed.new)
205
279
  receivers.each do |r|
206
280
  raise Glueby::Contract::Errors::InvalidAmount unless r[:amount].positive?
207
281
  end
208
- funding_tx = create_funding_tx(wallet: sender, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider?
209
- funding_tx = sender.internal_wallet.broadcast(funding_tx) if funding_tx
210
282
 
211
283
  tx = create_multi_transfer_tx(
212
- funding_tx: funding_tx,
213
284
  color_id: color_id,
214
285
  sender: sender,
215
286
  receivers: receivers,
216
- only_finalized: only_finalized?
287
+ only_finalized: only_finalized?,
288
+ fee_estimator: fee_estimator
217
289
  )
218
290
  sender.internal_wallet.broadcast(tx)
219
291
  [color_id, tx]
@@ -224,33 +296,17 @@ module Glueby
224
296
  #
225
297
  # @param sender [Glueby::Wallet] wallet to send this token
226
298
  # @param amount [Integer]
299
+ # @param fee_estimator [Glueby::Contract::FeeEstimator]
227
300
  # @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
228
301
  # @raise [InsufficientTokens] if wallet does not have enough token to send transaction.
229
302
  # @raise [InvalidAmount] if amount is not positive integer.
230
- def burn!(sender:, amount: 0)
303
+ def burn!(sender:, amount: 0, fee_estimator: FeeEstimator::Fixed.new)
231
304
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
232
305
  balance = sender.balances(only_finalized?)[color_id.to_hex]
233
306
  raise Glueby::Contract::Errors::InsufficientTokens unless balance
234
307
  raise Glueby::Contract::Errors::InsufficientTokens if balance < amount
235
308
 
236
- burn_all_amount_flag = true if balance - amount == 0
237
-
238
- utxo_provider = Glueby::UtxoProvider.new if Glueby.configuration.use_utxo_provider?
239
- if utxo_provider
240
- funding_tx = create_funding_tx(
241
- wallet: sender,
242
- # When it burns all the amount of the color id, burn tx is not going to be have any output
243
- # because change outputs is not necessary. Transactions needs one output at least.
244
- # At that time, set true to this option to get more value to be created change output to
245
- # the tx.
246
- need_value_for_change_output: burn_all_amount_flag,
247
- only_finalized: only_finalized?
248
- )
249
- end
250
-
251
- funding_tx = sender.internal_wallet.broadcast(funding_tx) if funding_tx
252
-
253
- tx = create_burn_tx(funding_tx: funding_tx, color_id: color_id, sender: sender, amount: amount, only_finalized: only_finalized?)
309
+ tx = create_burn_tx(color_id: color_id, sender: sender, amount: amount, only_finalized: only_finalized?, fee_estimator: fee_estimator)
254
310
  sender.internal_wallet.broadcast(tx)
255
311
  end
256
312
 
@@ -264,6 +320,31 @@ module Glueby
264
320
  results.sum { |result| result[:amount] }
265
321
  end
266
322
 
323
+ # Return metadata for this token
324
+ # @return [String] metadata. if token is not associated with metadata, return nil
325
+ def metadata
326
+ token_metadata&.metadata
327
+ end
328
+
329
+ # Return pay to contract address
330
+ # @return [String] p2c_address. if token is not associated with metadata, return nil
331
+ def p2c_address
332
+ token_metadata&.p2c_address
333
+ end
334
+
335
+ # Return public key used to generate pay to contract address
336
+ # @return [String] payment_base. if token is not associated with metadata, return nil
337
+ def payment_base
338
+ token_metadata&.payment_base
339
+ end
340
+
341
+ # Return Glueby::Contract::AR::TokenMetadata instance for this token
342
+ # @return [Glueby::Contract::AR::TokenMetadata]
343
+ def token_metadata
344
+ return @token_metadata if defined? @token_metadata
345
+ @token_metadata = Glueby::Contract::AR::TokenMetadata.find_by(color_id: color_id.to_hex)
346
+ end
347
+
267
348
  # Return token type
268
349
  # @return [Tapyrus::Color::TokenTypes]
269
350
  def token_type
@@ -312,18 +393,18 @@ module Glueby
312
393
  end
313
394
 
314
395
  # Verify that wallet is the issuer of the reissuable token
315
- # reutrn [Boolean]
316
- def validate_reissuer(wallet:)
317
- addresses = wallet.internal_wallet.get_addresses
318
- addresses.each do |address|
319
- decoded_address = Tapyrus.decode_base58_address(address)
320
- pubkey_hash_from_address = decoded_address[0]
321
- pubkey_hash_from_script = Tapyrus::Script.parse_from_payload(script_pubkey.chunks[2])
322
- if pubkey_hash_from_address == pubkey_hash_from_script.to_s
323
- return true
396
+ # @param wallet [Glueby::Wallet]
397
+ # @param token_metadata [Glueby::Contract::AR::TokenMetadata] metadata to be stored in blockchain as p2c address
398
+ # @return [Boolean] true if the wallet is the issuer of this token
399
+ def valid_reissuer?(wallet:, token_metadata: nil)
400
+ return false unless script_pubkey&.p2pkh?
401
+ address = if token_metadata
402
+ payment_base = Tapyrus::Key.new(pubkey: token_metadata.payment_base, key_type: Tapyrus::Key::TYPES[:compressed])
403
+ payment_base.to_p2pkh
404
+ else
405
+ script_pubkey.to_addr
324
406
  end
325
- end
326
- false
407
+ wallet.internal_wallet.has_address?(address)
327
408
  end
328
409
  end
329
410
  end
@@ -8,16 +8,16 @@ module Glueby
8
8
  end
9
9
 
10
10
  # Create new public key, and new transaction that sends TPC to it
11
- def create_funding_tx(wallet:, script: nil, fee_estimator: FeeEstimator::Fixed.new, need_value_for_change_output: false, only_finalized: true)
11
+ def create_funding_tx(wallet:, script: nil, fee_estimator: FeeEstimator::Fixed.new, only_finalized: true)
12
12
  if Glueby.configuration.use_utxo_provider?
13
- utxo_provider = UtxoProvider.new
13
+ utxo_provider = UtxoProvider.instance
14
14
  script_pubkey = script ? script : Tapyrus::Script.parse_from_addr(wallet.internal_wallet.receive_address)
15
- funding_tx, _index = utxo_provider.get_utxo(script_pubkey, funding_tx_amount(need_value_for_change_output: need_value_for_change_output))
15
+ funding_tx, _index = utxo_provider.get_utxo(script_pubkey, funding_tx_amount)
16
16
  utxo_provider.wallet.sign_tx(funding_tx)
17
17
  else
18
18
  txb = Tapyrus::TxBuilder.new
19
19
  fee = fee_estimator.fee(FeeEstimator.dummy_tx(txb.build))
20
- amount = fee + funding_tx_amount(need_value_for_change_output: need_value_for_change_output)
20
+ amount = fee + funding_tx_amount
21
21
  sum, outputs = wallet.internal_wallet.collect_uncolored_outputs(amount, nil, only_finalized)
22
22
  outputs.each do |utxo|
23
23
  txb.add_utxo({
@@ -44,20 +44,39 @@ module Glueby
44
44
  tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
45
45
  output = funding_tx.outputs.first
46
46
 
47
- receiver_script = Tapyrus::Script.parse_from_payload(output.script_pubkey.to_payload)
48
- color_id = Tapyrus::Color::ColorIdentifier.reissuable(receiver_script)
47
+ receiver_script = Tapyrus::Script.parse_from_addr(issuer.internal_wallet.receive_address)
48
+ funding_script = Tapyrus::Script.parse_from_payload(output.script_pubkey.to_payload)
49
+ color_id = Tapyrus::Color::ColorIdentifier.reissuable(funding_script)
49
50
  receiver_colored_script = receiver_script.add_color(color_id)
50
51
 
51
52
  add_split_output(tx, amount, split, receiver_colored_script)
52
53
 
53
54
  fee = fee_estimator.fee(FeeEstimator.dummy_tx(tx))
54
- fill_change_tpc(tx, issuer, output.value - fee)
55
+
55
56
  prev_txs = [{
56
57
  txid: funding_tx.txid,
57
58
  vout: 0,
58
59
  scriptPubKey: output.script_pubkey.to_hex,
59
60
  amount: output.value
60
61
  }]
62
+
63
+ if Glueby.configuration.use_utxo_provider?
64
+ utxo_provider = UtxoProvider.instance
65
+ tx, fee, input_amount, provided_utxos = utxo_provider.fill_inputs(
66
+ tx,
67
+ target_amount: 0,
68
+ current_amount: output.value,
69
+ fee_estimator: fee_estimator
70
+ )
71
+ prev_txs.concat(provided_utxos)
72
+ else
73
+ # TODO: Support the case of providing UTXOs from sender's wallet.
74
+ input_amount = output.value
75
+ end
76
+
77
+ fill_change_tpc(tx, issuer, input_amount - fee)
78
+
79
+ UtxoProvider.instance.wallet.sign_tx(tx, prev_txs) if Glueby.configuration.use_utxo_provider?
61
80
  issuer.internal_wallet.sign_tx(tx, prev_txs)
62
81
  end
63
82
 
@@ -96,18 +115,32 @@ module Glueby
96
115
  receiver_colored_script = receiver_script.add_color(color_id)
97
116
  add_split_output(tx, amount, split, receiver_colored_script)
98
117
 
99
- fill_change_tpc(tx, issuer, sum - fee)
100
118
  prev_txs = if funding_tx
101
- output = funding_tx.outputs.first
102
- [{
103
- txid: funding_tx.txid,
104
- vout: 0,
105
- scriptPubKey: output.script_pubkey.to_hex,
106
- amount: output.value
107
- }]
108
- else
109
- []
119
+ output = funding_tx.outputs.first
120
+ [{
121
+ txid: funding_tx.txid,
122
+ vout: 0,
123
+ scriptPubKey: output.script_pubkey.to_hex,
124
+ amount: output.value
125
+ }]
126
+ else
127
+ []
128
+ end
129
+
130
+ if Glueby.configuration.use_utxo_provider?
131
+ utxo_provider = UtxoProvider.instance
132
+ tx, fee, sum, provided_utxos = utxo_provider.fill_inputs(
133
+ tx,
134
+ target_amount: 0,
135
+ current_amount: sum,
136
+ fee_estimator: fee_estimator
137
+ )
138
+ prev_txs.concat(provided_utxos)
110
139
  end
140
+
141
+ fill_change_tpc(tx, issuer, sum - fee)
142
+
143
+ UtxoProvider.instance.wallet.sign_tx(tx, prev_txs) if Glueby.configuration.use_utxo_provider?
111
144
  issuer.internal_wallet.sign_tx(tx, prev_txs)
112
145
  end
113
146
 
@@ -118,25 +151,42 @@ module Glueby
118
151
  tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
119
152
  output = funding_tx.outputs.first
120
153
 
121
- receiver_script = Tapyrus::Script.parse_from_payload(output.script_pubkey.to_payload)
154
+ receiver_script = Tapyrus::Script.parse_from_addr(issuer.internal_wallet.receive_address)
122
155
  receiver_colored_script = receiver_script.add_color(color_id)
123
156
  add_split_output(tx, amount, split, receiver_colored_script)
124
157
 
125
158
  fee = fee_estimator.fee(FeeEstimator.dummy_tx(tx))
126
- fill_change_tpc(tx, issuer, output.value - fee)
159
+
127
160
  prev_txs = [{
128
161
  txid: funding_tx.txid,
129
162
  vout: 0,
130
163
  scriptPubKey: output.script_pubkey.to_hex,
131
164
  amount: output.value
132
165
  }]
166
+
167
+ if Glueby.configuration.use_utxo_provider?
168
+ utxo_provider = UtxoProvider.instance
169
+ tx, fee, input_amount, provided_utxos = utxo_provider.fill_inputs(
170
+ tx,
171
+ target_amount: 0,
172
+ current_amount: output.value,
173
+ fee_estimator: fee_estimator
174
+ )
175
+ prev_txs.concat(provided_utxos)
176
+ else
177
+ # TODO: Support the case of providing UTXOs from sender's wallet.
178
+ input_amount = output.value
179
+ end
180
+
181
+ fill_change_tpc(tx, issuer, input_amount - fee)
182
+
183
+ UtxoProvider.instance.wallet.sign_tx(tx, prev_txs) if Glueby.configuration.use_utxo_provider?
133
184
  issuer.internal_wallet.sign_tx(tx, prev_txs)
134
185
  end
135
186
 
136
- def create_transfer_tx(funding_tx:nil, color_id:, sender:, receiver_address:, amount:, fee_estimator: FeeEstimator::Fixed.new, only_finalized: true)
187
+ def create_transfer_tx(color_id:, sender:, receiver_address:, amount:, fee_estimator: FeeEstimator::Fixed.new, only_finalized: true)
137
188
  receivers = [{ address: receiver_address, amount: amount }]
138
189
  create_multi_transfer_tx(
139
- funding_tx: funding_tx,
140
190
  color_id: color_id,
141
191
  sender: sender,
142
192
  receivers: receivers,
@@ -145,7 +195,7 @@ module Glueby
145
195
  )
146
196
  end
147
197
 
148
- def create_multi_transfer_tx(funding_tx:nil, color_id:, sender:, receivers:, fee_estimator: FeeEstimator::Fixed.new, only_finalized: true)
198
+ def create_multi_transfer_tx(color_id:, sender:, receivers:, fee_estimator: FeeEstimator::Fixed.new, only_finalized: true)
149
199
  tx = Tapyrus::Tx.new
150
200
 
151
201
  amount = receivers.reduce(0) { |sum, r| sum + r[:amount].to_i }
@@ -162,32 +212,30 @@ module Glueby
162
212
  fill_change_token(tx, sender, sum_token - amount, color_id)
163
213
 
164
214
  fee = fee_estimator.fee(FeeEstimator.dummy_tx(tx))
165
- sum_tpc = if funding_tx
166
- out_point = Tapyrus::OutPoint.from_txid(funding_tx.txid, 0)
167
- tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
168
- funding_tx.outputs.first.value
215
+
216
+ # Fill inputs for paying fee
217
+ prev_txs = []
218
+ if Glueby.configuration.use_utxo_provider?
219
+ utxo_provider = UtxoProvider.instance
220
+ tx, fee, sum_tpc, provided_utxos = utxo_provider.fill_inputs(
221
+ tx,
222
+ target_amount: 0,
223
+ current_amount: 0,
224
+ fee_estimator: fee_estimator
225
+ )
226
+ prev_txs.concat(provided_utxos)
169
227
  else
228
+ # TODO: Support the case of increasing fee by adding multiple inputs
170
229
  sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee, nil, only_finalized)
171
230
  fill_input(tx, outputs)
172
- sum_tpc
173
231
  end
174
232
 
175
233
  fill_change_tpc(tx, sender, sum_tpc - fee)
176
- prev_txs = if funding_tx
177
- output = funding_tx.outputs.first
178
- [{
179
- txid: funding_tx.txid,
180
- vout: 0,
181
- scriptPubKey: output.script_pubkey.to_hex,
182
- amount: output.value
183
- }]
184
- else
185
- []
186
- end
234
+ UtxoProvider.instance.wallet.sign_tx(tx, prev_txs) if Glueby.configuration.use_utxo_provider?
187
235
  sender.internal_wallet.sign_tx(tx, prev_txs)
188
236
  end
189
237
 
190
- def create_burn_tx(funding_tx:nil, color_id:, sender:, amount: 0, fee_estimator: FeeEstimator::Fixed.new, only_finalized: true)
238
+ def create_burn_tx(color_id:, sender:, amount: 0, fee_estimator: FeeEstimator::Fixed.new, only_finalized: true)
191
239
  tx = Tapyrus::Tx.new
192
240
 
193
241
  utxos = sender.internal_wallet.list_unspent(only_finalized)
@@ -195,32 +243,27 @@ module Glueby
195
243
  fill_input(tx, outputs)
196
244
 
197
245
  fill_change_token(tx, sender, sum_token - amount, color_id) if amount.positive?
198
-
199
246
  fee = fee_estimator.fee(FeeEstimator.dummy_tx(tx))
200
247
 
201
- sum_tpc = if funding_tx
202
- out_point = Tapyrus::OutPoint.from_txid(funding_tx.txid, 0)
203
- tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
204
- funding_tx.outputs.first.value
248
+ provided_utxos = []
249
+ if Glueby.configuration.use_utxo_provider?
250
+ # When it burns all the amount of the color id, burn tx is not going to have any output
251
+ # because change outputs are not necessary, though such a transaction is not 'standard' and would be rejected by the Tapyrus node.
252
+ # UtxoProvider#fill_inputs method provides an extra output with a DUST_LIMIT value in this case
253
+ # , so that it created at least one output to the burn tx.
254
+ tx, fee, sum_tpc, provided_utxos = UtxoProvider.instance.fill_inputs(
255
+ tx,
256
+ target_amount: 0,
257
+ current_amount: 0,
258
+ fee_estimator: fee_estimator
259
+ )
205
260
  else
206
261
  sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee + DUST_LIMIT, nil, only_finalized)
207
262
  fill_input(tx, outputs)
208
- sum_tpc
209
263
  end
210
-
211
264
  fill_change_tpc(tx, sender, sum_tpc - fee)
212
- prev_txs = if funding_tx
213
- output = funding_tx.outputs.first
214
- [{
215
- txid: funding_tx.txid,
216
- vout: 0,
217
- scriptPubKey: output.script_pubkey.to_hex,
218
- amount: output.value
219
- }]
220
- else
221
- []
222
- end
223
- sender.internal_wallet.sign_tx(tx, prev_txs)
265
+ UtxoProvider.instance.wallet.sign_tx(tx, provided_utxos) if Glueby.configuration.use_utxo_provider?
266
+ sender.internal_wallet.sign_tx(tx, provided_utxos)
224
267
  end
225
268
 
226
269
  def add_split_output(tx, amount, split, script_pubkey)
@@ -245,7 +288,7 @@ module Glueby
245
288
  return unless change.positive?
246
289
 
247
290
  if Glueby.configuration.use_utxo_provider?
248
- change_script = Tapyrus::Script.parse_from_addr(UtxoProvider.new.wallet.change_address)
291
+ change_script = Tapyrus::Script.parse_from_addr(UtxoProvider.instance.wallet.change_address)
249
292
  else
250
293
  change_script = Tapyrus::Script.parse_from_addr(wallet.internal_wallet.change_address)
251
294
  end
@@ -294,14 +337,8 @@ module Glueby
294
337
  # The amount of output in funding tx
295
338
  # It returns same amount with FeeEstimator::Fixed's fixed fee. Because it is enough for paying fee for consumer
296
339
  # transactions of the funding transactions.
297
- #
298
- # @option [Boolean] need_value_for_change_output If it is true, adds more value than the fee for producing change output.
299
- def funding_tx_amount(need_value_for_change_output: false)
300
- if need_value_for_change_output
301
- FeeEstimator::Fixed.new.fixed_fee + DUST_LIMIT
302
- else
303
- FeeEstimator::Fixed.new.fixed_fee
304
- end
340
+ def funding_tx_amount
341
+ FeeEstimator::Fixed.new.fixed_fee
305
342
  end
306
343
  end
307
344
  end