Near链使用AssemblyScript调用Aurora合约时如何编码参数
问题场景
在NEAR区块链上使用AssemblyScript开发的合约,需要调用部署在Aurora上的EVM合约时,Aurora合约抛出ERR_BORSH_DESERIALIZE panic错误,原实现代码如下:
import { BorshSerializer } from '@serial-as/borsh' @serializable class FunctionCallArgs { contract: Uint8Array; input: Uint8Array; } export function myFunction(): void { const args: FunctionCallArgs = { contract: util.stringToBytes(contractAddress), input: util.stringToBytes(abiEncodedFn), }; const argsBorsh = BorshSerializer.encode(args); ContractPromise.create("aurora", "call", argsBorsh, 100); }
错误根因
- 字段类型不匹配:Aurora引擎合约
call方法的第一个参数contract是固定长度20字节的EVM地址原始字节数组,Borsh对固定长度数组不会写入长度前缀;原代码将其定义为动态长度Uint8Array,序列化时会额外写入4字节小端序长度值,和Aurora预期的序列化结构完全错位,直接触发反序列化失败。 - 字节转换逻辑错误:
util.stringToBytes是将字符串按UTF-8编码转成字节流,传入hex格式的地址/ABI调用数据时,会把每个十六进制字符的ASCII码作为字节值(比如字符'0'会被转为0x30,而非十六进制对应的0x00),完全不符合参数的字节要求。 - Gas配置不足:原代码传入的100单位gas远低于跨合约调用+Aurora EVM执行的最低要求,即使参数序列化正确也会后续执行失败(该问题不会触发反序列化报错,但属于必踩的坑)。
正确实现代码
直接手动按Borsh规则序列化参数,避免第三方序列化库的类型标注、字段顺序不匹配问题,同时修正字节转换逻辑:
/** * 将hex格式字符串转为原始字节数组 * 自动处理带0x前缀/不带前缀的输入 */ function hexToBytes(hex: string): Uint8Array { const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex; assert(cleanHex.length % 2 === 0, "Invalid hex string length"); const res = new Uint8Array(cleanHex.length / 2); for (let i = 0; i < res.length; i++) { const byteStr = cleanHex.slice(i * 2, i * 2 + 2); res[i] = u8(parseInt(byteStr, 16)); } return res; } /** * 按Aurora call方法的Borsh序列化规则编码参数 * 结构:[20字节固定EVM地址][4字节小端序input长度][input原始字节] */ function serializeAuroraCallArgs(targetEvmAddr: Uint8Array, callInput: Uint8Array): Uint8Array { assert(targetEvmAddr.length === 20, "EVM address must be 20 bytes"); const args = new Uint8Array(20 + 4 + callInput.length); // 写入固定长度20字节地址 memory.copy(args.dataStart, targetEvmAddr.dataStart, 20); // 写入input长度(小端u32) store<u32>(args.dataStart + 20, callInput.length as u32); // 写入input内容 memory.copy(args.dataStart + 24, callInput.dataStart, callInput.length); return args; } export function myFunction(): void { // 替换为实际目标Aurora合约的EVM地址(hex格式) const targetContract = "0x你的合约地址"; // 替换为实际ABI编码后的EVM调用数据(hex格式) const abiEncodedCallData = "0x你的ABI编码调用数据"; const contractBytes = hexToBytes(targetContract); const inputBytes = hexToBytes(abiEncodedCallData); const serializedArgs = serializeAuroraCallArgs(contractBytes, inputBytes); // 附加Gas建议不低于30Tgas,可根据实际调用复杂度上调 const attachedGas = 30_000_000_000_000; ContractPromise.create("aurora", "call", serializedArgs, attachedGas); }
注意事项
- 不要依赖通用Borsh序列化库处理Aurora调用参数,必须确认序列化结构和Aurora合约定义完全一致:固定长度字段无长度前缀,动态长度字段前置4字节小端序长度。
- 所有链上传递的EVM地址、ABI编码数据都必须是原始字节,不能直接把hex字符串用UTF-8编码转字节。
- Aurora跨调用需要预留足够的Gas,避免因Gas不足导致交易失败。
内容的提问来源于stack exchange,提问作者Gwin Pin
相关产品推荐
相关产品推荐

