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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +3 -4
- data/README.md +3 -3
- data/glueby.gemspec +2 -1
- data/lib/generators/glueby/contract/templates/initializer.rb.erb +8 -8
- data/lib/generators/glueby/contract/templates/key_table.rb.erb +16 -16
- data/lib/generators/glueby/contract/templates/timestamp_table.rb.erb +2 -0
- data/lib/generators/glueby/contract/templates/token_metadata_table.rb.erb +13 -0
- data/lib/generators/glueby/contract/templates/utxo_table.rb.erb +1 -0
- data/lib/generators/glueby/contract/{reissuable_token_generator.rb → token_generator.rb} +12 -1
- data/lib/glueby/block_syncer.rb +97 -97
- data/lib/glueby/configuration.rb +2 -0
- data/lib/glueby/contract/active_record/timestamp.rb +9 -2
- data/lib/glueby/contract/active_record/token_metadata.rb +8 -0
- data/lib/glueby/contract/active_record.rb +1 -0
- data/lib/glueby/contract/fee_estimator/auto.rb +9 -1
- data/lib/glueby/contract/fee_estimator.rb +12 -5
- data/lib/glueby/contract/timestamp/syncer.rb +1 -1
- data/lib/glueby/contract/token.rb +161 -80
- data/lib/glueby/contract/tx_builder.rb +104 -67
- data/lib/glueby/fee_provider/tasks.rb +4 -1
- data/lib/glueby/internal/wallet/abstract_wallet_adapter.rb +26 -0
- data/lib/glueby/internal/wallet/active_record/key.rb +2 -2
- data/lib/glueby/internal/wallet/active_record/utxo.rb +9 -0
- data/lib/glueby/internal/wallet/active_record_wallet_adapter.rb +22 -9
- data/lib/glueby/internal/wallet/errors.rb +1 -0
- data/lib/glueby/internal/wallet/mysql_wallet_adapter.rb +17 -0
- data/lib/glueby/internal/wallet/tapyrus_core_wallet_adapter.rb +1 -1
- data/lib/glueby/internal/wallet.rb +16 -0
- data/lib/glueby/util/digest.rb +23 -0
- data/lib/glueby/utxo_provider/tasks.rb +3 -1
- data/lib/glueby/utxo_provider.rb +56 -9
- data/lib/glueby/version.rb +1 -1
- data/lib/tasks/glueby/block_syncer.rake +7 -0
- 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 [
|
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
|
90
|
-
|
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
|
-
|
110
|
-
funding_tx = issuer
|
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
|
-
|
113
|
-
|
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
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
126
|
-
funding_tx = issuer
|
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
|
-
|
129
|
-
|
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
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
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
|
-
|
158
|
-
|
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
|
-
|
164
|
-
|
165
|
-
|
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
|
-
|
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
|
-
#
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
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
|
-
|
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,
|
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.
|
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
|
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
|
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.
|
48
|
-
|
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
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
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.
|
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
|
-
|
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(
|
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(
|
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
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
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
|
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(
|
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
|
-
|
202
|
-
|
203
|
-
tx
|
204
|
-
|
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
|
-
|
213
|
-
|
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.
|
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
|
-
|
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
|