decimal shifting + max outstanding in terra token bridge
Change-Id: I830ea33abfd0b836ea5e69a77678f962cd771e01
This commit is contained in:
parent
7728b47659
commit
c8bc1b57cc
|
@ -23,8 +23,8 @@ pub fn init<S: Storage, A: Api, Q: Querier>(
|
||||||
) -> StdResult<InitResponse> {
|
) -> StdResult<InitResponse> {
|
||||||
// store token info using cw20-base format
|
// store token info using cw20-base format
|
||||||
let data = TokenInfo {
|
let data = TokenInfo {
|
||||||
name: String::from("Wormhole Wrapped"),
|
name: msg.name,
|
||||||
symbol: String::from("WWT"),
|
symbol: msg.symbol,
|
||||||
decimals: msg.decimals,
|
decimals: msg.decimals,
|
||||||
total_supply: Uint128(0),
|
total_supply: Uint128(0),
|
||||||
// set creator as minter
|
// set creator as minter
|
||||||
|
|
|
@ -7,6 +7,8 @@ use cw20::Expiration;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||||
pub struct InitMsg {
|
pub struct InitMsg {
|
||||||
|
pub name: String,
|
||||||
|
pub symbol: String,
|
||||||
pub asset_chain: u16,
|
pub asset_chain: u16,
|
||||||
pub asset_address: Binary,
|
pub asset_address: Binary,
|
||||||
pub decimals: u8,
|
pub decimals: u8,
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
use crate::msg::WrappedRegistryResponse;
|
use crate::msg::WrappedRegistryResponse;
|
||||||
use cosmwasm_std::{
|
use cosmwasm_std::{log, to_binary, Api, CanonicalAddr, CosmosMsg, Env, Extern, HandleResponse, HumanAddr, InitResponse, Querier, QueryRequest, StdError, StdResult, Storage, Uint128, WasmMsg, WasmQuery, Binary};
|
||||||
log, to_binary, Api, Binary, CanonicalAddr, CosmosMsg, Env, Extern, HandleResponse, HumanAddr,
|
|
||||||
InitResponse, Querier, QueryRequest, StdError, StdResult, Storage, Uint128, WasmMsg, WasmQuery,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::msg::{HandleMsg, InitMsg, QueryMsg};
|
use crate::msg::{HandleMsg, InitMsg, QueryMsg};
|
||||||
use crate::state::{
|
use crate::state::{
|
||||||
bridge_contracts, bridge_contracts_read, config, config_read, wrapped_asset,
|
bridge_contracts, bridge_contracts_read, config, config_read, receive_native, send_native,
|
||||||
wrapped_asset_address, wrapped_asset_address_read, wrapped_asset_read, Action, AssetMeta,
|
wrapped_asset, wrapped_asset_address, wrapped_asset_address_read, wrapped_asset_read, Action,
|
||||||
ConfigInfo, RegisterChain, TokenBridgeMessage, TransferInfo,
|
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::{extend_address_to_32, extend_string_to_32};
|
||||||
|
use wormhole::byte_utils::{get_string_from_32, ByteUtils};
|
||||||
use wormhole::error::ContractError;
|
use wormhole::error::ContractError;
|
||||||
|
|
||||||
use cw20_base::msg::HandleMsg as TokenMsg;
|
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 cw20_wrapped::msg::{InitHook, WrappedAssetInfoResponse};
|
||||||
|
|
||||||
use sha3::{Digest, Keccak256};
|
use sha3::{Digest, Keccak256};
|
||||||
|
use std::cmp::{min, max};
|
||||||
|
|
||||||
// Chain ID of Terra
|
// Chain ID of Terra
|
||||||
const CHAIN_ID: u16 = 3;
|
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 {
|
messages: vec![CosmosMsg::Wasm(WasmMsg::Instantiate {
|
||||||
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)?,
|
||||||
|
symbol: get_string_from_32(&meta.symbol)?,
|
||||||
asset_chain: meta.token_chain,
|
asset_chain: meta.token_chain,
|
||||||
asset_address: meta.token_address.to_vec().into(),
|
asset_address: meta.token_address.to_vec().into(),
|
||||||
decimals: meta.decimals,
|
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,
|
||||||
|
@ -190,13 +190,13 @@ fn handle_create_asset_meta<S: Storage, A: Api, Q: Querier>(
|
||||||
) -> StdResult<HandleResponse> {
|
) -> StdResult<HandleResponse> {
|
||||||
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.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 {
|
let meta: AssetMeta = AssetMeta {
|
||||||
token_chain: CHAIN_ID,
|
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 target_address = (&transfer_info.recipient.as_slice()).get_address(0);
|
||||||
|
|
||||||
let (not_supported_amount, mut amount) = transfer_info.amount;
|
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;
|
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 recipient = deps.api.human_address(&target_address)?;
|
||||||
let contract_addr = deps.api.human_address(&token_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 {
|
let mut messages = vec![CosmosMsg::Wasm(WasmMsg::Execute {
|
||||||
contract_addr: contract_addr.clone(),
|
contract_addr: contract_addr.clone(),
|
||||||
msg: to_binary(&TokenMsg::Transfer {
|
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>,
|
deps: &mut Extern<S, A, Q>,
|
||||||
env: Env,
|
env: Env,
|
||||||
asset: HumanAddr,
|
asset: HumanAddr,
|
||||||
amount: Uint128,
|
mut amount: Uint128,
|
||||||
recipient_chain: u16,
|
recipient_chain: u16,
|
||||||
recipient: Vec<u8>,
|
recipient: Vec<u8>,
|
||||||
fee: Uint128,
|
mut fee: Uint128,
|
||||||
nonce: u32,
|
nonce: u32,
|
||||||
) -> StdResult<HandleResponse> {
|
) -> StdResult<HandleResponse> {
|
||||||
// if recipient_chain == CHAIN_ID {
|
if recipient_chain == CHAIN_ID {
|
||||||
// return ContractError::SameSourceAndTarget.std_err();
|
return ContractError::SameSourceAndTarget.std_err();
|
||||||
// }
|
}
|
||||||
|
|
||||||
if amount.is_zero() {
|
if amount.is_zero() {
|
||||||
return ContractError::AmountTooLow.std_err();
|
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();
|
asset_address = wrapped_token_info.asset_address.as_slice().to_vec();
|
||||||
}
|
}
|
||||||
Err(_) => {
|
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
|
// This is a regular asset, transfer its balance
|
||||||
messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
|
messages.push(CosmosMsg::Wasm(WasmMsg::Execute {
|
||||||
contract_addr: asset,
|
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_address = extend_address_to_32(&asset_canonical);
|
||||||
asset_chain = CHAIN_ID;
|
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)?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use cosmwasm_std::{Binary, HumanAddr, StdResult, Storage};
|
use cosmwasm_std::{CanonicalAddr, HumanAddr, StdError, StdResult, Storage, Uint128};
|
||||||
use cosmwasm_storage::{
|
use cosmwasm_storage::{
|
||||||
bucket, bucket_read, singleton, singleton_read, Bucket, ReadonlyBucket, ReadonlySingleton,
|
bucket, bucket_read, singleton, singleton_read, Bucket, ReadonlyBucket, ReadonlySingleton,
|
||||||
Singleton,
|
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_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";
|
||||||
pub static BRIDGE_CONTRACTS: &[u8] = b"bridge_contracts";
|
pub static BRIDGE_CONTRACTS: &[u8] = b"bridge_contracts";
|
||||||
|
pub static NATIVE_COUNTER: &[u8] = b"native_counter";
|
||||||
|
|
||||||
// Guardian set information
|
// Guardian set information
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
#[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)
|
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;
|
pub struct Action;
|
||||||
|
|
||||||
impl Action {
|
impl Action {
|
||||||
|
|
|
@ -65,3 +65,12 @@ pub fn extend_string_to_32(s: &String) -> StdResult<Vec<u8>> {
|
||||||
result.extend(bytes);
|
result.extend(bytes);
|
||||||
Ok(result)
|
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")))
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue