terra: cw20 migrations
Change-Id: I3c75291a0d3016a197a15d9ee0717b16b3b479d3
This commit is contained in:
parent
3491a1370b
commit
c832b123fc
|
@ -42,6 +42,7 @@ use crate::{
|
|||
msg::{
|
||||
ExecuteMsg,
|
||||
InstantiateMsg,
|
||||
MigrateMsg,
|
||||
QueryMsg,
|
||||
WrappedAssetInfoResponse,
|
||||
},
|
||||
|
@ -219,11 +220,16 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||
pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult<Response> {
|
||||
Ok(Response::new())
|
||||
}
|
||||
|
||||
pub fn query_token_info(deps: Deps) -> StdResult<TokenInfoResponse> {
|
||||
let info = TOKEN_INFO.load(deps.storage)?;
|
||||
Ok(TokenInfoResponse {
|
||||
name: String::from("Wormhole:") + info.name.as_str(),
|
||||
symbol: String::from("wh") + info.symbol.as_str(),
|
||||
name: info.name + " (Wormhole)",
|
||||
symbol: info.symbol,
|
||||
decimals: info.decimals,
|
||||
total_supply: info.total_supply,
|
||||
})
|
||||
|
|
|
@ -37,6 +37,10 @@ pub struct InitMint {
|
|||
pub amount: Uint128,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct MigrateMsg {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecuteMsg {
|
||||
|
|
|
@ -13,10 +13,13 @@ use cosmwasm_std::{
|
|||
Empty,
|
||||
Env,
|
||||
MessageInfo,
|
||||
Order,
|
||||
QueryRequest,
|
||||
Reply,
|
||||
Response,
|
||||
StdError,
|
||||
StdResult,
|
||||
SubMsg,
|
||||
Uint128,
|
||||
WasmMsg,
|
||||
WasmQuery,
|
||||
|
@ -43,12 +46,14 @@ use crate::{
|
|||
wrapped_asset_read,
|
||||
wrapped_asset_seq,
|
||||
wrapped_asset_seq_read,
|
||||
wrapped_transfer_tmp,
|
||||
Action,
|
||||
AssetMeta,
|
||||
ConfigInfo,
|
||||
RegisterChain,
|
||||
TokenBridgeMessage,
|
||||
TransferInfo,
|
||||
TransferState,
|
||||
UpgradeContract,
|
||||
},
|
||||
};
|
||||
|
@ -79,7 +84,10 @@ use wormhole::state::{
|
|||
ParsedVAA,
|
||||
};
|
||||
|
||||
use cw20::TokenInfoResponse;
|
||||
use cw20::{
|
||||
BalanceResponse,
|
||||
TokenInfoResponse,
|
||||
};
|
||||
|
||||
use cw20_wrapped::msg::{
|
||||
ExecuteMsg as WrappedMsg,
|
||||
|
@ -97,9 +105,12 @@ use sha3::{
|
|||
Digest,
|
||||
Keccak256,
|
||||
};
|
||||
use std::cmp::{
|
||||
use std::{
|
||||
cmp::{
|
||||
max,
|
||||
min,
|
||||
},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
type HumanAddr = String;
|
||||
|
@ -110,8 +121,27 @@ const CHAIN_ID: u16 = 3;
|
|||
const WRAPPED_ASSET_UPDATING: &str = "updating";
|
||||
|
||||
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||
pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult<Response> {
|
||||
Ok(Response::default())
|
||||
pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult<Response> {
|
||||
let bucket = wrapped_asset_address(deps.storage);
|
||||
let mut messages = vec![];
|
||||
for item in bucket.range(None, None, Order::Ascending) {
|
||||
let contract_address = item?.0;
|
||||
messages.push(CosmosMsg::Wasm(WasmMsg::Migrate {
|
||||
contract_addr: deps
|
||||
.api
|
||||
.addr_humanize(&contract_address.into())?
|
||||
.to_string(),
|
||||
new_code_id: 3,
|
||||
msg: to_binary(&MigrateMsg {})?,
|
||||
}));
|
||||
}
|
||||
|
||||
let count = messages.len();
|
||||
|
||||
Ok(Response::new()
|
||||
.add_messages(messages)
|
||||
.add_attribute("migrate", "upgrade cw20 wrappers")
|
||||
.add_attribute("count", count.to_string()))
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||
|
@ -133,6 +163,58 @@ pub fn instantiate(
|
|||
Ok(Response::default())
|
||||
}
|
||||
|
||||
// When CW20 transfers complete, we need to verify the actual amount that is being transferred out
|
||||
// of the bridge. This is to handle fee tokens where the amount expected to be transferred may be
|
||||
// less due to burns, fees, etc.
|
||||
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||
pub fn reply(deps: DepsMut, env: Env, _msg: Reply) -> StdResult<Response> {
|
||||
let cfg = config_read(deps.storage).load()?;
|
||||
let state = wrapped_transfer_tmp(deps.storage).load()?;
|
||||
let mut info = TransferInfo::deserialize(&state.message)?;
|
||||
|
||||
// Fetch CW20 Balance post-transfer.
|
||||
let new_balance: BalanceResponse = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
|
||||
contract_addr: state.token_address.clone(),
|
||||
msg: to_binary(&TokenQuery::Balance {
|
||||
address: env.contract.address.to_string(),
|
||||
})?,
|
||||
}))?;
|
||||
|
||||
// Actual amount should be the difference in balance of the CW20 account in question to account
|
||||
// for fee tokens.
|
||||
let multiplier = Uint128::from_str(&state.multiplier)?;
|
||||
let real_amount = new_balance.balance - Uint128::from_str(&state.previous_balance)?;
|
||||
let real_amount = real_amount / multiplier;
|
||||
|
||||
// If the fee is too large the user would receive nothing.
|
||||
if info.fee.1 > real_amount.u128() {
|
||||
return Err(StdError::generic_err("fee greater than sent amount"));
|
||||
}
|
||||
|
||||
// Update Wormhole message to correct amount.
|
||||
info.amount.1 = real_amount.u128();
|
||||
|
||||
let token_bridge_message = TokenBridgeMessage {
|
||||
action: Action::TRANSFER,
|
||||
payload: info.serialize(),
|
||||
};
|
||||
|
||||
// Post Wormhole Message
|
||||
let message = CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: cfg.wormhole_contract,
|
||||
funds: vec![],
|
||||
msg: to_binary(&WormholeExecuteMsg::PostMessage {
|
||||
message: Binary::from(token_bridge_message.serialize()),
|
||||
nonce: state.nonce,
|
||||
})?,
|
||||
});
|
||||
|
||||
send_native(deps.storage, &state.token_canonical, info.amount.1.into())?;
|
||||
Ok(Response::default()
|
||||
.add_message(message)
|
||||
.add_attribute("action", "reply_handler"))
|
||||
}
|
||||
|
||||
pub fn coins_after_tax(deps: DepsMut, coins: Vec<Coin>) -> StdResult<Vec<Coin>> {
|
||||
let mut res = vec![];
|
||||
for coin in coins {
|
||||
|
@ -805,9 +887,6 @@ fn handle_initiate_transfer_token(
|
|||
if amount.is_zero() {
|
||||
return ContractError::AmountTooLow.std_err();
|
||||
}
|
||||
if fee > amount {
|
||||
return Err(StdError::generic_err("fee greater than sent amount"));
|
||||
}
|
||||
|
||||
let asset_chain: u16;
|
||||
let asset_address: Vec<u8>;
|
||||
|
@ -816,9 +895,15 @@ fn handle_initiate_transfer_token(
|
|||
let asset_canonical: CanonicalAddr = deps.api.addr_canonicalize(&asset)?;
|
||||
|
||||
let mut messages: Vec<CosmosMsg> = vec![];
|
||||
let mut submessages: Vec<SubMsg> = vec![];
|
||||
|
||||
match wrapped_asset_address_read(deps.storage).load(asset_canonical.as_slice()) {
|
||||
Ok(_) => {
|
||||
// If the fee is too large the user will receive nothing.
|
||||
if fee > amount {
|
||||
return Err(StdError::generic_err("fee greater than sent amount"));
|
||||
}
|
||||
|
||||
// This is a deployed wrapped asset, burn it
|
||||
messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: asset.clone(),
|
||||
|
@ -836,50 +921,6 @@ fn handle_initiate_transfer_token(
|
|||
deps.querier.custom_query(&request)?;
|
||||
asset_chain = wrapped_token_info.asset_chain;
|
||||
asset_address = wrapped_token_info.asset_address.as_slice().to_vec();
|
||||
}
|
||||
Err(_) => {
|
||||
// normalize amount to 8 decimals when it sent over the wormhole
|
||||
let token_info: TokenInfoResponse =
|
||||
deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
|
||||
contract_addr: asset.clone(),
|
||||
msg: to_binary(&TokenQuery::TokenInfo {})?,
|
||||
}))?;
|
||||
|
||||
let decimals = token_info.decimals;
|
||||
let multiplier = 10u128.pow((max(decimals, 8u8) - 8u8) as u32);
|
||||
// chop off dust
|
||||
amount = Uint128::new(
|
||||
amount
|
||||
.u128()
|
||||
.checked_sub(amount.u128().checked_rem(multiplier).unwrap())
|
||||
.unwrap(),
|
||||
);
|
||||
fee = Uint128::new(
|
||||
fee.u128()
|
||||
.checked_sub(fee.u128().checked_rem(multiplier).unwrap())
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
// This is a regular asset, transfer its balance
|
||||
messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: asset,
|
||||
msg: to_binary(&TokenMsg::TransferFrom {
|
||||
owner: info.sender.to_string(),
|
||||
recipient: env.contract.address.to_string(),
|
||||
amount,
|
||||
})?,
|
||||
funds: vec![],
|
||||
}));
|
||||
asset_address = extend_address_to_32(&asset_canonical);
|
||||
asset_chain = CHAIN_ID;
|
||||
|
||||
// convert to normalized amounts before recording & posting vaa
|
||||
amount = Uint128::new(amount.u128().checked_div(multiplier).unwrap());
|
||||
fee = Uint128::new(fee.u128().checked_div(multiplier).unwrap());
|
||||
|
||||
send_native(deps.storage, &asset_canonical, amount)?;
|
||||
}
|
||||
};
|
||||
|
||||
let transfer_info = TransferInfo {
|
||||
token_chain: asset_chain,
|
||||
|
@ -904,9 +945,87 @@ fn handle_initiate_transfer_token(
|
|||
// forward coins sent to this message
|
||||
funds: coins_after_tax(deps.branch(), info.funds.clone())?,
|
||||
}));
|
||||
}
|
||||
Err(_) => {
|
||||
// normalize amount to 8 decimals when it sent over the wormhole
|
||||
let token_info: TokenInfoResponse =
|
||||
deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
|
||||
contract_addr: asset.clone(),
|
||||
msg: to_binary(&TokenQuery::TokenInfo {})?,
|
||||
}))?;
|
||||
|
||||
let decimals = token_info.decimals;
|
||||
let multiplier = 10u128.pow((max(decimals, 8u8) - 8u8) as u32);
|
||||
|
||||
// chop off dust
|
||||
amount = Uint128::new(
|
||||
amount
|
||||
.u128()
|
||||
.checked_sub(amount.u128().checked_rem(multiplier).unwrap())
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
fee = Uint128::new(
|
||||
fee.u128()
|
||||
.checked_sub(fee.u128().checked_rem(multiplier).unwrap())
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
// This is a regular asset, transfer its balance
|
||||
submessages.push(SubMsg::reply_on_success(
|
||||
CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: asset.clone(),
|
||||
msg: to_binary(&TokenMsg::TransferFrom {
|
||||
owner: info.sender.to_string(),
|
||||
recipient: env.contract.address.to_string(),
|
||||
amount,
|
||||
})?,
|
||||
funds: vec![],
|
||||
}),
|
||||
1,
|
||||
));
|
||||
|
||||
asset_address = extend_address_to_32(&asset_canonical);
|
||||
asset_chain = CHAIN_ID;
|
||||
|
||||
// convert to normalized amounts before recording & posting vaa
|
||||
amount = Uint128::new(amount.u128().checked_div(multiplier).unwrap());
|
||||
fee = Uint128::new(fee.u128().checked_div(multiplier).unwrap());
|
||||
|
||||
let transfer_info = TransferInfo {
|
||||
token_chain: asset_chain,
|
||||
token_address: asset_address.clone(),
|
||||
amount: (0, amount.u128()),
|
||||
recipient_chain,
|
||||
recipient: recipient.clone(),
|
||||
fee: (0, fee.u128()),
|
||||
};
|
||||
|
||||
// Fetch current CW20 Balance pre-transfer.
|
||||
let balance: BalanceResponse =
|
||||
deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
|
||||
contract_addr: asset.to_string(),
|
||||
msg: to_binary(&TokenQuery::Balance {
|
||||
address: env.contract.address.to_string(),
|
||||
})?,
|
||||
}))?;
|
||||
|
||||
// Wrap up state to be captured by the submessage reply.
|
||||
wrapped_transfer_tmp(deps.storage).save(&TransferState {
|
||||
previous_balance: balance.balance.to_string(),
|
||||
account: info.sender.to_string(),
|
||||
token_address: asset,
|
||||
token_canonical: asset_canonical.clone(),
|
||||
message: transfer_info.serialize(),
|
||||
multiplier: Uint128::new(multiplier).to_string(),
|
||||
nonce,
|
||||
})?;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Response::new()
|
||||
.add_messages(messages)
|
||||
.add_submessages(submessages)
|
||||
.add_attribute("transfer.token_chain", asset_chain.to_string())
|
||||
.add_attribute("transfer.token", hex::encode(asset_address))
|
||||
.add_attribute(
|
||||
|
|
|
@ -27,6 +27,7 @@ use wormhole::byte_utils::ByteUtils;
|
|||
type HumanAddr = String;
|
||||
|
||||
pub static CONFIG_KEY: &[u8] = b"config";
|
||||
pub static TRANSFER_TMP_KEY: &[u8] = b"transfer_tmp";
|
||||
pub static WRAPPED_ASSET_KEY: &[u8] = b"wrapped_asset";
|
||||
pub static WRAPPED_ASSET_SEQ_KEY: &[u8] = b"wrapped_seq_asset";
|
||||
pub static WRAPPED_ASSET_ADDRESS_KEY: &[u8] = b"wrapped_asset_address";
|
||||
|
@ -93,6 +94,25 @@ pub fn wrapped_asset_address_read(storage: &dyn Storage) -> ReadonlyBucket<Vec<u
|
|||
bucket_read(storage, WRAPPED_ASSET_ADDRESS_KEY)
|
||||
}
|
||||
|
||||
type Serialized128 = String;
|
||||
|
||||
/// Structure to keep track of an active CW20 transfer, required to pass state through to the reply
|
||||
/// handler for submessages during a transfer.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct TransferState {
|
||||
pub account: String,
|
||||
pub message: Vec<u8>,
|
||||
pub multiplier: Serialized128,
|
||||
pub nonce: u32,
|
||||
pub previous_balance: Serialized128,
|
||||
pub token_address: HumanAddr,
|
||||
pub token_canonical: CanonicalAddr,
|
||||
}
|
||||
|
||||
pub fn wrapped_transfer_tmp(storage: &mut dyn Storage) -> Singleton<TransferState> {
|
||||
singleton(storage, TRANSFER_TMP_KEY)
|
||||
}
|
||||
|
||||
pub fn send_native(
|
||||
storage: &mut dyn Storage,
|
||||
asset_address: &CanonicalAddr,
|
||||
|
@ -161,6 +181,7 @@ impl TokenBridgeMessage {
|
|||
// 98 u16 recipient_chain
|
||||
// 100 u256 fee
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct TransferInfo {
|
||||
pub amount: (u128, u128),
|
||||
pub token_address: Vec<u8>,
|
||||
|
|
Loading…
Reference in New Issue