@multiplechain/bitcoin 0.1.10 → 0.1.12

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.10",
4
+ "version": "0.1.12",
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
+ detected: 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,7 +33,6 @@ 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
38
  detected: Boolean(typeof window.unisat !== 'undefined' && window.unisat.requestAccounts)
@@ -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
+ detected: 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
@@ -48,22 +48,33 @@ class Provider {
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
- constructor(options) {
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
80
  this.detectWallets();
@@ -77,74 +88,62 @@ class Provider {
77
88
  let receiver = options.receiver;
78
89
  let ws = new WebSocket(this.wsUrl);
79
90
 
80
- if (this.testnet) {
81
- let subscription = {
82
- unsubscribe: () => {
83
- ws.close();
84
- }
85
- }
86
-
87
- ws.addEventListener('open', () => {
88
- ws.send(JSON.stringify({
89
- event: "unconfirmed-tx",
90
- address: receiver,
91
- token: "6d9cba333f234b9498473955497c40d9"
92
- }));
93
- });
94
-
95
- let startCallback = async (data) => {
96
- try {
97
- let tx = this.Transaction(data.hash);
98
- await tx.getData();
99
- callback(subscription, tx);
100
- } catch (error) {
101
- setTimeout(() => {
102
- startCallback(data);
103
- }, 2500);
104
- }
105
- }
106
-
107
- ws.addEventListener('message', (res) => {
108
- setTimeout(() => {
109
- startCallback(JSON.parse(res.data));
110
- }, 6000);
91
+ let message;
92
+ if (this.testnet || this.blockcypherToken) {
93
+ message = JSON.stringify({
94
+ event: "unconfirmed-tx",
95
+ address: receiver,
96
+ token: this.blockcypherToken || "6d9cba333f234b9498473955497c40d9"
111
97
  });
112
98
  } else {
113
- let subscription = {
114
- unsubscribe: () => {
99
+ message = JSON.stringify({
100
+ "op": "unconfirmed_sub"
101
+ });
102
+ }
103
+
104
+ let subscription = {
105
+ unsubscribe: () => {
106
+ if (!this.testnet && !this.blockcypherToken) {
115
107
  ws.send(JSON.stringify({
116
- "op": "addr_unsub",
117
- "addr": receiver
108
+ "op": "unconfirmed_unsub"
118
109
  }));
119
- ws.close();
120
110
  }
111
+ ws.close();
121
112
  }
122
-
123
- ws.addEventListener('open', () => {
124
- ws.send(JSON.stringify({
125
- "op": "addr_sub",
126
- "addr": receiver
127
- }));
128
- });
113
+ }
129
114
 
130
- let startCallback = async (data) => {
131
- try {
132
- let tx = this.Transaction(data.x.hash);
133
- await tx.getData();
134
- callback(subscription, tx);
135
- } catch (error) {
136
- setTimeout(() => {
137
- startCallback(data);
138
- }, 2500);
139
- }
115
+ let startCallback = async (data) => {
116
+ try {
117
+ let tx = this.Transaction(data.hash || data.x.hash);
118
+ await tx.getData();
119
+ callback(subscription, tx);
120
+ } catch (error) {
121
+ setTimeout(() => {
122
+ startCallback(data);
123
+ }, 2500);
124
+ }
125
+ }
126
+
127
+ ws.addEventListener('open', () => {
128
+ ws.send(message);
129
+ });
130
+
131
+ ws.addEventListener('message', (res) => {
132
+ let result = true;
133
+ let data = JSON.parse(res.data);
134
+
135
+ if (!this.testnet && !this.blockcypherToken) {
136
+ result = data.x.out.find(o => {
137
+ return String(o.addr).toLowerCase() == receiver.toLowerCase();
138
+ });
140
139
  }
141
140
 
142
- ws.addEventListener('message', (res) => {
141
+ if (result) {
143
142
  setTimeout(() => {
144
- startCallback(JSON.parse(res.data));
143
+ startCallback(data);
145
144
  }, 6000);
146
- });
147
- }
145
+ }
146
+ });
148
147
  }
149
148
 
150
149
  /**
@@ -167,7 +166,7 @@ class Provider {
167
166
  resolve(wallet);
168
167
  })
169
168
  .catch(error => {
170
-
169
+ reject(error);
171
170
  });
172
171
  } else {
173
172
  reject('wallet-not-found');
@@ -184,7 +183,9 @@ class Provider {
184
183
  const Wallet = require('./wallet');
185
184
 
186
185
  const wallets = {
187
- unisat: new Wallet('unisat', this)
186
+ unisat: new Wallet('unisat', this),
187
+ xverse: new Wallet('xverse', this),
188
+ leather: new Wallet('leather', this),
188
189
  };
189
190
 
190
191
  return Object.fromEntries(Object.entries(wallets).filter(([key]) => {
@@ -209,6 +210,14 @@ class Provider {
209
210
  if (typeof window.unisat !== 'undefined' && unisat.requestAccounts) {
210
211
  this.detectedWallets['unisat'] = new Wallet('unisat', this);
211
212
  }
213
+
214
+ if (typeof window.XverseProviders !== 'undefined' && XverseProviders.BitcoinProvider) {
215
+ this.detectedWallets['xverse'] = new Wallet('xverse', this);
216
+ }
217
+
218
+ if (typeof window.LeatherProvider !== 'undefined') {
219
+ this.detectedWallets['leather'] = new Wallet('leather', this);
220
+ }
212
221
  }
213
222
  }
214
223
 
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
  /**
@@ -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
  })