glueby 0.1.0 → 0.2.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/.ruby-gemset +1 -1
- data/.ruby-version +1 -1
- data/.travis.yml +3 -2
- data/README.md +27 -17
- data/glueby.gemspec +1 -1
- data/lib/generators/glueby/{initializer_generator.rb → contract/initializer_generator.rb} +0 -0
- data/lib/generators/glueby/contract/templates/initializer.rb.erb +3 -0
- data/lib/generators/glueby/contract/templates/key_table.rb.erb +15 -0
- data/lib/generators/glueby/{templates → contract/templates}/timestamp_table.rb.erb +2 -1
- data/lib/generators/glueby/contract/templates/utxo_table.rb.erb +15 -0
- data/lib/generators/glueby/contract/templates/wallet_table.rb.erb +10 -0
- data/lib/generators/glueby/contract/timestamp_generator.rb +26 -0
- data/lib/generators/glueby/contract/wallet_adapter_generator.rb +46 -0
- data/lib/glueby.rb +18 -1
- data/lib/glueby/contract.rb +3 -14
- data/lib/glueby/contract/active_record/timestamp.rb +8 -5
- data/lib/glueby/contract/errors.rb +6 -0
- data/lib/glueby/contract/payment.rb +54 -0
- data/lib/glueby/contract/timestamp.rb +39 -38
- data/lib/glueby/contract/token.rb +193 -0
- data/lib/glueby/contract/tx_builder.rb +197 -31
- data/lib/glueby/generator.rb +5 -0
- data/lib/glueby/generator/migrate_generator.rb +38 -0
- data/lib/glueby/internal.rb +6 -0
- data/lib/glueby/internal/rpc.rb +35 -0
- data/lib/glueby/internal/wallet.rb +122 -0
- data/lib/glueby/internal/wallet/abstract_wallet_adapter.rb +131 -0
- data/lib/glueby/internal/wallet/active_record.rb +15 -0
- data/lib/glueby/internal/wallet/active_record/key.rb +72 -0
- data/lib/glueby/internal/wallet/active_record/utxo.rb +50 -0
- data/lib/glueby/internal/wallet/active_record/wallet.rb +54 -0
- data/lib/glueby/internal/wallet/active_record_wallet_adapter.rb +133 -0
- data/lib/glueby/internal/wallet/errors.rb +11 -0
- data/lib/glueby/internal/wallet/tapyrus_core_wallet_adapter.rb +158 -0
- data/lib/glueby/version.rb +1 -1
- data/lib/glueby/wallet.rb +51 -0
- data/lib/tasks/glueby/contract/timestamp.rake +5 -5
- data/lib/tasks/glueby/contract/wallet_adapter.rake +42 -0
- metadata +30 -10
- data/lib/generators/glueby/templates/initializer.rb.erb +0 -4
- data/lib/generators/glueby/timestamp_generator.rb +0 -57
- data/lib/glueby/contract/rpc.rb +0 -15
@@ -0,0 +1,193 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Glueby
|
4
|
+
module Contract
|
5
|
+
# This class represents custom token issued by application user.
|
6
|
+
# Application users can
|
7
|
+
# - issue their own tokens.
|
8
|
+
# - send to other users.
|
9
|
+
# - make the tokens disable.
|
10
|
+
#
|
11
|
+
# Examples:
|
12
|
+
#
|
13
|
+
# alice = Glueby::Wallet.create
|
14
|
+
# bob = Glueby::Wallet.create
|
15
|
+
#
|
16
|
+
# Issue
|
17
|
+
# token = Token.issue!(issuer: alice, amount: 100)
|
18
|
+
# token.amount(wallet: alice)
|
19
|
+
# => 100
|
20
|
+
#
|
21
|
+
# Send
|
22
|
+
# token.transfer!(sender: alice, receiver: bob, amount: 1)
|
23
|
+
# token.amount(wallet: alice)
|
24
|
+
# => 99
|
25
|
+
# token.amount(wallet: bob)
|
26
|
+
# => 1
|
27
|
+
#
|
28
|
+
# Burn
|
29
|
+
# token.burn!(sender: alice, amount: 10)
|
30
|
+
# token.amount(wallet: alice)
|
31
|
+
# => 89
|
32
|
+
# token.burn!(sender: alice)
|
33
|
+
# token.amount(wallet: alice)
|
34
|
+
# => 0
|
35
|
+
#
|
36
|
+
# Reissue
|
37
|
+
# token.reissue!(issuer: alice, amount: 100)
|
38
|
+
# token.amount(wallet: alice)
|
39
|
+
# => 100
|
40
|
+
#
|
41
|
+
class Token
|
42
|
+
include Glueby::Contract::TxBuilder
|
43
|
+
extend Glueby::Contract::TxBuilder
|
44
|
+
|
45
|
+
class << self
|
46
|
+
# Issue new token with specified amount and token type.
|
47
|
+
# REISSUABLE token can be reissued with #reissue! method, and
|
48
|
+
# NON_REISSUABLE and NFT token can not.
|
49
|
+
# Amount is set to 1 when the token type is NFT
|
50
|
+
#
|
51
|
+
# @param issuer [Glueby::Wallet]
|
52
|
+
# @param token_type [TokenTypes]
|
53
|
+
# @param amount [Integer]
|
54
|
+
# @return [Token] token
|
55
|
+
# @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
|
56
|
+
# @raise [InvalidAmount] if amount is not positive integer.
|
57
|
+
# @raise [UnspportedTokenType] if token is not supported.
|
58
|
+
def issue!(issuer:, token_type: Tapyrus::Color::TokenTypes::REISSUABLE, amount: 1)
|
59
|
+
raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
|
60
|
+
|
61
|
+
txs, color_id, script_pubkey = case token_type
|
62
|
+
when Tapyrus::Color::TokenTypes::REISSUABLE
|
63
|
+
issue_reissuable_token(issuer: issuer, amount: amount)
|
64
|
+
when Tapyrus::Color::TokenTypes::NON_REISSUABLE
|
65
|
+
issue_non_reissuable_token(issuer: issuer, amount: amount)
|
66
|
+
when Tapyrus::Color::TokenTypes::NFT
|
67
|
+
issue_nft_token(issuer: issuer)
|
68
|
+
else
|
69
|
+
raise Glueby::Contract::Errors::UnsupportedTokenType
|
70
|
+
end
|
71
|
+
txs.each { |tx| issuer.internal_wallet.broadcast(tx) }
|
72
|
+
new(color_id: color_id, script_pubkey: script_pubkey)
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def issue_reissuable_token(issuer:, amount:)
|
78
|
+
estimated_fee = FixedFeeProvider.new.fee(Tapyrus::Tx.new)
|
79
|
+
funding_tx = create_funding_tx(wallet: issuer, amount: estimated_fee)
|
80
|
+
tx = create_issue_tx_for_reissuable_token(funding_tx: funding_tx, issuer: issuer, amount: amount)
|
81
|
+
script_pubkey = funding_tx.outputs.first.script_pubkey
|
82
|
+
color_id = Tapyrus::Color::ColorIdentifier.reissuable(script_pubkey)
|
83
|
+
[[funding_tx, tx], color_id, script_pubkey]
|
84
|
+
end
|
85
|
+
|
86
|
+
def issue_non_reissuable_token(issuer:, amount:)
|
87
|
+
tx = create_issue_tx_for_non_reissuable_token(issuer: issuer, amount: amount)
|
88
|
+
out_point = tx.inputs.first.out_point
|
89
|
+
color_id = Tapyrus::Color::ColorIdentifier.non_reissuable(out_point)
|
90
|
+
[[tx], color_id, nil]
|
91
|
+
end
|
92
|
+
|
93
|
+
def issue_nft_token(issuer:)
|
94
|
+
tx = create_issue_tx_for_nft_token(issuer: issuer)
|
95
|
+
out_point = tx.inputs.first.out_point
|
96
|
+
color_id = Tapyrus::Color::ColorIdentifier.nft(out_point)
|
97
|
+
[[tx], color_id, nil]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
attr_reader :color_id
|
102
|
+
|
103
|
+
# Re-issue the token with specified amount.
|
104
|
+
# A wallet can issue the token only when it is REISSUABLE token.
|
105
|
+
# @param issuer [Glueby::Wallet]
|
106
|
+
# @param amount [Integer]
|
107
|
+
# @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
|
108
|
+
# @raise [InvalidAmount] if amount is not positive integer.
|
109
|
+
# @raise [InvalidTokenType] if token is not reissuable.
|
110
|
+
# @raise [UnknownScriptPubkey] when token is reissuable but it doesn't know script pubkey to issue token.
|
111
|
+
def reissue!(issuer:, amount:)
|
112
|
+
raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
|
113
|
+
raise Glueby::Contract::Errors::InvalidTokenType unless token_type == Tapyrus::Color::TokenTypes::REISSUABLE
|
114
|
+
raise Glueby::Contract::Errors::UnknownScriptPubkey unless @script_pubkey
|
115
|
+
|
116
|
+
estimated_fee = FixedFeeProvider.new.fee(Tapyrus::Tx.new)
|
117
|
+
funding_tx = create_funding_tx(wallet: issuer, amount: estimated_fee, script: @script_pubkey)
|
118
|
+
tx = create_reissue_tx(funding_tx: funding_tx, issuer: issuer, amount: amount, color_id: color_id)
|
119
|
+
[funding_tx, tx].each { |tx| issuer.internal_wallet.broadcast(tx) }
|
120
|
+
end
|
121
|
+
|
122
|
+
# Send the token to other wallet
|
123
|
+
#
|
124
|
+
# @param sender [Glueby::Wallet] wallet to send this token
|
125
|
+
# @param receiver [Glueby::Wallet] wallet to receive this token
|
126
|
+
# @param amount [Integer]
|
127
|
+
# @return [Token] receiver token
|
128
|
+
# @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
|
129
|
+
# @raise [InsufficientTokens] if wallet does not have enough token to send.
|
130
|
+
# @raise [InvalidAmount] if amount is not positive integer.
|
131
|
+
def transfer!(sender:, receiver:, amount: 1)
|
132
|
+
raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
|
133
|
+
|
134
|
+
tx = create_transfer_tx(color_id: color_id, sender: sender, receiver: receiver, amount: amount)
|
135
|
+
sender.internal_wallet.broadcast(tx)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Burn token
|
139
|
+
# If amount is not specified or 0, burn all token associated with the wallet.
|
140
|
+
#
|
141
|
+
# @param sender [Glueby::Wallet] wallet to send this token
|
142
|
+
# @param amount [Integer]
|
143
|
+
# @raise [InsufficientFunds] if wallet does not have enough TPC to send transaction.
|
144
|
+
# @raise [InsufficientTokens] if wallet does not have enough token to send transaction.
|
145
|
+
# @raise [InvalidAmount] if amount is not positive integer.
|
146
|
+
def burn!(sender:, amount: 0)
|
147
|
+
raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
|
148
|
+
|
149
|
+
tx = create_burn_tx(color_id: color_id, sender: sender, amount: amount)
|
150
|
+
sender.internal_wallet.broadcast(tx)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Return balance of token in the specified wallet.
|
154
|
+
# @param wallet [Glueby::Wallet]
|
155
|
+
# @return [Integer] amount of utxo value associated with this token.
|
156
|
+
def amount(wallet:)
|
157
|
+
# collect utxo associated with this address
|
158
|
+
utxos = wallet.internal_wallet.list_unspent
|
159
|
+
_, results = collect_colored_outputs(utxos, color_id)
|
160
|
+
results.sum { |result| result[:amount] }
|
161
|
+
end
|
162
|
+
|
163
|
+
# Return token type
|
164
|
+
# @return [Tapyrus::Color::TokenTypes]
|
165
|
+
def token_type
|
166
|
+
color_id.type
|
167
|
+
end
|
168
|
+
|
169
|
+
# Return serialized payload
|
170
|
+
# @return [String] payload
|
171
|
+
def to_payload
|
172
|
+
payload = +''
|
173
|
+
payload << @color_id.to_payload
|
174
|
+
payload << @script_pubkey.to_payload if @script_pubkey
|
175
|
+
end
|
176
|
+
|
177
|
+
# Restore token from payload
|
178
|
+
# @param payload [String]
|
179
|
+
# @return [Glueby::Contract::Token]
|
180
|
+
def self.parse_from_payload(payload)
|
181
|
+
color_id, script_pubkey = payload.unpack('a33a*')
|
182
|
+
color_id = Tapyrus::Color::ColorIdentifier.parse_from_payload(color_id)
|
183
|
+
script_pubkey = Tapyrus::Script.parse_from_payload(script_pubkey) if script_pubkey
|
184
|
+
new(color_id: color_id, script_pubkey: script_pubkey)
|
185
|
+
end
|
186
|
+
|
187
|
+
def initialize(color_id:, script_pubkey:nil)
|
188
|
+
@color_id = color_id
|
189
|
+
@script_pubkey = script_pubkey
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -1,46 +1,212 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Glueby
|
2
4
|
module Contract
|
3
5
|
module TxBuilder
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
# @return [(Numeric, Array)] sum value of outputs and outputs
|
8
|
-
# @raise [InsufficientFunds] if result of listunspent is not enough to pay the specified amount
|
9
|
-
def collect_outputs(results, amount)
|
10
|
-
results.inject([0, []]) do |sum, output|
|
11
|
-
new_sum = sum[0] + (output['amount'] * 100_000_000)
|
12
|
-
new_outputs = sum[1] << output
|
13
|
-
return [new_sum, new_outputs] if new_sum >= amount
|
6
|
+
def receive_address(wallet:)
|
7
|
+
wallet.receive_address
|
8
|
+
end
|
14
9
|
|
15
|
-
|
10
|
+
# Create new public key, and new transaction that sends TPC to it
|
11
|
+
def create_funding_tx(wallet:, amount:, script: nil, fee_provider: FixedFeeProvider.new)
|
12
|
+
tx = Tapyrus::Tx.new
|
13
|
+
fee = fee_provider.fee(dummy_tx(tx))
|
14
|
+
|
15
|
+
sum, outputs = wallet.internal_wallet.collect_uncolored_outputs(fee + amount)
|
16
|
+
fill_input(tx, outputs)
|
17
|
+
|
18
|
+
receiver_script = script ? script : Tapyrus::Script.parse_from_addr(wallet.internal_wallet.receive_address)
|
19
|
+
tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_script)
|
20
|
+
|
21
|
+
fill_change_tpc(tx, wallet, sum - fee - amount)
|
22
|
+
wallet.internal_wallet.sign_tx(tx)
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_issue_tx_for_reissuable_token(funding_tx:, issuer:, amount:, fee_provider: FixedFeeProvider.new)
|
26
|
+
tx = Tapyrus::Tx.new
|
27
|
+
|
28
|
+
out_point = Tapyrus::OutPoint.from_txid(funding_tx.txid, 0)
|
29
|
+
tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
|
30
|
+
output = funding_tx.outputs.first
|
31
|
+
|
32
|
+
receiver_script = Tapyrus::Script.parse_from_payload(output.script_pubkey.to_payload)
|
33
|
+
color_id = Tapyrus::Color::ColorIdentifier.reissuable(receiver_script)
|
34
|
+
receiver_colored_script = receiver_script.add_color(color_id)
|
35
|
+
tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_colored_script)
|
36
|
+
|
37
|
+
fee = fee_provider.fee(dummy_tx(tx))
|
38
|
+
fill_change_tpc(tx, issuer, output.value - fee)
|
39
|
+
prev_txs = [{
|
40
|
+
txid: funding_tx.txid,
|
41
|
+
vout: 0,
|
42
|
+
scriptPubKey: output.script_pubkey.to_hex,
|
43
|
+
amount: output.value
|
44
|
+
}]
|
45
|
+
issuer.internal_wallet.sign_tx(tx, prev_txs)
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_issue_tx_for_non_reissuable_token(issuer:, amount:, fee_provider: FixedFeeProvider.new)
|
49
|
+
create_issue_tx_from_out_point(token_type: Tapyrus::Color::TokenTypes::NON_REISSUABLE, issuer: issuer, amount: amount, fee_provider: fee_provider)
|
50
|
+
end
|
51
|
+
|
52
|
+
def create_issue_tx_for_nft_token(issuer:, fee_provider: FixedFeeProvider.new)
|
53
|
+
create_issue_tx_from_out_point(token_type: Tapyrus::Color::TokenTypes::NFT, issuer: issuer, amount: 1, fee_provider: fee_provider)
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_issue_tx_from_out_point(token_type:, issuer:, amount:, fee_provider: FixedFeeProvider.new)
|
57
|
+
tx = Tapyrus::Tx.new
|
58
|
+
|
59
|
+
fee = fee_provider.fee(dummy_issue_tx_from_out_point)
|
60
|
+
sum, outputs = issuer.internal_wallet.collect_uncolored_outputs(fee)
|
61
|
+
fill_input(tx, outputs)
|
62
|
+
|
63
|
+
out_point = tx.inputs.first.out_point
|
64
|
+
color_id = case token_type
|
65
|
+
when Tapyrus::Color::TokenTypes::NON_REISSUABLE
|
66
|
+
Tapyrus::Color::ColorIdentifier.non_reissuable(out_point)
|
67
|
+
when Tapyrus::Color::TokenTypes::NFT
|
68
|
+
Tapyrus::Color::ColorIdentifier.nft(out_point)
|
69
|
+
else
|
70
|
+
raise Glueby::Contract::Errors::UnsupportedTokenType
|
16
71
|
end
|
17
|
-
|
72
|
+
|
73
|
+
receiver_script = Tapyrus::Script.parse_from_addr(issuer.internal_wallet.receive_address)
|
74
|
+
receiver_colored_script = receiver_script.add_color(color_id)
|
75
|
+
tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_colored_script)
|
76
|
+
|
77
|
+
fill_change_tpc(tx, issuer, sum - fee)
|
78
|
+
issuer.internal_wallet.sign_tx(tx)
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_reissue_tx(funding_tx:, issuer:, amount:, color_id:, fee_provider: FixedFeeProvider.new)
|
82
|
+
tx = Tapyrus::Tx.new
|
83
|
+
|
84
|
+
out_point = Tapyrus::OutPoint.from_txid(funding_tx.txid, 0)
|
85
|
+
tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
|
86
|
+
output = funding_tx.outputs.first
|
87
|
+
|
88
|
+
receiver_script = Tapyrus::Script.parse_from_payload(output.script_pubkey.to_payload)
|
89
|
+
receiver_colored_script = receiver_script.add_color(color_id)
|
90
|
+
tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_colored_script)
|
91
|
+
|
92
|
+
fee = fee_provider.fee(dummy_tx(tx))
|
93
|
+
fill_change_tpc(tx, issuer, output.value - fee)
|
94
|
+
prev_txs = [{
|
95
|
+
txid: funding_tx.txid,
|
96
|
+
vout: 0,
|
97
|
+
scriptPubKey: output.script_pubkey.to_hex,
|
98
|
+
amount: output.value
|
99
|
+
}]
|
100
|
+
issuer.internal_wallet.sign_tx(tx, prev_txs)
|
101
|
+
end
|
102
|
+
|
103
|
+
def create_transfer_tx(color_id:, sender:, receiver:, amount:, fee_provider: FixedFeeProvider.new)
|
104
|
+
tx = Tapyrus::Tx.new
|
105
|
+
|
106
|
+
utxos = sender.internal_wallet.list_unspent
|
107
|
+
sum_token, outputs = collect_colored_outputs(utxos, color_id, amount)
|
108
|
+
fill_input(tx, outputs)
|
109
|
+
|
110
|
+
receiver_script = Tapyrus::Script.parse_from_addr(receiver.internal_wallet.receive_address)
|
111
|
+
receiver_colored_script = receiver_script.add_color(color_id)
|
112
|
+
tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_colored_script)
|
113
|
+
|
114
|
+
fill_change_token(tx, sender, sum_token - amount, color_id)
|
115
|
+
|
116
|
+
fee = fee_provider.fee(dummy_tx(tx))
|
117
|
+
sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee)
|
118
|
+
fill_input(tx, outputs)
|
119
|
+
|
120
|
+
fill_change_tpc(tx, sender, sum_tpc - fee)
|
121
|
+
sender.internal_wallet.sign_tx(tx)
|
122
|
+
end
|
123
|
+
|
124
|
+
def create_burn_tx(color_id:, sender:, amount: 0, fee_provider: FixedFeeProvider.new)
|
125
|
+
tx = Tapyrus::Tx.new
|
126
|
+
|
127
|
+
utxos = sender.internal_wallet.list_unspent
|
128
|
+
sum_token, outputs = collect_colored_outputs(utxos, color_id, amount)
|
129
|
+
fill_input(tx, outputs)
|
130
|
+
|
131
|
+
fill_change_token(tx, sender, sum_token - amount, color_id) if amount.positive?
|
132
|
+
|
133
|
+
fee = fee_provider.fee(dummy_tx(tx))
|
134
|
+
|
135
|
+
dust = 600 # in case that the wallet has output which has just fee amount.
|
136
|
+
sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee + dust)
|
137
|
+
fill_input(tx, outputs)
|
138
|
+
|
139
|
+
fill_change_tpc(tx, sender, sum_tpc - fee)
|
140
|
+
sender.internal_wallet.sign_tx(tx)
|
18
141
|
end
|
19
142
|
|
20
|
-
# Add inputs to tx
|
21
|
-
# @param [Tapyrus::Tx] tx
|
22
|
-
# @param [Array] outputs provided as result of listunspent and extracted by #collect_outputs methods
|
23
|
-
# @return [Tapyrus::Tx] tx which has enough amount in inputs.
|
24
143
|
def fill_input(tx, outputs)
|
25
144
|
outputs.each do |output|
|
26
|
-
out_point = Tapyrus::OutPoint.new(output[
|
145
|
+
out_point = Tapyrus::OutPoint.new(output[:txid].rhex, output[:vout])
|
27
146
|
tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
|
28
147
|
end
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
148
|
+
end
|
149
|
+
|
150
|
+
def fill_change_tpc(tx, wallet, change)
|
151
|
+
return unless change.positive?
|
152
|
+
|
153
|
+
change_script = Tapyrus::Script.parse_from_addr(wallet.internal_wallet.change_address)
|
154
|
+
tx.outputs << Tapyrus::TxOut.new(value: change, script_pubkey: change_script)
|
155
|
+
end
|
156
|
+
|
157
|
+
def fill_change_token(tx, wallet, change, color_id)
|
158
|
+
return unless change.positive?
|
159
|
+
|
160
|
+
change_script = Tapyrus::Script.parse_from_addr(wallet.internal_wallet.change_address)
|
161
|
+
change_colored_script = change_script.add_color(color_id)
|
162
|
+
tx.outputs << Tapyrus::TxOut.new(value: change, script_pubkey: change_colored_script)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Returns the set of utxos that satisfies the specified amount and has the specified color_id.
|
166
|
+
# if amount is not specified or 0, return all utxos with color_id
|
167
|
+
# @param results [Array] response of Glueby::Internal::Wallet#list_unspent
|
168
|
+
# @param color_id [Tapyrus::Color::ColorIdentifier] color identifier
|
169
|
+
# @param amount [Integer]
|
170
|
+
def collect_colored_outputs(results, color_id, amount = 0)
|
171
|
+
results = results.inject([0, []]) do |sum, output|
|
172
|
+
next sum unless output[:color_id] == color_id.to_hex
|
173
|
+
|
174
|
+
new_sum = sum[0] + output[:amount]
|
175
|
+
new_outputs = sum[1] << output
|
176
|
+
return [new_sum, new_outputs] if new_sum >= amount && amount.positive?
|
177
|
+
|
178
|
+
[new_sum, new_outputs]
|
42
179
|
end
|
43
|
-
|
180
|
+
raise Glueby::Contract::Errors::InsufficientTokens if amount.positive?
|
181
|
+
|
182
|
+
results
|
183
|
+
end
|
184
|
+
|
185
|
+
# Add dummy inputs and outputs to tx
|
186
|
+
def dummy_tx(tx)
|
187
|
+
dummy = Tapyrus::Tx.parse_from_payload(tx.to_payload)
|
188
|
+
|
189
|
+
# dummy input for tpc
|
190
|
+
out_point = Tapyrus::OutPoint.new('00' * 32, 0)
|
191
|
+
dummy.inputs << Tapyrus::TxIn.new(out_point: out_point)
|
192
|
+
|
193
|
+
# Add script_sig to all intpus
|
194
|
+
dummy.inputs.each do |input|
|
195
|
+
input.script_sig = Tapyrus::Script.parse_from_payload('000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'.htb)
|
196
|
+
end
|
197
|
+
|
198
|
+
# dummy output to return change
|
199
|
+
change_script = Tapyrus::Script.to_p2pkh('0000000000000000000000000000000000000000')
|
200
|
+
dummy.outputs << Tapyrus::TxOut.new(value: 0, script_pubkey: change_script)
|
201
|
+
dummy
|
202
|
+
end
|
203
|
+
|
204
|
+
# Add dummy inputs and outputs to tx for issue non-reissuable transaction and nft transaction
|
205
|
+
def dummy_issue_tx_from_out_point
|
206
|
+
tx = Tapyrus::Tx.new
|
207
|
+
receiver_colored_script = Tapyrus::Script.parse_from_payload('21c20000000000000000000000000000000000000000000000000000000000000000bc76a914000000000000000000000000000000000000000088ac'.htb)
|
208
|
+
tx.outputs << Tapyrus::TxOut.new(value: 0, script_pubkey: receiver_colored_script)
|
209
|
+
dummy_tx(tx)
|
44
210
|
end
|
45
211
|
end
|
46
212
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Glueby
|
2
|
+
module Generator
|
3
|
+
module MigrateGenerator
|
4
|
+
module ClassMethod
|
5
|
+
def next_migration_number(dirname)
|
6
|
+
# ::ActiveRecord::Migration.next_migration_number(number)
|
7
|
+
# ::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
8
|
+
next_migration_number = current_migration_number(dirname) + 1
|
9
|
+
::ActiveRecord::Migration.next_migration_number(next_migration_number)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
MYSQL_ADAPTERS = [
|
14
|
+
"ActiveRecord::ConnectionAdapters::MysqlAdapter",
|
15
|
+
"ActiveRecord::ConnectionAdapters::Mysql2Adapter"
|
16
|
+
].freeze
|
17
|
+
|
18
|
+
def migration_version
|
19
|
+
major = ::Rails::VERSION::MAJOR
|
20
|
+
if major >= 5
|
21
|
+
"[#{major}.#{::Rails::VERSION::MINOR}]"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def mysql?
|
26
|
+
MYSQL_ADAPTERS.include?(::ActiveRecord::Base.connection.class.name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def table_options
|
30
|
+
if mysql?
|
31
|
+
', { options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci" }'
|
32
|
+
else
|
33
|
+
""
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|