From 18f9d8db89fa3d2888ef17fe14db7901d846bbda Mon Sep 17 00:00:00 2001 From: Gaze Date: Tue, 16 Jul 2024 19:41:15 +0700 Subject: [PATCH 01/21] feat: add get tokens api --- electrumx/server/session/http_unified_api.py | 131 +++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index a9335cd..8af1dac 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -136,6 +136,7 @@ def mount_routes(self, router: UrlDispatcher): router.add_get("/v2/arc20/holders/{id}", self.get_arc20_holders) router.add_get("/v2/arc20/info/{id}", self.get_arc20_token) router.add_get("/v2/arc20/utxos/wallet/{wallet}", self.get_arc20_utxos) + router.add_get("/v2/arc20/tokens", self.get_arc20_token_list) def _resolve_ticker_to_atomical_id(self, ticker: str) -> bytes | None: bp = self.session_mgr.bp @@ -1000,6 +1001,136 @@ async def get_arc20_token(self, request: "Request") -> "Response": } ) + @error_handler + async def get_arc20_token_list(self, request: "Request") -> "Response": + infos = [] + atomical_ids = await self.session_mgr.db.get_atomicals_list(10,0,asc=true) + for atomical_id in atomical_ids: + # get data + atomical: dict = await self._get_atomical(atomical_id) + + atomical_type = atomical.get("type", "") + if atomical_type == "NFT": + return format_response(None, 400, "NFT is not supported.") + + subtype = atomical.get("subtype", "") + mint_mode = atomical.get("$mint_mode", "") + mint_info: dict = atomical.get("mint_info", {}) + mint_info_args: dict = mint_info.get("args", {}) + ticker = atomical.get("$ticker", "") + + mint_count = 0 + minted_amount = 0 + max_supply = 0 + mint_amount = 0 # mint size + + if atomical_type == "FT": + if mint_mode == "fixed": + max_supply = atomical.get("$max_supply", 0) + mint_amount = mint_info_args.get("mint_amount", 0) + else: + max_supply = atomical.get("$max_supply", -1) + # NOTE: unsupported + # elif atomical_type == "NFT": + else: + raise Exception("unreachable code: invalid atomical type") + + if subtype == "decentralized": + atomical: dict = await self.session_mgr.bp.get_dft_mint_info_rpc_format_by_atomical_id(atomical_id) + mint_count = atomical["dft_info"]["mint_count"] + minted_amount = mint_count * mint_amount # total minted + elif subtype == "direct": + atomical: dict = await self.session_mgr.bp.get_ft_mint_info_rpc_format_by_atomical_id(atomical_id) + minted_amount = max_supply # entire mint in direct mint + else: + raise Exception("unreachable code: invalid subtype") + + location_summary: dict = atomical.get("location_summary", {}) + holder_count = location_summary.get("unique_holders", 0) + circulating_supply = location_summary.get("circulating_supply", 0) + + # deployment data + commit_tx_id = mint_info.get("commit_txid") + commit_tx_height = mint_info.get("commit_height") + + reveal_location_script: str = mint_info.get("reveal_location_script") + deployer_address = get_address_from_output_script(bytes.fromhex(reveal_location_script)) + if not deployer_address: + deployer_address = "" + + deployed_at_height = commit_tx_height + deployed_at = self._block_height_to_unix_timestamp(deployed_at_height) + deploy_tx_hash = commit_tx_id + + # change time-sensitive data from block_height + if block_height != latest_block_height: + # support only atomical FT + data = await self._get_arc20_holders_by_block_height(atomical_id, block_height) + holder_count = data["count"] + circulating_supply = data["total"] + if subtype == "decentralized": + mint_count = await self.session_mgr.db.get_atomical_mint_count_at_height(atomical_id, block_height) + minted_amount = mint_count * mint_amount + + # mint completion data + completed_at_height = None + completed_at = None # unix timestamp + if minted_amount == max_supply: + if subtype == "direct": + completed_at_height = deployed_at_height + completed_at = deployed_at + else: + completed_at_height = self.session_mgr.db.get_mint_completed_at_height(atomical_id) + if completed_at_height: + completed_at = self._block_height_to_unix_timestamp(completed_at_height) + # clear historical data + if completed_at_height and completed_at_height > block_height: + completed_at_height = None + completed_at = None + + compact_atomical_id = location_id_bytes_to_compact(atomical_id) + + infos.append({ + "id": compact_atomical_id, + "name": ticker, + "symbol": ticker, + "totalSupply": str(max_supply), + "circulatingSupply": str(circulating_supply), + "mintedAmount": str(minted_amount), + "burnedAmount": str(minted_amount - circulating_supply), + "decimals": get_decimals(), + "deployedAt": deployed_at, + "deployedAtHeight": deployed_at_height, + "deployTxHash": deploy_tx_hash, + "completedAt": completed_at, + "completedAtHeight": completed_at_height, + "holdersCount": holder_count, + # arc20-specific data + "extend": { + "atomicalId": compact_atomical_id, + "atomicalNumber": atomical.get("atomical_number", 0), + "atomicalRef": atomical.get("atomical_ref", ""), + "amountPerMint": str(mint_amount), + "maxMints": str(atomical.get("$max_mints", 0)), # number of times this token can be minted + "deployedBy": deployer_address, + "mintHeight": atomical.get("$mint_height", 0), # the block height this FT can start to be minted + "mintInfo": { + "commitTxHash": commit_tx_id, + # commit tx output index of utxo used in reveal tx + "commitIndex": mint_info.get("commit_index"), + "revealTxHash": mint_info.get("reveal_location_txid"), + "revealIndex": mint_info.get("reveal_location_index"), + "args": mint_info_args, # raw atomicals operation payload + "metadata": mint_info.get("meta", {}), # metadata.json used during deployment + }, + "subtype": subtype, + "mintMode": mint_mode, + }, + }) + return format_response({ + list: infos + }) + async def _utxo_to_formatted(self, utxo: UTXO) -> "dict": # TODO: use data from AtomicalUTXO only when it has atomical_id in it tx_id_str = hash_to_hex_str(utxo.tx_hash) From d6523369d0f69281819e5a813b0890aa64a118db Mon Sep 17 00:00:00 2001 From: Gaze Date: Tue, 16 Jul 2024 20:36:25 +0700 Subject: [PATCH 02/21] fix: wrong boolean syntax --- electrumx/server/session/http_unified_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index 8af1dac..49cbe46 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1004,7 +1004,7 @@ async def get_arc20_token(self, request: "Request") -> "Response": @error_handler async def get_arc20_token_list(self, request: "Request") -> "Response": infos = [] - atomical_ids = await self.session_mgr.db.get_atomicals_list(10,0,asc=true) + atomical_ids = await self.session_mgr.db.get_atomicals_list(10,0,asc=True) for atomical_id in atomical_ids: # get data atomical: dict = await self._get_atomical(atomical_id) From 491b39103d63bad70f7f5b688342a560aef4a9de Mon Sep 17 00:00:00 2001 From: Gaze Date: Tue, 16 Jul 2024 20:40:26 +0700 Subject: [PATCH 03/21] feat: add latest block height --- electrumx/server/session/http_unified_api.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index 49cbe46..955b236 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1003,6 +1003,8 @@ async def get_arc20_token(self, request: "Request") -> "Response": @error_handler async def get_arc20_token_list(self, request: "Request") -> "Response": + block_height = self.session_mgr.db.db_height + infos = [] atomical_ids = await self.session_mgr.db.get_atomicals_list(10,0,asc=True) for atomical_id in atomical_ids: @@ -1062,16 +1064,6 @@ async def get_arc20_token_list(self, request: "Request") -> "Response": deployed_at = self._block_height_to_unix_timestamp(deployed_at_height) deploy_tx_hash = commit_tx_id - # change time-sensitive data from block_height - if block_height != latest_block_height: - # support only atomical FT - data = await self._get_arc20_holders_by_block_height(atomical_id, block_height) - holder_count = data["count"] - circulating_supply = data["total"] - if subtype == "decentralized": - mint_count = await self.session_mgr.db.get_atomical_mint_count_at_height(atomical_id, block_height) - minted_amount = mint_count * mint_amount - # mint completion data completed_at_height = None completed_at = None # unix timestamp From 1e25c110bbdec5c2e425d56b06096364952e6024 Mon Sep 17 00:00:00 2001 From: Gaze Date: Tue, 16 Jul 2024 20:42:46 +0700 Subject: [PATCH 04/21] feat: skip nft --- electrumx/server/session/http_unified_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index 955b236..9ee293a 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1013,7 +1013,7 @@ async def get_arc20_token_list(self, request: "Request") -> "Response": atomical_type = atomical.get("type", "") if atomical_type == "NFT": - return format_response(None, 400, "NFT is not supported.") + continue subtype = atomical.get("subtype", "") mint_mode = atomical.get("$mint_mode", "") From e5b4174188498dadaf887eb00f4c613435650f3b Mon Sep 17 00:00:00 2001 From: Gaze Date: Tue, 16 Jul 2024 20:49:39 +0700 Subject: [PATCH 05/21] fix: dick syntax --- electrumx/server/session/http_unified_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index 9ee293a..ee74cb7 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1120,7 +1120,7 @@ async def get_arc20_token_list(self, request: "Request") -> "Response": }, }) return format_response({ - list: infos + "list": infos, }) async def _utxo_to_formatted(self, utxo: UTXO) -> "dict": From f3781293d5014e35ee1a8ad79cb1e903c19085e4 Mon Sep 17 00:00:00 2001 From: Gaze Date: Tue, 16 Jul 2024 20:58:18 +0700 Subject: [PATCH 06/21] feat: suppport limit, offset query --- electrumx/server/session/http_unified_api.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index ee74cb7..75c0fdd 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -45,6 +45,7 @@ class MAX_LIMIT: get_arc20_transactions = 3000 get_arc20_holders = 1000 get_arc20_utxos = 3000 # large limit since pagination does not help performance + get_arc20_token_list = 1000 class DEFAULT_LIMIT: @@ -52,6 +53,7 @@ class DEFAULT_LIMIT: get_arc20_transactions = 100 get_arc20_holders = 100 get_arc20_utxos = 100 + get_arc20_token_list = 100 class JSONBytesEncoder(json.JSONEncoder): @@ -1003,10 +1005,17 @@ async def get_arc20_token(self, request: "Request") -> "Response": @error_handler async def get_arc20_token_list(self, request: "Request") -> "Response": + limit, offset = self._parse_limit_offset( + request.query.get("limit"), + request.query.get("offset"), + DEFAULT_LIMIT.get_arc20_token_list, + MAX_LIMIT.get_arc20_token_list, + ) + block_height = self.session_mgr.db.db_height infos = [] - atomical_ids = await self.session_mgr.db.get_atomicals_list(10,0,asc=True) + atomical_ids = await self.session_mgr.db.get_atomicals_list(limit,offset,asc=True) for atomical_id in atomical_ids: # get data atomical: dict = await self._get_atomical(atomical_id) From 6e338146ef7fe1a258bcd3e8f1dc651716169fbf Mon Sep 17 00:00:00 2001 From: Gaze Date: Tue, 16 Jul 2024 21:23:50 +0700 Subject: [PATCH 07/21] feat: add todo note --- electrumx/server/session/http_unified_api.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index 75c0fdd..913cea7 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1012,10 +1012,10 @@ async def get_arc20_token_list(self, request: "Request") -> "Response": MAX_LIMIT.get_arc20_token_list, ) - block_height = self.session_mgr.db.db_height infos = [] - atomical_ids = await self.session_mgr.db.get_atomicals_list(limit,offset,asc=True) + atomical_ids = await self._get_atomicals_ft_list(limit,offset,asc=True) + block_height = self.session_mgr.db.db_height for atomical_id in atomical_ids: # get data atomical: dict = await self._get_atomical(atomical_id) @@ -1131,6 +1131,12 @@ async def get_arc20_token_list(self, request: "Request") -> "Response": return format_response({ "list": infos, }) + async def _get_atomicals_ft_list(self, limit, offset, asc=True) -> list: + atomical_ids = await self.session_mgr.db.get_atomicals_list(limit, offset, asc) + # TODO: + # - implement logic from `electrumx/server/db.py.get_atomicals_list()` + # - each iteration should call `self.session_mgr.bp.get_atomicals_id_mint_info(atomical_id, True) to check type is FT or NFT` + return atomical_ids async def _utxo_to_formatted(self, utxo: UTXO) -> "dict": # TODO: use data from AtomicalUTXO only when it has atomical_id in it From 820b9ccaa3c27bf3eb8f079d6577ae454aaf3de9 Mon Sep 17 00:00:00 2001 From: Gaze Date: Wed, 17 Jul 2024 21:26:35 +0700 Subject: [PATCH 08/21] feat: custom atomicals list func --- electrumx/server/session/http_unified_api.py | 56 ++++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index 913cea7..f6b938e 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1133,10 +1133,58 @@ async def get_arc20_token_list(self, request: "Request") -> "Response": }) async def _get_atomicals_ft_list(self, limit, offset, asc=True) -> list: atomical_ids = await self.session_mgr.db.get_atomicals_list(limit, offset, asc) - # TODO: - # - implement logic from `electrumx/server/db.py.get_atomicals_list()` - # - each iteration should call `self.session_mgr.bp.get_atomicals_id_mint_info(atomical_id, True) to check type is FT or NFT` - return atomical_ids + if limit > 100: + limit = 100 + atomical_number_tip = self.session_mgr.db.db_atomical_count + + def read_atomical_list(): + atomical_ids = [] + # If no offset provided, then assume we want to start from the highest one + search_starting_at_atomical_number = atomical_number_tip + if offset >= 0: + search_starting_at_atomical_number = offset + elif offset < 0: + # if offset is negative, then we assume it is subtracted from the latest number + search_starting_at_atomical_number = atomical_number_tip + offset # adding a minus + + # safety checking for less than 0 + if search_starting_at_atomical_number < 0: + search_starting_at_atomical_number = 0 + + # Generate up to limit number of keys to search + list_of_keys = [] + x = 0 + while x < limit: + if asc: + current_key = b"n" + pack_be_uint64(search_starting_at_atomical_number + x) + atomical_id_value = self.utxo_db.get(current_key) + if atomical_id_value: + init_mint_info = self.session_mgr.bp.get_atomicals_id_mint_info(atomical_id_value, True) + if not init_mint_info: + continue + if init_mint_info["type"] != "FT": + continue + atomical_ids.append(atomical_id_value) + x += 1 + else: + break + else: + if search_starting_at_atomical_number - x < 0: + break + current_key = b"n" + pack_be_uint64(search_starting_at_atomical_number - x) + atomical_id_value = self.utxo_db.get(current_key) + if atomical_id_value: + init_mint_info = self.session_mgr.bp.get_atomicals_id_mint_info(atomical_id_value, True) + if not init_mint_info: + continue + if init_mint_info["type"] != "FT": + continue + atomical_ids.append(atomical_id_value) + x += 1 + else: + break + return atomical_ids + return await run_in_thread(read_atomical_list) async def _utxo_to_formatted(self, utxo: UTXO) -> "dict": # TODO: use data from AtomicalUTXO only when it has atomical_id in it From 86cd9ddcda85d01a273bdfe3c1b2b695b8afcd57 Mon Sep 17 00:00:00 2001 From: Gaze Date: Wed, 17 Jul 2024 21:31:03 +0700 Subject: [PATCH 09/21] feat: import packuint64 --- electrumx/server/session/http_unified_api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index f6b938e..7c67915 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -28,6 +28,9 @@ ) from electrumx.server.db import UTXO, AtomicalUTXO from electrumx.server.history import TXNUM_LEN +from electrumx.lib.util import ( + pack_be_uint64, +) if TYPE_CHECKING: from aiohttp.web import Request, Response From e2c257f063f970ac6e95fb3d1d49e860339e4428 Mon Sep 17 00:00:00 2001 From: Gaze Date: Wed, 17 Jul 2024 21:35:57 +0700 Subject: [PATCH 10/21] fix --- electrumx/server/session/http_unified_api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index 7c67915..d46dcb7 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1135,7 +1135,7 @@ async def get_arc20_token_list(self, request: "Request") -> "Response": "list": infos, }) async def _get_atomicals_ft_list(self, limit, offset, asc=True) -> list: - atomical_ids = await self.session_mgr.db.get_atomicals_list(limit, offset, asc) + # atomical_ids = await self.session_mgr.db.get_atomicals_list(limit, offset, asc) if limit > 100: limit = 100 atomical_number_tip = self.session_mgr.db.db_atomical_count @@ -1160,7 +1160,7 @@ def read_atomical_list(): while x < limit: if asc: current_key = b"n" + pack_be_uint64(search_starting_at_atomical_number + x) - atomical_id_value = self.utxo_db.get(current_key) + atomical_id_value = self.session_mgr.db.utxo_db.get(current_key) if atomical_id_value: init_mint_info = self.session_mgr.bp.get_atomicals_id_mint_info(atomical_id_value, True) if not init_mint_info: @@ -1175,7 +1175,7 @@ def read_atomical_list(): if search_starting_at_atomical_number - x < 0: break current_key = b"n" + pack_be_uint64(search_starting_at_atomical_number - x) - atomical_id_value = self.utxo_db.get(current_key) + atomical_id_value = self.session_mgr.db.utxo_db.get(current_key) if atomical_id_value: init_mint_info = self.session_mgr.bp.get_atomicals_id_mint_info(atomical_id_value, True) if not init_mint_info: From 2cf22c4ae02b3c2f51353a8aad8d2524aaaa6ffd Mon Sep 17 00:00:00 2001 From: Gaze Date: Wed, 17 Jul 2024 21:56:58 +0700 Subject: [PATCH 11/21] feat: update loop condition --- electrumx/server/session/http_unified_api.py | 31 +++++++++----------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index d46dcb7..6852cb1 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1136,8 +1136,8 @@ async def get_arc20_token_list(self, request: "Request") -> "Response": }) async def _get_atomicals_ft_list(self, limit, offset, asc=True) -> list: # atomical_ids = await self.session_mgr.db.get_atomicals_list(limit, offset, asc) - if limit > 100: - limit = 100 + if limit > 50: + limit = 50 atomical_number_tip = self.session_mgr.db.db_atomical_count def read_atomical_list(): @@ -1157,36 +1157,33 @@ def read_atomical_list(): # Generate up to limit number of keys to search list_of_keys = [] x = 0 + i = 0 while x < limit: if asc: - current_key = b"n" + pack_be_uint64(search_starting_at_atomical_number + x) + current_key = b"n" + pack_be_uint64(search_starting_at_atomical_number + i) atomical_id_value = self.session_mgr.db.utxo_db.get(current_key) if atomical_id_value: init_mint_info = self.session_mgr.bp.get_atomicals_id_mint_info(atomical_id_value, True) - if not init_mint_info: - continue - if init_mint_info["type"] != "FT": - continue - atomical_ids.append(atomical_id_value) - x += 1 + if bool(init_mint_info) && init_mint_info["type"] == "FT": + atomical_ids.append(atomical_id_value) + x += 1 else: break else: - if search_starting_at_atomical_number - x < 0: + if search_starting_at_atomical_number - i < 0: break - current_key = b"n" + pack_be_uint64(search_starting_at_atomical_number - x) + current_key = b"n" + pack_be_uint64(search_starting_at_atomical_number - i) atomical_id_value = self.session_mgr.db.utxo_db.get(current_key) if atomical_id_value: init_mint_info = self.session_mgr.bp.get_atomicals_id_mint_info(atomical_id_value, True) - if not init_mint_info: - continue - if init_mint_info["type"] != "FT": - continue - atomical_ids.append(atomical_id_value) - x += 1 + if bool(init_mint_info) && init_mint_info["type"] == "FT": + atomical_ids.append(atomical_id_value) + x += 1 else: break + i += 1 return atomical_ids + return await run_in_thread(read_atomical_list) async def _utxo_to_formatted(self, utxo: UTXO) -> "dict": From 1c4391c4a3a68738ccb3e5d5c93ede0e773926e5 Mon Sep 17 00:00:00 2001 From: Gaze Date: Wed, 17 Jul 2024 22:00:29 +0700 Subject: [PATCH 12/21] fix: and operation syntax --- electrumx/server/session/http_unified_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index 6852cb1..9082583 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1164,7 +1164,7 @@ def read_atomical_list(): atomical_id_value = self.session_mgr.db.utxo_db.get(current_key) if atomical_id_value: init_mint_info = self.session_mgr.bp.get_atomicals_id_mint_info(atomical_id_value, True) - if bool(init_mint_info) && init_mint_info["type"] == "FT": + if bool(init_mint_info) and init_mint_info["type"] == "FT": atomical_ids.append(atomical_id_value) x += 1 else: From 1e7bdee46ec8cd57ded5b20cf73259e7c7f19a1b Mon Sep 17 00:00:00 2001 From: Gaze Date: Wed, 17 Jul 2024 22:02:50 +0700 Subject: [PATCH 13/21] fix and operator syntax --- electrumx/server/session/http_unified_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index 9082583..868df28 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1176,7 +1176,7 @@ def read_atomical_list(): atomical_id_value = self.session_mgr.db.utxo_db.get(current_key) if atomical_id_value: init_mint_info = self.session_mgr.bp.get_atomicals_id_mint_info(atomical_id_value, True) - if bool(init_mint_info) && init_mint_info["type"] == "FT": + if bool(init_mint_info) and init_mint_info["type"] == "FT": atomical_ids.append(atomical_id_value) x += 1 else: From 6b19395b6c5abdc83978f1167d975b4551554b17 Mon Sep 17 00:00:00 2001 From: Gaze Date: Wed, 17 Jul 2024 22:16:35 +0700 Subject: [PATCH 14/21] feat: handle ft offset --- electrumx/server/session/http_unified_api.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index 868df28..9b4b107 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1149,24 +1149,27 @@ def read_atomical_list(): elif offset < 0: # if offset is negative, then we assume it is subtracted from the latest number search_starting_at_atomical_number = atomical_number_tip + offset # adding a minus - # safety checking for less than 0 if search_starting_at_atomical_number < 0: search_starting_at_atomical_number = 0 # Generate up to limit number of keys to search list_of_keys = [] - x = 0 i = 0 - while x < limit: + current = 0 + total = 0 + while total < limit: if asc: current_key = b"n" + pack_be_uint64(search_starting_at_atomical_number + i) atomical_id_value = self.session_mgr.db.utxo_db.get(current_key) if atomical_id_value: init_mint_info = self.session_mgr.bp.get_atomicals_id_mint_info(atomical_id_value, True) if bool(init_mint_info) and init_mint_info["type"] == "FT": - atomical_ids.append(atomical_id_value) - x += 1 + if offset >= current: + atomical_ids.append(atomical_id_value) + total += 1 + current += 1 + else: break else: @@ -1177,8 +1180,10 @@ def read_atomical_list(): if atomical_id_value: init_mint_info = self.session_mgr.bp.get_atomicals_id_mint_info(atomical_id_value, True) if bool(init_mint_info) and init_mint_info["type"] == "FT": - atomical_ids.append(atomical_id_value) - x += 1 + if offset >= current: + atomical_ids.append(atomical_id_value) + total += 1 + current += 1 else: break i += 1 From 8be2db1ea99405db5edebeabeb853384a359ad0f Mon Sep 17 00:00:00 2001 From: Gaze Date: Wed, 17 Jul 2024 22:19:40 +0700 Subject: [PATCH 15/21] fix: wrong condition --- electrumx/server/session/http_unified_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index 9b4b107..4cd8542 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1165,7 +1165,7 @@ def read_atomical_list(): if atomical_id_value: init_mint_info = self.session_mgr.bp.get_atomicals_id_mint_info(atomical_id_value, True) if bool(init_mint_info) and init_mint_info["type"] == "FT": - if offset >= current: + if current >= offset: atomical_ids.append(atomical_id_value) total += 1 current += 1 @@ -1180,7 +1180,7 @@ def read_atomical_list(): if atomical_id_value: init_mint_info = self.session_mgr.bp.get_atomicals_id_mint_info(atomical_id_value, True) if bool(init_mint_info) and init_mint_info["type"] == "FT": - if offset >= current: + if current >= offset: atomical_ids.append(atomical_id_value) total += 1 current += 1 From ec0727a892703c17dd222c3d194ef49e018b1bd0 Mon Sep 17 00:00:00 2001 From: Gaze Date: Wed, 17 Jul 2024 22:23:08 +0700 Subject: [PATCH 16/21] feat: update limit --- electrumx/server/session/http_unified_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index 4cd8542..80dda42 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1136,8 +1136,8 @@ async def get_arc20_token_list(self, request: "Request") -> "Response": }) async def _get_atomicals_ft_list(self, limit, offset, asc=True) -> list: # atomical_ids = await self.session_mgr.db.get_atomicals_list(limit, offset, asc) - if limit > 50: - limit = 50 + if limit > 1000: + limit = 1000 atomical_number_tip = self.session_mgr.db.db_atomical_count def read_atomical_list(): From df21fa7b594c45e312ff2dea8e6cc670a377355f Mon Sep 17 00:00:00 2001 From: Gaze Date: Wed, 17 Jul 2024 22:34:57 +0700 Subject: [PATCH 17/21] feat: remove old offset logic --- electrumx/server/session/http_unified_api.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index 80dda42..dfefdb8 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1144,11 +1144,6 @@ def read_atomical_list(): atomical_ids = [] # If no offset provided, then assume we want to start from the highest one search_starting_at_atomical_number = atomical_number_tip - if offset >= 0: - search_starting_at_atomical_number = offset - elif offset < 0: - # if offset is negative, then we assume it is subtracted from the latest number - search_starting_at_atomical_number = atomical_number_tip + offset # adding a minus # safety checking for less than 0 if search_starting_at_atomical_number < 0: search_starting_at_atomical_number = 0 From e458a17e2dd26989836abac0b49f7850e7224a62 Mon Sep 17 00:00:00 2001 From: Gaze Date: Wed, 17 Jul 2024 22:39:23 +0700 Subject: [PATCH 18/21] feat: update offest logic --- electrumx/server/session/http_unified_api.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index dfefdb8..60281e9 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1142,12 +1142,7 @@ async def _get_atomicals_ft_list(self, limit, offset, asc=True) -> list: def read_atomical_list(): atomical_ids = [] - # If no offset provided, then assume we want to start from the highest one - search_starting_at_atomical_number = atomical_number_tip - # safety checking for less than 0 - if search_starting_at_atomical_number < 0: - search_starting_at_atomical_number = 0 - + search_starting_at_atomical_number = 0 # Generate up to limit number of keys to search list_of_keys = [] i = 0 @@ -1168,6 +1163,7 @@ def read_atomical_list(): else: break else: + # not supported yet if search_starting_at_atomical_number - i < 0: break current_key = b"n" + pack_be_uint64(search_starting_at_atomical_number - i) From 1b6345f9dd437f7a882c81e5253cce2e7177d067 Mon Sep 17 00:00:00 2001 From: Gaze Date: Thu, 18 Jul 2024 15:16:39 +0700 Subject: [PATCH 19/21] docs: update note --- electrumx/server/session/http_unified_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index 60281e9..2113780 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1135,7 +1135,7 @@ async def get_arc20_token_list(self, request: "Request") -> "Response": "list": infos, }) async def _get_atomicals_ft_list(self, limit, offset, asc=True) -> list: - # atomical_ids = await self.session_mgr.db.get_atomicals_list(limit, offset, asc) + # ref logic from self.session_mgr.db.get_atomicals_list(limit, offset, asc) if limit > 1000: limit = 1000 atomical_number_tip = self.session_mgr.db.db_atomical_count From 94e1f635570974707fe9b3336af10ffec3ef3b32 Mon Sep 17 00:00:00 2001 From: Gaze Date: Thu, 18 Jul 2024 15:17:10 +0700 Subject: [PATCH 20/21] feat: throw error if not support --- electrumx/server/session/http_unified_api.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index 2113780..b223360 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -1164,19 +1164,7 @@ def read_atomical_list(): break else: # not supported yet - if search_starting_at_atomical_number - i < 0: - break - current_key = b"n" + pack_be_uint64(search_starting_at_atomical_number - i) - atomical_id_value = self.session_mgr.db.utxo_db.get(current_key) - if atomical_id_value: - init_mint_info = self.session_mgr.bp.get_atomicals_id_mint_info(atomical_id_value, True) - if bool(init_mint_info) and init_mint_info["type"] == "FT": - if current >= offset: - atomical_ids.append(atomical_id_value) - total += 1 - current += 1 - else: - break + raise Exception("not supported yet") i += 1 return atomical_ids From 250e36e9f85518698da1216e359e0d6b522edd65 Mon Sep 17 00:00:00 2001 From: Gaze Date: Thu, 18 Jul 2024 15:23:14 +0700 Subject: [PATCH 21/21] feat: fix lint --- electrumx/server/session/http_unified_api.py | 92 ++++++++++---------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/electrumx/server/session/http_unified_api.py b/electrumx/server/session/http_unified_api.py index b223360..8baf0d7 100644 --- a/electrumx/server/session/http_unified_api.py +++ b/electrumx/server/session/http_unified_api.py @@ -20,6 +20,7 @@ get_address_from_output_script, get_script_from_address, ) +from electrumx.lib.util import pack_be_uint64 from electrumx.lib.util_atomicals import ( DFT_MINT_MAX_MAX_COUNT_DENSITY, compact_to_location_id_bytes, @@ -28,9 +29,6 @@ ) from electrumx.server.db import UTXO, AtomicalUTXO from electrumx.server.history import TXNUM_LEN -from electrumx.lib.util import ( - pack_be_uint64, -) if TYPE_CHECKING: from aiohttp.web import Request, Response @@ -1015,9 +1013,8 @@ async def get_arc20_token_list(self, request: "Request") -> "Response": MAX_LIMIT.get_arc20_token_list, ) - infos = [] - atomical_ids = await self._get_atomicals_ft_list(limit,offset,asc=True) + atomical_ids = await self._get_atomicals_ft_list(limit, offset, asc=True) block_height = self.session_mgr.db.db_height for atomical_id in atomical_ids: # get data @@ -1094,46 +1091,53 @@ async def get_arc20_token_list(self, request: "Request") -> "Response": compact_atomical_id = location_id_bytes_to_compact(atomical_id) - infos.append({ - "id": compact_atomical_id, - "name": ticker, - "symbol": ticker, - "totalSupply": str(max_supply), - "circulatingSupply": str(circulating_supply), - "mintedAmount": str(minted_amount), - "burnedAmount": str(minted_amount - circulating_supply), - "decimals": get_decimals(), - "deployedAt": deployed_at, - "deployedAtHeight": deployed_at_height, - "deployTxHash": deploy_tx_hash, - "completedAt": completed_at, - "completedAtHeight": completed_at_height, - "holdersCount": holder_count, - # arc20-specific data - "extend": { - "atomicalId": compact_atomical_id, - "atomicalNumber": atomical.get("atomical_number", 0), - "atomicalRef": atomical.get("atomical_ref", ""), - "amountPerMint": str(mint_amount), - "maxMints": str(atomical.get("$max_mints", 0)), # number of times this token can be minted - "deployedBy": deployer_address, - "mintHeight": atomical.get("$mint_height", 0), # the block height this FT can start to be minted - "mintInfo": { - "commitTxHash": commit_tx_id, - # commit tx output index of utxo used in reveal tx - "commitIndex": mint_info.get("commit_index"), - "revealTxHash": mint_info.get("reveal_location_txid"), - "revealIndex": mint_info.get("reveal_location_index"), - "args": mint_info_args, # raw atomicals operation payload - "metadata": mint_info.get("meta", {}), # metadata.json used during deployment + infos.append( + { + "id": compact_atomical_id, + "name": ticker, + "symbol": ticker, + "totalSupply": str(max_supply), + "circulatingSupply": str(circulating_supply), + "mintedAmount": str(minted_amount), + "burnedAmount": str(minted_amount - circulating_supply), + "decimals": get_decimals(), + "deployedAt": deployed_at, + "deployedAtHeight": deployed_at_height, + "deployTxHash": deploy_tx_hash, + "completedAt": completed_at, + "completedAtHeight": completed_at_height, + "holdersCount": holder_count, + # arc20-specific data + "extend": { + "atomicalId": compact_atomical_id, + "atomicalNumber": atomical.get("atomical_number", 0), + "atomicalRef": atomical.get("atomical_ref", ""), + "amountPerMint": str(mint_amount), + "maxMints": str(atomical.get("$max_mints", 0)), # number of times this token can be minted + "deployedBy": deployer_address, + "mintHeight": atomical.get( + "$mint_height", 0 + ), # the block height this FT can start to be minted + "mintInfo": { + "commitTxHash": commit_tx_id, + # commit tx output index of utxo used in reveal tx + "commitIndex": mint_info.get("commit_index"), + "revealTxHash": mint_info.get("reveal_location_txid"), + "revealIndex": mint_info.get("reveal_location_index"), + "args": mint_info_args, # raw atomicals operation payload + "metadata": mint_info.get("meta", {}), # metadata.json used during deployment + }, + "subtype": subtype, + "mintMode": mint_mode, }, - "subtype": subtype, - "mintMode": mint_mode, - }, - }) - return format_response({ - "list": infos, - }) + } + ) + return format_response( + { + "list": infos, + } + ) + async def _get_atomicals_ft_list(self, limit, offset, asc=True) -> list: # ref logic from self.session_mgr.db.get_atomicals_list(limit, offset, asc) if limit > 1000: