Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions contracts/Controller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ contract Controller is ERC20, Ownable, ReentrancyGuard {
uint public depositLimit;
uint public userDepositLimit;

uint public depositShareThreshold;
uint public withdrawShareThreshold;

event NewStrategy(address oldStrategy, address newStrategy);
event NewTreasury(address oldTreasury, address newTreasury);
event NewDepositLimit(uint oldLimit, uint newLimit);
Expand All @@ -56,6 +59,10 @@ contract Controller is ERC20, Ownable, ReentrancyGuard {
want = _want;
archimedes = _archimedes;
treasury = _treasury;

// 99% of share/token value
depositShareThreshold = (10 ** _want.decimals()) * 9900 / RATIO_PRECISION;
withdrawShareThreshold = (10 ** _want.decimals()) * 9900 / RATIO_PRECISION;
}

function decimals() override public view returns (uint8) {
Expand Down Expand Up @@ -90,6 +97,18 @@ contract Controller is ERC20, Ownable, ReentrancyGuard {
return pid;
}

function setDepositShareThreshold(uint _t) external onlyOwner nonReentrant {
require(depositShareThreshold != _t, "Same value");

depositShareThreshold = _t;
}

function setWithdrawShareThreshold(uint _t) external onlyOwner nonReentrant {
require(withdrawShareThreshold != _t, "Same value");

withdrawShareThreshold = _t;
}

function setTreasury(address _treasury) external onlyOwner nonReentrant {
require(_treasury != treasury, "Same address");
require(_treasury != address(0), "!ZeroAddress");
Expand Down Expand Up @@ -145,6 +164,8 @@ contract Controller is ERC20, Ownable, ReentrancyGuard {
function deposit(address _senderUser, uint _amount) external onlyArchimedes nonReentrant {
require(!_strategyPaused(), "Strategy paused");
require(_amount > 0, "Insufficient amount");
// Ensure pool is healthy at deposit
require(currentSharePrice() >= depositShareThreshold, "Low Share Price");
_checkDepositLimit(_senderUser, _amount);

IStrategy(strategy).beforeMovement();
Expand All @@ -169,11 +190,16 @@ contract Controller is ERC20, Ownable, ReentrancyGuard {
_mint(_senderUser, shares);

_strategyDeposit();

// Ensure the final step still is healthy
require(currentSharePrice() >= depositShareThreshold, "Low Share Price");
}

// Withdraw partial funds, normally used with a vault withdrawal
function withdraw(address _senderUser, uint _shares) external onlyArchimedes nonReentrant returns (uint) {
require(_shares > 0, "Insufficient shares");
// Ensure pool is healthy before withdraw
require(currentSharePrice() >= withdrawShareThreshold, "Low Share Price");
IStrategy(strategy).beforeMovement();

// This line has to be calc before burn
Expand Down Expand Up @@ -208,6 +234,9 @@ contract Controller is ERC20, Ownable, ReentrancyGuard {

if (!_strategyPaused()) { _strategyDeposit(); }

// Ensure pool still healthy
require(currentSharePrice() >= withdrawShareThreshold, "Low Share Price");

return withdrawn;
}

Expand Down Expand Up @@ -277,4 +306,11 @@ contract Controller is ERC20, Ownable, ReentrancyGuard {
require(_amount <= availableUserDeposit(_user), "Max userDepositLimit reached");
}
}

function currentSharePrice() public view returns (uint) {
uint _totalSupply = totalSupply();
uint _precision = 10 ** decimals();

return _totalSupply <= 0 ? _precision : (balance() * _precision / _totalSupply);
}
}
58 changes: 58 additions & 0 deletions test/integration/ControllerMStableStrat-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -511,4 +511,62 @@ describe('Controller mStable Strat with DAI', () => {
expect(await DAI.balanceOf(strat.address)).to.be.equal(0)
expect(await REWARD_TOKEN.balanceOf(strat.address)).to.be.equal(0)
})

describe('setDepositShareThreshold', async () => {
it('should be reverted for non admin', async () => {
await expect(controller.connect(bob).setDepositShareThreshold(10)).to.be.revertedWith(
'Ownable: caller is not the owner'
)
})

it('should change depositShareThreshold', async () => {
expect(await controller.depositShareThreshold()).to.be.equal(0.99e18 + '')
await controller.setDepositShareThreshold(10)
expect(await controller.depositShareThreshold()).to.be.equal(10)
})

it('should revert for low depositShareThreshold', async () => {
const newBalance = ethers.BigNumber.from('' + 1e18).mul(100000) // 100000 DAI
await setCustomBalanceFor(DAI.address, bob.address, newBalance)

await controller.setDepositShareThreshold(1.1e18 + '') // just to prove it

await DAI.connect(bob).approve(archimedes.address, newBalance)
await expect(archimedes.connect(bob).depositAll(0, zeroAddress)).to.be.revertedWith('Low Share Price')
})

it('should revert for low depositShareThreshold after deposit in strat', async () => {
const newBalance = ethers.BigNumber.from('' + 1e18).mul(100000) // 100000 DAI
await setCustomBalanceFor(DAI.address, bob.address, newBalance)

await controller.setDepositShareThreshold(1.0e18 + '') // just to prove it

await DAI.connect(bob).approve(archimedes.address, newBalance)
await expect(archimedes.connect(bob).depositAll(0, zeroAddress)).to.be.revertedWith('Low Share Price')
})
})

describe('setWithdrawShareThreshold', async () => {
it('should be reverted for non admin', async () => {
await expect(controller.connect(bob).setWithdrawShareThreshold(10)).to.be.revertedWith(
'Ownable: caller is not the owner'
)
})

it('should change withdrawShareThreshold', async () => {
expect(await controller.withdrawShareThreshold()).to.be.equal(0.99e18 + '')
await controller.setWithdrawShareThreshold(10)
expect(await controller.withdrawShareThreshold()).to.be.equal(10)
})

it('should revert for low withdrawShareThreshold', async () => {
const newBalance = ethers.BigNumber.from('' + 1e18).mul(100000) // 100000 DAI
await setCustomBalanceFor(DAI.address, bob.address, newBalance)
await controller.setWithdrawShareThreshold(1.1e18 + '') // just to prove it

await DAI.connect(bob).approve(archimedes.address, newBalance)
await waitFor(archimedes.connect(bob).depositAll(0, zeroAddress))
await expect(archimedes.connect(bob).withdrawAll(0)).to.be.revertedWith('Low Share Price')
})
})
})
2 changes: 1 addition & 1 deletion utils
Submodule utils updated from db6562 to 4dc8bb