diff --git a/app/controllers/api/v2/bitcoin_addresses_controller.rb b/app/controllers/api/v2/bitcoin_addresses_controller.rb index 17e77809f..f2ba1b5de 100644 --- a/app/controllers/api/v2/bitcoin_addresses_controller.rb +++ b/app/controllers/api/v2/bitcoin_addresses_controller.rb @@ -45,6 +45,34 @@ def rgb_cells render json: { data: { rgb_cells: cells }, meta: { total: bitcoin_vouts.total_count, page_size: @page_size } } end + def udt_accounts + expires_in 1.minute, public: true, must_revalidate: true, stale_while_revalidate: 10.seconds + + address = Addresses::Explore.run!(key: params[:id]) + address_ids = address.map(&:id) + + cell_types = %w(udt xudt xudt_compatible) + cell_outputs = CellOutput.live.includes(:bitcoin_vout).where(cell_outputs: { address_id: address_ids, cell_type: cell_types }). + where.not(bitcoin_vouts: { status: "unbound" }).group(:cell_type, :type_hash).sum(:udt_amount) + + udt_accounts = cell_outputs.map do |k, v| + udt = Udt.find_by(type_hash: k[1], published: true) + next unless udt + + { + symbol: udt.symbol, + decimal: udt.decimal, + amount: v, + type_hash: k[1], + udt_icon_file: udt.icon_file, + udt_type: udt.udt_type, + udt_type_script: udt.type_script, + } + end.compact + + render json: { data: { udt_accounts: } } + end + private def set_pagination_params diff --git a/app/models/ckb_sync/new_node_data_processor.rb b/app/models/ckb_sync/new_node_data_processor.rb index 9334ac121..b1440ec30 100644 --- a/app/models/ckb_sync/new_node_data_processor.rb +++ b/app/models/ckb_sync/new_node_data_processor.rb @@ -914,13 +914,13 @@ def prepare_script_ids(outputs) local_cache.fetch("NodeData/LockScript/#{output.lock.code_hash}-#{output.lock.hash_type}-#{output.lock.args}") do # TODO use LockScript.where(script_hash: output.lock.compute_hash).select(:id)&.first replace search by code_hash, hash_type and args query after script_hash has been filled LockScript.where(code_hash: output.lock.code_hash, hash_type: output.lock.hash_type, - args: output.lock.args).take! + args: output.lock.args).order("id asc").first end if output.type.present? local_cache.fetch("NodeData/TypeScript/#{output.type.code_hash}-#{output.type.hash_type}-#{output.type.args}") do # TODO use TypeScript.where(script_hash: output.type.compute_hash).select(:id)&.first replace search by code_hash, hash_type and args query after script_hash has been filled TypeScript.where(code_hash: output.type.code_hash, hash_type: output.type.hash_type, - args: output.type.args).take! + args: output.type.args).order("id asc").first end end end diff --git a/config/routes/v2.rb b/config/routes/v2.rb index 19d447119..b431ae327 100644 --- a/config/routes/v2.rb +++ b/config/routes/v2.rb @@ -97,6 +97,7 @@ resources :bitcoin_statistics, only: :index resources :bitcoin_addresses, only: :show do get :rgb_cells, on: :member + get :udt_accounts, on: :member end resources :rgb_live_cells, only: :index end diff --git a/lib/tasks/migration/update_cell_output_script_id.rake b/lib/tasks/migration/update_cell_output_script_id.rake new file mode 100644 index 000000000..bbc43b6ca --- /dev/null +++ b/lib/tasks/migration/update_cell_output_script_id.rake @@ -0,0 +1,87 @@ +namespace :migration do + desc "Usage: RAILS_ENV=production bundle exec rake migration:update_cell_output_script_id" + task update_cell_output_script_id: :environment do + ActiveRecord::Base.connection.execute("SET statement_timeout = 0") + p "==============lock scripts" + duplicate_script_hashes = LockScript. + select(:script_hash). + group(:script_hash). + having("COUNT(*) > 1"). + pluck(:script_hash) + duplicate_script_hashes.each do |hash| + LockScript.where(script_hash: hash).each do |script| + unless CellOutput.live.where(lock_script_id: script.id).exists? + script.destroy + end + end + end; nil + + duplicate_script_hashes.each_with_index do |lock_script_hash, index| + p lock_script_hash + p index + lock_script_ids = LockScript.where(script_hash: lock_script_hash).order("id asc").pluck(:id) + base_lock_script_id = lock_script_ids.delete_at(0) + lock_script_ids.each do |id| + CellOutput.where(lock_script_id: id).in_batches(of: 10000) do |batch| + batch.update_all(lock_script_id: base_lock_script_id) + end + end + end; nil + + duplicate_script_hashes = LockScript. + select(:script_hash). + group(:script_hash). + having("COUNT(*) > 1"). + pluck(:script_hash) + duplicate_script_hashes.each do |hash| + LockScript.where(script_hash: hash).each do |script| + unless CellOutput.live.where(lock_script_id: script.id).exists? + script.destroy + end + end + end; nil + + p "==============type scripts" + duplicate_type_script_hashes = TypeScript. + select(:script_hash). + group(:script_hash). + having("COUNT(*) > 1"). + pluck(:script_hash) + + duplicate_type_script_hashes.each do |hash| + TypeScript.where(script_hash: hash).each do |script| + unless CellOutput.live.where(type_script_id: script.id).exists? + script.destroy + end + end + end; nil + + duplicate_type_script_hashes.each_with_index do |type_script_hash, index| + p type_script_hash + p index + type_script_ids = TypeScript.where(script_hash: type_script_hash).order("id asc").pluck(:id) + base_type_script_id = type_script_ids.delete_at(0) + type_script_ids.each do |id| + CellOutput.where(type_script_id: id).in_batches(of: 10000) do |batch| + batch.update_all(type_script_id: base_type_script_id) + end + end + end; nil + + duplicate_type_script_hashes = TypeScript. + select(:script_hash). + group(:script_hash). + having("COUNT(*) > 1"). + pluck(:script_hash) + + duplicate_type_script_hashes.each do |hash| + TypeScript.where(script_hash: hash).each do |script| + unless CellOutput.live.where(type_script_id: script.id).exists? + script.destroy + end + end + end; nil + + puts "done" + end +end