@alephium/web3 0.39.3 → 0.41.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.
Files changed (64) hide show
  1. package/dist/alephium-web3.min.js +1 -1
  2. package/dist/alephium-web3.min.js.map +1 -1
  3. package/dist/src/api/api-alephium.d.ts +1 -1
  4. package/dist/src/api/api-alephium.js +1 -1
  5. package/dist/src/api/node-provider.d.ts +2 -0
  6. package/dist/src/api/node-provider.js +12 -6
  7. package/dist/src/api/utils.d.ts +1 -1
  8. package/dist/src/block/block.d.ts +28 -0
  9. package/dist/src/block/block.js +131 -0
  10. package/dist/src/block/index.d.ts +1 -0
  11. package/dist/src/block/index.js +22 -0
  12. package/dist/src/codec/contract-output-codec.js +4 -4
  13. package/dist/src/codec/instr-codec.d.ts +3 -0
  14. package/dist/src/codec/instr-codec.js +19 -3
  15. package/dist/src/codec/lockup-script-codec.js +2 -2
  16. package/dist/src/codec/method-codec.d.ts +3 -1
  17. package/dist/src/codec/method-codec.js +27 -2
  18. package/dist/src/codec/script-codec.d.ts +11 -6
  19. package/dist/src/codec/script-codec.js +13 -2
  20. package/dist/src/codec/transaction-codec.js +2 -2
  21. package/dist/src/codec/unlock-script-codec.d.ts +2 -2
  22. package/dist/src/codec/unsigned-tx-codec.d.ts +2 -2
  23. package/dist/src/contract/contract.d.ts +25 -7
  24. package/dist/src/contract/contract.js +136 -51
  25. package/dist/src/contract/events.d.ts +1 -2
  26. package/dist/src/contract/events.js +28 -14
  27. package/dist/src/index.d.ts +1 -0
  28. package/dist/src/index.js +1 -0
  29. package/dist/src/signer/tx-builder.js +4 -4
  30. package/dist/src/transaction/status.js +28 -4
  31. package/dist/src/utils/address.js +29 -16
  32. package/dist/src/utils/exchange.js +25 -15
  33. package/dist/src/utils/number.d.ts +1 -1
  34. package/dist/src/utils/sign.js +6 -6
  35. package/dist/src/utils/subscription.d.ts +4 -4
  36. package/dist/src/utils/subscription.js +1 -1
  37. package/package.json +5 -5
  38. package/src/api/api-alephium.ts +1 -1
  39. package/src/api/node-provider.ts +8 -1
  40. package/src/api/utils.ts +1 -1
  41. package/src/block/block.ts +139 -0
  42. package/src/block/index.ts +19 -0
  43. package/src/codec/contract-output-codec.ts +1 -1
  44. package/src/codec/instr-codec.ts +14 -1
  45. package/src/codec/lockup-script-codec.ts +3 -3
  46. package/src/codec/method-codec.ts +41 -3
  47. package/src/codec/script-codec.ts +23 -5
  48. package/src/codec/transaction-codec.ts +1 -1
  49. package/src/codec/unlock-script-codec.ts +2 -2
  50. package/src/codec/unsigned-tx-codec.ts +2 -2
  51. package/src/contract/contract.ts +178 -67
  52. package/src/contract/events.ts +6 -18
  53. package/src/index.ts +1 -0
  54. package/src/signer/tx-builder.ts +2 -2
  55. package/src/transaction/status.ts +4 -4
  56. package/src/utils/address.ts +15 -2
  57. package/src/utils/exchange.ts +32 -10
  58. package/src/utils/number.ts +1 -1
  59. package/src/utils/sign.ts +1 -1
  60. package/src/utils/subscription.ts +4 -4
  61. package/std/fungible_token_interface.ral +1 -0
  62. package/std/nft_collection_interface.ral +1 -0
  63. package/std/nft_collection_with_royalty_interface.ral +1 -0
  64. package/std/nft_interface.ral +1 -0
@@ -28,6 +28,9 @@ const blakejs_1 = __importDefault(require("blakejs"));
28
28
  const bs58_1 = __importDefault(require("./bs58"));
29
29
  const djb2_1 = __importDefault(require("./djb2"));
30
30
  const utils_1 = require("./utils");
31
+ const lockup_script_codec_1 = require("../codec/lockup-script-codec");
32
+ const buffer_1 = require("buffer/");
33
+ const codec_1 = require("../codec");
31
34
  const ec = new elliptic_1.ec('secp256k1');
32
35
  var AddressType;
