pink_subrpc/contracts/
objects.rs

1use crate::transaction::MultiAddress;
2use alloc::vec::Vec;
3use scale::{Compact, Decode, Encode};
4
5pub(crate) fn build_contract_call<AccountId, AccountIndex, Balance, ARGS: Encode>(
6    contract_id: AccountId,
7    contract_method: [u8; 4],
8    contract_args: Option<&ARGS>,
9    value: Balance,
10    gas_limit: WeightV2,
11) -> ContractCall<AccountId, AccountIndex, Balance> {
12    let storage_deposit_limit = None;
13
14    let mut data = Vec::new();
15    contract_method.encode_to(&mut data);
16    if let Some(args) = contract_args {
17        let mut encoded_contract_args = args.encode();
18        data.append(&mut encoded_contract_args);
19    }
20
21    ContractCall {
22        dest: MultiAddress::Id(contract_id),
23        value,
24        gas_limit,
25        storage_deposit_limit,
26        data,
27    }
28}
29
30pub(crate) fn build_contract_query<AccountId, Balance, ARGS: Encode>(
31    origin: AccountId,
32    contract_id: AccountId,
33    contract_method: [u8; 4],
34    contract_args: Option<&ARGS>,
35    value: Balance,
36) -> ContractQuery<AccountId, Balance> {
37    let mut data = Vec::new();
38    contract_method.encode_to(&mut data);
39    if let Some(args) = contract_args {
40        let mut encoded_contract_args = args.encode();
41        data.append(&mut encoded_contract_args);
42    }
43
44    ContractQuery {
45        origin,
46        dest: contract_id,
47        value,
48        gas_limit: None,
49        storage_deposit_limit: None,
50        data,
51    }
52}
53
54/// Struct used to send an encoded transaction to the contract
55#[derive(Encode, Decode, PartialEq, Eq, Clone, Debug)]
56#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
57pub(crate) struct ContractCall<AccountId, AccountIndex, Balance> {
58    /// Contract address
59    dest: MultiAddress<AccountId, AccountIndex>,
60    /// Only for payable messages, call will fail otherwise
61    #[codec(compact)]
62    value: Balance,
63    /// Maximum gas to be consumed. If it is too small the extrinsic will fail
64    gas_limit: WeightV2,
65    /// A limit to how much Balance to be used to pay for the storage created by the contract call.
66    /// if None is passed, unlimited balance can be used
67    storage_deposit_limit: Option<Compact<Balance>>,
68    /// data: method name + args
69    data: Vec<u8>,
70}
71
72/// Gas to be consumed: gaz = ref_time * proof_size
73#[derive(Encode, Decode, PartialEq, Eq, Clone, Copy, Debug)]
74#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
75pub struct WeightV2 {
76    #[codec(compact)]
77    pub ref_time: u64,
78    #[codec(compact)]
79    pub proof_size: u64,
80}
81
82/// Struct used to query a wasm contract
83#[derive(Encode, Decode, PartialEq, Eq, Clone, Debug)]
84#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
85pub(crate) struct ContractQuery<AccountId, Balance> {
86    origin: AccountId,
87    dest: AccountId,
88    value: Balance,
89    gas_limit: Option<WeightV2>,
90    storage_deposit_limit: Option<Balance>,
91    data: Vec<u8>,
92}
93
94/// Result when we query a wasm contract
95#[derive(Encode, Decode, Clone, Debug)]
96#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
97pub struct ContractQueryResult<Error, Balance> {
98    pub(crate) gas_consumed: WeightV2,
99    pub(crate) gas_required: WeightV2,
100    pub(crate) storage_deposit: StorageDeposit<Balance>,
101    pub(crate) debug_message: Vec<u8>,
102    pub(crate) result: ExecReturnValue<Error>,
103}
104
105#[derive(Encode, Decode, Clone, Debug)]
106#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
107pub(crate) struct ExecReturnValue<Error> {
108    pub(crate) flags: u32,
109    pub(crate) data: Result<Vec<u8>, Error>,
110}
111
112#[derive(Encode, Decode, PartialEq, Eq, Clone, Debug)]
113#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
114pub(crate) enum StorageDeposit<Balance> {
115    Refund(Balance),
116    Charge(Balance),
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122
123    #[test]
124    fn test_encode_contract_call() {
125        let contract_id: [u8; 32] =
126            hex_literal::hex!("f77bfd16d61d39dcd8c4413ac88642354f5726bb5915bf52bc4f502a671f1aa5");
127        let contract_method = hex_literal::hex!("1d32619f");
128        let contract_args = Some(&2i32);
129
130        let call: ContractCall<[u8; 32], u32, u128> = build_contract_call(
131            contract_id,
132            contract_method,
133            contract_args,
134            0u128,
135            WeightV2 {
136                ref_time: 3991666688u64,
137                proof_size: 131072u64,
138            },
139        );
140
141        let encoded_call = Encode::encode(&call);
142        let expected =  hex_literal::hex!("00f77bfd16d61d39dcd8c4413ac88642354f5726bb5915bf52bc4f502a671f1aa500030000eced0200080000201d32619f02000000");
143        assert_eq!(&expected, encoded_call.as_slice());
144    }
145
146    #[test]
147    fn test_encode_contract_query() {
148        let origin: [u8; 32] =
149            hex_literal::hex!("d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d");
150        let contract_id: [u8; 32] =
151            hex_literal::hex!("f77bfd16d61d39dcd8c4413ac88642354f5726bb5915bf52bc4f502a671f1aa5");
152        let contract_method = hex_literal::hex!("2f865bd9");
153        let contract_args: Option<&()> = None; // no args
154
155        let call = build_contract_query(origin, contract_id, contract_method, contract_args, 0u128);
156
157        let encoded_call = Encode::encode(&call);
158        let expected =  hex_literal::hex!("d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27df77bfd16d61d39dcd8c4413ac88642354f5726bb5915bf52bc4f502a671f1aa5000000000000000000000000000000000000102f865bd9");
159        assert_eq!(&expected, encoded_call.as_slice());
160    }
161
162    /// this struct should match with the error returned by the contract
163    #[derive(Decode, Debug)]
164    enum Error {
165        Error1,
166        Error2,
167    }
168
169    #[test]
170    fn test_decode_contract_query_result() {
171        let result =  hex_literal::hex!("d6b2469e3a3d0100030000eced020008000100000000000000000000000000000000000000000000140003000000");
172
173        let contract_query_result =
174            <ContractQueryResult<Error, u128>>::decode(&mut result.as_slice()).unwrap();
175
176        assert_eq!(663858357u64, contract_query_result.gas_consumed.ref_time);
177        assert_eq!(20302u64, contract_query_result.gas_consumed.proof_size);
178        assert_eq!(3991666688u64, contract_query_result.gas_required.ref_time);
179        assert_eq!(131072u64, contract_query_result.gas_required.proof_size);
180        assert_eq!(
181            StorageDeposit::Charge(0),
182            contract_query_result.storage_deposit
183        );
184        assert_eq!(0u32, contract_query_result.result.flags);
185        assert!(contract_query_result.result.data.is_ok());
186        let result = contract_query_result.result.data.unwrap();
187        let data = <Result<i32, Error>>::decode(&mut result.as_slice()).unwrap();
188
189        assert!(data.is_ok());
190        assert_eq!(3i32, data.unwrap());
191    }
192}