---
# System prepended metadata

title: 使用 `gmeta` 为 Gear 合约生成 Metadata

---



# 使用 `gmeta` 为 Gear 合约生成 Metadata

---

上节回顾: 编译 Gear 模板合约并在 [idea](https://idea.gear-tech.io) 上部署，进行全量/部分状态查询

https://github.com/gear-dapps/app

1. 部署合约 code
2. 上传 metadata -> 合约交互 / 内部状态
3. 上传 metawasm -> 状态查询


---

# Gear 合约的 Metadata

Gear 使用 Scale 编解码器([SCALE codec](https://docs.substrate.io/reference/scale-codec/))作为其默认的二进制编解码格式
 

- 高效性：Scale编解码器在编码和解码时非常高效，因为它是一种紧凑的二进制格式，可以将数据转换为紧凑的字节流。这使得它在网络传输和存储数据时非常有效。
- 通用性：Scale编解码器是一种通用格式，可以用于多种类型的数据，例如数字、布尔、字符串、结构体等。这使得它非常适合用于区块链数据结构，因为区块链通常需要处理多种类型的数据。
- 可扩展性：Scale编解码器是可扩展的，这意味着它可以轻松地支持新的数据类型和协议变更。这使得Polkadot可以轻松地升级其协议，同时保持向后兼容性。

Typed Value: `u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `Vec`, `Option`, `String`, `struct`, `enum` -> Binary `0xdeadbeef...`

编码省略了类型信息，不同数据可能有同样的编码:

`"\nX": String` => `0x0a58`
`2648: u32` => `0x0a58`

https://www.shawntabrizi.com/substrate-js-utilities/codec/

---

## Metadata: 包含合约相关数据的类型信息

> Metadata is a kind of interface map that helps to transform a set of bytes into an understandable structure and indicates the function this structure is intended for. Metadata determines how all incoming and outgoing data will be encoded/decoded.

https://wiki.gear-tech.io/docs/developing-contracts/metadata/

没有 Metadata 便无法
- 获得合约输入消息定义，编码调用参数，向合约发送消息
- 获得合约返回消息定义，解码合约返回的消息，获取返回值
- 获得合约状态类型定义，读取合约状态

Metadata 经编译脚本 SCALE 编码后保存为 *.meta.txt, 结构体定义：

```
#[derive(Encode, Debug, Decode, Eq, PartialEq)]
pub struct MetadataRepr {
    pub init: TypesRepr,
    pub handle: TypesRepr,
    pub reply: TypesRepr,
    pub others: TypesRepr,
    pub signal: Option<u32>,
    pub state: Option<u32>,
    pub registry: Vec<u8>, // scale_info::PortableRegistry
}

#[derive(Encode, Debug, Decode, Eq, PartialEq)]
pub struct TypesRepr {
    pub input: Option<u32>,
    pub output: Option<u32>,
}
```


# 使用 `@gear-js/api` 解码 Metadata 内容

https://github.com/gear-tech/gear-js/tree/main/api#getting-metadata

使用 `getProgramMetadata` 转换为 JSON 表示:

  <details>
    <summary>Click to expand</summary>

```
{
  "registry": {},
  "regTypes": {},
  "portableRegistry": {
    "types": [
      {
        "id": 0,
        "type": {
          "path": [
            "app_io",
            "PingPong"
          ],
          "params": [],
          "def": {
            "variant": {
              "variants": [
                {
                  "name": "Ping",
                  "fields": [],
                  "index": 0,
                  "docs": []
                },
                {
                  "name": "Pong",
                  "fields": [],
                  "index": 1,
                  "docs": []
                }
              ]
            }
          },
          "docs": []
        }
      },
      {
        "id": 1,
        "type": {
          "path": [],
          "params": [],
          "def": {
            "sequence": {
              "type": 2
            }
          },
          "docs": []
        }
      },
      {
        "id": 2,
        "type": {
          "path": [],
          "params": [],
          "def": {
            "tuple": [
              3,
              6
            ]
          },
          "docs": []
        }
      },
      {
        "id": 3,
        "type": {
          "path": [
            "gstd",
            "common",
            "primitives",
            "ActorId"
          ],
          "params": [],
          "def": {
            "composite": {
              "fields": [
                {
                  "name": null,
                  "type": 4,
                  "typeName": "[u8; 32]",
                  "docs": []
                }
              ]
            }
          },
          "docs": []
        }
      },
      {
        "id": 4,
        "type": {
          "path": [],
          "params": [],
          "def": {
            "array": {
              "len": 32,
              "type": 5
            }
          },
          "docs": []
        }
      },
      {
        "id": 5,
        "type": {
          "path": [],
          "params": [],
          "def": {
            "primitive": "U8"
          },
          "docs": []
        }
      },
      {
        "id": 6,
        "type": {
          "path": [],
          "params": [],
          "def": {
            "primitive": "U128"
          },
          "docs": []
        }
      }
    ]
  },
  "types": {
    "init": {
      "input": null,
      "output": null
    },
    "handle": {
      "input": 0,
      "output": 0
    },
    "reply": {
      "input": null,
      "output": null
    },
    "others": {
      "input": null,
      "output": null
    },
    "signal": null,
    "state": 1
  }
}
```

</details>
    

---

# 在项目中定义 Metadata

一般将 Metadata 的定义放在 ./io 中

下面以模板合约为例：https://github.com/gear-dapps/app

## 目录结构

不同的 Crate 及各自对应的编译产物:

- ./ : `app` 合约逻辑 (code) 
  - ./target/wasm32-unknown-unknown/release/app.opt.wasm
- ./io : `app-io` 类型定义 (metadata)
  - ./app.meta.txt
- ./state : `app-state` 状态查询 (metawasm)
  - ./target/wasm32-unknown-unknown/release/app_state.meta.wasm


---

## `gear-wasm-builder`

各种产物对应的编译方式

```
// 编译 code (./build.rs)
gear_wasm_builder::build();

// 编译 code + metadata (./build.rs)
gear_wasm_builder::build_with_metadata::<app_io::ContractMetadata>();

// 编译 metawasm (./state/build.rs)
gear_wasm_builder::build_metawasm();
```

```
pub fn build_with_metadata<T: Metadata>()
```

--- 

合约的 Metadata 应实现 `gmeta::Metadata` trait，定义如下

https://github.com/gear-tech/gear/blob/master/gmeta/src/lib.rs

```
pub trait Metadata {
    type Init: Types;
    type Handle: Types;
    type Reply: Types;
    type Others: Types;
    type Signal: Type;
    type State: Type;

    fn repr() -> MetadataRepr {
        let mut registry = Registry::new();

        MetadataRepr {
            init: Self::Init::register(&mut registry),
            handle: Self::Handle::register(&mut registry),
            reply: Self::Reply::register(&mut registry),
            others: Self::Others::register(&mut registry),
            signal: Self::Signal::register(&mut registry),
            state: Self::State::register(&mut registry),
            registry: PortableRegistry::from(registry).encode(),
        }
    }
}

pub trait Types {
    type Input: Type;
    type Output: Type;

    fn register(registry: &mut Registry) -> TypesRepr {
        let input = Self::Input::register(registry);
        let output = Self::Output::register(registry);

        TypesRepr { input, output }
    }
}

pub type InOut<I, O> = (I, O);
pub type In<I> = InOut<I, ()>;
pub type Out<O> = InOut<(), O>;

impl<I: Type, O: Type> Types for InOut<I, O> {
    type Input = I;
    type Output = O;
}

impl Types for () {
    type Input = ();
    type Output = ();
}
```


---


# 示例

`app_io::ContractMetadata` 实现了 `gmeta`

```
use gmeta::{InOut, Metadata};
use gstd::{prelude::*, ActorId};

...

pub struct ContractMetadata;

impl Metadata for ContractMetadata {
    type Init = ();
    type Handle = InOut<PingPong, PingPong>;
    type Others = ();
    type Reply = ();
    type Signal = ();
    type State = Vec<(ActorId, u128)>;
}

```

https://wiki.gear-tech.io/docs/developing-contracts/metadata/

# 作业:

尝试修改 https://github.com/gear-dapps/app, 将 State 的类型变为 `BTreeMap<ActorId, u128>`, 并在 [idea](https://idea.gear-tech.io) 上部署 
