@multiplechain/bitcoin 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,6 +14,10 @@
14
14
 
15
15
  /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
16
16
 
17
+ /*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
18
+
19
+ /*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
20
+
17
21
  /*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
18
22
 
19
23
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "namespace": "multiplechain",
3
3
  "name": "@multiplechain/bitcoin",
4
- "version": "0.1.11",
4
+ "version": "0.1.13",
5
5
  "description": "Bitcoin JS provider",
6
6
  "scripts": {
7
7
  "serve": "parcel test.html --no-cache --dist-dir test",
@@ -34,6 +34,7 @@
34
34
  "bignumber.js": "^9.1.1",
35
35
  "install": "^0.13.0",
36
36
  "npm": "^9.7.2",
37
+ "sats-connect": "^1.1.1",
37
38
  "web3-utils": "^1.8.1",
38
39
  "ws": "^8.13.0"
39
40
  },
@@ -0,0 +1,75 @@
1
+ module.exports = leather = (provider) => {
2
+
3
+ if (window.crypto && !window.crypto.randomUUID) {
4
+ window.crypto.randomUUID = () => {
5
+ let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
6
+ const r = (Math.random() * 16) | 0;
7
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
8
+ return v.toString(16);
9
+ });
10
+ return uuid;
11
+ };
12
+ }
13
+
14
+ const network = provider.testnet ? 'testnet' : 'mainnet';
15
+
16
+ let wallet = Object.assign({
17
+ sendBitcoin: (to, amount) => {
18
+ return new Promise(async (resolve, reject) => {
19
+ try {
20
+ await wallet.request('sendTransfer', {
21
+ address: to,
22
+ amount,
23
+ network
24
+ })
25
+ .then((response) => {
26
+ resolve(response.result.txid);
27
+ })
28
+ .catch(({error}) => {
29
+ reject(error);
30
+ });
31
+ } catch (error) {
32
+ reject(error)
33
+ }
34
+ });
35
+ },
36
+ on: (event, callback) => {
37
+ if (window.btc && btc.listen) {
38
+ btc.listen(event, callback);
39
+ }
40
+ }
41
+ }, window.LeatherProvider);
42
+
43
+ const connect = async () => {
44
+ return new Promise(async (resolve, reject) => {
45
+ try {
46
+ const addresses = (await wallet.request('getAddresses', {
47
+ network
48
+ })).result.addresses;
49
+ const bitcoin = addresses.find(address => address.type == 'p2wpkh');
50
+
51
+ // for ordinals & BRC-20 integrations
52
+ // const ordinals = addresses.find(address => address.type == 'p2tr');
53
+
54
+ wallet = Object.assign(wallet, window.LeatherProvider);
55
+
56
+ wallet.getAddress = async () => {
57
+ return bitcoin.address;
58
+ }
59
+
60
+ resolve(wallet);
61
+ } catch (error) {
62
+ reject(error);
63
+ }
64
+ });
65
+ }
66
+
67
+ return {
68
+ key: 'leather',
69
+ name: 'Leather',
70
+ supports: ['browser'],
71
+ connect,
72
+ download: 'https://leather.io/install-extension',
73
+ isDetected: () => Boolean(typeof window.LeatherProvider !== 'undefined')
74
+ }
75
+ }
@@ -1,15 +1,20 @@
1
1
  module.exports = unisat = (provider) => {
2
2
 
3
3
  const wallet = window.unisat;
4
+ const network = provider.testnet ? 'testnet' : 'livenet';
5
+
6
+ wallet.getAddress = async () => {
7
+ return (await wallet.getAccounts())[0];
8
+ }
4
9
 
5
10
  const connect = async () => {
6
11
  return new Promise(async (resolve, reject) => {
7
12
  try {
8
13
  wallet.requestAccounts()
9
14
  .then(async () => {
10
- wallet.switchNetwork(provider.network)
15
+ wallet.switchNetwork(network)
11
16
  .then(async () => {
12
- resolve((await wallet.getAccounts())[0]);
17
+ resolve(wallet);
13
18
  })
14
19
  .catch(error => {
15
20
  reject(error);
@@ -28,9 +33,8 @@ module.exports = unisat = (provider) => {
28
33
  key: 'unisat',
29
34
  name: 'UniSat',
30
35
  supports: ['browser'],
31
- wallet,
32
36
  connect,
33
37
  download: 'https://unisat.io/download',
34
- detected: Boolean(typeof window.unisat !== 'undefined' && window.unisat.requestAccounts)
38
+ isDetected: () => Boolean(typeof window.unisat !== 'undefined' && window.unisat.requestAccounts)
35
39
  }
36
40
  }
@@ -0,0 +1,86 @@
1
+ const {getAddress, sendBtcTransaction, BitcoinNetworkType} = require('sats-connect');
2
+
3
+ module.exports = xverse = (provider) => {
4
+
5
+ const type = provider.testnet ?
6
+ BitcoinNetworkType.Testnet:
7
+ BitcoinNetworkType.Mainnet;
8
+
9
+ let wallet = {
10
+ sendBitcoin: (to, amount) => {
11
+ return new Promise(async (resolve, reject) => {
12
+ sendBtcTransaction({
13
+ payload: {
14
+ network: {
15
+ type,
16
+ },
17
+ recipients: [
18
+ {
19
+ address: to,
20
+ amountSats: BigInt(amount),
21
+ }
22
+ ],
23
+ senderAddress: provider.connectedWallet.connectedAccount,
24
+ },
25
+ onFinish: (txId) => {
26
+ resolve(txId);
27
+ },
28
+ onCancel: () => {
29
+ reject('request-rejected');
30
+ }
31
+ });
32
+ });
33
+ },
34
+ on: (event, callback) => {
35
+ // TODO: implement
36
+ }
37
+ }
38
+
39
+ const connect = async () => {
40
+ return new Promise(async (resolve, reject) => {
41
+ try {
42
+ getAddress({
43
+ payload: {
44
+ purposes: ['ordinals', 'payment'],
45
+ message: 'Address for receiving Ordinals and payments',
46
+ network: {
47
+ type
48
+ },
49
+ },
50
+ onFinish: (response) => {
51
+ const addresses = Object.values(response.addresses);
52
+ const bitcoin = addresses.find(address => address.purpose == 'payment');
53
+
54
+ // for ordinals & BRC-20 integrations
55
+ // const ordinals = addresses.find(address => address.purpose == 'ordinals');
56
+
57
+ wallet = Object.assign(wallet, window.XverseProviders.BitcoinProvider);
58
+
59
+ wallet.getAddress = async () => {
60
+ return bitcoin.address;
61
+ }
62
+
63
+ resolve(wallet);
64
+ },
65
+ onCancel: () => {
66
+ reject('request-rejected');
67
+ }
68
+ });
69
+ } catch (error) {
70
+ reject(error);
71
+ }
72
+ });
73
+ }
74
+
75
+ return {
76
+ key: 'xverse',
77
+ name: 'Xverse',
78
+ supports: [
79
+ 'browser',
80
+ 'mobile'
81
+ ],
82
+ connect,
83
+ download: 'https://www.xverse.app/download',
84
+ isDetected: () => Boolean(typeof window.XverseProviders !== 'undefined' && XverseProviders.BitcoinProvider)
85
+ }
86
+ }
@@ -43,40 +43,13 @@ class Coin {
43
43
  /**
44
44
  * @returns {Object}
45
45
  */
