glueby 1.2.3 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9490bd9983bf524a8b937c6b495ecb4260b65d8f84f590bffd4d06bcf8db3c60
4
- data.tar.gz: 187ab2c69864a94245dbb94c3c79abb1cfce1317138dcc9a9f6aea10b91864d6
3
+ metadata.gz: 41286c658b45bd9914f4c46f27e8e0f8696b61a8d763b5fcc2b0117d0e91668d
4
+ data.tar.gz: 3237815c98b14f3fc126e5531c8ff229d468fe77832bcca4b22828ffd386c7bd
5
5
  SHA512:
6
- metadata.gz: 71285a57ead31c478d4880ae64b1ab9e29091a22027f489fc92f30005d8cbf1eecf5a5e79f0bc81bbb8b99723399e4b6c1f856c8c2e9eb21ecadef64e591c593
7
- data.tar.gz: 7609a05ffd89aa3eb2dad0c75b9201a356bdcc83ff7f76522a8888b16f80071edb4d809093fb7e6e17322653aed701c1561d25c760073b75c21a00723996f632
6
+ metadata.gz: dad03d9f436a2720b854450fb95e8fab7fedde3d721f5db3bf0c69c1aa7b818ebfc359cda0c1057f4d98075295459f55f5261c69a4cc99a49f752bba3683e95c
7
+ data.tar.gz: eb8f0724b6b7b849a343b14e5630480584df5b6f408509dcdeebcca711055f8bcd3a62c188130b75118a7f93e18e015aeb2afd60784d4382cd27741b010f2c06
@@ -7,19 +7,15 @@
7
7
 
8
8
  name: Ruby
9
9
 
10
- on:
11
- push:
12
- branches: [master, v1.1, v1.2]
13
- pull_request:
14
- branches: [master, v1.1, v1.2]
10
+ on: [push,pull_request]
15
11
 
16
12
  jobs:
17
13
  test:
18
14
  runs-on: ubuntu-latest
19
15
  strategy:
20
16
  matrix:
21
- ruby-version: ["2.7", "3.0", "3.1", "3.2"]
22
-
17
+ ruby-version: ["3.0", "3.1", "3.2", "3.3"]
18
+ permissions: write-all
23
19
  steps:
24
20
  - run: docker pull tapyrus/tapyrusd:v0.5.2
25
21
  - uses: actions/checkout@v2
@@ -31,4 +27,4 @@ jobs:
31
27
  ruby-version: ${{ matrix.ruby-version }}
32
28
  bundler-cache: true # runs 'bundle install' and caches installed gems automatically
33
29
  - name: Run tests
34
- run: bundle exec rake
30
+ run: bundle exec rake
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-3.2.2
1
+ ruby-3.3.0
data/Gemfile CHANGED
@@ -6,3 +6,5 @@ gemspec
6
6
  gem "rake", "~> 12.0"
7
7
  gem "rspec", "~> 3.0"
8
8
  gem "docker-api", "~> 2.1.0"
9
+
10
+ gem "simplecov", require: false
data/glueby.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.description = %q{A Ruby library of smart contracts that can be used on Tapyrus.}
11
11
  spec.homepage = "https://github.com/chaintope/glueby"
12
12
  spec.license = "MIT"
13
- spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
14
14
 
15
15
 
16
16
  spec.metadata["homepage_uri"] = spec.homepage
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
28
28
 
29
29
  spec.add_runtime_dependency 'tapyrus', '>= 0.3.1'
30
30
  spec.add_runtime_dependency 'activerecord', '~> 7.0.0'
31
+ spec.add_runtime_dependency 'kaminari'
31
32
  spec.add_development_dependency 'sqlite3'
32
33
  spec.add_development_dependency 'mysql2'
33
34
  spec.add_development_dependency 'rails', '~> 7.0.0'
@@ -13,7 +13,7 @@ class CreateTimestamp < ActiveRecord::Migration<%= migration_version %>
13
13
  t.string :payment_base
14
14
  t.bigint :prev_id
15
15
  t.boolean :latest, null: false, default: true
16
- t.boolean :hex, null: false, default: false
16
+ t.string :version, null: false, default: "1"
17
17
  end
18
18
 
19
19
  add_index :glueby_timestamps, [:prev_id], unique: true
@@ -4,6 +4,7 @@ class CreateUtxo < ActiveRecord::Migration<%= migration_version %>
4
4
  t.string :txid
5
5
  t.integer :index
