use cosmwasm_std::{
entry_point, to_json_binary, Addr, Binary, CosmosMsg, Deps, DepsMut, Empty, Env, MessageInfo,
Response, StdResult, Uint128, WasmMsg,
};
use cw2::{get_contract_version, set_contract_version};
use cw20::Cw20ReceiveMsg;
use cw20_base::state::{TokenInfo, BALANCES, TOKEN_INFO};
use crate::error::ContractError;
use crate::msg::{
BurnStatsResponse, ConfigResponse, ExecuteMsg, InstantiateMsg, IsAuthorizedCallerResponse,
QueryMsg, TestingOverrides, TotalMintedResponse,
};
use crate::state::{CoreConfig, AUTHORIZED_CALLERS, BURN_TOTAL, CONFIG, FEE_TOTAL, TOTAL_MINTED};
const CONTRACT_NAME: &str = "crates.io:litium-core";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
/// Total supply cap: 10^15 tokens * 10^6 decimals = 10^21 atomic units
pub const SUPPLY_CAP: u128 = 1_000_000_000_000_000_000_000u128;
// ============================================================
// Instantiate
// ============================================================
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: InstantiateMsg,
) -> Result<Response, ContractError> {
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
let admin = msg
.admin
.map(|a| deps.api.addr_validate(&a))
.transpose()?
.unwrap_or(info.sender.clone());
// Initialize CW-20 token info
let token_info = TokenInfo {
name: msg.name.clone(),
symbol: msg.symbol.clone(),
decimals: msg.decimals,
total_supply: Uint128::zero(),
mint: None,
};
TOKEN_INFO.save(deps.storage, &token_info)?;
let mine_contract = msg
.mine_contract
.map(|a| deps.api.addr_validate(&a))
.transpose()?;
let stake_contract = msg
.stake_contract
.map(|a| deps.api.addr_validate(&a))
.transpose()?;
let refer_contract = msg
.refer_contract
.map(|a| deps.api.addr_validate(&a))
.transpose()?;
let wrap_contract = msg
.wrap_contract
.map(|a| deps.api.addr_validate(&a))
.transpose()?;
let config = CoreConfig {
admin: admin.clone(),
paused: false,
mine_contract,
stake_contract,
refer_contract,
wrap_contract,
};
CONFIG.save(deps.storage, &config)?;
BURN_TOTAL.save(deps.storage, &Uint128::zero())?;
FEE_TOTAL.save(deps.storage, &Uint128::zero())?;
TOTAL_MINTED.save(deps.storage, &Uint128::zero())?;
Ok(Response::new()
.add_attribute("action", "instantiate")
.add_attribute("name", msg.name)
.add_attribute("symbol", msg.symbol)
.add_attribute("decimals", msg.decimals.to_string())
.add_attribute("admin", admin.to_string()))
}
// ============================================================
// Execute
// ============================================================
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::Transfer { recipient, amount } => {
execute_transfer(deps, info, recipient, amount)
}
ExecuteMsg::TransferFrom {
owner,
recipient,
amount,
} => execute_transfer_from(deps, env, info, owner, recipient, amount),
ExecuteMsg::Send {
contract,
amount,
msg: send_msg,
} => execute_send(deps, info, contract, amount, send_msg),
ExecuteMsg::SendFrom {
owner,
contract,
amount,
msg: send_msg,
} => execute_send_from(deps, env, info, owner, contract, amount, send_msg),
ExecuteMsg::Burn { amount } => execute_burn(deps, info, amount),
ExecuteMsg::BurnFrom { owner, amount } => execute_burn_from(deps, env, info, owner, amount),
ExecuteMsg::IncreaseAllowance {
spender,
amount,
expires,
} => execute_increase_allowance(deps, env, info, spender, amount, expires),
ExecuteMsg::DecreaseAllowance {
spender,
amount,
expires,
} => execute_decrease_allowance(deps, env, info, spender, amount, expires),
ExecuteMsg::Mint { to, amount } => execute_mint(deps, info, to, amount),
ExecuteMsg::RegisterAuthorizedCaller { contract_addr } => {
execute_register_authorized_caller(deps, info, contract_addr)
}
ExecuteMsg::RemoveAuthorizedCaller { contract_addr } => {
execute_remove_authorized_caller(deps, info, contract_addr)
}
ExecuteMsg::UpdateConfig {
admin,
mine_contract,
stake_contract,
refer_contract,
wrap_contract,
} => execute_update_config(
deps,
info,
admin,
mine_contract,
stake_contract,
refer_contract,
wrap_contract,
),
ExecuteMsg::ApplyTestingOverrides { overrides } => {
execute_apply_testing_overrides(deps, info, overrides)
}
ExecuteMsg::ResetState {} => execute_reset_state(deps, info),
ExecuteMsg::Pause {} => execute_pause(deps, info),
ExecuteMsg::Unpause {} => execute_unpause(deps, info),
ExecuteMsg::Receive(_) => Ok(Response::new()),
}
}
// ============================================================
// Burn computation
// ============================================================
/// Message type for litium-mine AccrueFees call.
#[derive(serde::Serialize)]
#[serde(rename_all = "snake_case")]
enum LitiumMineExecuteMsg {
AccrueFees { amount: Uint128 },
}
/// Compute (net_amount, fee_amount) for a transfer.
/// Transfer is fee-exempt if sender OR recipient is one of the 4 spec-defined
/// contract slots (mine, stake, refer, wrap).
fn compute_fee(
deps: &DepsMut,
sender: &Addr,
recipient: &Addr,
amount: Uint128,
) -> Result<(Uint128, Uint128), ContractError> {
let config = CONFIG.load(deps.storage)?;
let is_exempt = |addr: &Addr| -> bool {
config.mine_contract.as_ref() == Some(addr)
|| config.stake_contract.as_ref() == Some(addr)
|| config.refer_contract.as_ref() == Some(addr)
|| config.wrap_contract.as_ref() == Some(addr)
};
if is_exempt(sender) || is_exempt(recipient) {
return Ok((amount, Uint128::zero()));
}
let fee = amount.multiply_ratio(1u128, 100u128);
let net = amount.saturating_sub(fee);
Ok((net, fee))
}
/// Apply fee: burn 1% permanently and notify litium-mine for PID tracking.
fn apply_fee(
storage: &mut dyn cosmwasm_std::Storage,
fee_amount: Uint128,
) -> Result<Option<CosmosMsg>, ContractError> {
if fee_amount.is_zero() {
return Ok(None);
}
// Burn fee permanently (reduce total supply per spec ยง4)
TOKEN_INFO.update(storage, |mut info| -> StdResult<_> {
info.total_supply = info.total_supply.checked_sub(fee_amount)?;
Ok(info)
})?;
BURN_TOTAL.update(storage, |v| -> StdResult<_> { Ok(v + fee_amount) })?;
FEE_TOTAL.update(storage, |v| -> StdResult<_> { Ok(v + fee_amount) })?;
// Notify litium-mine of burned fee amount for PID controller tracking
let config = CONFIG.load(storage)?;
if let Some(ref mine_addr) = config.mine_contract {
let msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: mine_addr.to_string(),
msg: cosmwasm_std::to_json_binary(&LitiumMineExecuteMsg::AccrueFees {
amount: fee_amount,
})?,
funds: vec![],
});
return Ok(Some(msg));
}
Ok(None)
}
// ============================================================
// CW-20 Transfer / Send
// ============================================================
fn execute_transfer(
deps: DepsMut,
info: MessageInfo,
recipient: String,
amount: Uint128,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
if config.paused {
return Err(ContractError::Paused {});
}
if amount.is_zero() {
return Err(ContractError::InvalidAmount {});
}
let rcpt_addr = deps.api.addr_validate(&recipient)?;
let (net, fee) = compute_fee(&deps, &info.sender, &rcpt_addr, amount)?;
if net.is_zero() {
return Err(ContractError::InvalidTransferAmount {});
}
// Deduct full amount from sender
BALANCES.update(
deps.storage,
&info.sender,
|balance| -> StdResult<Uint128> { Ok(balance.unwrap_or_default().checked_sub(amount)?) },
)?;
// Credit net to recipient
BALANCES.update(deps.storage, &rcpt_addr, |balance| -> StdResult<Uint128> {
Ok(balance.unwrap_or_default() + net)
})?;
let fee_msg = apply_fee(deps.storage, fee)?;
let mut resp = Response::new()
.add_attribute("action", "transfer")
.add_attribute("from", info.sender.to_string())
.add_attribute("to", rcpt_addr.to_string())
.add_attribute("amount", amount.to_string());
if !fee.is_zero() {
resp = resp
.add_attribute("fee", fee.to_string())
.add_attribute("net", net.to_string());
}
if let Some(msg) = fee_msg {
resp = resp.add_message(msg);
}
Ok(resp)
}
fn execute_transfer_from(
deps: DepsMut,
env: Env,
info: MessageInfo,
owner: String,
recipient: String,
amount: Uint128,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
if config.paused {
return Err(ContractError::Paused {});
}
if amount.is_zero() {
return Err(ContractError::InvalidAmount {});
}
let owner_addr = deps.api.addr_validate(&owner)?;
let rcpt_addr = deps.api.addr_validate(&recipient)?;
// Deduct allowance
cw20_base::allowances::deduct_allowance(
deps.storage,
&owner_addr,
&info.sender,
&env.block,
amount,
)?;
let (net, fee) = compute_fee(&deps, &owner_addr, &rcpt_addr, amount)?;
if net.is_zero() {
return Err(ContractError::InvalidTransferAmount {});
}
// Deduct full amount from owner
BALANCES.update(deps.storage, &owner_addr, |balance| -> StdResult<Uint128> {
Ok(balance.unwrap_or_default().checked_sub(amount)?)
})?;
// Credit net to recipient
BALANCES.update(deps.storage, &rcpt_addr, |balance| -> StdResult<Uint128> {
Ok(balance.unwrap_or_default() + net)
})?;
let fee_msg = apply_fee(deps.storage, fee)?;
let mut resp = Response::new()
.add_attribute("action", "transfer_from")
.add_attribute("from", owner_addr.to_string())
.add_attribute("by", info.sender.to_string())
.add_attribute("to", rcpt_addr.to_string())
.add_attribute("amount", amount.to_string());
if !fee.is_zero() {
resp = resp
.add_attribute("fee", fee.to_string())
.add_attribute("net", net.to_string());
}
if let Some(msg) = fee_msg {
resp = resp.add_message(msg);
}
Ok(resp)
}
fn execute_send(
deps: DepsMut,
info: MessageInfo,
contract: String,
amount: Uint128,
msg: Binary,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
if config.paused {
return Err(ContractError::Paused {});
}
if amount.is_zero() {
return Err(ContractError::InvalidAmount {});
}
let contract_addr = deps.api.addr_validate(&contract)?;
let (net, fee) = compute_fee(&deps, &info.sender, &contract_addr, amount)?;
if net.is_zero() {
return Err(ContractError::InvalidTransferAmount {});
}
// Deduct full amount from sender
BALANCES.update(
deps.storage,
&info.sender,
|balance| -> StdResult<Uint128> { Ok(balance.unwrap_or_default().checked_sub(amount)?) },
)?;
// Credit net to contract
BALANCES.update(
deps.storage,
&contract_addr,
|balance| -> StdResult<Uint128> { Ok(balance.unwrap_or_default() + net) },
)?;
let fee_msg = apply_fee(deps.storage, fee)?;
let cw20_msg = Cw20ReceiveMsg {
sender: info.sender.to_string(),
amount: net,
msg,
};
let mut resp = Response::new()
.add_message(cw20_msg.into_cosmos_msg(contract_addr.to_string())?)
.add_attribute("action", "send")
.add_attribute("from", info.sender.to_string())
.add_attribute("to", contract_addr.to_string())
.add_attribute("amount", amount.to_string());
if !fee.is_zero() {
resp = resp
.add_attribute("fee", fee.to_string())
.add_attribute("net", net.to_string());
}
if let Some(msg) = fee_msg {
resp = resp.add_message(msg);
}
Ok(resp)
}
fn execute_send_from(
deps: DepsMut,
env: Env,
info: MessageInfo,
owner: String,
contract: String,
amount: Uint128,
msg: Binary,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
if config.paused {
return Err(ContractError::Paused {});
}
if amount.is_zero() {
return Err(ContractError::InvalidAmount {});
}
let owner_addr = deps.api.addr_validate(&owner)?;
let contract_addr = deps.api.addr_validate(&contract)?;
// Deduct allowance
cw20_base::allowances::deduct_allowance(
deps.storage,
&owner_addr,
&info.sender,
&env.block,
amount,
)?;
let (net, fee) = compute_fee(&deps, &owner_addr, &contract_addr, amount)?;
if net.is_zero() {
return Err(ContractError::InvalidTransferAmount {});
}
// Deduct full amount from owner
BALANCES.update(deps.storage, &owner_addr, |balance| -> StdResult<Uint128> {
Ok(balance.unwrap_or_default().checked_sub(amount)?)
})?;
// Credit net to contract
BALANCES.update(
deps.storage,
&contract_addr,
|balance| -> StdResult<Uint128> { Ok(balance.unwrap_or_default() + net) },
)?;
let fee_msg = apply_fee(deps.storage, fee)?;
let cw20_msg = Cw20ReceiveMsg {
sender: owner_addr.to_string(),
amount: net,
msg,
};
let mut resp = Response::new()
.add_message(cw20_msg.into_cosmos_msg(contract_addr.to_string())?)
.add_attribute("action", "send_from")
.add_attribute("from", owner_addr.to_string())
.add_attribute("by", info.sender.to_string())
.add_attribute("to", contract_addr.to_string())
.add_attribute("amount", amount.to_string());
if !fee.is_zero() {
resp = resp
.add_attribute("fee", fee.to_string())
.add_attribute("net", net.to_string());
}
if let Some(msg) = fee_msg {
resp = resp.add_message(msg);
}
Ok(resp)
}
// ============================================================
// Burn
// ============================================================
fn execute_burn(
deps: DepsMut,
info: MessageInfo,
amount: Uint128,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
if config.paused {
return Err(ContractError::Paused {});
}
if amount.is_zero() {
return Err(ContractError::InvalidAmount {});
}
BALANCES.update(
deps.storage,
&info.sender,
|balance| -> StdResult<Uint128> { Ok(balance.unwrap_or_default().checked_sub(amount)?) },
)?;
TOKEN_INFO.update(deps.storage, |mut info| -> StdResult<_> {
info.total_supply = info.total_supply.checked_sub(amount)?;
Ok(info)
})?;
BURN_TOTAL.update(deps.storage, |v| -> StdResult<_> { Ok(v + amount) })?;
Ok(Response::new()
.add_attribute("action", "burn")
.add_attribute("from", info.sender.to_string())
.add_attribute("amount", amount.to_string()))
}
fn execute_burn_from(
deps: DepsMut,
env: Env,
info: MessageInfo,
owner: String,
amount: Uint128,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
if config.paused {
return Err(ContractError::Paused {});
}
if amount.is_zero() {
return Err(ContractError::InvalidAmount {});
}
let owner_addr = deps.api.addr_validate(&owner)?;
// Deduct allowance
cw20_base::allowances::deduct_allowance(
deps.storage,
&owner_addr,
&info.sender,
&env.block,
amount,
)?;
BALANCES.update(deps.storage, &owner_addr, |balance| -> StdResult<Uint128> {
Ok(balance.unwrap_or_default().checked_sub(amount)?)
})?;
TOKEN_INFO.update(deps.storage, |mut info| -> StdResult<_> {
info.total_supply = info.total_supply.checked_sub(amount)?;
Ok(info)
})?;
BURN_TOTAL.update(deps.storage, |v| -> StdResult<_> { Ok(v + amount) })?;
Ok(Response::new()
.add_attribute("action", "burn_from")
.add_attribute("from", owner_addr.to_string())
.add_attribute("by", info.sender.to_string())
.add_attribute("amount", amount.to_string()))
}
// ============================================================
// Allowances (delegate to cw20-base)
// ============================================================
fn execute_increase_allowance(
deps: DepsMut,
env: Env,
info: MessageInfo,
spender: String,
amount: Uint128,
expires: Option<cw20::Expiration>,
) -> Result<Response, ContractError> {
Ok(cw20_base::allowances::execute_increase_allowance(
deps, env, info, spender, amount, expires,
)?)
}
fn execute_decrease_allowance(
deps: DepsMut,
env: Env,
info: MessageInfo,
spender: String,
amount: Uint128,
expires: Option<cw20::Expiration>,
) -> Result<Response, ContractError> {
Ok(cw20_base::allowances::execute_decrease_allowance(
deps, env, info, spender, amount, expires,
)?)
}
// ============================================================
// Mint
// ============================================================
fn execute_mint(
deps: DepsMut,
info: MessageInfo,
to: String,
amount: Uint128,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
if config.paused {
return Err(ContractError::Paused {});
}
require_authorized_caller(deps.as_ref(), &info)?;
if amount.is_zero() {
return Err(ContractError::InvalidAmount {});
}
let to_addr = deps.api.addr_validate(&to)?;
// Check supply cap
let mut total_minted = TOTAL_MINTED.load(deps.storage)?;
if total_minted + amount > Uint128::from(SUPPLY_CAP) {
return Err(ContractError::SupplyCapExceeded {});
}
total_minted += amount;
TOTAL_MINTED.save(deps.storage, &total_minted)?;
// Update CW-20 balance and total_supply
BALANCES.update(deps.storage, &to_addr, |balance| -> StdResult<Uint128> {
Ok(balance.unwrap_or_default() + amount)
})?;
TOKEN_INFO.update(deps.storage, |mut token_info| -> StdResult<_> {
token_info.total_supply += amount;
Ok(token_info)
})?;
Ok(Response::new()
.add_attribute("action", "mint")
.add_attribute("to", to_addr.to_string())
.add_attribute("amount", amount.to_string()))
}
// ============================================================
// Admin operations
// ============================================================
fn execute_register_authorized_caller(
deps: DepsMut,
info: MessageInfo,
contract_addr: String,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
require_admin(&config, &info)?;
let addr = deps.api.addr_validate(&contract_addr)?;
AUTHORIZED_CALLERS.save(deps.storage, &addr, &true)?;
Ok(Response::new()
.add_attribute("action", "register_authorized_caller")
.add_attribute("contract_addr", addr.to_string()))
}
fn execute_remove_authorized_caller(
deps: DepsMut,
info: MessageInfo,
contract_addr: String,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
require_admin(&config, &info)?;
let addr = deps.api.addr_validate(&contract_addr)?;
AUTHORIZED_CALLERS.remove(deps.storage, &addr);
Ok(Response::new()
.add_attribute("action", "remove_authorized_caller")
.add_attribute("contract_addr", addr.to_string()))
}
fn execute_update_config(
deps: DepsMut,
info: MessageInfo,
admin: Option<String>,
mine_contract: Option<String>,
stake_contract: Option<String>,
refer_contract: Option<String>,
wrap_contract: Option<String>,
) -> Result<Response, ContractError> {
let mut config = CONFIG.load(deps.storage)?;
require_admin(&config, &info)?;
if let Some(a) = admin {
config.admin = deps.api.addr_validate(&a)?;
}
if let Some(a) = mine_contract {
config.mine_contract = Some(deps.api.addr_validate(&a)?);
}
if let Some(a) = stake_contract {
config.stake_contract = Some(deps.api.addr_validate(&a)?);
}
if let Some(a) = refer_contract {
config.refer_contract = Some(deps.api.addr_validate(&a)?);
}
if let Some(a) = wrap_contract {
config.wrap_contract = Some(deps.api.addr_validate(&a)?);
}
CONFIG.save(deps.storage, &config)?;
Ok(Response::new().add_attribute("action", "update_config"))
}
fn execute_apply_testing_overrides(
deps: DepsMut,
info: MessageInfo,
overrides: TestingOverrides,
) -> Result<Response, ContractError> {
let mut config = CONFIG.load(deps.storage)?;
require_admin(&config, &info)?;
if let Some(paused) = overrides.paused {
config.paused = paused;
}
CONFIG.save(deps.storage, &config)?;
if let Some(total) = overrides.burn_total {
BURN_TOTAL.save(deps.storage, &total)?;
}
if let Some(total) = overrides.fee_total {
FEE_TOTAL.save(deps.storage, &total)?;
}
if let Some(total) = overrides.total_minted {
TOTAL_MINTED.save(deps.storage, &total)?;
}
Ok(Response::new().add_attribute("action", "apply_testing_overrides"))
}
/// Full state reset for daily testing. Zeroes all counters and CW-20 balances/supply.
fn execute_reset_state(deps: DepsMut, info: MessageInfo) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
require_admin(&config, &info)?;
// Zero counters
TOTAL_MINTED.save(deps.storage, &Uint128::zero())?;
BURN_TOTAL.save(deps.storage, &Uint128::zero())?;
FEE_TOTAL.save(deps.storage, &Uint128::zero())?;
// Zero all CW-20 balances
let holders: Vec<_> = BALANCES
.keys(deps.storage, None, None, cosmwasm_std::Order::Ascending)
.collect::<Result<Vec<_>, _>>()?;
for addr in &holders {
BALANCES.remove(deps.storage, addr);
}
// Zero total supply
TOKEN_INFO.update(deps.storage, |mut ti| -> StdResult<_> {
ti.total_supply = Uint128::zero();
Ok(ti)
})?;
Ok(Response::new()
.add_attribute("action", "reset_state")
.add_attribute("balances_cleared", holders.len().to_string()))
}
fn execute_pause(deps: DepsMut, info: MessageInfo) -> Result<Response, ContractError> {
let mut config = CONFIG.load(deps.storage)?;
require_admin(&config, &info)?;
config.paused = true;
CONFIG.save(deps.storage, &config)?;
Ok(Response::new().add_attribute("action", "pause"))
}
fn execute_unpause(deps: DepsMut, info: MessageInfo) -> Result<Response, ContractError> {
let mut config = CONFIG.load(deps.storage)?;
require_admin(&config, &info)?;
config.paused = false;
CONFIG.save(deps.storage, &config)?;
Ok(Response::new().add_attribute("action", "unpause"))
}
// ============================================================
// Migrate
// ============================================================
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> Result<Response, ContractError> {
let stored = get_contract_version(deps.storage)?;
let stored_ver = stored
.version
.parse::<semver::Version>()
.map_err(|_| ContractError::MigrationError {})?;
let current_ver = CONTRACT_VERSION
.parse::<semver::Version>()
.map_err(|_| ContractError::MigrationError {})?;
if stored_ver >= current_ver {
return Err(ContractError::MigrationError {});
}
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
// Initialize FEE_TOTAL if not present (migration from burn-based to fee-routing)
if FEE_TOTAL.may_load(deps.storage)?.is_none() {
FEE_TOTAL.save(deps.storage, &Uint128::zero())?;
}
Ok(Response::default())
}
// ============================================================
// Helpers
// ============================================================
fn require_admin(config: &CoreConfig, info: &MessageInfo) -> Result<(), ContractError> {
if info.sender != config.admin {
return Err(ContractError::Unauthorized {});
}
Ok(())
}
fn require_authorized_caller(deps: Deps, info: &MessageInfo) -> Result<(), ContractError> {
let is_authorized = AUTHORIZED_CALLERS
.may_load(deps.storage, &info.sender)?
.unwrap_or(false);
if !is_authorized {
return Err(ContractError::NotAuthorizedCaller {
addr: info.sender.to_string(),
});
}
Ok(())
}
// ============================================================
// Queries
// ============================================================
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {
QueryMsg::Config {} => to_json_binary(&query_config(deps)?),
QueryMsg::BurnStats {} => to_json_binary(&query_burn_stats(deps)?),
QueryMsg::TotalMinted {} => to_json_binary(&query_total_minted(deps)?),
QueryMsg::IsAuthorizedCaller { address } => {
to_json_binary(&query_is_authorized_caller(deps, address)?)
}
QueryMsg::Balance { address } => {
to_json_binary(&cw20_base::contract::query_balance(deps, address)?)
}
QueryMsg::TokenInfo {} => to_json_binary(&cw20_base::contract::query_token_info(deps)?),
QueryMsg::Minter {} => to_json_binary(&query_minter()?),
QueryMsg::Allowance { owner, spender } => to_json_binary(
&cw20_base::allowances::query_allowance(deps, owner, spender)?,
),
QueryMsg::AllAllowances {
owner,
start_after,
limit,
} => to_json_binary(&cw20_base::enumerable::query_owner_allowances(
deps,
owner,
start_after,
limit,
)?),
QueryMsg::AllSpenderAllowances {
spender,
start_after,
limit,
} => to_json_binary(&cw20_base::enumerable::query_spender_allowances(
deps,
spender,
start_after,
limit,
)?),
QueryMsg::AllAccounts { start_after, limit } => to_json_binary(
&cw20_base::enumerable::query_all_accounts(deps, start_after, limit)?,
),
}
}
fn query_config(deps: Deps) -> StdResult<ConfigResponse> {
let config = CONFIG.load(deps.storage)?;
Ok(ConfigResponse {
admin: config.admin.to_string(),
paused: config.paused,
mine_contract: config.mine_contract.map(|a| a.to_string()),
stake_contract: config.stake_contract.map(|a| a.to_string()),
refer_contract: config.refer_contract.map(|a| a.to_string()),
wrap_contract: config.wrap_contract.map(|a| a.to_string()),
})
}
fn query_burn_stats(deps: Deps) -> StdResult<BurnStatsResponse> {
let total_burned = BURN_TOTAL
.may_load(deps.storage)?
.unwrap_or_else(Uint128::zero);
// total_burned tracks explicit Burn calls (beta burn from litium-mine).
// 1% transfer fees are now routed to litium-mine, tracked in FEE_TOTAL.
Ok(BurnStatsResponse { total_burned })
}
fn query_total_minted(deps: Deps) -> StdResult<TotalMintedResponse> {
let total_minted = TOTAL_MINTED
.may_load(deps.storage)?
.unwrap_or_else(Uint128::zero);
Ok(TotalMintedResponse {
total_minted,
supply_cap: Uint128::from(SUPPLY_CAP),
})
}
fn query_is_authorized_caller(
deps: Deps,
address: String,
) -> StdResult<IsAuthorizedCallerResponse> {
let addr = deps.api.addr_validate(&address)?;
let authorized = AUTHORIZED_CALLERS
.may_load(deps.storage, &addr)?
.unwrap_or(false);
Ok(IsAuthorizedCallerResponse {
address,
authorized,
})
}
fn query_minter() -> StdResult<Option<cw20::MinterResponse>> {
// No single minter โ minting is managed via AUTHORIZED_CALLERS (mine, stake, refer).
// Return None per CW-20 convention (matches cw20-base's optional minter).
Ok(None)
}
cw-cyber/contracts/litium-core/src/contract.rs
ฯ 0.0%
use ;
use ;
use Cw20ReceiveMsg;
use ;
use crateContractError;
use crate;
use crate;
const CONTRACT_NAME: &str = "crates.io:litium-core";
const CONTRACT_VERSION: &str = env!;
/// Total supply cap: 10^15 tokens * 10^6 decimals = 10^21 atomic units
pub const SUPPLY_CAP: u128 = 1_000_000_000_000_000_000_000u128;
// ============================================================
// Instantiate
// ============================================================
// ============================================================
// Execute
// ============================================================
// ============================================================
// Burn computation
// ============================================================
/// Message type for litium-mine AccrueFees call.
/// Compute (net_amount, fee_amount) for a transfer.
/// Transfer is fee-exempt if sender OR recipient is one of the 4 spec-defined
/// contract slots (mine, stake, refer, wrap).
/// Apply fee: burn 1% permanently and notify litium-mine for PID tracking.
// ============================================================
// CW-20 Transfer / Send
// ============================================================
// ============================================================
// Burn
// ============================================================
// ============================================================
// Allowances (delegate to cw20-base)
// ============================================================
// ============================================================
// Mint
// ============================================================
// ============================================================
// Admin operations
// ============================================================
/// Full state reset for daily testing. Zeroes all counters and CW-20 balances/supply.
// ============================================================
// Migrate
// ============================================================
// ============================================================
// Helpers
// ============================================================
// ============================================================
// Queries
// ============================================================