@alephium/web3 1.11.6 → 1.12.0-beta.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/address/address.d.ts +11 -1
- package/dist/src/address/address.js +117 -11
- package/dist/src/api/api-alephium.d.ts +80 -0
- package/dist/src/api/api-alephium.js +50 -0
- package/dist/src/api/node-provider.d.ts +2 -0
- package/dist/src/api/node-provider.js +1 -0
- package/dist/src/api/types.d.ts +1 -1
- package/dist/src/api/types.js +10 -5
- package/dist/src/codec/lockup-script-codec.d.ts +11 -2
- package/dist/src/codec/lockup-script-codec.js +8 -1
- package/dist/src/codec/unlock-script-codec.d.ts +11 -3
- package/dist/src/codec/unlock-script-codec.js +14 -4
- package/dist/src/contract/contract.d.ts +3 -3
- package/dist/src/contract/contract.js +12 -5
- package/dist/src/contract/ralph.js +2 -1
- package/dist/src/signer/tx-builder.d.ts +7 -1
- package/dist/src/signer/tx-builder.js +67 -0
- package/dist/src/signer/types.d.ts +27 -1
- package/dist/src/signer/types.js +3 -0
- package/dist/src/utils/sign.js +2 -2
- package/dist/src/utils/webcrypto.d.ts +3 -1
- package/package.json +1 -1
- package/src/address/address.ts +120 -12
- package/src/api/api-alephium.ts +134 -14
- package/src/api/node-provider.ts +3 -0
- package/src/api/types.ts +10 -5
- package/src/codec/lockup-script-codec.ts +19 -2
- package/src/codec/unlock-script-codec.ts +23 -8
- package/src/contract/contract.ts +33 -10
- package/src/contract/ralph.ts +5 -2
- package/src/signer/tx-builder.ts +88 -1
- package/src/signer/types.ts +34 -1
- package/src/utils/sign.ts +2 -2
|
@@ -23,6 +23,7 @@ const utils_1 = require("../utils");
|
|
|
23
23
|
const codec_1 = require("../codec");
|
|
24
24
|
const codec_2 = require("../codec/codec");
|
|
25
25
|
const error_1 = require("../error");
|
|
26
|
+
const address_1 = require("../address");
|
|
26
27
|
function encodeByteVec(hex) {
|
|
27
28
|
if (!(0, utils_1.isHexString)(hex)) {
|
|
28
29
|
throw Error(`Given value ${hex} is not a valid hex string`);
|
|
@@ -32,7 +33,7 @@ function encodeByteVec(hex) {
|
|
|
32
33
|
}
|
|
33
34
|
exports.encodeByteVec = encodeByteVec;
|
|
34
35
|
function encodeAddress(address) {
|
|
35
|
-
return
|
|
36
|
+
return (0, address_1.addressToBytes)(address);
|
|
36
37
|
}
|
|
37
38
|
exports.encodeAddress = encodeAddress;
|
|
38
39
|
var VmValType;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NodeProvider } from '../api';
|
|
2
|
-
import { SignChainedTxParams, SignChainedTxResult, SignDeployContractTxParams, SignDeployContractTxResult, SignExecuteScriptTxParams, SignExecuteScriptTxResult, SignTransferTxParams, SignTransferTxResult, SignUnsignedTxParams, SignUnsignedTxResult } from './types';
|
|
2
|
+
import { SignChainedTxParams, SignChainedTxResult, SignDeployContractTxParams, SignDeployContractTxResult, SignExecuteScriptTxParams, SignExecuteScriptTxResult, SignTransferTxParams, SignTransferTxResult, SignUnsignedTxParams, SignUnsignedTxResult, SignGrouplessTransferTxParams, SignGrouplessDeployContractTxParams, SignGrouplessExecuteScriptTxParams } from './types';
|
|
3
3
|
export declare abstract class TransactionBuilder {
|
|
4
4
|
abstract get nodeProvider(): NodeProvider;
|
|
5
5
|
static from(nodeProvider: NodeProvider): TransactionBuilder;
|
|
@@ -9,8 +9,14 @@ export declare abstract class TransactionBuilder {
|
|
|
9
9
|
buildDeployContractTx(params: SignDeployContractTxParams, publicKey: string): Promise<Omit<SignDeployContractTxResult, 'signature'>>;
|
|
10
10
|
buildExecuteScriptTx(params: SignExecuteScriptTxParams, publicKey: string): Promise<Omit<SignExecuteScriptTxResult, 'signature'>>;
|
|
11
11
|
buildChainedTx(params: SignChainedTxParams[], publicKeys: string[]): Promise<Omit<SignChainedTxResult, 'signature'>[]>;
|
|
12
|
+
buildGrouplessTransferTx(params: SignGrouplessTransferTxParams): Promise<Omit<SignChainedTxResult, 'signature'>[]>;
|
|
13
|
+
buildGrouplessDeployContractTx(params: SignGrouplessDeployContractTxParams): Promise<Omit<SignChainedTxResult, 'signature'>[]>;
|
|
14
|
+
buildGrouplessExecuteScriptTx(params: SignGrouplessExecuteScriptTxParams): Promise<Omit<SignChainedTxResult, 'signature'>[]>;
|
|
12
15
|
static buildUnsignedTx(params: SignUnsignedTxParams): Omit<SignUnsignedTxResult, 'signature'>;
|
|
13
16
|
private buildTransferTxParams;
|
|
17
|
+
private buildGrouplessTransferTxParams;
|
|
18
|
+
private buildGrouplessDeployContractTxParams;
|
|
19
|
+
private buildGrouplessExecuteScriptTxParams;
|
|
14
20
|
private buildDeployContractTxParams;
|
|
15
21
|
private buildExecuteScriptTxParams;
|
|
16
22
|
private convertTransferTxResult;
|
|
@@ -109,6 +109,42 @@ class TransactionBuilder {
|
|
|
109
109
|
});
|
|
110
110
|
return results;
|
|
111
111
|
}
|
|
112
|
+
async buildGrouplessTransferTx(params) {
|
|
113
|
+
const data = this.buildGrouplessTransferTxParams(params);
|
|
114
|
+
const response = await this.nodeProvider.groupless.postGrouplessTransfer(data);
|
|
115
|
+
return response.map((result) => {
|
|
116
|
+
return {
|
|
117
|
+
...this.convertTransferTxResult(result),
|
|
118
|
+
type: 'Transfer'
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
async buildGrouplessDeployContractTx(params) {
|
|
123
|
+
const data = this.buildGrouplessDeployContractTxParams(params);
|
|
124
|
+
const response = await this.nodeProvider.groupless.postGrouplessDeployContract(data);
|
|
125
|
+
const transferTxs = response.transferTxs.map((result) => ({
|
|
126
|
+
...this.convertTransferTxResult(result),
|
|
127
|
+
type: 'Transfer'
|
|
128
|
+
}));
|
|
129
|
+
const deployContractTx = {
|
|
130
|
+
...this.convertDeployContractTxResult(response.deployContractTx),
|
|
131
|
+
type: 'DeployContract'
|
|
132
|
+
};
|
|
133
|
+
return [...transferTxs, deployContractTx];
|
|
134
|
+
}
|
|
135
|
+
async buildGrouplessExecuteScriptTx(params) {
|
|
136
|
+
const data = this.buildGrouplessExecuteScriptTxParams(params);
|
|
137
|
+
const response = await this.nodeProvider.groupless.postGrouplessExecuteScript(data);
|
|
138
|
+
const transferTxs = response.transferTxs.map((result) => ({
|
|
139
|
+
...this.convertTransferTxResult(result),
|
|
140
|
+
type: 'Transfer'
|
|
141
|
+
}));
|
|
142
|
+
const executeScriptTx = {
|
|
143
|
+
...this.convertExecuteScriptTxResult(response.executeScriptTx),
|
|
144
|
+
type: 'ExecuteScript'
|
|
145
|
+
};
|
|
146
|
+
return [...transferTxs, executeScriptTx];
|
|
147
|
+
}
|
|
112
148
|
static buildUnsignedTx(params) {
|
|
113
149
|
const unsignedTxBin = (0, utils_1.hexToBinUnsafe)(params.unsignedTx);
|
|
114
150
|
const decoded = codec_1.unsignedTxCodec.decode(unsignedTxBin);
|
|
@@ -134,6 +170,37 @@ class TransactionBuilder {
|
|
|
134
170
|
...rest
|
|
135
171
|
};
|
|
136
172
|
}
|
|
173
|
+
buildGrouplessTransferTxParams(params) {
|
|
174
|
+
return {
|
|
175
|
+
fromAddress: params.fromAddress,
|
|
176
|
+
destinations: (0, signer_1.toApiDestinations)(params.destinations),
|
|
177
|
+
gasPrice: (0, api_1.toApiNumber256Optional)(params.gasPrice),
|
|
178
|
+
targetBlockHash: params.targetBlockHash
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
buildGrouplessDeployContractTxParams(params) {
|
|
182
|
+
return {
|
|
183
|
+
fromAddress: params.fromAddress,
|
|
184
|
+
bytecode: params.bytecode,
|
|
185
|
+
initialAttoAlphAmount: (0, api_1.toApiNumber256Optional)(params.initialAttoAlphAmount),
|
|
186
|
+
initialTokenAmounts: (0, api_1.toApiTokens)(params.initialTokenAmounts),
|
|
187
|
+
issueTokenAmount: (0, api_1.toApiNumber256Optional)(params.issueTokenAmount),
|
|
188
|
+
issueTokenTo: params.issueTokenTo,
|
|
189
|
+
gasPrice: (0, api_1.toApiNumber256Optional)(params.gasPrice),
|
|
190
|
+
targetBlockHash: params.targetBlockHash
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
buildGrouplessExecuteScriptTxParams(params) {
|
|
194
|
+
return {
|
|
195
|
+
fromAddress: params.fromAddress,
|
|
196
|
+
bytecode: params.bytecode,
|
|
197
|
+
attoAlphAmount: (0, api_1.toApiNumber256Optional)(params.attoAlphAmount),
|
|
198
|
+
tokens: (0, api_1.toApiTokens)(params.tokens),
|
|
199
|
+
gasPrice: (0, api_1.toApiNumber256Optional)(params.gasPrice),
|
|
200
|
+
targetBlockHash: params.targetBlockHash,
|
|
201
|
+
gasEstimationMultiplier: params.gasEstimationMultiplier
|
|
202
|
+
};
|
|
203
|
+
}
|
|
137
204
|
buildDeployContractTxParams(params, publicKey) {
|
|
138
205
|
TransactionBuilder.validatePublicKey(params, publicKey, params.signerKeyType);
|
|
139
206
|
const { initialAttoAlphAmount, initialTokenAmounts, issueTokenAmount, gasPrice, ...rest } = params;
|
|
@@ -11,7 +11,7 @@ export interface Destination {
|
|
|
11
11
|
lockTime?: number;
|
|
12
12
|
message?: string;
|
|
13
13
|
}
|
|
14
|
-
export type KeyType = 'default' | 'bip340-schnorr';
|
|
14
|
+
export type KeyType = 'default' | 'bip340-schnorr' | 'groupless';
|
|
15
15
|
export interface Account {
|
|
16
16
|
keyType: KeyType;
|
|
17
17
|
address: string;
|
|
@@ -113,6 +113,32 @@ export type SignExecuteScriptChainedTxResult = SignExecuteScriptTxResult & {
|
|
|
113
113
|
type: 'ExecuteScript';
|
|
114
114
|
};
|
|
115
115
|
export type SignChainedTxResult = SignTransferChainedTxResult | SignDeployContractChainedTxResult | SignExecuteScriptChainedTxResult;
|
|
116
|
+
export interface SignGrouplessTransferTxParams {
|
|
117
|
+
fromAddress: string;
|
|
118
|
+
destinations: Destination[];
|
|
119
|
+
gasPrice?: Number256;
|
|
120
|
+
targetBlockHash?: string;
|
|
121
|
+
}
|
|
122
|
+
export interface SignGrouplessDeployContractTxParams {
|
|
123
|
+
fromAddress: string;
|
|
124
|
+
bytecode: string;
|
|
125
|
+
initialAttoAlphAmount?: Number256;
|
|
126
|
+
initialTokenAmounts?: Token[];
|
|
127
|
+
issueTokenAmount?: Number256;
|
|
128
|
+
issueTokenTo?: string;
|
|
129
|
+
gasPrice?: Number256;
|
|
130
|
+
targetBlockHash?: string;
|
|
131
|
+
}
|
|
132
|
+
export interface SignGrouplessExecuteScriptTxParams {
|
|
133
|
+
fromAddress: string;
|
|
134
|
+
bytecode: string;
|
|
135
|
+
attoAlphAmount?: Number256;
|
|
136
|
+
tokens?: Token[];
|
|
137
|
+
gasPrice?: Number256;
|
|
138
|
+
targetBlockHash?: string;
|
|
139
|
+
gasEstimationMultiplier?: number;
|
|
140
|
+
}
|
|
141
|
+
export type SignGrouplessTxParams = SignGrouplessTransferTxParams | SignGrouplessDeployContractTxParams | SignGrouplessExecuteScriptTxParams;
|
|
116
142
|
export type MessageHasher = 'alephium' | 'sha256' | 'blake2b' | 'identity';
|
|
117
143
|
export interface SignMessageParams {
|
|
118
144
|
signerAddress: string;
|
package/dist/src/signer/types.js
CHANGED
package/dist/src/utils/sign.js
CHANGED
|
@@ -60,7 +60,7 @@ necc.utils.hmacSha256Sync = (key, ...messages) => {
|
|
|
60
60
|
// hash has to be 32 bytes
|
|
61
61
|
function sign(hash, privateKey, _keyType) {
|
|
62
62
|
const keyType = _keyType ?? 'default';
|
|
63
|
-
if (keyType === 'default') {
|
|
63
|
+
if (keyType === 'default' || keyType === 'groupless') {
|
|
64
64
|
const key = ec.keyFromPrivate(privateKey);
|
|
65
65
|
const signature = key.sign(hash);
|
|
66
66
|
return (0, utils_1.encodeSignature)(signature);
|
|
@@ -74,7 +74,7 @@ exports.sign = sign;
|
|
|
74
74
|
function verifySignature(hash, publicKey, signature, _keyType) {
|
|
75
75
|
const keyType = _keyType ?? 'default';
|
|
76
76
|
try {
|
|
77
|
-
if (keyType === 'default') {
|
|
77
|
+
if (keyType === 'default' || keyType === 'groupless') {
|
|
78
78
|
const key = ec.keyFromPublic(publicKey, 'hex');
|
|
79
79
|
return key.verify(hash, (0, utils_1.signatureDecode)(ec, signature));
|
|
80
80
|
}
|
package/package.json
CHANGED
package/src/address/address.ts
CHANGED
|
@@ -24,10 +24,11 @@ import bs58, { base58ToBytes } from '../utils/bs58'
|
|
|
24
24
|
import { binToHex, concatBytes, hexToBinUnsafe, isHexString, xorByte } from '../utils'
|
|
25
25
|
import { KeyType } from '../signer'
|
|
26
26
|
import { P2MPKH, lockupScriptCodec } from '../codec/lockup-script-codec'
|
|
27
|
-
import { i32Codec } from '../codec'
|
|
27
|
+
import { i32Codec, intAs4BytesCodec } from '../codec'
|
|
28
28
|
import { LockupScript } from '../codec/lockup-script-codec'
|
|
29
29
|
import djb2 from '../utils/djb2'
|
|
30
30
|
import { TraceableError } from '../error'
|
|
31
|
+
import { byteCodec } from '../codec/codec'
|
|
31
32
|
|
|
32
33
|
const ec = new EC('secp256k1')
|
|
33
34
|
const PublicKeyHashSize = 32
|
|
@@ -36,7 +37,8 @@ export enum AddressType {
|
|
|
36
37
|
P2PKH = 0x00,
|
|
37
38
|
P2MPKH = 0x01,
|
|
38
39
|
P2SH = 0x02,
|
|
39
|
-
P2C = 0x03
|
|
40
|
+
P2C = 0x03,
|
|
41
|
+
P2PK = 0x04
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
export function validateAddress(address: string) {
|
|
@@ -53,7 +55,7 @@ export function isValidAddress(address: string): boolean {
|
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
function decodeAndValidateAddress(address: string): Uint8Array {
|
|
56
|
-
const decoded =
|
|
58
|
+
const decoded = addressToBytes(address)
|
|
57
59
|
if (decoded.length === 0) throw new Error('Address is empty')
|
|
58
60
|
const addressType = decoded[0]
|
|
59
61
|
if (addressType === AddressType.P2MPKH) {
|
|
@@ -75,14 +77,77 @@ function decodeAndValidateAddress(address: string): Uint8Array {
|
|
|
75
77
|
} else if (addressType === AddressType.P2PKH || addressType === AddressType.P2SH || addressType === AddressType.P2C) {
|
|
76
78
|
// [type, ...hash]
|
|
77
79
|
if (decoded.length === 33) return decoded
|
|
80
|
+
} else if (isGrouplessAddressWithGroup(decoded)) {
|
|
81
|
+
// [type, keyType, ...publicKey, ...checkSum, ...groupByte]
|
|
82
|
+
const publicKeyToIndex = decoded.length - 1 - 4
|
|
83
|
+
const publicKeyLikeBytes = decoded.slice(1, publicKeyToIndex)
|
|
84
|
+
const checksum = binToHex(decoded.slice(publicKeyToIndex, publicKeyToIndex + 4))
|
|
85
|
+
const expectedChecksum = binToHex(intAs4BytesCodec.encode(djb2(publicKeyLikeBytes)))
|
|
86
|
+
if (checksum !== expectedChecksum) {
|
|
87
|
+
throw new Error(`Invalid checksum for P2PK address: ${address}`)
|
|
88
|
+
}
|
|
89
|
+
const group = byteCodec.decode(decoded.slice(decoded.length - 1, decoded.length))
|
|
90
|
+
validateGroupIndex(group)
|
|
91
|
+
|
|
92
|
+
return decoded
|
|
78
93
|
}
|
|
79
94
|
|
|
80
95
|
throw new Error(`Invalid address: ${address}`)
|
|
81
96
|
}
|
|
82
97
|
|
|
98
|
+
function isGrouplessAddressWithoutGroup(decoded: Uint8Array): boolean {
|
|
99
|
+
return decoded[0] === AddressType.P2PK && (decoded.length === 38 || decoded.length === 39)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function isGrouplessAddressWithGroup(decoded: Uint8Array): boolean {
|
|
103
|
+
return decoded[0] === AddressType.P2PK && (decoded.length === 39 || decoded.length === 40)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function addressToBytes(address: string): Uint8Array {
|
|
107
|
+
if (hasExplicitGroupIndex(address)) {
|
|
108
|
+
const groupIndex = parseGroupIndex(address[address.length - 1])
|
|
109
|
+
const decoded = base58ToBytes(address.slice(0, address.length - 2))
|
|
110
|
+
if (isGrouplessAddressWithoutGroup(decoded)) {
|
|
111
|
+
const groupByte = byteCodec.encode(groupIndex)
|
|
112
|
+
return new Uint8Array([...decoded, ...groupByte])
|
|
113
|
+
}
|
|
114
|
+
throw new Error(`Invalid groupless address: ${address}`)
|
|
115
|
+
} else {
|
|
116
|
+
const decoded = base58ToBytes(address)
|
|
117
|
+
if (isGrouplessAddressWithoutGroup(decoded)) {
|
|
118
|
+
const group = defaultGroupOfGrouplessAddress(decoded.slice(2, decoded.length - 4))
|
|
119
|
+
const groupByte = byteCodec.encode(group)
|
|
120
|
+
return new Uint8Array([...decoded, ...groupByte])
|
|
121
|
+
}
|
|
122
|
+
return decoded
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
83
126
|
export function isAssetAddress(address: string) {
|
|
84
127
|
const addressType = decodeAndValidateAddress(address)[0]
|
|
85
|
-
return
|
|
128
|
+
return (
|
|
129
|
+
addressType === AddressType.P2PKH ||
|
|
130
|
+
addressType === AddressType.P2MPKH ||
|
|
131
|
+
addressType === AddressType.P2SH ||
|
|
132
|
+
addressType === AddressType.P2PK
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function isGrouplessAddress(address: string) {
|
|
137
|
+
const addressType = decodeAndValidateAddress(address)[0]
|
|
138
|
+
return addressType === AddressType.P2PK
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function isGrouplessAddressWithoutGroupIndex(address: string) {
|
|
142
|
+
return !hasExplicitGroupIndex(address) && isGrouplessAddress(address)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function isGrouplessAddressWithGroupIndex(address: string) {
|
|
146
|
+
return hasExplicitGroupIndex(address) && isGrouplessAddress(address)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function defaultGroupOfGrouplessAddress(pubKey: Uint8Array): number {
|
|
150
|
+
return pubKey[pubKey.length - 1] & 0xff % TOTAL_NUMBER_OF_GROUPS
|
|
86
151
|
}
|
|
87
152
|
|
|
88
153
|
export function isContractAddress(address: string) {
|
|
@@ -101,6 +166,8 @@ export function groupOfAddress(address: string): number {
|
|
|
101
166
|
return groupOfP2mpkhAddress(addressBody)
|
|
102
167
|
} else if (addressType == AddressType.P2SH) {
|
|
103
168
|
return groupOfP2shAddress(addressBody)
|
|
169
|
+
} else if (addressType == AddressType.P2PK) {
|
|
170
|
+
return groupOfP2pkAddress(addressBody)
|
|
104
171
|
} else {
|
|
105
172
|
// Contract Address
|
|
106
173
|
const id = contractIdFromAddress(address)
|
|
@@ -110,17 +177,21 @@ export function groupOfAddress(address: string): number {
|
|
|
110
177
|
|
|
111
178
|
// Pay to public key hash address
|
|
112
179
|
function groupOfP2pkhAddress(address: Uint8Array): number {
|
|
113
|
-
return
|
|
180
|
+
return groupFromBytes(address)
|
|
114
181
|
}
|
|
115
182
|
|
|
116
183
|
// Pay to multiple public key hash address
|
|
117
184
|
function groupOfP2mpkhAddress(address: Uint8Array): number {
|
|
118
|
-
return
|
|
185
|
+
return groupFromBytes(address.slice(1, 33))
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function groupOfP2pkAddress(address: Uint8Array): number {
|
|
189
|
+
return byteCodec.decode(address.slice(38, 39)) % TOTAL_NUMBER_OF_GROUPS
|
|
119
190
|
}
|
|
120
191
|
|
|
121
192
|
// Pay to script hash address
|
|
122
193
|
function groupOfP2shAddress(address: Uint8Array): number {
|
|
123
|
-
return
|
|
194
|
+
return groupFromBytes(address)
|
|
124
195
|
}
|
|
125
196
|
|
|
126
197
|
export function contractIdFromAddress(address: string): Uint8Array {
|
|
@@ -151,7 +222,7 @@ export function groupOfPrivateKey(privateKey: string, keyType?: KeyType): number
|
|
|
151
222
|
export function publicKeyFromPrivateKey(privateKey: string, _keyType?: KeyType): string {
|
|
152
223
|
const keyType = _keyType ?? 'default'
|
|
153
224
|
|
|
154
|
-
if (keyType === 'default') {
|
|
225
|
+
if (keyType === 'default' || keyType === 'groupless') {
|
|
155
226
|
const key = ec.keyFromPrivate(privateKey)
|
|
156
227
|
return key.getPublic(true, 'hex')
|
|
157
228
|
} else {
|
|
@@ -166,6 +237,11 @@ export function addressFromPublicKey(publicKey: string, _keyType?: KeyType): str
|
|
|
166
237
|
const hash = blake.blake2b(hexToBinUnsafe(publicKey), undefined, 32)
|
|
167
238
|
const bytes = new Uint8Array([AddressType.P2PKH, ...hash])
|
|
168
239
|
return bs58.encode(bytes)
|
|
240
|
+
} else if (keyType === 'groupless') {
|
|
241
|
+
const publicKeyBytes = new Uint8Array([0x00, ...hexToBinUnsafe(publicKey)])
|
|
242
|
+
const hashBytes = intAs4BytesCodec.encode(djb2(publicKeyBytes))
|
|
243
|
+
const bytes = new Uint8Array([0x04, ...publicKeyBytes, ...hashBytes])
|
|
244
|
+
return bs58.encode(bytes)
|
|
169
245
|
} else {
|
|
170
246
|
const lockupScript = hexToBinUnsafe(`0101000000000458144020${publicKey}8685`)
|
|
171
247
|
return addressFromScript(lockupScript)
|
|
@@ -215,11 +291,13 @@ export function subContractId(parentContractId: string, pathInHex: string, group
|
|
|
215
291
|
|
|
216
292
|
export function groupOfLockupScript(lockupScript: LockupScript): number {
|
|
217
293
|
if (lockupScript.kind === 'P2PKH') {
|
|
218
|
-
return
|
|
294
|
+
return groupFromBytes(lockupScript.value)
|
|
219
295
|
} else if (lockupScript.kind === 'P2MPKH') {
|
|
220
|
-
return
|
|
296
|
+
return groupFromBytes(lockupScript.value.publicKeyHashes[0])
|
|
221
297
|
} else if (lockupScript.kind === 'P2SH') {
|
|
222
|
-
return
|
|
298
|
+
return groupFromBytes(lockupScript.value)
|
|
299
|
+
} else if (lockupScript.kind === 'P2PK') {
|
|
300
|
+
return lockupScript.value.group % TOTAL_NUMBER_OF_GROUPS
|
|
223
301
|
} else {
|
|
224
302
|
// P2C
|
|
225
303
|
const contractId = lockupScript.value
|
|
@@ -227,8 +305,38 @@ export function groupOfLockupScript(lockupScript: LockupScript): number {
|
|
|
227
305
|
}
|
|
228
306
|
}
|
|
229
307
|
|
|
230
|
-
function
|
|
308
|
+
export function groupFromBytes(bytes: Uint8Array): number {
|
|
231
309
|
const hint = djb2(bytes) | 1
|
|
310
|
+
return groupFromHint(hint)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function groupFromHint(hint: number): number {
|
|
232
314
|
const hash = xorByte(hint)
|
|
233
315
|
return hash % TOTAL_NUMBER_OF_GROUPS
|
|
234
316
|
}
|
|
317
|
+
|
|
318
|
+
export function hasExplicitGroupIndex(address: string): boolean {
|
|
319
|
+
return address.length > 2 && address[address.length - 2] === ':'
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export function addressFromLockupScript(lockupScript: LockupScript): string {
|
|
323
|
+
if (lockupScript.kind === 'P2PK') {
|
|
324
|
+
const groupByte = lockupScriptCodec.encode(lockupScript).slice(-1)
|
|
325
|
+
const address = bs58.encode(lockupScriptCodec.encode(lockupScript).slice(0, -1))
|
|
326
|
+
return `${address}:${groupByte}`
|
|
327
|
+
} else {
|
|
328
|
+
return bs58.encode(lockupScriptCodec.encode(lockupScript))
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function parseGroupIndex(groupIndexStr: string): number {
|
|
333
|
+
return validateGroupIndex(parseInt(groupIndexStr), groupIndexStr)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function validateGroupIndex(groupIndex: number, groupIndexStr?: string): number {
|
|
337
|
+
if (isNaN(groupIndex) || groupIndex < 0 || groupIndex >= TOTAL_NUMBER_OF_GROUPS) {
|
|
338
|
+
throw new Error(`Invalid group index: ${groupIndexStr ?? groupIndex}`)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return groupIndex
|
|
342
|
+
}
|
package/src/api/api-alephium.ts
CHANGED
|
@@ -320,6 +320,62 @@ export interface BuildExecuteScriptTxResult {
|
|
|
320
320
|
simulationResult: SimulationResult
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
+
/** BuildGrouplessDeployContractTx */
|
|
324
|
+
export interface BuildGrouplessDeployContractTx {
|
|
325
|
+
fromAddress: string
|
|
326
|
+
/** @format hex-string */
|
|
327
|
+
bytecode: string
|
|
328
|
+
/** @format uint256 */
|
|
329
|
+
initialAttoAlphAmount?: string
|
|
330
|
+
initialTokenAmounts?: Token[]
|
|
331
|
+
/** @format uint256 */
|
|
332
|
+
issueTokenAmount?: string
|
|
333
|
+
/** @format address */
|
|
334
|
+
issueTokenTo?: string
|
|
335
|
+
/** @format uint256 */
|
|
336
|
+
gasPrice?: string
|
|
337
|
+
/** @format block-hash */
|
|
338
|
+
targetBlockHash?: string
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** BuildGrouplessDeployContractTxResult */
|
|
342
|
+
export interface BuildGrouplessDeployContractTxResult {
|
|
343
|
+
transferTxs: BuildTransferTxResult[]
|
|
344
|
+
deployContractTx: BuildDeployContractTxResult
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** BuildGrouplessExecuteScriptTx */
|
|
348
|
+
export interface BuildGrouplessExecuteScriptTx {
|
|
349
|
+
fromAddress: string
|
|
350
|
+
/** @format hex-string */
|
|
351
|
+
bytecode: string
|
|
352
|
+
/** @format uint256 */
|
|
353
|
+
attoAlphAmount?: string
|
|
354
|
+
tokens?: Token[]
|
|
355
|
+
/** @format uint256 */
|
|
356
|
+
gasPrice?: string
|
|
357
|
+
/** @format block-hash */
|
|
358
|
+
targetBlockHash?: string
|
|
359
|
+
/** @format double */
|
|
360
|
+
gasEstimationMultiplier?: number
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** BuildGrouplessExecuteScriptTxResult */
|
|
364
|
+
export interface BuildGrouplessExecuteScriptTxResult {
|
|
365
|
+
transferTxs: BuildTransferTxResult[]
|
|
366
|
+
executeScriptTx: BuildExecuteScriptTxResult
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** BuildGrouplessTransferTx */
|
|
370
|
+
export interface BuildGrouplessTransferTx {
|
|
371
|
+
fromAddress: string
|
|
372
|
+
destinations: Destination[]
|
|
373
|
+
/** @format uint256 */
|
|
374
|
+
gasPrice?: string
|
|
375
|
+
/** @format block-hash */
|
|
376
|
+
targetBlockHash?: string
|
|
377
|
+
}
|
|
378
|
+
|
|
323
379
|
/** BuildInfo */
|
|
324
380
|
export interface BuildInfo {
|
|
325
381
|
releaseVersion: string
|
|
@@ -1564,8 +1620,8 @@ export class HttpClient<SecurityDataType = unknown> {
|
|
|
1564
1620
|
property instanceof Blob
|
|
1565
1621
|
? property
|
|
1566
1622
|
: typeof property === 'object' && property !== null
|
|
1567
|
-
|
|
1568
|
-
|
|
1623
|
+
? JSON.stringify(property)
|
|
1624
|
+
: `${property}`
|
|
1569
1625
|
)
|
|
1570
1626
|
return formData
|
|
1571
1627
|
}, new FormData()),
|
|
@@ -1645,18 +1701,18 @@ export class HttpClient<SecurityDataType = unknown> {
|
|
|
1645
1701
|
const data = !responseFormat
|
|
1646
1702
|
? r
|
|
1647
1703
|
: await response[responseFormat]()
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1704
|
+
.then((data) => {
|
|
1705
|
+
if (r.ok) {
|
|
1706
|
+
r.data = data
|
|
1707
|
+
} else {
|
|
1708
|
+
r.error = data
|
|
1709
|
+
}
|
|
1710
|
+
return r
|
|
1711
|
+
})
|
|
1712
|
+
.catch((e) => {
|
|
1713
|
+
r.error = e
|
|
1714
|
+
return r
|
|
1715
|
+
})
|
|
1660
1716
|
|
|
1661
1717
|
if (cancelToken) {
|
|
1662
1718
|
this.abortControllers.delete(cancelToken)
|
|
@@ -3722,4 +3778,68 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
|
|
|
3722
3778
|
...params
|
|
3723
3779
|
}).then(convertHttpResponse)
|
|
3724
3780
|
}
|
|
3781
|
+
groupless = {
|
|
3782
|
+
/**
|
|
3783
|
+
* No description
|
|
3784
|
+
*
|
|
3785
|
+
* @tags Groupless
|
|
3786
|
+
* @name PostGrouplessTransfer
|
|
3787
|
+
* @summary Build unsigned transfer transactions from a groupless address
|
|
3788
|
+
* @request POST:/groupless/transfer
|
|
3789
|
+
*/
|
|
3790
|
+
postGrouplessTransfer: (data: BuildGrouplessTransferTx, params: RequestParams = {}) =>
|
|
3791
|
+
this.request<
|
|
3792
|
+
BuildTransferTxResult[],
|
|
3793
|
+
BadRequest | Unauthorized | NotFound | InternalServerError | ServiceUnavailable | GatewayTimeout
|
|
3794
|
+
>({
|
|
3795
|
+
path: `/groupless/transfer`,
|
|
3796
|
+
method: 'POST',
|
|
3797
|
+
body: data,
|
|
3798
|
+
type: ContentType.Json,
|
|
3799
|
+
format: 'json',
|
|
3800
|
+
...params
|
|
3801
|
+
}).then(convertHttpResponse),
|
|
3802
|
+
|
|
3803
|
+
/**
|
|
3804
|
+
* No description
|
|
3805
|
+
*
|
|
3806
|
+
* @tags Groupless
|
|
3807
|
+
* @name PostGrouplessExecuteScript
|
|
3808
|
+
* @summary Build an unsigned execute script transaction from a groupless address
|
|
3809
|
+
* @request POST:/groupless/execute-script
|
|
3810
|
+
*/
|
|
3811
|
+
postGrouplessExecuteScript: (data: BuildGrouplessExecuteScriptTx, params: RequestParams = {}) =>
|
|
3812
|
+
this.request<
|
|
3813
|
+
BuildGrouplessExecuteScriptTxResult,
|
|
3814
|
+
BadRequest | Unauthorized | NotFound | InternalServerError | ServiceUnavailable | GatewayTimeout
|
|
3815
|
+
>({
|
|
3816
|
+
path: `/groupless/execute-script`,
|
|
3817
|
+
method: 'POST',
|
|
3818
|
+
body: data,
|
|
3819
|
+
type: ContentType.Json,
|
|
3820
|
+
format: 'json',
|
|
3821
|
+
...params
|
|
3822
|
+
}).then(convertHttpResponse),
|
|
3823
|
+
|
|
3824
|
+
/**
|
|
3825
|
+
* No description
|
|
3826
|
+
*
|
|
3827
|
+
* @tags Groupless
|
|
3828
|
+
* @name PostGrouplessDeployContract
|
|
3829
|
+
* @summary Build an unsigned deploy contract transaction from a groupless address
|
|
3830
|
+
* @request POST:/groupless/deploy-contract
|
|
3831
|
+
*/
|
|
3832
|
+
postGrouplessDeployContract: (data: BuildGrouplessDeployContractTx, params: RequestParams = {}) =>
|
|
3833
|
+
this.request<
|
|
3834
|
+
BuildGrouplessDeployContractTxResult,
|
|
3835
|
+
BadRequest | Unauthorized | NotFound | InternalServerError | ServiceUnavailable | GatewayTimeout
|
|
3836
|
+
>({
|
|
3837
|
+
path: `/groupless/deploy-contract`,
|
|
3838
|
+
method: 'POST',
|
|
3839
|
+
body: data,
|
|
3840
|
+
type: ContentType.Json,
|
|
3841
|
+
format: 'json',
|
|
3842
|
+
...params
|
|
3843
|
+
}).then(convertHttpResponse)
|
|
3844
|
+
}
|
|
3725
3845
|
}
|
package/src/api/node-provider.ts
CHANGED
|
@@ -55,6 +55,7 @@ interface NodeProviderApis {
|
|
|
55
55
|
utils: NodeApi<string>['utils']
|
|
56
56
|
miners: NodeApi<string>['miners']
|
|
57
57
|
events: NodeApi<string>['events']
|
|
58
|
+
groupless: NodeApi<string>['groupless']
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
export class NodeProvider implements NodeProviderApis {
|
|
@@ -69,6 +70,7 @@ export class NodeProvider implements NodeProviderApis {
|
|
|
69
70
|
readonly utils: NodeApi<string>['utils']
|
|
70
71
|
readonly miners: NodeApi<string>['miners']
|
|
71
72
|
readonly events: NodeApi<string>['events']
|
|
73
|
+
readonly groupless: NodeApi<string>['groupless']
|
|
72
74
|
|
|
73
75
|
constructor(baseUrl: string, apiKey?: string, customFetch?: typeof fetch)
|
|
74
76
|
constructor(provider: NodeProvider)
|
|
@@ -95,6 +97,7 @@ export class NodeProvider implements NodeProviderApis {
|
|
|
95
97
|
this.utils = { ...nodeApi.utils }
|
|
96
98
|
this.miners = { ...nodeApi.miners }
|
|
97
99
|
this.events = { ...nodeApi.events }
|
|
100
|
+
this.groupless = { ...nodeApi.groupless }
|
|
98
101
|
requestWithLog(this)
|
|
99
102
|
}
|
|
100
103
|
|
package/src/api/types.ts
CHANGED
|
@@ -16,6 +16,7 @@ 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 { hasExplicitGroupIndex } from '../address'
|
|
19
20
|
import { ZERO_ADDRESS } from '../constants'
|
|
20
21
|
import { isDebugModeEnabled } from '../debug'
|
|
21
22
|
import { TraceableError } from '../error'
|
|
@@ -99,14 +100,18 @@ export function toApiByteVec(v: Val): string {
|
|
|
99
100
|
throw new Error(`Invalid hex-string: ${v}`)
|
|
100
101
|
}
|
|
101
102
|
|
|
102
|
-
export function toApiAddress(
|
|
103
|
-
if (typeof
|
|
103
|
+
export function toApiAddress(v0: Val): string {
|
|
104
|
+
if (typeof v0 === 'string') {
|
|
105
|
+
let v = v0
|
|
106
|
+
if (hasExplicitGroupIndex(v)) {
|
|
107
|
+
v = v.slice(0, -2)
|
|
108
|
+
}
|
|
104
109
|
if (isBase58(v)) {
|
|
105
|
-
return
|
|
110
|
+
return v0
|
|
106
111
|
}
|
|
107
|
-
throw new Error(`Invalid base58 string: ${
|
|
112
|
+
throw new Error(`Invalid base58 string: ${v0}`)
|
|
108
113
|
} else {
|
|
109
|
-
throw new Error(`Invalid value: ${
|
|
114
|
+
throw new Error(`Invalid value: ${v0}, expected a base58 string`)
|
|
110
115
|
}
|
|
111
116
|
}
|
|
112
117
|
|