1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use cosmwasm_std::{
    HumanAddr, Uint128, Storage, StdResult, CosmosMsg,
    Binary, Api, Querier, Extern, Env,
};
// use cosmwasm_std::testing::{mock_env};  // mock_dependencies, MockStorage, MockApi, MockQuerier,

use secret_toolkit::{
    utils::{HandleCallback}, 
};

pub const RESPONSE_BLOCK_SIZE: usize = 256;

/////////////////////////////////////////////////////////////////////////////////
// Intercontract messages
/////////////////////////////////////////////////////////////////////////////////

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum InterContrMsg{
    /// Receiver interface function for SNIP721 contract. Msg to be sent to SNIP721 contract
    /// register that the message sending contract implements ReceiveNft and possibly
    /// BatchReceiveNft.  If a contract implements BatchReceiveNft, SendNft will always
    /// call BatchReceiveNft even if there is only one token transferred (the token_ids
    /// Vec will only contain one ID)
    RegisterReceiveNft {
        /// receving contract's code hash
        code_hash: String,
        /// optionally true if the contract also implements BatchReceiveNft.  Defaults
        /// to false if not specified
        also_implements_batch_receive_nft: Option<bool>,
        /// optional message length padding
        padding: Option<String>,
    },
    /// Message to send to SNIP721 contract
    TransferNft {
        recipient: HumanAddr, 
        token_id: String,
    },
    /// Message to send to SNIP721 contract
    SendNft {
        /// address to send the token to
        contract: HumanAddr,
        token_id: String,
        /// optional message to send with the (Batch)RecieveNft callback
        msg: Option<Binary>,
    },
    /// `Send` message to send to SNIP20 token address
    Send {
        recipient: HumanAddr,
        recipient_code_hash: Option<String>,
        amount: Uint128,
        msg: Option<Binary>,
        memo: Option<String>,
        padding: Option<String>,
    },
    /// `SendFrom` message to send to SNIP20 token address
    SendFrom {
        /// the address to send from
        owner: HumanAddr,
        recipient: HumanAddr,
        recipient_code_hash: Option<String>,
        amount: Uint128,
        msg: Option<Binary>,
        memo: Option<String>,
        padding: Option<String>,
    },
    /// `Transfer` message to send to SNIP20 token address
    Transfer {
        recipient: HumanAddr,
        amount: Uint128,
        memo: Option<String>,
        padding: Option<String>,
    },
    /// `TransferFrom` message to send to SNIP20 token address
    TransferFrom {
        owner: HumanAddr,
        recipient: HumanAddr,
        amount: Uint128,
        memo: Option<String>,
        padding: Option<String>,
    }
}

impl InterContrMsg {
    pub fn register_receive(code_hash: &String) -> Self {
        InterContrMsg::RegisterReceiveNft {
            code_hash: code_hash.to_string(),
            also_implements_batch_receive_nft: Some(true), 
            padding: None, // TODO add padding calculation
        }
    }
}

impl HandleCallback for InterContrMsg {
    const BLOCK_SIZE: usize = RESPONSE_BLOCK_SIZE;
}


/////////////////////////////////////////////////////////////////////////////////
// States
/////////////////////////////////////////////////////////////////////////////////

/// ftoken overall config which is stored in the ftoken contract. 
/// Sent as init in fractionalize tx, and stored in ftoken contract 
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Default)]
pub struct FtokenConf {
    /// Number of blocks that ftokens will be bonded after a vote (on reservation
    /// price or on proposals). Important to prevent vote spamming and manipulation 
    pub min_ftkn_bond_prd: u64,
    /// Proportion of ftoken ownership required before private metadata of underlying
    /// NFT can be queried by ftoken owner. This needs to be done with authenticated
    /// query, either through viewing keys or viewing permit. Unit in basis points (ie:
    /// 1/10_000)
    pub priv_metadata_view_threshold: u32,
    /// Configurations for auctions
    pub auc_conf: AucConf,
    /// Configurations for proposals
    pub prop_conf: PropConf,
}

/// ftoken config for bidding. Nested in a larger struct
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Default)]
pub struct AucConf {
    /// Determines the token that bids are made in (eg: sSCRT)
    pub bid_token: ContractInfo,
    /// Number of blocks that a bid remains live before a finalize_vote_count tx can be called
    pub auc_period: u64,
    /// User needs to vote a reservation price within this boundary. Boundary is the percentage above and below
    /// current reservation price.
    /// Floor = `current reservation price` * 100 / `minmax_boundary`.
    /// Ceiling = `current reservation price` * `minmax_boundary` / 100.
    pub resv_boundary: u32,
    /// Min bid increment proportion in basis points ie: 1/10_000. So a setting of 10 means that if the current highest bid
    /// is 100_000 tokens, the next bid needs to be at least 1/1000 higher, or 100_100 tokens  
    pub min_bid_inc: u32,
    /// Proportion of ftoken OF TOTAL SUPPLY before NFT gets unlocked. Unit in basis points (1/1000)
    pub unlock_threshold: Uint128,
}

