diff --git a/bridge_ui/src/hooks/useHandleTransfer.ts b/bridge_ui/src/hooks/useHandleTransfer.ts index d274685b0..6a62aef35 100644 --- a/bridge_ui/src/hooks/useHandleTransfer.ts +++ b/bridge_ui/src/hooks/useHandleTransfer.ts @@ -58,7 +58,7 @@ import { import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry"; import parseError from "../utils/parseError"; import { signSendAndConfirm } from "../utils/solana"; -import { waitForTerraExecution } from "../utils/terra"; +import { waitForTerraExecution, calculateTerraTax } from "../utils/terra"; import useTransferTargetAddressHex from "./useTransferTargetAddress"; async function eth( @@ -198,11 +198,13 @@ async function terra( dispatch(setIsSending(true)); try { const amountParsed = parseUnits(amount, decimals).toString(); + const amountTaxed = await calculateTerraTax(amountParsed, asset); const msgs = await transferFromTerra( wallet.terraAddress, TERRA_TOKEN_BRIDGE_ADDRESS, asset, amountParsed, + amountTaxed, targetChain, targetAddress ); diff --git a/bridge_ui/src/utils/terra.ts b/bridge_ui/src/utils/terra.ts index 138d275f9..a0c703ace 100644 --- a/bridge_ui/src/utils/terra.ts +++ b/bridge_ui/src/utils/terra.ts @@ -1,6 +1,6 @@ import { isNativeTerra } from "@certusone/wormhole-sdk"; import { formatUnits } from "@ethersproject/units"; -import { LCDClient } from "@terra-money/terra.js"; +import { LCDClient, Dec, Int } from "@terra-money/terra.js"; import { TxResult } from "@terra-money/wallet-provider"; // import { TerraTokenMetadata } from "../hooks/useTerraTokenMap"; import { TERRA_HOST } from "./consts"; @@ -37,3 +37,24 @@ export async function waitForTerraExecution(transaction: TxResult) { } return info; } + +export async function calculateTerraTax( + amount: string, + denom: string +): Promise { + try { + // Fetch terra tax state from current chain height. + const lcd = new LCDClient(TERRA_HOST); + const taxRate = await lcd.treasury.taxRate(); + const taxCap = await lcd.treasury.taxCap(denom); + + // Calculate tax rate for the current denomination. + const untaxed = new Int(amount); + const tax = untaxed.toDec().mul(taxRate); + const cap = (taxCap.amount as Int).toDec(); + const min = new Dec((tax.constructor as any).min(tax, cap)); + return untaxed.sub(min.toInt()).toString(); + } catch(e) { + return "0"; + } +} diff --git a/sdk/js/src/token_bridge/transfer.ts b/sdk/js/src/token_bridge/transfer.ts index b91579098..c57c3d281 100644 --- a/sdk/js/src/token_bridge/transfer.ts +++ b/sdk/js/src/token_bridge/transfer.ts @@ -87,6 +87,7 @@ export async function transferFromTerra( tokenBridgeAddress: string, tokenAddress: string, amount: string, + taxed: string, recipientChain: ChainId, recipientAddress: Uint8Array ) { @@ -108,7 +109,7 @@ export async function transferFromTerra( { initiate_transfer: { asset: { - amount: amount, + amount: taxed, info: { native_token: { denom: tokenAddress, diff --git a/terra/contracts-5/token-bridge/src/msg.rs b/terra/contracts-5/token-bridge/src/msg.rs index b85a0dc75..fb2b0bd6d 100644 --- a/terra/contracts-5/token-bridge/src/msg.rs +++ b/terra/contracts-5/token-bridge/src/msg.rs @@ -2,6 +2,7 @@ use cosmwasm_std::{ Binary, Uint128, }; +use terraswap::asset::{Asset, AssetInfo}; use schemars::JsonSchema; use serde::{ Deserialize, @@ -27,9 +28,14 @@ pub enum ExecuteMsg { asset_id: Binary, }, + DepositTokens {}, + WithdrawTokens { + asset: AssetInfo, + }, + + InitiateTransfer { - asset: HumanAddr, - amount: Uint128, + asset: Asset, recipient_chain: u16, recipient: Binary, fee: Uint128, @@ -41,7 +47,7 @@ pub enum ExecuteMsg { }, CreateAssetMeta { - asset_address: HumanAddr, + asset_info: AssetInfo, nonce: u32, }, } diff --git a/terra/contracts/token-bridge/src/contract.rs b/terra/contracts/token-bridge/src/contract.rs index 4adcb6acc..3ee6de0e2 100644 --- a/terra/contracts/token-bridge/src/contract.rs +++ b/terra/contracts/token-bridge/src/contract.rs @@ -193,14 +193,18 @@ fn deposit_tokens( deps: &mut Extern, env: Env, ) -> StdResult { - for fund in env.message.sent_funds { - let deposit_key = format!("{}:{}", env.message.sender, fund.denom); - bridge_deposit(&mut deps.storage).update(deposit_key.as_bytes(), |amount: Option| { - match amount { - Some(v) => Ok(v + fund.amount), - None => Ok(fund.amount) - } - })?; + for coin in env.message.sent_funds { + let asset = Asset { + amount: coin.amount.clone(), + info: AssetInfo::NativeToken { + denom: coin.denom.clone(), + }, + }; + let deducted_amount = asset.deduct_tax(&deps)?.amount; + let deposit_key = format!("{}:{}", env.message.sender, coin.denom); + bridge_deposit(&mut deps.storage).update(deposit_key.as_bytes(), |amount: Option| + Ok(amount.unwrap_or(Uint128(0)) + deducted_amount) + )?; } Ok(HandleResponse {