suidouble 1.14.2-1 → 1.14.3-1

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,94 @@ 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';
99
+ }
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', separateThousands);
107
+ if (asBig >= 1000000n && asBig < 1000000000n) return this.formatWithAbbr(asBig, 1000000n, 'M', separateThousands);
108
+ if (asBig >= 1000000000n && asBig < 1000000000000n) return this.formatWithAbbr(asBig, 1000000000n, 'B', separateThousands);
109
+ if (asBig >= 1000000000000n) return this.formatWithAbbr(asBig, 1000000000000n, 'T', separateThousands);
91
110
  }
92
- i--;
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, separateThousands = false) {
137
+ let showAmount = (''+Math.floor(Number(amount) / Number(measureUnit / 1000n)));
138
+ showAmount = showAmount.padEnd(4, '0');
139
+
140
+ const result = Intl.NumberFormat('en-US').format(Number(showAmount));
141
+
142
+ let separator = '';
143
+ if (separateThousands) {
144
+ separator = (typeof separateThousands === 'string') ? separateThousands : ',';
145
+ }
146
+
147
+ const pcs = result.split(',');
148
+ const lastPc = pcs.pop();
149
+ return pcs.join( separator ) + '.' + lastPc + abbrSymbol;
150
+ }
151
+
152
+
98
153
  get suiMaster() {
99
154
  return this._suiCoins.suiMaster;
100
155
  }
