Mandos复合值
原文:https://docs.elrond.com/developers/mandos-reference/values-complex
我们已经在这里讨论了简单类型的表示。对于像usize
、BigUint
或&[u8]
这样的类型的参数来说,这已经足够了,但是我们还需要以某种方式指定复杂的类型,比如定制结构或项目列表。
串联
可以使用管道运算符(|
)连接多个表达式。管道操作符优先于所有操作,因此目前不可能将一个函数连接起来然后应用于整个结果。
这是短列表或小结构的理想选择。
举例
- 一个
Vec<u32>
可以表示为"u32:1|u32:2|u32:3"
。 - 一个
(BigUint, BigUint)
元组可以表示为"biguint:1|biguint:2"
- a
SimpleStruct { a: u8, b: BoxedBytes }
可以表示为"u8:4|nested:str:value-b"
请注意,管道操作符只负责连接本身。你负责确保在适当的地方使用了嵌套编码。
使用 JSON 列表作为值
Mandos允许使用 JSON 列表来表达更长的值。当所表示的值本身是智能合约中的列表时,这尤其有意义。
举例
- a
Vec<u32>
也可以表示为["u32:1", "u32:2", "u32:3"]
。 - 一个
(BigUint, BigUint)
元组也可以表示为["biguint:1", "biguint:2"]
- 一个
SimpleStruct { a: u8, b: BoxedBytes }
也可以表示为["u8:4", "nested:str:value-b"]
,虽然在这种情况下一个 JSON 映射可能更合适。
确保不要将表示为 JSON 列表的值与Mandos语法的其他元素混淆。
举例
{
"step": "scCall",
"txId": "echo_managed_vec_of_managed_vec",
"tx": {
"from": "address:an_account",
"to": "sc:basic-features",
"value": "0",
"function": "echo_managed_vec_of_managed_vec",
"arguments": [
[
"u32:3",
["u32:1", "u32:2", "u32:3"],
"u32:0",
"u32:2",
["u32:5", "u32:6"]
]
],
"gasLimit": "50,000,000",
"gasPrice": "0"
}
},
在上面的例子中,实际上只有一个参数要传递给端点。"arguments": [ ... ]
中的外括号是参数列表的Mandos语法。直接嵌套的括号表示 JSON 列表值。注意列表本身是如何包含更多列表的。它们最终都会连接成一个值。
在这个例子中,唯一的参数是0x0000000300000001000000020000000300000000000000020000000500000006
。
提示
我们在上面提到了开发者需要如何处理嵌套项的序列化。这实际上是一个很好的例子。端点echo_managed_vec_of_managed_vec
接受一个列表列表,所以我们需要在第二层序列化列表的长度。注意长度是如何作为 JSON 字符串给出的,内容是如何作为 JSON 列表给出的;第一个"u32:3"
是第一个项目的序列化长度,即["u32:1", "u32:2", "u32:3"]
,以此类推。
使用 JSON 地图作为值
JSON 列表对于表示一系列项目很有意义,但是对于结构来说,JSON 映射更有表现力。
规则如下:
- Mandos将连接所有的 JSON 映射值,并去掉键。
- 这些键需要按字母数字顺序排列,所以我们习惯用数字作为它们的前缀。JSON 中的映射键基本上是无序的,这是对值实施确定性排序的最简单方法。
- 映射值可以是 JSON 字符串、列表或其他映射,所有Mandos值规则都以相同的方式向下应用。
举例
这是示例中实际彩票合约的节略部分。
{
"step": "checkState",
"accounts": {
"sc:lottery": {
"storage": {
"str:lotteryInfo|nested:str:lottery_name": {
"0-token_identifier": "nested:str:LOTTERY-123456",
"1-ticket_price": "biguint:100",
"2-tickets-left": "u32:0",
"3-deadline": "u64:123,456",
"4-max_entries_per_user": "u32:1",
"5-prize_distribution": ["u32:2", "u8:75", "u8:25"],
"6-whitelist": ["u32:3", "address:acc1", "address:acc2", "address:acc3"],
"7-prize_pool": "biguint:500"
}
},
"code": "file:../output/lottery-esdt.wasm"
}
}
}
这转化成的 Rust 结构是:
#[derive(NestedEncode, NestedDecode, TopEncode, TopDecode, TypeAbi)]
pub struct LotteryInfo<M: ManagedTypeApi> {
pub token_identifier: TokenIdentifier<M>,
pub ticket_price: BigUint<M>,
pub tickets_left: u32,
pub deadline: u64,
pub max_entries_per_user: u32,
pub prize_distribution: Vec<u8>,
pub whitelist: Vec<Address>,
pub prize_pool: BigUint<M>,
}
提示
再次注意,所有包含的值都采用了嵌套编码格式:
- 代币标识符的长度自动加上前缀
nested:
, - 大整数用
biguint:
语法给出,它预先考虑了它们的字节长度, - 小整数以完整长度给出,
- 列表的长度在开始时被显式编码,总是 4 字节(如
u32
)。
关于列举的一个说明
我们在 Rust 中使用两种类型的枚举:
- 简单枚举简单编码为
u8
- 复杂枚举的编码就像结构一样,在开头添加了一个
u8
判别式。
Rust 代码中没有明确的判别式,它们是自动生成的。鉴别从0
开始。
举例
这是 Multisig 合约测试的节略部分。
{
"step": "checkState",
"accounts": {
"sc:multisig": {
"storage": {
"str:action_data.item|u32:3": {
"1-discriminant": "0x06",
"2-amount": "u32:0",
"3-code": "nested:file:../test-contracts/adder.wasm",
"4-code_metadata": "0x0000",
"5-arguments": [
"u32:1",
"u32:2|1234"
]
},
"+": ""
},
"code": "file:../output/multisig.wasm"
},
"+": ""
}
}
在这个例子中,我们有一个SCDeploy
:
#[derive(NestedEncode, NestedDecode, TopEncode, TopDecode, TypeAbi)]
pub enum Action<M: ManagedTypeApi> {
Nothing,
AddBoardMember(ManagedAddress<M>),
AddProposer(ManagedAddress<M>),
RemoveUser(ManagedAddress<M>),
ChangeQuorum(usize),
SendEgld {
to: ManagedAddress<M>,
amount: BigUint<M>,
data: BoxedBytes,
},
SCDeploy {
amount: BigUint<M>,
code: ManagedBuffer<M>,
code_metadata: CodeMetadata,
arguments: Vec<BoxedBytes>,
},
SCCall {
to: ManagedAddress<M>,
egld_payment: BigUint<M>,
endpoint_name: BoxedBytes,
arguments: Vec<BoxedBytes>,
},
}