glueby 1.1.2 → 1.2.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -48,9 +48,6 @@ module Glueby
48
48
  # token.metadata
49
49
  # => "metadata"
50
50
  class Token
51
- include Glueby::Contract::TxBuilder
52
- extend Glueby::Contract::TxBuilder
53
-
54
51
  class << self
55
52
  # Issue new token with specified amount and token type.
56
53
  # REISSUABLE token can be reissued with #reissue! method, and
@@ -112,92 +109,132 @@ module Glueby
112
109
  end
113
110
 
114
111
  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
112
+ txb = Internal::ContractBuilder.new(
113
+ sender_wallet: issuer.internal_wallet,
114
+ fee_estimator: fee_estimator,
115
+ use_auto_fulfill_inputs: true
116
+ )
117
+
118
+ if metadata
119
+ txb.add_p2c_utxo_to!(
120
+ metadata: metadata,
121
+ amount: FeeEstimator::Fixed.new.fixed_fee,
122
+ only_finalized: only_finalized?,
123
+ fee_estimator: Contract::FeeEstimator::Fixed.new
124
+ )
125
+ else
126
+ txb.add_utxo_to!(
127
+ address: issuer.internal_wallet.receive_address,
128
+ amount: FeeEstimator::Fixed.new.fixed_fee,
129
+ only_finalized: only_finalized?,
130
+ fee_estimator: Contract::FeeEstimator::Fixed.new
131
+ )
132
+ end
116
133
 
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?)
134
+ funding_tx = txb.prev_txs.first
122
135
  script_pubkey = funding_tx.outputs.first.script_pubkey
123
136
  color_id = Tapyrus::Color::ColorIdentifier.reissuable(script_pubkey)
124
137
 
125
- ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
126
- funding_tx = issuer.internal_wallet.broadcast(funding_tx)
127
- end
128
-
129
138
  ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
130
139
  # Store the script_pubkey for reissue the token.
131
- Glueby::Contract::AR::ReissuableToken.create!(color_id: color_id.to_hex, script_pubkey: script_pubkey.to_hex)
140
+ Glueby::Contract::AR::ReissuableToken.create!(
141
+ color_id: color_id.to_hex,
142
+ script_pubkey: script_pubkey.to_hex
143
+ )
132
144
 
133
- tx = create_issue_tx_for_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount, split: split, fee_estimator: fee_estimator)
134
145
  if metadata
146
+ p2c_utxo = txb.p2c_utxos.first
135
147
  Glueby::Contract::AR::TokenMetadata.create!(
136
148
  color_id: color_id.to_hex,
137
149
  metadata: metadata,
138
- p2c_address: p2c_address,
139
- payment_base: payment_base
150
+ p2c_address: p2c_utxo[:p2c_address],
151
+ payment_base: p2c_utxo[:payment_base]
140
152
  )
141
- tx = sign_to_p2c_output(issuer, tx, funding_tx, payment_base, metadata)
142
153
  end
154
+
155
+ tx = txb.reissuable_split(script_pubkey, issuer.internal_wallet.receive_address, amount, split)
156
+ .build
143
157
  tx = issuer.internal_wallet.broadcast(tx)
144
158
  [[funding_tx, tx], color_id]
145
159
  end
146
160
  end
147
161
 
148
162
  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
163
+ issue_token_from_out_point(
164
+ Tapyrus::Color::TokenTypes::NON_REISSUABLE,
165
+ issuer: issuer,
166
+ amount: amount,
167
+ split: split,
168
+ fee_estimator: fee_estimator,
169
+ metadata: metadata
170
+ )
171
+ end
156
172
 
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)
173
+ def issue_nft_token(issuer:, metadata: nil)
174
+ issue_token_from_out_point(
175
+ Tapyrus::Color::TokenTypes::NFT,
176
+ issuer: issuer,
177
+ amount: 1,
178
+ split: 1,
179
+ fee_estimator: Contract::FeeEstimator::Fixed.new,
180
+ metadata: metadata
181
+ )
182
+ end
171
183
 
