Skip to content

Commit

Permalink
feat: implement token contract
Browse files Browse the repository at this point in the history
  • Loading branch information
foxpy committed Dec 10, 2023
1 parent c7e374f commit 1b3e902
Show file tree
Hide file tree
Showing 13 changed files with 652 additions and 18 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
inputs:
toolchain:
description: 'Default Rust Toolchain'
default: "1.71.0"
default: "1.73.0"
required: true
type: string
target:
Expand All @@ -31,7 +31,7 @@ on:
type: string

env:
TOOLCHAIN: ${{ inputs.toolchain || '1.71.0' }}
TOOLCHAIN: ${{ inputs.toolchain || '1.73.0' }}
TARGET: ${{ inputs.target || 'wasm32-unknown-unknown' }}
REF: ${{ github.event_name == 'push' && github.ref || inputs.branch || 'main' }}
ID: ${{ inputs.id || 'scheduled' }}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
fetch-depth: 1
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.71.0
toolchain: 1.73.0
components: clippy
profile: minimal
override: true
Expand All @@ -31,7 +31,7 @@ jobs:
fetch-depth: 1
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.71.0
toolchain: 1.73.0
components: rustfmt
profile: minimal
override: true
Expand All @@ -49,7 +49,7 @@ jobs:
fetch-depth: 1
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.71.0
toolchain: 1.73.0
profile: minimal
- run: cargo fetch --verbose
- run: cargo build
Expand Down
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 23 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
[workspace]
members = ["contracts/interchain-interceptor", "contracts/interchain-interceptor-authz", "packages/interchain-interceptor-base"]

[workspace.dependencies]
cw-ownable = "0.5.1"
thiserror = "1.0.50"
members = [
"contracts/interchain-interceptor",
"contracts/interchain-interceptor-authz",
"contracts/token",
"packages/interchain-interceptor-base"
]
resolver = "2"

[profile.release]
opt-level = 3
debug = false
rpath = false
lto = true
debug-assertions = false
codegen-units = 1
panic = 'abort'
incremental = false
overflow-checks = true

[workspace.dependencies]
cosmwasm-std = { version = "1.5.0", default-features = false }
cosmwasm-schema = { version = "1.5.0", default-features = false }
cw-storage-plus = { version = "1.2.0", default-features = false }
cw2 = { version = "1.1.2", default-features = false }
cw-utils = { version = "1.0.3", default-features = false }
neutron-sdk = { git = "https://github.com/neutron-org/neutron-sdk", branch = "sdk/47", default-features = false }
cw-ownable = "0.5.1"
thiserror = "1.0.50"
12 changes: 9 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@ clippy:
fmt:
@cargo fmt -- --check

doc:
@cargo doc

compile:
@./build_release.sh
@docker run --rm -v "$(CURDIR)":/code \
--mount type=volume,source="$(notdir $(CURDIR))_cache",target=/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
--platform linux/amd64 \
cosmwasm/workspace-optimizer:0.15.0

