Skip to content

Commit

Permalink
Issue 697 (#1991)
Browse files Browse the repository at this point in the history
* feat: add udt holder allocation

* chore: update db structure

* chore:adjust holder allocation filtering criteria

* fix: iteration variable for allocation_data

* chore: remove ckb_holder_count
  • Loading branch information
rabbitz authored Jun 26, 2024
1 parent 7c2c422 commit 7af8f65
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 26 deletions.
20 changes: 19 additions & 1 deletion app/controllers/api/v1/udts_controller.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module Api
module V1
class UdtsController < ApplicationController
before_action :validate_query_params, only: :show
before_action :validate_query_params, only: %i[show holder_allocation]
before_action :validate_pagination_params, :pagination_params, only: :index

def index
Expand Down Expand Up @@ -84,6 +84,24 @@ def download_csv
raise Api::V1::Exceptions::UdtNotFoundError
end

def holder_allocation
udt = Udt.find_by!(type_hash: params[:id], published: true)
holder_allocation = udt.udt_holder_allocations.find_by(contract_id: nil)
btc_holder_count = holder_allocation&.btc_holder_count || 0

lock_hashes = udt.udt_holder_allocations.includes(:contract).where.not(contract_id: nil).map do |allocation|
{
name: allocation.contract.name,
code_hash: allocation.contract.code_hash,
holder_count: allocation.ckb_holder_count,
}
end

render json: { btc_holder_count:, lock_hashes: }
rescue ActiveRecord::RecordNotFound
raise Api::V1::Exceptions::UdtNotFoundError
end

private

def validate_query_params
Expand Down
1 change: 1 addition & 0 deletions app/models/udt.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class Udt < ApplicationRecord
has_one :udt_verification
has_one :omiga_inscription_info
has_one :xudt_tag
has_many :udt_holder_allocations

enum udt_type: { sudt: 0, m_nft_token: 1, nrc_721_token: 2, spore_cell: 3,
omiga_inscription: 4, xudt: 5, xudt_compatible: 6 }
Expand Down
21 changes: 21 additions & 0 deletions app/models/udt_holder_allocation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class UdtHolderAllocation < ApplicationRecord
belongs_to :udt
belongs_to :contract, optional: true
end

# == Schema Information
#
# Table name: udt_holder_allocations
#
# id :bigint not null, primary key
# udt_id :bigint not null
# contract_id :bigint
# ckb_holder_count :integer default(0), not null
# btc_holder_count :integer default(0), not null
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_udt_holder_allocations_on_udt_id (udt_id)
#
23 changes: 0 additions & 23 deletions app/workers/generate_Udt_holders_worker.rb

This file was deleted.

61 changes: 61 additions & 0 deletions app/workers/generate_udt_holder_allocation_worker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
class GenerateUdtHolderAllocationWorker
include Sidekiq::Worker
sidekiq_options retry: 3

def perform(type_hash)
udt = Udt.find_by(type_hash:)
return unless udt

update_udt_holder_allocation(udt)
update_contract_holder_allocation(udt)
end

def update_udt_holder_allocation(udt)
type_script = TypeScript.find_by(udt.type_script)
return unless type_script

holder_allocation = UdtHolderAllocation.find_or_initialize_by(udt:, contract_id: nil)
ckb_address_ids = CellOutput.live.where(type_script:).distinct.pluck(:address_id)
btc_address_ids = []
ckb_address_ids.each_slice(1000) do |address_ids|
ids = BitcoinAddressMapping.where(ckb_address_id: address_ids).pluck(:bitcoin_address_id)
btc_address_ids.concat(ids).uniq!
end

holder_allocation.update!(btc_holder_count: btc_address_ids.count)
end

def update_contract_holder_allocation(udt)
type_script = TypeScript.find_by(udt.type_script)
return unless type_script

unique_ckb_address_ids = []
CellOutput.live.where(type_script:).find_in_batches(batch_size: 1000) do |batch|
batch_ckb_address_ids = batch.pluck(:address_id)
excluded_ckb_address_ids = BitcoinAddressMapping.where(ckb_address_id: batch_ckb_address_ids).pluck(:ckb_address_id)
filtered_ckb_address_ids = batch_ckb_address_ids - excluded_ckb_address_ids
unique_ckb_address_ids.concat(filtered_ckb_address_ids).uniq!
end

allocation_data = {}
unique_ckb_address_ids.each_slice(1000) do |batch_address_ids|
holder_count = CellOutput.joins(:lock_script).
where(address_id: batch_address_ids).
group("lock_scripts.code_hash").
count("DISTINCT cell_outputs.address_id")

holder_count.each do |code_hash, count|
allocation_data[code_hash] ||= 0
allocation_data[code_hash] += count
end
end

allocation_data.each do |code_hash, count|
contract = Contract.find_by(code_hash:, role: ["LockScript", "lock_script"])
next unless contract

allocation = UdtHolderAllocation.find_or_initialize_by(udt:, contract:)
allocation.update!(ckb_holder_count: count)
end
end
end
2 changes: 1 addition & 1 deletion app/workers/update_udt_info_worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def perform(block_number)
end, unique_by: :type_hash
)

