@alephium/web3 0.30.2 → 0.31.0

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.
@@ -20,14 +20,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
20
20
  return (mod && mod.__esModule) ? mod : { "default": mod };
21
21
  };
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
23
- exports.assertType = exports.isDevnet = exports.sleep = exports.hexToString = exports.stringToHex = exports.blockChainIndex = exports.subContractId = exports.contractIdFromTx = exports.addressFromTokenId = exports.addressFromContractId = exports.addressFromScript = exports.addressFromPublicKey = exports.publicKeyFromPrivateKey = exports.groupOfPrivateKey = exports.binToHex = exports.hexToBinUnsafe = exports.tokenIdFromAddress = exports.contractIdFromAddress = exports.groupOfAddress = exports.AddressType = exports.toNonNegativeBigInt = exports.isHexString = exports.xorByte = exports.signatureDecode = exports.encodeHexSignature = exports.encodeSignature = exports.networkIds = void 0;
23
+ exports.assertType = exports.isDevnet = exports.sleep = exports.hexToString = exports.stringToHex = exports.blockChainIndex = exports.binToHex = exports.hexToBinUnsafe = exports.toNonNegativeBigInt = exports.isHexString = exports.signatureDecode = exports.encodeHexSignature = exports.encodeSignature = exports.networkIds = void 0;
24
24
  const elliptic_1 = require("elliptic");
25
25
  const bn_js_1 = __importDefault(require("bn.js"));
26
- const blakejs_1 = __importDefault(require("blakejs"));
27
- const bs58_1 = __importDefault(require("./bs58"));
28
26
  const buffer_1 = require("buffer/");
29
27
  const constants_1 = require("../constants");
30
- const djb2_1 = __importDefault(require("./djb2"));
31
28
  exports.networkIds = ['mainnet', 'testnet', 'devnet'];
32
29
  const ec = new elliptic_1.ec('secp256k1');
33
30
  function encodeSignature(signature) {
@@ -60,14 +57,6 @@ function signatureDecode(ec, signature) {
60
57
  }
61
58
  }
62
59
  exports.signatureDecode = signatureDecode;
63
- function xorByte(intValue) {
64
- const byte0 = (intValue >> 24) & 0xff;
65
- const byte1 = (intValue >> 16) & 0xff;
66
- const byte2 = (intValue >> 8) & 0xff;
67
- const byte3 = intValue & 0xff;
68
- return (byte0 ^ byte1 ^ byte2 ^ byte3) & 0xff;
69
- }
70
- exports.xorByte = xorByte;
71
60
  function isHexString(input) {
72
61
  return input.length % 2 === 0 && /^[0-9a-fA-F]*$/.test(input);
73
62
  }
@@ -82,80 +71,6 @@ function toNonNegativeBigInt(input) {
82
71
  }
83
72
  }
84
73
  exports.toNonNegativeBigInt = toNonNegativeBigInt;
