# ABI 与调用数据

> NeuroChain 合约调用格式——包含 function 和 args 的 JSON 调用数据、合约如何读取参数、通过宿主获取调用者身份、附带金额、返回数据、错误以及标准 NRC-20 接口。

Source: https://docs.nro.world/zh/build/abi

合约调用是一笔发往 `.app` 地址的交易，其 `data` 字段携带调用内容。规范的载荷格式是 UTF-8 JSON：

```json
{"function":"transfer","args":{"to":"neuro:bob.human","amount":"250"}}
```

在交易中，`data` 以**字符串**形式保存这段 JSON（也接受十六进制编码的载荷）。节点把这些字节交给合约的 `call(input_ptr, input_len)` 导出函数。

## 规则

| 规则 | 原因 |
|------|------|
| **方法名放在 `function` 中。** | 旧合约也接受 `method`；新代码不应再使用。 |
| **身份来自宿主。** 调用 `get_caller()`；忽略 JSON 中的任何 `caller` 字段。 | 任何人都可以往 JSON 里写任意 `caller`。宿主给出的值是经过验证的交易签名者（在跨合约调用中则是发起调用的合约）。 |
| **发送紧凑 JSON。** `:` 和 `,` 之后不留空格——即 `JSON.stringify` 的输出。 | 小型 `no_std` 合约通过逐字节扫描 `"key":` 来读取参数；空白会使其失效。 |
| **金额用十进制字符串**表示，单位为整枚代币：`"amount":"250"`。 | 节点会进行换算（见下文）；字符串可避免 JavaScript 中的精度损失。 |
| **`args` 中的参数名保持唯一。** | 逐字节扫描的合约会在载荷中任意位置找到第一个 `"key":`，因此 `args` 也可以平铺在顶层传入。 |

## 金额由节点换算

在调用到达合约之前，节点会替你改写一个参数：

- **合约调用：** 以十进制字符串或数字给出的 `args.amount` 被视为**整枚代币**金额，并按合约的 `decimals` 换算为代币的最小单位（6 位小数时 `"250"` → `250000000`）。
- **部署：** `init_args.total_supply` 以同样方式按 `init_args.decimals` 换算，后者必须是 JSON **数字**（字符串会被当作 0 位小数）。

其他所有参数都按原样到达合约。合约永远看不到人类可读的金额。

## 附带金额

交易的 `value`（单位 uNRO）在调用成功**之后**记入合约自己的账户。如果调用 trap 或耗尽 fuel，金额留在发送方。目前没有宿主函数能告诉合约附带了多少金额；需要该信息的合约可以比较调用前后的 `get_balance()`，或把金额作为参数传入并加以校验。

## 返回数据与错误

合约通过 `set_return(ptr, len)` 返回字节。按约定：

```json
{"ok":"<value>"}            // 成功
{"error":"Unknown function"}  // 合约报告的失败
```

合约一旦 **trap**（`unreachable`、耗尽 fuel、越界访问内存），交易即失败：存储变更被回滚，金额不转移，手续费和 nonce 照常消耗。状态：`failed`。

无需交易即可读取返回值：

```bash
curl -s -X POST "$GATEWAY/api/v1/contracts/neuro:<10 hex>.app/call" \
  -H 'Content-Type: application/json' \
  -d '{"function":"balance_of","args":{"address":"neuro:<15 hex>.human"},"dry_run":true}'
```

响应包含 `success`、`return_data`、`gas_used` 以及该调用将会发出的事件。当节点以 `CHAIN_ENV=mainnet` 运行时，通过此端点的写调用（`dry_run: false`）会被拒绝；请改为发送交易。

## 构造函数与 `initialize`

- 如果存在 `init` 导出，它会在合约部署时运行一次。
- 如果部署携带 `init_args`，随后会以 `{"function":"initialize", …init_args}` 调用合约——参数平铺在顶层。参见[部署合约](./deploy#_4-what-every-validator-does)。

## 标准接口

接口是有文档的方法集合，而不是额外的 WASM 导出：合约只要响应这些函数名，就实现了该接口。

### NRC-20（同质化代币）

由 `neurochain/contracts/nrc20` 实现。在合约内部，所有金额都是以代币最小单位表示的整数；以整枚代币发送 `amount`，由节点换算（见上文）。

| 函数 | 参数 | 说明 |
|------|------|------|
| `initialize` | `name`、`symbol`、`decimals`、`total_supply` | 仅一次；把全部供应量铸造给调用者，调用者成为 `owner` |
| `name`、`symbol`、`decimals`、`total_supply` | — | 读取 |
| `balance_of` | `address` | 读取 |
| `transfer` | `to`、`amount` | 从调用者转出 |
| `approve` | `spender`、`amount` | |
| `allowance` | `owner`、`spender` | 读取 |
| `transfer_from` | `from`、`to`、`amount` | 消耗授予调用者的额度 |
| `mint` | `to`、`amount` | 仅限 owner |
| `burn` | `amount` | 从调用者扣除 |
| `owner` | — | 读取 |

也接受 camelCase 别名（`balanceOf`、`totalSupply`、`transferFrom`）。转账会发出 `Transfer` 事件。

仓库中使用的其他接口：NRC-721 风格的证书（`ncert`）和 DAO 治理（`dao`、`gov_dao`）；它们的 ABI 文件与源码放在一起（`*.abi.json`）。

## 跨合约调用

合约可以通过宿主函数 `call_contract` 调用另一个合约，最多嵌套 **8 层**（`MAX_CALL_DEPTH`）。在被调用方内部，`get_caller()` 返回发起调用的合约地址。参见[跨合约调用](/zh/neurowasm/cross-contract)。