@@ -150,6 +205,7 @@ export default class SuiCoin {
150
205
  /**
151
206
  * set predefined coin metadata so it will not be fetched from RPC
152
207
  * @param {CoinMeta} meta
208
+ *
153
209
  * @returns {boolean} if processed ok
154
210
  */
155
211
  setMetadata(meta) {
@@ -162,11 +218,25 @@ export default class SuiCoin {
162
218
  return false;
163
219
  }
164
220
 
221
+ /**
222
+ * Load coin metadata from the blockchain. As a good pattern, it's better to have metadata hard-coded or cached
223
+ * and set via suiMaster.suiCoins.setCoinMetas(...)
224
+ *
225
+ * @returns {Promise.<boolean>}
226
+ */
165
227
  async getMetadata() {
166
228
  if (this._metadata) {
167
229
  return this._metadata;
168
230
  }
169
231
 
232
+ if (this.__getMetadataPromise) {
233
+ return await this.__getMetadataPromise();
234
+ }
235
+
236
+ // be sure it asyncs in 1 thread
237
+ this.__getMetadataResolver = null;
238
+ this.__getMetadataPromise = new Promise((res)=>{ this.__getMetadataResolver = res; });
239
+
170
240
  let result = null;
171
241
  try {
172
242
  result = await this.suiMaster.client.getCoinMetadata({
@@ -183,9 +253,17 @@ export default class SuiCoin {
183
253
  this._exists = true;
184
254
  }
185
255
 
186
- return this._metadata;
256
+ this.__getMetadataResolver(true);
257
+
258
+ return true;
187
259
  }
188
260
 
261
+ /**
262
+ * Get coin balance of the wallet
263
+ * @param {string} owner
264
+ *
265
+ * @returns {Promise.<bigint>}
266
+ */
189
267
  async getBalance(owner) {
190
268
  const coins = [];
191
269
  let result = null;
@@ -218,7 +296,8 @@ export default class SuiCoin {
218
296
  * @param {string} owner - address of the owner
219
297
  * @param {BigInt|string} amount - amount of coin. BigIng or String to be normalized via Coin decimals, "0.05" for 0.05 sui
220
298
  * @param {boolean} addEmptyCoins - attach coins == 0 to the list
221
- * @returns {TransactionObjectArgument}
299
+ *
300
+ * @returns {Promise.<TransactionObjectArgument>}
222
301
  */
223
302
  async coinOfAmountToTxCoin(txb, owner, amount, addEmptyCoins = false) {
224
303
  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-1",
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,111 @@ 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 toDisplayM2spec = suiCoin.amountToString(148518424765531477n, {withAbbr: true}); // 1000 SUI + 1 mist
112
+ t.equal(toDisplayM2spec, '148.518M');
113
+
114
+
115
+ const toDisplayB1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000000000) + BigInt(1), {withAbbr: true}); // 1000 SUI + 1 mist
116
+ t.equal(toDisplayB1, '1.000B');
117
+
118
+ const toDisplayB2spec = suiCoin.amountToString(148518424765531477000n, {withAbbr: true}); // 1000 SUI + 1 mist
119
+ t.equal(toDisplayB2spec, '148.518B');
120
+
121
+ const toDisplayT1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000000000000) + BigInt(1), {withAbbr: true}); // 1000 SUI + 1 mist
122
+ t.equal(toDisplayT1, '1.000T');
123
+
124
+ const toDisplayT2spec = suiCoin.amountToString(148518424765531477000000n, {withAbbr: true}); // 1000 SUI + 1 mist
125
+ t.equal(toDisplayT2spec, '148.518T');
126
+
127
+ const toDisplayT20spec = suiCoin.amountToString(148518424765531477000000000n, {withAbbr: true}); // 1000 SUI + 1 mist
128
+ t.equal(toDisplayT20spec, '148518.424T');
129
+
130
+ const toDisplayT20spec2 = suiCoin.amountToString(148518424765531477000000000n, {withAbbr: true, separateThousands: true}); // 1000 SUI + 1 mist
131
+ t.equal(toDisplayT20spec2, '148,518.424T');
132
+ });
133
+
134
+
135
+ test('string representation (separateThousands) works ok', async t => {
136
+ const suiCoin = suiMaster.suiCoins.get('sui');
137
+ await suiCoin.getMetadata();
138
+
139
+ // it should dispaly the same on the low amounts:
140
+
141
+ const toDisplay1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI, {separateThousands: true});
142
+ t.equal(toDisplay1, '1.0');
143
+
144
+ const toDisplay2 = suiCoin.amountToString(1, {separateThousands: true}); // 1 mist
145
+ t.equal(toDisplay2, '0.000000001');
146
+
147
+ const toDisplay1000 = suiCoin.amountToString(1000, {separateThousands: true}); // 1000 mist
148
+ t.equal(toDisplay1000, '0.000001');
149
+
150
+ const toDisplay3 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000) + BigInt(1), {separateThousands: true}); // 1000 SUI + 1 mist
151
+ t.equal(toDisplay3, '1,000.000000001');
152
+
153
+ const toDisplay4 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000) - BigInt(1), {separateThousands: true}); // 1000 SUI - 1 mist
154
+ t.equal(toDisplay4, '999.999999999');
155
+
156
+ // things are getting interesting starting from '1001.0'
157
+
158
+ const toDisplayK1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1001) + BigInt(1), {separateThousands: true});
159
+ t.equal(toDisplayK1, '1,001.000000001');
160
+ const toDisplayK2 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(2000), {separateThousands: true});
161
+ t.equal(toDisplayK2, '2,000.0');
162
+ const toDisplayK3 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(999900), {separateThousands: true});
163
+ t.equal(toDisplayK3, '999,900.0');
164
+
165
+ const toDisplayM1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000000) + BigInt(1), {separateThousands: true});
166
+ t.equal(toDisplayM1, '1,000,000.000000001');
167
+ const toDisplayM2 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(2000000) + BigInt(900), {separateThousands: true});
168
+ t.equal(toDisplayM2, '2,000,000.0000009');
169
+
170
+ const toDisplayB1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000000000) + BigInt(1), {separateThousands: true});
171
+ t.equal(toDisplayB1, '1,000,000,000.000000001');
172
+
173
+ const toDisplayT1 = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000000000000) + BigInt(1), {separateThousands: true});
174
+ t.equal(toDisplayT1, '1,000,000,000,000.000000001');
175
+
176
+ const toDisplayT1q = suiCoin.amountToString(suiMaster.MIST_PER_SUI * BigInt(1000000000000) + BigInt(1), {separateThousands: ' '});
177
+ t.equal(toDisplayT1q, '1 000 000 000 000.000000001');
178
+ });
179
+
79
180
  test('you have no SUI on the fresh node', async t => {
80
181
  const suiCoin = suiMaster.suiCoins.get('sui');
81
182
  const balance = await suiCoin.getBalance(suiMaster.address);