check_contracts:
@cargo install cosmwasm-check
@cosmwasm-check --available-capabilities iterator,staking,stargate,neutron,cosmwasm_1_1 artifacts/*.wasm

build: schema clippy test fmt compile check_contracts

build: schema clippy test fmt doc compile check_contracts
4 changes: 0 additions & 4 deletions build_release.sh

This file was deleted.

2 changes: 2 additions & 0 deletions contracts/token/.cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[alias]
schema = "run --bin lido-token-schema"
22 changes: 22 additions & 0 deletions contracts/token/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
authors = [ "Murad Karammaev <[email protected]>" ]
description = "Contract module which mints and burns tokens"
edition = "2021"
name = "lido-token"
version = "1.0.0"

[lib]
crate-type = [ "cdylib", "rlib" ]

[features]
backtraces = [ "cosmwasm-std/backtraces" ]
library = []

[dependencies]
cosmwasm-std = { workspace = true }
cosmwasm-schema = { workspace = true }
cw-storage-plus = { workspace = true }
cw2 = { workspace = true }
cw-utils = { workspace = true }
neutron-sdk = { workspace = true }
thiserror = { workspace = true }
1 change: 1 addition & 0 deletions contracts/token/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Token contract
11 changes: 11 additions & 0 deletions contracts/token/src/bin/lido-token-schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use cosmwasm_schema::write_api;
use lido_token::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};

fn main() {
write_api! {
instantiate: InstantiateMsg,
query: QueryMsg,
execute: ExecuteMsg,
migrate: MigrateMsg
}
}
197 changes: 197 additions & 0 deletions contracts/token/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
use cosmwasm_std::{
attr, ensure_eq, ensure_ne, to_json_binary, Addr, Attribute, Binary, Deps, DepsMut, Env, Event,
MessageInfo, Reply, Response, StdError, SubMsg, Uint128,
};
use cw_storage_plus::Item;
use neutron_sdk::{
bindings::{msg::NeutronMsg, query::NeutronQuery},
query::token_factory::query_full_denom,
};

#[cfg(test)]
mod tests;

#[derive(thiserror::Error, Debug, PartialEq)]
pub enum ContractError {
#[error("{0}")]
Std(#[from] StdError),

#[error("{0}")]
NeutronError(#[from] neutron_sdk::NeutronError),

#[error("{0}")]
PaymentError(#[from] cw_utils::PaymentError),

#[error("unauthorized")]
Unauthorized,

#[error("nothing to mint")]
NothingToMint,

#[error("unknown reply id: {id}")]
UnknownReplyId { id: u64 },
}

pub type ContractResult<T> = Result<T, ContractError>;

#[cosmwasm_schema::cw_serde]
pub struct InstantiateMsg {
pub core: String,
pub subdenom: String,
}

#[cosmwasm_schema::cw_serde]
pub enum ExecuteMsg {
Mint { amount: Uint128, receiver: String },
Burn,
}

#[cosmwasm_schema::cw_serde]
#[derive(cosmwasm_schema::QueryResponses)]
pub enum QueryMsg {
#[returns(ConfigResponse)]
Config {},
}

#[cosmwasm_schema::cw_serde]
pub struct ConfigResponse {
pub core: String,
pub denom: String,
}

#[cosmwasm_schema::cw_serde]
pub enum MigrateMsg {}

const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME");
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

const CORE: Item<Addr> = Item::new("core");
const DENOM: Item<String> = Item::new("denom");

const CREATE_DENOM_REPLY_ID: u64 = 1;

fn response<A: Into<Attribute>>(
ty: &str,
attrs: impl IntoIterator<Item = A>,
) -> Response<NeutronMsg> {
Response::new().add_event(Event::new(format!("{}-{}", CONTRACT_NAME, ty)).add_attributes(attrs))
}

fn attr_coin(
key: impl Into<String>,
amount: impl std::fmt::Display,
denom: impl std::fmt::Display,
) -> Attribute {
attr(key, format!("{}{}", amount, denom))
}

#[cfg_attr(not(feature = "library"), cosmwasm_std::entry_point)]
pub fn instantiate(
deps: DepsMut<NeutronQuery>,
_env: Env,
_info: MessageInfo,
msg: InstantiateMsg,
) -> ContractResult<Response<NeutronMsg>> {
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;

let core = deps.api.addr_validate(&msg.core)?;
CORE.save(deps.storage, &core)?;

DENOM.save(deps.storage, &msg.subdenom)?;
let create_denom_msg = SubMsg::reply_on_success(
NeutronMsg::submit_create_denom(&msg.subdenom),
CREATE_DENOM_REPLY_ID,
);

Ok(response(
"instantiate",
[attr("core", core), attr("subdenom", msg.subdenom)],
)
.add_submessage(create_denom_msg))
}

#[cfg_attr(not(feature = "library"), cosmwasm_std::entry_point)]
pub fn execute(
deps: DepsMut<NeutronQuery>,
_env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> ContractResult<Response<NeutronMsg>> {
let core = CORE.load(deps.storage)?;
ensure_eq!(info.sender, core, ContractError::Unauthorized);

match msg {
ExecuteMsg::Mint { amount, receiver } => mint(deps, amount, receiver),
ExecuteMsg::Burn => burn(deps, info),
}
}

fn mint(
deps: DepsMut<NeutronQuery>,
amount: Uint128,
receiver: String,
) -> ContractResult<Response<NeutronMsg>> {
ensure_ne!(amount, Uint128::zero(), ContractError::NothingToMint);

let denom = DENOM.load(deps.storage)?;
let mint_msg = NeutronMsg::submit_mint_tokens(&denom, amount, &receiver);

Ok(response(
"execute-mint",
[
attr_coin("amount", amount, denom),
attr("receiver", receiver),
],
)
.add_message(mint_msg))
}

fn burn(deps: DepsMut<NeutronQuery>, info: MessageInfo) -> ContractResult<Response<NeutronMsg>> {
let denom = DENOM.load(deps.storage)?;
let amount = cw_utils::must_pay(&info, &denom)?;

let burn_msg = NeutronMsg::submit_burn_tokens(&denom, amount);

Ok(response("execute-burn", [attr_coin("amount", amount, denom)]).add_message(burn_msg))
}

#[cfg_attr(not(feature = "library"), cosmwasm_std::entry_point)]
pub fn query(deps: Deps<NeutronQuery>, _env: Env, msg: QueryMsg) -> ContractResult<Binary> {
match msg {
QueryMsg::Config {} => {
let core = CORE.load(deps.storage)?.into_string();
let denom = DENOM.load(deps.storage)?;
Ok(to_json_binary(&ConfigResponse { core, denom })?)
}
}
}

#[cfg_attr(not(feature = "library"), cosmwasm_std::entry_point)]
pub fn migrate(
_deps: DepsMut,
_env: Env,
_msg: MigrateMsg,
) -> ContractResult<Response<NeutronMsg>> {
Ok(Response::new())
}

#[cfg_attr(not(feature = "library"), cosmwasm_std::entry_point)]
pub fn reply(
deps: DepsMut<NeutronQuery>,
env: Env,
msg: Reply,
) -> ContractResult<Response<NeutronMsg>> {
match msg.id {
CREATE_DENOM_REPLY_ID => {
let subdenom = DENOM.load(deps.storage)?;
let full_denom = query_full_denom(deps.as_ref(), env.contract.address, subdenom)?;
DENOM.save(deps.storage, &full_denom.denom)?;

Ok(response(
"reply-create-denom",
[attr("denom", full_denom.denom)],
))
}
id => Err(ContractError::UnknownReplyId { id }),
}
}
Loading

0 comments on commit 1b3e902

Please sign in to comment.