diff --git a/.env.example b/.env.example
index 0e20491..799d0b8 100644
--- a/.env.example
+++ b/.env.example
@@ -1,4 +1,4 @@
-# EVM CHAINS
+# EVM Chains
EVM_RPC_URL='https://sepolia.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161'
EVM_WS_URL='wss://sepolia.infura.io/ws/v3/9aa3d95b3bc440fa88ea12eaa4456161'
@@ -13,7 +13,7 @@ EVM_TRANSACTION_LISTENER_TEST_IS_ACTIVE=false
EVM_COIN_BALANCE_TEST_AMOUNT=0.01
EVM_TOKEN_BALANCE_TEST_AMOUNT=1000
EVM_NFT_BALANCE_TEST_AMOUNT=2
-EVM_TRANSFER_TEST_AMOUNT=0.0001
+EVM_TRANSFER_TEST_AMOUNT=0.01
EVM_TOKEN_TRANSFER_TEST_AMOUNT=1
EVM_TOKEN_APPROVE_TEST_AMOUNT=100
EVM_NFT_TRANSFER_ID=7
@@ -43,9 +43,9 @@ EVM_ETHER_TRANSFER_TX='0x566002399664e92f82ed654c181095bdd7ff3d3f1921d9632575858
EVM_TOKEN_TRANSFER_TX='0xdabda3905e585db91768f2ef877f7fbef7c0e8612c0a09c7b379981bdbc48975'
EVM_NFT_TRANSFER_TX='0x272a4698cd2062f2463481cf9eb78b68b35d59938383679b7642e6d669ac87eb'
# Models
-# EVM CHAINS
+# EVM Chains
-#TRON
+#Tron
# Assets
TRON_COIN_TRANSFER_TEST_IS_ACTIVE=false
TRON_TOKEN_TRANSFER_TEST_IS_ACTIVE=false
@@ -82,4 +82,58 @@ TRON_MODEL_TEST_RECEIVER='TS1WYZNoNw32hog68m5GyhwZnkhf6HNzhi'
TRON_TRX_TRANSFER_TX='8697ad2c4e1713227c16a65a5845636458df2d3db3adf526e07e17699bc6b3c4'
TRON_TOKEN_TRANSFER_TX='bd0ba6ebb8d2f910b27de1565c66cc89337b792dfdb6484847c817ccbd240760'
TRON_NFT_TRANSFER_TX='d5dd97c09efdb93f36808a9f8e14642ef226880aa91a846583d4ce98c0084637'
-#TRON
\ No newline at end of file
+#Tron
+
+#Bitcoin
+BTC_TRANSFER_TEST_IS_ACTIVE=false
+BTC_LISTENER_TEST_IS_ACTIVE=false
+BTC_BLOCKCYPHER_TOKEN='49d43a59a4f24d31a9731eb067ab971c'
+BTC_SENDER_PRIVATE_KEY='cNHUtnWqwAwGajUGjyHLNbUcfHaDC3ujqjc6qcZik5Xa58Hj46vG'
+BTC_SENDER_ADDRESS='tb1q8juz7c302wdcpfz83zvvyf4jxc8sfq4wyth3pr'
+BTC_RECEIVER_ADDRESS='tb1q9uxj8p043sjkm0qzlsys7677mv98j76k8cvgtg'
+BTC_TRANSFER_AMOUNT=0.00001
+#Bitcoin
+
+#Solana
+# Assets
+SOL_COIN_TRANSFER_TEST_IS_ACTIVE=false
+SOL_TOKEN_TRANSFER_TEST_IS_ACTIVE=false
+SOL_TOKEN_APPROVE_TEST_IS_ACTIVE=false
+SOL_TOKEN_TRANSFER_FROM_TEST_IS_ACTIVE=false
+SOL_NFT_TRANSACTION_TEST_IS_ACTIVE=false
+SOL_TRANSACTION_LISTENER_TEST_IS_ACTIVE=false
+
+SOL_COIN_BALANCE_TEST_AMOUNT=1
+SOL_TOKEN_BALANCE_TEST_AMOUNT=20
+SOL_NFT_BALANCE_TEST_AMOUNT=2
+SOL_TRANSFER_TEST_AMOUNT=0.001
+SOL_TOKEN_TRANSFER_TEST_AMOUNT=1
+SOL_TOKEN_APPROVE_TEST_AMOUNT=10
+#NFT ID
+SOL_NFT_TRANSFER_ID='FxN19KB5UeZJFwxLFgT57WvYYXYhBFKxVumfq37xU4Ck'
+SOL_NFT_TRANSFER_ID2='EBQjkpi6VdY3WF9ihi1sVhjkNDjfCorg1LJGd3Vnn9Ec'
+SOL_TOKEN_PROGRAM='TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
+SOL_BALANCE_TEST_ADDRESS='2i6NifwwBGJKUEhYhqbGwu66kDhbfyjgp7zPN5dQsKsE'
+SOL_SENDER_PRIVATE_KEY='2hh51RHd1cEgiuEj2azZJFNphh27S8578otEHw8WvUia1yEBaW7o7XNuxdV1qQYbvxL5FHrcHpbZMCCxULTkCHRq'
+SOL_RECEIVER_PRIVATE_KEY='57XnG9wtVMVV5BZnZbmaN3JTQ7Mzkmbyh4ooMC45YypJz3gF16nGfCJw3hWGQjLFbKmruaF3N3BH3fJAGD6stGCb'
+SOL_SENDER_TEST_ADDRESS='7bn9So6CKmdn2vHYZU5oG6gaVEewW3N7U8vJoNmSv2vB'
+SOL_RECEIVER_TEST_ADDRESS='GHVMV3zZscR8V4K5GEgQTUjuV4jEixB9a4QX3KhgvVKy'
+SOL_TOKEN_TEST_ADDRESS='2ZHwL3dXk3szRgiBLZi244NmKs2VmoBx764AYMY2tQfx'
+SOL_TOKEN_2022_TEST_ADDRESS='FQPbc46pp1b3QWHFJFfsvQrv7YedX8XEum5c18mLnipE'
+#Collection Mint Address
+SOL_NFT_TEST_ADDRESS='9B8bxQWovwm5na85xtDPuAyUVingcVdqWk3d9yQV2zsA'
+# Assets
+
+# Models
+SOL_NFT_ID='BFqd1SZFWMci42bUrhxzWhnvodCnG32xKYZkSp2rXVAf'
+SOL_TOKEN_AMOUNT=50000
+SOL_COIN_AMOUNT=10
+
+SOL_MODEL_TEST_SENDER='37p742pby4ACHiGcT3d58gsjmC3Kd9bH2L89E3hY8FHZ'
+SOL_MODEL_TEST_RECEIVER='7bn9So6CKmdn2vHYZU5oG6gaVEewW3N7U8vJoNmSv2vB'
+
+SOL_TRANSFER_TX='2RDU1otuPR6UtevwYCQWnngvvjPiTFuHFdyCnzwQVR8wyZ7niqACt2QBmfuyD5aXJbEXSEUcqFquiCEtcQZzkWif'
+SOL_TOKEN_TRANSFER_TX='4XLpHmpiKXXDM7pAg8CXeSLjw7SYKZaSzJjXHP2E1vL2ndvrJ6GnuHUvaQpY3LHQeJww8fzFLJ9MiLnvgsyyyt3i'
+SOL_TOKEN_2022_TRANSFER_TX='3c2Myd3k4Pw1NbsCjuskkZCtbD9HRTjoyoxh2u7qsVLgFN1RbRYAXXRKzBRzwTAmv2pXDjArbotzVL6AVehBMeyg'
+SOL_NFT_TRANSFER_TX='3vrCoNVmeNgGG4LB1qvvdx21TYm6dnPBmhFqXChsusuLn5ZEjFZNFG3BwQQ8fodBYiPXG8QokdBLWjRtxgi7tnRD'
+#Solana
\ No newline at end of file
diff --git a/package.json b/package.json
index 22035f9..47d94e0 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,7 @@
"type": "module",
"scripts": {
"test": "vitest",
+ "test-ui": "vitest watch --ui",
"coverage": "vitest run --coverage",
"format": "prettier --write packages/",
"lint": "eslint . --ext .ts --ignore-path .gitignore"
@@ -12,6 +13,7 @@
"@types/node": "^20.11.20",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@vitest/coverage-istanbul": "^1.5.3",
+ "@vitest/ui": "^1.6.0",
"esbuild": "^0.20.2",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
diff --git a/packages/networks/bitcoin/.eslintrc.json b/packages/networks/bitcoin/.eslintrc.json
new file mode 100644
index 0000000..b62472f
--- /dev/null
+++ b/packages/networks/bitcoin/.eslintrc.json
@@ -0,0 +1,7 @@
+{
+ "extends": [
+ "plugin:require-extensions/recommended",
+ "../../../.eslintrc.json"
+ ],
+ "plugins": ["require-extensions"]
+}
\ No newline at end of file
diff --git a/packages/networks/bitcoin/.npmignore b/packages/networks/bitcoin/.npmignore
new file mode 100644
index 0000000..4f4a3d7
--- /dev/null
+++ b/packages/networks/bitcoin/.npmignore
@@ -0,0 +1,6 @@
+src
+tests
+node_modules
+.eslintrc.cjs
+pnpm-lock.yaml
+tsconfig.json
\ No newline at end of file
diff --git a/packages/networks/bitcoin/README.md b/packages/networks/bitcoin/README.md
new file mode 100644
index 0000000..e69de29
diff --git a/packages/networks/bitcoin/esbuild.ts b/packages/networks/bitcoin/esbuild.ts
new file mode 100644
index 0000000..5714b9c
--- /dev/null
+++ b/packages/networks/bitcoin/esbuild.ts
@@ -0,0 +1,3 @@
+void import('../../../esbuild.ts').then((module) => {
+ module.default()
+})
diff --git a/packages/networks/bitcoin/index.html b/packages/networks/bitcoin/index.html
new file mode 100644
index 0000000..2324a82
--- /dev/null
+++ b/packages/networks/bitcoin/index.html
@@ -0,0 +1,324 @@
+
+
+
+
+
+
+ Browser Tests
+
+
+
+
+
+
+
+
+ Adapter id:
+
+
+ Adapter name:
+
+
+
Adapter icon: ![icon]()
+
+
+ Platforms:
+
+
+ Download link:
+
+
+ Deep link:
+
+
+ Connected address:
+
+
+
+ Result:
+
+
+
+ Result:
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/networks/bitcoin/package-lock.json b/packages/networks/bitcoin/package-lock.json
new file mode 100644
index 0000000..312fc86
--- /dev/null
+++ b/packages/networks/bitcoin/package-lock.json
@@ -0,0 +1,772 @@
+{
+ "name": "@multiplechain/bitcoin",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "@multiplechain/bitcoin",
+ "version": "0.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "@multiplechain/types": "^0.1.55",
+ "@multiplechain/utils": "^0.1.18",
+ "axios": "^1.6.8",
+ "bitcore-lib": "^10.0.28",
+ "ws": "^8.17.0"
+ },
+ "devDependencies": {
+ "@types/bitcore-lib": "^0.15.6"
+ }
+ },
+ "node_modules/@multiplechain/types": {
+ "version": "0.1.55",
+ "resolved": "https://registry.npmjs.org/@multiplechain/types/-/types-0.1.55.tgz",
+ "integrity": "sha512-9fYrLaDxX2pj9zfIbmvk1hPF3FH/bK+Q3uTF+k3zdsitP/HzQ1S9zImNz20Tvoqkk1ns+/8ecE0Y6GNowsDOQA=="
+ },
+ "node_modules/@multiplechain/utils": {
+ "version": "0.1.18",
+ "resolved": "https://registry.npmjs.org/@multiplechain/utils/-/utils-0.1.18.tgz",
+ "integrity": "sha512-UCoOOBJrawp/lInepxuoEiYwId0wMPg7rX8p78FLqzGl8f/b93sAtv7sP87/VVKwhdoEuNZ/uQjckycmNeks/w==",
+ "dependencies": {
+ "@types/ws": "^8.5.10",
+ "bignumber.js": "^9.1.2",
+ "web3-utils": "^4.2.0",
+ "ws": "^8.16.0"
+ }
+ },
+ "node_modules/@noble/curves": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz",
+ "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==",
+ "dependencies": {
+ "@noble/hashes": "1.3.3"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@noble/hashes": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz",
+ "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==",
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@scure/base": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.6.tgz",
+ "integrity": "sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==",
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@scure/bip32": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.3.tgz",
+ "integrity": "sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==",
+ "dependencies": {
+ "@noble/curves": "~1.3.0",
+ "@noble/hashes": "~1.3.2",
+ "@scure/base": "~1.1.4"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@scure/bip39": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.2.tgz",
+ "integrity": "sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==",
+ "dependencies": {
+ "@noble/hashes": "~1.3.2",
+ "@scure/base": "~1.1.4"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@types/bitcore-lib": {
+ "version": "0.15.6",
+ "resolved": "https://registry.npmjs.org/@types/bitcore-lib/-/bitcore-lib-0.15.6.tgz",
+ "integrity": "sha512-CtKDBgSBubPXZ0wFeCiUCSdzH+cuy6nFya3FboOqf44evi+OmkQPqEg3ASMpmPDYE8vkcxV302Iu8lZqCjYieg==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.12.11",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz",
+ "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@types/ws": {
+ "version": "8.5.10",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
+ "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/axios": {
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
+ "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/base-x": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz",
+ "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/bech32": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz",
+ "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg=="
+ },
+ "node_modules/bigi": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/bigi/-/bigi-1.4.2.tgz",
+ "integrity": "sha512-ddkU+dFIuEIW8lE7ZwdIAf2UPoM90eaprg5m3YXAVVTmKlqV/9BX4A2M8BOK2yOq6/VgZFVhK6QAxJebhlbhzw=="
+ },
+ "node_modules/bignumber.js": {
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
+ "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/bip-schnorr": {
+ "version": "0.6.4",
+ "resolved": "https://registry.npmjs.org/bip-schnorr/-/bip-schnorr-0.6.4.tgz",
+ "integrity": "sha512-dNKw7Lea8B0wMIN4OjEmOk/Z5qUGqoPDY0P2QttLqGk1hmDPytLWW8PR5Pb6Vxy6CprcdEgfJpOjUu+ONQveyg==",
+ "dependencies": {
+ "bigi": "^1.4.2",
+ "ecurve": "^1.0.6",
+ "js-sha256": "^0.9.0",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/bitcore-lib": {
+ "version": "10.0.28",
+ "resolved": "https://registry.npmjs.org/bitcore-lib/-/bitcore-lib-10.0.28.tgz",
+ "integrity": "sha512-uOWHpWbUxEj411p+tp6DCb9NfZdsCAHl6Z/rs96mquQMsef29fOqWUyk4Bl7yLf+KwzU5ZKy0HIPnjGFlmpXpg==",
+ "dependencies": {
+ "bech32": "=2.0.0",
+ "bip-schnorr": "=0.6.4",
+ "bn.js": "=4.11.8",
+ "bs58": "^4.0.1",
+ "buffer-compare": "=1.1.1",
+ "elliptic": "^6.5.3",
+ "inherits": "=2.0.1",
+ "lodash": "^4.17.20"
+ }
+ },
+ "node_modules/bitcore-lib/node_modules/inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+ "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA=="
+ },
+ "node_modules/bn.js": {
+ "version": "4.11.8",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
+ "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
+ },
+ "node_modules/brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w=="
+ },
+ "node_modules/bs58": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
+ "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==",
+ "dependencies": {
+ "base-x": "^3.0.2"
+ }
+ },
+ "node_modules/buffer-compare": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-compare/-/buffer-compare-1.1.1.tgz",
+ "integrity": "sha512-O6NvNiHZMd3mlIeMDjP6t/gPG75OqGPeiRZXoMQZJ6iy9GofCls4Ijs5YkPZZwoysizLiedhticmdyx/GyHghA=="
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/ecurve": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/ecurve/-/ecurve-1.0.6.tgz",
+ "integrity": "sha512-/BzEjNfiSuB7jIWKcS/z8FK9jNjmEWvUV2YZ4RLSmcDtP7Lq0m6FvDuSnJpBlDpGRpfRQeTLGLBI8H+kEv0r+w==",
+ "dependencies": {
+ "bigi": "^1.1.0",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/elliptic": {
+ "version": "6.5.5",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz",
+ "integrity": "sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==",
+ "dependencies": {
+ "bn.js": "^4.11.9",
+ "brorand": "^1.1.0",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.1",
+ "inherits": "^2.0.4",
+ "minimalistic-assert": "^1.0.1",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/elliptic/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dependencies": {
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ethereum-cryptography": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz",
+ "integrity": "sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==",
+ "dependencies": {
+ "@noble/curves": "1.3.0",
+ "@noble/hashes": "1.3.3",
+ "@scure/bip32": "1.3.3",
+ "@scure/bip39": "1.2.2"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.6",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/for-each": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+ "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+ "dependencies": {
+ "is-callable": "^1.1.3"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dependencies": {
+ "get-intrinsic": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hash.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
+ "dependencies": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/is-arguments": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+ "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+ "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz",
+ "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==",
+ "dependencies": {
+ "which-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/js-sha256": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz",
+ "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+ },
+ "node_modules/minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg=="
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
+ "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+ },
+ "node_modules/util": {
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
+ "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "is-arguments": "^1.0.4",
+ "is-generator-function": "^1.0.7",
+ "is-typed-array": "^1.1.3",
+ "which-typed-array": "^1.1.2"
+ }
+ },
+ "node_modules/web3-errors": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/web3-errors/-/web3-errors-1.1.4.tgz",
+ "integrity": "sha512-WahtszSqILez+83AxGecVroyZsMuuRT+KmQp4Si5P4Rnqbczno1k748PCrZTS1J4UCPmXMG2/Vt+0Bz2zwXkwQ==",
+ "dependencies": {
+ "web3-types": "^1.3.1"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-types": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/web3-types/-/web3-types-1.6.0.tgz",
+ "integrity": "sha512-qgOtADqlD5hw+KPKBUGaXAcdNLL0oh6qTeVgXwewCfbL/lG9R+/GrgMQB1gbTJ3cit8hMwtH8KX2Em6OwO0HRw==",
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-utils": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-4.2.3.tgz",
+ "integrity": "sha512-m5plKTC2YtQntHITQRyIePw52UVP1IrShhmA2FACtn4zmc5ADmrXOlQWiPzxFP/18eRJsAaUAw2+CQn1u4WPxQ==",
+ "dependencies": {
+ "ethereum-cryptography": "^2.0.0",
+ "eventemitter3": "^5.0.1",
+ "web3-errors": "^1.1.4",
+ "web3-types": "^1.6.0",
+ "web3-validator": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/web3-validator": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/web3-validator/-/web3-validator-2.0.5.tgz",
+ "integrity": "sha512-2gLOSW8XqEN5pw5jVUm20EB7A8SbQiekpAtiI0JBmCIV0a2rp97v8FgWY5E3UEqnw5WFfEqvcDVW92EyynDTyQ==",
+ "dependencies": {
+ "ethereum-cryptography": "^2.0.0",
+ "util": "^0.12.5",
+ "web3-errors": "^1.1.4",
+ "web3-types": "^1.5.0",
+ "zod": "^3.21.4"
+ },
+ "engines": {
+ "node": ">=14",
+ "npm": ">=6.12.0"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz",
+ "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.17.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz",
+ "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/zod": {
+ "version": "3.23.8",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
+ "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ }
+ }
+}
diff --git a/packages/networks/bitcoin/package.json b/packages/networks/bitcoin/package.json
new file mode 100644
index 0000000..7040f81
--- /dev/null
+++ b/packages/networks/bitcoin/package.json
@@ -0,0 +1,75 @@
+{
+ "name": "@multiplechain/bitcoin",
+ "version": "0.4.0",
+ "type": "module",
+ "main": "dist/index.cjs",
+ "module": "dist/index.es.js",
+ "unpkg": "dist/index.umd.js",
+ "browser": "dist/index.umd.js",
+ "jsdelivr": "dist/index.umd.js",
+ "exports": {
+ ".": {
+ "import": {
+ "default": "./dist/index.es.js",
+ "types": "./dist/browser/index.d.ts"
+ },
+ "require": {
+ "default": "./dist/index.cjs",
+ "types": "./dist/index.d.ts"
+ }
+ },
+ "./node": {
+ "default": "./dist/index.cjs",
+ "types": "./dist/index.d.ts"
+ },
+ "./browser": {
+ "default": "./dist/index.es.js",
+ "types": "./dist/browser/index.d.ts"
+ }
+ },
+ "files": [
+ "dist",
+ "README.md",
+ "!tsconfig.tsbuildinfo"
+ ],
+ "scripts": {
+ "dev": "vite",
+ "clean": "rm -rf dist",
+ "watch": "tsc --watch",
+ "build:vite": "vite build",
+ "build:node": "tsx esbuild.ts",
+ "typecheck": "tsc --noEmit",
+ "lint": "eslint . --ext .ts",
+ "test": "vitest run --dir tests",
+ "test-ui": "vitest watch --ui",
+ "prepublishOnly": "pnpm run build",
+ "build": "pnpm run build:vite && pnpm run build:node"
+ },
+ "keywords": [
+ "web3",
+ "crypto",
+ "blockchain",
+ "multiple-chain"
+ ],
+ "author": "MultipleChain",
+ "license": "MIT",
+ "homepage": "https://github.com/MultipleChain/js/tree/master/packages/networks/network-name",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/MultipleChain/js.git"
+ },
+ "bugs": {
+ "url": "https://github.com/MultipleChain/js/issues"
+ },
+ "dependencies": {
+ "@multiplechain/types": "^0.1.56",
+ "@multiplechain/utils": "^0.1.20",
+ "axios": "^1.6.8",
+ "bitcore-lib": "^10.0.28",
+ "sats-connect": "^2.3.1",
+ "ws": "^8.17.0"
+ },
+ "devDependencies": {
+ "@types/bitcore-lib": "^0.15.6"
+ }
+}
diff --git a/packages/networks/bitcoin/pnpm-lock.yaml b/packages/networks/bitcoin/pnpm-lock.yaml
new file mode 100644
index 0000000..3cc25a4
--- /dev/null
+++ b/packages/networks/bitcoin/pnpm-lock.yaml
@@ -0,0 +1,630 @@
+lockfileVersion: '6.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+dependencies:
+ '@multiplechain/types':
+ specifier: ^0.1.56
+ version: 0.1.56
+ '@multiplechain/utils':
+ specifier: ^0.1.20
+ version: 0.1.20
+ axios:
+ specifier: ^1.6.8
+ version: 1.6.8
+ bitcore-lib:
+ specifier: ^10.0.28
+ version: 10.0.28
+ sats-connect:
+ specifier: ^2.3.1
+ version: 2.3.1(typescript@5.4.4)
+ ws:
+ specifier: ^8.17.0
+ version: 8.17.0
+
+devDependencies:
+ '@types/bitcore-lib':
+ specifier: ^0.15.6
+ version: 0.15.6
+
+packages:
+
+ /@multiplechain/types@0.1.56:
+ resolution: {integrity: sha512-tnkgDwW0AWxEfhE/392LR6ZPE7Pxz5/Ei1XYsQISGkOfYYZrsvJXr69fXTjDOHCq7RCuQqytKmhnKs5K4xHd7w==}
+ dev: false
+
+ /@multiplechain/utils@0.1.20:
+ resolution: {integrity: sha512-4eEs4YR/V4F2Qnkl5KTZvPpewRQiX7C193d0tjpQuIfyumTEBJtdHYm1CW9CoJ6EjQUaB0oqSRKXomhzfK1qkw==}
+ dependencies:
+ '@types/ws': 8.5.10
+ bignumber.js: 9.1.2
+ web3-utils: 4.2.1
+ ws: 8.17.0
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+ dev: false
+
+ /@noble/curves@1.3.0:
+ resolution: {integrity: sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==}
+ dependencies:
+ '@noble/hashes': 1.3.3
+ dev: false
+
+ /@noble/hashes@1.3.3:
+ resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==}
+ engines: {node: '>= 16'}
+ dev: false
+
+ /@noble/secp256k1@1.7.1:
+ resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==}
+ dev: false
+
+ /@sats-connect/core@0.0.7:
+ resolution: {integrity: sha512-4m5amq+orHDbqLqCRWojvDQigKAys33Ntwc7U5xNtFeib4j+DpYz6lVAL/s3cay1kq03WUZ+Gil3l5rv+5bQWQ==}
+ dependencies:
+ axios: 1.6.8
+ bitcoin-address-validation: 2.2.3
+ buffer: 6.0.3
+ jsontokens: 4.0.1
+ lodash.omit: 4.5.0
+ transitivePeerDependencies:
+ - debug
+ dev: false
+
+ /@sats-connect/core@0.0.8:
+ resolution: {integrity: sha512-vb7drnd8lFfO4ahCzaVAFkX1eHF1J7jheJl2V/JuuJd5f1sy6nHeNzKMp1zmiuql8uNwe0Sx1WrK1I+4tUmDHg==}
+ dependencies:
+ axios: 1.6.8
+ bitcoin-address-validation: 2.2.3
+ buffer: 6.0.3
+ jsontokens: 4.0.1
+ lodash.omit: 4.5.0
+ transitivePeerDependencies:
+ - debug
+ dev: false
+
+ /@sats-connect/make-default-provider-config@0.0.4(typescript@5.4.4):
+ resolution: {integrity: sha512-PsLzg1hV3FxMXUp9XrOUmDJgbuyR4VDHq/7mh1O1CtC3dDZQnJFa+Ue43duPMmUaRGinuVKtS2hnMhPLyURdGA==}
+ peerDependencies:
+ typescript: 5.4.4
+ dependencies:
+ '@sats-connect/core': 0.0.7
+ '@sats-connect/ui': 0.0.5-c661c02
+ bowser: 2.11.0
+ typescript: 5.4.4
+ transitivePeerDependencies:
+ - debug
+ dev: false
+
+ /@sats-connect/ui@0.0.5-c661c02:
+ resolution: {integrity: sha512-6MUXFDGTapBhZAxb6deAdqKuB64GOe6k927gGww5JYwVnOUCaHGDcfaZ/lwexzYL45u8RJof12I4np7MgS+Bwg==}
+ dev: false
+
+ /@sats-connect/ui@0.0.6:
+ resolution: {integrity: sha512-H3bFFhr9CcY1oNosNi/QJszmMHSht4U19bUWfM3vzayAKgV4ebY6iUnRK5g3p2rVLLWVzlpaw1J9m+7JWwyBfA==}
+ dev: false
+
+ /@scure/base@1.1.6:
+ resolution: {integrity: sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==}
+ dev: false
+
+ /@scure/bip32@1.3.3:
+ resolution: {integrity: sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==}
+ dependencies:
+ '@noble/curves': 1.3.0
+ '@noble/hashes': 1.3.3
+ '@scure/base': 1.1.6
+ dev: false
+
+ /@scure/bip39@1.2.2:
+ resolution: {integrity: sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==}
+ dependencies:
+ '@noble/hashes': 1.3.3
+ '@scure/base': 1.1.6
+ dev: false
+
+ /@types/bitcore-lib@0.15.6:
+ resolution: {integrity: sha512-CtKDBgSBubPXZ0wFeCiUCSdzH+cuy6nFya3FboOqf44evi+OmkQPqEg3ASMpmPDYE8vkcxV302Iu8lZqCjYieg==}
+ dependencies:
+ '@types/node': 20.12.7
+ dev: true
+
+ /@types/node@20.12.7:
+ resolution: {integrity: sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==}
+ dependencies:
+ undici-types: 5.26.5
+
+ /@types/ws@8.5.10:
+ resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
+ dependencies:
+ '@types/node': 20.12.7
+ dev: false
+
+ /asynckit@0.4.0:
+ resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+ dev: false
+
+ /available-typed-arrays@1.0.7:
+ resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ possible-typed-array-names: 1.0.0
+ dev: false
+
+ /axios@1.6.8:
+ resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==}
+ dependencies:
+ follow-redirects: 1.15.6
+ form-data: 4.0.0
+ proxy-from-env: 1.1.0
+ transitivePeerDependencies:
+ - debug
+ dev: false
+
+ /base-x@3.0.9:
+ resolution: {integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==}
+ dependencies:
+ safe-buffer: 5.2.1
+ dev: false
+
+ /base58-js@1.0.5:
+ resolution: {integrity: sha512-LkkAPP8Zu+c0SVNRTRVDyMfKVORThX+rCViget00xdgLRrKkClCTz1T7cIrpr69ShwV5XJuuoZvMvJ43yURwkA==}
+ engines: {node: '>= 8'}
+ dev: false
+
+ /base64-js@1.5.1:
+ resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+ dev: false
+
+ /bech32@2.0.0:
+ resolution: {integrity: sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==}
+ dev: false
+
+ /bigi@1.4.2:
+ resolution: {integrity: sha512-ddkU+dFIuEIW8lE7ZwdIAf2UPoM90eaprg5m3YXAVVTmKlqV/9BX4A2M8BOK2yOq6/VgZFVhK6QAxJebhlbhzw==}
+ dev: false
+
+ /bignumber.js@9.1.2:
+ resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==}
+ dev: false
+
+ /bip-schnorr@0.6.4:
+ resolution: {integrity: sha512-dNKw7Lea8B0wMIN4OjEmOk/Z5qUGqoPDY0P2QttLqGk1hmDPytLWW8PR5Pb6Vxy6CprcdEgfJpOjUu+ONQveyg==}
+ engines: {node: '>=8.0.0'}
+ dependencies:
+ bigi: 1.4.2
+ ecurve: 1.0.6
+ js-sha256: 0.9.0
+ randombytes: 2.1.0
+ safe-buffer: 5.2.1
+ dev: false
+
+ /bitcoin-address-validation@2.2.3:
+ resolution: {integrity: sha512-1uGCGl26Ye8JG5qcExtFLQfuib6qEZWNDo1ZlLlwp/z7ygUFby3IxolgEfgMGaC+LG9csbVASLcH8fRLv7DIOg==}
+ dependencies:
+ base58-js: 1.0.5
+ bech32: 2.0.0
+ sha256-uint8array: 0.10.7
+ dev: false
+
+ /bitcore-lib@10.0.28:
+ resolution: {integrity: sha512-uOWHpWbUxEj411p+tp6DCb9NfZdsCAHl6Z/rs96mquQMsef29fOqWUyk4Bl7yLf+KwzU5ZKy0HIPnjGFlmpXpg==}
+ dependencies:
+ bech32: 2.0.0
+ bip-schnorr: 0.6.4
+ bn.js: 4.11.8
+ bs58: 4.0.1
+ buffer-compare: 1.1.1
+ elliptic: 6.5.5
+ inherits: 2.0.1
+ lodash: 4.17.21
+ dev: false
+
+ /bn.js@4.11.8:
+ resolution: {integrity: sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==}
+ dev: false
+
+ /bn.js@4.12.0:
+ resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==}
+ dev: false
+
+ /bowser@2.11.0:
+ resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==}
+ dev: false
+
+ /brorand@1.1.0:
+ resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==}
+ dev: false
+
+ /bs58@4.0.1:
+ resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==}
+ dependencies:
+ base-x: 3.0.9
+ dev: false
+
+ /buffer-compare@1.1.1:
+ resolution: {integrity: sha512-O6NvNiHZMd3mlIeMDjP6t/gPG75OqGPeiRZXoMQZJ6iy9GofCls4Ijs5YkPZZwoysizLiedhticmdyx/GyHghA==}
+ dev: false
+
+ /buffer@6.0.3:
+ resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
+ dependencies:
+ base64-js: 1.5.1
+ ieee754: 1.2.1
+ dev: false
+
+ /call-bind@1.0.7:
+ resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ es-define-property: 1.0.0
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+ get-intrinsic: 1.2.4
+ set-function-length: 1.2.2
+ dev: false
+
+ /combined-stream@1.0.8:
+ resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+ engines: {node: '>= 0.8'}
+ dependencies:
+ delayed-stream: 1.0.0
+ dev: false
+
+ /define-data-property@1.1.4:
+ resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ es-define-property: 1.0.0
+ es-errors: 1.3.0
+ gopd: 1.0.1
+ dev: false
+
+ /delayed-stream@1.0.0:
+ resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+ engines: {node: '>=0.4.0'}
+ dev: false
+
+ /ecurve@1.0.6:
+ resolution: {integrity: sha512-/BzEjNfiSuB7jIWKcS/z8FK9jNjmEWvUV2YZ4RLSmcDtP7Lq0m6FvDuSnJpBlDpGRpfRQeTLGLBI8H+kEv0r+w==}
+ dependencies:
+ bigi: 1.4.2
+ safe-buffer: 5.2.1
+ dev: false
+
+ /elliptic@6.5.5:
+ resolution: {integrity: sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==}
+ dependencies:
+ bn.js: 4.12.0
+ brorand: 1.1.0
+ hash.js: 1.1.7
+ hmac-drbg: 1.0.1
+ inherits: 2.0.4
+ minimalistic-assert: 1.0.1
+ minimalistic-crypto-utils: 1.0.1
+ dev: false
+
+ /es-define-property@1.0.0:
+ resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ get-intrinsic: 1.2.4
+ dev: false
+
+ /es-errors@1.3.0:
+ resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+ engines: {node: '>= 0.4'}
+ dev: false
+
+ /ethereum-cryptography@2.1.3:
+ resolution: {integrity: sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA==}
+ dependencies:
+ '@noble/curves': 1.3.0
+ '@noble/hashes': 1.3.3
+ '@scure/bip32': 1.3.3
+ '@scure/bip39': 1.2.2
+ dev: false
+
+ /eventemitter3@5.0.1:
+ resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
+ dev: false
+
+ /follow-redirects@1.15.6:
+ resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+ dev: false
+
+ /for-each@0.3.3:
+ resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
+ dependencies:
+ is-callable: 1.2.7
+ dev: false
+
+ /form-data@4.0.0:
+ resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
+ engines: {node: '>= 6'}
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ mime-types: 2.1.35
+ dev: false
+
+ /function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+ dev: false
+
+ /get-intrinsic@1.2.4:
+ resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+ has-proto: 1.0.3
+ has-symbols: 1.0.3
+ hasown: 2.0.2
+ dev: false
+
+ /gopd@1.0.1:
+ resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
+ dependencies:
+ get-intrinsic: 1.2.4
+ dev: false
+
+ /has-property-descriptors@1.0.2:
+ resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
+ dependencies:
+ es-define-property: 1.0.0
+ dev: false
+
+ /has-proto@1.0.3:
+ resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==}
+ engines: {node: '>= 0.4'}
+ dev: false
+
+ /has-symbols@1.0.3:
+ resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
+ engines: {node: '>= 0.4'}
+ dev: false
+
+ /has-tostringtag@1.0.2:
+ resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ has-symbols: 1.0.3
+ dev: false
+
+ /hash.js@1.1.7:
+ resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==}
+ dependencies:
+ inherits: 2.0.4
+ minimalistic-assert: 1.0.1
+ dev: false
+
+ /hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ function-bind: 1.1.2
+ dev: false
+
+ /hmac-drbg@1.0.1:
+ resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==}
+ dependencies:
+ hash.js: 1.1.7
+ minimalistic-assert: 1.0.1
+ minimalistic-crypto-utils: 1.0.1
+ dev: false
+
+ /ieee754@1.2.1:
+ resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+ dev: false
+
+ /inherits@2.0.1:
+ resolution: {integrity: sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==}
+ dev: false
+
+ /inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+ dev: false
+
+ /is-arguments@1.1.1:
+ resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.7
+ has-tostringtag: 1.0.2
+ dev: false
+
+ /is-callable@1.2.7:
+ resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
+ engines: {node: '>= 0.4'}
+ dev: false
+
+ /is-generator-function@1.0.10:
+ resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ has-tostringtag: 1.0.2
+ dev: false
+
+ /is-typed-array@1.1.13:
+ resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ which-typed-array: 1.1.15
+ dev: false
+
+ /js-sha256@0.9.0:
+ resolution: {integrity: sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==}
+ dev: false
+
+ /jsontokens@4.0.1:
+ resolution: {integrity: sha512-+MO415LEN6M+3FGsRz4wU20g7N2JA+2j9d9+pGaNJHviG4L8N0qzavGyENw6fJqsq9CcrHOIL6iWX5yeTZ86+Q==}
+ dependencies:
+ '@noble/hashes': 1.3.3
+ '@noble/secp256k1': 1.7.1
+ base64-js: 1.5.1
+ dev: false
+
+ /lodash.omit@4.5.0:
+ resolution: {integrity: sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==}
+ dev: false
+
+ /lodash@4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+ dev: false
+
+ /mime-db@1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+ dev: false
+
+ /mime-types@2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+ dependencies:
+ mime-db: 1.52.0
+ dev: false
+
+ /minimalistic-assert@1.0.1:
+ resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
+ dev: false
+
+ /minimalistic-crypto-utils@1.0.1:
+ resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==}
+ dev: false
+
+ /possible-typed-array-names@1.0.0:
+ resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
+ engines: {node: '>= 0.4'}
+ dev: false
+
+ /proxy-from-env@1.1.0:
+ resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+ dev: false
+
+ /randombytes@2.1.0:
+ resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
+ dependencies:
+ safe-buffer: 5.2.1
+ dev: false
+
+ /safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+ dev: false
+
+ /sats-connect@2.3.1(typescript@5.4.4):
+ resolution: {integrity: sha512-3KzqRO5KVBlge7Q4a/L828SfCkFD+M4MVdtgJZS+L+oHiDYoXlLkvnu3almh9Ynhcm0HnsGmVH1pKVL0lonjyQ==}
+ dependencies:
+ '@sats-connect/core': 0.0.8
+ '@sats-connect/make-default-provider-config': 0.0.4(typescript@5.4.4)
+ '@sats-connect/ui': 0.0.6
+ transitivePeerDependencies:
+ - debug
+ - typescript
+ dev: false
+
+ /set-function-length@1.2.2:
+ resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ define-data-property: 1.1.4
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+ get-intrinsic: 1.2.4
+ gopd: 1.0.1
+ has-property-descriptors: 1.0.2
+ dev: false
+
+ /sha256-uint8array@0.10.7:
+ resolution: {integrity: sha512-1Q6JQU4tX9NqsDGodej6pkrUVQVNapLZnvkwIhddH/JqzBZF1fSaxSWNY6sziXBE8aEa2twtGkXUrwzGeZCMpQ==}
+ dev: false
+
+ /typescript@5.4.4:
+ resolution: {integrity: sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+ dev: false
+
+ /undici-types@5.26.5:
+ resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
+
+ /util@0.12.5:
+ resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==}
+ dependencies:
+ inherits: 2.0.4
+ is-arguments: 1.1.1
+ is-generator-function: 1.0.10
+ is-typed-array: 1.1.13
+ which-typed-array: 1.1.15
+ dev: false
+
+ /web3-errors@1.1.4:
+ resolution: {integrity: sha512-WahtszSqILez+83AxGecVroyZsMuuRT+KmQp4Si5P4Rnqbczno1k748PCrZTS1J4UCPmXMG2/Vt+0Bz2zwXkwQ==}
+ engines: {node: '>=14', npm: '>=6.12.0'}
+ dependencies:
+ web3-types: 1.5.0
+ dev: false
+
+ /web3-types@1.5.0:
+ resolution: {integrity: sha512-geWuMIeegQ8AedKAO6wO4G4j1gyQ1F/AyKLMw2vud4bsfZayyzWJgCMDZtjYMm5uo2a7i8j1W3/4QFmzlSy5cw==}
+ engines: {node: '>=14', npm: '>=6.12.0'}
+ dev: false
+
+ /web3-utils@4.2.1:
+ resolution: {integrity: sha512-Fk29BlEqD9Q9Cnw4pBkKw7czcXiRpsSco/BzEUl4ye0ZTSHANQFfjsfQmNm4t7uY11u6Ah+8F3tNjBeU4CA80A==}
+ engines: {node: '>=14', npm: '>=6.12.0'}
+ dependencies:
+ ethereum-cryptography: 2.1.3
+ eventemitter3: 5.0.1
+ web3-errors: 1.1.4
+ web3-types: 1.5.0
+ web3-validator: 2.0.4
+ dev: false
+
+ /web3-validator@2.0.4:
+ resolution: {integrity: sha512-qRxVePwdW+SByOmTpDZFWHIUAa7PswvxNszrOua6BoGqAhERo5oJZBN+EbWtK/+O+ApNxt5FR3nCPmiZldiOQA==}
+ engines: {node: '>=14', npm: '>=6.12.0'}
+ dependencies:
+ ethereum-cryptography: 2.1.3
+ util: 0.12.5
+ web3-errors: 1.1.4
+ web3-types: 1.5.0
+ zod: 3.22.4
+ dev: false
+
+ /which-typed-array@1.1.15:
+ resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.7
+ for-each: 0.3.3
+ gopd: 1.0.1
+ has-tostringtag: 1.0.2
+ dev: false
+
+ /ws@8.17.0:
+ resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+ dev: false
+
+ /zod@3.22.4:
+ resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
+ dev: false
diff --git a/packages/networks/bitcoin/src/assets/Coin.ts b/packages/networks/bitcoin/src/assets/Coin.ts
new file mode 100644
index 0000000..a50d1ea
--- /dev/null
+++ b/packages/networks/bitcoin/src/assets/Coin.ts
@@ -0,0 +1,109 @@
+import axios from 'axios'
+import { Provider } from '../services/Provider.ts'
+import { fromSatoshi, toSatoshi } from '../utils.ts'
+import { Transaction, Script, Address } from 'bitcore-lib'
+import { ErrorTypeEnum, type CoinInterface } from '@multiplechain/types'
+import { CoinTransactionSigner } from '../services/TransactionSigner.ts'
+
+export class Coin implements CoinInterface {
+ /**
+ * Blockchain network provider
+ */
+ provider: Provider
+
+ /**
+ * @param {Provider} provider network provider
+ */
+ constructor(provider?: Provider) {
+ this.provider = provider ?? Provider.instance
+ }
+
+ /**
+ * @returns {string} Coin name
+ */
+ getName(): string {
+ return 'Bitcoin'
+ }
+
+ /**
+ * @returns {string} Coin symbol
+ */
+ getSymbol(): string {
+ return 'BTC'
+ }
+
+ /**
+ * @returns {number} Decimal value of the coin
+ */
+ getDecimals(): number {
+ return 8
+ }
+
+ /**
+ * @param {string} owner Wallet address
+ * @returns {Promise} Wallet balance as currency of COIN
+ */
+ async getBalance(owner: string): Promise {
+ const addressStatsApi = this.provider.createEndpoint('address/' + owner)
+ const addressStats = await axios.get(addressStatsApi).then((res) => res.data)
+ const balanceSat =
+ addressStats.chain_stats.funded_txo_sum - addressStats.chain_stats.spent_txo_sum
+ return fromSatoshi(balanceSat)
+ }
+
+ /**
+ * @param {string} sender Sender wallet address
+ * @param {string} receiver Receiver wallet address
+ * @param {number} amount Amount of assets that will be transferred
+ * @returns {Promise} Transaction signer
+ */
+ async transfer(
+ sender: string,
+ receiver: string,
+ amount: number
+ ): Promise {
+ if (amount < 0) {
+ throw new Error(ErrorTypeEnum.INVALID_AMOUNT)
+ }
+
+ if (amount > (await this.getBalance(sender))) {
+ throw new Error(ErrorTypeEnum.INSUFFICIENT_BALANCE)
+ }
+
+ if (sender === receiver) {
+ throw new Error(ErrorTypeEnum.INVALID_ADDRESS)
+ }
+
+ const inputs = []
+ const transaction = new Transaction()
+ const senderAddress = new Address(sender)
+ const satoshiToSend = toSatoshi(amount)
+
+ const utxos = await axios
+ .get(this.provider.createEndpoint('address/' + sender + '/utxo'))
+ .then((res) => res.data)
+
+ for (const utxo of utxos) {
+ inputs.push(
+ new Transaction.UnspentOutput({
+ txId: utxo.txid,
+ satoshis: utxo.value,
+ address: senderAddress,
+ outputIndex: utxo.vout,
+ script: Script.fromAddress(senderAddress)
+ })
+ )
+ }
+
+ transaction.from(inputs)
+ transaction.change(sender)
+ transaction.to(receiver, satoshiToSend)
+
+ return new CoinTransactionSigner({
+ sender,
+ receiver,
+ amount: satoshiToSend,
+ bitcoreLib: transaction
+ })
+ }
+}
diff --git a/packages/networks/bitcoin/src/assets/Contract.ts b/packages/networks/bitcoin/src/assets/Contract.ts
new file mode 100644
index 0000000..14044c0
--- /dev/null
+++ b/packages/networks/bitcoin/src/assets/Contract.ts
@@ -0,0 +1,59 @@
+import { Provider } from '../services/Provider.ts'
+import type { ContractInterface } from '@multiplechain/types'
+
+export class Contract implements ContractInterface {
+ /**
+ * Contract address
+ */
+ address: string
+
+ /**
+ * Blockchain network provider
+ */
+ provider: Provider
+
+ /**
+ * @param {string} address Contract address
+ * @param {Provider} provider Blockchain network provider
+ */
+ constructor(address: string, provider?: Provider) {
+ this.address = address
+ this.provider = provider ?? Provider.instance
+ throw new Error('This class is not implemented for Bitcoin.')
+ }
+
+ /**
+ * @returns {string} Contract address
+ */
+ getAddress(): string {
+ return this.address
+ }
+
+ /**
+ * @param {string} method Method name
+ * @param {any[]} args Method parameters
+ * @returns {Promise} Method result
+ */
+ async callMethod(method: string, ...args: any[]): Promise {
+ return {}
+ }
+
+ /**
+ * @param {string} method Method name
+ * @param {any[]} args Sender wallet address
+ * @returns {Promise} Encoded method data
+ */
+ async getMethodData(method: string, ...args: any[]): Promise {
+ return {}
+ }
+
+ /**
+ * @param {string} method Method name
+ * @param {string} from Sender wallet address
+ * @param {any[]} args Method parameters
+ * @returns {Promise} Encoded method data
+ */
+ async createTransactionData(method: string, from: string, ...args: any[]): Promise {
+ return ''
+ }
+}
diff --git a/packages/networks/bitcoin/src/assets/NFT.ts b/packages/networks/bitcoin/src/assets/NFT.ts
new file mode 100644
index 0000000..0a89a51
--- /dev/null
+++ b/packages/networks/bitcoin/src/assets/NFT.ts
@@ -0,0 +1,96 @@
+import { Contract } from './Contract.ts'
+import type { NftInterface } from '@multiplechain/types'
+import { NftTransactionSigner } from '../services/TransactionSigner.ts'
+
+export class NFT extends Contract implements NftInterface {
+ /**
+ * @returns {Promise} NFT name
+ */
+ async getName(): Promise {
+ return 'example'
+ }
+
+ /**
+ * @returns {Promise} NFT symbol
+ */
+ async getSymbol(): Promise {
+ return 'example'
+ }
+
+ /**
+ * @param {string} owner Wallet address
+ * @returns {Promise} Wallet balance as currency of NFT
+ */
+ async getBalance(owner: string): Promise {
+ return 0
+ }
+
+ /**
+ * @param {number | string} nftId NFT ID
+ * @returns {Promise} Wallet address of the owner of the NFT
+ */
+ async getOwner(nftId: number | string): Promise {
+ return 'example'
+ }
+
+ /**
+ * @param {number | string} nftId NFT ID
+ * @returns {Promise} URI of the NFT
+ */
+ async getTokenURI(nftId: number | string): Promise {
+ return 'example'
+ }
+
+ /**
+ * @param {number | string} nftId ID of the NFT that will be transferred
+ * @returns {Promise} Wallet address of the approved spender
+ */
+ async getApproved(nftId: number | string): Promise {
+ return 'example'
+ }
+
+ /**
+ * @param {string} sender Sender address
+ * @param {string} receiver Receiver address
+ * @param {number | string} nftId NFT ID
+ * @returns {Promise} Transaction signer
+ */
+ async transfer(
+ sender: string,
+ receiver: string,
+ nftId: number | string
+ ): Promise {
+ return new NftTransactionSigner('example')
+ }
+
+ /**
+ * @param {string} spender Spender address
+ * @param {string} owner Owner address
+ * @param {string} receiver Receiver address
+ * @param {number | string} nftId NFT ID
+ * @returns {Promise} Transaction signer
+ */
+ async transferFrom(
+ spender: string,
+ owner: string,
+ receiver: string,
+ nftId: number | string
+ ): Promise {
+ return new NftTransactionSigner('example')
+ }
+
+ /**
+ * Gives permission to the spender to spend owner's tokens
+ * @param {string} owner Address of owner of the tokens that will be used
+ * @param {string} spender Address of the spender that will use the tokens of owner
+ * @param {number | string} nftId ID of the NFT that will be transferred
+ * @returns {Promise} Transaction signer
+ */
+ async approve(
+ owner: string,
+ spender: string,
+ nftId: number | string
+ ): Promise {
+ return new NftTransactionSigner('example')
+ }
+}
diff --git a/packages/networks/bitcoin/src/assets/Token.ts b/packages/networks/bitcoin/src/assets/Token.ts
new file mode 100644
index 0000000..a090488
--- /dev/null
+++ b/packages/networks/bitcoin/src/assets/Token.ts
@@ -0,0 +1,92 @@
+import { Contract } from './Contract.ts'
+import type { TokenInterface } from '@multiplechain/types'
+import { TokenTransactionSigner } from '../services/TransactionSigner.ts'
+
+export class Token extends Contract implements TokenInterface {
+ /**
+ * @returns {Promise} Token name
+ */
+ async getName(): Promise {
+ return 'example'
+ }
+
+ /**
+ * @returns {Promise} Token symbol
+ */
+ async getSymbol(): Promise {
+ return 'example'
+ }
+
+ /**
+ * @returns {Promise} Decimal value of the token
+ */
+ async getDecimals(): Promise {
+ return 18
+ }
+
+ /**
+ * @param {string} owner Wallet address
+ * @returns {Promise} Wallet balance as currency of TOKEN
+ */
+ async getBalance(owner: string): Promise {
+ return 0
+ }
+
+ /**
+ * @returns {Promise} Total supply of the token
+ */
+ async getTotalSupply(): Promise {
+ return 0
+ }
+
+ /**
+ * @param {string} owner Address of owner of the tokens that is being used
+ * @param {string} spender Address of the spender that is using the tokens of owner
+ * @returns {Promise} Amount of tokens that the spender is allowed to spend
+ */
+ async getAllowance(owner: string, spender: string): Promise {
+ return 0
+ }
+
+ /**
+ * transfer() method is the main method for processing transfers for fungible assets (TOKEN, COIN)
+ * @param {string} sender Sender wallet address
+ * @param {string} receiver Receiver wallet address
+ * @param {number} amount Amount of assets that will be transferred
+ * @returns {Promise} Transaction signer
+ */
+ async transfer(
+ sender: string,
+ receiver: string,
+ amount: number
+ ): Promise {
+ return new TokenTransactionSigner('example')
+ }
+
+ /**
+ * @param {string} spender Address of the spender of transaction
+ * @param {string} owner Sender wallet address
+ * @param {string} receiver Receiver wallet address
+ * @param {number} amount Amount of tokens that will be transferred
+ * @returns {Promise} Transaction signer
+ */
+ async transferFrom(
+ spender: string,
+ owner: string,
+ receiver: string,
+ amount: number
+ ): Promise {
+ return new TokenTransactionSigner('example')
+ }
+
+ /**
+ * Gives permission to the spender to spend owner's tokens
+ * @param {string} owner Address of owner of the tokens that will be used
+ * @param {string} spender Address of the spender that will use the tokens of owner
+ * @param {number} amount Amount of the tokens that will be used
+ * @returns {Promise} Transaction signer
+ */
+ async approve(owner: string, spender: string, amount: number): Promise {
+ return new TokenTransactionSigner('example')
+ }
+}
diff --git a/packages/networks/bitcoin/src/assets/index.ts b/packages/networks/bitcoin/src/assets/index.ts
new file mode 100644
index 0000000..7ea522f
--- /dev/null
+++ b/packages/networks/bitcoin/src/assets/index.ts
@@ -0,0 +1,4 @@
+export * from './NFT.ts'
+export * from './Coin.ts'
+export * from './Token.ts'
+export * from './Contract.ts'
diff --git a/packages/networks/bitcoin/src/browser/Wallet.ts b/packages/networks/bitcoin/src/browser/Wallet.ts
new file mode 100644
index 0000000..cb8e8d0
--- /dev/null
+++ b/packages/networks/bitcoin/src/browser/Wallet.ts
@@ -0,0 +1,168 @@
+import {
+ type WalletInterface,
+ type WalletAdapterInterface,
+ type WalletPlatformEnum,
+ type TransactionSignerInterface,
+ type ProviderInterface,
+ ErrorTypeEnum
+} from '@multiplechain/types'
+import { Provider } from '../services/Provider.ts'
+import type { TransactionData } from '../services/TransactionSigner.ts'
+
+export interface BitcoinWalletAdapter {
+ getAddress: () => Promise
+ signMessage: (message: string) => Promise
+ sendBitcoin: (to: string, amount: number) => Promise
+ on: (event: string, callback: (data: any) => void) => void
+}
+
+const rejectMap = (error: any, reject: (a: any) => any): any => {
+ console.error('MultipleChain Bitcoin Wallet Error:', error)
+
+ if (typeof error === 'object') {
+ if (error.code === 4001 || String(error.message).includes('User rejected the request')) {
+ return reject(ErrorTypeEnum.WALLET_REQUEST_REJECTED)
+ } else if (String(error).includes('is not valid JSON')) {
+ return reject(ErrorTypeEnum.UNACCEPTED_CHAIN)
+ }
+ }
+
+ return reject(error)
+}
+
+export class Wallet implements WalletInterface {
+ adapter: WalletAdapterInterface
+
+ walletProvider: BitcoinWalletAdapter
+
+ networkProvider: Provider
+
+ /**
+ * @param {WalletAdapterInterface} adapter
+ * @param {Provider} provider
+ */
+ constructor(adapter: WalletAdapterInterface, provider?: Provider) {
+ this.adapter = adapter
+ this.networkProvider = provider ?? Provider.instance
+ }
+
+ /**
+ * @returns {string}
+ */
+ getId(): string {
+ return this.adapter.id
+ }
+
+ /**
+ * @returns {string}
+ */
+ getName(): string {
+ return this.adapter.name
+ }
+
+ /**
+ * @returns {string}
+ */
+ getIcon(): string {
+ return this.adapter.icon
+ }
+
+ /**
+ * @returns {WalletPlatformEnum[]}
+ */
+ getPlatforms(): WalletPlatformEnum[] {
+ return this.adapter.platforms
+ }
+
+ /**
+ * @returns {string}
+ */
+ getDownloadLink(): string | undefined {
+ return this.adapter.downloadLink
+ }
+
+ /**
+ * @param {string} url
+ * @param {object} ops
+ * @returns {string}
+ */
+ createDeepLink(url: string, ops?: object): string | null {
+ if (this.adapter.createDeepLink === undefined) {
+ return null
+ }
+
+ return this.adapter.createDeepLink(url, ops)
+ }
+
+ /**
+ * @param {ProviderInterface} provider
+ * @param {Object} ops
+ * @returns {Promise}
+ */
+ async connect(provider?: ProviderInterface, ops?: object): Promise {
+ return await new Promise((resolve, reject) => {
+ this.adapter
+ .connect(provider, ops)
+ .then(async (provider) => {
+ this.walletProvider = provider as BitcoinWalletAdapter
+ resolve(await this.getAddress())
+ })
+ .catch((error) => {
+ const customReject = (error: any): void => {
+ if (error.message === ErrorTypeEnum.WALLET_REQUEST_REJECTED) {
+ reject(new Error(ErrorTypeEnum.WALLET_CONNECT_REJECTED))
+ } else {
+ reject(error)
+ }
+ }
+ rejectMap(error, customReject)
+ })
+ })
+ }
+
+ /**
+ * @returns {boolean}
+ */
+ async isDetected(): Promise {
+ return await this.adapter.isDetected()
+ }
+
+ /**
+ * @returns {boolean}
+ */
+ async isConnected(): Promise {
+ return await this.adapter.isConnected()
+ }
+
+ /**
+ * @returns {Promise}
+ */
+ async getAddress(): Promise {
+ return await this.walletProvider.getAddress()
+ }
+
+ /**
+ * @param {string} message
+ */
+ async signMessage(message: string): Promise {
+ return await this.walletProvider.signMessage(message)
+ }
+
+ /**
+ * @param {TransactionSignerInterface} transactionSigner
+ * @returns {Promise}
+ */
+ async sendTransaction(transactionSigner: TransactionSignerInterface): Promise {
+ const data = (await transactionSigner.getRawData()) as TransactionData
+ return await this.walletProvider.sendBitcoin(data.receiver, data.amount)
+ }
+
+ /**
+ * @param {string} eventName
+ * @param {Function} callback
+ * @returns {void}
+ */
+ on(eventName: string, callback: (...args: any[]) => void): void {
+ this.walletProvider.on(eventName, callback)
+ }
+}
diff --git a/packages/networks/bitcoin/src/browser/adapters/Leather.ts b/packages/networks/bitcoin/src/browser/adapters/Leather.ts
new file mode 100644
index 0000000..3f363fa
--- /dev/null
+++ b/packages/networks/bitcoin/src/browser/adapters/Leather.ts
@@ -0,0 +1,105 @@
+import icons from './icons.ts'
+import { WalletPlatformEnum } from '@multiplechain/types'
+import type { ProviderInterface, WalletAdapterInterface } from '@multiplechain/types'
+import type { BitcoinWalletAdapter } from '../Wallet.ts'
+
+declare global {
+ interface Window {
+ btc: {
+ listen?: (event: string, callback: (data: any) => void) => void
+ }
+ LeatherProvider: {
+ request: (method: string, params: any) => Promise
+ }
+ }
+}
+
+let connected = false
+
+const Leather: WalletAdapterInterface = {
+ id: 'leather',
+ name: 'Leather',
+ icon: icons.Leather,
+ platforms: [WalletPlatformEnum.BROWSER],
+ downloadLink: 'https://leather.io/install-extension',
+ isDetected: () => Boolean(window.LeatherProvider),
+ isConnected: async () => connected,
+ connect: async (provider?: ProviderInterface): Promise => {
+ return await new Promise((resolve, reject) => {
+ const leather = window.LeatherProvider
+
+ const network = provider !== undefined && provider?.isTestnet() ? 'testnet' : 'mainnet'
+
+ const walletAdapter: BitcoinWalletAdapter = {
+ on: (event, callback) => {
+ if (window.btc?.listen !== undefined) {
+ window.btc.listen(event, callback)
+ }
+ },
+ signMessage: async (message: string) => {
+ const response = await leather.request('signMessage', {
+ message,
+ network,
+ account: 0,
+ paymentType: 'p2wpkh'
+ })
+
+ return response.result.signature as string
+ },
+ sendBitcoin: async (to: string, amount: number) => {
+ return await new Promise((resolve, reject) => {
+ try {
+ leather
+ .request('sendTransfer', {
+ address: to,
+ amount,
+ network
+ })
+ .then((response) => {
+ resolve(response.result.txid as string)
+ })
+ .catch(({ error }) => {
+ reject(error)
+ })
+ } catch (error) {
+ reject(error)
+ }
+ })
+ },
+ getAddress: async () => ''
+ }
+
+ const connect = async (): Promise => {
+ const addresses = (
+ await leather.request('getAddresses', {
+ network
+ })
+ ).result.addresses
+
+ const bitcoin = addresses.find((address: any) => address.type === 'p2wpkh')
+
+ // for ordinals & BRC-20 integrations
+ // const ordinals = addresses.find(address => address.type == 'p2tr');
+
+ walletAdapter.getAddress = async () => {
+ return bitcoin.address
+ }
+
+ return walletAdapter
+ }
+
+ try {
+ connect()
+ .then((walletAdapter) => {
+ connected = true
+ resolve(walletAdapter)
+ })
+ .catch(reject)
+ } catch (error) {
+ reject(error)
+ }
+ })
+ }
+}
+
+export default Leather
diff --git a/packages/networks/bitcoin/src/browser/adapters/UniSat.ts b/packages/networks/bitcoin/src/browser/adapters/UniSat.ts
new file mode 100644
index 0000000..b089c8c
--- /dev/null
+++ b/packages/networks/bitcoin/src/browser/adapters/UniSat.ts
@@ -0,0 +1,60 @@
+import icons from './icons.ts'
+import { WalletPlatformEnum } from '@multiplechain/types'
+import type { ProviderInterface, WalletAdapterInterface } from '@multiplechain/types'
+import type { BitcoinWalletAdapter } from '../Wallet.ts'
+
+declare global {
+ interface Window {
+ unisat: {
+ _isConnected: boolean
+ getAccounts: () => Promise
+ requestAccounts: () => Promise
+ signMessage: (message: string) => Promise
+ switchNetwork: (network: string) => Promise
+ on: (event: string, callback: (data: any) => void) => void
+ sendBitcoin: (to: string, amount: number) => Promise
+ }
+ }
+}
+
+const UniSat: WalletAdapterInterface = {
+ id: 'unisat',
+ name: 'UniSat',
+ icon: icons.UniSat,
+ platforms: [WalletPlatformEnum.BROWSER],
+ downloadLink: 'https://unisat.io/download',
+ isDetected: () => Boolean(window.unisat?.requestAccounts),
+ isConnected: async () => window?.unisat?._isConnected ?? false,
+ connect: async (provider?: ProviderInterface): Promise => {
+ return await new Promise((resolve, reject) => {
+ const network = provider !== undefined && provider?.isTestnet() ? 'testnet' : 'livenet'
+
+ const walletAdapter: BitcoinWalletAdapter = {
+ on: window.unisat.on,
+ signMessage: window.unisat.signMessage,
+ sendBitcoin: window.unisat.sendBitcoin,
+ getAddress: async () => {
+ return (await window.unisat.getAccounts())[0]
+ }
+ }
+
+ try {
+ window.unisat
+ .requestAccounts()
+ .then(() => {
+ window.unisat
+ .switchNetwork(network)
+ .then(() => {
+ resolve(walletAdapter)
+ })
+ .catch(reject)
+ })
+ .catch(reject)
+ } catch (error) {
+ reject(error)
+ }
+ })
+ }
+}
+
+export default UniSat
diff --git a/packages/networks/bitcoin/src/browser/adapters/Xverse.ts b/packages/networks/bitcoin/src/browser/adapters/Xverse.ts
new file mode 100644
index 0000000..ef50592
--- /dev/null
+++ b/packages/networks/bitcoin/src/browser/adapters/Xverse.ts
@@ -0,0 +1,139 @@
+import icons from './icons.ts'
+import type { BitcoinWalletAdapter } from '../Wallet.ts'
+import {
+ ErrorTypeEnum,
+ WalletPlatformEnum,
+ type ProviderInterface,
+ type WalletAdapterInterface
+} from '@multiplechain/types'
+import {
+ getAddress,
+ sendBtcTransaction,
+ BitcoinNetworkType,
+ signMessage,
+ AddressPurpose
+} from 'sats-connect'
+
+let connected = false
+
+const Xverse: WalletAdapterInterface = {
+ id: 'xverse',
+ name: 'Xverse',
+ icon: icons.Xverse,
+ platforms: [WalletPlatformEnum.BROWSER, WalletPlatformEnum.MOBILE],
+ downloadLink: 'https://www.xverse.app/download',
+ isDetected: () => Boolean(window.XverseProviders?.BitcoinProvider),
+ isConnected: async () => connected,
+ connect: async (provider?: ProviderInterface): Promise => {
+ return await new Promise((resolve, reject) => {
+ const type =
+ provider !== undefined && provider?.isTestnet()
+ ? BitcoinNetworkType.Testnet
+ : BitcoinNetworkType.Mainnet
+
+ const walletAdapter: BitcoinWalletAdapter = {
+ on: (_event: string, _callback: (data: any) => void) => {},
+ signMessage: async (message: string) => {
+ const address = await walletAdapter.getAddress()
+ return await new Promise((resolve, reject) => {
+ signMessage({
+ payload: {
+ network: {
+ type
+ },
+ address,
+ message
+ },
+ onFinish: (signature) => {
+ resolve(signature)
+ },
+ onCancel: () => {
+ reject(ErrorTypeEnum.WALLET_REQUEST_REJECTED)
+ }
+ }).catch(reject)
+ })
+ },
+ sendBitcoin: async (to: string, amount: number) => {
+ const senderAddress = await walletAdapter.getAddress()
+ return await new Promise((resolve, reject) => {
+ sendBtcTransaction({
+ payload: {
+ network: {
+ type
+ },
+ recipients: [
+ {
+ address: to,
+ amountSats: BigInt(amount)
+ }
+ ],
+ senderAddress
+ },
+ onFinish: (txId) => {
+ resolve(txId)
+ },
+ onCancel: () => {
+ reject(ErrorTypeEnum.WALLET_REQUEST_REJECTED)
+ }
+ }).catch(reject)
+ })
+ },
+ getAddress: async () => ''
+ }
+
+ const connect = async (): Promise => {
+ return await new Promise((resolve, reject) => {
+ try {
+ getAddress({
+ payload: {
+ purposes: [AddressPurpose.Payment, AddressPurpose.Ordinals],
+ message: 'Address for receiving Ordinals and payments',
+ network: {
+ type
+ }
+ },
+ onFinish: (response) => {
+ const addresses = Object.values(response.addresses)
+ const bitcoin = addresses.find(
+ (address) => address.purpose === AddressPurpose.Payment
+ )
+
+ // for ordinals & BRC-20 integrations
+ // const ordinals = addresses.find(address => address.purpose == 'ordinals');
+
+ if (bitcoin === undefined) {
+ reject(ErrorTypeEnum.WALLET_CONNECTION_FAILED)
+ return
+ }
+
+ walletAdapter.getAddress = async () => {
+ return bitcoin.address
+ }
+
+ resolve(walletAdapter)
+ },
+ onCancel: () => {
+ reject(ErrorTypeEnum.WALLET_REQUEST_REJECTED)
+ }
+ }).catch(reject)
+ } catch (error) {
+ reject(error)
+ }
+ })
+ }
+
+ try {
+ connect()
+ .then((walletAdapter) => {
+ connected = true
+ resolve(walletAdapter)
+ })
+ .catch(reject)
+ } catch (error) {
+ reject(error)
+ }
+ })
+ }
+}
+
+export default Xverse
diff --git a/packages/networks/bitcoin/src/browser/adapters/icons.ts b/packages/networks/bitcoin/src/browser/adapters/icons.ts
new file mode 100644
index 0000000..5417b1d
--- /dev/null
+++ b/packages/networks/bitcoin/src/browser/adapters/icons.ts
@@ -0,0 +1,6 @@
+export default {
+ UniSat: 'data:image/png;base64,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',
+ Xverse: 'data:image/png;base64,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',
+ Leather:
+ 'data:image/png;base64,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'
+}
diff --git a/packages/networks/bitcoin/src/browser/adapters/index.ts b/packages/networks/bitcoin/src/browser/adapters/index.ts
new file mode 100644
index 0000000..6408790
--- /dev/null
+++ b/packages/networks/bitcoin/src/browser/adapters/index.ts
@@ -0,0 +1,3 @@
+export { default as UniSat } from './UniSat.ts'
+export { default as Xverse } from './Xverse.ts'
+export { default as Leather } from './Leather.ts'
diff --git a/packages/networks/bitcoin/src/browser/index.ts b/packages/networks/bitcoin/src/browser/index.ts
new file mode 100644
index 0000000..787e9bd
--- /dev/null
+++ b/packages/networks/bitcoin/src/browser/index.ts
@@ -0,0 +1,25 @@
+import { Wallet } from './Wallet.ts'
+import * as adapterList from './adapters/index.ts'
+import type {
+ WalletAdapterListType,
+ WalletAdapterInterface,
+ RegisterWalletAdapterType
+} from '@multiplechain/types'
+
+const adapters: WalletAdapterListType = {}
+
+const registerAdapter: RegisterWalletAdapterType = (adapter: WalletAdapterInterface): void => {
+ if (Object.values(adapters).find((a) => a.id === adapter.id) !== undefined) {
+ throw new Error(`Adapter with id ${adapter.id} already exists`)
+ }
+
+ adapters[adapter.id] = adapter
+}
+
+export * from '../index.ts'
+
+export const browser = {
+ Wallet,
+ registerAdapter,
+ adapters: Object.assign(adapters, adapterList)
+}
diff --git a/packages/networks/bitcoin/src/index.ts b/packages/networks/bitcoin/src/index.ts
new file mode 100644
index 0000000..7b0e1cd
--- /dev/null
+++ b/packages/networks/bitcoin/src/index.ts
@@ -0,0 +1,8 @@
+export * from './services/Provider.ts'
+
+export * as assets from './assets/index.ts'
+export * as models from './models/index.ts'
+export * as services from './services/index.ts'
+
+export * as utils from './utils.ts'
+export * as types from '@multiplechain/types'
diff --git a/packages/networks/bitcoin/src/models/CoinTransaction.ts b/packages/networks/bitcoin/src/models/CoinTransaction.ts
new file mode 100644
index 0000000..d681e05
--- /dev/null
+++ b/packages/networks/bitcoin/src/models/CoinTransaction.ts
@@ -0,0 +1,63 @@
+import { fromSatoshi } from '../utils.ts'
+import { Transaction } from './Transaction.ts'
+import { TransactionStatusEnum } from '@multiplechain/types'
+import { AssetDirectionEnum, type CoinTransactionInterface } from '@multiplechain/types'
+
+export class CoinTransaction extends Transaction implements CoinTransactionInterface {
+ /**
+ * @returns {Promise} Wallet address of the receiver of transaction
+ */
+ async getReceiver(): Promise {
+ const data = await this.getData()
+ return data?.vout[0].scriptpubkey_address ?? ''
+ }
+
+ /**
+ * @returns {Promise} Wallet address of the sender of transaction
+ */
+ async getSender(): Promise {
+ return await this.getSigner()
+ }
+
+ /**
+ * @returns {Promise} Amount of coin that will be transferred
+ */
+ async getAmount(): Promise {
+ const data = await this.getData()
+ return fromSatoshi(data?.vout[0].value ?? 0)
+ }
+
+ /**
+ * @param {AssetDirectionEnum} direction - Direction of the transaction (asset)
+ * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction
+ * @param {number} amount Amount of assets that will be transferred
+ * @returns {Promise} Status of the transaction
+ */
+ async verifyTransfer(
+ direction: AssetDirectionEnum,
+ address: string,
+ amount: number
+ ): Promise {
+ const status = await this.getStatus()
+
+ if (status === TransactionStatusEnum.PENDING) {
+ return TransactionStatusEnum.PENDING
+ }
+
+ if ((await this.getAmount()) !== amount) {
+ return TransactionStatusEnum.FAILED
+ }
+
+ if (direction === AssetDirectionEnum.INCOMING) {
+ if ((await this.getReceiver()).toLowerCase() !== address.toLowerCase()) {
+ return TransactionStatusEnum.FAILED
+ }
+ } else {
+ if ((await this.getSender()).toLowerCase() !== address.toLowerCase()) {
+ return TransactionStatusEnum.FAILED
+ }
+ }
+
+ return TransactionStatusEnum.CONFIRMED
+ }
+}
diff --git a/packages/networks/bitcoin/src/models/ContractTransaction.ts b/packages/networks/bitcoin/src/models/ContractTransaction.ts
new file mode 100644
index 0000000..4813a5a
--- /dev/null
+++ b/packages/networks/bitcoin/src/models/ContractTransaction.ts
@@ -0,0 +1,21 @@
+import { Transaction } from './Transaction.ts'
+import type { Provider } from '../services/Provider.ts'
+import type { ContractTransactionInterface } from '@multiplechain/types'
+
+export class ContractTransaction extends Transaction implements ContractTransactionInterface {
+ /**
+ * @param {string} id Transaction id
+ * @param {Provider} provider Blockchain network provider
+ */
+ constructor(id: string, provider?: Provider) {
+ super(id, provider)
+ throw new Error('This class is not implemented for Bitcoin.')
+ }
+
+ /**
+ * @returns {Promise} Contract address of the transaction
+ */
+ async getAddress(): Promise {
+ return 'example'
+ }
+}
diff --git a/packages/networks/bitcoin/src/models/NftTransaction.ts b/packages/networks/bitcoin/src/models/NftTransaction.ts
new file mode 100644
index 0000000..09b8d80
--- /dev/null
+++ b/packages/networks/bitcoin/src/models/NftTransaction.ts
@@ -0,0 +1,41 @@
+import { ContractTransaction } from './ContractTransaction.ts'
+import { TransactionStatusEnum } from '@multiplechain/types'
+import type { NftTransactionInterface, AssetDirectionEnum } from '@multiplechain/types'
+
+export class NftTransaction extends ContractTransaction implements NftTransactionInterface {
+ /**
+ * @returns {Promise} Receiver wallet address
+ */
+ async getReceiver(): Promise {
+ return 'example'
+ }
+
+ /**
+ * @returns {Promise} Wallet address of the sender of transaction
+ */
+ async getSender(): Promise {
+ return 'example'
+ }
+
+ /**
+ * @returns {Promise} NFT ID
+ */
+ async getNftId(): Promise {
+ return 0
+ }
+
+ /**
+ * @param {AssetDirectionEnum} direction - Direction of the transaction (nft)
+ * @param {string} address - Wallet address of the receiver or sender of the transaction, dependant on direction
+ * @param {number} nftId ID of the NFT that will be transferred
+ * @override verifyTransfer() in AssetTransactionInterface
+ * @returns {Promise} Status of the transaction
+ */
+ async verifyTransfer(
+ direction: AssetDirectionEnum,
+ address: string,
+ nftId: number | string
+ ): Promise {
+ return TransactionStatusEnum.PENDING
+ }
+}
diff --git a/packages/networks/bitcoin/src/models/TokenTransaction.ts b/packages/networks/bitcoin/src/models/TokenTransaction.ts
new file mode 100644
index 0000000..b1ea29e
--- /dev/null
+++ b/packages/networks/bitcoin/src/models/TokenTransaction.ts
@@ -0,0 +1,40 @@
+import { ContractTransaction } from './ContractTransaction.ts'
+import { TransactionStatusEnum } from '@multiplechain/types'
+import type { AssetDirectionEnum, TokenTransactionInterface } from '@multiplechain/types'
+
+export class TokenTransaction extends ContractTransaction implements TokenTransactionInterface {
+ /**
+ * @returns {Promise} Wallet address of the receiver of transaction
+ */
+ async getReceiver(): Promise {
+ return 'example'
+ }
+
+ /**
+ * @returns {Promise} Wallet address of the sender of transaction
+ */
+ async getSender(): Promise {
+ return 'example'
+ }
+
+ /**
+ * @returns {Promise} Amount of tokens that will be transferred
+ */
+ async getAmount(): Promise {
+ return 0
+ }
+
+ /**
+ * @param {AssetDirectionEnum} direction - Direction of the transaction (token)
+ * @param {string} address - Wallet address of the owner or spender of the transaction, dependant on direction
+ * @param {number} amount Amount of tokens that will be approved
+ * @returns {Promise} Status of the transaction
+ */
+ async verifyTransfer(
+ direction: AssetDirectionEnum,
+ address: string,
+ amount: number
+ ): Promise {
+ return TransactionStatusEnum.PENDING
+ }
+}
diff --git a/packages/networks/bitcoin/src/models/Transaction.ts b/packages/networks/bitcoin/src/models/Transaction.ts
new file mode 100644
index 0000000..4cdeed8
--- /dev/null
+++ b/packages/networks/bitcoin/src/models/Transaction.ts
@@ -0,0 +1,205 @@
+import { fromSatoshi, sleep } from '../utils.ts'
+import axios, { type AxiosError } from 'axios'
+import { Provider } from '../services/Provider.ts'
+import type { TransactionInterface } from '@multiplechain/types'
+import { ErrorTypeEnum, TransactionStatusEnum } from '@multiplechain/types'
+
+export interface VinObject {
+ txid: string
+ vout: number
+ prevout: {
+ scriptpubkey: string
+ scriptpubkey_asm: string
+ scriptpubkey_type: string
+ scriptpubkey_address: string
+ value: number
+ }
+ scriptsig: string
+ scriptsig_asm: string
+ witness: string[]
+ is_coinbase: boolean
+ sequence: number
+}
+
+export interface VoutObject {
+ scriptpubkey: string
+ scriptpubkey_asm: string
+ scriptpubkey_type: string
+ scriptpubkey_address: string
+ value: number
+}
+
+export interface TransactionData {
+ txid: string
+ version: number
+ locktime: number
+ vin: VinObject[]
+ vout: VoutObject[]
+ size: number
+ weight: number
+ fee: number
+ status: {
+ confirmed: boolean
+ block_height: number
+ block_hash: string
+ block_time: number
+ }
+}
+
+let counter = 0
+
+export class Transaction implements TransactionInterface {
+ /**
+ * Each transaction has its own unique ID defined by the user
+ */
+ id: string
+
+ /**
+ * Blockchain network provider
+ */
+ provider: Provider
+
+ /**
+ * Transaction data
+ */
+ data: TransactionData | null = null
+
+ /**
+ * @param {string} id Transaction id
+ * @param {Provider} provider Blockchain network provider
+ */
+ constructor(id: string, provider?: Provider) {
+ this.id = id
+ this.provider = provider ?? Provider.instance
+ }
+
+ /**
+ * @returns {Promise} Transaction data
+ */
+ async getData(): Promise {
+ if (this.data !== null) {
+ return this.data
+ }
+ try {
+ const data = (await axios.get(this.provider.createEndpoint('tx/' + this.id))).data
+
+ if (data?.txid !== this.id) {
+ return (this.data = null)
+ }
+
+ return (this.data = data as TransactionData)
+ } catch (error) {
+ const axiosError = error as AxiosError
+ // Returns empty data when the transaction is first created. For this reason, it would be better to check it intermittently and give an error if it still does not exist. Average 10 seconds.
+ if (String(axiosError?.response?.data).includes('Transaction not found')) {
+ if (counter > 5) {
+ throw new Error(ErrorTypeEnum.TRANSACTION_NOT_FOUND)
+ }
+ counter++
+ await sleep(2000)
+ return await this.getData()
+ }
+ throw new Error(ErrorTypeEnum.RPC_REQUEST_ERROR)
+ }
+ }
+
+ /**
+ * @param {number} ms - Milliseconds to wait for the transaction to be confirmed. Default is 4000ms
+ * @returns {Promise} Status of the transaction
+ */
+ async wait(ms: number = 4000): Promise {
+ return await new Promise((resolve, reject) => {
+ const check = async (): Promise => {
+ try {
+ const status = await this.getStatus()
+ if (status === TransactionStatusEnum.CONFIRMED) {
+ resolve(TransactionStatusEnum.CONFIRMED)
+ return
+ } else if (status === TransactionStatusEnum.FAILED) {
+ reject(TransactionStatusEnum.FAILED)
+ return
+ }
+ setTimeout(check, ms)
+ } catch (error) {
+ reject(TransactionStatusEnum.FAILED)
+ }
+ }
+ void check()
+ })
+ }
+
+ /**
+ * @returns {string} Transaction ID
+ */
+ getId(): string {
+ return this.id
+ }
+
+ /**
+ * @returns {string} Transaction URL
+ */
+ getUrl(): string {
+ return this.provider.explorer + 'tx/' + this.id
+ }
+
+ /**
+ * @returns {Promise} Wallet address of the sender of transaction
+ */
+ async getSigner(): Promise {
+ const data = await this.getData()
+ return data?.vin[0].prevout.scriptpubkey_address ?? ''
+ }
+
+ /**
+ * @returns {Promise} Transaction fee
+ */
+ async getFee(): Promise {
+ const data = await this.getData()
+ return fromSatoshi(data?.fee ?? 0)
+ }
+
+ /**
+ * @returns {Promise} Block number that transaction
+ */
+ async getBlockNumber(): Promise {
+ const data = await this.getData()
+ return data?.status?.block_height ?? 0
+ }
+
+ /**
+ * @returns {Promise} Block timestamp that transaction
+ */
+ async getBlockTimestamp(): Promise {
+ const data = await this.getData()
+ return data?.status?.block_time ?? 0
+ }
+
+ /**
+ * @returns {Promise} Confirmation count of the block
+ */
+ async getBlockConfirmationCount(): Promise {
+ const data = await this.getData()
+ if (data === null) {
+ return 0
+ }
+ const latestBlock = await axios.get(this.provider.createEndpoint('blocks/tip/height'))
+ return (latestBlock.data as number) - data?.status?.block_height
+ }
+
+ /**
+ * @returns {Promise} Status of the transaction
+ */
+ async getStatus(): Promise {
+ const data = await this.getData()
+ if (data === null) {
+ return TransactionStatusEnum.PENDING
+ } else if (data.status?.block_height !== undefined) {
+ if (data.status.confirmed) {
+ return TransactionStatusEnum.CONFIRMED
+ } else {
+ return TransactionStatusEnum.FAILED
+ }
+ }
+ return TransactionStatusEnum.PENDING
+ }
+}
diff --git a/packages/networks/bitcoin/src/models/index.ts b/packages/networks/bitcoin/src/models/index.ts
new file mode 100644
index 0000000..cc0a27c
--- /dev/null
+++ b/packages/networks/bitcoin/src/models/index.ts
@@ -0,0 +1,5 @@
+export * from './Transaction.ts'
+export * from './NftTransaction.ts'
+export * from './CoinTransaction.ts'
+export * from './TokenTransaction.ts'
+export * from './ContractTransaction.ts'
diff --git a/packages/networks/bitcoin/src/services/Provider.ts b/packages/networks/bitcoin/src/services/Provider.ts
new file mode 100644
index 0000000..30086e2
--- /dev/null
+++ b/packages/networks/bitcoin/src/services/Provider.ts
@@ -0,0 +1,156 @@
+import axios from 'axios'
+import { checkWebSocket } from '@multiplechain/utils'
+import { ErrorTypeEnum, type ProviderInterface } from '@multiplechain/types'
+
+export interface BitcoinNetworkConfigInterface {
+ testnet: boolean
+ blockCypherToken?: string
+}
+
+export class Provider implements Omit {
+ /**
+ * Network configuration of the provider
+ */
+ network: BitcoinNetworkConfigInterface
+
+ /**
+ * API URL
+ */
+ api: string
+
+ /**
+ * Explorer URL
+ */
+ explorer: string
+
+ /**
+ * Websocket URL
+ */
+ wsUrl: string
+
+ /**
+ * BlockCypher token
+ */
+ blockCypherToken?: string
+
+ /**
+ * Default BlockCypher token
+ */
+ defaultBlockCypherToken = '49d43a59a4f24d31a9731eb067ab971c'
+
+ /**
+ * Static instance of the provider
+ */
+ private static _instance: Provider
+
+ /**
+ * @param network - Network configuration of the provider
+ */
+ constructor(network: BitcoinNetworkConfigInterface) {
+ this.update(network)
+ }
+
+ /**
+ * Get the static instance of the provider
+ * @returns {Provider} Provider
+ */
+ static get instance(): Provider {
+ if (Provider._instance === undefined) {
+ throw new Error(ErrorTypeEnum.PROVIDER_IS_NOT_INITIALIZED)
+ }
+ return Provider._instance
+ }
+
+ /**
+ * Initialize the static instance of the provider
+ * @param {BitcoinNetworkConfigInterface} network - Network configuration of the provider
+ * @returns {void}
+ */
+ static initialize(network: BitcoinNetworkConfigInterface): void {
+ if (Provider._instance !== undefined) {
+ throw new Error(ErrorTypeEnum.PROVIDER_IS_ALREADY_INITIALIZED)
+ }
+ Provider._instance = new Provider(network)
+ }
+
+ /**
+ * Check RPC connection
+ * @param {string} url - RPC URL
+ * @returns {Promise}
+ */
+ async checkRpcConnection(url?: string): Promise {
+ try {
+ const response = await axios.get(url ?? this.createEndpoint('blocks/tip/height'))
+
+ if (response.status !== 200) {
+ return new Error(response.statusText + ': ' + JSON.stringify(response.data))
+ }
+
+ return true
+ } catch (error) {
+ return error as Error
+ }
+ }
+
+ /**
+ * Check WS connection
+ * @param {string} url - Websocket URL
+ * @returns {Promise}
+ */
+ async checkWsConnection(url?: string): Promise {
+ try {
+ const result: any = await checkWebSocket(url ?? this.wsUrl)
+
+ if (result instanceof Error) {
+ return result
+ }
+
+ return true
+ } catch (error) {
+ return error as Error
+ }
+ }
+
+ /**
+ * Update network configuration of the provider
+ * @param network - Network configuration of the provider
+ */
+ update(network: BitcoinNetworkConfigInterface): void {
+ this.network = network
+ Provider._instance = this
+ this.blockCypherToken = this.network.blockCypherToken
+ if (this.network.testnet) {
+ this.api = 'https://blockstream.info/testnet/api/'
+ this.explorer = 'https://blockstream.info/testnet/'
+ const token = this.network.blockCypherToken ?? this.defaultBlockCypherToken
+ this.wsUrl = 'wss://socket.blockcypher.com/v1/btc/test3?token=' + token
+ } else {
+ this.api = 'https://blockstream.info/api/'
+ this.explorer = 'https://blockstream.info/'
+ if (this.network.blockCypherToken !== undefined) {
+ this.wsUrl =
+ 'wss://socket.blockcypher.com/v1/btc/main?token=' +
+ this.network.blockCypherToken
+ } else {
+ this.wsUrl = 'wss://ws.blockchain.info/inv'
+ }
+ }
+ }
+
+ /**
+ * Create a new endpoint
+ * @param {string} endpoint - Endpoint
+ * @returns {string} Endpoint
+ */
+ createEndpoint(endpoint: string): string {
+ return this.api + endpoint
+ }
+
+ /**
+ * Get the current network configuration is testnet or not
+ * @returns boolean
+ */
+ isTestnet(): boolean {
+ return this.network?.testnet ?? false
+ }
+}
diff --git a/packages/networks/bitcoin/src/services/TransactionListener.ts b/packages/networks/bitcoin/src/services/TransactionListener.ts
new file mode 100644
index 0000000..04fa972
--- /dev/null
+++ b/packages/networks/bitcoin/src/services/TransactionListener.ts
@@ -0,0 +1,352 @@
+import type {
+ TransactionTypeEnum,
+ DynamicTransactionType,
+ TransactionListenerInterface,
+ DynamicTransactionListenerFilterType
+} from '@multiplechain/types'
+import WebSocket from 'ws'
+import { Provider } from './Provider.ts'
+import { fromSatoshi } from '../utils.ts'
+import { Transaction } from '../models/Transaction.ts'
+import type { NftTransaction } from '../models/NftTransaction.ts'
+import { CoinTransaction } from '../models/CoinTransaction.ts'
+import type { TokenTransaction } from '../models/TokenTransaction.ts'
+import type { ContractTransaction } from '../models/ContractTransaction.ts'
+import { checkWebSocket, objectsEqual } from '@multiplechain/utils'
+import { TransactionListenerProcessIndex } from '@multiplechain/types'
+
+interface Values {
+ txId: string
+ amount?: number
+ sender?: string
+ receiver?: string
+}
+
+type TransactionListenerTriggerType = DynamicTransactionType<
+ T,
+ Transaction,
+ ContractTransaction,
+ CoinTransaction,
+ TokenTransaction,
+ NftTransaction
+>
+
+type TransactionListenerCallbackType<
+ T extends TransactionTypeEnum,
+ Transaction = TransactionListenerTriggerType
+> = (transaction: Transaction) => void
+
+export class TransactionListener<
+ T extends TransactionTypeEnum,
+ DTransaction extends TransactionListenerTriggerType,
+ CallBackType extends TransactionListenerCallbackType
+> implements TransactionListenerInterface
+{
+ /**
+ * Transaction type
+ */
+ type: T
+
+ /**
+ * Transaction listener callback
+ */
+ callbacks: CallBackType[] = []
+
+ /**
+ * Transaction listener filter
+ */
+ filter?: DynamicTransactionListenerFilterType | Record
+
+ /**
+ * Provider
+ */
+ provider: Provider
+
+ /**
+ * Listener status
+ */
+ status: boolean = false
+
+ /**
+ * Triggered transactions
+ */
+ triggeredTransactions: string[] = []
+
+ /**
+ * WebSocket
+ */
+ webSocket: WebSocket
+
+ /**
+ * Dynamic stop method
+ */
+ dynamicStop: () => void = () => {}
+
+ /**
+ * @param {T} type - Transaction type
+ * @param {DynamicTransactionListenerFilterType} filter - Transaction listener filter
+ * @param {Provider} provider - Provider
+ */
+ constructor(type: T, filter?: DynamicTransactionListenerFilterType, provider?: Provider) {
+ this.type = type
+ this.filter = filter ?? {}
+ this.provider = provider ?? Provider.instance
+ }
+
+ /**
+ * Close the listener
+ * @returns {void}
+ */
+ stop(): void {
+ if (this.status) {
+ this.status = false
+ this.dynamicStop()
+ }
+ }
+
+ /**
+ * Start the listener
+ * @returns {void}
+ */
+ start(): void {
+ if (!this.status) {
+ this.status = true
+ // @ts-expect-error allow dynamic access
+ this[TransactionListenerProcessIndex[this.type]]()
+ }
+ }
+
+ /**
+ * Get the listener status
+ * @returns {boolean} Listener status
+ */
+ getStatus(): boolean {
+ return this.status
+ }
+
+ /**
+ * Listen to the transaction events
+ * @param {CallBackType} callback - Transaction listener callback
+ * @returns {Promise}
+ */
+ async on(callback: CallBackType): Promise {
+ if (this.webSocket === undefined) {
+ try {
+ await checkWebSocket(this.provider.wsUrl)
+ this.webSocket = new WebSocket(this.provider.wsUrl)
+ } catch (error) {
+ throw new Error(
+ 'WebSocket connection is not available' +
+ (error instanceof Error ? ': ' + error.message : '')
+ )
+ }
+ }
+
+ this.start()
+ this.callbacks.push(callback)
+
+ return true
+ }
+
+ /**
+ * Trigger the event when a transaction is detected
+ * @param {TransactionListenerTriggerType} transaction - Transaction data
+ * @returns {void}
+ */
+ trigger(transaction: TransactionListenerTriggerType): void {
+ if (!this.triggeredTransactions.includes(transaction.id)) {
+ this.triggeredTransactions.push(transaction.id)
+ this.callbacks.forEach((callback) => {
+ callback(transaction as unknown as DTransaction)
+ })
+ }
+ }
+
+ isBlockCypherProcess(): boolean {
+ return this.provider.isTestnet() || this.provider.blockCypherToken !== undefined
+ }
+
+ /**
+ * Create message for the listener
+ * @param {string} receiver - Receiver address
+ * @returns {string} Message
+ */
+ createMessage(receiver?: string): string {
+ let message
+ if (this.isBlockCypherProcess()) {
+ interface Config {
+ event: string
+ token: string
+ address?: string
+ }
+
+ const config: Config = {
+ event: 'unconfirmed-tx',
+ token: this.provider.blockCypherToken ?? this.provider.defaultBlockCypherToken
+ }
+
+ if (receiver !== undefined) {
+ config.address = receiver
+ }
+
+ message = JSON.stringify(config)
+ } else {
+ message = JSON.stringify({
+ op: 'unconfirmed_sub'
+ })
+ }
+
+ this.dynamicStop = () => {
+ if (!this.isBlockCypherProcess()) {
+ this.webSocket.send(
+ JSON.stringify({
+ op: 'unconfirmed_unsub'
+ })
+ )
+ }
+ this.webSocket.close()
+ }
+
+ return message
+ }
+
+ /**
+ * Parse the data
+ * @param {any} data - Data
+ * @returns {Values} Parsed data
+ */
+ getValues(data: any): Values {
+ const values: Values = {
+ txId: ''
+ }
+
+ if (this.isBlockCypherProcess()) {
+ values.txId = data.hash
+ values.sender = data.inputs[0].addresses[0]
+ values.receiver = data.outputs[0].addresses[0]
+ values.amount = fromSatoshi(data.outputs[0].value as number)
+ } else {
+ values.txId = data.x.hash
+ values.receiver = data.x.out[0].addr
+ values.sender = data.x.inputs[0].prev_out.addr
+ values.amount = fromSatoshi(data.x.out[0].value as number)
+ }
+
+ return values
+ }
+
+ /**
+ * General transaction process
+ * @returns {void}
+ */
+ generalProcess(): void {
+ const message = this.createMessage()
+
+ this.webSocket.addEventListener('open', () => {
+ this.webSocket.send(message)
+ })
+
+ this.webSocket.addEventListener('message', async (res) => {
+ const values = this.getValues(JSON.parse(res.data as string))
+
+ if (
+ this.filter?.signer !== undefined &&
+ values.sender !== this.filter.signer.toLowerCase()
+ ) {
+ return
+ }
+
+ this.trigger(new Transaction(values.txId))
+ })
+ }
+
+ /**
+ * Contract transaction process
+ * @returns {void}
+ */
+ contractProcess(): void {
+ throw new Error('This method is not implemented for Bitcoin.')
+ }
+
+ /**
+ * Coin transaction process
+ * @returns {void}
+ */
+ coinProcess(): void {
+ const filter = this.filter as DynamicTransactionListenerFilterType
+
+ if (
+ filter.signer !== undefined &&
+ filter.sender !== undefined &&
+ filter.signer !== filter.sender
+ ) {
+ throw new Error(
+ 'Sender and signer must be the same in coin transactions. Or only one of them can be defined.'
+ )
+ }
+
+ const sender = filter.sender ?? filter.signer
+
+ const message = this.createMessage(filter.receiver)
+
+ this.webSocket.addEventListener('open', () => {
+ this.webSocket.send(message)
+ })
+
+ this.webSocket.addEventListener('message', async (res) => {
+ const data = JSON.parse(res.data as string)
+
+ interface ParamsType {
+ sender?: string
+ receiver?: string
+ }
+
+ const expectedParams: ParamsType = {}
+ const receivedParams: ParamsType = {}
+
+ if (String(data.event).includes('events limit reached')) {
+ throw new Error('BlockCypher events limit reached.')
+ }
+
+ const values = this.getValues(data)
+
+ if (sender !== undefined) {
+ expectedParams.sender = sender.toLowerCase()
+ receivedParams.sender = values.sender?.toLowerCase()
+ }
+
+ if (filter.receiver !== undefined) {
+ expectedParams.receiver = filter.receiver.toLowerCase()
+ receivedParams.receiver = values.receiver?.toLowerCase()
+ }
+
+ if (!objectsEqual(expectedParams, receivedParams)) {
+ return
+ }
+
+ const transaction = new CoinTransaction(values.txId)
+
+ if (filter.amount !== undefined && values.amount !== filter.amount) {
+ return
+ }
+
+ this.trigger(transaction)
+ })
+ }
+
+ /**
+ * Token transaction process
+ * @returns {void}
+ */
+ tokenProcess(): void {
+ throw new Error('This method is not implemented for Bitcoin.')
+ }
+
+ /**
+ * NFT transaction process
+ * @returns {void}
+ */
+ nftProcess(): void {
+ throw new Error('This method is not implemented for Bitcoin.')
+ }
+}
diff --git a/packages/networks/bitcoin/src/services/TransactionSigner.ts b/packages/networks/bitcoin/src/services/TransactionSigner.ts
new file mode 100644
index 0000000..15ae13a
--- /dev/null
+++ b/packages/networks/bitcoin/src/services/TransactionSigner.ts
@@ -0,0 +1,115 @@
+import axios from 'axios'
+import type { AxiosError } from 'axios'
+import { Provider } from '../services/Provider.ts'
+import { Transaction } from '../models/Transaction.ts'
+import { NftTransaction } from '../models/NftTransaction.ts'
+import { CoinTransaction } from '../models/CoinTransaction.ts'
+import { TokenTransaction } from '../models/TokenTransaction.ts'
+import { type TransactionSignerInterface } from '@multiplechain/types'
+import type { Transaction as BitcoreLibTransactionData } from 'bitcore-lib'
+
+export interface TransactionData {
+ sender: string
+ receiver: string
+ amount: number
+ bitcoreLib: BitcoreLibTransactionData
+}
+
+export class TransactionSigner implements TransactionSignerInterface {
+ /**
+ * Transaction data from the blockchain network
+ */
+ rawData: TransactionData
+
+ /**
+ * Signed transaction data
+ */
+ signedData?: string
+
+ /**
+ * Blockchain network provider
+ */
+ provider: Provider
+
+ /**
+ * @param {TransactionData} rawData - Transaction data
+ */
+ constructor(rawData: TransactionData, provider?: Provider) {
+ this.rawData = rawData
+ this.provider = provider ?? Provider.instance
+ }
+
+ /**
+ * Sign the transaction
+ * @param {string} privateKey - Transaction data
+ * @returns {Promise} Signed transaction data
+ */
+ async sign(privateKey: string): Promise {
+ this.rawData.bitcoreLib.sign(privateKey)
+ this.signedData = this.rawData.bitcoreLib.serialize()
+ return this
+ }
+
+ /**
+ * Send the transaction to the blockchain network
+ * @returns {Promise}
+ */
+ async send(): Promise {
+ try {
+ const result = await axios({
+ method: 'POST',
+ url: `https://blockstream.info/testnet/api/tx`,
+ data: this.signedData
+ })
+ return new Transaction(result.data as string)
+ } catch (error: any) {
+ throw new Error(JSON.stringify((error as AxiosError).response?.data))
+ }
+ }
+
+ /**
+ * Get the raw transaction data
+ * @returns Transaction data
+ */
+ getRawData(): TransactionData {
+ return this.rawData
+ }
+
+ /**
+ * Get the signed transaction data
+ * @returns Signed transaction data
+ */
+ getSignedData(): string {
+ return this.signedData ?? ''
+ }
+}
+
+export class CoinTransactionSigner extends TransactionSigner {
+ /**
+ * Send the transaction to the blockchain network
+ * @returns {Promise} Transaction data
+ */
+ async send(): Promise {
+ return new CoinTransaction((await super.send()).getId())
+ }
+}
+
+export class TokenTransactionSigner extends TransactionSigner {
+ /**
+ * Send the transaction to the blockchain network
+ * @returns {Promise} Transaction data
+ */
+ async send(): Promise {
+ return new TokenTransaction((await super.send()).getId())
+ }
+}
+
+export class NftTransactionSigner extends TransactionSigner {
+ /**
+ * Send the transaction to the blockchain network
+ * @returns {Promise} Transaction data
+ */
+ async send(): Promise {
+ return new NftTransaction((await super.send()).getId())
+ }
+}
diff --git a/packages/networks/bitcoin/src/services/index.ts b/packages/networks/bitcoin/src/services/index.ts
new file mode 100644
index 0000000..549a382
--- /dev/null
+++ b/packages/networks/bitcoin/src/services/index.ts
@@ -0,0 +1,2 @@
+export * from './TransactionSigner.ts'
+export * from './TransactionListener.ts'
diff --git a/packages/networks/bitcoin/src/utils.ts b/packages/networks/bitcoin/src/utils.ts
new file mode 100644
index 0000000..14bd3ec
--- /dev/null
+++ b/packages/networks/bitcoin/src/utils.ts
@@ -0,0 +1,11 @@
+import { math } from '@multiplechain/utils'
+
+export * from '@multiplechain/utils'
+
+export const fromSatoshi = (amount: number): number => {
+ return math.div(amount, 100000000, 8)
+}
+
+export const toSatoshi = (amount: number): number => {
+ return math.mul(amount, 100000000, 8)
+}
diff --git a/packages/networks/bitcoin/tests/assets.spec.ts b/packages/networks/bitcoin/tests/assets.spec.ts
new file mode 100644
index 0000000..36147df
--- /dev/null
+++ b/packages/networks/bitcoin/tests/assets.spec.ts
@@ -0,0 +1,63 @@
+import { describe, it, expect, assert } from 'vitest'
+
+import { Coin } from '../src/assets/Coin.ts'
+import { math } from '@multiplechain/utils'
+import { Transaction } from '../src/models/Transaction.ts'
+import { TransactionStatusEnum } from '@multiplechain/types'
+import { TransactionSigner } from '../src/services/TransactionSigner.ts'
+
+const testAmount = Number(process.env.BTC_TRANSFER_AMOUNT)
+const senderTestAddress = String(process.env.BTC_SENDER_ADDRESS)
+const receiverTestAddress = String(process.env.BTC_RECEIVER_ADDRESS)
+const senderPrivateKey = String(process.env.BTC_SENDER_PRIVATE_KEY)
+const transferTestIsActive = Boolean(process.env.BTC_TRANSFER_TEST_IS_ACTIVE !== 'false')
+
+const checkSigner = async (signer: TransactionSigner, privateKey?: string): Promise => {
+ expect(signer).toBeInstanceOf(TransactionSigner)
+
+ const rawData = signer.getRawData()
+
+ assert.isObject(rawData)
+
+ await signer.sign(privateKey ?? senderPrivateKey)
+
+ assert.isString(signer.getSignedData())
+}
+
+const checkTx = async (transaction: Transaction): Promise => {
+ expect(transaction).toBeInstanceOf(Transaction)
+ const status = await transaction.wait(10 * 1000)
+ expect(status).toBe(TransactionStatusEnum.CONFIRMED)
+}
+
+describe('Coin', () => {
+ const coin = new Coin()
+ it('Name and symbol', () => {
+ expect(coin.getName()).toBe('Bitcoin')
+ expect(coin.getSymbol()).toBe('BTC')
+ })
+
+ it('Decimals', () => {
+ expect(coin.getDecimals()).toBe(8)
+ })
+
+ it('Balance', async () => {
+ const balance = await coin.getBalance('tb1qc240vx54n08hnhx8l4rqxjzcxf4f0ssq5asawm')
+ expect(balance).toBe(0.00003)
+ })
+
+ it('Transfer', async () => {
+ const signer = await coin.transfer(senderTestAddress, receiverTestAddress, testAmount)
+
+ await checkSigner(signer)
+
+ if (!transferTestIsActive) return
+
+ const beforeBalance = await coin.getBalance(receiverTestAddress)
+
+ await checkTx(await signer.send())
+
+ const afterBalance = await coin.getBalance(receiverTestAddress)
+ expect(afterBalance).toBe(math.add(beforeBalance, testAmount))
+ })
+})
diff --git a/packages/networks/bitcoin/tests/models.spec.ts b/packages/networks/bitcoin/tests/models.spec.ts
new file mode 100644
index 0000000..d4265de
--- /dev/null
+++ b/packages/networks/bitcoin/tests/models.spec.ts
@@ -0,0 +1,78 @@
+import { describe, it, expect } from 'vitest'
+import { Transaction } from '../src/models/Transaction.ts'
+import { CoinTransaction } from '../src/models/CoinTransaction.ts'
+import { AssetDirectionEnum, TransactionStatusEnum } from '@multiplechain/types'
+
+const testAmount = Number(process.env.BTC_TRANSFER_AMOUNT)
+const senderTestAddress = String(process.env.BTC_SENDER_ADDRESS)
+const receiverTestAddress = String(process.env.BTC_RECEIVER_ADDRESS)
+const txId = '335c8a251e5f18121977c3159f46983d5943325abccc19e4718c49089553d60c'
+
+describe('Transaction', () => {
+ const tx = new Transaction(txId)
+ it('Id', async () => {
+ expect(tx.getId()).toBe(txId)
+ })
+
+ it('Data', async () => {
+ expect(await tx.getData()).toBeTypeOf('object')
+ })
+
+ it('Wait', async () => {
+ expect(await tx.wait()).toBe(TransactionStatusEnum.CONFIRMED)
+ })
+
+ it('URL', async () => {
+ expect(tx.getUrl()).toBe('https://blockstream.info/testnet/tx/' + txId)
+ })
+
+ it('Sender', async () => {
+ expect((await tx.getSigner()).toLowerCase()).toBe(senderTestAddress.toLowerCase())
+ })
+
+ it('Fee', async () => {
+ expect(await tx.getFee()).toBe(0.00014)
+ })
+
+ it('Block Number', async () => {
+ expect(await tx.getBlockNumber()).toBe(2814543)
+ })
+
+ it('Block Timestamp', async () => {
+ expect(await tx.getBlockTimestamp()).toBe(1715328679)
+ })
+
+ it('Block Confirmation Count', async () => {
+ expect(await tx.getBlockConfirmationCount()).toBeGreaterThan(13)
+ })
+
+ it('Status', async () => {
+ expect(await tx.getStatus()).toBe(TransactionStatusEnum.CONFIRMED)
+ })
+})
+
+describe('Coin Transaction', () => {
+ const tx = new CoinTransaction(txId)
+
+ it('Receiver', async () => {
+ expect((await tx.getReceiver()).toLowerCase()).toBe(receiverTestAddress.toLowerCase())
+ })
+
+ it('Amount', async () => {
+ expect(await tx.getAmount()).toBe(testAmount)
+ })
+
+ it('Verify Transfer', async () => {
+ expect(
+ await tx.verifyTransfer(AssetDirectionEnum.INCOMING, receiverTestAddress, testAmount)
+ ).toBe(TransactionStatusEnum.CONFIRMED)
+
+ expect(
+ await tx.verifyTransfer(AssetDirectionEnum.OUTGOING, senderTestAddress, testAmount)
+ ).toBe(TransactionStatusEnum.CONFIRMED)
+
+ expect(
+ await tx.verifyTransfer(AssetDirectionEnum.OUTGOING, receiverTestAddress, testAmount)
+ ).toBe(TransactionStatusEnum.FAILED)
+ })
+})
diff --git a/packages/networks/bitcoin/tests/services.spec.ts b/packages/networks/bitcoin/tests/services.spec.ts
new file mode 100644
index 0000000..d5e59cb
--- /dev/null
+++ b/packages/networks/bitcoin/tests/services.spec.ts
@@ -0,0 +1,67 @@
+import { describe, it, expect } from 'vitest'
+
+import { provider } from './setup.ts'
+import { Provider } from '../src/services/Provider.ts'
+import { TransactionListener } from '../src/services/TransactionListener.ts'
+import { TransactionTypeEnum } from '@multiplechain/types'
+import { CoinTransaction } from '../src/models/CoinTransaction.ts'
+import { Coin } from '../src/assets/Coin.ts'
+import { sleep } from '@multiplechain/utils'
+
+const senderTestAddress = String(process.env.BTC_SENDER_ADDRESS)
+const receiverTestAddress = String(process.env.BTC_RECEIVER_ADDRESS)
+const senderPrivateKey = String(process.env.BTC_SENDER_PRIVATE_KEY)
+const listenerTestIsActive = Boolean(process.env.BTC_LISTENER_TEST_IS_ACTIVE !== 'false')
+
+describe('Provider', () => {
+ it('isTestnet', () => {
+ expect(provider.isTestnet()).toBe(true)
+ })
+
+ it('instance', () => {
+ expect(Provider.instance).toBe(provider)
+ })
+
+ it('checkRpcConnection', async () => {
+ expect(await provider.checkRpcConnection()).toBe(true)
+ })
+
+ it('checkWsConnection', async () => {
+ expect(await provider.checkWsConnection()).toBe(true)
+ })
+})
+
+describe('Transaction Listener', () => {
+ if (!listenerTestIsActive) {
+ it('No test is active', () => {
+ expect(true).toBe(true)
+ })
+ return
+ }
+
+ it('Coin', async () => {
+ const listener = new TransactionListener(TransactionTypeEnum.COIN, {
+ signer: senderTestAddress,
+ receiver: receiverTestAddress
+ })
+
+ const signer = await new Coin().transfer(senderTestAddress, receiverTestAddress, 0.0001)
+
+ const waitListenerEvent = async (): Promise => {
+ return await new Promise((resolve, reject) => {
+ void listener
+ .on((transaction) => {
+ listener.stop()
+ resolve(transaction)
+ })
+ .then(async () => {
+ await sleep(2000)
+ void (await signer.sign(senderPrivateKey)).send()
+ })
+ .catch(reject)
+ })
+ }
+
+ expect(await waitListenerEvent()).toBeInstanceOf(CoinTransaction)
+ })
+})
diff --git a/packages/networks/bitcoin/tests/setup.ts b/packages/networks/bitcoin/tests/setup.ts
new file mode 100644
index 0000000..6c04b6b
--- /dev/null
+++ b/packages/networks/bitcoin/tests/setup.ts
@@ -0,0 +1,13 @@
+import { Provider } from '../src/services/Provider.ts'
+
+let provider: Provider
+
+try {
+ provider = Provider.instance
+} catch (e) {
+ provider = new Provider({
+ testnet: true
+ })
+}
+
+export { provider }
diff --git a/packages/networks/bitcoin/tsconfig.json b/packages/networks/bitcoin/tsconfig.json
new file mode 100644
index 0000000..e40c547
--- /dev/null
+++ b/packages/networks/bitcoin/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "noEmit": true,
+ "composite": true,
+ "declaration": true,
+ "outDir": "./dist/esm",
+ "declarationDir": "./dist/types"
+ },
+ "extends": "../../../tsconfig.json",
+ "include": [
+ "src",
+ ".eslintrc.json",
+ "tests",
+ "vite.config.ts",
+ "esbuild.ts",
+ "vitest.config.ts",
+ "../../../esbuild.ts",
+ "../../../vite.config.ts",
+ "../../../vitest.config.ts",
+ ]
+}
diff --git a/packages/networks/bitcoin/vite.config.ts b/packages/networks/bitcoin/vite.config.ts
new file mode 100644
index 0000000..87de654
--- /dev/null
+++ b/packages/networks/bitcoin/vite.config.ts
@@ -0,0 +1,10 @@
+import { mergeConfig } from 'vite'
+import mainConfig from '../../../vite.config.ts'
+
+export default mergeConfig(mainConfig, {
+ build: {
+ lib: {
+ name: 'Bitcoin'
+ }
+ }
+})
diff --git a/packages/networks/bitcoin/vite.svg b/packages/networks/bitcoin/vite.svg
new file mode 100644
index 0000000..e7b8dfb
--- /dev/null
+++ b/packages/networks/bitcoin/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/networks/bitcoin/vitest.config.ts b/packages/networks/bitcoin/vitest.config.ts
new file mode 100644
index 0000000..c96edcb
--- /dev/null
+++ b/packages/networks/bitcoin/vitest.config.ts
@@ -0,0 +1,12 @@
+import { mergeConfig, defineConfig } from 'vitest/config'
+import mainConfig from '../../../vite.config.ts'
+
+export default mergeConfig(
+ mainConfig,
+ defineConfig({
+ test: {
+ testTimeout: 600000,
+ setupFiles: ['./tests/setup.ts']
+ }
+ })
+)
diff --git a/packages/networks/boilerplate/index.html b/packages/networks/boilerplate/index.html
index 236da6d..4e508e4 100644
--- a/packages/networks/boilerplate/index.html
+++ b/packages/networks/boilerplate/index.html
@@ -187,9 +187,6 @@
Deep link:
-
- Chain id:
-
Connected address:
@@ -215,7 +212,7 @@
+