6
6
  t.bigint :value
7
+ t.string :color_id, index: true
7
8
  t.string :script_pubkey
8
9
  t.string :label, index: true
9
10
  t.integer :status
@@ -24,7 +24,7 @@ module Glueby
24
24
  end
25
25
 
26
26
  # Specify wallet adapter.
27
- # @param [Symbol] adapter - The adapter type :activerecord or :core is currently supported.
27
+ # @param [Symbol] adapter - The adapter type :activerecord, :core, or :mysql is currently supported.
28
28
  def wallet_adapter=(adapter)
29
29
  case adapter
30
30
  when :core
@@ -9,7 +9,14 @@ module Glueby
9
9
 
10
10
  attr_reader :tx
11
11
 
12
- belongs_to :prev, class_name: 'Glueby::Contract::AR::Timestamp', optional: true
12
+ belongs_to :prev, class_name: 'Glueby::Contract::AR::Timestamp', inverse_of: :next, optional: true
13
+ has_one :next, class_name: 'Glueby::Contract::AR::Timestamp', foreign_key: 'prev_id'
14
+
15
+ validates :version, presence: true, inclusion: { in: ['1', '2'] }
16
+
17
+ def next_id
18
+ self.next&.id
19
+ end
13
20
 
14
21
  validate :validate_prev
15
22
 
@@ -35,11 +42,14 @@ module Glueby
35
42
  # - content
36
43
  # - prefix(optional)
37
44
  # - timestamp_type(optional)
38
- # - hex(optional) [Boolean] If true, the strings set in prefix and content are treated as hex strings.
45
+ # - version [String] Version of the timestamp recording method.
46
+ # The format in which the timestamp is recorded differs depending on the version.
47
+ # Version "1" treats the specified content and prefix as a binary string.
48
+ # Version "2" treats the specified content and prefix as a hexadecimal string with the string set to prefix and content.
39
49
  # @raise [Glueby::ArgumentError] If the timestamp_type is not in :simple or :trackable
40
50
  def initialize(attributes = nil)
41
51
  # Set content_hash from :content attribute
42
- hex = attributes[:hex] || false
52
+ hex = attributes[:version] == '1' ? false : true
43
53
  content_hash = Timestamp.digest_content(attributes[:content], attributes[:digest] || :sha256, hex)
44
54
  super(
45
55
  wallet_id: attributes[:wallet_id],
@@ -48,7 +58,7 @@ module Glueby
48
58
  status: :init,
49
59
  timestamp_type: attributes[:timestamp_type] || :simple,
50
60
  prev_id: attributes[:prev_id],
51
- hex: hex
61
+ version: attributes[:version]
52
62
  )
53
63
  rescue ::ArgumentError => e
54
64
  raise Glueby::ArgumentError, e.message
@@ -114,6 +124,7 @@ module Glueby
114
124
 
115
125
  if update_trackable?
116
126
  prev.latest = false
127
+ prev.next = self
117
128
  prev.save!
118
129
  end
119
130
  end
@@ -129,6 +140,12 @@ module Glueby
129
140
  raise Errors::FailedToBroadcast, "failed to broadcast (id=#{id}, reason=#{e.message})"
130
141
  end
131
142
 
143
+ def hex?
144
+ version != '1'
145
+ end
146
+
147
+ alias_method :hex, :hex?
148
+
132
149
  private
133
150
 
134
151
  def wallet
@@ -139,11 +156,13 @@ module Glueby
139
156
  builder = builder_class.new(wallet, fee_estimator)
140
157
 
141
158
  if builder.instance_of?(Contract::Timestamp::TxBuilder::UpdatingTrackable)
159
+ prev_prefix = prev.hex ? prev.prefix&.htb : prev.prefix
160
+ prev_content_hash = prev.hex ? prev.content_hash&.htb : prev.content_hash
142
161
  builder.set_prev_timestamp_info(
143
162
  timestamp_utxo: prev.utxo,
144
163
  payment_base: prev.payment_base,
145
- prefix: prev.prefix,
146
- data: prev.content_hash
164
+ prefix: prev_prefix,
165
+ data: prev_content_hash
147
166
  )
148
167
  end
149
168
 
@@ -15,6 +15,7 @@ module Glueby
15
15
  class UnsupportedTokenType < ArgumentError; end
16
16
  class UnknownScriptPubkey < ArgumentError; end
17
17
  class UnsupportedDigestType < ArgumentError; end
18
+ class UnsupportedTimestampVersion < ArgumentError; end
18
19
  class PrevTimestampNotFound < ArgumentError; end
19
20
  class PrevTimestampIsNotTrackable < ArgumentError; end
20
21
  class UnnecessaryPrevTimestamp < ArgumentError; end
@@ -7,6 +7,51 @@ module Glueby
7
7
  # * 1 output to send the change TPC back to the wallet.
8
8
  #
9
9
  # Storing timestamp transaction to the blockchain enables everyone to verify that the data existed at that time and a user signed it.
10
+ #
11
+ # == Versioning Timestamp
12
+ #
13
+ # The timestamp has an attribute called "version".
14
+ # Version indicates how the timestamp is recorded in the blockchain. Currently, only versions 1 and 2 are supported, and each is recorded in the following manner
15
+ # * Version 1: The first version of the blockchain is used to record timestamps.
16
+ # treats the content and prefix received as parameters as a binary string
17
+ # * Version 2:.
18
+ # treats the specified content and prefix as a hexadecimal string with the string set to prefix and content.
19
+ #
20
+ # For example, when recording a simple type of timestamp, the difference between the content recorded by version 1 and version 2 is as follows
21
+ # Version 1:
22
+ # Save the timestamp as follows:
23
+ #
24
+ # Glueby::Contract::Timestamp.new(
25
+ # wallet: wallet,
26
+ # content: "1234",
27
+ # prefix: "071222",
28
+ # digest: :none,
29
+ # version: "1"
30
+ # )
31
+ #
32
+ # The output script of the recorded transaction will include OP_RETURN and will look like this:
33
+ #
34
+ # OP_RETURN 30373132323231323334
35
+ #
36
+ # Note that prefix: "071222" and content: "1234" are interpreted as ASCII strings and their hexadecimal representation "3037313232323132323334" is recorded in the actual blockchain, respectively.
37
+ #
38
+ # Version 2:
39
+ #
40
+ # To save the timestamp in version 2, simply change the version of the previous example to "2".
41
+ #
42
+ # Glueby::Contract::Timestamp.new(
43
+ # wallet: wallet,
44
+ # content: "1234",
45
+ # prefix: "071222",
46
+ # digest: :none,
47
+ # version: "2"
48
+ # )
49
+ #
50
+ # The output script will look like this:
51
+ #
52
+ # OP_RETURN 0712221234
53
+ #
54
+ # In this case, prefix: "071222" and content: "1234" are treated as a hexadecimal string and recorded directly in the blockchain.
10
55
  class Timestamp
11
56
  P2C_DEFAULT_VALUE = 1_000
12
57
 
@@ -29,7 +74,10 @@ module Glueby
29
74
  # - :simple
30
75
  # - :trackable
31
76
  # @param [Integer] prev_timestamp_id The id column of glueby_timestamps that will be updated by the timestamp that will be created
32
- # @param [Boolean] hex If true, prefix and content are treated as hex strings
77
+ # @param [String] version Version of the timestamp recording method.
78
+ # The format in which the timestamp is recorded differs depending on the version.
79
+ # Version "1" treats the specified content and prefix as a binary string.
80
+ # Version "2" treats the specified content and prefix as a hexadecimal string with the string set to prefix and content.
33
81
  # @raise [Glueby::Contract::Errors::UnsupportedDigestType] if digest is unsupported
34
82
  # @raise [Glueby::Contract::Errors::InvalidTimestampType] if timestamp_type is unsupported
35
83
  def initialize(
@@ -41,7 +89,7 @@ module Glueby
41
89
  utxo_provider: nil,
42
90
  timestamp_type: :simple,
43
91
  prev_timestamp_id: nil,
44
- hex: false
92
+ version:
45
93
  )
46
94
  @wallet = wallet
47
95
  @content = content
@@ -53,7 +101,8 @@ module Glueby
53
101
  raise Glueby::Contract::Errors::InvalidTimestampType, "#{timestamp_type} is invalid type, supported types are :simple, and :trackable." unless [:simple, :trackable].include?(timestamp_type)
54
102
  @timestamp_type = timestamp_type
55
103
  @prev_timestamp_id = prev_timestamp_id
56
- @hex = hex
104
+ raise Glueby::Contract::Errors::UnsupportedTimestampVersion, "#{version} is unsupported, supported versions are '1' and '2'." unless ['1', '2'].include?(version)
105
+ @version = version
57
106
  end
58
107
 