/// ftoken contract config for dao proposals. Nested in a larger struct
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Default)]
pub struct PropConf {
    /// Minimum ftoken stake to make a proposal
    pub min_stake: Uint128,
    /// Number of blocks that a proposal remains live before a finalization tx can be called
    pub vote_period: u64,
    /// Proportion of ftoken-weighted votes OF TOTAL SUPPLY before quorum is reached. Unit in basis points (1/1000)
    pub vote_quorum: Uint128,
    /// Proportion of ftoken-weighted votes OF TOTAL SUPPLY that needs to vote `veto` for a veto to apply. Unit in basis points (1/1000)
    pub veto_threshold: Uint128,
}

/// ftoken contract information, stored in ftoken contracts
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct FtokenInfo {
    /// ftoken contract instance information, created at initialization
    pub instance: FtokenInstance,
    /// Is underlying nft still in the vault (ie: fractionalized)
    pub vault_active: bool,
}

/// ftoken contract information created at initialization, stored directly in fractionalizer contract, also within
/// the FtokenInfo struct stored in ftoken contracts
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct FtokenInstance {
    /// ftoken contract index from the fractionalizer contract's perspective 
    pub ftkn_idx: u32,
    /// Address which deposited the nft
    pub depositor: HumanAddr,
    /// Code hash and address of ftoken contract
    pub ftoken_contr: ContractInfo,
    /// Information on the underlying nft that was initially deposited
    pub init_nft_info: UndrNftInfo,
    /// Name of ftoken
    pub name: String,
    /// Symbol of ftoken
    pub symbol: String,
    /// Decimal of ftoken
    pub decimals: u8,
}

/// Part of initialization message sent by USERS to fractionalizer 
/// initial configuration of fractionalized tokens
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct FtokenInit {  //FtokenConf
    /// Name of the ftoken
    pub name: String,
    /// Symbol of the ftoken
    pub symbol: String,
    /// Supply in the lowest denomination
    pub supply: Uint128,
    /// Determines the lowest denomination
    pub decimals: u8,
    /// Label String of the ftoken contract which will be instantiated. Instantiation of the new ftoken
    /// contract will fail if the label already exists on another contract on Secret Network 
    pub contract_label: String, 
    /// Initial reservation price which determines the initial min and max reservation price vote
    /// for the first user who votes on reservation price
    pub init_resv_price: Uint128,
    /// ftoken config which is stored in the ftoken contract
    pub ftkn_conf: FtokenConf,
}

/// Part of information sent from fractionalizer contract to ftoken contract on instantiation tx
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Default)]
pub struct FtokenContrInit {
    /// Index of ftoken contract. Starts from 0 
    pub ftkn_idx: u32,
    /// Depositor of NFT into fractionalizer
    pub depositor: HumanAddr,
    /// Contract hash of fractionalizer
    pub fract_hash: String,
    /// Underlying NFT info
    pub nft_info: UndrNftInfo,
    /// Initial reservation price which determines the initial min and max reservation price vote
    /// for the first user who votes on reservation price
    pub init_resv_price: Uint128,
    /// ftoken config which is stored in the ftoken contract
    pub ftkn_conf: FtokenConf,
}

/// code hash and address of a contract
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Default)]
pub struct ContractInfo {
    /// Contract's code hash string
    pub code_hash: String,
    /// Contract's address in HumanAddr
    pub address: HumanAddr,
}

/// underlying NFT information
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Default)]
pub struct UndrNftInfo {
    /// Token id of underlying nft
    pub token_id: String,
    /// Contract code hash and address of contract of underlying nft 
    pub nft_contr: ContractInfo,
}

/////////////////////////////////////////////////////////////////////////////////
// functions
/////////////////////////////////////////////////////////////////////////////////

/// Creates a `SendNft` cosmos msg to be sent to NFT contract 
/// * `deps` - mutable reference to Extern containing all the contract's external dependencies
/// * `env` - Env of contract's environment
/// * `contract` - HumanAddr (String) of receiver of nft, ie: ftoken contract address
pub fn send_nft_msg<S: Storage, A: Api, Q: Querier>(
    _deps: &mut Extern<S, A, Q>,
    _env: Env,
    nft_contr_addr: HumanAddr,
    nft_contr_hash: String,
    contract: HumanAddr,
    token_id: String,
    msg: Option<Binary>,
) -> StdResult<CosmosMsg> {

    let contract_msg = InterContrMsg::SendNft {
            // address of recipient of nft
            contract, 
            token_id,
            msg,
        };

    let cosmos_msg = contract_msg.to_cosmos_msg(
        nft_contr_hash, 
        HumanAddr(nft_contr_addr.to_string()), 
        None
    )?;

    Ok(cosmos_msg)
}