33
36
  (function (AddressType) {
@@ -52,9 +55,19 @@ function decodeAndValidateAddress(address) {
52
55
  throw new Error('Address is empty');
53
56
  const addressType = decoded[0];
54
57
  if (addressType === AddressType.P2MPKH) {
55
- // [1, n, ...hashes, m]
56
- if ((decoded.length - 3) % 32 === 0)
57
- return decoded;
58
+ let multisig;
59
+ try {
60
+ multisig = lockup_script_codec_1.lockupScriptCodec.decode(buffer_1.Buffer.from(decoded)).script;
61
+ }
62
+ catch (_) {
63
+ throw new Error(`Invalid multisig address: ${address}`);
64
+ }
65
+ const n = multisig.publicKeyHashes.value.length;
66
+ const m = codec_1.compactSignedIntCodec.toI32(multisig.m);
67
+ if (n < m) {
68
+ throw new Error(`Invalid multisig address, n: ${n}, m: ${m}`);
69
+ }
70
+ return decoded;
58
71
  }
59
72
  else if (addressType === AddressType.P2PKH || addressType === AddressType.P2SH || addressType === AddressType.P2C) {
60
73
  // [type, ...hash]
@@ -150,27 +163,27 @@ exports.publicKeyFromPrivateKey = publicKeyFromPrivateKey;
150
163
  function addressFromPublicKey(publicKey, _keyType) {
151
164
  const keyType = _keyType ?? 'default';
152
165
  if (keyType === 'default') {
153
- const addressType = Buffer.from([AddressType.P2PKH]);
154
- const hash = Buffer.from(blakejs_1.default.blake2b(Buffer.from(publicKey, 'hex'), undefined, 32));
155
- const bytes = Buffer.concat([addressType, hash]);
166
+ const addressType = buffer_1.Buffer.from([AddressType.P2PKH]);
167
+ const hash = buffer_1.Buffer.from(blakejs_1.default.blake2b(buffer_1.Buffer.from(publicKey, 'hex'), undefined, 32));
168
+ const bytes = buffer_1.Buffer.concat([addressType, hash]);
156
169
  return bs58_1.default.encode(bytes);
157
170
  }
158
171
  else {
159
- const lockupScript = Buffer.from(`0101000000000458144020${publicKey}8685`, 'hex');
172
+ const lockupScript = buffer_1.Buffer.from(`0101000000000458144020${publicKey}8685`, 'hex');
160
173
  return addressFromScript(lockupScript);
161
174
  }
162
175
  }
163
176
  exports.addressFromPublicKey = addressFromPublicKey;
164
177
  function addressFromScript(script) {
165
178
  const scriptHash = blakejs_1.default.blake2b(script, undefined, 32);
166
- const addressType = Buffer.from([AddressType.P2SH]);
167
- return bs58_1.default.encode(Buffer.concat([addressType, scriptHash]));
179
+ const addressType = buffer_1.Buffer.from([AddressType.P2SH]);
180
+ return bs58_1.default.encode(buffer_1.Buffer.concat([addressType, scriptHash]));
168
181
  }
169
182
  exports.addressFromScript = addressFromScript;
170
183
  function addressFromContractId(contractId) {
171
- const addressType = Buffer.from([AddressType.P2C]);
172
- const hash = Buffer.from((0, utils_1.hexToBinUnsafe)(contractId));
173
- const bytes = Buffer.concat([addressType, hash]);
184
+ const addressType = buffer_1.Buffer.from([AddressType.P2C]);
185
+ const hash = buffer_1.Buffer.from((0, utils_1.hexToBinUnsafe)(contractId));
186
+ const bytes = buffer_1.Buffer.concat([addressType, hash]);
174
187
  return bs58_1.default.encode(bytes);
175
188
  }
176
189
  exports.addressFromContractId = addressFromContractId;
@@ -181,7 +194,7 @@ function addressFromTokenId(tokenId) {
181
194
  exports.addressFromTokenId = addressFromTokenId;
182
195
  function contractIdFromTx(txId, outputIndex) {
183
196
  const txIdBin = (0, utils_1.hexToBinUnsafe)(txId);
184
- const data = Buffer.concat([txIdBin, Buffer.from([outputIndex])]);
197
+ const data = buffer_1.Buffer.concat([txIdBin, buffer_1.Buffer.from([outputIndex])]);
185
198
  const hash = blakejs_1.default.blake2b(data, undefined, 32);
186
199
  return (0, utils_1.binToHex)(hash);
187
200
  }
@@ -190,10 +203,10 @@ function subContractId(parentContractId, pathInHex, group) {
190
203
  if (group < 0 || group >= constants_1.TOTAL_NUMBER_OF_GROUPS) {
191
204
  throw new Error(`Invalid group ${group}`);
192
205
  }
193
- const data = Buffer.concat([(0, utils_1.hexToBinUnsafe)(parentContractId), (0, utils_1.hexToBinUnsafe)(pathInHex)]);
194
- const bytes = Buffer.concat([
206
+ const data = buffer_1.Buffer.concat([(0, utils_1.hexToBinUnsafe)(parentContractId), (0, utils_1.hexToBinUnsafe)(pathInHex)]);
207
+ const bytes = buffer_1.Buffer.concat([
195
208
  blakejs_1.default.blake2b(blakejs_1.default.blake2b(data, undefined, 32), undefined, 32).slice(0, -1),
196
- Buffer.from([group])
209
+ buffer_1.Buffer.from([group])
197
210
  ]);
198
211
  return (0, utils_1.binToHex)(bytes);
199
212
  }
@@ -18,11 +18,14 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.
18
18
  */
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
20
  exports.getAddressFromUnlockScript = exports.getSenderAddress = exports.getALPHDepositInfo = exports.isALPHTransferTx = exports.validateExchangeAddress = void 0;
21
- const __1 = require("..");
21
+ const utils_1 = require("../utils");
22
+ const unlock_script_codec_1 = require("../codec/unlock-script-codec");
23
+ const buffer_1 = require("buffer/");
24
+ const script_codec_1 = require("../codec/script-codec");
22
25
  function validateExchangeAddress(address) {
23
26
  let decoded;
24
27
  try {
25
- decoded = __1.bs58.decode(address);
28
+ decoded = utils_1.bs58.decode(address);
26
29
  }
27
30
  catch (_) {
28
31
  throw new Error('Invalid base58 string');
@@ -30,7 +33,7 @@ function validateExchangeAddress(address) {
30
33
  if (decoded.length === 0)
31
34
  throw new Error('Address is empty');
32
35
  const addressType = decoded[0];
33
- if (addressType !== __1.AddressType.P2PKH && addressType !== __1.AddressType.P2SH) {
36
+ if (addressType !== utils_1.AddressType.P2PKH && addressType !== utils_1.AddressType.P2SH) {
34
37
  throw new Error('Invalid address type');
35
38
  }
36
39
  if (decoded.length !== 33) {
@@ -83,27 +86,34 @@ var UnlockScriptType;
83
86
  UnlockScriptType[UnlockScriptType["P2SH"] = 2] = "P2SH";
84
87
  })(UnlockScriptType || (UnlockScriptType = {}));
85
88
  function getAddressFromUnlockScript(unlockScript) {
86
- const decoded = (0, __1.hexToBinUnsafe)(unlockScript);
89
+ if (!(0, utils_1.isHexString)(unlockScript)) {
90
+ throw new Error(`Invalid unlock script ${unlockScript}, expected a hex string`);
91
+ }
92
+ const decoded = (0, utils_1.hexToBinUnsafe)(unlockScript);
87
93
  if (decoded.length === 0)
88
94
  throw new Error('UnlockScript is empty');
89
95
  const unlockScriptType = decoded[0];
90
96
  const unlockScriptBody = decoded.slice(1);
91
97
  if (unlockScriptType === UnlockScriptType.P2PKH) {
92
- return (0, __1.addressFromPublicKey)((0, __1.binToHex)(unlockScriptBody));
98
+ if (unlockScriptBody.length !== 33) {
99
+ throw new Error(`Invalid p2pkh unlock script: ${unlockScript}`);
100
+ }
101
+ return (0, utils_1.addressFromPublicKey)((0, utils_1.binToHex)(unlockScriptBody));
93
102
  }
94
- else if (unlockScriptType === UnlockScriptType.P2MPKH) {
103
+ if (unlockScriptType === UnlockScriptType.P2MPKH) {
95
104
  throw new Error('Naive multi-sig address is not supported for exchanges as it will be replaced by P2SH');
96
105
  }
97
- else if (unlockScriptType === UnlockScriptType.P2SH) {
98
- // FIXEME: for now we assume that the params is empty, so we need to
99
- // remove the last byte from the `unlockScriptBody`, we can decode
100
- // the unlock script once the codec PR is merged
101
- const script = unlockScriptBody.slice(0, -1);
102
- return (0, __1.addressFromScript)(script);
103
- }
104
- else {
105
- throw new Error('Invalid unlock script type');
106
+ if (unlockScriptType === UnlockScriptType.P2SH) {
107
+ let p2sh;
108
+ try {
109
+ p2sh = unlock_script_codec_1.unlockScriptCodec.decode(buffer_1.Buffer.from(decoded)).script;
110
+ }
111
+ catch (_) {
112
+ throw new Error(`Invalid p2sh unlock script: ${unlockScript}`);
113
+ }
114
+ return (0, utils_1.addressFromScript)(script_codec_1.scriptCodec.encode(p2sh.script));
106
115
  }
116
+ throw new Error('Invalid unlock script type');
107
117
  }
108
118
  exports.getAddressFromUnlockScript = getAddressFromUnlockScript;
109
119
  function checkALPHOutput(tx) {
@@ -1,4 +1,4 @@
1
- import { Number256 } from '..';
1
+ import { Number256 } from '../api/types';
2
2
  export declare const isNumeric: (numToCheck: any) => boolean;
3
3
  export interface IPrettifyNumberConfig {
4
4
  minDecimalPlaces: number;
@@ -42,7 +42,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
42
42
  Object.defineProperty(exports, "__esModule", { value: true });
43
43
  exports.verifySignature = exports.sign = void 0;
44
44
  const elliptic_1 = require("elliptic");
45
- const __1 = require("..");
45
+ const utils_1 = require("../utils");
46
46
  const necc = __importStar(require("@noble/secp256k1"));
47
47
  const crypto_1 = require("crypto");
48
48
  const ec = new elliptic_1.ec('secp256k1');
@@ -63,11 +63,11 @@ function sign(hash, privateKey, _keyType) {
63
63
  if (keyType === 'default') {
64
64
  const key = ec.keyFromPrivate(privateKey);
65
65
  const signature = key.sign(hash);
66
- return (0, __1.encodeSignature)(signature);
66
+ return (0, utils_1.encodeSignature)(signature);
67
67
  }
68
68
  else {
69
- const signature = necc.schnorr.signSync((0, __1.hexToBinUnsafe)(hash), (0, __1.hexToBinUnsafe)(privateKey));
70
- return (0, __1.binToHex)(signature);
69
+ const signature = necc.schnorr.signSync((0, utils_1.hexToBinUnsafe)(hash), (0, utils_1.hexToBinUnsafe)(privateKey));
70
+ return (0, utils_1.binToHex)(signature);
71
71
  }
72
72
  }
73
73
  exports.sign = sign;
@@ -76,10 +76,10 @@ function verifySignature(hash, publicKey, signature, _keyType) {
76
76
  try {
77
77
  if (keyType === 'default') {
78
78
  const key = ec.keyFromPublic(publicKey, 'hex');
79
- return key.verify(hash, (0, __1.signatureDecode)(ec, signature));
79
+ return key.verify(hash, (0, utils_1.signatureDecode)(ec, signature));
80
80
  }
81
81
  else {
82
- return necc.schnorr.verifySync((0, __1.hexToBinUnsafe)(signature), (0, __1.hexToBinUnsafe)(hash), (0, __1.hexToBinUnsafe)(publicKey));
82
+ return necc.schnorr.verifySync((0, utils_1.hexToBinUnsafe)(signature), (0, utils_1.hexToBinUnsafe)(hash), (0, utils_1.hexToBinUnsafe)(publicKey));
83
83
  }
84
84
  }
85
85
  catch (error) {
@@ -1,6 +1,6 @@
1
1
  import EventEmitter from 'eventemitter3';
2
- type MessageCallback<Message> = (message: Message) => Promise<void>;
3
- type ErrorCallback<Message> = (error: any, subscription: Subscription<Message>) => Promise<void>;
2
+ type MessageCallback<Message> = (message: Message) => Promise<void> | void;
3
+ type ErrorCallback<Message> = (error: any, subscription: Subscription<Message>) => Promise<void> | void;
4
4
  export interface SubscribeOptions<Message> {
5
5
  pollingInterval: number;
6
6
  messageCallback: MessageCallback<Message>;
@@ -14,9 +14,9 @@ export declare abstract class Subscription<Message> {
14
14
  protected eventEmitter: EventEmitter;
15
15
  protected cancelled: boolean;
16
16
  constructor(options: SubscribeOptions<Message>);
17
- startPolling(): void;
17
+ subscribe(): void;
18
18
  unsubscribe(): void;
19
19
  isCancelled(): boolean;
20
- abstract polling(): Promise<void>;
20
+ protected abstract polling(): Promise<void>;
21
21
  }
22
22
  export {};
@@ -31,7 +31,7 @@ class Subscription {
31
31
  this.cancelled = false;
32
32
  this.eventEmitter = new eventemitter3_1.default();
33
33
  }
34
- startPolling() {
34
+ subscribe() {
35
35
  this.eventEmitter.on('tick', async () => {
36
36
  await this.polling();
37
37
  if (!this.cancelled) {
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@alephium/web3",
3
- "version": "0.39.3",
3
+ "version": "0.41.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",
7
7
  "browser": "dist/alephium-web3.min.js",
8
8
  "types": "dist/src/index.d.ts",
9
9
  "exports": {
10
- ".": "./dist/src/index.js",
11
- "./test": "./dist/src/test/index.js"
10
+ "node": "./dist/src/index.js",
11
+ "default": "./dist/alephium-web3.min.js"
12
12
  },
13
13
  "typesVersions": {
14
14
  "*": {
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "author": "Alephium dev <dev@alephium.org>",
29
29
  "config": {
30
- "alephium_version": "2.11.0",
30
+ "alephium_version": "2.14.0",
31
31
  "explorer_backend_version": "1.17.0"
32
32
  },
33
33
  "type": "commonjs",
@@ -90,7 +90,7 @@
90
90
  "build": "rm -rf dist/* && npx tsc --build . && webpack",
91
91
  "test": "jest -i --config ./jest-config.json",
92
92
  "update-schemas": "npm run update-schema:alephium && npm run update-schema:explorer",
93
- "update-schema:alephium": "npx swagger-typescript-api --disable-throw-on-error -t ./configs -o ./src/api -n api-alephium.ts -p https://raw.githubusercontent.com/alephium/alephium/rhone-upgrade/api/src/main/resources/openapi.json",
93
+ "update-schema:alephium": "npx swagger-typescript-api --disable-throw-on-error -t ./configs -o ./src/api -n api-alephium.ts -p https://raw.githubusercontent.com/alephium/alephium/v${npm_package_config_alephium_version}/api/src/main/resources/openapi.json",
94
94
  "update-schema:explorer": "npx swagger-typescript-api --disable-throw-on-error -t ./configs -o ./src/api -n api-explorer.ts -p https://raw.githubusercontent.com/alephium/explorer-backend/v${npm_package_config_explorer_backend_version}/app/src/main/resources/explorer-backend-openapi.json",
95
95
  "check-versions": "node scripts/check-versions.js ${npm_package_config_alephium_version} ${npm_package_config_explorer_backend_version}"
96
96
  }
@@ -1306,7 +1306,7 @@ export class HttpClient<SecurityDataType = unknown> {
1306
1306
 
1307
1307
  /**
1308
1308
  * @title Alephium API
1309
- * @version 2.11.0
1309
+ * @version 2.14.0
1310
1310
  * @baseUrl ../
1311
1311
  */
1312
1312
  export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDataType> {
@@ -28,7 +28,6 @@ import {
28
28
  requestWithLog
29
29
  } from './types'
30
30
  import { Api as NodeApi, CallContractFailed, CallContractSucceeded } from './api-alephium'
31
- import { tryGetCallResult } from '../contract'
32
31
  import {
33
32
  HexString,
34
33
  addressFromContractId,
@@ -38,6 +37,7 @@ import {
38
37
  isHexString,
39
38
  toNonNegativeBigInt
40
39
  } from '../utils'
40
+ import * as node from '../api/api-alephium'
41
41
 
42
42
  function initializeNodeApi(baseUrl: string, apiKey?: string, customFetch?: typeof fetch): NodeApi<string> {
43
43
  const nodeApi = new NodeApi<string>({
@@ -253,3 +253,10 @@ export class NodeProvider implements NodeProviderApis {
253
253
  }
254
254
  }
255
255
  }
256
+
257
+ export function tryGetCallResult(result: node.CallContractResult): node.CallContractSucceeded {
258
+ if (result.type === 'CallContractFailed') {
259
+ throw new Error(`Failed to call contract, error: ${(result as node.CallContractFailed).error}`)
260
+ }
261
+ return result as node.CallContractSucceeded
262
+ }
package/src/api/utils.ts CHANGED
@@ -17,7 +17,7 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
 
19
19
  import 'cross-fetch/polyfill'
20
- import { node } from '..'
20
+ import * as node from '../api/api-alephium'
21
21
 
22
22
  export function convertHttpResponse<T>(response: { status: number; data: T; error?: { detail: string } }): T {
23
23
  if (response.error) {
@@ -0,0 +1,139 @@
1
+ /*
2
+ Copyright 2018 - 2022 The Alephium Authors
3
+ This file is part of the alephium project.
4
+
5
+ The library is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU Lesser General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ The library is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU Lesser General Public License for more details.
14
+
15
+ You should have received a copy of the GNU Lesser General Public License
16
+ along with the library. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ import { Subscription, SubscribeOptions } from '../utils/subscription'
20
+ import * as node from '../api/api-alephium'
21
+ import { NodeProvider } from '../api'
22
+ import * as web3 from '../global'
23
+
24
+ export type ReorgCallback = (orphanBlocks: node.BlockEntry[], newBlocks: node.BlockEntry[]) => Promise<void> | void
25
+
26
+ export interface BlockSubscribeOptions extends SubscribeOptions<node.BlockEntry> {
27
+ reorgCallback?: ReorgCallback
28
+ }
29
+
30
+ export abstract class BlockSubscriptionBase extends Subscription<node.BlockEntry> {
31
+ abstract readonly reorgCallback?: ReorgCallback
32
+ abstract readonly fromGroup: number
33
+ abstract readonly toGroup: number
34
+
35
+ abstract getHashesAtHeight(height: number): Promise<string[]>
36
+ abstract getBlockByHash(hash: string): Promise<node.BlockEntry>
37
+
38
+ protected getParentHash(block: node.BlockEntry): string {
39
+ const index = Math.floor(block.deps.length / 2) + this.toGroup
40
+ return block.deps[index]
41
+ }
42
+
43
+ protected async handleReorg(blockHash: string, blockHeight: number) {
44
+ console.info(`reorg occur, hash: ${blockHash}, height: ${blockHeight}`)
45
+ if (this.reorgCallback === undefined) return
46
+
47
+ const orphans: string[] = []
48
+ const newHashes: string[] = []
49
+ let fromHash = blockHash
50
+ let fromHeight = blockHeight
51
+ while (true) {
52
+ const hashes = await this.getHashesAtHeight(fromHeight)
53
+ const canonicalHash = hashes[0]
54
+ if (canonicalHash !== fromHash) {
55
+ orphans.push(fromHash)
56
+ newHashes.push(canonicalHash)
57
+ const block = await this.getBlockByHash(fromHash)
58
+ fromHash = this.getParentHash(block)
59
+ fromHeight -= 1
60
+ } else {
61
+ break
62
+ }
63
+ }
64
+
65
+ const orphanBlocks: node.BlockEntry[] = []
66
+ for (const hash of orphans.reverse()) {
67
+ const block = await this.getBlockByHash(hash)
68
+ orphanBlocks.push(block)
69
+ }
70
+
71
+ const newBlocks: node.BlockEntry[] = []
72
+ for (const hash of newHashes.reverse()) {
73
+ const block = await this.getBlockByHash(hash)
74
+ newBlocks.push(block)
75
+ }
76
+ console.info(`orphan hashes: ${orphanBlocks.map((b) => b.hash)}, new hashes: ${newBlocks.map((b) => b.hash)}`)
77
+ await this.reorgCallback(orphanBlocks, newBlocks)
78
+ }
79
+ }
80
+
81
+ export class BlockSubscription extends BlockSubscriptionBase {
82
+ readonly nodeProvider: NodeProvider
83
+ readonly fromGroup: number
84
+ readonly toGroup: number
85
+ readonly reorgCallback?: ReorgCallback
86
+ private currentBlockHeight: number
87
+ private parentBlockHash: string | undefined
88
+
89
+ constructor(
90
+ options: BlockSubscribeOptions,
91
+ fromGroup: number,
92
+ toGroup: number,
93
+ fromBlockHeight: number,
94
+ nodeProvider: NodeProvider | undefined = undefined
95
+ ) {
96
+ super(options)
97
+ this.nodeProvider = nodeProvider ?? web3.getCurrentNodeProvider()
98
+ this.fromGroup = fromGroup
99
+ this.toGroup = toGroup
100
+ this.reorgCallback = options.reorgCallback
101
+ this.currentBlockHeight = fromBlockHeight
102
+ this.parentBlockHash = undefined
103
+ }
104
+
105
+ override async getHashesAtHeight(height: number): Promise<string[]> {
106
+ const result = await this.nodeProvider.blockflow.getBlockflowHashes({
107
+ fromGroup: this.fromGroup,
108
+ toGroup: this.toGroup,
109
+ height
110
+ })
111
+ return result.headers
112
+ }
113
+
114
+ override async getBlockByHash(hash: string): Promise<node.BlockEntry> {
115
+ return await this.nodeProvider.blockflow.getBlockflowBlocksBlockHash(hash)
116
+ }
117
+
118
+ override async polling(): Promise<void> {
119
+ try {
120
+ const chainInfo = await this.nodeProvider.blockflow.getBlockflowChainInfo({
121
+ fromGroup: this.fromGroup,
122
+ toGroup: this.toGroup
123
+ })
124
+
125
+ while (this.currentBlockHeight <= chainInfo.currentHeight) {
126
+ const hashes = await this.getHashesAtHeight(this.currentBlockHeight)
127
+ const block = await this.getBlockByHash(hashes[0])
128
+ if (this.parentBlockHash !== undefined && this.getParentHash(block) !== this.parentBlockHash) {
129
+ await this.handleReorg(this.parentBlockHash, this.currentBlockHeight - 1)
130
+ }
131
+ await this.messageCallback(block)
132
+ this.currentBlockHeight += 1
133
+ this.parentBlockHash = hashes[0]
134
+ }
135
+ } catch (err) {
136
+ await this.errorCallback(err, this)
137
+ }
138
+ }
139
+ }
@@ -0,0 +1,19 @@
1
+ /*
2
+ Copyright 2018 - 2022 The Alephium Authors
3
+ This file is part of the alephium project.
4
+
5
+ The library is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU Lesser General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ The library is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU Lesser General Public License for more details.
14
+
15
+ You should have received a copy of the GNU Lesser General Public License
16
+ along with the library. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ export { ReorgCallback, BlockSubscribeOptions, BlockSubscription } from './block'
@@ -24,7 +24,7 @@ import { Codec } from './codec'
24
24
  import { Token, tokensCodec } from './token-codec'
25
25
  import { ContractOutput as ApiContractOutput } from '../api/api-alephium'
26
26
  import { blakeHash, createHint } from './hash'
27
- import { binToHex, bs58 } from '..'
27
+ import { binToHex, bs58 } from '../utils'
28
28
  import { signedIntCodec } from './signed-int-codec'
29
29
  import { lockupScriptCodec } from './lockup-script-codec'
30
30
 
@@ -22,6 +22,7 @@ import { compactUnsignedIntCodec, compactSignedIntCodec, DecodedCompactInt } fro
22
22
  import { ByteString, byteStringCodec } from './bytestring-codec'
23
23
  import { LockupScript, lockupScriptCodec } from './lockup-script-codec'
24
24
  import { Codec } from './codec'
25
+ import { signedIntCodec } from './signed-int-codec'
25
26
 
26
27
  const byteStringArrayCodec = new ArrayCodec(byteStringCodec)
27
28
 
@@ -192,6 +193,7 @@ export const AddModN: Instr = { code: 0x88, value: {} }
192
193
  export const U256ToString: Instr = { code: 0x89, value: {} }
193
194
  export const I256ToString: Instr = { code: 0x8a, value: {} }
194
195
  export const BoolToString: Instr = { code: 0x8b, value: {} }
196
+ export const GroupOfAddress: Instr = { code: 0x8c, value: {} }
195
197
  export const LoadMutField = (index: number): Instr => ({ code: 0xa0, value: { index } })
196
198
  export const StoreMutField = (index: number): Instr => ({ code: 0xa1, value: { index } })
197
199
  export const ApproveAlph: Instr = { code: 0xa2, value: {} }
@@ -246,6 +248,12 @@ export const CreateMapEntry: (immFields: number, mutFields: number) => Instr = (
246
248
  immFields: number,
247
249
  mutFields: number
248
250
  ) => ({ code: 0xd2, value: { immFields, mutFields } })
251
+ export const MethodSelector: (selector: number) => Instr = (selector: number) => {
252
+ return { code: 0xd3, value: { index: selector } }
253
+ }
254
+ export const CallExternalBySelector: (selector: number) => Instr = (selector: number) => {
255
+ return { code: 0xd4, value: { index: selector } }
256
+ }
249
257
 
250
258
  export class InstrCodec implements Codec<Instr> {
251
259
  parser = Parser.start()
@@ -393,6 +401,7 @@ export class InstrCodec implements Codec<Instr> {
393
401
  [U256ToString.code]: Parser.start(),
394
402
  [I256ToString.code]: Parser.start(),
395
403
  [BoolToString.code]: Parser.start(),
404
+ [GroupOfAddress.code]: Parser.start(),
396
405
  0xa0: Parser.start().uint8('index'),
397
406
  0xa1: Parser.start().uint8('index'),
398
407
  [ApproveAlph.code]: Parser.start(),
@@ -443,7 +452,9 @@ export class InstrCodec implements Codec<Instr> {
443
452
  [LoadImmFieldByIndex.code]: Parser.start(),
444
453
  [PayGasFee.code]: Parser.start(),
445
454
  [MinimalContractDeposit.code]: Parser.start(),
446
- 0xd2: Parser.start().uint8('immFields').uint8('mutFields')
455
+ 0xd2: Parser.start().uint8('immFields').uint8('mutFields'),
456
+ 0xd3: Parser.start().int32('index'),
457
+ 0xd4: Parser.start().int32('index')
447
458
  }
448
459
  })
449
460
 
@@ -465,6 +476,8 @@ export class InstrCodec implements Codec<Instr> {
465
476
  } else if (instr.code === 0xd2) {
466
477
  const value = instrValue as CreateMapEntryValue
467
478
  result.push(value.immFields, value.mutFields)
479
+ } else if (instr.code === 0xd3 || instr.code === 0xd4) {
480
+ result.push(...signedIntCodec.encode((instrValue as InstrValueWithIndex).index))
468
481
  }
469
482
 
470
483
  return Buffer.from(result)
@@ -17,7 +17,7 @@ along with the library. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
  import { Buffer } from 'buffer/'
19
19
  import { Parser } from 'binary-parser'
20
- import { DecodedCompactInt, compactUnsignedIntCodec } from './compact-int-codec'
20
+ import { DecodedCompactInt, compactSignedIntCodec } from './compact-int-codec'
21
21
  import { Codec } from './codec'
22
22
  import { ArrayCodec, DecodedArray } from './array-codec'
23
23
 
@@ -41,7 +41,7 @@ const publicKeyHashCodec = new PublicKeyHashCodec()
41
41
  const publicKeyHashesCodec = new ArrayCodec(publicKeyHashCodec)
42
42
  const multiSigParser = Parser.start()
43
43
  .nest('publicKeyHashes', { type: publicKeyHashesCodec.parser })
44
- .nest('m', { type: compactUnsignedIntCodec.parser })
44
+ .nest('m', { type: compactSignedIntCodec.parser })
45
45
  export interface MultiSig {
46
46
  publicKeyHashes: DecodedArray<PublicKeyHash>
47
47
  m: DecodedCompactInt
@@ -79,7 +79,7 @@ export class LockupScriptCodec implements Codec<LockupScript> {
79
79
  result.push(...(input.script as PublicKeyHash).publicKeyHash)
80
80
  } else if (input.scriptType === 1) {
81
81
  result.push(...publicKeyHashesCodec.encode((input.script as MultiSig).publicKeyHashes.value))
82
- result.push(...compactUnsignedIntCodec.encode((input.script as MultiSig).m))
82
+ result.push(...compactSignedIntCodec.encode((input.script as MultiSig).m))
83
83
  } else if (input.scriptType === 2) {
84
84
  result.push(...(input.script as P2SH).scriptHash)
85
85
  } else if (input.scriptType === 3) {
@@ -33,13 +33,51 @@ export interface DecodedMethod {
33
33
 
34
34
  export interface Method {
35
35
  isPublic: boolean
36
- assetModifier: number
36
+ usePreapprovedAssets: boolean
37
+ useContractAssets: boolean
38
+ usePayToContractOnly: boolean
37
39
  argsLength: number
38
40
  localsLength: number
39
41
  returnLength: number
40
42
  instrs: Instr[]
41
43
  }
42
44
 
45
+ function decodeAssetModifier(encoded: number): {
46
+ usePreapprovedAssets: boolean
47
+ useContractAssets: boolean
48
+ usePayToContractOnly: boolean
49
+ } {
50
+ const usePayToContractOnly = (encoded & 4) !== 0
51
+ switch (encoded & 3) {
52
+ case 0:
53
+ return { usePayToContractOnly, usePreapprovedAssets: false, useContractAssets: false }
54
+ case 1:
55
+ return { usePayToContractOnly, usePreapprovedAssets: true, useContractAssets: true }
56
+ case 2:
57
+ return { usePayToContractOnly, usePreapprovedAssets: false, useContractAssets: true }
58
+ case 3:
59
+ return { usePayToContractOnly, usePreapprovedAssets: true, useContractAssets: false }
60
+ default:
61
+ throw new Error(`Invalid asset modifier: ${encoded}`)
62
+ }
63
+ }
64
+
65
+ function encodeAssetModifier(arg: {
66
+ usePreapprovedAssets: boolean
67
+ useContractAssets: boolean
68
+ usePayToContractOnly: boolean
69
+ }): number {
70
+ const encoded =
71
+ !arg.usePreapprovedAssets && !arg.useContractAssets
72
+ ? 0
73
+ : arg.usePreapprovedAssets && arg.useContractAssets
74
+ ? 1
75
+ : !arg.usePreapprovedAssets && arg.useContractAssets
76
+ ? 2
77
+ : 3
78
+ return encoded | (arg.usePayToContractOnly ? 4 : 0)
79
+ }
80
+
43
81
  export class MethodCodec implements Codec<DecodedMethod> {
44
82
  parser = Parser.start()
45
83
  .uint8('isPublic')
@@ -73,7 +111,7 @@ export class MethodCodec implements Codec<DecodedMethod> {
73
111
  static toMethod(decodedMethod: DecodedMethod): Method {
74
112
  return {
75
113
  isPublic: decodedMethod.isPublic === 1,
76
- assetModifier: decodedMethod.assetModifier,
114
+ ...decodeAssetModifier(decodedMethod.assetModifier),
77
115
  argsLength: compactUnsignedIntCodec.toU32(decodedMethod.argsLength),
78
116
  localsLength: compactUnsignedIntCodec.toU32(decodedMethod.localsLength),
79
117
  returnLength: compactUnsignedIntCodec.toU32(decodedMethod.returnLength),
@@ -84,7 +122,7 @@ export class MethodCodec implements Codec<DecodedMethod> {
84
122
  static fromMethod(method: Method): DecodedMethod {
85
123
  return {
86
124
  isPublic: method.isPublic ? 1 : 0,
87
- assetModifier: method.assetModifier,
125
+ assetModifier: encodeAssetModifier(method),
88
126
  argsLength: compactUnsignedIntCodec.fromU32(method.argsLength),
89
127
  localsLength: compactUnsignedIntCodec.fromU32(method.localsLength),
90
128
  returnLength: compactUnsignedIntCodec.fromU32(method.returnLength),