token_bindings/
msg.rs

1use crate::types::Metadata;
2use cosmwasm_schema::cw_serde;
3use cosmwasm_std::{Binary, CosmosMsg, CustomMsg, StdResult, Uint128};
4
5/// Special messages to be supported by any chain that supports token_factory
6#[cw_serde]
7pub enum TokenFactoryMsg {
8    /// CreateDenom creates a new factory denom, of denomination:
9    /// factory/{creating contract bech32 address}/{Subdenom}
10    /// Subdenom can be of length at most 44 characters, in [0-9a-zA-Z./]
11    /// Empty subdenoms are valid.
12    /// The (creating contract address, subdenom) pair must be unique.
13    /// The created denom's admin is the creating contract address,
14    /// but this admin can be changed using the UpdateAdmin binding.
15    ///
16    /// If you set an initial metadata here, this is equivalent
17    /// to calling SetMetadata directly on the returned denom.
18    CreateDenom {
19        subdenom: String,
20        // TODO investigate if this is interoperable with Osmosis
21        metadata: Option<Metadata>,
22    },
23    /// ChangeAdmin changes the admin for a factory denom.
24    /// Can only be called by the current contract admin.
25    /// If the NewAdminAddress is empty, the denom will have no admin.
26    ChangeAdmin {
27        denom: String,
28        new_admin_address: String,
29    },
30    /// Contracts can mint native tokens for an existing factory denom
31    /// that they are the admin of.
32    MintTokens {
33        denom: String,
34        amount: Uint128,
35        mint_to_address: String,
36    },
37    /// Contracts can burn native tokens for an existing factory denom
38    /// tshat they are the admin of.
39    BurnTokens {
40        denom: String,
41        amount: Uint128,
42        burn_from_address: String,
43    },
44    /// Contracts can force transfer tokens for an existing factory denom
45    /// that they are the admin of.    
46    ForceTransfer {
47        denom: String,
48        amount: Uint128,
49        from_address: String,
50        to_address: String,
51    },
52    SetMetadata {
53        denom: String,
54        metadata: Metadata,
55    },
56}
57
58impl TokenFactoryMsg {
59    pub fn mint_contract_tokens(denom: String, amount: Uint128, mint_to_address: String) -> Self {
60        TokenFactoryMsg::MintTokens {
61            denom,
62            amount,
63            mint_to_address,
64        }
65    }
66
67    pub fn burn_contract_tokens(denom: String, amount: Uint128, burn_from_address: String) -> Self {
68        TokenFactoryMsg::BurnTokens {
69            denom,
70            amount,
71            burn_from_address,
72        }
73    }
74
75    pub fn force_transfer_tokens(
76        denom: String,
77        amount: Uint128,
78        from_address: String,
79        to_address: String,
80    ) -> Self {
81        TokenFactoryMsg::ForceTransfer {
82            denom,
83            amount,
84            from_address,
85            to_address,
86        }
87    }
88}
89
90impl From<TokenFactoryMsg> for CosmosMsg<TokenFactoryMsg> {
91    fn from(msg: TokenFactoryMsg) -> CosmosMsg<TokenFactoryMsg> {
92        CosmosMsg::Custom(msg)
93    }
94}
95
96impl CustomMsg for TokenFactoryMsg {}
97
98/// This is in the data field in the reply from a TokenFactoryMsg::CreateDenom SubMsg
99/// Custom code to parse from protobuf with minimal wasm bytecode bloat
100pub struct CreateDenomResponse {
101    pub new_token_denom: String,
102}
103
104impl CreateDenomResponse {
105    /// Call this to process data field from the SubMsg data field
106    pub fn from_reply_data(data: Binary) -> StdResult<Self> {
107        // Manual protobuf decoding
108        let mut data = Vec::from(data);
109        // Parse contract addr
110        let new_token_denom = copied_from_cw_utils::parse_protobuf_string(&mut data, 1)?;
111        Ok(CreateDenomResponse { new_token_denom })
112    }
113
114    pub fn encode(&self) -> StdResult<Binary> {
115        // TODO
116        Ok(b"".into())
117    }
118}
119
120// FIXME: just import cw_utils::parse_protobuf_string when it is exported
121mod copied_from_cw_utils {
122    use cosmwasm_std::{StdError, StdResult};
123
124    // Protobuf wire types (https://developers.google.com/protocol-buffers/docs/encoding)
125    const WIRE_TYPE_LENGTH_DELIMITED: u8 = 2;
126    // Up to 9 bytes of varints as a practical limit (https://github.com/multiformats/unsigned-varint#practical-maximum-of-9-bytes-for-security)
127    const VARINT_MAX_BYTES: usize = 9;
128
129    pub fn parse_protobuf_string(data: &mut Vec<u8>, field_number: u8) -> StdResult<String> {
130        let str_field = parse_protobuf_length_prefixed(data, field_number)?;
131        Ok(String::from_utf8(str_field)?)
132    }
133
134    /// Helper function to parse length-prefixed protobuf fields.
135    /// The remaining of the data is kept in the data parameter.
136    fn parse_protobuf_length_prefixed(data: &mut Vec<u8>, field_number: u8) -> StdResult<Vec<u8>> {
137        if data.is_empty() {
138            return Ok(vec![]);
139        };
140        let mut rest_1 = data.split_off(1);
141        let wire_type = data[0] & 0b11;
142        let field = data[0] >> 3;
143
144        if field != field_number {
145            return Err(StdError::parse_err(
146                "length_prefix_field",
147                format!(
148                    "failed to decode Protobuf message: invalid field #{} for field #{}",
149                    field, field_number
150                ),
151            ));
152        }
153        if wire_type != WIRE_TYPE_LENGTH_DELIMITED {
154            return Err(StdError::parse_err(
155                "length_prefix_field",
156                format!(
157                    "failed to decode Protobuf message: field #{}: invalid wire type {}",
158                    field_number, wire_type
159                ),
160            ));
161        }
162
163        let len = parse_protobuf_varint(&mut rest_1, field_number)?;
164        if rest_1.len() < len {
165            return Err(StdError::parse_err(
166                "length_prefix_field",
167                format!(
168                    "failed to decode Protobuf message: field #{}: message too short",
169                    field_number
170                ),
171            ));
172        }
173        *data = rest_1.split_off(len);
174
175        Ok(rest_1)
176    }
177
178    /// Base128 varint decoding.
179    /// The remaining of the data is kept in the data parameter.
180    fn parse_protobuf_varint(data: &mut Vec<u8>, field_number: u8) -> StdResult<usize> {
181        let data_len = data.len();
182        let mut len: u64 = 0;
183        let mut i = 0;
184        while i < VARINT_MAX_BYTES {
185            if data_len == i {
186                return Err(StdError::parse_err(
187                    "varint",
188                    format!(
189                        "failed to decode Protobuf message: field #{}: varint data too short",
190                        field_number
191                    ),
192                ));
193            }
194            len += ((data[i] & 0x7f) as u64) << (i * 7);
195            if data[i] & 0x80 == 0 {
196                break;
197            }
198            i += 1;
199        }
200        if i == VARINT_MAX_BYTES {
201            return Err(StdError::parse_err(
202                "varint",
203                format!(
204                    "failed to decode Protobuf message: field #{}: varint data too long",
205                    field_number
206                ),
207            ));
208        }
209        *data = data[i + 1..].to_owned();
210
211        Ok(len as usize) // Gently fall back to the arch's max addressable size
212    }
213}