172
- if funding_tx
173
- [[funding_tx, tx], color_id]
174
- else
175
- [[tx], color_id]
176
- end
184
+ def issue_token_from_out_point(token_type, issuer:, amount:, split: 1, fee_estimator: , metadata: nil)
185
+ txb = Internal::ContractBuilder.new(
186
+ sender_wallet: issuer.internal_wallet,
187
+ fee_estimator: fee_estimator,
188
+ use_auto_fulfill_inputs: true
189
+ )
190
+
191
+ funding_tx = nil
192
+
193
+ if metadata
194
+ txb.add_p2c_utxo_to!(
195
+ metadata: metadata,
196
+ amount: FeeEstimator::Fixed.new.fixed_fee,
197
+ only_finalized: only_finalized?,
198
+ fee_estimator: Contract::FeeEstimator::Fixed.new
199
+ )
200
+ funding_tx = txb.prev_txs.first
201
+ elsif Glueby.configuration.use_utxo_provider?
202
+ txb.add_utxo_to!(
203
+ address: issuer.internal_wallet.receive_address,
204
+ amount: FeeEstimator::Fixed.new.fixed_fee,
205
+ only_finalized: only_finalized?,
206
+ fee_estimator: Contract::FeeEstimator::Fixed.new
207
+ )
208
+ funding_tx = txb.prev_txs.first
209
+ else
210
+ # Here need to add just one UTXO to derive color_id from the UTXO out-point.
211
+ _, outputs = issuer.internal_wallet.collect_uncolored_outputs(1, nil, true)
212
+ txb.add_utxo(outputs.first)
177
213
  end
178
- end
179
214
 
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
215
+ utxo = txb.utxos.first
216
+ out_point = Tapyrus::OutPoint.from_txid(utxo[:txid], utxo[:index])
217
+
218
+ case token_type
219
+ when Tapyrus::Color::TokenTypes::NON_REISSUABLE
220
+ color_id = Tapyrus::Color::ColorIdentifier.non_reissuable(out_point)
221
+ tx = txb.non_reissuable_split(out_point, issuer.internal_wallet.receive_address, amount, split)
222
+ .build
223
+ when Tapyrus::Color::TokenTypes::NFT
224
+ color_id = Tapyrus::Color::ColorIdentifier.nft(out_point)
225
+ tx = txb.nft(out_point, issuer.internal_wallet.receive_address)
226
+ .build
187
227
  end
188
228
 
189
229
  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
230
  if metadata
231
+ p2c_utxo = txb.p2c_utxos.first
194
232
  Glueby::Contract::AR::TokenMetadata.create!(
195
233
  color_id: color_id.to_hex,
196
234
  metadata: metadata,
197
- p2c_address: p2c_address,
198
- payment_base: payment_base
235
+ p2c_address: p2c_utxo[:p2c_address],
236
+ payment_base: p2c_utxo[:payment_base]
199
237
  )
200
- tx = sign_to_p2c_output(issuer, tx, funding_tx, payment_base, metadata)
201
238
  end
202
239
  tx = issuer.internal_wallet.broadcast(tx)
203
240
 
@@ -208,6 +245,14 @@ module Glueby
208
245
  end
209
246
  end
210
247
  end
248
+
249
+ # Add dummy inputs and outputs to tx for issue non-reissuable transaction and nft transaction
250
+ def dummy_issue_tx_from_out_point
251
+ tx = Tapyrus::Tx.new
252
+ receiver_colored_script = Tapyrus::Script.parse_from_payload('21c20000000000000000000000000000000000000000000000000000000000000000bc76a914000000000000000000000000000000000000000088ac'.htb)
253
+ tx.outputs << Tapyrus::TxOut.new(value: 0, script_pubkey: receiver_colored_script)
254
+ FeeEstimator.dummy_tx(tx)
255
+ end
211
256
  end
212
257
 
213
258
  attr_reader :color_id
@@ -230,12 +275,33 @@ module Glueby
230
275
  token_metadata = Glueby::Contract::AR::TokenMetadata.find_by(color_id: color_id.to_hex)
231
276
  raise Glueby::Contract::Errors::UnknownScriptPubkey unless valid_reissuer?(wallet: issuer, token_metadata: token_metadata)
232
277
 
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)
278
+ txb = Internal::ContractBuilder.new(
279
+ sender_wallet: issuer.internal_wallet,
280
+ fee_estimator: fee_estimator,
281
+ use_auto_fulfill_inputs: true
282
+ )
283
+
236
284
  if token_metadata
237
- tx = Token.sign_to_p2c_output(issuer, tx, funding_tx, token_metadata.payment_base, token_metadata.metadata)
285
+ txb.add_p2c_utxo_to!(
286
+ metadata: metadata,
287
+ amount: FeeEstimator::Fixed.new.fixed_fee,
288
+ p2c_address: token_metadata.p2c_address,
289
+ payment_base: token_metadata.payment_base,
290
+ only_finalized: only_finalized?,
291
+ fee_estimator: Contract::FeeEstimator::Fixed.new
292
+ )
293
+ else
294
+ txb.add_utxo_to!(
295
+ address: @script_pubkey.to_addr,
296
+ amount: FeeEstimator::Fixed.new.fixed_fee,
297
+ only_finalized: only_finalized?,
298
+ fee_estimator: Contract::FeeEstimator::Fixed.new
299
+ )
238
300
  end
