TypeScript运行TON部署脚本报错:Unknown address type. Byte length is not 36
运行TON部署脚本时遇到"Unknown address type: byte length is not equal to 36"错误
错误日志
yarn ts-node manage/deploy-sandbox.ts yarn run v1.22.19 $ /Users/apple/proj/tonstart/node_modules/.bin/ts-node manage/deploy-sandbox.ts /Users/apple/proj/tonstart/node_modules/ton/dist/address/Address.js:17 throw new Error('Unknown address type: byte length is not equal to 36'); ^ Error: Unknown address type: byte length is not equal to 36 at parseFriendlyAddress (/Users/apple/proj/tonstart/node_modules/ton/dist/address/Address.js:17:15)
部署脚本代码(deploy-sandbox.ts)
import { Cell, contractAddress, StateInit, toNano, beginCell, Address } from 'ton'; import { readFileSync } from 'fs'; import qs from 'qs'; import qrcode from 'qrcode-terminal'; // Create a data cell similar to the initial contract state const dataCell = beginCell() .storeUint(0, 32) // counter .storeAddress(Address.parse('/* YOUR ADDRESS */')) .endCell(); // Load code from build const codeCell = Cell.fromBoc(readFileSync('./build/hello-world.boc'))[0]; // Calculate address from code & data const address = contractAddress({ workchain: 0, initialCode: codeCell, initialData: dataCell }); // Prepare init message const initCell = new Cell(); new StateInit({ code: codeCell, data: dataCell, }).writeTo(initCell); // Encode link to deploy contract let link = 'https://test.tonhub.com/transfer/' + address.toFriendly({ testOnly: true }) + '?' + qs.stringify({ text: 'Deploy contract', amount: toNano(1).toString(10), init: initCell.toBoc({ idx: false }).toString('base64') }); console.log('Address: ' + address.toFriendly({ testOnly: true })); qrcode.generate(link, { small: true }, (code) => { console.log(code) });
Address类型定义(Address.d.ts)
/// <reference types="node" /> import inspectSymbol from 'symbol.inspect'; export declare class Address { static isAddress(src: any): src is Address; static isFriendly(source: String): boolean; static normalize(source: string | Address): string; static parse(source: string): Address; static parseRaw(source: string): Address; static parseFriendly(source: string | Buffer): { isBounceable: boolean; isTestOnly: boolean; address: Address; }; readonly workChain: number; readonly hash: Buffer; constructor(workChain: number, hash: Buffer); toString: () => string; equals(src: Address): boolean; toBuffer: () => Buffer; toFriendlyBuffer: (args?: { bounceable?: boolean | undefined; testOnly?: boolean | undefined; } | undefined) => Buffer; toFriendly: (args?: { urlSafe?: boolean | undefined; bounceable?: boolean | undefined; testOnly?: boolean | undefined; } | undefined) => string;
Address实现代码(Address.js)
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.Address = void 0; const symbol_inspect_1 = __importDefault(require("symbol.inspect")); const crc16_1 = require("../utils/crc16"); const bounceable_tag = 0x11; const non_bounceable_tag = 0x51; const test_flag = 0x80; function parseFriendlyAddress(src) { const data = Buffer.isBuffer(src) ? src : Buffer.from(src, 'base64'); // 1byte tag + 1byte workchain + 32 bytes hash + 2 byte crc if (data.length !== 36) { throw new Error('Unknown address type: byte length is not equal to 36'); } // Prepare data const addr = data.slice(0, 34); const crc = data.slice(34, 36); const calcedCrc = (0, crc16_1.crc16)(addr); if (!(calcedCrc[0] === crc[0] && calcedCrc[1] === crc[1])) { throw new Error('Invalid checksum: ' + src); } // Parse tag let tag = addr[0]; let isTestOnly = false; let isBounceable = false; if (tag & test_flag) { isTestOnly = true; tag = tag ^ test_flag; } if ((tag !== bounceable_tag) && (tag !== non_bounceable_tag)) throw "Unknown address tag"; isBounceable = tag === bounceable_tag; let workchain = null; if (addr[1] === 0xff) { // TODO we should read signed integer here workchain = -1; } else { workchain = addr[1]; } const hashPart = addr.slice(2, 34); return { isTestOnly, isBounceable, workchain, hashPart }; } class Address { constructor(workChain, hash) { this.toString = () => { return this.workChain + ':' + this.hash.toString('hex'); }; this.toBuffer = () => { const addressWithChecksum = Buffer.alloc(36); addressWithChecksum.set(this.hash); addressWithChecksum.set([this.workChain, this.workChain, this.workChain, this.workChain], 32); return addressWithChecksum; }; this.toFriendlyBuffer = (args) => { let testOnly = (args && args.testOnly !== undefined) ? args.testOnly : false; let bounceable = (args && args.bounceable !== undefined) ? args.bounceable : true; let tag = bounceable ? bounceable_tag : non_bounceable_tag; if (testOnly) { tag |= test_flag; } const addr = Buffer.alloc(34); addr[0] = tag; addr[1] = this.workChain; addr.set(this.hash, 2); const addressWithChecksum = Buffer.alloc(36); addressWithChecksum.set(addr); addressWithChecksum.set((0, crc16_1.crc16) (addr), 34); return addressWithChecksum; }; this.toFriendly = (args) => { let urlSafe = (args && args.urlSafe !== undefined) ? args.urlSafe : true; let buffer = this.toFriendlyBuffer(args); if (urlSafe) { return buffer.toString('base64').replace(/\+/g, '- ').replace(/\//g, '_'); } else { return buffer.toString('base64'); } }; this[_a] = () => this.toFriendly(); this.workChain = workChain; this.hash = hash; Object.freeze(this); } static isAddress(src) { return src instanceof Address; } static isFriendly(source) { return source.indexOf(':') < 0; } static normalize(source) { if (typeof source === 'string') { return Address.parse(source).toFriendly(); } else { return source.toFriendly(); } } static parse(source) { if (Address.isFriendly(source)) { return this.parseFriendly(source).address; } else { return this.parseRaw(source); } } static parseRaw(source) { let workChain = parseInt(source.split(":")[0]); let hash = Buffer.from(source.split(":")[1], 'hex'); return new Address(workChain, hash); } static parseFriendly(source) { if (Buffer.isBuffer(source)) { let r = parseFriendlyAddress(source); return { isBounceable: r.isBounceable, isTestOnly: r.isTestOnly, address: new Address(r.workchain, r.hashPart) }; } else { let addr = source.replace(/\-/g, '+').replace(/\_/g, '\/'); // Convert from url-friendly to true base64 let r = parseFriendlyAddress(addr); return { isBounceable: r.isBounceable, isTestOnly: r.isTestOnly, address: new Address(r.workchain, r.hashPart) }; } } equals(src) { if (src.workChain !== this.workChain) { return false; } return src.hash.equals(this.hash); } } exports.Address = Address; _a = symbol_inspect_1.default;
错误原因及解决方法
原因
脚本中Address.parse('/* YOUR ADDRESS */')使用了占位符字符串,并非有效的TON地址。Address.parse会先判断字符串是否为友好地址(不含冒号),然后调用parseFriendly解析。占位符转base64后的buffer长度远不等于36,触发parseFriendlyAddress中的长度检查报错。
解决步骤
- 替换占位符为有效测试网地址:
- 支持两种格式的地址:
- 原始格式:例如
0:abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890(工作链ID+64位十六进制哈希) - 友好格式:例如
EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N(TON测试网的base64编码地址)
- 原始格式:例如
- 支持两种格式的地址:
- 获取测试网地址:
- 如果没有测试网地址,可通过TON官方钱包创建测试网账户,导出对应地址即可。
修改后的代码片段示例:
const dataCell = beginCell() .storeUint(0, 32) // counter .storeAddress(Address.parse('EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N')) // 替换为你的测试网地址 .endCell();
内容的提问来源于stack exchange,提问作者Draco
相关产品推荐
相关产品推荐