46
- async getBalance() {
47
- let balance = await this.provider.connectedWallet.wallet.getBalance();
48
- return {
49
- confirmed: utils.toDec(balance.confirmed, this.getDecimals()),
50
- unconfirmed: utils.toDec(balance.unconfirmed, this.getDecimals()),
51
- total: utils.toDec(balance.total, this.getDecimals())
52
- }
46
+ async getBalance(address) {
47
+ let addressStatsApi = this.provider.api + 'address/' + address;
48
+ let addressStats = await fetch(addressStatsApi).then(res => res.json());
49
+ let balanceSat = addressStats.chain_stats.funded_txo_sum - addressStats.chain_stats.spent_txo_sum;
50
+ return utils.toBitcoin(balanceSat);
53
51
  }
54
52
 
55
- /**
56
- * @returns {Number}
57
- */
58
- async getConfirmedBalance() {
59
- let balance = await this.getBalance();
60
- return balance.confirmed;
61
- }
62
-
63
- /**
64
- * @returns {Number}
65
- */
66
- async getUnconfirmedBalance() {
67
- let balance = await this.getBalance();
68
- return balance.unconfirmed;
69
- }
70
-
71
- /**
72
- * @returns {Number}
73
- */
74
- async getTotalBalance() {
75
- let balance = await this.getBalance();
76
- return balance.total;
77
- }
78
-
79
-
80
53
  /**
81
54
  * @param {String} from
82
55
  * @param {String} to
@@ -86,18 +59,14 @@ class Coin {
86
59
  transfer(from, to, amount) {
87
60
  return new Promise(async (resolve, reject) => {
88
61
 
89
- if (parseFloat(amount) > await this.getConfirmedBalance(from)) {
62
+ if (parseFloat(amount) > await this.getBalance(from)) {
90
63
  return reject('insufficient-balance');
91
64
  }
92
65
 
93
66
  amount = utils.toSatoshi(amount);
94
- this.provider.connectedWallet.wallet.sendBitcoin(to, amount)
95
- .then((txid) => {
96
- resolve(txid);
97
- })
98
- .catch((error) => {
99
- reject(error);
100
- });
67
+
68
+ // develop for be side
69
+
101
70
  });
102
71
  }
103
72
  }
@@ -1,5 +1,7 @@
1
1
  const adapters = {
2
2
  unisat: require('./adapters/unisat'),
3
+ xverse: require('./adapters/xverse'),
4
+ leather: require('./adapters/leather'),
3
5
  }
4
6
 
5
7
  /**
package/src/provider.js CHANGED
@@ -41,32 +41,43 @@ class Provider {
41
41
  /**
42
42
  * @var {Object}
43
43
  */
44
- detectedWallets = [];
44
+ supportedWallets = {};
45
45
 
46
46
  /**
47
47
  * @var {Object}
48
48
  */
49
49
  connectedWallet;
50
50
 
51
+ /**
52
+ * @var {String}
53
+ */
54
+ blockcypherToken;
55
+
51
56
  /**
52
57
  * @param {Object} options
53
58
  */
54
59
  constructor(options = {}) {
55
60
 
56
61
  this.testnet = options.testnet;
57
- this.network = options.testnet ? 'testnet' : 'livenet';
62
+ this.network = options.testnet ? 'testnet' : 'mainnet';
63
+ this.blockcypherToken = options.blockcypherToken;
58
64
 
59
65
  if (this.testnet) {
60
66
  this.api = "https://blockstream.info/testnet/api/";
61
67
  this.explorer = "https://blockstream.info/testnet/";
62
- this.wsUrl = "wss://socket.blockcypher.com/v1/btc/test3?token=6d9cba333f234b9498473955497c40d9";
68
+ let token = this.blockcypherToken || "6d9cba333f234b9498473955497c40d9";
69
+ this.wsUrl = "wss://socket.blockcypher.com/v1/btc/test3?token=" + token;
63
70
  } else {
64
71
  this.api = "https://blockstream.info/api/";
65
72
  this.explorer = "https://blockstream.info/";
66
- this.wsUrl = "wss://ws.blockchain.info/inv";
73
+ if (this.blockcypherToken) {
74
+ this.wsUrl = "wss://socket.blockcypher.com/v1/btc/main?token=" + this.blockcypherToken;
75
+ } else {
76
+ this.wsUrl = "wss://ws.blockchain.info/inv";
77
+ }
67
78
  }
68
79
 
69
- this.detectWallets();
80
+ this.initSupportedWallets();
70
81
  }
71
82
 
72
83
  getWalletOpenLink(address, amount) {
@@ -78,11 +89,11 @@ class Provider {
78
89
  let ws = new WebSocket(this.wsUrl);
79
90
 
80
91
  let message;
81
- if (this.testnet) {
92
+ if (this.testnet || this.blockcypherToken) {
82
93
  message = JSON.stringify({
83
94
  event: "unconfirmed-tx",
84
95
  address: receiver,
85
- token: "6d9cba333f234b9498473955497c40d9"
96
+ token: this.blockcypherToken || "6d9cba333f234b9498473955497c40d9"
86
97
  });
87
98
  } else {
88
99
  message = JSON.stringify({
@@ -92,7 +103,7 @@ class Provider {
92
103
 
93
104
  let subscription = {
94
105
  unsubscribe: () => {
95
- if (!this.testnet) {
106
+ if (!this.testnet && !this.blockcypherToken) {
96
107
  ws.send(JSON.stringify({
97
108
  "op": "unconfirmed_unsub"
98
109
  }));
@@ -121,7 +132,7 @@ class Provider {
121
132
  let result = true;
122
133
  let data = JSON.parse(res.data);
123
134
 
124
- if (!this.testnet) {
135
+ if (!this.testnet && !this.blockcypherToken) {
125
136
  result = data.x.out.find(o => {
126
137
  return String(o.addr).toLowerCase() == receiver.toLowerCase();
127
138
  });
@@ -155,7 +166,7 @@ class Provider {
155
166
  resolve(wallet);
156
167
  })
157
168
  .catch(error => {
158
-
169
+ reject(error);
159
170
  });
160
171
  } else {
161
172
  reject('wallet-not-found');
@@ -164,40 +175,38 @@ class Provider {
164
175
  }
165
176
 
166
177
  /**
167
- * @param {Array|null} filter
168
- * @returns {Array}
178
+ * @returns {void}
169
179
  */
170
- getSupportedWallets(filter) {
171
-
180
+ initSupportedWallets() {
172
181
  const Wallet = require('./wallet');
173
182
 
174
- const wallets = {
175
- unisat: new Wallet('unisat', this)
183
+ this.supportedWallets = {
184
+ unisat: new Wallet('unisat', this),
185
+ xverse: new Wallet('xverse', this),
186
+ leather: new Wallet('leather', this),
176
187
  };
177
188
 
178
- return Object.fromEntries(Object.entries(wallets).filter(([key]) => {
179
- return !filter ? true : filter.includes(key);
180
- }));
181
189
  }
182
190
 
183
191
  /**
184
192
  * @param {Array|null} filter
185
193
  * @returns {Array}
186
194
  */
187
- getDetectedWallets(filter) {
188
- return Object.fromEntries(Object.entries(this.detectedWallets).filter(([key]) => {
195
+ getSupportedWallets(filter) {
196
+ return Object.fromEntries(Object.entries(this.supportedWallets).filter(([key]) => {
189
197
  return !filter ? true : filter.includes(key);
190
198
  }));
191
199
  }
192
200
 
193
- detectWallets() {
194
- if (typeof window != 'undefined') {
195
- const Wallet = require('./wallet');
196
-
197
- if (typeof window.unisat !== 'undefined' && unisat.requestAccounts) {
198
- this.detectedWallets['unisat'] = new Wallet('unisat', this);
199
- }
200
- }
201
+ /**
202
+ * @param {Array|null} filter
203
+ * @returns {Array}
204
+ */
205
+ getDetectedWallets(filter) {
206
+ let detectedWallets = this.getSupportedWallets(filter);
207
+ return Object.fromEntries(Object.entries(detectedWallets).filter(([key, value]) => {
208
+ return value.isDetected() == undefined ? true : value.isDetected()
209
+ }));
201
210
  }
202
211
 
203
212
  Coin() {
package/src/utils.js CHANGED
@@ -8,11 +8,16 @@ module.exports = Object.assign(utils, {
8
8
  let value = new BigNumber(amount.toString(10), 10).times(length);
9
9
  return parseInt(value.toString(10));
10
10
  },
11
+ toBitcoin(amount) {
12
+ return parseFloat(amount.toString(10) / 100000000);
13
+ },
11
14
  rejectMessage(error, reject) {
12
15
 
13
16
  if (typeof error == 'object') {
14
17
  if (error.code == 4001 || error.message == 'User rejected the request.') {
15
18
  return reject('request-rejected');
19
+ } else if (String(error).includes('is not valid JSON')) {
20
+ return reject('not-accepted-chain');
16
21
  }
17
22
  }
18
23
 
package/src/wallet.js CHANGED
@@ -37,7 +37,6 @@ class Wallet {
37
37
  */
38
38
  setAdapter(adapter) {
39
39
  this.adapter = getAdapter(adapter, this.provider);
40
- this.wallet = this.adapter.wallet;
41
40
  }
42
41
 
43
42
  /**
@@ -79,7 +78,7 @@ class Wallet {
79
78
  * @returns {Boolean}
80
79
  */
81
80
  isDetected() {
82
- return this.adapter.detected;
81
+ return this.adapter.isDetected();
83
82
  }
84
83
 
85
84
  /**
@@ -88,14 +87,11 @@ class Wallet {
88
87
  connect() {
89
88
  return new Promise((resolve, reject) => {
90
89
  this.adapter.connect()
91
- .then(async (connectedAccount) => {
92
- let network = await this.wallet.getNetwork();
93
- if (this.provider.network == network) {
94
- this.provider.setConnectedWallet(this);
95
- resolve(this.connectedAccount = connectedAccount);
96
- } else {
97
- reject('not-accepted-chain');
98
- }
90
+ .then(async (wallet) => {
91
+ this.wallet = wallet;
92
+ this.connectedAccount = await wallet.getAddress();
93
+ this.provider.setConnectedWallet(this);
94
+ resolve(this.connectedAccount);
99
95
  })
100
96
  .catch((error) => {
101
97
  utils.rejectMessage(error, reject);
@@ -128,8 +124,13 @@ class Wallet {
128
124
  return new Promise(async (resolve, reject) => {
129
125
  try {
130
126
  let coin = this.provider.Coin();
131
-
132
- coin.transfer(this.connectedAccount, to, amount)
127
+ if (parseFloat(amount) > await coin.getBalance(this.connectedAccount)) {
128
+ return reject('insufficient-balance');
129
+ }
130
+
131
+ amount = utils.toSatoshi(amount);
132
+
133
+ this.wallet.sendBitcoin(to, amount)
133
134
  .then((transactionId) => {
134
135
  resolve(this.provider.Transaction(transactionId));
135
136
  })