glueby 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|