@multiplechain/bitcoin 0.1.0 → 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.0",
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
 
@@ -166,42 +139,25 @@ class Transaction {
166
139
  }
167
140
 
168
141
  /**
169
- * @param {String} receiver
170
- * @param {Number} amount
142
+ * @param {Object} config
171
143
  * @returns {Boolean}
172
144
  */
173
- async verifyTransferWithData(receiver, amount) {
145
+ async verifyTransferWithData(config) {
174
146
 
175
147
  if (await this.validateTransaction()) {
176
148
 
177
- let data;
178
- if (this.provider.testnet) {
179
-
180
- let index = this.data.vout.findIndex(object => {
181
- return object.scriptpubkey_address == receiver;
182
- });
183
-
184
- data = this.data.vout[index];
185
-
186
- data = {
187
- receiver: data.scriptpubkey_address,
188
- amount: utils.toDec(data.value, 8)
189
- };
190
- } else {
191
-
192
- let index = this.data.out.findIndex(object => {
193
- return object.addr == receiver;
194
- });
149
+ let index = this.data.vout.findIndex(object => {
150
+ return object.scriptpubkey_address == config.receiver;
151
+ });
195
152
 
196
- data = this.data.out[index];
197
-
198
- data = {
199
- receiver: data.addr,
200
- amount: utils.toDec(data.value, 8)
201
- };
153
+ let data = this.data.vout[index];
154
+
155
+ data = {
156
+ receiver: data.scriptpubkey_address,
157
+ amount: utils.toDec(data.value, 8)
202
158
  }
203
159
 
204
- if (data.receiver == receiver && data.amount == amount) {
160
+ if (data.receiver == config.receiver && data.amount == config.amount) {
205
161
  return true;
206
162
  } else {
207
163
  return false;
@@ -215,11 +171,7 @@ class Transaction {
215
171
  * @returns {String}
216
172
  */
217
173
  getUrl() {
218
- if (this.provider.testnet) {
219
- return this.provider.explorer + 'tx/' + this.hash;
220
- } else {
221
- return this.provider.explorer + 'transactions/btc/' + this.hash;
222
- }
174
+ return this.provider.explorer + 'tx/' + this.hash;
223
175
  }
224
176
  }
225
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,72 +1,180 @@
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) {
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
+ }
78
+ }
79
+
80
+ return data;
81
+ }
82
+
83
+ /**
84
+ * @param {String} receiver
85
+ * @returns
86
+ */
87
+ async getLastTransactionByReceiver(receiver) {
29
88
 
30
- let apiUrl;
31
- if (this.testnet) {
32
- apiUrl = this.api + 'address/' + receiver + '/txs';
33
- } else {
34
- apiUrl = this.api + 'rawaddr/' + receiver;
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;
35
98
  }
36
99
 
37
- let data = await fetch(apiUrl).then(res => res.json());
100
+ if (data.length == 0) {
101
+ return {
102
+ hash: null,
103
+ amount: 0
104
+ }
105
+ }
106
+
107
+ let tx = data[0];
38
108
 
39
- if (data.txs) {
109
+ let index = tx.vout.findIndex(object => {
110
+ return object.scriptpubkey_address == receiver;
111
+ });
40
112
 
41
- let tx = data.txs[0];
113
+ data = tx.vout[index];
42
114
 
43
- let index = tx.out.findIndex(object => {
44
- return object.addr == receiver;
45
- });
115
+ return {
116
+ hash: tx.txid,
117
+ amount: utils.toDec(data.value, 8)
118
+ };
119
+ }
46
120
 
47
- data = tx.out[index];
48
-
49
- return {
50
- hash: tx.hash,
51
- 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');
52
145
  }
53
- } else {
54
-
55
- let tx = data[0];
146
+ });
147
+ }
56
148
 
57
- let index = tx.vout.findIndex(object => {
58
- return object.scriptpubkey_address == receiver;
59
- });
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
+ }
60
159
 
61
- data = tx.vout[index];
160
+ detectWallets() {
161
+ if (typeof window != 'undefined') {
162
+ const Wallet = require('./wallet');
62
163
 
63
- return {
64
- hash: tx.txid,
65
- amount: utils.toDec(data.value, 8)
66
- };
164
+ if (typeof window.unisat !== 'undefined') {
165
+ this.detectedWallets['unisat'] = new Wallet('unisat', this);
166
+ }
67
167
  }
68
168
  }
69
169
 
170
+ Coin() {
171
+ return new Coin(this);
172
+ }
173
+
174
+ Token(address) {
175
+ return new Token(address, this);
176
+ }
177
+
70
178
  Transaction(hash) {
71
179
  return new Transaction(hash, this);
72
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;