Skip to content

Commit

Permalink
Merge pull request #65 from balancednetwork/fix/calculate-limit-issue
Browse files Browse the repository at this point in the history
fix: rate limit issue
  • Loading branch information
AntonAndell authored Jun 17, 2024
2 parents 74be3a5 + ae14740 commit d37faa9
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 6 deletions.
66 changes: 66 additions & 0 deletions contracts/core-contracts/cw-asset-manager/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,72 @@ mod tests {
assert_eq!(limit.last_update, mock_env.block.time.seconds());
}

#[test]
fn test_stale_rate_limit() {
let (mut deps, env, _, _) = test_setup();

let token = "denom/ibc-ics-20/token";
let owner = OWNER.load(&deps.storage).unwrap();

// 10% every 100 seconds || protect 90% of fund in a timeframe of 100 seconds
let exe_msg = ExecuteMsg::ConfigureRateLimit {
asset: token.to_string(),
period: 100,
percentage: 9000,
};
let mock_info = mock_info(&owner.to_string(), &[]);

let resp = execute(deps.as_mut(), env.clone(), mock_info.clone(), exe_msg);
assert!(resp.is_ok());

deps.querier.update_balance(
env.clone().contract.address,
vec![Coin {
denom: token.to_string(),
amount: Uint128::new(1000),
}],
);
let res = verify_withdraw(deps.as_mut(), env.clone(), token.to_string(), 100, true);
assert!(res.is_ok());
deps.querier.update_balance(
env.clone().contract.address,
vec![Coin {
denom: token.to_string(),
amount: Uint128::new(900),
}],
);

let limit = RATE_LIMITS
.load(deps.as_mut().storage, token.to_string())
.unwrap();
assert_eq!(limit.current_limit, 900);
assert_eq!(limit.last_update, env.block.time.seconds());
let res = verify_withdraw(deps.as_mut(), env.clone(), token.to_string(), 100, true);
assert!(res.is_err());

// Let many periods pass
let block_info = BlockInfo {
height: env.block.height,
time: env.block.time.plus_seconds(2000),
chain_id: env.block.chain_id,
};
let mock_env = Env {
block: block_info,
transaction: env.transaction,
contract: env.contract,
};
// many periods passes we should be able to withdraw up to the full limit, 90
let res = verify_withdraw(deps.as_mut(), mock_env.clone(), token.to_string(), 50, true);
assert!(res.is_ok());

let limit = RATE_LIMITS
.load(deps.as_mut().storage, token.to_string())
.unwrap();
// Limit should be 900-90 = 810
assert_eq!(limit.current_limit, 810);
assert_eq!(limit.last_update, mock_env.block.time.seconds());
}

#[cfg(feature = "archway")]
#[test]
fn test_withdraw_native_archway() {
Expand Down
21 changes: 15 additions & 6 deletions contracts/cw-common/src/rate_limit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,30 @@ impl RateLimited for RateLimit {

// The maximum amount that can be withdraw in one period
let max_withdraw = balance.checked_sub(max_limit).unwrap();
let time_diff = current_time.checked_sub(self.last_update).unwrap();
let time_diff = current_time
.checked_sub(self.last_update)
.unwrap()
.min(self.period);

// The amount that should be added as available
let added_allowed_withdrawal = max_withdraw
.checked_mul(time_diff.into())
.unwrap()
.checked_div(self.period.into())
.unwrap();
let calculated_limit = self
.current_limit
.checked_sub(added_allowed_withdrawal)
.unwrap();

let mut calculated_limit = self.current_limit;

if self.current_limit > added_allowed_withdrawal {
calculated_limit = self
.current_limit
.checked_sub(added_allowed_withdrawal)
.unwrap();
}

// If the balance is below the limit then set limt to current balance (no withdraws are possible)
// If limit goes below what the protected percentage is set it to the maxLimit
let limit = balance.min(calculated_limit).max(max_limit);
let limit = calculated_limit.max(max_limit);
if balance.checked_sub(amount).unwrap() < limit {
return Err(ContractError::Std(StdError::GenericErr {
msg: "Exceeds Withdrawal limits".to_string(),
Expand Down

0 comments on commit d37faa9

Please sign in to comment.