@multiplechain/bitcoin 0.1.1 → 0.1.2

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.
package/package.json CHANGED
@@ -1,45 +1,45 @@
1
- {
2
- "namespace": "multiplechain",
3
- "name": "@multiplechain/bitcoin",
4
- "version": "0.1.1",
5
- "description": "Bitcoin JS provider",
6
- "scripts": {
7
- "serve": "parcel test.html --no-cache --dist-dir test",
8
- "build": "webpack"
9
- },
10
- "files": [
11
- "src"
12
- ],
13
- "main": "src/provider.js",
14
- "types": "index.d.ts",
15
- "typings": "index.d.ts",
16
- "repository": {
17
- "type": "git",
18
- "url": "git+https://github.com/MultipleChain/bitcoin.git"
19
- },
20
- "keywords": [
21
- "Blockchain",
22
- "Bitcoin",
23
- "Cryptocurrency"
24
- ],
25
- "author": "MultipleChain",
26
- "license": "MIT",
27
- "bugs": {
28
- "url": "https://github.com/MultipleChain/bitcoin/issues"
29
- },
30
- "homepage": "https://github.com/MultipleChain/bitcoin",
31
- "dependencies": {
32
- "@multiplechain/utils": "^0.1.1",
33
- "bignumber.js": "^9.1.1",
34
- "web3-utils": "^1.8.1"
35
- },
36
- "devDependencies": {
37
- "assert": "^2.0.0",
38
- "buffer": "^5.7.1",
39
- "events": "^3.3.0",
40
- "process": "^0.11.10",
41
- "stream-browserify": "^3.0.0",
42
- "webpack": "^5.76.0",
43
- "webpack-cli": "^5.0.1"
44
- }
45
- }
1
+ {
2
+ "namespace": "multiplechain",
3
+ "name": "@multiplechain/bitcoin",
4
+ "version": "0.1.2",
5
+ "description": "Bitcoin JS provider",
6
+ "scripts": {
7
+ "serve": "parcel test.html --no-cache --dist-dir test",
8
+ "build": "webpack"
9
+ },
10
+ "files": [
11
+ "src"
12
+ ],
13
+ "main": "src/bitcoin-provider.js",
14
+ "types": "index.d.ts",
15
+ "typings": "index.d.ts",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/MultipleChain/bitcoin.git"
19
+ },
20
+ "keywords": [
21
+ "Blockchain",
22
+ "Bitcoin",
23
+ "Cryptocurrency"
24
+ ],
25
+ "author": "MultipleChain",
26
+ "license": "MIT",
27
+ "bugs": {
28
+ "url": "https://github.com/MultipleChain/bitcoin/issues"
29
+ },
30
+ "homepage": "https://github.com/MultipleChain/bitcoin",
31
+ "dependencies": {
32
+ "@multiplechain/utils": "^0.1.1",
33
+ "bignumber.js": "^9.1.1",
34
+ "web3-utils": "^1.8.1"
35
+ },
36
+ "devDependencies": {
37
+ "assert": "^2.0.0",
38
+ "buffer": "^5.7.1",
39
+ "events": "^3.3.0",
40
+ "process": "^0.11.10",
41
+ "stream-browserify": "^3.0.0",
42
+ "webpack": "^5.76.0",
43
+ "webpack-cli": "^5.0.1"
44
+ }
45
+ }
@@ -0,0 +1,35 @@
1
+ module.exports = unisat = (provider) => {
2
+
3
+ const wallet = window.unisat;
4
+
5
+ const connect = async () => {
6
+ return new Promise(async (resolve, reject) => {
7
+ try {
8
+ wallet.requestAccounts()
9
+ .then(async () => {
10
+ wallet.switchNetwork(provider.network)
11
+ .then(async () => {
12
+ resolve((await wallet.getAccounts())[0]);
13
+ })
14
+ .catch(error => {
15
+ reject(error);
16
+ });
17
+ })
18
+ .catch(error => {
19
+ reject(error);
20
+ });
21
+ } catch (error) {
22
+ reject(error);
23
+ }
24
+ });
25
+ }
26
+
27
+ return {
28
+ key: 'unisat',
29
+ name: 'UniSat',
30
+ type: 'browser',
31
+ wallet,
32
+ connect,
33
+ download: 'https://unisat.io/download'
34
+ }
35
+ }
@@ -0,0 +1,105 @@
1
+ const utils = require('../utils');
2
+
3
+ class Coin {
4
+
5
+ /**
6
+ * @var {String}
7
+ */
8
+ symbol;
9
+
10
+ /**
11
+ * @var {String}
12
+ */
13
+ decimals;
14
+
15
+ /**
16
+ * @var {Provider}
17
+ */
18
+ provider;
19
+
20
+ /**
21
+ * @param {Provider} provider
22
+ */
23
+ constructor(provider) {
24
+ this.provider = provider;
25
+ this.decimals = 8;
26
+ this.symbol = 'BTC';
27
+ }
28
+
29
+ /**
30
+ * @returns {String}
31
+ */
32
+ getSymbol() {
33
+ return this.symbol;
34
+ }
35
+
36
+ /**
37
+ * @returns {Integer}
38
+ */
39
+ getDecimals() {
40
+ return this.decimals;
41
+ }
42
+
43
+ /**
44
+ * @returns {Object}
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
+ }
53
+ }
54
+
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
+ /**
81
+ * @param {String} from
82
+ * @param {String} to
83
+ * @param {Number} amount
84
+ * @returns {String|Object}
85
+ */
86
+ transfer(from, to, amount) {
87
+ return new Promise(async (resolve, reject) => {
88
+
89
+ if (parseFloat(amount) > await this.getConfirmedBalance(from)) {
90
+ return reject('insufficient-balance');
91
+ }
92
+
93
+ 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
+ });
101
+ });
102
+ }
103
+ }
104
+
105
+ module.exports = Coin;
@@ -0,0 +1,5 @@
1
+ class Token {
2
+ // TODO: Implement this class
3
+ }
4
+
5
+ module.exports = Token;
@@ -20,7 +20,7 @@ class Transaction {
20
20
  /**
21
21
  * @var {Number}
22
22
  */
23
- timer;
23
+ timer = 30;
24
24
 
25
25
  /**
26
26
  * @param {String} hash
@@ -49,15 +49,7 @@ class Transaction {
49
49
  */
50
50
  async getData() {
51
51
  try {
52
-
53
- let txApi;
54
-
55
- if (this.provider.testnet) {
56
- txApi = this.provider.api + 'tx/' + this.hash;
57
- } else {
58
- txApi = this.provider.api + 'rawtx/' + this.hash;
59
- }
60
-
52
+ let txApi = this.provider.api + 'tx/' + this.hash;
61
53
  this.data = await fetch(txApi).then(res => res.json());
62
54
  } catch (error) {
63
55
  throw new Error('There was a problem retrieving transaction data!');
@@ -72,13 +64,7 @@ class Transaction {
72
64
  async getConfirmations() {
73
65
  try {
74
66
 
75
- let blockApi;
76
- if (this.provider.testnet) {
77
- blockApi = this.provider.api + 'blocks/tip/height';
78
- } else {
79
- blockApi = this.provider.api + 'latestblock';
80
- }
81
-
67
+ let blockApi = this.provider.api + 'blocks/tip/height';
82
68
  if (!this.data) await this.getData();
83
69
  let latestBlock = await fetch(blockApi).then(res => res.json());
84
70
 
@@ -86,14 +72,7 @@ class Transaction {
86
72
  latestBlock = latestBlock.height;
87
73
  }
88
74
 
89
- let blockHeight;
90
- if (this.provider.testnet) {
91
- blockHeight = this.data.status.block_height;
92
- } else {
93
- blockHeight = this.data.block_height;
94
- }
95
-
96
- return ((latestBlock - blockHeight) + 1);
75
+ return ((latestBlock - (this.data.status.block_height || 0)) + 1);
97
76
  } catch (error) {}
98
77
  }
99
78
 
@@ -133,14 +112,8 @@ class Transaction {
133
112
  if (this.data == null) {
134
113
  result = false;
135
114
  } else {
136
- if (this.provider.testnet) {
137
- if (this.data.status.block_height) {
138
- result = true;
139
- }
140
- } else {
141
- if (this.data.block_height) {
142
- result = true;
143
- }
115
+ if (this.data.status.block_height) {
116
+ result = true;
144
117
  }
145
118
  }
146
119
 
@@ -173,31 +146,15 @@ class Transaction {
173
146
 
174
147
  if (await this.validateTransaction()) {
175
148
 
176
- let data;
177
- if (this.provider.testnet) {
178
-
179
- let index = this.data.vout.findIndex(object => {
180
- return object.scriptpubkey_address == config.receiver;
181
- });
182
-
183
- data = this.data.vout[index];
184
-
185
- data = {
186
- receiver: data.scriptpubkey_address,
187
- amount: utils.toDec(data.value, 8)
188
- };
189
- } else {
190
-
191
- let index = this.data.out.findIndex(object => {
192
- return object.addr == config.receiver;
193
- });
149
+ let index = this.data.vout.findIndex(object => {
150
+ return object.scriptpubkey_address == config.receiver;
151
+ });
194
152
 
195
- data = this.data.out[index];
196
-
197
- data = {
198
- receiver: data.addr,
199
- amount: utils.toDec(data.value, 8)
200
- };
153
+ let data = this.data.vout[index];
154
+
155
+ data = {
156
+ receiver: data.scriptpubkey_address,
157
+ amount: utils.toDec(data.value, 8)
201
158
  }
202
159
 
203
160
  if (data.receiver == config.receiver && data.amount == config.amount) {
@@ -214,11 +171,7 @@ class Transaction {
214
171
  * @returns {String}
215
172
  */
216
173
  getUrl() {
217
- if (this.provider.testnet) {
218
- return this.provider.explorer + 'tx/' + this.hash;
219
- } else {
220
- return this.provider.explorer + 'transactions/btc/' + this.hash;
221
- }
174
+ return this.provider.explorer + 'tx/' + this.hash;
222
175
  }
223
176
  }
224
177
 
@@ -0,0 +1,11 @@
1
+ const adapters = {
2
+ unisat: require('./adapters/unisat'),
3
+ }
4
+
5
+ /**
6
+ * @param {String} adapter
7
+ * @param {Object} provider
8
+ */
9
+ module.exports = getAdapter = (adapter, provider) => {
10
+ return adapters[adapter](provider);
11
+ }
package/src/provider.js CHANGED
@@ -1,40 +1,101 @@
1
- const Transaction = require("./transaction");
2
1
  const utils = require("@multiplechain/utils");
2
+ const Coin = require("./entity/coin");
3
+ const Token = require("./entity/token");
4
+ const Transaction = require("./entity/transaction");
3
5
 
4
6
  class Provider {
5
7
 
8
+ /**
9
+ * @var {String}
10
+ */
6
11
  api;
7
12
 
13
+ /**
14
+ * @var {String}
15
+ */
8
16
  explorer;
9
17
 
18
+ /**
19
+ * @var {Boolean}
20
+ */
10
21
  testnet;
11
22
 
23
+ /**
24
+ * @var {String}
25
+ */
26
+ network;
27
+
28
+ /**
29
+ * @var {Object}
30
+ */
31
+ detectedWallets = [];
32
+
33
+ /**
34
+ * @var {Object}
35
+ */
36
+ connectedWallet;
37
+
12
38
  constructor(testnet = false) {
13
39
  this.testnet = testnet;
40
+ this.network = testnet ? 'testnet' : 'livenet';
14
41
 
15
42
  if (!this.testnet) {
16
- this.api = "https://blockchain.info/";
17
- this.explorer = "https://www.blockchain.com/explorer/";
43
+ this.api = "https://blockstream.info/api/";
44
+ this.explorer = "https://blockstream.info/";
18
45
  } else {
19
46
  this.api = "https://blockstream.info/testnet/api/";
20
47
  this.explorer = "https://blockstream.info/testnet/";
21
48
  }
49
+
50
+ this.detectWallets();
22
51
  }
23
52
 
24
53
  getWalletOpenLink(address, amount) {
25
54
  return 'bitcoin' + ':' + String(address).toUpperCase() + '?amount=' + amount;
26
55
  }
27
56
 
28
- async getAddressLastTransaction(receiver) {
29
-
30
- let apiUrl;
31
- if (this.testnet) {
32
- apiUrl = this.api + 'address/' + receiver + '/txs';
33
- } else {
34
- apiUrl = this.api + 'rawaddr/' + receiver;
57
+ errorCheck(data) {
58
+ if (typeof data == 'string') {
59
+ if (data == 'Address on invalid network') {
60
+ return {
61
+ error: 'invalid-address'
62
+ }
63
+ } else {
64
+ return {
65
+ error: 'any-error'
66
+ }
67
+ }
68
+ } else if (data.error) {
69
+ if (data.error == 'not-found-or-invalid-arg') {
70
+ return {
71
+ error: 'invalid-address'
72
+ }
73
+ } else {
74
+ return {
75
+ error: 'any-error'
76
+ }
77
+ }
35
78
  }
36
79
 
37
- let data = await fetch(apiUrl).then(res => res.json());
80
+ return data;
81
+ }
82
+
83
+ /**
84
+ * @param {String} receiver
85
+ * @returns
86
+ */
87
+ async getLastTransactionByReceiver(receiver) {
88
+
89
+ let apiUrl = this.api + 'address/' + receiver + '/txs';
90
+ let data = await fetch(apiUrl).then(response => response.text());
91
+ try {
92
+ data = JSON.parse(data);
93
+ } catch (error) {}
94
+
95
+ data = this.errorCheck(data);
96
+ if (data.error) {
97
+ return data;
98
+ }
38
99
 
39
100
  if (data.length == 0) {
40
101
  return {
@@ -42,38 +103,78 @@ class Provider {
42
103
  amount: 0
43
104
  }
44
105
  }
106
+
107
+ let tx = data[0];
45
108
 
46
- if (data.txs) {
109
+ let index = tx.vout.findIndex(object => {
110
+ return object.scriptpubkey_address == receiver;
111
+ });
47
112
 
48
- let tx = data.txs[0];
113
+ data = tx.vout[index];
49
114
 
50
- let index = tx.out.findIndex(object => {
51
- return object.addr == receiver;
52
- });
115
+ return {
116
+ hash: tx.txid,
117
+ amount: utils.toDec(data.value, 8)
118
+ };
119
+ }
53
120
 
54
- data = tx.out[index];
55
-
56
- return {
57
- hash: tx.hash,
58
- amount: utils.toDec(data.value, 8)
121
+ /**
122
+ * @param {Wallet} wallet
123
+ */
124
+ setConnectedWallet(wallet) {
125
+ this.connectedWallet = wallet;
126
+ }
127
+
128
+ /**
129
+ * @param {String} adapter
130
+ * @returns {Promise}
131
+ */
132
+ connectWallet(adapter) {
133
+ return new Promise(async (resolve, reject) => {
134
+ if (this.detectedWallets[adapter]) {
135
+ let wallet = this.detectedWallets[adapter];
136
+ wallet.connect()
137
+ .then(() => {
138
+ resolve(wallet);
139
+ })
140
+ .catch(error => {
141
+
142
+ });
143
+ } else {
144
+ reject('wallet-not-found');
59
145
  }
60
- } else {
61
-
62
- let tx = data[0];
146
+ });
147
+ }
63
148
 
64
- let index = tx.vout.findIndex(object => {
65
- return object.scriptpubkey_address == receiver;
66
- });
149
+ /**
150
+ * @param {Array} filter
151
+ * @returns {Array}
152
+ */
153
+ getDetectedWallets(filter) {
154
+ if (!filter) return this.detectedWallets;
155
+ return Object.fromEntries(Object.entries(this.detectedWallets).filter(([key]) => {
156
+ return filter.includes(key);
157
+ }));
158
+ }
67
159
 
68
- data = tx.vout[index];
160
+ detectWallets() {
161
+ if (typeof window != 'undefined') {
162
+ const Wallet = require('./wallet');
69
163
 
70
- return {
71
- hash: tx.txid,
72
- amount: utils.toDec(data.value, 8)
73
- };
164
+ if (typeof window.unisat !== 'undefined') {
165
+ this.detectedWallets['unisat'] = new Wallet('unisat', this);
166
+ }
74
167
  }
75
168
  }
76
169
 
170
+ Coin() {
171
+ return new Coin(this);
172
+ }
173
+
174
+ Token(address) {
175
+ return new Token(address, this);
176
+ }
177
+
77
178
  Transaction(hash) {
78
179
  return new Transaction(hash, this);
79
180
  }
package/src/utils.js ADDED
@@ -0,0 +1,21 @@
1
+ const BigNumber = require('bignumber.js');
2
+ const utils = require('@multiplechain/utils');
3
+
4
+ module.exports = Object.assign(utils, {
5
+ toSatoshi(amount) {
6
+ let decimals = 8;
7
+ let length = '1' + '0'.repeat(decimals);
8
+ let value = new BigNumber(amount.toString(10), 10).times(length);
9
+ return parseInt(value.toString(10));
10
+ },
11
+ rejectMessage(error, reject) {
12
+
13
+ if (typeof error == 'object') {
14
+ if (error.code == 4001 || error.message == 'User rejected the request.') {
15
+ return reject('request-rejected');
16
+ }
17
+ }
18
+
19
+ return reject(error);
20
+ }
21
+ })
package/src/wallet.js ADDED
@@ -0,0 +1,185 @@
1
+ const getAdapter = require('./get-adapter');
2
+ const utils = require('./utils');
3
+
4
+ class Wallet {
5
+
6
+ /**
7
+ * @var {Object}
8
+ */
9
+ adapter;
10
+
11
+ /**
12
+ * @var {Object}
13
+ */
14
+ wallet;
15
+
16
+ /**
17
+ * @var {Object}
18
+ */
19
+ provider;
20
+
21
+ /**
22
+ * @var {String}
23
+ */
24
+ connectedAccount;
25
+
26
+ /**
27
+ * @param {String} adapter
28
+ * @param {Object} provider
29
+ */
30
+ constructor(adapter, provider) {
31
+ this.provider = provider;
32
+ this.setAdapter(adapter);
33
+ }
34
+
35
+ /**
36
+ * @param {String} adapter
37
+ */
38
+ setAdapter(adapter) {
39
+ this.adapter = getAdapter(adapter, this.provider);
40
+ this.wallet = this.adapter.wallet;
41
+ }
42
+
43
+ /**
44
+ * @returns {String}
45
+ */
46
+ getKey() {
47
+ return this.adapter.key;
48
+ }
49
+
50
+ /**
51
+ * @returns {String}
52
+ */
53
+ getName() {
54
+ return this.adapter.name;
55
+ }
56
+
57
+ /**
58
+ * @returns {String}
59
+ */
60
+ getType() {
61
+ return this.adapter.type;
62
+ }
63
+
64
+ /**
65
+ * @returns {String}
66
+ */
67
+ getDeepLink() {
68
+ return this.adapter.deepLink;
69
+ }
70
+
71
+ /**
72
+ * @returns {String}
73
+ */
74
+ getDownloadLink() {
75
+ return this.adapter.download;
76
+ }
77
+
78
+ /**
79
+ * @returns {String}
80
+ */
81
+ connect() {
82
+ return new Promise((resolve, reject) => {
83
+ let time = 0;
84
+ let timeout = 15;
85
+ let timer = setInterval(async () => {
86
+ time += 1;
87
+ if (time > timeout) {
88
+ clearInterval(timer);
89
+ reject('timeout');
90
+ }
91
+ }, 1000);
92
+
93
+ this.adapter.connect()
94
+ .then(async (connectedAccount) => {
95
+ let network = await this.wallet.getNetwork();
96
+ if (this.provider.network == network) {
97
+ this.provider.setConnectedWallet(this);
98
+ resolve(this.connectedAccount = connectedAccount);
99
+ } else {
100
+ reject('not-accepted-chain');
101
+ }
102
+ })
103
+ .catch((error) => {
104
+ utils.rejectMessage(error, reject);
105
+ })
106
+ .finally(() => {
107
+ clearInterval(timer);
108
+ });
109
+ });
110
+ }
111
+
112
+ /**
113
+ * @returns {Array}
114
+ */
115
+ getAccounts() {
116
+ return this.wallet.getAccounts();
117
+ }
118
+
119
+ /**
120
+ * @returns {String}
121
+ */
122
+ getPublicKey() {
123
+ return this.wallet.getPublicKey();
124
+ }
125
+
126
+
127
+ /**
128
+ * @param {String} to
129
+ * @param {Integer} amount
130
+ * @return {Transaction|Object}
131
+ * @throws {Error}
132
+ */
133
+ coinTransfer(to, amount) {
134
+ return new Promise(async (resolve, reject) => {
135
+ try {
136
+ let coin = this.provider.Coin();
137
+
138
+ coin.transfer(this.connectedAccount, to, amount)
139
+ .then((transactionId) => {
140
+ resolve(this.provider.Transaction(transactionId));
141
+ })
142
+ .catch((error) => {
143
+ utils.rejectMessage(error, reject);
144
+ });
145
+ } catch (error) {
146
+ utils.rejectMessage(error, reject);
147
+ }
148
+ });
149
+ }
150
+
151
+ // TODO: implement
152
+ tokenTransfer(to, amount, tokenAddress) {
153
+ }
154
+
155
+ /**
156
+ * @param {String} to
157
+ * @param {Integer} amount
158
+ * @param {String|null} tokenAddress
159
+ * @return {Transaction|Object}
160
+ * @throws {Error}
161
+ */
162
+ transfer(to, amount, tokenAddress = null) {
163
+ if (tokenAddress) {
164
+ return this.tokenTransfer(to, amount, tokenAddress);
165
+ } else {
166
+ return this.coinTransfer(to, amount);
167
+ }
168
+ }
169
+
170
+ // Events
171
+ accountsChanged(callback) {
172
+ this.wallet.on('accountsChanged', (accounts) => {
173
+ callback(accounts);
174
+ });
175
+ }
176
+
177
+ networkChanged(callback) {
178
+ this.wallet.on('networkChanged', (network) => {
179
+ callback(network);
180
+ });
181
+ }
182
+
183
+ }
184
+
185
+ module.exports = Wallet;