suidouble 1.14.2-1 → 1.14.3

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/lib/SuiCoin.js CHANGED
@@ -25,7 +25,6 @@ import { Commands, Transaction } from '@mysten/sui/transactions';
25
25
  *
26
26
  */
27
27
 
28
-
29
28
  /** Coin metadata object */
30
29
  export default class SuiCoin {
31
30
 
@@ -63,38 +62,86 @@ export default class SuiCoin {
63
62
 
64
63
  /**
65
64
  * Normalize amount based on .decimals. Pass a string with a dot ('5.22', '0.0005') to convert it to units. No worries about loading metadata first.
66
- * @param {String|Number|BigInt} amount
67
- * @returns {BigInt}
65
+ * @param {String|Number|bigint} amount
66
+ * @returns {Promise.<bigint>}
68
67
  */
69
68
  async lazyNormalizeAmount(amount) {
70
69
  await this.getMetadata();
71
70
  return this.normalizeAmount(amount);
72
71
  }
73
72
 
74
- amountToString(amount) {
73
+ /**
74
+ * Get readable representation of amount value (system one, bigint or converted from bigint, NOT the '1.99' etc)
75
+ * based on coin decimals metadata ( so it expects metadata to be loaded or set).
76
+ *
77
+ * @param {Object} params - format parameters
78
+ * @param {boolean} params.withAbbr - append K, M, B, T for large amounts. Suiet-style
79
+ * @param {boolean|string} params.separateThousands - separate thousands, by ',' or by specific delimeter
80
+ *
81
+ * @returns {string}
82
+ */
83
+ amountToString(amount, options = {}) {
75
84
  if (!this.decimals) {
76
85
  throw new Error('Please load coin metadata first');
77
86
  }
78
87
 
88
+ const withAbbr = !!options.withAbbr;
89
+ const separateThousands = options.separateThousands;
90
+
79
91
  const str = (''+BigInt(amount)).padStart(this.decimals + 1,'0');
80
92
  const ind = str.length - this.decimals;
81
93
  let asFloatString = str.substring(0, ind) + '.' + str.substring(ind);
82
94
 
83
- /// yep, I can't find a better way to strip extra 0 at the end. All regexes are not ok. Ping me if you have a good one
84
- let i = asFloatString.length - 1;
85
- let haveNotMetNoZero = false;
86
- while (i > 0 && !haveNotMetNoZero) {
87
- if (asFloatString.substring(i, i+1) === '0' && asFloatString.substring(i-1, i) !== '.') {
88
- asFloatString = asFloatString.substring(0, i);
89
- } else {
90
- haveNotMetNoZero = true;
95
+ if (asFloatString.includes('.')) {
96
+ asFloatString = asFloatString.replace(/(\.\d*?)0+$/, '$1').replace(/\.$/, '');
97
+ if (!asFloatString.includes('.')) {
98
+ asFloatString = '' + asFloatString + '.0';
91
99
  }
92
- i--;
100
+ }
101
+
102
+ if (withAbbr) {
103
+ const asBig = BigInt(Math.floor(Number(asFloatString)));
104
+
105
+ if (asBig > 1000n) {
106
+ if (asBig < 1000000n) return this.formatWithAbbr(asBig, 1000n, 'K');
107
+ if (asBig >= 1000000n && asBig < 1000000000n) return this.formatWithAbbr(asBig, 1000000n, 'M');
108
+ if (asBig >= 1000000000n && asBig < 1000000000000n) return this.formatWithAbbr(asBig, 1000000000n, 'B');
109
+ if (asBig >= 1000000000000n) return this.formatWithAbbr(asBig, 1000000000000n, 'T');
110
+ }
111
+ }
112
+
113
+ if (separateThousands) {
114
+ // asFloatString has '.' anyway ( see above )
115
+ const [integerPart, decimalPart] = asFloatString.split('.'); // Split into integer and decimal parts
116
+ const separator = (typeof separateThousands === 'string') ? separateThousands : ',';
117
+ const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
118
+ asFloatString = '' + formattedInteger + '.' + decimalPart;
93
119
  }
94
120
 
95
121
  return asFloatString;
96
122
  }
97
123
 
124
+
125
+ /**
126
+ * Format large amounts
127
+ *
128
+ * thanks @bruceeewong and @suiet
129
+ *
130
+ * @param {bigint} amount
131
+ * @param {bigint} measureUnit
132
+ * @param {string} abbrSymbol
133
+ *
134
+ * @returns {string}
135
+ */
136
+ formatWithAbbr(amount, measureUnit, abbrSymbol) {
137
+ let showAmount = (''+(Number(amount) / Number(measureUnit / 1000n)));
138
+ showAmount = showAmount.padEnd(4, '0');
139
+
140
+ const result = Intl.NumberFormat('en-US').format(Number(showAmount));
141
+ return result.replace(',', '.') + abbrSymbol;
142
+ }
143
+
144
+
98
145
  get suiMaster() {
99
146
  return this._suiCoins.suiMaster;
100
147
  }
@@ -150,6 +197,7 @@ export default class SuiCoin {
150
197
  /**
151
198
  * set predefined coin metadata so it will not be fetched from RPC
152
199
  * @param {CoinMeta} meta
200
+ *
153
201
  * @returns {boolean} if processed ok
154
202
  */
155
203
  setMetadata(meta) {
@@ -162,11 +210,25 @@ export default class SuiCoin {
162
210
  return false;
163
211
  }
164
212
 
213
+ /**
214
+ * Load coin metadata from the blockchain. As a good pattern, it's better to have metadata hard-coded or cached
215
+ * and set via suiMaster.suiCoins.setCoinMetas(...)
216
+ *
217
+ * @returns {Promise.<boolean>}
218
+ */
165
219
  async getMetadata() {
166
220
  if (this._metadata) {
167
221
  return this._metadata;
168
222
  }
169
223
 
224
+ if (this.__getMetadataPromise) {
225
+ return await this.__getMetadataPromise();
226
+ }
227
+
228
+ // be sure it asyncs in 1 thread
229
+ this.__getMetadataResolver = null;
230
+ this.__getMetadataPromise = new Promise((res)=>{ this.__getMetadataResolver = res; });
231
+
170
232
  let result = null;
171
233
  try {
172
234
  result = await this.suiMaster.client.getCoinMetadata({
@@ -183,9 +245,17 @@ export default class SuiCoin {
183
245
  this._exists = true;
184
246
  }
185
247
 
186
- return this._metadata;
248
+ this.__getMetadataResolver(true);
249
+
250
+ return true;
187
251
  }
188
252
 
253
+ /**
254
+ * Get coin balance of the wallet
255
+ * @param {string} owner
256
+ *
257
+ * @returns {Promise.<bigint>}
258
+ */
189
259
  async getBalance(owner) {
190
260
  const coins = [];
191
261
  let result = null;
@@ -218,7 +288,8 @@ export default class SuiCoin {
218
288
  * @param {string} owner - address of the owner
219
289
  * @param {BigInt|string} amount - amount of coin. BigIng or String to be normalized via Coin decimals, "0.05" for 0.05 sui
220
290
  * @param {boolean} addEmptyCoins - attach coins == 0 to the list
221
- * @returns {TransactionObjectArgument}
291
+ *
292
+ * @returns {Promise.<TransactionObjectArgument>}
222
293
  */
223
294
  async coinOfAmountToTxCoin(txb, owner, amount, addEmptyCoins = false) {
224
295
  const normalizedAmount = await this.lazyNormalizeAmount(amount);
package/lib/SuiPackage.js CHANGED
@@ -501,7 +501,7 @@ export default class SuiPackage extends SuiObject {
501
501
  * @returns Boolean true on success
502
502
  */
503
503
  async build(params = {}) {
504
- this.log('builing a package...');
504
+ this.log('building a package...');
505
505
 
506
506
  const path = this._path;
507
507
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "suidouble",
3
- "version": "1.14.2-1",
3
+ "version": "1.14.3",
4
4
  "description": "Set of provider, package and object classes for javascript representation of Sui Move smart contracts. Use same code for publishing, upgrading, integration testing, interaction with smart contracts and integration in browser web3 dapps",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -22,8 +22,8 @@
22
22
  "author": "suidouble (https://github.com/suidouble)",
23
23
  "license": "Apache-2.0",
24
24
  "dependencies": {
25
- "@mysten/sui": "^1.14.2",
26
- "@polymedia/coinmeta": "^0.0.16",
25
+ "@mysten/sui": "^1.14.3",
26
+ "@polymedia/coinmeta": "^0.0.17",
27
27
  "@wallet-standard/core": "^1.0.3",
28
28
  "websocket": "^1.0.35"
29
29
  },
@@ -35,13 +35,6 @@ test('type is normalized for SUI', async t => {
35
35
  const suiCoin5 = suiMaster.suiCoins.get('2::sui::SUI');
36
36
  const suiCoin6 = suiMaster.suiCoins.get('0000000000000000000000000000000000000000000000000000000000000002::sui::SUI');
37
37
 
38
- console.log(suiCoin1.coinType);
39
- console.log(suiCoin2.coinType);
40
- console.log(suiCoin3.coinType);
41
- console.log(suiCoin4.coinType);
42
- console.log(suiCoin5.coinType);
43
- console.log(suiCoin6.coinType);
44
-
45
38
  t.ok(suiCoin1.coinType == suiCoin2.coinType);
46
39
  t.ok(suiCoin1.coinType == suiCoin3.coinType);
47
40
  t.ok(suiCoin1.coinType == suiCoin4.coinType);
@@ -69,6 +62,9 @@ test('string representation works ok', async t => {
69
62
  const toDisplay2 = suiCoin.amountToString(1); // 1 mist
70
63
  t.equal(toDisplay2, '0.000000001');
71
64
 
65
+ const toDisplay1000 = suiCoin.amountToString(1000); // 1000 mist
66
+ t.equal(toDisplay1000, '0.000001');
67
+
72
68
  const toDisplay3 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000) + BigInt(1)); // 1000 SUI + 1 mist
73
69
  t.equal(toDisplay3, '1000.000000001');
74
70
 
@@ -76,6 +72,95 @@ test('string representation works ok', async t => {
76
72
  t.equal(toDisplay4, '999.999999999');
77
73
  });
78
74
 
75
+
76
+ test('string representation (withAbbr) works ok', async t => {
77
+ const suiCoin = suiMaster.suiCoins.get('sui');
78
+ await suiCoin.getMetadata();
79
+
80
+ // it should dispaly the same on the low amounts:
81
+
82
+ const toDisplay1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI, {withAbbr: true});
83
+ t.equal(toDisplay1, '1.0');
84
+
85
+ const toDisplay2 = suiCoin.amountToString(1, {withAbbr: true}); // 1 mist
86
+ t.equal(toDisplay2, '0.000000001');
87
+
88
+ const toDisplay1000 = suiCoin.amountToString(1000, {withAbbr: true}); // 1000 mist
89
+ t.equal(toDisplay1000, '0.000001');
90
+
91
+ const toDisplay3 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000) + BigInt(1), {withAbbr: true}); // 1000 SUI + 1 mist
92
+ t.equal(toDisplay3, '1000.000000001');
93
+
94
+ const toDisplay4 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000) - BigInt(1), {withAbbr: true}); // 1000 SUI - 1 mist
95
+ t.equal(toDisplay4, '999.999999999');
96
+
97
+ // things are getting interesting starting from '1001.0'
98
+
99
+ const toDisplayK1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1001) + BigInt(1), {withAbbr: true}); // 1000 SUI + 1 mist
100
+ t.equal(toDisplayK1, '1.001K');
101
+ const toDisplayK2 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(2000), {withAbbr: true});
102
+ t.equal(toDisplayK2, '2.000K');
103
+ const toDisplayK3 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(999900), {withAbbr: true});
104
+ t.equal(toDisplayK3, '999.900K');
105
+
106
+ const toDisplayM1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000000) + BigInt(1), {withAbbr: true}); // 1000 SUI + 1 mist
107
+ t.equal(toDisplayM1, '1.000M');
108
+ const toDisplayM2 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(2000000) + BigInt(900), {withAbbr: true}); // 1000 SUI + 1 mist
109
+ t.equal(toDisplayM2, '2.000M');
110
+
111
+ const toDisplayB1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000000000) + BigInt(1), {withAbbr: true}); // 1000 SUI + 1 mist
112
+ t.equal(toDisplayB1, '1.000B');
113
+
114
+ const toDisplayT1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000000000000) + BigInt(1), {withAbbr: true}); // 1000 SUI + 1 mist
115
+ t.equal(toDisplayT1, '1.000T');
116
+ });
117
+
118
+
119
+ test('string representation (separateThousands) works ok', async t => {
120
+ const suiCoin = suiMaster.suiCoins.get('sui');
121
+ await suiCoin.getMetadata();
122
+
123
+ // it should dispaly the same on the low amounts:
124
+
125
+ const toDisplay1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI, {separateThousands: true});
126
+ t.equal(toDisplay1, '1.0');
127
+
128
+ const toDisplay2 = suiCoin.amountToString(1, {separateThousands: true}); // 1 mist
129
+ t.equal(toDisplay2, '0.000000001');
130
+
131
+ const toDisplay1000 = suiCoin.amountToString(1000, {separateThousands: true}); // 1000 mist
132
+ t.equal(toDisplay1000, '0.000001');
133
+
134
+ const toDisplay3 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000) + BigInt(1), {separateThousands: true}); // 1000 SUI + 1 mist
135
+ t.equal(toDisplay3, '1,000.000000001');
136
+
137
+ const toDisplay4 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000) - BigInt(1), {separateThousands: true}); // 1000 SUI - 1 mist
138
+ t.equal(toDisplay4, '999.999999999');
139
+
140
+ // things are getting interesting starting from '1001.0'
141
+
142
+ const toDisplayK1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1001) + BigInt(1), {separateThousands: true});
143
+ t.equal(toDisplayK1, '1,001.000000001');
144
+ const toDisplayK2 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(2000), {separateThousands: true});
145
+ t.equal(toDisplayK2, '2,000.0');
146
+ const toDisplayK3 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(999900), {separateThousands: true});
147
+ t.equal(toDisplayK3, '999,900.0');
148
+
149
+ const toDisplayM1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000000) + BigInt(1), {separateThousands: true});
150
+ t.equal(toDisplayM1, '1,000,000.000000001');
151
+ const toDisplayM2 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(2000000) + BigInt(900), {separateThousands: true});
152
+ t.equal(toDisplayM2, '2,000,000.0000009');
153
+
154
+ const toDisplayB1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000000000) + BigInt(1), {separateThousands: true});
155
+ t.equal(toDisplayB1, '1,000,000,000.000000001');
156
+
157
+ const toDisplayT1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000000000000) + BigInt(1), {separateThousands: true});
158
+ t.equal(toDisplayT1, '1,000,000,000,000.000000001');
159
+
160
+ const toDisplayT1q = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000000000000) + BigInt(1), {separateThousands: ' '});
161
+ t.equal(toDisplayT1q, '1 000 000 000 000.000000001');
162
+ });
163
+
79
164
  test('you have no SUI on the fresh node', async t => {
80
165
  const suiCoin = suiMaster.suiCoins.get('sui');
81
166
  const balance = await suiCoin.getBalance(suiMaster.address);