301
+
302
+ tx = txb.reissuable_split(@script_pubkey, issuer.internal_wallet.receive_address, amount, split)
303
+ .build
304
+
239
305
  tx = issuer.internal_wallet.broadcast(tx)
240
306
 
241
307
  [color_id, tx]
@@ -252,18 +318,13 @@ module Glueby
252
318
  # @raise [InsufficientTokens] if wallet does not have enough token to send.
253
319
  # @raise [InvalidAmount] if amount is not positive integer.
254
320
  def transfer!(sender:, receiver_address:, amount: 1, fee_estimator: FeeEstimator::Fixed.new)
255
- raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
256
-
257
- tx = create_transfer_tx(
258
- color_id: color_id,
321
+ multi_transfer!(
259
322
  sender: sender,
260
- receiver_address: receiver_address,
261
- amount: amount,
262
- only_finalized: only_finalized?,
263
- fee_estimator: fee_estimator
264
- )
265
- sender.internal_wallet.broadcast(tx)
266
- [color_id, tx]
323
+ receivers: [{
324
+ address: receiver_address,
325
+ amount: amount
326
+ }],
327
+ fee_estimator: fee_estimator)
267
328
  end
268
329
 
269
330
  # Send the tokens to multiple wallets
@@ -280,14 +341,20 @@ module Glueby
280
341
  raise Glueby::Contract::Errors::InvalidAmount unless r[:amount].positive?
281
342
  end
282
343
 
283
- tx = create_multi_transfer_tx(
284
- color_id: color_id,
285
- sender: sender,
286
- receivers: receivers,
287
- only_finalized: only_finalized?,
288
- fee_estimator: fee_estimator
289
- )
290
- sender.internal_wallet.broadcast(tx)
344
+ txb = Internal::ContractBuilder
345
+ .new(
346
+ sender_wallet: sender.internal_wallet,
347
+ fee_estimator: fee_estimator,
348
+ use_unfinalized_utxo: !only_finalized?,
349
+ use_auto_fulfill_inputs: true
350
+ )
351
+ .change_address(sender.internal_wallet.receive_address, color_id)
352
+
353
+ receivers.each do |r|
354
+ txb.pay(r[:address], r[:amount].to_i, color_id)
355
+ end
356
+
357
+ tx = sender.internal_wallet.broadcast(txb.build)
291
358
  [color_id, tx]
292
359
  end
293
360
 
@@ -306,7 +373,17 @@ module Glueby
306
373
  raise Glueby::Contract::Errors::InsufficientTokens unless balance
307
374
  raise Glueby::Contract::Errors::InsufficientTokens if balance < amount
308
375
 
309
- tx = create_burn_tx(color_id: color_id, sender: sender, amount: amount, only_finalized: only_finalized?, fee_estimator: fee_estimator)
376
+ tx = Internal::ContractBuilder
377
+ .new(
378
+ sender_wallet: sender.internal_wallet,
379
+ fee_estimator: fee_estimator,
380
+ use_unfinalized_utxo: !only_finalized?,
381
+ use_auto_fulfill_inputs: true
382
+ )
383
+ .burn(amount, color_id)
384
+ .change_address(sender.internal_wallet.receive_address, color_id)
385
+ .build
386
+
310
387
  sender.internal_wallet.broadcast(tx)
311
388
  end
312
389
 
@@ -314,10 +391,10 @@ module Glueby
314
391
  # @param wallet [Glueby::Wallet]
315
392
  # @return [Integer] amount of utxo value associated with this token.
316
393
  def amount(wallet:)
317
- # collect utxo associated with this address
318
- utxos = wallet.internal_wallet.list_unspent(only_finalized?)
319
- _, results = collect_colored_outputs(utxos, color_id)
320
- results.sum { |result| result[:amount] }
394
+ amount, _utxos = wallet
395
+ .internal_wallet
396
+ .collect_colored_outputs(color_id, nil, nil, only_finalized?)
397
+ amount
321
398
  end
322
399
 
323
400
  # Return metadata for this token
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Glueby
4
4
  module Contract
5
+ # @deprecated Use Glueby::Internal::ContractBuilder instead. This class will be removed in the future.
5
6
  module TxBuilder
6
7
  def receive_address(wallet:)
7
8
  wallet.receive_address