59
108
  # broadcast to Tapyrus Core
@@ -70,7 +119,7 @@ module Glueby
70
119
  timestamp_type: @timestamp_type,
71
120
  digest: @digest,
72
121
  prev_id: @prev_timestamp_id,
73
- hex: @hex
122
+ version: @version
74
123
  )
75
124
  @ar.save_with_broadcast!(fee_estimator: @fee_estimator, utxo_provider: @utxo_provider)
76
125
  @ar.txid
@@ -86,27 +86,8 @@ module Glueby
86
86
  Glueby::AR::SystemInformation.use_only_finalized_utxo?
87
87
  end
88
88
 
89
- # Sign to pay-to-contract output.
90
- #
91
- # @param issuer [Glueby::Walelt] Issuer of the token
92
- # @param tx [Tapyrus::Tx] The transaction to be signed with metadata
93
- # @param funding_tx [Tapyrus::Tx] The funding transaction that has pay-to-contract output in its first output
94
- # @param payment_base [String] The public key used to generate pay to contract public key
95
- # @param metadata [String] Data that represents token metadata
96
- # @return [Tapyrus::Tx] signed tx
97
- def sign_to_p2c_output(issuer, tx, funding_tx, payment_base, metadata)
98
- utxo = { txid: funding_tx.txid, vout: 0, script_pubkey: funding_tx.outputs[0].script_pubkey.to_hex }
99
- issuer.internal_wallet.sign_to_pay_to_contract_address(tx, utxo, payment_base, metadata)
100
- end
101
-
102
89
  private
103
90
 
104
- def create_p2c_address(wallet, metadata)
105
- p2c_address, payment_base = wallet.internal_wallet.create_pay_to_contract_address(metadata)
106
- script = Tapyrus::Script.parse_from_addr(p2c_address)
107
- [script, p2c_address, payment_base]
108
- end
109
-
110
91
  def issue_reissuable_token(issuer:, amount:, split: 1, fee_estimator:, metadata: nil)
111
92
  txb = Internal::ContractBuilder.new(
112
93
  sender_wallet: issuer.internal_wallet,
@@ -244,14 +225,6 @@ module Glueby
244
225
  end
245
226
  end
246
227
  end
247
-
248
- # Add dummy inputs and outputs to tx for issue non-reissuable transaction and nft transaction
249
- def dummy_issue_tx_from_out_point
250
- tx = Tapyrus::Tx.new
251
- receiver_colored_script = Tapyrus::Script.parse_from_payload('21c20000000000000000000000000000000000000000000000000000000000000000bc76a914000000000000000000000000000000000000000088ac'.htb)
252
- tx.outputs << Tapyrus::TxOut.new(value: 0, script_pubkey: receiver_colored_script)
253
- FeeEstimator.dummy_tx(tx)
254
- end
255
228
  end
256
229
 
257
230
  attr_reader :color_id
@@ -206,8 +206,8 @@ module Glueby
206
206
  tx = Tapyrus::Tx.new
207
207
 
208
208
  amount = receivers.reduce(0) { |sum, r| sum + r[:amount].to_i }
209
- utxos = sender.internal_wallet.list_unspent(only_finalized)
210
- sum_token, outputs = collect_colored_outputs(utxos, color_id, amount)
209
+ utxos = sender.internal_wallet.list_unspent(only_finalized, color_id: color_id)
210
+ sum_token, outputs = collect_colored_outputs(utxos, amount)
211
211
  fill_input(tx, outputs)
212
212
 
213
213
  receivers.each do |r|
@@ -247,8 +247,8 @@ module Glueby
247
247
  def create_burn_tx(color_id:, sender:, amount: 0, fee_estimator: FeeEstimator::Fixed.new, only_finalized: true)
248
248
  tx = Tapyrus::Tx.new
249
249
 
250
- utxos = sender.internal_wallet.list_unspent(only_finalized)
251
- sum_token, outputs = collect_colored_outputs(utxos, color_id, amount)
250
+ utxos = sender.internal_wallet.list_unspent(only_finalized, color_id: color_id)
251
+ sum_token, outputs = collect_colored_outputs(utxos, amount)
252
252
  fill_input(tx, outputs)
253
253
 
254
254
  fill_change_token(tx, sender, sum_token - amount, color_id) if amount.positive?
@@ -319,12 +319,9 @@ module Glueby
319
319
  # Returns the set of utxos that satisfies the specified amount and has the specified color_id.
320
320
  # if amount is not specified or 0, return all utxos with color_id
321
321
  # @param results [Array] response of Glueby::Internal::Wallet#list_unspent
322
- # @param color_id [Tapyrus::Color::ColorIdentifier] color identifier
323
322
  # @param amount [Integer]
324
- def collect_colored_outputs(results, color_id, amount = 0)
323
+ def collect_colored_outputs(results, amount = 0)
325
324
  results = results.inject([0, []]) do |sum, output|
326
- next sum unless output[:color_id] == color_id.to_hex
327
-
328
325
  new_sum = sum[0] + output[:amount]
329
326
  new_outputs = sum[1] << output
330
327
  return [new_sum, new_outputs] if new_sum >= amount && amount.positive?
@@ -90,16 +90,6 @@ module Glueby
90
90
 
91
91
  private
92
92
 
93
- def check_wallet_amount!
94
- if tpc_amount < fee_provider.fixed_fee
95
- raise InsufficientTPC, <<~MESSAGE
96
- FeeProvider has insufficient TPC to create fee outputs to fill the UTXO pool.
97
- 1. Please replenishment TPC which is for paying fee to FeeProvider. FeeProvider needs #{fee_provider.utxo_pool_size * fee_provider.fixed_fee} tapyrus at least. FeeProvider wallet's address is '#{wallet.receive_address}'
98
- 2. Then create UTXOs for paying in UTXO pool with 'rake glueby:fee_provider:manage_utxo_pool'
99
- MESSAGE
100
- end
101
- end
102
-
103
93
  def tpc_amount
104
94
  wallet.balance(false)
105
95
  end
@@ -357,7 +357,7 @@ module Glueby
357
357
  end
358
358
 
359
359
  def get_fee_estimator(fee_estimator_name)
360
- Glueby::Contract::FeeEstimator.get_const("#{fee_estimator_name.capitalize}", false).new
360
+ Glueby::Contract::FeeEstimator.const_get(fee_estimator_name.capitalize, false).new
361
361
  end
362
362
 
363
363
  def valid_fee_estimator?(fee_estimator)
@@ -65,6 +65,21 @@ module Glueby
65
65
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
66
66
  end
67
67
 
68
+ # Returns all tokens with specified color_id
69
+ #
70
+ # @param [String] wallet_id - The wallet id that is offered by `create_wallet()` method.
71
+ # @param [Boolean] only_finalized - includes only finalized UTXO value if it
72
+ # is true. Default is true.
73
+ # @param [Tapyrus::Color::ColorIdentifier] color_id The color identifier associated with UTXO.
74
+ # It will return only UTXOs with specified color_id. If color_id is nil, it will return all UTXOs.
75
+ # If Tapyrus::Color::ColorIdentifier.default is specified, it will return uncolored UTXOs(i.e. TPC)
76
+ # @param [Integer] page - The page parameter is responsible for specifying the current page being viewed within the paginated results. default is 1.
77
+ # @param [Integer] per - The per parameter is used to determine the number of items to display per page. default is 25.
78
+ # @return [Array<Utxo>] The array of the utxos with specified color_id
79
+ def list_unspent_with_count(wallet_id, only_finalized = true, label = nil, color_id: nil, page: 1, per: 25)
80
+ raise NotImplementedError, "You must implement #{self.class}##{__method__}"
81
+ end
82
+
68
83
  # Returns the UTXOs that the wallet has.
69
84
  # If label is specified, return UTXOs filtered with label
70
85
  #
@@ -74,6 +89,9 @@ module Glueby
74
89
  # @param [String] label - Label for filtering UTXOs
75
90
  # - If label is nil or :unlabeled, only unlabeled UTXOs will be returned.
76
91
  # - If label=:all, it will return all utxos
92
+ # @param [Tapyrus::Color::ColorIdentifier] color_id - The color identifier.
93
+ # It will return only UTXOs with specified color_id. If color_id is nil, it will return all UTXOs.
94
+ # If Tapyrus::Color::ColorIdentifier.default is specified, it will return uncolored UTXOs(i.e. TPC)
77
95
  # @return [Array of UTXO]
78
96
  #
79
97
  # ## The UTXO structure
@@ -84,7 +102,7 @@ module Glueby
84
102
  # - finalized: [Boolean] Whether the UTXO is finalized
85
103
  # - color_id: [String] Color id of the UTXO. If it is TPC UTXO, color_id is nil.
86
104
  # - script_pubkey: [String] Script pubkey of the UTXO
87
- def list_unspent(wallet_id, only_finalized = true, label = nil)
105
+ def list_unspent(wallet_id, only_finalized = true, label = nil, color_id: nil)
88
106
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
89
107
  end
90
108
 
@@ -139,7 +157,7 @@ module Glueby
139
157
 
140
158
  # Returns information for the addresses
141
159
  #
142
- # @param [String] address - The p2pkh address to get information about
160
+ # @param [String, Array<String>] addresses - The p2pkh address to get information about
143
161
  # @return [Array<Hash>] The array of hash instance which has keys wallet_id, label and purpose.
144
162
  # Returns blank array if the key correspond with the address is not exist.
145
163
  def get_addresses_info(addresses)
@@ -38,6 +38,7 @@ module Glueby
38
38
  utxo = Utxo.find_or_initialize_by(txid: tx.txid, index: index)
39
39
  utxo.update!(
40
40
  label: key.label,
41
+ color_id: output.script_pubkey.color_id&.to_hex,
41
42
  script_pubkey: output.script_pubkey.to_hex,
42
43
  value: output.value,
43
44
  status: status,
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'securerandom'
4
+ require 'kaminari'
4
5
 
5
6
  module Glueby
6
7
  module Internal
@@ -89,29 +90,18 @@ module Glueby
89
90
  utxos.sum(&:value)
90
91
  end
91
92
 
92
- def list_unspent(wallet_id, only_finalized = true, label = nil)
93
- wallet = AR::Wallet.find_by(wallet_id: wallet_id)
94
- utxos = wallet.utxos.where(locked_at: nil)
95
- utxos = utxos.where(status: :finalized) if only_finalized
96
- if [:unlabeled, nil].include?(label)
97
- utxos = utxos.where(label: nil)
98
- elsif label && (label != :all)
99
- utxos = utxos.where(label: label)
100
- else
101
- utxos
102
- end
93
+ def list_unspent_with_count(wallet_id, only_finalized = true, label = nil, color_id: nil, page: 1, per: 25)
94
+ utxos = list_unspent_internal(wallet_id, color_id, only_finalized, label)
95
+ utxos = utxos.page(page).per(per) if per > 0
96
+ {
97
+ count: utxos.total_count,
98
+ outputs: utxos_to_h(utxos)
99
+ }
100
+ end
103
101
 
104
- utxos.map do |utxo|
105
- {
106
- txid: utxo.txid,
107
- vout: utxo.index,
108
- script_pubkey: utxo.script_pubkey,
109
- color_id: utxo.color_id,
110
- amount: utxo.value,
111
- finalized: utxo.status == 'finalized',
112
- label: utxo.label
113
- }
114
- end
102
+ def list_unspent(wallet_id, only_finalized = true, label = nil, color_id: nil)
103
+ utxos = list_unspent_internal(wallet_id, color_id, only_finalized, label)
104
+ utxos_to_h(utxos)
115
105
  end
116
106
 
117
107
  def sign_tx(wallet_id, tx, prevtxs = [], sighashtype: Tapyrus::SIGHASH_TYPE[:all])
@@ -245,6 +235,37 @@ module Glueby
245
235
  key_type: Tapyrus::Key::TYPES[:compressed]
246
236
  )
247
237
  end
238
+
239
+ def list_unspent_internal(wallet_id, color_id = nil, only_finalized = true, label = nil)
240
+ wallet = AR::Wallet.find_by(wallet_id: wallet_id)
241
+ utxos = wallet.utxos.where(locked_at: nil)
242
+ utxos = utxos.where(color_id: color_id.to_hex) if color_id && !color_id.default?
243
+ utxos = utxos.where(color_id: nil) if color_id && color_id.default?
244
+ utxos = utxos.where(status: :finalized) if only_finalized
245
+ utxos = utxos.order(:id)
246
+ utxos = if [:unlabeled, nil].include?(label)
247
+ utxos = utxos.where(label: nil)
248
+ elsif label && (label != :all)
249
+ utxos = utxos.where(label: label)
250
+ else
251
+ utxos
252
+ end
253
+ utxos
254
+ end
255
+
256
+ def utxos_to_h(utxos)
257
+ utxos.map do |utxo|
258
+ {
259
+ txid: utxo.txid,
260
+ vout: utxo.index,
261
+ script_pubkey: utxo.script_pubkey,
262
+ color_id: utxo.color_id,
263
+ amount: utxo.value,
264
+ finalized: utxo.status == 'finalized',
265
+ label: utxo.label
266
+ }
267
+ end
268
+ end
248
269
  end
249
270
  end
250
271
  end
@@ -84,7 +84,7 @@ module Glueby
84
84
 
85
85
  # If label=nil, it will return unlabeled utxos to protect labeled utxos for specific purpose
86
86
  # If label=:all, it will return all utxos
87
- def list_unspent(wallet_id, only_finalized = true, label = nil)
87
+ def list_unspent(wallet_id, only_finalized = true, label = nil, color_id: nil)
88
88
  perform_as(wallet_id) do |client|
89
89
  min_conf = only_finalized ? 1 : 0
90
90
  res = client.listunspent(min_conf)
@@ -97,6 +97,13 @@ module Glueby
97
97
  res
98
98
  end
99
99
 
100
+ if color_id
101
+ res = res.filter do |i|
102
+ script = Tapyrus::Script.parse_from_payload(i['scriptPubKey'].htb)
103
+ script.cp2pkh? || script.cp2sh? && color_id == Tapyrus::Color::ColorIdentifier.parse_from_payload(script.chunks[0].pushed_data)
104
+ end
105
+ end
106
+
100
107
  res.map do |i|
101
108
  script = Tapyrus::Script.parse_from_payload(i['scriptPubKey'].htb)
102
109
  color_id = if script.cp2pkh? || script.cp2sh?
@@ -177,7 +184,7 @@ module Glueby
177
184
  type = case sighashtype & (~(Tapyrus::SIGHASH_TYPE[:anyonecanpay]))
178
185
  when Tapyrus::SIGHASH_TYPE[:all] then 'ALL'
179
186
  when Tapyrus::SIGHASH_TYPE[:none] then 'NONE'
180
- when Tapyrus::SIGHASH_TYPE[:single] then 'SIGNLE'
187
+ when Tapyrus::SIGHASH_TYPE[:single] then 'SINGLE'
181
188
  else
182
189
  raise Errors::InvalidSighashType, "Invalid sighash type '#{sighashtype}'"
183
190
  end
@@ -93,9 +93,25 @@ module Glueby
93
93
  # @param label [String] This label is used to filtered the UTXOs with labeled if a key or Utxo is labeled.
94
94
  # - If label is nil or :unlabeled, only unlabeled UTXOs will be returned.
95
95
  # - If label=:all, all UTXOs will be returned.
96
- def list_unspent(only_finalized = true, label = :unlabeled)
96
+ # @param color_id [Tapyrus::Color::ColorIdentifier] The color identifier associated with UTXO.
97
+ # It will return only UTXOs with specified color_id. If color_id is nil, it will return all UTXOs.
98
+ # If Tapyrus::Color::ColorIdentifier.default is specified, it will return uncolored UTXOs(i.e. TPC)
99
+ # @param page [Integer] The page parameter is responsible for specifying the current page being viewed within the paginated results. default is 1.
100
+ # @param per [Integer] The per parameter is used to determine the number of items to display per page. default is 25.
101
+ def list_unspent_with_count(only_finalized = true, label = nil, color_id: nil, page: 1, per: 25)
102
+ wallet_adapter.list_unspent_with_count(id, only_finalized, label, color_id: color_id, page: page, per: per)
103
+ end
104
+
105
+ # @param only_finalized [Boolean] The flag to get a UTXO with status only finalized
106
+ # @param label [String] This label is used to filtered the UTXOs with labeled if a key or Utxo is labeled.
107
+ # - If label is nil or :unlabeled, only unlabeled UTXOs will be returned.
108
+ # - If label=:all, all UTXOs will be returned.
109
+ # @param color_id [Tapyrus::Color::ColorIdentifier] The color identifier associated with UTXO.
110
+ # It will return only UTXOs with specified color_id. If color_id is nil, it will return all UTXOs.
111
+ # If Tapyrus::Color::ColorIdentifier.default is specified, it will return uncolored UTXOs(i.e. TPC)
112
+ def list_unspent(only_finalized = true, label = :unlabeled, color_id: nil )
97
113
  label = :unlabeled unless label
98
- wallet_adapter.list_unspent(id, only_finalized, label)
114
+ wallet_adapter.list_unspent(id, only_finalized, label, color_id: color_id)
99
115
  end
100
116
 
101
117
  def lock_unspent(utxo)
@@ -171,8 +187,7 @@ module Glueby
171
187
  lock_utxos = false,
172
188
  excludes = []
173
189
  )
