Safety mechanisms

Managing Exposure

Gro Protocol is optimised also for stability and safety and not just the highest possible yields. Risk Balancer keeps track of exposure to tokens and protocols and keeps a running calculation of the maximum exposure allowable before compromising the integrity of the PWRD deposit protection. This allows the system to be resilient to failure of stablecoins or protocols.

Risk Balancing & Stability

Risk Balancer has mechanisms so that the user interactions themselves continuously push the system towards equilibrium by swapping in and out of under- or overexposed assets. If exposures despite this happens to go out of bounds at any time, the protocol will call a system rebalance to ensure equilibrium.

Attack Resistance

We have added various mechanisms to prevent common smart contract attacks.
  • Price & oracle manipulation: We use Curve to price assets during deposits and withdrawals for the most responsive and accurate asset pricing. Because Curve can be manipulated with flash loans, we are also sanity checking stablecoin price ratios with an external third-party price oracle (Chainlink). Learn more about this in the next section.
  • Smart contract interactions: In the short term, we are blocking smart contracts from interacting with the protocol (only allowing tx.origin == msg.sender), which also acts as an additional flash loan attack protection.
  • Withdrawal fee: a small withdrawal fee of 0.5% prevents front-runners from taking advantage of harvests, and prevents attack vectors that would take advantage of minor price changes.
  • Strategy slippage control: if a withdrawal would cause over 1% in slippage due to its size relative to the assets allocated to the strategy from which it pulls funds, the withdrawal would not be able to proceed. Please reach out in Gro discord if this is the case.

Price & oracle manipulation: mitigated by safety checks

A "safety check" is implemented to ensure deposits and withdrawals are not affected by price oracle manipulations. This check compares the relative price of DAI, USDC, and UST cached in the last harvest and Chainlink. Learn more about it below:
If safety check returns false, deposits and withdrawals would be paused until Chainlink and Curve agree on the stablecoin prices. This is because both DepositHandler and WithdrawHandler smart contracts require the safety check to be True for transactions.
If you are trying to make transactions on the dApp when this happens, you would get a "Recalibration in Progress" notification.
You can also see if the safety check returns false by viewing the safetyCheck variable in the Buoy3pool smart contract here. See the screenshot below showing when it returns True.
When the safetyCheck returns False, deposits and withdrawals would be put on pause (see the following _withdraw function line 5 for implementation). You can see the deposit equivalent in DepositHandler contract which you can find in Protocol operations
function _withdraw(WithdrawParameter memory parameters) private {
IController _ctrl = ctrl;
IBuoy _buoy = buoy;
require(_buoy.safetyCheck(), "!safetyCheck");
uint256 deductUsd;
uint256 returnUsd;
uint256 lpAmountFee;
uint256[N_COINS] memory tokenAmounts;
uint256 virtualPrice = _buoy.getVirtualPrice();
if (parameters.all) {
deductUsd = _ctrl.getUserAssets(parameters.pwrd, parameters.account);
returnUsd = deductUsd.sub(deductUsd.mul(withdrawalFee(parameters.pwrd)).div(PERCENTAGE_DECIMAL_FACTOR));
lpAmountFee = returnUsd.mul(DEFAULT_DECIMALS_FACTOR).div(virtualPrice);
} else {
uint256 userAssets = _ctrl.getUserAssets(parameters.pwrd, parameters.account);
uint256 lpAmount = parameters.lpAmount;
uint256 fee = lpAmount.mul(withdrawalFee(parameters.pwrd)).div(PERCENTAGE_DECIMAL_FACTOR);
lpAmountFee = lpAmount.sub(fee);
returnUsd = lpAmountFee.mul(virtualPrice).div(DEFAULT_DECIMALS_FACTOR);
deductUsd = lpAmount.mul(virtualPrice).div(DEFAULT_DECIMALS_FACTOR);
require(deductUsd <= userAssets, "!withdraw: not enough balance");
uint256 hodlerBonus = deductUsd.sub(returnUsd);
bool whale = _ctrl.isValidBigFish(parameters.pwrd, false, returnUsd);
if (parameters.balanced) {
(returnUsd, tokenAmounts) = _withdrawBalanced(
} else {
(returnUsd, tokenAmounts[parameters.index]) = _withdrawSingle(
_ctrl.burnGToken(parameters.pwrd, parameters.all, parameters.account, deductUsd, hodlerBonus);
emit LogNewWithdrawal(

Reminder about Risk

DeFi is still a very new space, and while that's exciting, it comes with risk. Gro Protocol's software helps you access this world, but make sure you do your own research and only supply assets you can afford to lose.