Documentation Index
Fetch the complete documentation index at: https://injectivelabs-mintlify-jp-cosmwasm-translations.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
このドキュメントでは、CW-20トークンとInjectiveが発行するネイティブトークン(TokenFactoryモジュールを使用)を相互に交換できるCW20 Adapter Contractについて解説します。CW-20 AdapterのGitHubリポジトリはこちらを参照してください。
CW-20はCosmWasmにおけるfungibleトークンの仕様で、ERC-20仕様を緩やかに参考にしています。CosmWasm内で任意のfungibleトークンを作成・管理することを可能にし、これらのトークンの作成、ミント、バーン、およびアカウント間の転送のメソッドを規定します。adapter contractは、認可されたソースのCW-20コントラクトのみがトークンをミントできるように保証します(「偽造」トークンの作成を防ぐため)。
CW-20標準は比較的成熟し完成されている一方、トークンはCosmWasmのコンテキスト内のみに存在し、発行側のコントラクトによって完全に管理されます(アカウント残高の追跡を含む)。つまり、これらのトークンはInjectiveのネイティブモジュールと直接相互作用できません(例えば、Injective exchange moduleでの取引や、発行コントラクトを介さずに転送することはできません)。
上記を踏まえると、CW20とInjective bank moduleの橋渡しとして機能するソリューションを提供する必要があります。
コントラクトのワークフローは以下のとおりです:
- 新しいCW-20トークンを登録する
- X個のCW-20トークンをY個のTokenFactoryトークンと交換する(元のCW-20トークンはコントラクトが保持)
- Y個のTFトークンをX個のCW-20トークンに戻す(CW-20トークンが解放され、TokenFactoryトークンはバーンされる)
Messages
RegisterCw20Contract { addr: Addr }
adapterで処理される新しいCW-20コントラクト(addr)を登録し、factory/{adapter_contract}/{cw20_contract}形式の新しいTokenFactoryトークンを作成します。
ExecuteMsg::RegisterCw20Contract { addr } => execute_register::handle_register_msg(deps, env, info, addr)
pub fn handle_register_msg(
deps: DepsMut<InjectiveQueryWrapper>,
env: Env,
info: MessageInfo,
addr: Addr,
) -> Result<Response<InjectiveMsgWrapper>, ContractError> {
if is_contract_registered(&deps, &addr) {
return Err(ContractError::ContractAlreadyRegistered);
}
let required_funds = query_denom_creation_fee(&deps.querier)?;
if info.funds.len() > required_funds.len() {
return Err(ContractError::SuperfluousFundsProvided);
}
let mut provided_funds = info.funds.iter();
for required_coin in &required_funds {
let pf = provided_funds
.find(|c| -> bool { c.denom == required_coin.denom })
.ok_or(ContractError::NotEnoughBalanceToPayDenomCreationFee)?;
match pf.amount.cmp(&required_coin.amount) {
Ordering::Greater => return Err(ContractError::SuperfluousFundsProvided),
Ordering::Less => return Err(ContractError::NotEnoughBalanceToPayDenomCreationFee),
Ordering::Equal => {}
}
}
let create_denom_msg = register_contract_and_get_message(deps, &env, &addr)?;
Ok(Response::new().add_message(create_denom_msg))
}
Receive { sender: String, amount: Uint128, msg: Binary }
Receiver CW-20インターフェースの実装。
CW-20コントラクトからのみ呼び出される必要があります。
ExecuteMsg::Receive { sender, amount, msg: _ } => execute_receive::handle_on_received_cw20_funds_msg(deps, env, info, sender, amount)
pub fn handle_on_received_cw20_funds_msg(
deps: DepsMut<InjectiveQueryWrapper>,
env: Env,
info: MessageInfo,
recipient: String,
amount: Uint128,
) -> Result<Response<InjectiveMsgWrapper>, ContractError> {
if!info.funds.is_empty() {
return Err(ContractError::SuperfluousFundsProvided);
}
let mut response = Response::new();
let token_contract = info.sender;
if!is_contract_registered(&deps, &token_contract) {
ensure_sufficient_create_denom_balance(&deps, &env)?;
response = response.add_message(register_contract_and_get_message(deps, &env, &token_contract)?);
}
let master = env.contract.address;
let denom = get_denom(&master, &token_contract);
let coins_to_mint = Coin::new(amount.u128(), denom);
let mint_tf_tokens_message = create_mint_tokens_msg(master, coins_to_mint, recipient);
Ok(response.add_message(mint_tf_tokens_message))
}
RedeemAndTransfer \{ recipient: Option\<String\> \}
添付されたTokenFactoryトークンをredeemし、CW-20トークンをrecipientに転送します。recipientが指定されない場合、メッセージの送信者に送られます。
RedeemAndSend { recipient: String, submessage: Binary }
添付されたTokenFactoryトークンをredeemし、CW-20トークンをrecipientコントラクトに送信します。呼び出し元はオプションのsubmessageを提供できます。
ExecuteMsg::RedeemAndTransfer { recipient } => execute_redeem::handle_redeem_msg(deps, env, info, recipient, None)
ExecuteMsg::RedeemAndSend { recipient, submsg } => execute_redeem::handle_redeem_msg(deps, env, info, Some(recipient), Some(submsg))
pub fn handle_redeem_msg(
deps: DepsMut<InjectiveQueryWrapper>,
env: Env,
info: MessageInfo,
recipient: Option<String>,
submessage: Option<Binary>,
) -> Result<Response<InjectiveMsgWrapper>, ContractError> {
let recipient = recipient.unwrap_or_else(|| info.sender.to_string());
if info.funds.len() > 1 {
return Err(ContractError::SuperfluousFundsProvided);
}
let tokens_to_exchange = info
.funds
.iter()
.find_map(|c| -> Option<AdapterCoin> {
match AdapterDenom::new(&c.denom) {
Ok(denom) => Some(AdapterCoin { amount: c.amount, denom }),
Err(_) => None,
}
})
.ok_or(ContractError::NoRegisteredTokensProvided)?;
let cw20_addr = tokens_to_exchange.denom.cw20_addr.clone();
let is_contract_registered = CW20_CONTRACTS.contains(deps.storage, &tokens_to_exchange.denom.cw20_addr);
if!is_contract_registered {
return Err(ContractError::NoRegisteredTokensProvided);
}
let burn_tf_tokens_message = create_burn_tokens_msg(env.contract.address, tokens_to_exchange.as_coin());
let cw20_message: WasmMsg = match submessage {
None => WasmMsg::Execute {
contract_addr: cw20_addr,
msg: to_binary(&Cw20ExecuteMsg::Transfer {
recipient,
amount: tokens_to_exchange.amount,
})?,
funds: vec![],
},
Some(msg) => WasmMsg::Execute {
contract_addr: cw20_addr,
msg: to_binary(&Cw20ExecuteMsg::Send {
contract: recipient,
amount: tokens_to_exchange.amount,
msg,
})?,
funds: vec![],
},
};
Ok(Response::new().add_message(cw20_message).add_message(burn_tf_tokens_message))
}
(登録済みであれば)CW-20アドレスにメタデータをクエリし、bankモジュールのsetMetadataを呼び出します(TokenFactoryのアクセスメソッドを使用)。
ExecuteMsg::UpdateMetadata { addr } => execute_metadata::handle_update_metadata(deps, env, addr)
pub fn handle_update_metadata(
deps: DepsMut<InjectiveQueryWrapper>,
env: Env,
cw20_addr: Addr,
) -> Result<Response<InjectiveMsgWrapper>, ContractError> {
let is_contract_registered = CW20_CONTRACTS.contains(deps.storage, cw20_addr.as_str());
if!is_contract_registered {
return Err(ContractError::ContractNotRegistered);
}
let token_metadata = fetch_cw20_metadata(&deps, cw20_addr.as_str())?;
let denom = get_denom(&env.contract.address, &cw20_addr);
let set_metadata_message = create_set_token_metadata_msg(denom, token_metadata.name, token_metadata.symbol, token_metadata.decimals);
Ok(Response::new().add_message(set_metadata_message))
}
Queries
RegisteredContracts {}
登録済みのCW-20コントラクトのリストを返します。
QueryMsg::RegisteredContracts {} => to_binary(&query::registered_contracts(deps)?)
pub fn registered_contracts(deps: Deps<InjectiveQueryWrapper>) -> StdResult<Vec<Addr>> {}
NewDenomFee {}
新しいtokenFactory denomを登録するために必要な手数料を返します。
QueryMsg::NewDenomFee {} => to_binary(&query::new_denom_fee(deps)?)
pub fn new_denom_fee(deps: Deps<InjectiveQueryWrapper>) -> StdResult<Uint128> {}