decimal shifting + max outstanding in terra token bridge

Change-Id: I830ea33abfd0b836ea5e69a77678f962cd771e01
This commit is contained in:
Alwin Peng 2021-07-27 15:00:00 -04:00 committed by Hendrik Hofstadt
parent 7728b47659
commit c8bc1b57cc
5 changed files with 93 additions and 20 deletions

View File

@ -23,8 +23,8 @@ pub fn init<S: Storage, A: Api, Q: Querier>(
) -> StdResult<InitResponse> {
// store token info using cw20-base format
let data = TokenInfo {
name: String::from("Wormhole Wrapped"),
symbol: String::from("WWT"),
name: msg.name,
symbol: msg.symbol,
decimals: msg.decimals,
total_supply: Uint128(0),
// set creator as minter

View File

@ -7,6 +7,8 @@ use cw20::Expiration;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InitMsg {
pub name: String,
pub symbol: String,
pub asset_chain: u16,
pub asset_address: Binary,
pub decimals: u8,

View File

@ -1,17 +1,14 @@
use crate::msg::WrappedRegistryResponse;
use cosmwasm_std::{
log, to_binary, Api, Binary, CanonicalAddr, CosmosMsg, Env, Extern, HandleResponse, HumanAddr,
InitResponse, Querier, QueryRequest, StdError, StdResult, Storage, Uint128, WasmMsg, WasmQuery,
};
use cosmwasm_std::{log, to_binary, Api, CanonicalAddr, CosmosMsg, Env, Extern, HandleResponse, HumanAddr, InitResponse, Querier, QueryRequest, StdError, StdResult, Storage, Uint128, WasmMsg, WasmQuery, Binary};
use crate::msg::{HandleMsg, InitMsg, QueryMsg};
use crate::state::{
bridge_contracts, bridge_contracts_read, config, config_read, wrapped_asset,
wrapped_asset_address, wrapped_asset_address_read, wrapped_asset_read, Action, AssetMeta,
ConfigInfo, RegisterChain, TokenBridgeMessage, TransferInfo,
bridge_contracts, bridge_contracts_read, config, config_read, receive_native, send_native,
wrapped_asset, wrapped_asset_address, wrapped_asset_address_read, wrapped_asset_read, Action,
AssetMeta, ConfigInfo, RegisterChain, TokenBridgeMessage, TransferInfo,
};
use wormhole::byte_utils::ByteUtils;
use wormhole::byte_utils::{extend_address_to_32, extend_string_to_32};
use wormhole::byte_utils::{get_string_from_32, ByteUtils};
use wormhole::error::ContractError;
use cw20_base::msg::HandleMsg as TokenMsg;
@ -30,6 +27,7 @@ use cw20_wrapped::msg::QueryMsg as WrappedQuery;
use cw20_wrapped::msg::{InitHook, WrappedAssetInfoResponse};
use sha3::{Digest, Keccak256};
use std::cmp::{min, max};
// Chain ID of Terra
const CHAIN_ID: u16 = 3;
@ -163,9 +161,11 @@ fn handle_attest_meta<S: Storage, A: Api, Q: Querier>(
messages: vec![CosmosMsg::Wasm(WasmMsg::Instantiate {
code_id: cfg.wrapped_asset_code_id,
msg: to_binary(&WrappedInit {
name: get_string_from_32(&meta.name)?,
symbol: get_string_from_32(&meta.symbol)?,
asset_chain: meta.token_chain,
asset_address: meta.token_address.to_vec().into(),
decimals: meta.decimals,
decimals: min(meta.decimals, 8u8),
mint: None,
init_hook: Some(InitHook {
contract_addr: env.contract.address,
@ -190,13 +190,13 @@ fn handle_create_asset_meta<S: Storage, A: Api, Q: Querier>(
) -> StdResult<HandleResponse> {
let cfg = config_read(&deps.storage).load()?;
let request = QueryRequest::<()>::Wasm(WasmQuery::Smart {
let request = QueryRequest::Wasm(WasmQuery::Smart {
contract_addr: asset_address.clone(),
msg: to_binary(&TokenQuery::TokenInfo {})?,
});
let asset_canonical = deps.api.canonical_address(asset_address)?;
let token_info: TokenInfoResponse = deps.querier.custom_query(&request)?;
let token_info: TokenInfoResponse = deps.querier.query(&request)?;
let meta: AssetMeta = AssetMeta {
token_chain: CHAIN_ID,
@ -345,7 +345,7 @@ fn handle_complete_transfer<S: Storage, A: Api, Q: Querier>(
let target_address = (&transfer_info.recipient.as_slice()).get_address(0);
let (not_supported_amount, mut amount) = transfer_info.amount;
let (not_supported_fee, fee) = transfer_info.fee;
let (not_supported_fee, mut fee) = transfer_info.fee;
amount -= fee;
@ -407,6 +407,20 @@ fn handle_complete_transfer<S: Storage, A: Api, Q: Querier>(
let recipient = deps.api.human_address(&target_address)?;
let contract_addr = deps.api.human_address(&token_address)?;
receive_native(&mut deps.storage, &token_address, Uint128(amount + fee))?;
// undo normalization to 8 decimals
let token_info: TokenInfoResponse =
deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart {
contract_addr: contract_addr.clone(),
msg: to_binary(&TokenQuery::TokenInfo {})?,
}))?;
let decimals = token_info.decimals;
let multiplier = 10u128.pow((max(decimals, 8u8) - 8u8) as u32);
amount *= multiplier;
fee *= multiplier;
let mut messages = vec![CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: contract_addr.clone(),
msg: to_binary(&TokenMsg::Transfer {
@ -444,15 +458,15 @@ fn handle_initiate_transfer<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
env: Env,
asset: HumanAddr,
amount: Uint128,
mut amount: Uint128,
recipient_chain: u16,
recipient: Vec<u8>,
fee: Uint128,
mut fee: Uint128,
nonce: u32,
) -> StdResult<HandleResponse> {
// if recipient_chain == CHAIN_ID {
// return ContractError::SameSourceAndTarget.std_err();
// }
if recipient_chain == CHAIN_ID {
return ContractError::SameSourceAndTarget.std_err();
}
if amount.is_zero() {
return ContractError::AmountTooLow.std_err();
@ -491,6 +505,19 @@ fn handle_initiate_transfer<S: Storage, A: Api, Q: Querier>(
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(amount.u128() - (amount.u128() % multiplier));
fee = Uint128(fee.u128() - (fee.u128() % multiplier));
// This is a regular asset, transfer its balance
messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: asset,
@ -503,6 +530,12 @@ fn handle_initiate_transfer<S: Storage, A: Api, Q: Querier>(
}));
asset_address = extend_address_to_32(&asset_canonical);
asset_chain = CHAIN_ID;
// convert to normalized amounts before recording & posting vaa
amount = Uint128(amount.u128() / multiplier);
fee = Uint128(fee.u128() / multiplier);
send_native(&mut deps.storage, &asset_canonical, amount)?;
}
};

View File

@ -1,7 +1,7 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use cosmwasm_std::{Binary, HumanAddr, StdResult, Storage};
use cosmwasm_std::{CanonicalAddr, HumanAddr, StdError, StdResult, Storage, Uint128};
use cosmwasm_storage::{
bucket, bucket_read, singleton, singleton_read, Bucket, ReadonlyBucket, ReadonlySingleton,
Singleton,
@ -13,6 +13,7 @@ pub static CONFIG_KEY: &[u8] = b"config";
pub static WRAPPED_ASSET_KEY: &[u8] = b"wrapped_asset";
pub static WRAPPED_ASSET_ADDRESS_KEY: &[u8] = b"wrapped_asset_address";
pub static BRIDGE_CONTRACTS: &[u8] = b"bridge_contracts";
pub static NATIVE_COUNTER: &[u8] = b"native_counter";
// Guardian set information
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
@ -57,6 +58,34 @@ pub fn wrapped_asset_address_read<S: Storage>(storage: &S) -> ReadonlyBucket<S,
bucket_read(WRAPPED_ASSET_ADDRESS_KEY, storage)
}
pub fn send_native<S: Storage>(
storage: &mut S,
asset_address: &CanonicalAddr,
amount: Uint128,
) -> StdResult<()> {
let mut counter_bucket = bucket(NATIVE_COUNTER, storage);
let new_total = amount
+ counter_bucket
.load(asset_address.as_slice())
.unwrap_or(Uint128::zero());
if new_total > Uint128(u64::MAX as u128) {
return Err(StdError::generic_err(
"transfer exceeds max outstanding bridged token amount",
));
}
counter_bucket.save(asset_address.as_slice(), &new_total)
}
pub fn receive_native<S: Storage>(
storage: &mut S,
asset_address: &CanonicalAddr,
amount: Uint128,
) -> StdResult<()> {
let mut counter_bucket = bucket(NATIVE_COUNTER, storage);
let total: Uint128 = counter_bucket.load(asset_address.as_slice())?;
counter_bucket.save(asset_address.as_slice(), &(total - amount)?)
}
pub struct Action;
impl Action {

View File

@ -65,3 +65,12 @@ pub fn extend_string_to_32(s: &String) -> StdResult<Vec<u8>> {
result.extend(bytes);
Ok(result)
}
pub fn get_string_from_32(v: &Vec<u8>) -> StdResult<String> {
let mut idx = 31usize;
while v[idx] == 0 {
idx -= 1
}
String::from_utf8(v[..idx + 1].to_vec())
.or_else(|_| Err(StdError::generic_err("could not parse string")))
}