osmosis-labs / isotonic

Smart Contracts for the Lendex Protocol
MIT License
1 stars 0 forks source link

Minimal Lendex Token (no rebasing) #2

Closed ethanfrey closed 2 years ago

ethanfrey commented 2 years ago

Please reference cw20-base... much can be copied

The lender token will be used both for the collateral (l-Tokens) as well as the loans (b-Tokens). The difference is the logic in minting, burning, and allowing transfer. That logic will be delegated to another contract that has a more holistic view of the system, called the "Market". For now, we develop the common parts.

They should be a minimal subset of cw20, which we extend. Init something like this (note initial balance is 0):

#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
pub struct InstantiateMsg {
    pub name: String,
    pub symbol: String,
    pub decimals: u8,
    /// controller is allowed to mint, burn, rebase and must be checked with to enable transfer
    pub controller: String,
}

We will implement this without allowances for now:

#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
    /// Transfer is a base message to move tokens to another account without triggering actions
    Transfer { recipient: String, amount: Uint128 },
    /// Burn is a base message to destroy tokens forever
    Burn { amount: Uint128 },
    /// Send is a base message to transfer tokens to a contract and trigger an action
    /// on the receiving contract.
    Send {
        contract: String,
        amount: Uint128,
        msg: Binary,
    },
    /// Only with the "mintable" extension. If authorized, creates amount new tokens
    /// and adds to the recipient balance.
    Mint { recipient: String, amount: Uint128 },
}

Burn and Mint should only be callable by the controller (with no further restrictions). Transfer and Send will call the controller address with the following query and only allow the transfer on success.

#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ControllerQuery {
    CanTransfer {
        /// this is the address of the contract itself that calls "CanTransfer"
        token: String,
        /// the address that wishes to transfer
        account: String,
        /// the amount we wish to transfer from their account
        amount: Uint128,
    }
}

Please provide a mock contract for multi-test testing that can either return true, false, or amount < X.

We also support the following queries:

#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
    /// Returns the current balance of the given address, 0 if unset.
    /// Return type: BalanceResponse.
    Balance { address: String },
    /// Returns metadata on the contract - name, decimals, supply, etc.
    /// Return type: TokenInfoResponse.
    TokenInfo {},
}

#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
pub struct BalanceResponse {
    pub balance: Uint128,
}

#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
pub struct TokenInfoResponse {
    pub name: String,
    pub symbol: String,
    pub decimals: u8,
    pub controller: Addr,
    pub total_supply: Uint128,
}

NB: I recommend storing the total_supply in it's own Item separate from the rest of the config, just combining them for queries. You can look at cw20-base to see an implementation of all these (feel free to copy useful code)

hashedone commented 2 years ago

@ethanfrey already question: why ControlQuery::CanTransfer has the token field? Why the MessageInfo::sender is not used as it should be more accurate?

ethanfrey commented 2 years ago

Queries have no sender, only messages do.

So, when querying for a balance, you must specify which account