85
- var AddressType;
86
- (function (AddressType) {
87
- AddressType[AddressType["P2PKH"] = 0] = "P2PKH";
88
- AddressType[AddressType["P2MPKH"] = 1] = "P2MPKH";
89
- AddressType[AddressType["P2SH"] = 2] = "P2SH";
90
- AddressType[AddressType["P2C"] = 3] = "P2C";
91
- })(AddressType = exports.AddressType || (exports.AddressType = {}));
92
- function groupOfAddress(address) {
93
- const decoded = bs58_1.default.decode(address);
94
- if (decoded.length == 0)
95
- throw new Error('Address string is empty');
96
- const addressType = decoded[0];
97
- const addressBody = decoded.slice(1);
98
- if (addressType == AddressType.P2PKH) {
99
- return groupOfP2pkhAddress(addressBody);
100
- }
101
- else if (addressType == AddressType.P2MPKH) {
102
- return groupOfP2mpkhAddress(addressBody);
103
- }
104
- else if (addressType == AddressType.P2SH) {
105
- return groupOfP2shAddress(addressBody);
106
- }
107
- else {
108
- // Contract Address
109
- const id = contractIdFromAddress(address);
110
- return id[`${id.length - 1}`];
111
- }
112
- }
113
- exports.groupOfAddress = groupOfAddress;
114
- function groupOfAddressBytes(bytes) {
115
- const hint = (0, djb2_1.default)(bytes) | 1;
116
- const hash = xorByte(hint);
117
- const group = hash % constants_1.TOTAL_NUMBER_OF_GROUPS;
118
- return group;
119
- }
120
- // Pay to public key hash address
121
- function groupOfP2pkhAddress(address) {
122
- if (address.length != 32) {
123
- throw new Error(`Invalid p2pkh address length: ${address.length}`);
124
- }
125
- return groupOfAddressBytes(address);
126
- }
127
- // Pay to multiple public key hash address
128
- function groupOfP2mpkhAddress(address) {
129
- if ((address.length - 2) % 32 != 0) {
130
- throw new Error(`Invalid p2mpkh address length: ${address.length}`);
131
- }
132
- return groupOfAddressBytes(address.slice(1, 33));
133
- }
134
- // Pay to script hash address
135
- function groupOfP2shAddress(address) {
136
- return groupOfAddressBytes(address);
137
- }
138
- function contractIdFromAddress(address) {
139
- return idFromAddress(address);
140
- }
141
- exports.contractIdFromAddress = contractIdFromAddress;
142
- function tokenIdFromAddress(address) {
143
- return idFromAddress(address);
144
- }
145
- exports.tokenIdFromAddress = tokenIdFromAddress;
146
- function idFromAddress(address) {
147
- const decoded = bs58_1.default.decode(address);
148
- if (decoded.length == 0)
149
- throw new Error('Address string is empty');
150
- const addressType = decoded[0];
151
- const addressBody = decoded.slice(1);
152
- if (addressType == AddressType.P2C) {
153
- return addressBody;
154
- }
155
- else {
156
- throw new Error(`Invalid contract address type: ${addressType}`);
157
- }
158
- }
159
74
  function hexToBinUnsafe(hex) {
160
75
  return buffer_1.Buffer.from(hex, 'hex');
161
76
  }
@@ -164,72 +79,6 @@ function binToHex(bin) {
164
79
  return buffer_1.Buffer.from(bin).toString('hex');
165
80
  }
166
81
  exports.binToHex = binToHex;
