-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Contract: Bridge fees collection and claiming implementation (#60)
- Implemented fee collection and claiming for all tokens. How it works: Every time a user sends a token it will collect fees into the contract (part of the amount). The truncated amount counts towards the fee (so that users can choose what they want to receive on the other side). For claiming, any relayer can claim when ever he wants and the fees will be proportionally distributed to all of them.
- Loading branch information
Showing
10 changed files
with
1,233 additions
and
208 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
use coreum_wasm_sdk::core::CoreumMsg; | ||
use cosmwasm_std::{coin, BankMsg, Coin, Response, Storage, Uint128}; | ||
|
||
use crate::{ | ||
error::ContractError, | ||
state::{CONFIG, FEES_COLLECTED}, | ||
}; | ||
|
||
pub fn amount_after_fees( | ||
amount: Uint128, | ||
bridging_fee: Uint128, | ||
truncated_portion: Uint128, | ||
) -> Result<Uint128, ContractError> { | ||
let fee_to_collect = bridging_fee.saturating_sub(truncated_portion); | ||
|
||
let amount_after_fees = amount | ||
.checked_sub(fee_to_collect) | ||
.map_err(|_| ContractError::CannotCoverBridgingFees {})?; | ||
|
||
Ok(amount_after_fees) | ||
} | ||
|
||
pub fn handle_fee_collection( | ||
storage: &mut dyn Storage, | ||
bridging_fee: Uint128, | ||
token_denom: String, | ||
truncated_portion: Uint128, | ||
) -> Result<Uint128, ContractError> { | ||
// We substract the truncated portion from the bridging_fee. If truncated portion >= fee, | ||
// then we already paid the fees and we collect the truncated portion instead of bridging fee (because it might be bigger than the bridging fee) | ||
let fee_to_collect = bridging_fee.saturating_sub(truncated_portion); | ||
let fee_collected = if fee_to_collect.is_zero() { | ||
truncated_portion | ||
} else { | ||
bridging_fee | ||
}; | ||
|
||
collect_fees(storage, coin(fee_collected.u128(), token_denom))?; | ||
Ok(fee_collected) | ||
} | ||
|
||
pub fn collect_fees(storage: &mut dyn Storage, fee: Coin) -> Result<(), ContractError> { | ||
// We only collect fees if there is something to collect | ||
// If for some reason there is a coin that we are not charging fees for, we don't collect it | ||
if !fee.amount.is_zero() { | ||
let mut fees_collected = FEES_COLLECTED.load(storage)?; | ||
// If we already have the coin in the fee collected array, we update the amount, if not, we add it as a new element. | ||
match fees_collected.iter_mut().find(|c| c.denom == fee.denom) { | ||
Some(coin) => coin.amount += fee.amount, | ||
None => fees_collected.push(fee), | ||
} | ||
FEES_COLLECTED.save(storage, &fees_collected)?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
pub fn claim_fees_for_relayers( | ||
storage: &mut dyn Storage, | ||
) -> Result<Response<CoreumMsg>, ContractError> { | ||
let mut fees_collected = FEES_COLLECTED.load(storage)?; | ||
let relayers = CONFIG.load(storage)?.relayers; | ||
let mut coins_for_each_relayer = vec![]; | ||
|
||
for fee in fees_collected.iter_mut() { | ||
// For each token collected in fees, we will divide the amount by the number of relayers to know how much we need to send to each relayer | ||
let amount_for_each_relayer = fee | ||
.amount | ||
.u128() | ||
.checked_div(relayers.len() as u128) | ||
.unwrap(); | ||
|
||
// If the amount is 0, we don't send it to the relayers | ||
if amount_for_each_relayer != 0 { | ||
coins_for_each_relayer.push(coin(amount_for_each_relayer, fee.denom.to_owned())); | ||
} | ||
|
||
// We substract the amount we are sending to the relayers from the total amount collected | ||
// We can't simply remove it from the array because there might be small amounts left due to truncation when dividing | ||
fee.amount = fee | ||
.amount | ||
.checked_sub(Uint128::from( | ||
amount_for_each_relayer | ||
.checked_mul(relayers.len() as u128) | ||
.unwrap(), | ||
)) | ||
.unwrap(); | ||
} | ||
|
||
// We'll have 1 multi send message for each relayer | ||
let mut send_messages = vec![]; | ||
for relayer in relayers.iter() { | ||
send_messages.push(BankMsg::Send { | ||
to_address: relayer.coreum_address.to_string(), | ||
amount: coins_for_each_relayer.clone(), | ||
}); | ||
} | ||
|
||
// Last thing we do is to clean the fees collected array removing the coins that have 0 amount | ||
// We need to do this step to avoid the posibility of iterating over them next claim | ||
fees_collected.retain(|c| !c.amount.is_zero()); | ||
FEES_COLLECTED.save(storage, &fees_collected)?; | ||
|
||
Ok(Response::new().add_messages(send_messages)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.