@alephium/web3 0.40.0 → 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.
- package/dist/alephium-web3.min.js +1 -1
- package/dist/alephium-web3.min.js.map +1 -1
- package/dist/src/api/node-provider.d.ts +2 -0
- package/dist/src/api/node-provider.js +12 -6
- package/dist/src/api/utils.d.ts +1 -1
- package/dist/src/block/block.d.ts +28 -0
- package/dist/src/block/block.js +131 -0
- package/dist/src/block/index.d.ts +1 -0
- package/dist/src/block/index.js +22 -0
- package/dist/src/codec/contract-output-codec.js +4 -4
- package/dist/src/codec/lockup-script-codec.js +2 -2
- package/dist/src/codec/method-codec.d.ts +3 -1
- package/dist/src/codec/method-codec.js +27 -2
- package/dist/src/codec/script-codec.d.ts +11 -6
- package/dist/src/codec/script-codec.js +13 -2
- package/dist/src/codec/transaction-codec.js +2 -2
- package/dist/src/codec/unlock-script-codec.d.ts +2 -2
- package/dist/src/codec/unsigned-tx-codec.d.ts +2 -2
- package/dist/src/contract/contract.d.ts +19 -10
- package/dist/src/contract/contract.js +103 -56
- package/dist/src/contract/events.d.ts +1 -2
- package/dist/src/contract/events.js +28 -14
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/signer/tx-builder.js +4 -4
- package/dist/src/transaction/status.js +28 -4
- package/dist/src/utils/address.js +29 -16
- package/dist/src/utils/exchange.js +25 -15
- package/dist/src/utils/number.d.ts +1 -1
- package/dist/src/utils/sign.js +6 -6
- package/dist/src/utils/subscription.d.ts +4 -4
- package/dist/src/utils/subscription.js +1 -1
- package/package.json +3 -3
- package/src/api/node-provider.ts +8 -1
- package/src/api/utils.ts +1 -1
- package/src/block/block.ts +139 -0
- package/src/block/index.ts +19 -0
- package/src/codec/contract-output-codec.ts +1 -1
- package/src/codec/lockup-script-codec.ts +3 -3
- package/src/codec/method-codec.ts +41 -3
- package/src/codec/script-codec.ts +23 -5
- package/src/codec/transaction-codec.ts +1 -1
- package/src/codec/unlock-script-codec.ts +2 -2
- package/src/codec/unsigned-tx-codec.ts +2 -2
- package/src/contract/contract.ts +139 -78
- package/src/contract/events.ts +6 -18
- package/src/index.ts +1 -0
- package/src/signer/tx-builder.ts +2 -2
- package/src/transaction/status.ts +4 -4
- package/src/utils/address.ts +15 -2
- package/src/utils/exchange.ts +32 -10
- package/src/utils/number.ts +1 -1
- package/src/utils/sign.ts +1 -1
- package/src/utils/subscription.ts +4 -4
- package/std/fungible_token_interface.ral +1 -0
- package/std/nft_collection_interface.ral +1 -0
- package/std/nft_collection_with_royalty_interface.ral +1 -0
- package/std/nft_interface.ral +1 -0
|
@@ -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
|
|
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 =
|
|
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 !==
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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) {
|
package/dist/src/utils/sign.js
CHANGED
|
@@ -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
|
|
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,
|
|
66
|
+
return (0, utils_1.encodeSignature)(signature);
|
|
67
67
|
}
|
|
68
68
|
else {
|
|
69
|
-
const signature = necc.schnorr.signSync((0,
|
|
70
|
-
return (0,
|
|
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,
|
|
79
|
+
return key.verify(hash, (0, utils_1.signatureDecode)(ec, signature));
|
|
80
80
|
}
|
|
81
81
|
else {
|
|
82
|
-
return necc.schnorr.verifySync((0,
|
|
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
|
-
|
|
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 {};
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alephium/web3",
|
|
3
|
-
"version": "0.
|
|
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
|
-
"
|
|
11
|
-
"
|
|
10
|
+
"node": "./dist/src/index.js",
|
|
11
|
+
"default": "./dist/alephium-web3.min.js"
|
|
12
12
|
},
|
|
13
13
|
"typesVersions": {
|
|
14
14
|
"*": {
|
package/src/api/node-provider.ts
CHANGED
|
@@ -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
|
|
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
|
|
|
@@ -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,
|
|
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:
|
|
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(...
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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),
|
|
@@ -15,30 +15,48 @@ GNU Lesser General Public License for more details.
|
|
|
15
15
|
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 { Buffer } from 'buffer/'
|
|
19
20
|
import { Parser } from 'binary-parser'
|
|
20
21
|
import { DecodedArray } from './array-codec'
|
|
21
22
|
import { Codec } from './codec'
|
|
22
|
-
import { DecodedMethod, methodsCodec } from './method-codec'
|
|
23
|
+
import { DecodedMethod, methodsCodec, Method, MethodCodec } from './method-codec'
|
|
23
24
|
import { OptionCodec } from './option-codec'
|
|
25
|
+
import { compactUnsignedIntCodec } from './compact-int-codec'
|
|
24
26
|
|
|
25
|
-
export interface
|
|
27
|
+
export interface DecodedScript {
|
|
26
28
|
methods: DecodedArray<DecodedMethod>
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
export
|
|
31
|
+
export interface Script {
|
|
32
|
+
methods: Method[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class ScriptCodec implements Codec<DecodedScript> {
|
|
30
36
|
parser = Parser.start().nest('methods', {
|
|
31
37
|
type: methodsCodec.parser
|
|
32
38
|
})
|
|
33
39
|
|
|
34
|
-
encode(input:
|
|
40
|
+
encode(input: DecodedScript): Buffer {
|
|
35
41
|
const script = methodsCodec.encode(input.methods.value)
|
|
36
42
|
return Buffer.from(script)
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
decode(input: Buffer):
|
|
45
|
+
decode(input: Buffer): DecodedScript {
|
|
40
46
|
return this.parser.parse(input)
|
|
41
47
|
}
|
|
48
|
+
|
|
49
|
+
decodeScript(input: Buffer): Script {
|
|
50
|
+
const decodedTxScript = this.decode(input)
|
|
51
|
+
const methods = decodedTxScript.methods.value.map((decodedMethod) => MethodCodec.toMethod(decodedMethod))
|
|
52
|
+
return { methods }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
encodeScript(inputTxScript: Script): Buffer {
|
|
56
|
+
const methodLength = compactUnsignedIntCodec.fromU32(inputTxScript.methods.length)
|
|
57
|
+
const decodedMethods = inputTxScript.methods.map((method) => MethodCodec.fromMethod(method))
|
|
58
|
+
return this.encode({ methods: { value: decodedMethods, length: methodLength } })
|
|
59
|
+
}
|
|
42
60
|
}
|
|
43
61
|
|
|
44
62
|
export const scriptCodec = new ScriptCodec()
|
|
@@ -27,7 +27,7 @@ import { Either } from './either-codec'
|
|
|
27
27
|
import { AssetOutput, AssetOutputCodec } from './asset-output-codec'
|
|
28
28
|
import { ContractOutput, ContractOutputCodec } from './contract-output-codec'
|
|
29
29
|
import { FixedAssetOutput, Transaction as ApiTransaction } from '../api/api-alephium'
|
|
30
|
-
import { hexToBinUnsafe } from '
|
|
30
|
+
import { hexToBinUnsafe } from '../utils'
|
|
31
31
|
import { ContractOutput as ApiContractOutput } from '../api/api-alephium'
|
|
32
32
|
import { Codec } from './codec'
|
|
33
33
|
import { Output, outputCodec, outputsCodec } from './output-codec'
|
|
@@ -20,7 +20,7 @@ import { Parser } from 'binary-parser'
|
|
|
20
20
|
import { ArrayCodec, DecodedArray } from './array-codec'
|
|
21
21
|
import { compactUnsignedIntCodec, compactSignedIntCodec, DecodedCompactInt } from './compact-int-codec'
|
|
22
22
|
import { Codec } from './codec'
|
|
23
|
-
import { Script, scriptCodec } from './script-codec'
|
|
23
|
+
import { DecodedScript, Script, scriptCodec } from './script-codec'
|
|
24
24
|
import { ByteString, byteStringCodec } from './bytestring-codec'
|
|
25
25
|
import { LockupScript, lockupScriptCodec } from './lockup-script-codec'
|
|
26
26
|
|
|
@@ -122,7 +122,7 @@ const valCodec = new ValCodec()
|
|
|
122
122
|
const valsCodec = new ArrayCodec(valCodec)
|
|
123
123
|
|
|
124
124
|
export interface P2SH {
|
|
125
|
-
script:
|
|
125
|
+
script: DecodedScript
|
|
126
126
|
params: DecodedArray<Val>
|
|
127
127
|
}
|
|
128
128
|
|
|
@@ -19,7 +19,7 @@ import { Buffer } from 'buffer/'
|
|
|
19
19
|
import { Parser } from 'binary-parser'
|
|
20
20
|
import { UnsignedTx as ApiUnsignedTx } from '../api/api-alephium'
|
|
21
21
|
import { binToHex, hexToBinUnsafe } from '../utils'
|
|
22
|
-
import {
|
|
22
|
+
import { DecodedScript, scriptCodec, statefulScriptCodecOpt } from './script-codec'
|
|
23
23
|
import { Option } from './option-codec'
|
|
24
24
|
import { DecodedCompactInt, compactSignedIntCodec, compactUnsignedIntCodec } from './compact-int-codec'
|
|
25
25
|
import { Input, InputCodec, inputsCodec } from './input-codec'
|
|
@@ -31,7 +31,7 @@ import { Codec } from './codec'
|
|
|
31
31
|
export interface UnsignedTx {
|
|
32
32
|
version: number
|
|
33
33
|
networkId: number
|
|
34
|
-
statefulScript: Option<
|
|
34
|
+
statefulScript: Option<DecodedScript>
|
|
35
35
|
gasAmount: DecodedCompactInt
|
|
36
36
|
gasPrice: DecodedCompactInt
|
|
37
37
|
inputs: DecodedArray<Input>
|