terra/contracts: columbus-5 migration for token-bridge

Change-Id: I5b2466d72c8dd98a14729125ddc3495589871d9f
This commit is contained in:
Reisen 2021-09-06 08:27:27 +00:00 committed by David Paryente
parent d04e716619
commit c92442e3ce
4 changed files with 210 additions and 252 deletions

View File

@ -14,18 +14,17 @@ backtraces = ["cosmwasm-std/backtraces"]
library = [] library = []
[dependencies] [dependencies]
cosmwasm-std = { version = "0.10.0" } cosmwasm-std = { version = "0.16.0" }
cosmwasm-storage = { version = "0.10.0" } cosmwasm-storage = { version = "0.16.0" }
schemars = "0.7" schemars = "0.8.1"
serde = { version = "1.0.103", default-features = false, features = ["derive"] } serde = { version = "1.0.103", default-features = false, features = ["derive"] }
cw20 = "0.2.2" cw20 = "0.8.0"
cw20-base = { version = "0.2.2", features = ["library"] } cw20-base = { version = "0.8.0", features = ["library"] }
cw20-wrapped = { path = "../cw20-wrapped", features = ["library"] } cw20-wrapped = { path = "../cw20-wrapped", features = ["library"] }
terraswap = "1.1.0" terraswap = "2.4.0"
wormhole = { path = "../wormhole", features = ["library"] } wormhole = { path = "../wormhole", features = ["library"] }
thiserror = { version = "1.0.20" } thiserror = { version = "1.0.20" }
k256 = { version = "0.5.9", default-features = false, features = ["ecdsa"] } k256 = { version = "0.9.4", default-features = false, features = ["ecdsa"] }
sha3 = { version = "0.9.1", default-features = false } sha3 = { version = "0.9.1", default-features = false }
generic-array = { version = "0.14.4" } generic-array = { version = "0.14.4" }
hex = "0.4.2" hex = "0.4.2"
@ -33,5 +32,5 @@ lazy_static = "1.4.0"
bigint = "4" bigint = "4"
[dev-dependencies] [dev-dependencies]
cosmwasm-vm = { version = "0.10.0", default-features = false, features = ["default-cranelift"] } cosmwasm-vm = { version = "0.16.0", default-features = false }
serde_json = "1.0" serde_json = "1.0"

View File

