@nervosnetwork/ckb-sdk-utils
Version:
Utils module of @nervosnetwork/ckb-sdk-core
219 lines • 9.02 kB
JavaScript
import { blake160, bech32, bech32m } from '../index.js';
import { SECP256K1_BLAKE160, SECP256K1_MULTISIG, ANYONE_CAN_PAY_MAINNET, ANYONE_CAN_PAY_TESTNET, } from '../systemScripts.js';
import { hexToBytes, bytesToHex } from '../convertors/index.js';
import { HexStringWithout0xException, AddressException, AddressPayloadException, CodeHashException, HashTypeException, ParameterRequiredException, AddressFormatTypeException, AddressFormatTypeAndEncodeMethodNotMatchException, } from '../exceptions/index.js';
const MAX_BECH32_LIMIT = 1023;
export var AddressPrefix;
(function (AddressPrefix) {
AddressPrefix["Mainnet"] = "ckb";
AddressPrefix["Testnet"] = "ckt";
})(AddressPrefix || (AddressPrefix = {}));
export var AddressType;
(function (AddressType) {
AddressType["FullVersion"] = "0x00";
AddressType["HashIdx"] = "0x01";
AddressType["DataCodeHash"] = "0x02";
AddressType["TypeCodeHash"] = "0x04";
})(AddressType || (AddressType = {}));
export var Bech32Type;
(function (Bech32Type) {
Bech32Type["Bech32"] = "bech32";
Bech32Type["Bech32m"] = "bech32m";
})(Bech32Type || (Bech32Type = {}));
const payloadToAddress = (payload, isMainnet = true) => bech32m.encode(isMainnet ? AddressPrefix.Mainnet : AddressPrefix.Testnet, bech32m.toWords(payload), MAX_BECH32_LIMIT);
const scriptToPayload = ({ codeHash, hashType, args }) => {
if (!args.startsWith('0x')) {
throw new HexStringWithout0xException(args);
}
if (!codeHash.startsWith('0x') || codeHash.length !== 66) {
throw new CodeHashException(codeHash);
}
let HashType;
(function (HashType) {
HashType["data"] = "00";
HashType["type"] = "01";
HashType["data1"] = "02";
HashType["data2"] = "04";
})(HashType || (HashType = {}));
if (!HashType[hashType]) {
throw new HashTypeException(hashType);
}
return hexToBytes(`0x00${codeHash.slice(2)}${HashType[hashType]}${args.slice(2)}`);
};
export const scriptToAddress = (script, isMainnet = true) => payloadToAddress(scriptToPayload(script), isMainnet);
export const toAddressPayload = (args, type = AddressType.HashIdx, codeHashOrCodeHashIndex, hashType) => {
if (typeof args === 'string' && !args.startsWith('0x')) {
throw new HexStringWithout0xException(args);
}
if (![AddressType.HashIdx, AddressType.DataCodeHash, AddressType.TypeCodeHash, AddressType.FullVersion].includes(type)) {
throw new AddressFormatTypeException(+type);
}
if ([AddressType.DataCodeHash, AddressType.TypeCodeHash].includes(type)) {
console.warn(`Address of 'AddressType.DataCodeHash' or 'AddressType.TypeCodeHash' is deprecated, please use address of AddressPrefix.FullVersion`);
}
if (!codeHashOrCodeHashIndex) {
codeHashOrCodeHashIndex = type === AddressType.HashIdx ? '0x00' : SECP256K1_BLAKE160.codeHash;
}
if (type !== AddressType.FullVersion) {
return new Uint8Array([
...hexToBytes(type),
...hexToBytes(codeHashOrCodeHashIndex),
...(typeof args === 'string' ? hexToBytes(args) : args),
]);
}
if (!hashType && codeHashOrCodeHashIndex === SECP256K1_BLAKE160.codeHash) {
hashType = SECP256K1_BLAKE160.hashType;
}
if (!codeHashOrCodeHashIndex.startsWith('0x') || codeHashOrCodeHashIndex.length !== 66) {
throw new CodeHashException(codeHashOrCodeHashIndex);
}
if (!hashType) {
throw new ParameterRequiredException('hashType');
}
return scriptToPayload({
codeHash: codeHashOrCodeHashIndex,
hashType,
args: typeof args === 'string' ? args : bytesToHex(args),
});
};
export const bech32Address = (args, { prefix = AddressPrefix.Mainnet, type = AddressType.HashIdx, codeHashOrCodeHashIndex = '' } = {}) => bech32.encode(prefix, bech32.toWords(toAddressPayload(args, type, codeHashOrCodeHashIndex)), MAX_BECH32_LIMIT);
export const fullPayloadToAddress = ({ args, prefix, type = AddressType.DataCodeHash, codeHash, }) => bech32Address(args, { prefix, type, codeHashOrCodeHashIndex: codeHash });
export const pubkeyToAddress = (pubkey, options = {}) => {
const publicKeyHash = blake160(pubkey);
return bech32Address(publicKeyHash, options);
};
const isValidShortVersionPayload = (payload, bech32Type) => {
const [type, index, ...data] = payload;
if (bech32Type !== Bech32Type.Bech32) {
throw new AddressFormatTypeAndEncodeMethodNotMatchException(type, bech32Type);
}
switch (index) {
case 0:
case 1: {
if (data.length !== 20) {
throw new AddressPayloadException(payload, 'short');
}
break;
}
case 2: {
if (data.length === 20 || data.length === 22 || data.length === 24) {
break;
}
throw new AddressPayloadException(payload, 'short');
}
default: {
throw new AddressPayloadException(payload, 'short');
}
}
};
const isPayloadValid = (payload, bech32Type) => {
const type = payload[0];
const data = payload.slice(1);
switch (type) {
case +AddressType.HashIdx: {
isValidShortVersionPayload(payload, bech32Type);
break;
}
case +AddressType.DataCodeHash:
case +AddressType.TypeCodeHash: {
if (bech32Type !== Bech32Type.Bech32) {
throw new AddressFormatTypeAndEncodeMethodNotMatchException(type, bech32Type);
}
if (data.length < 32) {
throw new AddressPayloadException(payload, 'full');
}
break;
}
case +AddressType.FullVersion: {
if (bech32Type !== Bech32Type.Bech32m) {
throw new AddressFormatTypeAndEncodeMethodNotMatchException(type, bech32Type);
}
const codeHash = data.slice(0, 32);
if (codeHash.length < 32) {
throw new CodeHashException(bytesToHex(codeHash));
}
const hashType = parseInt(data[32].toString(), 16);
if (!Object.values(AddressType)
.map(i => +i)
.includes(hashType)) {
throw new HashTypeException(`0x${hashType.toString(16)}`);
}
break;
}
default: {
throw new AddressPayloadException(payload);
}
}
};
export const parseAddress = (address, encode = 'binary') => {
let bech32Type;
let payload = new Uint8Array();
try {
const decoded = bech32.decode(address, MAX_BECH32_LIMIT);
bech32Type = Bech32Type.Bech32;
payload = new Uint8Array(bech32.fromWords(new Uint8Array(decoded.words)));
}
catch (_a) {
const decoded = bech32m.decode(address, MAX_BECH32_LIMIT);
bech32Type = Bech32Type.Bech32m;
payload = new Uint8Array(bech32m.fromWords(new Uint8Array(decoded.words)));
}
try {
isPayloadValid(payload, bech32Type);
}
catch (err) {
if (err instanceof AddressFormatTypeAndEncodeMethodNotMatchException) {
throw err;
}
throw new AddressException(address, err.stack, err.type);
}
return encode === 'binary' ? payload : bytesToHex(payload);
};
export const addressToScript = (address) => {
const payload = parseAddress(address);
const type = payload[0];
switch (type) {
case +AddressType.FullVersion: {
const HASH_TYPE = {
'00': 'data',
'01': 'type',
'02': 'data1',
'04': 'data2'
};
const p = bytesToHex(payload);
const codeHash = `0x${p.substr(4, 64)}`;
const hashType = HASH_TYPE[p.substr(68, 2)];
const args = `0x${p.substr(70)}`;
return { codeHash, hashType, args };
}
case +AddressType.HashIdx: {
const codeHashIndices = [
SECP256K1_BLAKE160,
SECP256K1_MULTISIG,
address.startsWith(AddressPrefix.Mainnet) ? ANYONE_CAN_PAY_MAINNET : ANYONE_CAN_PAY_TESTNET,
];
const index = payload[1];
const args = payload.slice(2);
const script = codeHashIndices[index];
return {
codeHash: script.codeHash,
hashType: script.hashType,
args: bytesToHex(args),
};
}
case +AddressType.DataCodeHash:
case +AddressType.TypeCodeHash: {
const codeHashAndArgs = bytesToHex(payload.slice(1));
const hashType = type === +AddressType.DataCodeHash ? 'data' : 'type';
return {
codeHash: codeHashAndArgs.substr(0, 66),
hashType,
args: `0x${codeHashAndArgs.substr(66)}`,
};
}
default: {
throw new AddressFormatTypeException(type);
}
}
};
//# sourceMappingURL=index.js.map