|  | 
|  | 1 | +// SPDX-License-Identifier: Apache-2.0 | 
|  | 2 | +// | 
|  | 3 | +// Copyright © 2017 Trust Wallet. | 
|  | 4 | + | 
|  | 5 | +use crate::address::CosmosAddress; | 
|  | 6 | +use crate::modules::serializer::protobuf_serializer::build_coin; | 
|  | 7 | +use crate::proto::cosmwasm; | 
|  | 8 | +use crate::transaction::message::{CosmosMessage, JsonMessage, ProtobufMessage}; | 
|  | 9 | +use crate::transaction::Coin; | 
|  | 10 | +use serde::Serialize; | 
|  | 11 | +use serde_json::{json, Value as Json}; | 
|  | 12 | +use tw_coin_entry::error::prelude::*; | 
|  | 13 | +use tw_memory::Data; | 
|  | 14 | +use tw_proto::to_any; | 
|  | 15 | + | 
|  | 16 | +const DEFAULT_INSTANTIATE_JSON_MSG_TYPE: &str = "wasm/MsgInstantiateContract"; | 
|  | 17 | + | 
|  | 18 | +#[derive(Clone, Serialize)] | 
|  | 19 | +#[serde(untagged)] | 
|  | 20 | +pub enum InstantiateMsg { | 
|  | 21 | +    /// Either a regular string or a stringified JSON object. | 
|  | 22 | +    String(String), | 
|  | 23 | +    /// JSON object with a type. | 
|  | 24 | +    Json(Json), | 
|  | 25 | +} | 
|  | 26 | + | 
|  | 27 | +impl InstantiateMsg { | 
|  | 28 | +    /// Tries to convert [`InstantiateMsg::String`] to [`InstantiateMsg::Json`], otherwise returns the same object. | 
|  | 29 | +    pub fn try_to_json(&self) -> InstantiateMsg { | 
|  | 30 | +        if let InstantiateMsg::String(s) = self { | 
|  | 31 | +            if let Ok(json) = serde_json::from_str(s) { | 
|  | 32 | +                return InstantiateMsg::Json(json); | 
|  | 33 | +            } | 
|  | 34 | +        } | 
|  | 35 | +        self.clone() | 
|  | 36 | +    } | 
|  | 37 | + | 
|  | 38 | +    /// Creates an [`InstantiateMsg`] from a serializable payload. | 
|  | 39 | +    pub fn json<Payload: Serialize>(payload: Payload) -> SigningResult<InstantiateMsg> { | 
|  | 40 | +        let payload = serde_json::to_value(payload) | 
|  | 41 | +            .tw_err(SigningErrorType::Error_internal) | 
|  | 42 | +            .context("Error serializing message payload to JSON")?; | 
|  | 43 | +        Ok(InstantiateMsg::Json(payload)) | 
|  | 44 | +    } | 
|  | 45 | + | 
|  | 46 | +    /// Converts the message to bytes. | 
|  | 47 | +    pub fn to_bytes(&self) -> Data { | 
|  | 48 | +        match self { | 
|  | 49 | +            InstantiateMsg::String(ref s) => s.as_bytes().to_vec(), | 
|  | 50 | +            InstantiateMsg::Json(ref j) => j.to_string().as_bytes().to_vec(), | 
|  | 51 | +        } | 
|  | 52 | +    } | 
|  | 53 | +} | 
|  | 54 | + | 
|  | 55 | +/// This message is used for instantiating a new CosmWasm contract. | 
|  | 56 | +#[derive(Serialize)] | 
|  | 57 | +pub struct WasmInstantiateContractMessage<Address: CosmosAddress> { | 
|  | 58 | +    pub sender: Address, | 
|  | 59 | +    /// (Optional) The address with permission to perform migrations. | 
|  | 60 | +    /// If no admin is provided, this field will be an empty string in the protobuf. | 
|  | 61 | +    pub admin: Option<Address>, | 
|  | 62 | +    /// The Code ID referencing the stored contract code. | 
|  | 63 | +    pub code_id: u64, | 
|  | 64 | +    /// A human-readable label for the contract instance. | 
|  | 65 | +    pub label: String, | 
|  | 66 | +    /// A JSON-encoded initialization message. | 
|  | 67 | +    pub msg: InstantiateMsg, | 
|  | 68 | +    /// A list of coins to be sent along with the instantiation. | 
|  | 69 | +    pub funds: Vec<Coin>, | 
|  | 70 | +} | 
|  | 71 | + | 
|  | 72 | +impl<Address: CosmosAddress> CosmosMessage for WasmInstantiateContractMessage<Address> { | 
|  | 73 | +    fn to_proto(&self) -> SigningResult<ProtobufMessage> { | 
|  | 74 | +        let proto_msg = cosmwasm::wasm::v1::MsgInstantiateContract { | 
|  | 75 | +            sender: self.sender.to_string().into(), | 
|  | 76 | +            admin: self | 
|  | 77 | +                .admin | 
|  | 78 | +                .as_ref() | 
|  | 79 | +                .map_or("".into(), |admin| admin.to_string().into()), | 
|  | 80 | +            code_id: self.code_id, | 
|  | 81 | +            label: self.label.clone().into(), | 
|  | 82 | +            msg: self.msg.to_bytes().into(), | 
|  | 83 | +            // Use "init_funds" here, matching the protobuf definition. | 
|  | 84 | +            init_funds: self.funds.iter().map(build_coin).collect(), | 
|  | 85 | +        }; | 
|  | 86 | +        Ok(to_any(&proto_msg)) | 
|  | 87 | +    } | 
|  | 88 | + | 
|  | 89 | +    fn to_json(&self) -> SigningResult<JsonMessage> { | 
|  | 90 | +        let value = json!({ | 
|  | 91 | +            "sender": self.sender, | 
|  | 92 | +            "admin": self.admin, | 
|  | 93 | +            "code_id": self.code_id, | 
|  | 94 | +            "label": self.label, | 
|  | 95 | +            "msg": self.msg.try_to_json(), | 
|  | 96 | +            "init_funds": self.funds, | 
|  | 97 | +        }); | 
|  | 98 | +        Ok(JsonMessage { | 
|  | 99 | +            msg_type: DEFAULT_INSTANTIATE_JSON_MSG_TYPE.to_string(), | 
|  | 100 | +            value, | 
|  | 101 | +        }) | 
|  | 102 | +    } | 
|  | 103 | +} | 
0 commit comments