diff --git a/VERSION b/VERSION
index f1547e6..815e68d 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.0.7
+2.0.8
diff --git a/assets/btc-0/info.json b/assets/btc-0/info.json
index 76329b9..fc8d2a0 100644
--- a/assets/btc-0/info.json
+++ b/assets/btc-0/info.json
@@ -1,5 +1,28 @@
{
- "contracts": [],
+ "contracts": [
+ {
+ "address": "1111111111111111111114oLvT2",
+ "decimals": 8,
+ "name": "Bitcoin",
+ "network": "bitcoin-1",
+ "symbol": "BTC",
+ "tags": [
+ "mainnet",
+ "native-coin"
+ ]
+ },
+ {
+ "address": "1111111111111111111114oLvT2",
+ "decimals": 8,
+ "name": "Bitcoin",
+ "network": "bitcoin-2",
+ "symbol": "BTC",
+ "tags": [
+ "native-coin",
+ "testnet"
+ ]
+ }
+ ],
"id": "btc-0",
"images": {
"png128": true,
@@ -19,5 +42,7 @@
"url": "https://www.coingecko.com/en/coins/bitcoin"
}
],
- "tags": []
+ "tags": [
+ "bitcoin"
+ ]
}
diff --git a/assets/unknown-bitcoin/image-128.png b/assets/unknown-bitcoin/image-128.png
new file mode 100644
index 0000000..337897e
Binary files /dev/null and b/assets/unknown-bitcoin/image-128.png differ
diff --git a/assets/unknown-bitcoin/image-256.png b/assets/unknown-bitcoin/image-256.png
new file mode 100644
index 0000000..882c78f
Binary files /dev/null and b/assets/unknown-bitcoin/image-256.png differ
diff --git a/assets/unknown-bitcoin/image-32.png b/assets/unknown-bitcoin/image-32.png
new file mode 100644
index 0000000..66c269a
Binary files /dev/null and b/assets/unknown-bitcoin/image-32.png differ
diff --git a/assets/unknown-bitcoin/image-64.png b/assets/unknown-bitcoin/image-64.png
new file mode 100644
index 0000000..eb3a283
Binary files /dev/null and b/assets/unknown-bitcoin/image-64.png differ
diff --git a/assets/unknown-bitcoin/image.svg b/assets/unknown-bitcoin/image.svg
new file mode 100644
index 0000000..b648d44
--- /dev/null
+++ b/assets/unknown-bitcoin/image.svg
@@ -0,0 +1,13 @@
+
diff --git a/assets/unknown-bitcoin/info.json b/assets/unknown-bitcoin/info.json
new file mode 100644
index 0000000..75f383d
--- /dev/null
+++ b/assets/unknown-bitcoin/info.json
@@ -0,0 +1,16 @@
+{
+ "contracts": [],
+ "id": "unknown-bitcoin",
+ "images": {
+ "png128": true,
+ "png256": true,
+ "png32": true,
+ "png64": true,
+ "svg": true
+ },
+ "name": "Unknown Bitcoin Asset",
+ "references": [],
+ "tags": [
+ "bitcoin"
+ ]
+}
diff --git a/enums/ids/asset.json b/enums/ids/asset.json
index 451754f..4504222 100644
--- a/enums/ids/asset.json
+++ b/enums/ids/asset.json
@@ -1235,6 +1235,10 @@
"description": "Unknown Bifrost Asset",
"value": "unknown-bifrost"
},
+ {
+ "description": "Unknown Bitcoin Asset",
+ "value": "unknown-bitcoin"
+ },
{
"description": "Unknown BNB Asset",
"value": "unknown-bnb"
diff --git a/enums/ids/network.json b/enums/ids/network.json
index 31be78b..597cfa1 100644
--- a/enums/ids/network.json
+++ b/enums/ids/network.json
@@ -1,4 +1,12 @@
[
+ {
+ "description": "Bitcoin Mainnet",
+ "value": "bitcoin-1"
+ },
+ {
+ "description": "Bitcoin Testnet",
+ "value": "bitcoin-2"
+ },
{
"description": "Ethereum Mainnet",
"value": "evm-1"
diff --git a/enums/tags/asset.json b/enums/tags/asset.json
index 75d34d1..a8ffc8d 100644
--- a/enums/tags/asset.json
+++ b/enums/tags/asset.json
@@ -15,6 +15,10 @@
"value": "bifrost",
"description": "Main network of asset is Bifrost"
},
+ {
+ "value": "bitcoin",
+ "description": "Main network of asset is Bitcoin"
+ },
{
"value": "bnb",
"description": "Main network of asset is BNB chain"
diff --git a/enums/tags/network.json b/enums/tags/network.json
index 8c7fb09..334dd2d 100644
--- a/enums/tags/network.json
+++ b/enums/tags/network.json
@@ -15,6 +15,10 @@
"value": "bifrost",
"description": "Bifrost networks"
},
+ {
+ "value": "bitcoin",
+ "description": "Bitcoin networks"
+ },
{
"value": "bnb",
"description": "BNB networks"
diff --git a/libraries/models/terminals/address.py b/libraries/models/terminals/address.py
index 93231e1..101b4c3 100644
--- a/libraries/models/terminals/address.py
+++ b/libraries/models/terminals/address.py
@@ -1,16 +1,21 @@
-from typing import Union, Self
+from typing import Union, Self, get_args
-from pydantic import RootModel
+from pydantic import RootModel, model_validator
+from libraries.models.terminals.address_bitcoin import AddressBitcoin
from libraries.models.terminals.address_evm import AddressEvm
+ADDRESS_TYPES = Union[AddressEvm, AddressBitcoin]
-class Address(RootModel[Union[AddressEvm]]):
+
+class Address(RootModel[ADDRESS_TYPES]):
"""A union of constrained `str` about each address of blockchain networks."""
def __eq__(self, other: Self) -> bool:
if self.is_evm_address and other.is_evm_address:
return self.root.__eq__(other.root)
+ elif self.is_bitcoin_address and other.is_bitcoin_address:
+ return self.root.__eq__(other.root)
else:
raise ValueError(
f"Cannot compare difference addresses {type(self.root)} and {type(other.root)}."
@@ -22,6 +27,8 @@ def __ne__(self, other: Self) -> bool:
def __lt__(self, other: Self) -> bool:
if self.is_evm_address and other.is_evm_address:
return self.root.__lt__(other.root)
+ elif self.is_bitcoin_address and other.is_bitcoin_address:
+ return self.root.__lt__(other.root)
else:
raise ValueError(
f"Cannot compare difference addresses {type(self.root)} and {type(other.root)}."
@@ -50,3 +57,26 @@ def is_evm_address(self) -> bool:
Whether the address is an EVM address.
"""
return isinstance(self.root, AddressEvm)
+
+ @property
+ def is_bitcoin_address(self) -> bool:
+ """Check if the address is a Bitcoin address.
+
+ Returns:
+ Whether the address is a Bitcoin address.
+ """
+ return isinstance(self.root, AddressBitcoin)
+
+ @model_validator(mode="after")
+ def validator(self) -> Self:
+ root_types = get_args(ADDRESS_TYPES)
+ if type(self.root) in root_types:
+ return self
+ elif isinstance(self.root, str):
+ for root_type in root_types:
+ try:
+ self.root = root_type(self.root)
+ return self
+ except ValueError:
+ continue
+ raise ValueError(f"Invalid address: {self.root}")
diff --git a/libraries/models/terminals/address_bitcoin.py b/libraries/models/terminals/address_bitcoin.py
new file mode 100644
index 0000000..ce807b6
--- /dev/null
+++ b/libraries/models/terminals/address_bitcoin.py
@@ -0,0 +1,51 @@
+from typing import Self
+
+from bitcoinlib.encoding import EncodingError
+from bitcoinlib.keys import deserialize_address
+
+from libraries.models.templates.str_model import StrModel
+
+
+class AddressBitcoin(StrModel):
+ """A constrained `str` for the Bitcoin address."""
+
+ @property
+ def __deserialized_result(self) -> dict:
+ """The result from the deserialization the Bitcoin address."""
+ return deserialize_address(self.root)
+
+ @property
+ def public_key_hash(self) -> str:
+ """The public key hash of the Bitcoin address."""
+ return self.__deserialized_result.get("public_key_hash")
+
+ @property
+ def encoding_type(self) -> str:
+ """The encoding type of the Bitcoin address."""
+ return self.__deserialized_result.get("encoding")
+
+ @property
+ def script_type(self) -> str:
+ """The script type of the Bitcoin address."""
+ return self.__deserialized_result.get("script_type")
+
+ def __eq__(self, other: Self | str) -> bool:
+ match other:
+ case AddressBitcoin():
+ return self.root.lower() == other.root.lower()
+ case str():
+ return self.root.lower() == other.lower()
+ case _:
+ raise ValueError(f"Cannot compare {self} with {other}")
+
+ def __lt__(self, other: Self) -> bool:
+ return self.public_key_hash < other.public_key_hash
+
+ def __hash__(self) -> int:
+ return hash(self.public_key_hash)
+
+ def validate_str(self) -> Self:
+ try:
+ return deserialize_address(self.root).get("address")
+ except EncodingError:
+ raise ValueError(f"Invalid Bitcoin address: {self.root}")
diff --git a/libraries/models/terminals/engine.py b/libraries/models/terminals/engine.py
index 4701759..e093143 100644
--- a/libraries/models/terminals/engine.py
+++ b/libraries/models/terminals/engine.py
@@ -13,6 +13,7 @@ class _EngineEnum(StrEnum):
"""
EVM: str = "evm"
+ BITCOIN: str = "bitcoin"
UNKNOWN: str = "unknown"
@@ -28,6 +29,15 @@ def is_evm(self) -> bool:
"""
return self.root == _EngineEnum.EVM
+ @property
+ def is_bitcoin(self) -> bool:
+ """Check if the engine is a Bitcoin engine.
+
+ Returns:
+ Whether the engine is a Bitcoin engine.
+ """
+ return self.root == _EngineEnum.BITCOIN
+
@property
def is_unknown(self) -> bool:
"""Check if the engine is an unknown engine.
diff --git a/networks/bitcoin-1/image-128.png b/networks/bitcoin-1/image-128.png
new file mode 100644
index 0000000..458a5a8
Binary files /dev/null and b/networks/bitcoin-1/image-128.png differ
diff --git a/networks/bitcoin-1/image-256.png b/networks/bitcoin-1/image-256.png
new file mode 100644
index 0000000..21dbb9f
Binary files /dev/null and b/networks/bitcoin-1/image-256.png differ
diff --git a/networks/bitcoin-1/image-32.png b/networks/bitcoin-1/image-32.png
new file mode 100644
index 0000000..c3acebe
Binary files /dev/null and b/networks/bitcoin-1/image-32.png differ
diff --git a/networks/bitcoin-1/image-64.png b/networks/bitcoin-1/image-64.png
new file mode 100644
index 0000000..62ad717
Binary files /dev/null and b/networks/bitcoin-1/image-64.png differ
diff --git a/networks/bitcoin-1/image.svg b/networks/bitcoin-1/image.svg
new file mode 100644
index 0000000..d6b71fc
--- /dev/null
+++ b/networks/bitcoin-1/image.svg
@@ -0,0 +1,6 @@
+
diff --git a/networks/bitcoin-1/info.json b/networks/bitcoin-1/info.json
new file mode 100644
index 0000000..cb7b72e
--- /dev/null
+++ b/networks/bitcoin-1/info.json
@@ -0,0 +1,25 @@
+{
+ "currency": {
+ "address": "1111111111111111111114oLvT2",
+ "decimals": 8,
+ "id": "btc-0",
+ "name": "Bitcoin",
+ "symbol": "BTC"
+ },
+ "engine": "bitcoin",
+ "explorers": [],
+ "id": "bitcoin-1",
+ "images": {
+ "png128": true,
+ "png256": true,
+ "png32": true,
+ "png64": true,
+ "svg": true
+ },
+ "name": "Bitcoin Mainnet",
+ "network": "mainnet",
+ "tags": [
+ "bitcoin"
+ ],
+ "unknownAssetId": "unknown-bitcoin"
+}
diff --git a/networks/bitcoin-2/image-128.png b/networks/bitcoin-2/image-128.png
new file mode 100644
index 0000000..337897e
Binary files /dev/null and b/networks/bitcoin-2/image-128.png differ
diff --git a/networks/bitcoin-2/image-256.png b/networks/bitcoin-2/image-256.png
new file mode 100644
index 0000000..882c78f
Binary files /dev/null and b/networks/bitcoin-2/image-256.png differ
diff --git a/networks/bitcoin-2/image-32.png b/networks/bitcoin-2/image-32.png
new file mode 100644
index 0000000..66c269a
Binary files /dev/null and b/networks/bitcoin-2/image-32.png differ
diff --git a/networks/bitcoin-2/image-64.png b/networks/bitcoin-2/image-64.png
new file mode 100644
index 0000000..eb3a283
Binary files /dev/null and b/networks/bitcoin-2/image-64.png differ
diff --git a/networks/bitcoin-2/image.svg b/networks/bitcoin-2/image.svg
new file mode 100644
index 0000000..b6682af
--- /dev/null
+++ b/networks/bitcoin-2/image.svg
@@ -0,0 +1,13 @@
+
diff --git a/networks/bitcoin-2/info.json b/networks/bitcoin-2/info.json
new file mode 100644
index 0000000..d4ccf7b
--- /dev/null
+++ b/networks/bitcoin-2/info.json
@@ -0,0 +1,25 @@
+{
+ "currency": {
+ "address": "1111111111111111111114oLvT2",
+ "decimals": 8,
+ "id": "btc-0",
+ "name": "Bitcoin",
+ "symbol": "BTC"
+ },
+ "engine": "bitcoin",
+ "explorers": [],
+ "id": "bitcoin-2",
+ "images": {
+ "png128": true,
+ "png256": true,
+ "png32": true,
+ "png64": true,
+ "svg": true
+ },
+ "name": "Bitcoin Testnet",
+ "network": "testnet",
+ "tags": [
+ "bitcoin"
+ ],
+ "unknownAssetId": "unknown-bitcoin"
+}
diff --git a/requirements/essential.txt b/requirements/essential.txt
index 335a9fe..8e48c57 100644
--- a/requirements/essential.txt
+++ b/requirements/essential.txt
@@ -1,5 +1,6 @@
# Specifies the essential requirements of the project.
+bitcoinlib>=0.6.15
+build>=1.2.1
hexbytes>=0.3.1
pydantic>=2.5.3
web3>=6.14.0
-build>=1.2.1