glueby 0.7.0 → 0.9.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/README.md +5 -0
- data/glueby.gemspec +1 -1
- data/lib/glueby/active_record/system_information.rb +88 -1
- data/lib/glueby/configuration.rb +1 -1
- data/lib/glueby/contract/active_record/timestamp.rb +2 -2
- data/lib/glueby/contract/errors.rb +12 -9
- data/lib/glueby/contract/token.rb +51 -22
- data/lib/glueby/contract/tx_builder.rb +41 -22
- data/lib/glueby/fee_provider.rb +2 -2
- data/lib/glueby/internal/wallet/errors.rb +6 -6
- data/lib/glueby/utxo_provider/tasks.rb +15 -37
- data/lib/glueby/utxo_provider.rb +40 -9
- data/lib/glueby/version.rb +1 -1
- data/lib/glueby.rb +5 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5cae8a0e06be77affdb1895724a26137b5fde2bab8cc27fb73d9aa45f2c351c5
|
4
|
+
data.tar.gz: 2cc677f69e1cc1ad3e6468662e476b4bd5082b0e765f15f44b6b63426b2663f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e0a80b28635f37a37b4d37eac3fc3bc697bfb3ce580a8c481c60e40fe7776bfcfcb24720d33771e5d73d19c745d03afbee7b380dedd3d0ec51b7258bbb9363b
|
7
|
+
data.tar.gz: fd3d0edae5b99212cea7e4a710dd8f52173e1b707617beb881e7f9715afac575e76d5abc18b291f7defa62c7acb4c4e5772163a0953126a2f3fcfeb6a72aa953
|
data/README.md
CHANGED
@@ -457,6 +457,11 @@ Glueby.configure do |config|
|
|
457
457
|
end
|
458
458
|
```
|
459
459
|
|
460
|
+
## Error handling
|
461
|
+
|
462
|
+
Glueby has base error classes like `Glueby::Error` and `Glueby::ArgumentError`.
|
463
|
+
`Glueby::Error` is the base class for the all errors that are raises in the glueby.
|
464
|
+
`Glueby::ArgumentError` is the error class for argument errors in public contracts. This notifies the arguments is something wrong to glueby library user-side.
|
460
465
|
|
461
466
|
## Development
|
462
467
|
|
data/glueby.gemspec
CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
27
|
spec.require_paths = ["lib"]
|
28
28
|
|
29
|
-
spec.add_runtime_dependency 'tapyrus', '>= 0.2.
|
29
|
+
spec.add_runtime_dependency 'tapyrus', '>= 0.2.13'
|
30
30
|
spec.add_runtime_dependency 'activerecord', '~> 6.1.3'
|
31
31
|
spec.add_development_dependency 'sqlite3'
|
32
32
|
spec.add_development_dependency 'rails', '~> 6.1.3'
|
@@ -3,13 +3,100 @@ module Glueby
|
|
3
3
|
class SystemInformation < ::ActiveRecord::Base
|
4
4
|
|
5
5
|
def self.synced_block_height
|
6
|
-
|
6
|
+
find_by(info_key: "synced_block_number")
|
7
|
+
end
|
8
|
+
|
9
|
+
# Return if wallet allows to use only finalized utxo.
|
10
|
+
# @return [Boolean] true if wallet allows to use only finalized utxo, otherwise false.
|
11
|
+
def self.use_only_finalized_utxo?
|
12
|
+
find_by(info_key: "use_only_finalized_utxo")&.int_value != 0
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
# Set use_only_finalized_utxo
|
17
|
+
# @param [Boolean] status status of whether to use only finalized utxo
|
18
|
+
def self.set_use_only_finalized_utxo(status)
|
19
|
+
current = find_by(info_key: "use_only_finalized_utxo")
|
20
|
+
if current
|
21
|
+
current.update!(info_value: boolean_to_string(status))
|
22
|
+
else
|
23
|
+
create!(
|
24
|
+
info_key: "use_only_finalized_utxo",
|
25
|
+
info_value: boolean_to_string(status)
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return default value of the utxo provider
|
31
|
+
# @return [Integer] default value of utxo provider
|
32
|
+
def self.utxo_provider_default_value
|
33
|
+
find_by(info_key: "utxo_provider_default_value")&.int_value
|
34
|
+
end
|
35
|
+
|
36
|
+
# Set utxo_provider_default_value
|
37
|
+
# @param [Integer] value default value for utxo provider
|
38
|
+
def self.set_utxo_provider_default_value(value)
|
39
|
+
current = find_by(info_key: "utxo_provider_default_value")
|
40
|
+
if current
|
41
|
+
current.update!(info_value: value)
|
42
|
+
else
|
43
|
+
create!(
|
44
|
+
info_key: "utxo_provider_default_value",
|
45
|
+
info_value: value
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Return pool size of the utxo provider
|
51
|
+
# @return [Integer] pool size of utxo provider
|
52
|
+
def self.utxo_provider_pool_size
|
53
|
+
find_by(info_key: "utxo_provider_pool_size")&.int_value
|
54
|
+
end
|
55
|
+
|
56
|
+
# Set utxo_provider_pool_size
|
57
|
+
# @param [Integer] size pool size of the utxo provider
|
58
|
+
def self.set_utxo_provider_pool_size(size)
|
59
|
+
current = find_by(info_key: "utxo_provider_pool_size")
|
60
|
+
if current
|
61
|
+
current.update!(info_value: size)
|
62
|
+
else
|
63
|
+
create!(
|
64
|
+
info_key: "utxo_provider_pool_size",
|
65
|
+
info_value: size
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# If return timestamp is to be executed immediately
|
71
|
+
# @return [Boolean] true status of broadcast_on_background
|
72
|
+
def self.broadcast_on_background?
|
73
|
+
find_by(info_key: "broadcast_on_background")&.int_value != 0
|
74
|
+
end
|
75
|
+
|
76
|
+
# Set the status of broadcast_on_background
|
77
|
+
# @param [Boolean] status status of broadcast_on_background
|
78
|
+
def self.set_broadcast_on_background(status)
|
79
|
+
current = find_by(info_key: "broadcast_on_background")
|
80
|
+
if current
|
81
|
+
current.update!(info_value: boolean_to_string(status))
|
82
|
+
else
|
83
|
+
create!(
|
84
|
+
info_key: "broadcast_on_background",
|
85
|
+
info_value: boolean_to_string(status)
|
86
|
+
)
|
87
|
+
end
|
7
88
|
end
|
8
89
|
|
9
90
|
def int_value
|
10
91
|
info_value.to_i
|
11
92
|
end
|
12
93
|
|
94
|
+
private
|
95
|
+
|
96
|
+
def self.boolean_to_string(status)
|
97
|
+
status ? "1" : "0"
|
98
|
+
end
|
99
|
+
|
13
100
|
end
|
14
101
|
end
|
15
102
|
end
|
data/lib/glueby/configuration.rb
CHANGED
@@ -35,16 +35,16 @@ module Glueby
|
|
35
35
|
if funding_tx
|
36
36
|
::ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
|
37
37
|
wallet.internal_wallet.broadcast(funding_tx)
|
38
|
-
logger.info("funding tx was broadcasted(id=#{id}, funding_tx.txid=#{funding_tx.txid})")
|
39
38
|
end
|
39
|
+
logger.info("funding tx was broadcasted(id=#{id}, funding_tx.txid=#{funding_tx.txid})")
|
40
40
|
end
|
41
41
|
::ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
|
42
42
|
wallet.internal_wallet.broadcast(tx) do |tx|
|
43
43
|
assign_attributes(txid: tx.txid, status: :unconfirmed, p2c_address: p2c_address, payment_base: payment_base)
|
44
44
|
save!
|
45
45
|
end
|
46
|
-
logger.info("timestamp tx was broadcasted (id=#{id}, txid=#{tx.txid})")
|
47
46
|
end
|
47
|
+
logger.info("timestamp tx was broadcasted (id=#{id}, txid=#{tx.txid})")
|
48
48
|
true
|
49
49
|
rescue => e
|
50
50
|
logger.error("failed to broadcast (id=#{id}, reason=#{e.message})")
|
@@ -1,15 +1,18 @@
|
|
1
1
|
module Glueby
|
2
2
|
module Contract
|
3
3
|
module Errors
|
4
|
-
class InsufficientFunds <
|
5
|
-
class InsufficientTokens <
|
6
|
-
class
|
7
|
-
|
8
|
-
|
9
|
-
class
|
10
|
-
class
|
11
|
-
class
|
12
|
-
class
|
4
|
+
class InsufficientFunds < Error; end
|
5
|
+
class InsufficientTokens < Error; end
|
6
|
+
class TxAlreadyBroadcasted < Error; end
|
7
|
+
|
8
|
+
# Argument Errors
|
9
|
+
class InvalidAmount < ArgumentError; end
|
10
|
+
class InvalidSplit < ArgumentError; end
|
11
|
+
class InvalidTokenType < ArgumentError; end
|
12
|
+
class InvalidTimestampType < ArgumentError; end
|
13
|
+
class UnsupportedTokenType < ArgumentError; end
|
14
|
+
class UnknownScriptPubkey < ArgumentError; end
|
15
|
+
class UnsupportedDigestType < ArgumentError; end
|
13
16
|
end
|
14
17
|
end
|
15
18
|
end
|
@@ -56,18 +56,21 @@ module Glueby
|
|
56
56
|
# @param issuer [Glueby::Wallet]
|
57
57
|
# @param token_type [TokenTypes]
|
58
58
|
# @param amount [Integer]
|
59
|
+
# @param split [Integer] The tx outputs should be split by specified number.
|
59
60
|
# @return [Array<token, Array<tx>>] Tuple of tx array and token object
|
60
61
|
# @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
|
61
62
|
# @raise [InvalidAmount] if amount is not positive integer.
|
63
|
+
# @raise [InvalidSplit] if split is greater than 1 for NFT token.
|
62
64
|
# @raise [UnspportedTokenType] if token is not supported.
|
63
|
-
def issue!(issuer:, token_type: Tapyrus::Color::TokenTypes::REISSUABLE, amount: 1)
|
65
|
+
def issue!(issuer:, token_type: Tapyrus::Color::TokenTypes::REISSUABLE, amount: 1, split: 1)
|
64
66
|
raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
|
67
|
+
raise Glueby::Contract::Errors::InvalidSplit if token_type == Tapyrus::Color::TokenTypes::NFT && split > 1
|
65
68
|
|
66
69
|
txs, color_id = case token_type
|
67
70
|
when Tapyrus::Color::TokenTypes::REISSUABLE
|
68
|
-
issue_reissuable_token(issuer: issuer, amount: amount)
|
71
|
+
issue_reissuable_token(issuer: issuer, amount: amount, split: split)
|
69
72
|
when Tapyrus::Color::TokenTypes::NON_REISSUABLE
|
70
|
-
issue_non_reissuable_token(issuer: issuer, amount: amount)
|
73
|
+
issue_non_reissuable_token(issuer: issuer, amount: amount, split: split)
|
71
74
|
when Tapyrus::Color::TokenTypes::NFT
|
72
75
|
issue_nft_token(issuer: issuer)
|
73
76
|
else
|
@@ -77,29 +80,36 @@ module Glueby
|
|
77
80
|
[new(color_id: color_id), txs]
|
78
81
|
end
|
79
82
|
|
83
|
+
def only_finalized?
|
84
|
+
Glueby::AR::SystemInformation.use_only_finalized_utxo?
|
85
|
+
end
|
86
|
+
|
80
87
|
private
|
81
88
|
|
82
|
-
def issue_reissuable_token(issuer:, amount:)
|
83
|
-
funding_tx = create_funding_tx(wallet: issuer)
|
89
|
+
def issue_reissuable_token(issuer:, amount:, split: 1)
|
90
|
+
funding_tx = create_funding_tx(wallet: issuer, only_finalized: only_finalized?)
|
84
91
|
script_pubkey = funding_tx.outputs.first.script_pubkey
|
85
92
|
color_id = Tapyrus::Color::ColorIdentifier.reissuable(script_pubkey)
|
86
93
|
|
94
|
+
ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
|
95
|
+
funding_tx = issuer.internal_wallet.broadcast(funding_tx)
|
96
|
+
end
|
97
|
+
|
87
98
|
ActiveRecord::Base.transaction(joinable: false, requires_new: true) do
|
88
99
|
# Store the script_pubkey for reissue the token.
|
89
100
|
Glueby::Contract::AR::ReissuableToken.create!(color_id: color_id.to_hex, script_pubkey: script_pubkey.to_hex)
|
90
101
|
|
91
|
-
|
92
|
-
tx = create_issue_tx_for_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount)
|
102
|
+
tx = create_issue_tx_for_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount, split: split)
|
93
103
|
tx = issuer.internal_wallet.broadcast(tx)
|
94
104
|
[[funding_tx, tx], color_id]
|
95
105
|
end
|
96
106
|
end
|
97
107
|
|
98
|
-
def issue_non_reissuable_token(issuer:, amount:)
|
99
|
-
funding_tx = create_funding_tx(wallet: issuer) if Glueby.configuration.use_utxo_provider?
|
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?
|
100
110
|
funding_tx = issuer.internal_wallet.broadcast(funding_tx) if funding_tx
|
101
111
|
|
102
|
-
tx = create_issue_tx_for_non_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount)
|
112
|
+
tx = create_issue_tx_for_non_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount, split: split)
|
103
113
|
tx = issuer.internal_wallet.broadcast(tx)
|
104
114
|
|
105
115
|
out_point = tx.inputs.first.out_point
|
@@ -112,7 +122,7 @@ module Glueby
|
|
112
122
|
end
|
113
123
|
|
114
124
|
def issue_nft_token(issuer:)
|
115
|
-
funding_tx = create_funding_tx(wallet: issuer) if Glueby.configuration.use_utxo_provider?
|
125
|
+
funding_tx = create_funding_tx(wallet: issuer, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider?
|
116
126
|
funding_tx = issuer.internal_wallet.broadcast(funding_tx) if funding_tx
|
117
127
|
|
118
128
|
tx = create_issue_tx_for_nft_token(funding_tx: funding_tx, issuer: issuer)
|
@@ -134,19 +144,20 @@ module Glueby
|
|
134
144
|
# A wallet can issue the token only when it is REISSUABLE token.
|
135
145
|
# @param issuer [Glueby::Wallet]
|
136
146
|
# @param amount [Integer]
|
147
|
+
# @param split [Integer]
|
137
148
|
# @return [Array<String, tx>] Tuple of color_id and tx object
|
138
149
|
# @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
|
139
150
|
# @raise [InvalidAmount] if amount is not positive integer.
|
140
151
|
# @raise [InvalidTokenType] if token is not reissuable.
|
141
152
|
# @raise [UnknownScriptPubkey] when token is reissuable but it doesn't know script pubkey to issue token.
|
142
|
-
def reissue!(issuer:, amount:)
|
153
|
+
def reissue!(issuer:, amount:, split: 1)
|
143
154
|
raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
|
144
155
|
raise Glueby::Contract::Errors::InvalidTokenType unless token_type == Tapyrus::Color::TokenTypes::REISSUABLE
|
145
156
|
|
146
157
|
if validate_reissuer(wallet: issuer)
|
147
|
-
funding_tx = create_funding_tx(wallet: issuer, script: @script_pubkey)
|
158
|
+
funding_tx = create_funding_tx(wallet: issuer, script: @script_pubkey, only_finalized: only_finalized?)
|
148
159
|
funding_tx = issuer.internal_wallet.broadcast(funding_tx)
|
149
|
-
tx = create_reissue_tx(funding_tx: funding_tx, issuer: issuer, amount: amount, color_id: color_id)
|
160
|
+
tx = create_reissue_tx(funding_tx: funding_tx, issuer: issuer, amount: amount, color_id: color_id, split: split)
|
150
161
|
tx = issuer.internal_wallet.broadcast(tx)
|
151
162
|
|
152
163
|
[color_id, tx]
|
@@ -167,10 +178,17 @@ module Glueby
|
|
167
178
|
def transfer!(sender:, receiver_address:, amount: 1)
|
168
179
|
raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
|
169
180
|
|
170
|
-
funding_tx = create_funding_tx(wallet: sender) if Glueby.configuration.use_utxo_provider?
|
181
|
+
funding_tx = create_funding_tx(wallet: sender, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider?
|
171
182
|
funding_tx = sender.internal_wallet.broadcast(funding_tx) if funding_tx
|
172
183
|
|
173
|
-
tx = create_transfer_tx(
|
184
|
+
tx = create_transfer_tx(
|
185
|
+
funding_tx: funding_tx,
|
186
|
+
color_id: color_id,
|
187
|
+
sender: sender,
|
188
|
+
receiver_address: receiver_address,
|
189
|
+
amount: amount,
|
190
|
+
only_finalized: only_finalized?
|
191
|
+
)
|
174
192
|
sender.internal_wallet.broadcast(tx)
|
175
193
|
[color_id, tx]
|
176
194
|
end
|
@@ -187,10 +205,16 @@ module Glueby
|
|
187
205
|
receivers.each do |r|
|
188
206
|
raise Glueby::Contract::Errors::InvalidAmount unless r[:amount].positive?
|
189
207
|
end
|
190
|
-
funding_tx = create_funding_tx(wallet: sender) if Glueby.configuration.use_utxo_provider?
|
208
|
+
funding_tx = create_funding_tx(wallet: sender, only_finalized: only_finalized?) if Glueby.configuration.use_utxo_provider?
|
191
209
|
funding_tx = sender.internal_wallet.broadcast(funding_tx) if funding_tx
|
192
210
|
|
193
|
-
tx = create_multi_transfer_tx(
|
211
|
+
tx = create_multi_transfer_tx(
|
212
|
+
funding_tx: funding_tx,
|
213
|
+
color_id: color_id,
|
214
|
+
sender: sender,
|
215
|
+
receivers: receivers,
|
216
|
+
only_finalized: only_finalized?
|
217
|
+
)
|
194
218
|
sender.internal_wallet.broadcast(tx)
|
195
219
|
[color_id, tx]
|
196
220
|
end
|
@@ -205,7 +229,7 @@ module Glueby
|
|
205
229
|
# @raise [InvalidAmount] if amount is not positive integer.
|
206
230
|
def burn!(sender:, amount: 0)
|
207
231
|
raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
|
208
|
-
balance = sender.balances[color_id.to_hex]
|
232
|
+
balance = sender.balances(only_finalized?)[color_id.to_hex]
|
209
233
|
raise Glueby::Contract::Errors::InsufficientTokens unless balance
|
210
234
|
raise Glueby::Contract::Errors::InsufficientTokens if balance < amount
|
211
235
|
|
@@ -219,13 +243,14 @@ module Glueby
|
|
219
243
|
# because change outputs is not necessary. Transactions needs one output at least.
|
220
244
|
# At that time, set true to this option to get more value to be created change output to
|
221
245
|
# the tx.
|
222
|
-
need_value_for_change_output: burn_all_amount_flag
|
246
|
+
need_value_for_change_output: burn_all_amount_flag,
|
247
|
+
only_finalized: only_finalized?
|
223
248
|
)
|
224
249
|
end
|
225
250
|
|
226
251
|
funding_tx = sender.internal_wallet.broadcast(funding_tx) if funding_tx
|
227
252
|
|
228
|
-
tx = create_burn_tx(funding_tx: funding_tx, color_id: color_id, sender: sender, amount: amount)
|
253
|
+
tx = create_burn_tx(funding_tx: funding_tx, color_id: color_id, sender: sender, amount: amount, only_finalized: only_finalized?)
|
229
254
|
sender.internal_wallet.broadcast(tx)
|
230
255
|
end
|
231
256
|
|
@@ -234,7 +259,7 @@ module Glueby
|
|
234
259
|
# @return [Integer] amount of utxo value associated with this token.
|
235
260
|
def amount(wallet:)
|
236
261
|
# collect utxo associated with this address
|
237
|
-
utxos = wallet.internal_wallet.list_unspent
|
262
|
+
utxos = wallet.internal_wallet.list_unspent(only_finalized?)
|
238
263
|
_, results = collect_colored_outputs(utxos, color_id)
|
239
264
|
results.sum { |result| result[:amount] }
|
240
265
|
end
|
@@ -282,6 +307,10 @@ module Glueby
|
|
282
307
|
|
283
308
|
private
|
284
309
|
|
310
|
+
def only_finalized?
|
311
|
+
@only_finalized ||= Token.only_finalized?
|
312
|
+
end
|
313
|
+
|
285
314
|
# Verify that wallet is the issuer of the reissuable token
|
286
315
|
# reutrn [Boolean]
|
287
316
|
def validate_reissuer(wallet:)
|
@@ -8,7 +8,7 @@ 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: FixedFeeEstimator.new, need_value_for_change_output: false)
|
11
|
+
def create_funding_tx(wallet:, script: nil, fee_estimator: FixedFeeEstimator.new, need_value_for_change_output: false, only_finalized: true)
|
12
12
|
if Glueby.configuration.use_utxo_provider?
|
13
13
|
utxo_provider = UtxoProvider.new
|
14
14
|
script_pubkey = script ? script : Tapyrus::Script.parse_from_addr(wallet.internal_wallet.receive_address)
|
@@ -17,8 +17,8 @@ module Glueby
|
|
17
17
|
else
|
18
18
|
txb = Tapyrus::TxBuilder.new
|
19
19
|
fee = fee_estimator.fee(dummy_tx(txb.build))
|
20
|
-
|
21
|
-
sum, outputs = wallet.internal_wallet.collect_uncolored_outputs(
|
20
|
+
amount = fee + funding_tx_amount(need_value_for_change_output: need_value_for_change_output)
|
21
|
+
sum, outputs = wallet.internal_wallet.collect_uncolored_outputs(amount, nil, only_finalized)
|
22
22
|
outputs.each do |utxo|
|
23
23
|
txb.add_utxo({
|
24
24
|
script_pubkey: Tapyrus::Script.parse_from_payload(utxo[:script_pubkey].htb),
|
@@ -37,7 +37,7 @@ module Glueby
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
def create_issue_tx_for_reissuable_token(funding_tx:, issuer:, amount:, fee_estimator: FixedFeeEstimator.new)
|
40
|
+
def create_issue_tx_for_reissuable_token(funding_tx:, issuer:, amount:, split: 1, fee_estimator: FixedFeeEstimator.new)
|
41
41
|
tx = Tapyrus::Tx.new
|
42
42
|
|
43
43
|
out_point = Tapyrus::OutPoint.from_txid(funding_tx.txid, 0)
|
@@ -47,7 +47,8 @@ module Glueby
|
|
47
47
|
receiver_script = Tapyrus::Script.parse_from_payload(output.script_pubkey.to_payload)
|
48
48
|
color_id = Tapyrus::Color::ColorIdentifier.reissuable(receiver_script)
|
49
49
|
receiver_colored_script = receiver_script.add_color(color_id)
|
50
|
-
|
50
|
+
|
51
|
+
add_split_output(tx, amount, split, receiver_colored_script)
|
51
52
|
|
52
53
|
fee = fee_estimator.fee(dummy_tx(tx))
|
53
54
|
fill_change_tpc(tx, issuer, output.value - fee)
|
@@ -60,15 +61,15 @@ module Glueby
|
|
60
61
|
issuer.internal_wallet.sign_tx(tx, prev_txs)
|
61
62
|
end
|
62
63
|
|
63
|
-
def create_issue_tx_for_non_reissuable_token(funding_tx: nil, issuer:, amount:, fee_estimator: FixedFeeEstimator.new)
|
64
|
-
create_issue_tx_from_out_point(funding_tx: funding_tx, token_type: Tapyrus::Color::TokenTypes::NON_REISSUABLE, issuer: issuer, amount: amount, fee_estimator: fee_estimator)
|
64
|
+
def create_issue_tx_for_non_reissuable_token(funding_tx: nil, issuer:, amount:, split: 1, fee_estimator: FixedFeeEstimator.new, only_finalized: true)
|
65
|
+
create_issue_tx_from_out_point(funding_tx: funding_tx, token_type: Tapyrus::Color::TokenTypes::NON_REISSUABLE, issuer: issuer, amount: amount, split: split, fee_estimator: fee_estimator, only_finalized: only_finalized)
|
65
66
|
end
|
66
67
|
|
67
|
-
def create_issue_tx_for_nft_token(funding_tx: nil, issuer:, fee_estimator: FixedFeeEstimator.new)
|
68
|
-
create_issue_tx_from_out_point(funding_tx: funding_tx, token_type: Tapyrus::Color::TokenTypes::NFT, issuer: issuer, amount: 1, fee_estimator: fee_estimator)
|
68
|
+
def create_issue_tx_for_nft_token(funding_tx: nil, issuer:, fee_estimator: FixedFeeEstimator.new, only_finalized: true)
|
69
|
+
create_issue_tx_from_out_point(funding_tx: funding_tx, token_type: Tapyrus::Color::TokenTypes::NFT, issuer: issuer, amount: 1, fee_estimator: fee_estimator, only_finalized: only_finalized)
|
69
70
|
end
|
70
71
|
|
71
|
-
def create_issue_tx_from_out_point(funding_tx: nil, token_type:, issuer:, amount:, fee_estimator: FixedFeeEstimator.new)
|
72
|
+
def create_issue_tx_from_out_point(funding_tx: nil, token_type:, issuer:, amount:, split: 1, fee_estimator: FixedFeeEstimator.new, only_finalized: true)
|
72
73
|
tx = Tapyrus::Tx.new
|
73
74
|
|
74
75
|
fee = fee_estimator.fee(dummy_issue_tx_from_out_point)
|
@@ -77,7 +78,7 @@ module Glueby
|
|
77
78
|
tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
|
78
79
|
funding_tx.outputs.first.value
|
79
80
|
else
|
80
|
-
sum, outputs = issuer.internal_wallet.collect_uncolored_outputs(fee)
|
81
|
+
sum, outputs = issuer.internal_wallet.collect_uncolored_outputs(fee, nil, only_finalized)
|
81
82
|
fill_input(tx, outputs)
|
82
83
|
sum
|
83
84
|
end
|
@@ -93,7 +94,7 @@ module Glueby
|
|
93
94
|
|
94
95
|
receiver_script = Tapyrus::Script.parse_from_addr(issuer.internal_wallet.receive_address)
|
95
96
|
receiver_colored_script = receiver_script.add_color(color_id)
|
96
|
-
tx
|
97
|
+
add_split_output(tx, amount, split, receiver_colored_script)
|
97
98
|
|
98
99
|
fill_change_tpc(tx, issuer, sum - fee)
|
99
100
|
prev_txs = if funding_tx
|
@@ -110,7 +111,7 @@ module Glueby
|
|
110
111
|
issuer.internal_wallet.sign_tx(tx, prev_txs)
|
111
112
|
end
|
112
113
|
|
113
|
-
def create_reissue_tx(funding_tx:, issuer:, amount:, color_id:, fee_estimator: FixedFeeEstimator.new)
|
114
|
+
def create_reissue_tx(funding_tx:, issuer:, amount:, color_id:, split: 1, fee_estimator: FixedFeeEstimator.new)
|
114
115
|
tx = Tapyrus::Tx.new
|
115
116
|
|
116
117
|
out_point = Tapyrus::OutPoint.from_txid(funding_tx.txid, 0)
|
@@ -119,7 +120,7 @@ module Glueby
|
|
119
120
|
|
120
121
|
receiver_script = Tapyrus::Script.parse_from_payload(output.script_pubkey.to_payload)
|
121
122
|
receiver_colored_script = receiver_script.add_color(color_id)
|
122
|
-
tx
|
123
|
+
add_split_output(tx, amount, split, receiver_colored_script)
|
123
124
|
|
124
125
|
fee = fee_estimator.fee(dummy_tx(tx))
|
125
126
|
fill_change_tpc(tx, issuer, output.value - fee)
|
@@ -132,16 +133,23 @@ module Glueby
|
|
132
133
|
issuer.internal_wallet.sign_tx(tx, prev_txs)
|
133
134
|
end
|
134
135
|
|
135
|
-
def create_transfer_tx(funding_tx:nil, color_id:, sender:, receiver_address:, amount:, fee_estimator: FixedFeeEstimator.new)
|
136
|
+
def create_transfer_tx(funding_tx:nil, color_id:, sender:, receiver_address:, amount:, fee_estimator: FixedFeeEstimator.new, only_finalized: true)
|
136
137
|
receivers = [{ address: receiver_address, amount: amount }]
|
137
|
-
create_multi_transfer_tx(
|
138
|
+
create_multi_transfer_tx(
|
139
|
+
funding_tx: funding_tx,
|
140
|
+
color_id: color_id,
|
141
|
+
sender: sender,
|
142
|
+
receivers: receivers,
|
143
|
+
fee_estimator: fee_estimator,
|
144
|
+
only_finalized: only_finalized
|
145
|
+
)
|
138
146
|
end
|
139
147
|
|
140
|
-
def create_multi_transfer_tx(funding_tx:nil, color_id:, sender:, receivers:, fee_estimator: FixedFeeEstimator.new)
|
148
|
+
def create_multi_transfer_tx(funding_tx:nil, color_id:, sender:, receivers:, fee_estimator: FixedFeeEstimator.new, only_finalized: true)
|
141
149
|
tx = Tapyrus::Tx.new
|
142
150
|
|
143
151
|
amount = receivers.reduce(0) { |sum, r| sum + r[:amount].to_i }
|
144
|
-
utxos = sender.internal_wallet.list_unspent
|
152
|
+
utxos = sender.internal_wallet.list_unspent(only_finalized)
|
145
153
|
sum_token, outputs = collect_colored_outputs(utxos, color_id, amount)
|
146
154
|
fill_input(tx, outputs)
|
147
155
|
|
@@ -159,7 +167,7 @@ module Glueby
|
|
159
167
|
tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
|
160
168
|
funding_tx.outputs.first.value
|
161
169
|
else
|
162
|
-
sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee)
|
170
|
+
sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee, nil, only_finalized)
|
163
171
|
fill_input(tx, outputs)
|
164
172
|
sum_tpc
|
165
173
|
end
|
@@ -179,10 +187,10 @@ module Glueby
|
|
179
187
|
sender.internal_wallet.sign_tx(tx, prev_txs)
|
180
188
|
end
|
181
189
|
|
182
|
-
def create_burn_tx(funding_tx:nil, color_id:, sender:, amount: 0, fee_estimator: FixedFeeEstimator.new)
|
190
|
+
def create_burn_tx(funding_tx:nil, color_id:, sender:, amount: 0, fee_estimator: FixedFeeEstimator.new, only_finalized: true)
|
183
191
|
tx = Tapyrus::Tx.new
|
184
192
|
|
185
|
-
utxos = sender.internal_wallet.list_unspent
|
193
|
+
utxos = sender.internal_wallet.list_unspent(only_finalized)
|
186
194
|
sum_token, outputs = collect_colored_outputs(utxos, color_id, amount)
|
187
195
|
fill_input(tx, outputs)
|
188
196
|
|
@@ -195,7 +203,7 @@ module Glueby
|
|
195
203
|
tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
|
196
204
|
funding_tx.outputs.first.value
|
197
205
|
else
|
198
|
-
sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee + DUST_LIMIT)
|
206
|
+
sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee + DUST_LIMIT, nil, only_finalized)
|
199
207
|
fill_input(tx, outputs)
|
200
208
|
sum_tpc
|
201
209
|
end
|
@@ -215,6 +223,17 @@ module Glueby
|
|
215
223
|
sender.internal_wallet.sign_tx(tx, prev_txs)
|
216
224
|
end
|
217
225
|
|
226
|
+
def add_split_output(tx, amount, split, script_pubkey)
|
227
|
+
if amount < split
|
228
|
+
split = amount
|
229
|
+
value = 1
|
230
|
+
else
|
231
|
+
value = (amount/split).to_i
|
232
|
+
end
|
233
|
+
(split - 1).times { tx.outputs << Tapyrus::TxOut.new(value: value, script_pubkey: script_pubkey) }
|
234
|
+
tx.outputs << Tapyrus::TxOut.new(value: amount - value * (split - 1), script_pubkey: script_pubkey)
|
235
|
+
end
|
236
|
+
|
218
237
|
def fill_input(tx, outputs)
|
219
238
|
outputs.each do |output|
|
220
239
|
out_point = Tapyrus::OutPoint.new(output[:txid].rhex, output[:vout])
|
data/lib/glueby/fee_provider.rb
CHANGED
@@ -3,7 +3,7 @@ module Glueby
|
|
3
3
|
|
4
4
|
autoload :Tasks, 'glueby/fee_provider/tasks'
|
5
5
|
|
6
|
-
class NoUtxosInUtxoPool <
|
6
|
+
class NoUtxosInUtxoPool < Error; end
|
7
7
|
|
8
8
|
WALLET_ID = 'FEE_PROVIDER_WALLET'
|
9
9
|
DEFAULT_FIXED_FEE = 1000
|
@@ -42,7 +42,7 @@ module Glueby
|
|
42
42
|
# Provide an input for fee to the tx.
|
43
43
|
# @param [Tapyrus::Tx] tx - The tx that is provided fee as a input. It should be signed with ANYONECANPAY flag.
|
44
44
|
# @return [Tapyrus::Tx]
|
45
|
-
# @raise [ArgumentError] If the signatures that the tx inputs has don't have ANYONECANPAY flag.
|
45
|
+
# @raise [Glueby::ArgumentError] If the signatures that the tx inputs has don't have ANYONECANPAY flag.
|
46
46
|
# @raise [Glueby::FeeProvider::NoUtxosInUtxoPool] If there are no UTXOs for paying fee in FeeProvider's UTXO pool
|
47
47
|
def provide(tx)
|
48
48
|
tx.inputs.each do |txin|
|
@@ -2,12 +2,12 @@ module Glueby
|
|
2
2
|
module Internal
|
3
3
|
class Wallet
|
4
4
|
module Errors
|
5
|
-
class ShouldInitializeWalletAdapter <
|
6
|
-
class WalletUnloaded <
|
7
|
-
class WalletAlreadyLoaded <
|
8
|
-
class WalletAlreadyCreated <
|
9
|
-
class WalletNotFound <
|
10
|
-
class InvalidSighashType <
|
5
|
+
class ShouldInitializeWalletAdapter < Error; end
|
6
|
+
class WalletUnloaded < Error; end
|
7
|
+
class WalletAlreadyLoaded < Error; end
|
8
|
+
class WalletAlreadyCreated < Error; end
|
9
|
+
class WalletNotFound < Error; end
|
10
|
+
class InvalidSighashType < Error; end
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -30,14 +30,14 @@ module Glueby
|
|
30
30
|
|
31
31
|
utxos.each { |utxo| txb.add_utxo(utxo) }
|
32
32
|
|
33
|
-
shortage = [utxo_provider.utxo_pool_size - current_utxo_pool_size, 0].max
|
33
|
+
shortage = [utxo_provider.utxo_pool_size - utxo_provider.current_utxo_pool_size, 0].max
|
34
34
|
return if shortage == 0
|
35
35
|
|
36
36
|
added_outputs = 0
|
37
37
|
shortage.times do
|
38
38
|
fee = utxo_provider.fee_estimator.fee(dummy_tx(txb.build))
|
39
39
|
break if (sum - fee) < utxo_provider.default_value
|
40
|
-
txb.pay(address, utxo_provider.default_value)
|
40
|
+
txb.pay(utxo_provider.address, utxo_provider.default_value)
|
41
41
|
sum -= utxo_provider.default_value
|
42
42
|
added_outputs += 1
|
43
43
|
end
|
@@ -45,11 +45,11 @@ module Glueby
|
|
45
45
|
return if added_outputs == 0
|
46
46
|
|
47
47
|
fee = utxo_provider.fee_estimator.fee(dummy_tx(txb.build))
|
48
|
-
tx = txb.change_address(address)
|
48
|
+
tx = txb.change_address(utxo_provider.address)
|
49
49
|
.fee(fee)
|
50
50
|
.build
|
51
|
-
tx = wallet.sign_tx(tx)
|
52
|
-
wallet.broadcast(tx)
|
51
|
+
tx = utxo_provider.wallet.sign_tx(tx)
|
52
|
+
utxo_provider.wallet.broadcast(tx)
|
53
53
|
ensure
|
54
54
|
status
|
55
55
|
end
|
@@ -58,13 +58,13 @@ module Glueby
|
|
58
58
|
def status
|
59
59
|
status = :ready
|
60
60
|
|
61
|
-
if current_utxo_pool_size < utxo_provider.utxo_pool_size
|
62
|
-
if tpc_amount < value_to_fill_utxo_pool
|
61
|
+
if utxo_provider.current_utxo_pool_size < utxo_provider.utxo_pool_size
|
62
|
+
if utxo_provider.tpc_amount < utxo_provider.value_to_fill_utxo_pool
|
63
63
|
status = :insufficient_amount
|
64
64
|
message = <<~MESSAGE
|
65
65
|
1. Please replenishment TPC which is for paying tpc to UtxoProvider.
|
66
|
-
UtxoProvider needs #{value_to_fill_utxo_pool} tapyrus in UTXO pool.
|
67
|
-
UtxoProvider wallet's address is '#{address}'
|
66
|
+
UtxoProvider needs #{utxo_provider.value_to_fill_utxo_pool} tapyrus in UTXO pool.
|
67
|
+
UtxoProvider wallet's address is '#{utxo_provider.address}'
|
68
68
|
2. Then create UTXOs for paying in UTXO pool with 'rake glueby:utxo_provider:manage_utxo_pool'
|
69
69
|
MESSAGE
|
70
70
|
else
|
@@ -72,12 +72,12 @@ module Glueby
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
status = :not_ready if current_utxo_pool_size == 0
|
75
|
+
status = :not_ready if utxo_provider.current_utxo_pool_size == 0
|
76
76
|
|
77
77
|
puts <<~EOS
|
78
78
|
Status: #{STATUS[status]}
|
79
|
-
TPC amount: #{delimit(tpc_amount)}
|
80
|
-
UTXO pool size: #{delimit(current_utxo_pool_size)}
|
79
|
+
TPC amount: #{delimit(utxo_provider.tpc_amount)}
|
80
|
+
UTXO pool size: #{delimit(utxo_provider.current_utxo_pool_size)}
|
81
81
|
#{"\n" if message}#{message}
|
82
82
|
Configuration:
|
83
83
|
default_value = #{delimit(utxo_provider.default_value)}
|
@@ -87,17 +87,13 @@ module Glueby
|
|
87
87
|
|
88
88
|
# Show the address of Utxo Provider
|
89
89
|
def print_address
|
90
|
-
puts address
|
90
|
+
puts utxo_provider.address
|
91
91
|
end
|
92
92
|
|
93
93
|
private
|
94
94
|
|
95
|
-
def tpc_amount
|
96
|
-
wallet.balance(false)
|
97
|
-
end
|
98
|
-
|
99
95
|
def collect_outputs
|
100
|
-
wallet.list_unspent.inject([0, []]) do |sum, output|
|
96
|
+
utxo_provider.wallet.list_unspent.inject([0, []]) do |sum, output|
|
101
97
|
next sum if output[:color_id] || output[:amount] == utxo_provider.default_value
|
102
98
|
|
103
99
|
new_sum = sum[0] + output[:amount]
|
@@ -105,34 +101,16 @@ module Glueby
|
|
105
101
|
txid: output[:txid],
|
106
102
|
script_pubkey: output[:script_pubkey],
|
107
103
|
value: output[:amount],
|
108
|
-
index: output[:vout]
|
104
|
+
index: output[:vout],
|
109
105
|
finalized: output[:finalized]
|
110
106
|
}
|
111
107
|
[new_sum, new_outputs]
|
112
108
|
end
|
113
109
|
end
|
114
110
|
|
115
|
-
def current_utxo_pool_size
|
116
|
-
wallet
|
117
|
-
.list_unspent(false)
|
118
|
-
.count { |o| !o[:color_id] && o[:amount] == utxo_provider.default_value }
|
119
|
-
end
|
120
|
-
|
121
|
-
def value_to_fill_utxo_pool
|
122
|
-
utxo_provider.default_value * utxo_provider.utxo_pool_size
|
123
|
-
end
|
124
|
-
|
125
|
-
def wallet
|
126
|
-
utxo_provider.wallet
|
127
|
-
end
|
128
|
-
|
129
111
|
def delimit(num)
|
130
112
|
num.to_s.reverse.scan(/.{1,3}/).join('_').reverse
|
131
113
|
end
|
132
|
-
|
133
|
-
def address
|
134
|
-
@address ||= wallet.get_addresses.first || wallet.receive_address
|
135
|
-
end
|
136
114
|
end
|
137
115
|
end
|
138
116
|
end
|
data/lib/glueby/utxo_provider.rb
CHANGED
@@ -25,11 +25,9 @@ module Glueby
|
|
25
25
|
@wallet = load_wallet
|
26
26
|
validate_config!
|
27
27
|
@fee_estimator = (UtxoProvider.config && UtxoProvider.config[:fee_estimator]) || Glueby::Contract::FixedFeeEstimator.new
|
28
|
-
@default_value = (UtxoProvider.config && UtxoProvider.config[:default_value]) || DEFAULT_VALUE
|
29
|
-
@utxo_pool_size = (UtxoProvider.config && UtxoProvider.config[:utxo_pool_size]) || DEFAULT_UTXO_POOL_SIZE
|
30
28
|
end
|
31
29
|
|
32
|
-
attr_reader :wallet, :fee_estimator, :
|
30
|
+
attr_reader :wallet, :fee_estimator, :address
|
33
31
|
|
34
32
|
# Provide a UTXO
|
35
33
|
# @param [Tapyrus::Script] script_pubkey The script to be provided
|
@@ -62,6 +60,42 @@ module Glueby
|
|
62
60
|
[signed_tx, 0]
|
63
61
|
end
|
64
62
|
|
63
|
+
def default_value
|
64
|
+
@default_value ||=
|
65
|
+
(
|
66
|
+
Glueby::AR::SystemInformation.utxo_provider_default_value ||
|
67
|
+
(UtxoProvider.config && UtxoProvider.config[:default_value]) ||
|
68
|
+
DEFAULT_VALUE
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
def utxo_pool_size
|
73
|
+
@utxo_pool_size ||=
|
74
|
+
(
|
75
|
+
Glueby::AR::SystemInformation.utxo_provider_pool_size ||
|
76
|
+
(UtxoProvider.config && UtxoProvider.config[:utxo_pool_size]) ||
|
77
|
+
DEFAULT_UTXO_POOL_SIZE
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
def tpc_amount
|
82
|
+
wallet.balance(false)
|
83
|
+
end
|
84
|
+
|
85
|
+
def current_utxo_pool_size
|
86
|
+
wallet
|
87
|
+
.list_unspent(false)
|
88
|
+
.count { |o| !o[:color_id] && o[:amount] == default_value }
|
89
|
+
end
|
90
|
+
|
91
|
+
def address
|
92
|
+
@address ||= wallet.get_addresses.first || wallet.receive_address
|
93
|
+
end
|
94
|
+
|
95
|
+
def value_to_fill_utxo_pool
|
96
|
+
default_value * utxo_pool_size
|
97
|
+
end
|
98
|
+
|
65
99
|
private
|
66
100
|
|
67
101
|
# Create wallet for provider
|
@@ -74,7 +108,7 @@ module Glueby
|
|
74
108
|
end
|
75
109
|
|
76
110
|
def collect_uncolored_outputs(wallet, amount)
|
77
|
-
utxos = wallet.list_unspent.select { |o| !o[:color_id] && o[:amount] ==
|
111
|
+
utxos = wallet.list_unspent.select { |o| !o[:color_id] && o[:amount] == default_value }
|
78
112
|
utxos.shuffle!
|
79
113
|
|
80
114
|
utxos.inject([0, []]) do |sum, output|
|
@@ -88,11 +122,8 @@ module Glueby
|
|
88
122
|
end
|
89
123
|
|
90
124
|
def validate_config!
|
91
|
-
if
|
92
|
-
utxo_pool_size
|
93
|
-
if utxo_pool_size && (!utxo_pool_size.is_a?(Integer) || utxo_pool_size > MAX_UTXO_POOL_SIZE)
|
94
|
-
raise Glueby::Configuration::Errors::InvalidConfiguration, "utxo_pool_size(#{utxo_pool_size}) should not be greater than #{MAX_UTXO_POOL_SIZE}"
|
95
|
-
end
|
125
|
+
if !utxo_pool_size.is_a?(Integer) || utxo_pool_size > MAX_UTXO_POOL_SIZE
|
126
|
+
raise Glueby::Configuration::Errors::InvalidConfiguration, "utxo_pool_size(#{utxo_pool_size}) should not be greater than #{MAX_UTXO_POOL_SIZE}"
|
96
127
|
end
|
97
128
|
end
|
98
129
|
end
|
data/lib/glueby/version.rb
CHANGED
data/lib/glueby.rb
CHANGED
@@ -48,4 +48,9 @@ module Glueby
|
|
48
48
|
def self.configure
|
49
49
|
yield configuration if block_given?
|
50
50
|
end
|
51
|
+
|
52
|
+
# Base error classes. These error classes must be used as a super class in all error classes that is defined and
|
53
|
+
# raised in glueby library.
|
54
|
+
class Error < StandardError; end
|
55
|
+
class ArgumentError < Error; end
|
51
56
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glueby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- azuchi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: tapyrus
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.2.
|
19
|
+
version: 0.2.13
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.2.
|
26
|
+
version: 0.2.13
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activerecord
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|