174
- collect_utxos(amount, label, only_finalized, shuffle, lock_utxos, excludes) do |output|
175
- next false unless output[:color_id].nil?
190
+ collect_utxos(amount, label, Tapyrus::Color::ColorIdentifier.default, only_finalized, shuffle, lock_utxos, excludes) do |output|
176
191
  next yield(output) if block_given?
177
192
 
178
193
  true
@@ -206,8 +221,7 @@ module Glueby
206
221
  lock_utxos = false,
207
222
  excludes = []
208
223
  )
209
- collect_utxos(amount, label, only_finalized, shuffle, lock_utxos, excludes) do |output|
210
- next false unless output[:color_id] == color_id.to_hex
224
+ collect_utxos(amount, label, color_id, only_finalized, shuffle, lock_utxos, excludes) do |output|
211
225
  next yield(output) if block_given?
212
226
 
213
227
  true
@@ -261,7 +275,7 @@ module Glueby
261
275
  while current_amount - fee < target_amount
262
276
  sum, utxos = collect_uncolored_outputs(
263
277
  fee + target_amount - current_amount,
264
- nil, nil, true, true,
278
+ nil, false, true, true,
265
279
  provided_utxos,
266
280
  &block
267
281
  )
@@ -288,7 +302,8 @@ module Glueby
288
302
  def collect_utxos(
289
303
  amount,
290
304
  label,
291
- only_finalized,
305
+ color_id,
306
+ only_finalized = true,
292
307
  shuffle = true,
293
308
  lock_utxos = false,
294
309
  excludes = []
@@ -296,7 +311,7 @@ module Glueby
296
311
  collect_all = amount.nil?
297
312
 
298
313
  raise Glueby::ArgumentError, 'amount must be positive' unless collect_all || amount.positive?
299
- utxos = list_unspent(only_finalized, label)
314
+ utxos = list_unspent(only_finalized, label, color_id: color_id)
300
315
  utxos = utxos.shuffle if shuffle
301
316
 
302
317
  r = utxos.inject([0, []]) do |(sum, outputs), output|
@@ -1,3 +1,3 @@
1
1
  module Glueby
2
- VERSION = "1.2.3"
2
+ VERSION = "1.3.0"
3
3
  end
data/lib/glueby/wallet.rb CHANGED
@@ -46,6 +46,10 @@ module Glueby
46
46
  end
47
47
  end
48
48
 
49
+ def token_utxos(color_id = nil, only_finalized = true, page = 1, per = 25)
50
+ @internal_wallet.list_unspent_with_count(only_finalized, nil, color_id: color_id, page: page, per: per)
51
+ end
52
+
49
53
  private
50
54
 
51
55
  def initialize(internal_wallet)
data/lib/glueby.rb CHANGED
@@ -19,7 +19,7 @@ module Glueby
19
19
 
20
20
  module GluebyLogger
21
21
  def logger
22
- if defined?(Rails)
22
+ if defined?(Rails) && Rails.logger
23
23
  Rails.logger
24
24
  else
25
25
  Logger.new(STDOUT)
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: 1.2.3
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-10-18 00:00:00.000000000 Z
11
+ date: 2024-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tapyrus
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: 7.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: kaminari
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: sqlite3
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -173,7 +187,7 @@ metadata:
173
187
  homepage_uri: https://github.com/chaintope/glueby
174
188
  source_code_uri: https://github.com/chaintope/glueby
175
189
  changelog_uri: https://github.com/chaintope/glueby
176
- post_install_message:
190
+ post_install_message:
177
191
  rdoc_options: []
178
192
  require_paths:
179
193
  - lib
@@ -181,15 +195,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
181
195
  requirements:
182
196
  - - ">="
183
197
  - !ruby/object:Gem::Version
184
- version: 2.7.0
198
+ version: 3.0.0
185
199
  required_rubygems_version: !ruby/object:Gem::Requirement
186
200
  requirements:
187
201
  - - ">="
188
202
  - !ruby/object:Gem::Version
189
203
  version: '0'
190
204
  requirements: []
191
- rubygems_version: 3.4.10
192
- signing_key:
205
+ rubygems_version: 3.5.3
206
+ signing_key:
193
207
  specification_version: 4
194
208
  summary: A Ruby library of smart contracts that can be used on Tapyrus.
195
209
  test_files: []