@ -1,22 +1,20 @@
use crate::msg::WrappedRegistryResponse; use crate::msg::WrappedRegistryResponse;
use cosmwasm_std::{ use cosmwasm_std::{
log, entry_point,
to_binary, to_binary,
Api,
Binary, Binary,
CanonicalAddr, CanonicalAddr,
Coin, Coin,
CosmosMsg, CosmosMsg,
Deps,
DepsMut,
Empty,
Env, Env,
Extern, MessageInfo,
HandleResponse,
HumanAddr,
InitResponse,
Querier,
QueryRequest, QueryRequest,
Response,
StdError, StdError,
StdResult, StdResult,
Storage,
Uint128, Uint128,
WasmMsg, WasmMsg,
WasmQuery, WasmQuery,
@ -24,8 +22,8 @@ use cosmwasm_std::{
use crate::{ use crate::{
msg::{ msg::{
HandleMsg, ExecuteMsg,
InitMsg, InstantiateMsg,
QueryMsg, QueryMsg,
}, },
state::{ state::{
@ -58,12 +56,12 @@ use wormhole::{
}; };
use cw20_base::msg::{ use cw20_base::msg::{
HandleMsg as TokenMsg, ExecuteMsg as TokenMsg,
QueryMsg as TokenQuery, QueryMsg as TokenQuery,
}; };
use wormhole::msg::{ use wormhole::msg::{
HandleMsg as WormholeHandleMsg, ExecuteMsg as WormholeExecuteMsg,
QueryMsg as WormholeQueryMsg, QueryMsg as WormholeQueryMsg,
}; };
@ -77,9 +75,9 @@ use wormhole::state::{
use cw20::TokenInfoResponse; use cw20::TokenInfoResponse;
use cw20_wrapped::msg::{ use cw20_wrapped::msg::{
HandleMsg as WrappedMsg, ExecuteMsg as WrappedMsg,
InitHook, InitHook,
InitMsg as WrappedInit, InstantiateMsg as WrappedInit,
QueryMsg as WrappedQuery, QueryMsg as WrappedQuery,
WrappedAssetInfoResponse, WrappedAssetInfoResponse,
}; };
@ -97,16 +95,20 @@ use std::cmp::{
min, min,
}; };
type HumanAddr = String;
// Chain ID of Terra // Chain ID of Terra
const CHAIN_ID: u16 = 3; const CHAIN_ID: u16 = 3;
const WRAPPED_ASSET_UPDATING: &str = "updating"; const WRAPPED_ASSET_UPDATING: &str = "updating";
pub fn init<S: Storage, A: Api, Q: Querier>( #[cfg_attr(not(feature = "library"), entry_point)]
deps: &mut Extern<S, A, Q>, pub fn instantiate(
deps: DepsMut,
_env: Env, _env: Env,
msg: InitMsg, _info: MessageInfo,
) -> StdResult<InitResponse> { msg: InstantiateMsg,
) -> StdResult<Response> {
// Save general wormhole info // Save general wormhole info
let state = ConfigInfo { let state = ConfigInfo {
gov_chain: msg.gov_chain, gov_chain: msg.gov_chain,
@ -114,15 +116,12 @@ pub fn init<S: Storage, A: Api, Q: Querier>(
wormhole_contract: msg.wormhole_contract, wormhole_contract: msg.wormhole_contract,
wrapped_asset_code_id: msg.wrapped_asset_code_id, wrapped_asset_code_id: msg.wrapped_asset_code_id,
}; };
config(&mut deps.storage).save(&state)?; config(deps.storage).save(&state)?;
Ok(InitResponse::default()) Ok(Response::default())
} }
pub fn coins_after_tax<S: Storage, A: Api, Q: Querier>( pub fn coins_after_tax(deps: DepsMut, coins: Vec<Coin>) -> StdResult<Vec<Coin>> {
deps: &mut Extern<S, A, Q>,
coins: Vec<Coin>,
) -> StdResult<Vec<Coin>> {
let mut res = vec![]; let mut res = vec![];
for coin in coins { for coin in coins {
let asset = Asset { let asset = Asset {
@ -131,17 +130,13 @@ pub fn coins_after_tax<S: Storage, A: Api, Q: Querier>(
denom: coin.denom.clone(), denom: coin.denom.clone(),
}, },
}; };
res.push(asset.deduct_tax(&deps)?); res.push(asset.deduct_tax(&deps.querier)?);
} }
Ok(res) Ok(res)
} }
pub fn parse_vaa<S: Storage, A: Api, Q: Querier>( pub fn parse_vaa(deps: DepsMut, block_time: u64, data: &Binary) -> StdResult<ParsedVAA> {
deps: &mut Extern<S, A, Q>, let cfg = config_read(deps.storage).load()?;
block_time: u64,
data: &Binary,
) -> StdResult<ParsedVAA> {
let cfg = config_read(&deps.storage).load()?;
let vaa: ParsedVAA = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { let vaa: ParsedVAA = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
contract_addr: cfg.wormhole_contract.clone(), contract_addr: cfg.wormhole_contract.clone(),
msg: to_binary(&WormholeQueryMsg::VerifyVAA { msg: to_binary(&WormholeQueryMsg::VerifyVAA {
@ -152,16 +147,13 @@ pub fn parse_vaa<S: Storage, A: Api, Q: Querier>(
Ok(vaa) Ok(vaa)
} }
pub fn handle<S: Storage, A: Api, Q: Querier>( #[cfg_attr(not(feature = "library"), entry_point)]
deps: &mut Extern<S, A, Q>, pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult<Response> {
env: Env,
msg: HandleMsg,
) -> StdResult<HandleResponse> {
match msg { match msg {
HandleMsg::RegisterAssetHook { asset_id } => { ExecuteMsg::RegisterAssetHook { asset_id } => {
handle_register_asset(deps, env, &asset_id.as_slice()) handle_register_asset(deps, env, info, &asset_id.as_slice())
} }
HandleMsg::InitiateTransfer { ExecuteMsg::InitiateTransfer {
asset, asset,
amount, amount,
recipient_chain, recipient_chain,
@ -171,6 +163,7 @@ pub fn handle<S: Storage, A: Api, Q: Querier>(
} => handle_initiate_transfer( } => handle_initiate_transfer(
deps, deps,
env, env,
info,
asset, asset,
amount, amount,
recipient_chain, recipient_chain,
@ -178,59 +171,54 @@ pub fn handle<S: Storage, A: Api, Q: Querier>(
fee, fee,
nonce, nonce,
), ),
HandleMsg::SubmitVaa { data } => submit_vaa(deps, env, &data), ExecuteMsg::SubmitVaa { data } => submit_vaa(deps, env, info, &data),
HandleMsg::CreateAssetMeta { ExecuteMsg::CreateAssetMeta {
asset_address, asset_address,
nonce, nonce,
} => handle_create_asset_meta(deps, env, &asset_address, nonce), } => handle_create_asset_meta(deps, env, info, &asset_address, nonce),
} }
} }
/// Handle wrapped asset registration messages /// Handle wrapped asset registration messages
fn handle_register_asset<S: Storage, A: Api, Q: Querier>( fn handle_register_asset(
deps: &mut Extern<S, A, Q>, deps: DepsMut,
env: Env, _env: Env,
info: MessageInfo,
asset_id: &[u8], asset_id: &[u8],
) -> StdResult<HandleResponse> { ) -> StdResult<Response> {
let mut bucket = wrapped_asset(&mut deps.storage); let mut bucket = wrapped_asset(deps.storage);
let result = bucket.load(asset_id); let result = bucket.load(asset_id);
let result = result.map_err(|_| ContractError::RegistrationForbidden.std())?; let result = result.map_err(|_| ContractError::RegistrationForbidden.std())?;
if result != HumanAddr::from(WRAPPED_ASSET_UPDATING) { if result != HumanAddr::from(WRAPPED_ASSET_UPDATING) {
return ContractError::AssetAlreadyRegistered.std_err(); return ContractError::AssetAlreadyRegistered.std_err();
} }
bucket.save(asset_id, &env.message.sender)?; bucket.save(asset_id, &info.sender.to_string())?;
let contract_address: CanonicalAddr = deps.api.canonical_address(&env.message.sender)?; let contract_address: CanonicalAddr = deps.api.addr_canonicalize(&info.sender.as_str())?;
wrapped_asset_address(&mut deps.storage) wrapped_asset_address(deps.storage).save(contract_address.as_slice(), &asset_id.to_vec())?;
.save(contract_address.as_slice(), &asset_id.to_vec())?;
Ok(HandleResponse { Ok(Response::new()
messages: vec![], .add_attribute("action", "register_asset")
log: vec![ .add_attribute("asset_id", format!("{:?}", asset_id))
log("action", "register_asset"), .add_attribute("contract_addr", info.sender))
log("asset_id", format!("{:?}", asset_id)),
log("contract_addr", env.message.sender),
],
data: None,
})
} }
fn handle_attest_meta<S: Storage, A: Api, Q: Querier>( fn handle_attest_meta(
deps: &mut Extern<S, A, Q>, deps: DepsMut,
env: Env, env: Env,
emitter_chain: u16, emitter_chain: u16,
emitter_address: Vec<u8>, emitter_address: Vec<u8>,
data: &Vec<u8>, data: &Vec<u8>,
) -> StdResult<HandleResponse> { ) -> StdResult<Response> {
let meta = AssetMeta::deserialize(data)?; let meta = AssetMeta::deserialize(data)?;
let expected_contract = let expected_contract =
bridge_contracts_read(&deps.storage).load(&emitter_chain.to_be_bytes())?; bridge_contracts_read(deps.storage).load(&emitter_chain.to_be_bytes())?;
// must be sent by a registered token bridge contract // must be sent by a registered token bridge contract
if expected_contract != emitter_address { if expected_contract != emitter_address {
return Err(StdError::unauthorized()); return Err(StdError::generic_err("invalid emitter"));
} }
if CHAIN_ID == meta.token_chain { if CHAIN_ID == meta.token_chain {
@ -239,22 +227,20 @@ fn handle_attest_meta<S: Storage, A: Api, Q: Querier>(
)); ));
} }
let cfg = config_read(&deps.storage).load()?; let cfg = config_read(deps.storage).load()?;
let asset_id = build_asset_id(meta.token_chain, &meta.token_address.as_slice()); let asset_id = build_asset_id(meta.token_chain, &meta.token_address.as_slice());
if wrapped_asset_read(&mut deps.storage) if wrapped_asset_read(deps.storage).load(&asset_id).is_ok() {
.load(&asset_id)
.is_ok()
{
return Err(StdError::generic_err( return Err(StdError::generic_err(
"this asset has already been attested", "this asset has already been attested",
)); ));
} }
wrapped_asset(&mut deps.storage).save(&asset_id, &HumanAddr::from(WRAPPED_ASSET_UPDATING))?; wrapped_asset(deps.storage).save(&asset_id, &HumanAddr::from(WRAPPED_ASSET_UPDATING))?;
Ok(HandleResponse { Ok(
messages: vec![CosmosMsg::Wasm(WasmMsg::Instantiate { Response::new().add_message(CosmosMsg::Wasm(WasmMsg::Instantiate {
admin: None,
code_id: cfg.wrapped_asset_code_id, code_id: cfg.wrapped_asset_code_id,
msg: to_binary(&WrappedInit { msg: to_binary(&WrappedInit {
name: get_string_from_32(&meta.name)?, name: get_string_from_32(&meta.name)?,
@ -264,34 +250,33 @@ fn handle_attest_meta<S: Storage, A: Api, Q: Querier>(
decimals: min(meta.decimals, 8u8), decimals: min(meta.decimals, 8u8),
mint: None, mint: None,
init_hook: Some(InitHook { init_hook: Some(InitHook {
contract_addr: env.contract.address, contract_addr: env.contract.address.to_string(),
msg: to_binary(&HandleMsg::RegisterAssetHook { msg: to_binary(&ExecuteMsg::RegisterAssetHook {
asset_id: asset_id.to_vec().into(), asset_id: asset_id.to_vec().into(),
})?, })?,
}), }),
})?, })?,
send: vec![], funds: vec![],
label: None, label: String::new(),
})], })),
log: vec![], )
data: None,
})
} }
fn handle_create_asset_meta<S: Storage, A: Api, Q: Querier>( fn handle_create_asset_meta(
deps: &mut Extern<S, A, Q>, deps: DepsMut,
env: Env, env: Env,
info: MessageInfo,
asset_address: &HumanAddr, asset_address: &HumanAddr,
nonce: u32, nonce: u32,
) -> StdResult<HandleResponse> { ) -> StdResult<Response> {
let cfg = config_read(&deps.storage).load()?; let cfg = config_read(deps.storage).load()?;
let request = QueryRequest::Wasm(WasmQuery::Smart { let request = QueryRequest::Wasm(WasmQuery::Smart {
contract_addr: asset_address.clone(), contract_addr: asset_address.clone(),
msg: to_binary(&TokenQuery::TokenInfo {})?, msg: to_binary(&TokenQuery::TokenInfo {})?,
}); });
let asset_canonical = deps.api.canonical_address(asset_address)?; let asset_canonical = deps.api.addr_canonicalize(asset_address)?;
let token_info: TokenInfoResponse = deps.querier.query(&request)?; let token_info: TokenInfoResponse = deps.querier.query(&request)?;
let meta: AssetMeta = AssetMeta { let meta: AssetMeta = AssetMeta {
@ -307,40 +292,37 @@ fn handle_create_asset_meta<S: Storage, A: Api, Q: Querier>(
payload: meta.serialize().to_vec(), payload: meta.serialize().to_vec(),
}; };
Ok(HandleResponse { Ok(Response::new()
messages: vec![CosmosMsg::Wasm(WasmMsg::Execute { .add_message(CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: cfg.wormhole_contract, contract_addr: cfg.wormhole_contract,
msg: to_binary(&WormholeHandleMsg::PostMessage { msg: to_binary(&WormholeExecuteMsg::PostMessage {
message: Binary::from(token_bridge_message.serialize()), message: Binary::from(token_bridge_message.serialize()),
nonce, nonce,
})?, })?,
// forward coins sent to this message // forward coins sent to this message
send: coins_after_tax(deps, env.message.sent_funds.clone())?, funds: coins_after_tax(deps, info.funds.clone())?,
})], }))
log: vec![ .add_attribute("meta.token_chain", CHAIN_ID.to_string())
log("meta.token_chain", CHAIN_ID), .add_attribute("meta.token", asset_address)
log("meta.token", asset_address), .add_attribute("meta.nonce", nonce.to_string())
log("meta.nonce", nonce), .add_attribute("meta.block_time", env.block.time.seconds().to_string()))
log("meta.block_time", env.block.time),
],
data: None,
})
} }
fn submit_vaa<S: Storage, A: Api, Q: Querier>( fn submit_vaa(
deps: &mut Extern<S, A, Q>, mut deps: DepsMut,
env: Env, env: Env,
info: MessageInfo,
data: &Binary, data: &Binary,
) -> StdResult<HandleResponse> { ) -> StdResult<Response> {
let state = config_read(&deps.storage).load()?; let state = config_read(deps.storage).load()?;
let vaa = parse_vaa(deps, env.block.time, data)?; let vaa = parse_vaa(deps.branch(), env.block.time.seconds(), data)?;
let data = vaa.payload; let data = vaa.payload;
if vaa_archive_check(&deps.storage, vaa.hash.as_slice()) { if vaa_archive_check(deps.storage, vaa.hash.as_slice()) {
return ContractError::VaaAlreadyExecuted.std_err(); return ContractError::VaaAlreadyExecuted.std_err();
} }
vaa_archive_add(&mut deps.storage, vaa.hash.as_slice())?; vaa_archive_add(deps.storage, vaa.hash.as_slice())?;
// check if vaa is from governance // check if vaa is from governance
if state.gov_chain == vaa.emitter_chain && state.gov_address == vaa.emitter_address { if state.gov_chain == vaa.emitter_chain && state.gov_address == vaa.emitter_address {
@ -349,10 +331,11 @@ fn submit_vaa<S: Storage, A: Api, Q: Querier>(
let message = TokenBridgeMessage::deserialize(&data)?; let message = TokenBridgeMessage::deserialize(&data)?;
let result = match message.action { match message.action {
Action::TRANSFER => handle_complete_transfer( Action::TRANSFER => handle_complete_transfer(
deps, deps,
env, env,
info,
vaa.emitter_chain, vaa.emitter_chain,
vaa.emitter_address, vaa.emitter_address,
&message.payload, &message.payload,
@ -365,15 +348,10 @@ fn submit_vaa<S: Storage, A: Api, Q: Querier>(
&message.payload, &message.payload,
), ),
_ => ContractError::InvalidVAAAction.std_err(), _ => ContractError::InvalidVAAAction.std_err(),
}; }
return result;
} }
fn handle_governance_payload<S: Storage, A: Api, Q: Querier>( fn handle_governance_payload(deps: DepsMut, env: Env, data: &Vec<u8>) -> StdResult<Response> {
deps: &mut Extern<S, A, Q>,
env: Env,
data: &Vec<u8>,
) -> StdResult<HandleResponse> {
let gov_packet = GovernancePacket::deserialize(&data)?; let gov_packet = GovernancePacket::deserialize(&data)?;
let module = get_string_from_32(&gov_packet.module)?; let module = get_string_from_32(&gov_packet.module)?;
@ -393,51 +371,43 @@ fn handle_governance_payload<S: Storage, A: Api, Q: Querier>(
} }
} }
fn handle_register_chain<S: Storage, A: Api, Q: Querier>( fn handle_register_chain(deps: DepsMut, _env: Env, data: &Vec<u8>) -> StdResult<Response> {
deps: &mut Extern<S, A, Q>,
env: Env,
data: &Vec<u8>,
) -> StdResult<HandleResponse> {
let RegisterChain { let RegisterChain {
chain_id, chain_id,
chain_address, chain_address,
} = RegisterChain::deserialize(&data)?; } = RegisterChain::deserialize(&data)?;
let existing = bridge_contracts_read(&deps.storage).load(&chain_id.to_be_bytes()); let existing = bridge_contracts_read(deps.storage).load(&chain_id.to_be_bytes());
if existing.is_ok() { if existing.is_ok() {
return Err(StdError::generic_err( return Err(StdError::generic_err(
"bridge contract already exists for this chain", "bridge contract already exists for this chain",
)); ));
} }
let mut bucket = bridge_contracts(&mut deps.storage); let mut bucket = bridge_contracts(deps.storage);
bucket.save(&chain_id.to_be_bytes(), &chain_address)?; bucket.save(&chain_id.to_be_bytes(), &chain_address)?;
Ok(HandleResponse { Ok(Response::new()
messages: vec![], .add_attribute("chain_id", chain_id.to_string())
log: vec![ .add_attribute("chain_address", hex::encode(chain_address)))
log("chain_id", chain_id),
log("chain_address", hex::encode(chain_address)),
],
data: None,
})
} }
fn handle_complete_transfer<S: Storage, A: Api, Q: Querier>( fn handle_complete_transfer(
deps: &mut Extern<S, A, Q>, deps: DepsMut,
env: Env, _env: Env,
info: MessageInfo,
emitter_chain: u16, emitter_chain: u16,
emitter_address: Vec<u8>, emitter_address: Vec<u8>,
data: &Vec<u8>, data: &Vec<u8>,
) -> StdResult<HandleResponse> { ) -> StdResult<Response> {
let transfer_info = TransferInfo::deserialize(&data)?; let transfer_info = TransferInfo::deserialize(&data)?;
let expected_contract = let expected_contract =
bridge_contracts_read(&deps.storage).load(&emitter_chain.to_be_bytes())?; bridge_contracts_read(deps.storage).load(&emitter_chain.to_be_bytes())?;
// must be sent by a registered token bridge contract // must be sent by a registered token bridge contract
if expected_contract != emitter_address { if expected_contract != emitter_address {
return Err(StdError::unauthorized()); return Err(StdError::generic_err("invalid emitter"));
} }
if transfer_info.recipient_chain != CHAIN_ID { if transfer_info.recipient_chain != CHAIN_ID {
@ -464,62 +434,58 @@ fn handle_complete_transfer<S: Storage, A: Api, Q: Querier>(
let asset_id = build_asset_id(token_chain, &asset_address); let asset_id = build_asset_id(token_chain, &asset_address);
// Check if this asset is already deployed // Check if this asset is already deployed
let contract_addr = wrapped_asset_read(&deps.storage).load(&asset_id).ok(); let contract_addr = wrapped_asset_read(deps.storage).load(&asset_id).ok();
return if let Some(contract_addr) = contract_addr { return if let Some(contract_addr) = contract_addr {
// Asset already deployed, just mint // Asset already deployed, just mint
let recipient = deps let recipient = deps
.api .api
.human_address(&target_address) .addr_humanize(&target_address)
.or_else(|_| ContractError::WrongTargetAddressFormat.std_err())?; .or_else(|_| ContractError::WrongTargetAddressFormat.std_err())?;
let mut messages = vec![CosmosMsg::Wasm(WasmMsg::Execute { let mut messages = vec![CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: contract_addr.clone(), contract_addr: contract_addr.clone(),
msg: to_binary(&WrappedMsg::Mint { msg: to_binary(&WrappedMsg::Mint {
recipient: recipient.clone(), recipient: recipient.to_string(),
amount: Uint128::from(amount), amount: Uint128::from(amount),
})?, })?,
send: vec![], funds: vec![],
})]; })];
if fee != 0 { if fee != 0 {
messages.push(CosmosMsg::Wasm(WasmMsg::Execute { messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: contract_addr.clone(), contract_addr: contract_addr.clone(),
msg: to_binary(&WrappedMsg::Mint { msg: to_binary(&WrappedMsg::Mint {
recipient: env.message.sender.clone(), recipient: info.sender.to_string(),
amount: Uint128::from(fee), amount: Uint128::from(fee),
})?, })?,
send: vec![], funds: vec![],
})) }))
} }
Ok(HandleResponse { Ok(Response::new()
messages, .add_messages(messages)
log: vec![ .add_attribute("action", "complete_transfer_wrapped")
log("action", "complete_transfer_wrapped"), .add_attribute("contract", contract_addr)
log("contract", contract_addr), .add_attribute("recipient", recipient)
log("recipient", recipient), .add_attribute("amount", amount.to_string()))
log("amount", amount),
],
data: None,
})
} else { } else {
Err(StdError::generic_err("Wrapped asset not deployed. To deploy, invoke CreateWrapped with the associated AssetMeta")) Err(StdError::generic_err("Wrapped asset not deployed. To deploy, invoke CreateWrapped with the associated AssetMeta"))
}; };
} else { } else {
let token_address = transfer_info.token_address.as_slice().get_address(0); let token_address = transfer_info.token_address.as_slice().get_address(0);
let recipient = deps.api.human_address(&target_address)?; let recipient = deps.api.addr_humanize(&target_address)?;
let contract_addr = deps.api.human_address(&token_address)?; let contract_addr = deps.api.addr_humanize(&token_address)?;
// note -- here the amount is the amount the recipient will receive; // note -- here the amount is the amount the recipient will receive;
// amount + fee is the total sent // amount + fee is the total sent
receive_native(&mut deps.storage, &token_address, Uint128(amount + fee))?; receive_native(deps.storage, &token_address, Uint128::new(amount + fee))?;
// undo normalization to 8 decimals // undo normalization to 8 decimals
let token_info: TokenInfoResponse = let token_info: TokenInfoResponse =
deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
contract_addr: contract_addr.clone(), contract_addr: contract_addr.to_string(),
msg: to_binary(&TokenQuery::TokenInfo {})?, msg: to_binary(&TokenQuery::TokenInfo {})?,
}))?; }))?;
@ -529,48 +495,45 @@ fn handle_complete_transfer<S: Storage, A: Api, Q: Querier>(
fee = fee.checked_mul(multiplier).unwrap(); fee = fee.checked_mul(multiplier).unwrap();
let mut messages = vec![CosmosMsg::Wasm(WasmMsg::Execute { let mut messages = vec![CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: contract_addr.clone(), contract_addr: contract_addr.to_string(),
msg: to_binary(&TokenMsg::Transfer { msg: to_binary(&TokenMsg::Transfer {
recipient: recipient.clone(), recipient: recipient.to_string(),
amount: Uint128::from(amount), amount: Uint128::from(amount),
})?, })?,
send: vec![], funds: vec![],
})]; })];
if fee != 0 { if fee != 0 {
messages.push(CosmosMsg::Wasm(WasmMsg::Execute { messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: contract_addr.clone(), contract_addr: contract_addr.to_string(),
msg: to_binary(&TokenMsg::Transfer { msg: to_binary(&TokenMsg::Transfer {
recipient: env.message.sender.clone(), recipient: info.sender.to_string(),
amount: Uint128::from(fee), amount: Uint128::from(fee),
})?, })?,
send: vec![], funds: vec![],
})) }))
} }
Ok(HandleResponse { Ok(Response::new()
messages, .add_messages(messages)
log: vec![ .add_attribute("action", "complete_transfer_native")
log("action", "complete_transfer_native"), .add_attribute("recipient", recipient)
log("recipient", recipient), .add_attribute("contract", contract_addr)
log("contract", contract_addr), .add_attribute("amount", amount.to_string()))
log("amount", amount),
],
data: None,
})
} }
} }
fn handle_initiate_transfer<S: Storage, A: Api, Q: Querier>( fn handle_initiate_transfer(
deps: &mut Extern<S, A, Q>, mut deps: DepsMut,
env: Env, env: Env,
info: MessageInfo,
asset: HumanAddr, asset: HumanAddr,
mut amount: Uint128, mut amount: Uint128,
recipient_chain: u16, recipient_chain: u16,
recipient: Vec<u8>, recipient: Vec<u8>,
mut fee: Uint128, mut fee: Uint128,
nonce: u32, nonce: u32,
) -> StdResult<HandleResponse> { ) -> StdResult<Response> {
if recipient_chain == CHAIN_ID { if recipient_chain == CHAIN_ID {
return ContractError::SameSourceAndTarget.std_err(); return ContractError::SameSourceAndTarget.std_err();
} }
@ -586,23 +549,23 @@ fn handle_initiate_transfer<S: Storage, A: Api, Q: Querier>(
let asset_chain: u16; let asset_chain: u16;
let asset_address: Vec<u8>; let asset_address: Vec<u8>;
let cfg: ConfigInfo = config_read(&deps.storage).load()?; let cfg: ConfigInfo = config_read(deps.storage).load()?;
let asset_canonical: CanonicalAddr = deps.api.canonical_address(&asset)?; let asset_canonical: CanonicalAddr = deps.api.addr_canonicalize(&asset)?;
let mut messages: Vec<CosmosMsg> = vec![]; let mut messages: Vec<CosmosMsg> = vec![];
match wrapped_asset_address_read(&deps.storage).load(asset_canonical.as_slice()) { match wrapped_asset_address_read(deps.storage).load(asset_canonical.as_slice()) {
Ok(_) => { Ok(_) => {
// This is a deployed wrapped asset, burn it // This is a deployed wrapped asset, burn it
messages.push(CosmosMsg::Wasm(WasmMsg::Execute { messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: asset.clone(), contract_addr: asset.clone(),
msg: to_binary(&WrappedMsg::Burn { msg: to_binary(&WrappedMsg::Burn {
account: env.message.sender.clone(), account: info.sender.to_string(),
amount, amount,
})?, })?,
send: vec![], funds: vec![],
})); }));
let request = QueryRequest::<()>::Wasm(WasmQuery::Smart { let request = QueryRequest::<Empty>::Wasm(WasmQuery::Smart {
contract_addr: asset, contract_addr: asset,
msg: to_binary(&WrappedQuery::WrappedAssetInfo {})?, msg: to_binary(&WrappedQuery::WrappedAssetInfo {})?,
}); });
@ -622,13 +585,13 @@ fn handle_initiate_transfer<S: Storage, A: Api, Q: Querier>(
let decimals = token_info.decimals; let decimals = token_info.decimals;
let multiplier = 10u128.pow((max(decimals, 8u8) - 8u8) as u32); let multiplier = 10u128.pow((max(decimals, 8u8) - 8u8) as u32);
// chop off dust // chop off dust
amount = Uint128( amount = Uint128::new(
amount amount
.u128() .u128()
.checked_sub(amount.u128().checked_rem(multiplier).unwrap()) .checked_sub(amount.u128().checked_rem(multiplier).unwrap())
.unwrap(), .unwrap(),
); );
fee = Uint128( fee = Uint128::new(
fee.u128() fee.u128()
.checked_sub(fee.u128().checked_rem(multiplier).unwrap()) .checked_sub(fee.u128().checked_rem(multiplier).unwrap())
.unwrap(), .unwrap(),
@ -638,20 +601,20 @@ fn handle_initiate_transfer<S: Storage, A: Api, Q: Querier>(
messages.push(CosmosMsg::Wasm(WasmMsg::Execute { messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: asset, contract_addr: asset,
msg: to_binary(&TokenMsg::TransferFrom { msg: to_binary(&TokenMsg::TransferFrom {
owner: env.message.sender.clone(), owner: info.sender.to_string(),
recipient: env.contract.address.clone(), recipient: env.contract.address.to_string(),
amount, amount,
})?, })?,
send: vec![], funds: vec![],
})); }));
asset_address = extend_address_to_32(&asset_canonical); asset_address = extend_address_to_32(&asset_canonical);
asset_chain = CHAIN_ID; asset_chain = CHAIN_ID;
// convert to normalized amounts before recording & posting vaa // convert to normalized amounts before recording & posting vaa
amount = Uint128(amount.u128().checked_div(multiplier).unwrap()); amount = Uint128::new(amount.u128().checked_div(multiplier).unwrap());
fee = Uint128(fee.u128().checked_div(multiplier).unwrap()); fee = Uint128::new(fee.u128().checked_div(multiplier).unwrap());
send_native(&mut deps.storage, &asset_canonical, amount)?; send_native(deps.storage, &asset_canonical, amount)?;
} }
}; };
@ -671,39 +634,32 @@ fn handle_initiate_transfer<S: Storage, A: Api, Q: Querier>(
messages.push(CosmosMsg::Wasm(WasmMsg::Execute { messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: cfg.wormhole_contract, contract_addr: cfg.wormhole_contract,
msg: to_binary(&WormholeHandleMsg::PostMessage { msg: to_binary(&WormholeExecuteMsg::PostMessage {
message: Binary::from(token_bridge_message.serialize()), message: Binary::from(token_bridge_message.serialize()),
nonce, nonce,
})?, })?,
// forward coins sent to this message // forward coins sent to this message
send: coins_after_tax(deps, env.message.sent_funds.clone())?, funds: coins_after_tax(deps.branch(), info.funds.clone())?,
})); }));
Ok(HandleResponse { Ok(Response::new()
messages, .add_messages(messages)
log: vec![ .add_attribute("transfer.token_chain", asset_chain.to_string())
log("transfer.token_chain", asset_chain), .add_attribute("transfer.token", hex::encode(asset_address))
log("transfer.token", hex::encode(asset_address)), .add_attribute(
log(
"transfer.sender", "transfer.sender",
hex::encode(extend_address_to_32( hex::encode(extend_address_to_32(
&deps.api.canonical_address(&env.message.sender)?, &deps.api.addr_canonicalize(&info.sender.as_str())?,
)), )),
), )
log("transfer.recipient_chain", recipient_chain), .add_attribute("transfer.recipient_chain", recipient_chain.to_string())
log("transfer.recipient", hex::encode(recipient)), .add_attribute("transfer.recipient", hex::encode(recipient))
log("transfer.amount", amount), .add_attribute("transfer.amount", amount.to_string())
log("transfer.nonce", nonce), .add_attribute("transfer.nonce", nonce.to_string())
log("transfer.block_time", env.block.time), .add_attribute("transfer.block_time", env.block.time.seconds().to_string()))
],
data: None,
})
} }
pub fn query<S: Storage, A: Api, Q: Querier>( pub fn query(deps: Deps, msg: QueryMsg) -> StdResult<Binary> {
deps: &Extern<S, A, Q>,
msg: QueryMsg,
) -> StdResult<Binary> {
match msg { match msg {
QueryMsg::WrappedRegistry { chain, address } => { QueryMsg::WrappedRegistry { chain, address } => {
to_binary(&query_wrapped_registry(deps, chain, address.as_slice())?) to_binary(&query_wrapped_registry(deps, chain, address.as_slice())?)
@ -711,14 +667,14 @@ pub fn query<S: Storage, A: Api, Q: Querier>(
} }
} }
pub fn query_wrapped_registry<S: Storage, A: Api, Q: Querier>( pub fn query_wrapped_registry(
deps: &Extern<S, A, Q>, deps: Deps,
chain: u16, chain: u16,
address: &[u8], address: &[u8],
) -> StdResult<WrappedRegistryResponse> { ) -> StdResult<WrappedRegistryResponse> {
let asset_id = build_asset_id(chain, address); let asset_id = build_asset_id(chain, address);
// Check if this asset is already deployed // Check if this asset is already deployed
match wrapped_asset_read(&deps.storage).load(&asset_id) { match wrapped_asset_read(deps.storage).load(&asset_id) {
Ok(address) => Ok(WrappedRegistryResponse { address }), Ok(address) => Ok(WrappedRegistryResponse { address }),
Err(_) => ContractError::AssetNotFound.std_err(), Err(_) => ContractError::AssetNotFound.std_err(),
} }

View File

@ -1,6 +1,5 @@
use cosmwasm_std::{ use cosmwasm_std::{
Binary, Binary,
HumanAddr,
Uint128, Uint128,
}; };
use schemars::JsonSchema; use schemars::JsonSchema;
@ -9,8 +8,10 @@ use serde::{
Serialize, Serialize,
}; };
type HumanAddr = String;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InitMsg { pub struct InstantiateMsg {
// governance contract details // governance contract details
pub gov_chain: u16, pub gov_chain: u16,
pub gov_address: Binary, pub gov_address: Binary,
@ -21,7 +22,7 @@ pub struct InitMsg {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum HandleMsg { pub enum ExecuteMsg {
RegisterAssetHook { RegisterAssetHook {
asset_id: Binary, asset_id: Binary,
}, },

View File

@ -6,7 +6,6 @@ use serde::{
use cosmwasm_std::{ use cosmwasm_std::{
CanonicalAddr, CanonicalAddr,
HumanAddr,
StdError, StdError,
StdResult, StdResult,
Storage, Storage,
@ -25,6 +24,8 @@ use cosmwasm_storage::{
use wormhole::byte_utils::ByteUtils; use wormhole::byte_utils::ByteUtils;
type HumanAddr = String;
pub static CONFIG_KEY: &[u8] = b"config"; pub static CONFIG_KEY: &[u8] = b"config";
pub static WRAPPED_ASSET_KEY: &[u8] = b"wrapped_asset"; pub static WRAPPED_ASSET_KEY: &[u8] = b"wrapped_asset";
pub static WRAPPED_ASSET_ADDRESS_KEY: &[u8] = b"wrapped_asset_address"; pub static WRAPPED_ASSET_ADDRESS_KEY: &[u8] = b"wrapped_asset_address";
@ -42,49 +43,49 @@ pub struct ConfigInfo {
pub wrapped_asset_code_id: u64, pub wrapped_asset_code_id: u64,
} }
pub fn config<S: Storage>(storage: &mut S) -> Singleton<S, ConfigInfo> { pub fn config(storage: &mut dyn Storage) -> Singleton<ConfigInfo> {
singleton(storage, CONFIG_KEY) singleton(storage, CONFIG_KEY)
} }
pub fn config_read<S: Storage>(storage: &S) -> ReadonlySingleton<S, ConfigInfo> { pub fn config_read(storage: &dyn Storage) -> ReadonlySingleton<ConfigInfo> {
singleton_read(storage, CONFIG_KEY) singleton_read(storage, CONFIG_KEY)
} }
pub fn bridge_contracts<S: Storage>(storage: &mut S) -> Bucket<S, Vec<u8>> { pub fn bridge_contracts(storage: &mut dyn Storage) -> Bucket<Vec<u8>> {
bucket(BRIDGE_CONTRACTS, storage) bucket(storage, BRIDGE_CONTRACTS)
} }
pub fn bridge_contracts_read<S: Storage>(storage: &S) -> ReadonlyBucket<S, Vec<u8>> { pub fn bridge_contracts_read(storage: &dyn Storage) -> ReadonlyBucket<Vec<u8>> {
bucket_read(BRIDGE_CONTRACTS, storage) bucket_read(storage, BRIDGE_CONTRACTS)
} }
pub fn wrapped_asset<S: Storage>(storage: &mut S) -> Bucket<S, HumanAddr> { pub fn wrapped_asset(storage: &mut dyn Storage) -> Bucket<HumanAddr> {
bucket(WRAPPED_ASSET_KEY, storage) bucket(storage, WRAPPED_ASSET_KEY)
} }
pub fn wrapped_asset_read<S: Storage>(storage: &S) -> ReadonlyBucket<S, HumanAddr> { pub fn wrapped_asset_read(storage: &dyn Storage) -> ReadonlyBucket<HumanAddr> {
bucket_read(WRAPPED_ASSET_KEY, storage) bucket_read(storage, WRAPPED_ASSET_KEY)
} }
pub fn wrapped_asset_address<S: Storage>(storage: &mut S) -> Bucket<S, Vec<u8>> { pub fn wrapped_asset_address(storage: &mut dyn Storage) -> Bucket<Vec<u8>> {
bucket(WRAPPED_ASSET_ADDRESS_KEY, storage) bucket(storage, WRAPPED_ASSET_ADDRESS_KEY)
} }
pub fn wrapped_asset_address_read<S: Storage>(storage: &S) -> ReadonlyBucket<S, Vec<u8>> { pub fn wrapped_asset_address_read(storage: &dyn Storage) -> ReadonlyBucket<Vec<u8>> {
bucket_read(WRAPPED_ASSET_ADDRESS_KEY, storage) bucket_read(storage, WRAPPED_ASSET_ADDRESS_KEY)
} }
pub fn send_native<S: Storage>( pub fn send_native(
storage: &mut S, storage: &mut dyn Storage,
asset_address: &CanonicalAddr, asset_address: &CanonicalAddr,
amount: Uint128, amount: Uint128,
) -> StdResult<()> { ) -> StdResult<()> {
let mut counter_bucket = bucket(NATIVE_COUNTER, storage); let mut counter_bucket = bucket(storage, NATIVE_COUNTER);
let new_total = amount let new_total = amount
+ counter_bucket + counter_bucket
.load(asset_address.as_slice()) .load(asset_address.as_slice())
.unwrap_or(Uint128::zero()); .unwrap_or(Uint128::zero());
if new_total > Uint128(u64::MAX as u128) { if new_total > Uint128::new(u64::MAX as u128) {
return Err(StdError::generic_err( return Err(StdError::generic_err(
"transfer exceeds max outstanding bridged token amount", "transfer exceeds max outstanding bridged token amount",
)); ));
@ -92,14 +93,15 @@ pub fn send_native<S: Storage>(
counter_bucket.save(asset_address.as_slice(), &new_total) counter_bucket.save(asset_address.as_slice(), &new_total)
} }
pub fn receive_native<S: Storage>( pub fn receive_native(
storage: &mut S, storage: &mut dyn Storage,
asset_address: &CanonicalAddr, asset_address: &CanonicalAddr,
amount: Uint128, amount: Uint128,
) -> StdResult<()> { ) -> StdResult<()> {
let mut counter_bucket = bucket(NATIVE_COUNTER, storage); let mut counter_bucket = bucket(storage, NATIVE_COUNTER);
let total: Uint128 = counter_bucket.load(asset_address.as_slice())?; let total: Uint128 = counter_bucket.load(asset_address.as_slice())?;
counter_bucket.save(asset_address.as_slice(), &(total - amount)?) let result = total.checked_sub(amount)?;
counter_bucket.save(asset_address.as_slice(), &result)
} }
pub struct Action; pub struct Action;