type_hashes.each { GenerateUdtHoldersWorker.perform_async(_1) }
type_hashes.each { GenerateUdtHolderAllocationWorker.perform_async(_1) }
end
end
end
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
resources :market_data, only: %i[index show]
resources :udts, only: %i(index show update) do
get :download_csv, on: :collection
get :holder_allocation, on: :member
end
resources :xudts, only: %i(index show) do
get :download_csv, on: :collection
Expand Down
12 changes: 12 additions & 0 deletions db/migrate/20240625032839_create_udt_holder_allocations.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class CreateUdtHolderAllocations < ActiveRecord::Migration[7.0]
def change
create_table :udt_holder_allocations do |t|
t.bigint :udt_id, null: false, index: true
t.bigint :contract_id
t.integer :ckb_holder_count, null: false, default: 0
t.integer :btc_holder_count, null: false, default: 0

t.timestamps
end
end
end
59 changes: 58 additions & 1 deletion db/structure.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2400,6 +2400,40 @@ CREATE SEQUENCE public.udt_accounts_id_seq
ALTER SEQUENCE public.udt_accounts_id_seq OWNED BY public.udt_accounts.id;


--
-- Name: udt_holder_allocations; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.udt_holder_allocations (
id bigint NOT NULL,
udt_id bigint NOT NULL,
contract_id bigint,
ckb_holder_count integer DEFAULT 0 NOT NULL,
btc_holder_count integer DEFAULT 0 NOT NULL,
created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL
);


--
-- Name: udt_holder_allocations_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.udt_holder_allocations_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;


--
-- Name: udt_holder_allocations_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.udt_holder_allocations_id_seq OWNED BY public.udt_holder_allocations.id;


--
-- Name: udt_transactions; Type: TABLE; Schema: public; Owner: -
--
Expand Down Expand Up @@ -3010,6 +3044,13 @@ ALTER TABLE ONLY public.type_scripts ALTER COLUMN id SET DEFAULT nextval('public
ALTER TABLE ONLY public.udt_accounts ALTER COLUMN id SET DEFAULT nextval('public.udt_accounts_id_seq'::regclass);


--
-- Name: udt_holder_allocations id; Type: DEFAULT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.udt_holder_allocations ALTER COLUMN id SET DEFAULT nextval('public.udt_holder_allocations_id_seq'::regclass);


--
-- Name: udt_verifications id; Type: DEFAULT; Schema: public; Owner: -
--
Expand Down Expand Up @@ -3532,6 +3573,14 @@ ALTER TABLE ONLY public.udt_accounts
ADD CONSTRAINT udt_accounts_pkey PRIMARY KEY (id);


--
-- Name: udt_holder_allocations udt_holder_allocations_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.udt_holder_allocations
ADD CONSTRAINT udt_holder_allocations_pkey PRIMARY KEY (id);


--
-- Name: udt_verifications udt_verifications_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
Expand Down Expand Up @@ -4563,6 +4612,13 @@ CREATE UNIQUE INDEX index_udt_accounts_on_type_hash_and_address_id ON public.udt
CREATE INDEX index_udt_accounts_on_udt_id ON public.udt_accounts USING btree (udt_id);


--
-- Name: index_udt_holder_allocations_on_udt_id; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX index_udt_holder_allocations_on_udt_id ON public.udt_holder_allocations USING btree (udt_id);


--
-- Name: index_udt_transactions_on_ckb_transaction_id; Type: INDEX; Schema: public; Owner: -
--
Expand Down Expand Up @@ -5181,6 +5237,7 @@ INSERT INTO "schema_migrations" (version) VALUES
('20240507041552'),
('20240509074313'),
('20240513055849'),
('20240620083123');
('20240620083123'),
('20240625032839');


5 changes: 5 additions & 0 deletions test/factories/udt_holder_allocations.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FactoryBot.define do
factory :udt_holder_allocation do

end
end
7 changes: 7 additions & 0 deletions test/models/udt_holder_allocation_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require "test_helper"

class UdtHolderAllocationTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end

0 comments on commit 7af8f65

Please sign in to comment.