167
- function groupOfPrivateKey(privateKey, keyType) {
168
- return groupOfAddress(addressFromPublicKey(publicKeyFromPrivateKey(privateKey, keyType), keyType));
169
- }
170
- exports.groupOfPrivateKey = groupOfPrivateKey;
171
- function publicKeyFromPrivateKey(privateKey, _keyType) {
172
- const keyType = _keyType ?? 'default';
173
- if (keyType === 'default') {
174
- const key = ec.keyFromPrivate(privateKey);
175
- return key.getPublic(true, 'hex');
176
- }
177
- else {
178
- return ec.g.mul(new bn_js_1.default(privateKey, 16)).encode('hex', true).slice(2);
179
- }
180
- }
181
- exports.publicKeyFromPrivateKey = publicKeyFromPrivateKey;
182
- function addressFromPublicKey(publicKey, _keyType) {
183
- const keyType = _keyType ?? 'default';
184
- if (keyType === 'default') {
185
- const addressType = buffer_1.Buffer.from([AddressType.P2PKH]);
186
- const hash = buffer_1.Buffer.from(blakejs_1.default.blake2b(buffer_1.Buffer.from(publicKey, 'hex'), undefined, 32));
187
- const bytes = buffer_1.Buffer.concat([addressType, hash]);
188
- return bs58_1.default.encode(bytes);
189
- }
190
- else {
191
- const lockupScript = buffer_1.Buffer.from(`0101000000000458144020${publicKey}8685`, 'hex');
192
- return addressFromScript(lockupScript);
193
- }
194
- }
195
- exports.addressFromPublicKey = addressFromPublicKey;
196
- function addressFromScript(script) {
197
- const scriptHash = blakejs_1.default.blake2b(script, undefined, 32);
198
- const addressType = buffer_1.Buffer.from([AddressType.P2SH]);
199
- return bs58_1.default.encode(buffer_1.Buffer.concat([addressType, scriptHash]));
200
- }
201
- exports.addressFromScript = addressFromScript;
202
- function addressFromContractId(contractId) {
203
- const addressType = buffer_1.Buffer.from([AddressType.P2C]);
204
- const hash = buffer_1.Buffer.from(hexToBinUnsafe(contractId));
205
- const bytes = buffer_1.Buffer.concat([addressType, hash]);
206
- return bs58_1.default.encode(bytes);
207
- }
208
- exports.addressFromContractId = addressFromContractId;
209
- function addressFromTokenId(tokenId) {
210
- const contractId = tokenId; // contract ID is the same as token ID
211
- return addressFromContractId(contractId);
212
- }
213
- exports.addressFromTokenId = addressFromTokenId;
214
- function contractIdFromTx(txId, outputIndex) {
215
- const txIdBin = hexToBinUnsafe(txId);
216
- const data = buffer_1.Buffer.concat([txIdBin, buffer_1.Buffer.from([outputIndex])]);
217
- const hash = blakejs_1.default.blake2b(data, undefined, 32);
218
- return binToHex(hash);
219
- }
220
- exports.contractIdFromTx = contractIdFromTx;
221
- function subContractId(parentContractId, pathInHex, group) {
222
- if (group < 0 || group >= constants_1.TOTAL_NUMBER_OF_GROUPS) {
223
- throw new Error(`Invalid group ${group}`);
224
- }
225
- const data = buffer_1.Buffer.concat([hexToBinUnsafe(parentContractId), hexToBinUnsafe(pathInHex)]);
226
- const bytes = buffer_1.Buffer.concat([
227
- blakejs_1.default.blake2b(blakejs_1.default.blake2b(data, undefined, 32), undefined, 32).slice(0, -1),
228
- buffer_1.Buffer.from([group])
229
- ]);
230
- return binToHex(bytes);
231
- }
232
- exports.subContractId = subContractId;
233
82
  function blockChainIndex(blockHash) {
234
83
  if (blockHash.length != 64) {
235
84
  throw Error(`Invalid block hash: ${blockHash}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alephium/web3",
3
- "version": "0.30.2",
3
+ "version": "0.31.0",
4
4
  "description": "A JS/TS library to interact with the Alephium platform",
5
5
  "license": "GPL",
6
6
  "main": "dist/src/index.js",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "author": "Alephium dev <dev@alephium.org>",
29
29
  "config": {
30
- "alephium_version": "2.8.4",
30
+ "alephium_version": "2.8.5",
31
31
  "explorer_backend_version": "1.16.1"
32
32
  },
33
33
  "type": "commonjs",
@@ -752,6 +752,11 @@ export interface Reachable {
752
752
  type: string
753
753
  }
754
754
 
755
+ export interface Result {
756
+ /** @format bigint */
757
+ hashrate: string
758
+ }
759
+
755
760
  export interface RevealMnemonic {
756
761
  password: string
757
762
  }
@@ -842,6 +847,11 @@ export interface SweepAddressTransaction {
842
847
  gasPrice: string
843
848
  }
844
849
 
850
+ export interface TargetToHashrate {
851
+ /** @format hex-string */
852
+ target: string
853
+ }
854
+
845
855
  export interface TestContract {
846
856
  /** @format int32 */
847
857
  group?: number
@@ -1282,7 +1292,7 @@ export class HttpClient<SecurityDataType = unknown> {
1282
1292
 
1283
1293
  /**
1284
1294
  * @title Alephium API
1285
- * @version 2.8.4
1295
+ * @version 2.8.5
1286
1296
  * @baseUrl ../
1287
1297
  */
1288
1298
  export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDataType> {
@@ -2460,18 +2470,10 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
2460
2470
  * @summary Get contract state
2461
2471
  * @request GET:/contracts/{address}/state
2462
2472
  */
2463
- getContractsAddressState: (
2464
- address: string,
2465
- query: {
2466
- /** @format int32 */
2467
- group: number
2468
- },
2469
- params: RequestParams = {}
2470
- ) =>
2473
+ getContractsAddressState: (address: string, params: RequestParams = {}) =>
2471
2474
  this.request<ContractState, BadRequest | Unauthorized | NotFound | InternalServerError | ServiceUnavailable>({
2472
2475
  path: `/contracts/${address}/state`,
2473
2476
  method: 'GET',
2474
- query: query,
2475
2477
  format: 'json',
2476
2478
  ...params
2477
2479
  }).then(convertHttpResponse),
@@ -2619,40 +2621,6 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
2619
2621
  ...params
2620
2622
  }).then(convertHttpResponse)
2621
2623
  }
2622
- utils = {
2623
- /**
2624
- * No description
2625
- *
2626
- * @tags Utils
2627
- * @name PostUtilsVerifySignature
2628
- * @summary Verify the SecP256K1 signature of some data
2629
- * @request POST:/utils/verify-signature
2630
- */
2631
- postUtilsVerifySignature: (data: VerifySignature, params: RequestParams = {}) =>
2632
- this.request<boolean, BadRequest | Unauthorized | NotFound | InternalServerError | ServiceUnavailable>({
2633
- path: `/utils/verify-signature`,
2634
- method: 'POST',
2635
- body: data,
2636
- type: ContentType.Json,
2637
- format: 'json',
2638
- ...params
2639
- }).then(convertHttpResponse),
2640
-
2641
- /**
2642
- * No description
2643
- *
2644
- * @tags Utils
2645
- * @name PutUtilsCheckHashIndexing
2646
- * @summary Check and repair the indexing of block hashes
2647
- * @request PUT:/utils/check-hash-indexing
2648
- */
2649
- putUtilsCheckHashIndexing: (params: RequestParams = {}) =>
2650
- this.request<void, BadRequest | Unauthorized | NotFound | InternalServerError | ServiceUnavailable>({
2651
- path: `/utils/check-hash-indexing`,
2652
- method: 'PUT',
2653
- ...params
2654
- }).then(convertHttpResponse)
2655
- }
2656
2624
  miners = {
2657
2625
  /**
2658
2626
  * No description
@@ -2833,4 +2801,56 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
2833
2801
  ...params
2834
2802
  }).then(convertHttpResponse)
2835
2803
  }
2804
+ utils = {
2805
+ /**
2806
+ * No description
2807
+ *
2808
+ * @tags Utils
2809
+ * @name PostUtilsVerifySignature
2810
+ * @summary Verify the SecP256K1 signature of some data
2811
+ * @request POST:/utils/verify-signature
2812
+ */
2813
+ postUtilsVerifySignature: (data: VerifySignature, params: RequestParams = {}) =>
2814
+ this.request<boolean, BadRequest | Unauthorized | NotFound | InternalServerError | ServiceUnavailable>({
2815
+ path: `/utils/verify-signature`,
2816
+ method: 'POST',
2817
+ body: data,
2818
+ type: ContentType.Json,
2819
+ format: 'json',
2820
+ ...params
2821
+ }).then(convertHttpResponse),
2822
+
2823
+ /**
2824
+ * No description
2825
+ *
2826
+ * @tags Utils
2827
+ * @name PostUtilsTargetToHashrate
2828
+ * @summary Convert a target to hashrate
2829
+ * @request POST:/utils/target-to-hashrate
2830
+ */
2831
+ postUtilsTargetToHashrate: (data: TargetToHashrate, params: RequestParams = {}) =>
2832
+ this.request<Result, BadRequest | Unauthorized | NotFound | InternalServerError | ServiceUnavailable>({
2833
+ path: `/utils/target-to-hashrate`,
2834
+ method: 'POST',
2835
+ body: data,
2836
+ type: ContentType.Json,
2837
+ format: 'json',
2838
+ ...params
2839
+ }).then(convertHttpResponse),
2840
+
2841
+ /**
2842
+ * No description
2843
+ *
2844
+ * @tags Utils
2845
+ * @name PutUtilsCheckHashIndexing
2846
+ * @summary Check and repair the indexing of block hashes
2847
+ * @request PUT:/utils/check-hash-indexing
2848
+ */
2849
+ putUtilsCheckHashIndexing: (params: RequestParams = {}) =>
2850
+ this.request<void, BadRequest | Unauthorized | NotFound | InternalServerError | ServiceUnavailable>({
2851
+ path: `/utils/check-hash-indexing`,
2852
+ method: 'PUT',
2853
+ ...params
2854
+ }).then(convertHttpResponse)
2855
+ }
2836
2856
  }
@@ -220,8 +220,7 @@ export class NodeProvider implements NodeProviderApis {
220
220
 
221
221
  guessStdInterfaceId = async (tokenId: HexString): Promise<HexString | undefined> => {
222
222
  const address = addressFromTokenId(tokenId)
223
- const group = groupOfAddress(address)
224
- const rawState = await this.contracts.getContractsAddressState(addressFromTokenId(tokenId), { group })
223
+ const rawState = await this.contracts.getContractsAddressState(address)
225
224
  const lastImmField = rawState.immFields.slice(-1).pop()?.value
226
225
  const interfaceIdPrefix = '414c5048' // the hex of 'ALPH'
227
226
  if (typeof lastImmField === 'string' && lastImmField.startsWith(interfaceIdPrefix)) {
@@ -36,6 +36,7 @@ import {
36
36
  typeLength,
37
37
  getDefaultValue
38
38
  } from '../api'
39
+ import { CompileProjectResult } from '../api/api-alephium'
39
40
  import {
40
41
  SignDeployContractTxParams,
41
42
  SignDeployContractTxResult,
@@ -53,16 +54,17 @@ import {
53
54
  Eq,
54
55
  Optional,
55
56
  groupOfAddress,
56
- addressFromContractId,
57
57
  WebCrypto,
58
58
  hexToBinUnsafe,
59
- isDevnet
59
+ isDevnet,
60
+ addressFromContractId
60
61
  } from '../utils'
61
62
  import { getCurrentNodeProvider } from '../global'
62
63
  import * as path from 'path'
63
64
  import { EventSubscribeOptions, EventSubscription, subscribeToEvents } from './events'
64
65
  import { ONE_ALPH } from '../constants'
65
66
  import * as blake from 'blakejs'
67
+ import { parseError } from '../utils/error'
66
68
 
67
69
  const crypto = new WebCrypto()
68
70
 
@@ -190,6 +192,28 @@ type CodeInfo = {
190
192
  warnings: string[]
191
193
  }
192
194
 
195
+ type SourceInfoIndexes = {
196
+ sourceInfo: SourceInfo
197
+ startIndex: number
198
+ endIndex: number
199
+ }
200
+
201
+ function findSourceInfoAtLineNumber(sources: SourceInfo[], line: number): SourceInfoIndexes | undefined {
202
+ let currentLine = 0
203
+ const sourceInfosWithLine: SourceInfoIndexes[] = sources.map((source) => {
204
+ const startIndex = currentLine + 1
205
+ currentLine += source.sourceCode.split('\n').length
206
+ const endIndex = currentLine
207
+ return { sourceInfo: source, startIndex: startIndex, endIndex: endIndex }
208
+ })
209
+
210
+ const sourceInfo = sourceInfosWithLine.find((sourceInfoWithLine) => {
211
+ return line >= sourceInfoWithLine.startIndex && line <= sourceInfoWithLine.endIndex
212
+ })
213
+
214
+ return sourceInfo
215
+ }
216
+
193
217
  export class ProjectArtifact {
194
218
  static readonly artifactFileName = '.project.json'
195
219
 
@@ -431,6 +455,38 @@ export class Project {
431
455
  return contract.artifact
432
456
  }
433
457
 
458
+ private static async getCompileResult(
459
+ provider: NodeProvider,
460
+ compilerOptions: node.CompilerOptions,
461
+ sources: SourceInfo[]
462
+ ): Promise<CompileProjectResult> {
463
+ try {
464
+ const sourceStr = sources.map((f) => f.sourceCode).join('\n')
465
+ return await provider.contracts.postContractsCompileProject({
466
+ code: sourceStr,
467
+ compilerOptions: compilerOptions
468
+ })
469
+ } catch (error) {
470
+ if (!(error instanceof Error)) {
471
+ throw error
472
+ }
473
+
474
+ const parsed = parseError(error.message)
475
+ if (!parsed) {
476
+ throw error
477
+ }
478
+
479
+ const sourceInfo = findSourceInfoAtLineNumber(sources, parsed.lineStart)
480
+ if (!sourceInfo) {
481
+ throw error
482
+ }
483
+
484
+ const shiftIndex = parsed.lineStart - sourceInfo.startIndex + 1
485
+ const newError = parsed.reformat(shiftIndex, sourceInfo.sourceInfo.contractRelativePath)
486
+ throw new Error(newError)
487
+ }
488
+ }
489
+
434
490
  private static async compile(
435
491
  fullNodeVersion: string,
436
492
  provider: NodeProvider,
@@ -447,11 +503,8 @@ export class Project {
447
503
  }
448
504
  return acc
449
505
  }, [])
450
- const sourceStr = removeDuplicates.map((f) => f.sourceCode).join('\n')
451
- const result = await provider.contracts.postContractsCompileProject({
452
- code: sourceStr,
453
- compilerOptions: compilerOptions
454
- })
506
+
507
+ const result = await Project.getCompileResult(provider, compilerOptions, removeDuplicates)
455
508
  const contracts = new Map<string, Compiled<Contract>>()
456
509
  const scripts = new Map<string, Compiled<Script>>()
457
510
  result.contracts.forEach((contractResult) => {
@@ -1522,6 +1575,7 @@ export interface CallContractResult<R> {
1522
1575
  function specialContractAddress(n: number): string {
1523
1576
  const bytes = new Uint8Array(32).fill(0)
1524
1577
  bytes[31] = n
1578
+ console.log(addressFromContractId)
1525
1579
  return addressFromContractId(binToHex(bytes))
1526
1580
  }
1527
1581
 
@@ -1651,9 +1705,7 @@ export async function fetchContractState<F extends Fields, I extends ContractIns
1651
1705
  contract: ContractFactory<I, F>,
1652
1706
  instance: ContractInstance
1653
1707
  ): Promise<ContractState<F>> {
1654
- const contractState = await getCurrentNodeProvider().contracts.getContractsAddressState(instance.address, {
1655
- group: instance.groupIndex
1656
- })
1708
+ const contractState = await getCurrentNodeProvider().contracts.getContractsAddressState(instance.address)
1657
1709
  const state = contract.contract.fromApiContractState(contractState)
1658
1710
  return {
1659
1711
  ...state,
@@ -16,24 +16,179 @@ You should have received a copy of the GNU Lesser General Public License
16
16
  along with the library. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
+ import { ec as EC } from 'elliptic'
20
+ import BN from 'bn.js'
21
+ import { TOTAL_NUMBER_OF_GROUPS } from '../constants'
22
+ import blake from 'blakejs'
19
23
  import bs58 from './bs58'
20
24
  import djb2 from './djb2'
25
+ import { binToHex, hexToBinUnsafe } from './utils'
26
+ import { KeyType } from '../signer'
21
27
 
22
- export function addressToGroup(address: string, totalNumberOfGroups: number): number {
23
- const bytes = bs58.decode(address).slice(1)
24
- const value = djb2(bytes) | 1
25
- const hash = toPosInt(xorByte(value))
26
- const group = hash % totalNumberOfGroups
28
+ const ec = new EC('secp256k1')
29
+
30
+ export enum AddressType {
31
+ P2PKH = 0x00,
32
+ P2MPKH = 0x01,
33
+ P2SH = 0x02,
34
+ P2C = 0x03
35
+ }
36
+
37
+ export function validateAddress(address: string) {
38
+ let decoded: Uint8Array
39
+ try {
40
+ decoded = bs58.decode(address)
41
+ } catch (_) {
42
+ throw new Error('Invalid base58 string')
43
+ }
44
+
45
+ if (decoded.length === 0) throw new Error('Address is empty')
46
+ const addressType = decoded[0]
47
+ if (addressType === AddressType.P2MPKH) {
48
+ // [1, n, ...hashes, m]
49
+ if ((decoded.length - 3) % 32 === 0) return
50
+ } else if (addressType === AddressType.P2PKH || addressType === AddressType.P2SH || addressType === AddressType.P2C) {
51
+ // [type, ...hash]
52
+ if (decoded.length === 33) return
53
+ }
54
+
55
+ throw new Error(`Invalid address: ${address}`)
56
+ }
57
+
58
+ export function groupOfAddress(address: string): number {
59
+ validateAddress(address)
60
+
61
+ const decoded = bs58.decode(address)
62
+ const addressType = decoded[0]
63
+ const addressBody = decoded.slice(1)
64
+
65
+ if (addressType == AddressType.P2PKH) {
66
+ return groupOfP2pkhAddress(addressBody)
67
+ } else if (addressType == AddressType.P2MPKH) {
68
+ return groupOfP2mpkhAddress(addressBody)
69
+ } else if (addressType == AddressType.P2SH) {
70
+ return groupOfP2shAddress(addressBody)
71
+ } else {
72
+ // Contract Address
73
+ const id = contractIdFromAddress(address)
74
+ return id[`${id.length - 1}`]
75
+ }
76
+ }
77
+
78
+ function groupOfAddressBytes(bytes: Uint8Array): number {
79
+ const hint = djb2(bytes) | 1
80
+ const hash = xorByte(hint)
81
+ const group = hash % TOTAL_NUMBER_OF_GROUPS
27
82
  return group
28
83
  }
29
84
 
30
- function xorByte(value: number): number {
31
- const byte0 = value >> 24
32
- const byte1 = value >> 16
33
- const byte2 = value >> 8
34
- return byte0 ^ byte1 ^ byte2 ^ value
85
+ // Pay to public key hash address
86
+ function groupOfP2pkhAddress(address: Uint8Array): number {
87
+ return groupOfAddressBytes(address)
88
+ }
89
+
90
+ // Pay to multiple public key hash address
91
+ function groupOfP2mpkhAddress(address: Uint8Array): number {
92
+ return groupOfAddressBytes(address.slice(1, 33))
93
+ }
94
+
95
+ // Pay to script hash address
96
+ function groupOfP2shAddress(address: Uint8Array): number {
97
+ return groupOfAddressBytes(address)
98
+ }
99
+
100
+ export function contractIdFromAddress(address: string): Uint8Array {
101
+ return idFromAddress(address)
102
+ }
103
+
104
+ export function tokenIdFromAddress(address: string): Uint8Array {
105
+ return idFromAddress(address)
106
+ }
107
+
108
+ function idFromAddress(address: string): Uint8Array {
109
+ const decoded = bs58.decode(address)
110
+
111
+ if (decoded.length == 0) throw new Error('Address string is empty')
112
+ const addressType = decoded[0]
113
+ const addressBody = decoded.slice(1)
114
+
115
+ if (addressType == AddressType.P2C) {
116
+ return addressBody
117
+ } else {
118
+ throw new Error(`Invalid contract address type: ${addressType}`)
119
+ }
120
+ }
121
+
122
+ export function groupOfPrivateKey(privateKey: string, keyType?: KeyType): number {
123
+ return groupOfAddress(addressFromPublicKey(publicKeyFromPrivateKey(privateKey, keyType), keyType))
124
+ }
125
+
126
+ export function publicKeyFromPrivateKey(privateKey: string, _keyType?: KeyType): string {
127
+ const keyType = _keyType ?? 'default'
128
+
129
+ if (keyType === 'default') {
130
+ const key = ec.keyFromPrivate(privateKey)
131
+ return key.getPublic(true, 'hex')
132
+ } else {
133
+ return ec.g.mul(new BN(privateKey, 16)).encode('hex', true).slice(2)
134
+ }
135
+ }
136
+
137
+ export function addressFromPublicKey(publicKey: string, _keyType?: KeyType): string {
138
+ const keyType = _keyType ?? 'default'
139
+
140
+ if (keyType === 'default') {
141
+ const addressType = Buffer.from([AddressType.P2PKH])
142
+ const hash = Buffer.from(blake.blake2b(Buffer.from(publicKey, 'hex'), undefined, 32))
143
+ const bytes = Buffer.concat([addressType, hash])
144
+ return bs58.encode(bytes)
145
+ } else {
146
+ const lockupScript = Buffer.from(`0101000000000458144020${publicKey}8685`, 'hex')
147
+ return addressFromScript(lockupScript)
148
+ }
149
+ }
150
+
151
+ export function addressFromScript(script: Uint8Array): string {
152
+ const scriptHash = blake.blake2b(script, undefined, 32)
153
+ const addressType = Buffer.from([AddressType.P2SH])
154
+ return bs58.encode(Buffer.concat([addressType, scriptHash]))
155
+ }
156
+
157
+ export function addressFromContractId(contractId: string): string {
158
+ const addressType = Buffer.from([AddressType.P2C])
159
+ const hash = Buffer.from(hexToBinUnsafe(contractId))
160
+ const bytes = Buffer.concat([addressType, hash])
161
+ return bs58.encode(bytes)
162
+ }
163
+
164
+ export function addressFromTokenId(tokenId: string): string {
165
+ const contractId = tokenId // contract ID is the same as token ID
166
+ return addressFromContractId(contractId)
167
+ }
168
+
169
+ export function contractIdFromTx(txId: string, outputIndex: number): string {
170
+ const txIdBin = hexToBinUnsafe(txId)
171
+ const data = Buffer.concat([txIdBin, Buffer.from([outputIndex])])
172
+ const hash = blake.blake2b(data, undefined, 32)
173
+ return binToHex(hash)
174
+ }
175
+
176
+ export function subContractId(parentContractId: string, pathInHex: string, group: number): string {
177
+ if (group < 0 || group >= TOTAL_NUMBER_OF_GROUPS) {
178
+ throw new Error(`Invalid group ${group}`)
179
+ }
180
+ const data = Buffer.concat([hexToBinUnsafe(parentContractId), hexToBinUnsafe(pathInHex)])
181
+ const bytes = Buffer.concat([
182
+ blake.blake2b(blake.blake2b(data, undefined, 32), undefined, 32).slice(0, -1),
183
+ Buffer.from([group])
184
+ ])
185
+ return binToHex(bytes)
35
186
  }
36
187
 
37
- function toPosInt(byte: number): number {
38
- return byte & 0xff
188
+ export function xorByte(intValue: number): number {
189
+ const byte0 = (intValue >> 24) & 0xff
190
+ const byte1 = (intValue >> 16) & 0xff
191
+ const byte2 = (intValue >> 8) & 0xff
192
+ const byte3 = intValue & 0xff
193
+ return (byte0 ^ byte1 ^ byte2 ^ byte3) & 0xff
39
194
  }