UNPKG

@nervosnetwork/ckb-sdk-utils

Version:

Utils module of @nervosnetwork/ckb-sdk-core

219 lines 9.02 kB
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