Skip to content

Commit

Permalink
Merge pull request #116 from lista-dao/feature/241107_amo_rate0_bot_s…
Browse files Browse the repository at this point in the history
…etter

+ add bot update rate0 function
  • Loading branch information
qingyang-lista authored Nov 25, 2024
2 parents 9e99fa9 + 09edadf commit d1bf1e4
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 31 deletions.
78 changes: 54 additions & 24 deletions contracts/amo/DynamicDutyCalculator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ contract DynamicDutyCalculator is IDynamicDutyCalculator, Initializable, AccessC

bytes32 public constant INTERACTION = keccak256("INTERACTION");

bytes32 public constant BOT = keccak256("BOT");

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
Expand All @@ -90,7 +92,7 @@ contract DynamicDutyCalculator is IDynamicDutyCalculator, Initializable, AccessC
minPrice = 9 * PEG / 10;
maxPrice = 11 * PEG / 10;

require(_delta < (maxPrice - minPrice), "AggMonetaryPolicy/invalid-delta");
require(_delta < (maxPrice - minPrice), "AMO/invalid-delta");
delta = _delta;

_setupRole(DEFAULT_ADMIN_ROLE, _admin);
Expand All @@ -105,23 +107,13 @@ contract DynamicDutyCalculator is IDynamicDutyCalculator, Initializable, AccessC
* @param enabled If the collateral token is enabled for the dynamic interest rate mechanism.
*/
function setCollateralParams(address collateral, uint256 beta, uint256 rate0, bool enabled) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(collateral != address(0), "AggMonetaryPolicy/invalid-address");
require(beta > 3e5 && beta < 1e8, "AggMonetaryPolicy/invalid-beta");
require(collateral != address(0), "AMO/invalid-address");
require(beta > 3e5 && beta < 1e8, "AMO/invalid-beta");

ilks[collateral].beta = beta;
ilks[collateral].rate0 = rate0;
ilks[collateral].enabled = enabled;

uint256 price = oracle.peek(lisUSD);
ilks[collateral].lastPrice = price;

uint256 duty = calculateRate(price, beta, rate0) + 1e27;
if (duty > maxDuty) duty = maxDuty;
if (duty < minDuty) duty = minDuty;

IDao(interaction).setCollateralDuty(collateral, duty);

emit CollateralParamsUpdated(collateral, beta, rate0, enabled);
_setCollateralRate0(collateral, beta, rate0, enabled);
}

/**
Expand Down Expand Up @@ -192,8 +184,8 @@ contract DynamicDutyCalculator is IDynamicDutyCalculator, Initializable, AccessC
* @param _maxPrice The highest lisUSD price of the dynamic interest rate mechanism's effect.
*/
function setPriceRange(uint256 _minPrice, uint256 _maxPrice) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(_minPrice < PEG && _maxPrice > PEG, "AggMonetaryPolicy/invalid-price-range");
require(delta < (_maxPrice - _minPrice), "AggMonetaryPolicy/invalid-price-diff");
require(_minPrice < PEG && _maxPrice > PEG, "AMO/invalid-price-range");
require(delta < (_maxPrice - _minPrice), "AMO/invalid-price-diff");

minPrice = _minPrice;
maxPrice = _maxPrice;
Expand All @@ -207,7 +199,7 @@ contract DynamicDutyCalculator is IDynamicDutyCalculator, Initializable, AccessC
* @param _maxDuty The maximum duty.
*/
function setDutyRange(uint256 _minDuty, uint256 _maxDuty) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(_minDuty >= 1e27 && _minDuty < _maxDuty, "AggMonetaryPolicy/invalid-duty-range");
require(_minDuty >= 1e27 && _minDuty < _maxDuty, "AMO/invalid-duty-range");

minDuty = _minDuty;
maxDuty = _maxDuty;
Expand All @@ -220,8 +212,8 @@ contract DynamicDutyCalculator is IDynamicDutyCalculator, Initializable, AccessC
* @param _delta The minimum price change required to update duty dynamically.
*/
function setDelta(uint256 _delta) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(delta != _delta && _delta <= minPrice, "AggMonetaryPolicy/invalid-delta");
require(_delta < (maxPrice - minPrice), "AggMonetaryPolicy/delta-is-too-large");
require(delta != _delta && _delta <= minPrice, "AMO/invalid-delta");
require(_delta < (maxPrice - minPrice), "AMO/delta-is-too-large");

delta = _delta;

Expand All @@ -234,20 +226,20 @@ contract DynamicDutyCalculator is IDynamicDutyCalculator, Initializable, AccessC
* @param _addr The address to set.
*/
function file(bytes32 what, address _addr) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(_addr != address(0), "AggMonetaryPolicy/zero-address-provided");
require(_addr != address(0), "AMO/zero-address-provided");

if (what == "interaction") {
require(interaction != _addr, "AggMonetaryPolicy/interaction-already-set");
require(interaction != _addr, "AMO/interaction-already-set");
revokeRole(INTERACTION, interaction);
interaction = _addr;
grantRole(INTERACTION, _addr);
} else if (what == "lisUSD") {
require(lisUSD != _addr, "AggMonetaryPolicy/lisUSD-already-set");
require(lisUSD != _addr, "AMO/lisUSD-already-set");
lisUSD = _addr;
} else if (what == "oracle") {
require(address(oracle) != _addr, "AggMonetaryPolicy/oracle-already-set");
require(address(oracle) != _addr, "AMO/oracle-already-set");
oracle = IResilientOracle(_addr);
} else revert("AggMonetaryPolicy/file-unrecognized-param");
} else revert("AMO/file-unrecognized-param");

emit File(what, _addr);
}
Expand Down Expand Up @@ -281,4 +273,42 @@ contract DynamicDutyCalculator is IDynamicDutyCalculator, Initializable, AccessC
return 1e18;
}
}

/**
* @dev Set rate0 for a collateral tokens.
* @param collaterals The collateral token address list.
* @param rates0 The rates0 list when the price is equal to PEG.
*/
function setCollateralRate0(address[] memory collaterals, uint256[] memory rates0) external onlyRole(BOT) {
require(collaterals.length > 0 && collaterals.length == rates0.length, "AMO/invalid-params");

for (uint256 i = 0; i < collaterals.length; i++) {
address collateral = collaterals[i];
require(collateral != address(0), "AMO/invalid-address");
require(ilks[collateral].enabled, "AMO/invalid-status");

_setCollateralRate0(collateral, ilks[collateral].beta, rates0[i], ilks[collateral].enabled);
}
}

function _setCollateralRate0(address collateral, uint256 beta, uint256 rate0, bool enabled) private {
ilks[collateral].rate0 = rate0;

uint256 price = oracle.peek(lisUSD);
ilks[collateral].lastPrice = price;

uint256 duty;
if (price <= minPrice) {
duty = maxDuty;
} else if (price >= maxPrice) {
duty = minDuty;
} else {
duty = calculateRate(price, beta, rate0) + 1e27;
if (duty > maxDuty) duty = maxDuty;
if (duty < minDuty) duty = minDuty;
}

IDao(interaction).setCollateralDuty(collateral, duty);
emit CollateralParamsUpdated(collateral, beta, rate0, enabled);
}
}
2 changes: 1 addition & 1 deletion scripts/upgrades/deploy_impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const {deployImplementation, verifyImpContract} = require('./utils/upgrade_utils

const oldContractAddress = ''
const oldContractName = ''
const contractName = 'VaultManager'
const contractName = 'DynamicDutyCalculator'


async function main() {
Expand Down
111 changes: 106 additions & 5 deletions test/foundry/DynamicDutyCalculator.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,15 @@ contract DynamicDutyCalculatorTest is Test {
uint256 delta = 200000;

address public proxyAdminOwner = address(0x2A11AA);
address public bot = address(0x3A11AA);

address public collateral = address(0x5A11AA); // random address
address public collateral1 = address(0x6A11AA); // random address

uint256 beta = 1e6;
uint256 rate0 = 3309234382829741600; // 11% APY
uint256 rate0_15p = 4431822000000000000; // 15% APY
uint256 rate0_400p = 51034942716352291304; // 400% APY

address admin;

Expand All @@ -35,7 +40,7 @@ contract DynamicDutyCalculatorTest is Test {

admin = msg.sender;

vm.expectRevert("AggMonetaryPolicy/invalid-delta");
vm.expectRevert("AMO/invalid-delta");
TransparentUpgradeableProxy __dynamicDutyCalculatorProxy = new TransparentUpgradeableProxy(
address(dynamicDutyCalculatorImpl),
proxyAdminOwner,
Expand All @@ -54,6 +59,10 @@ contract DynamicDutyCalculatorTest is Test {
)
);
dynamicDutyCalculator = DynamicDutyCalculator(address(dynamicDutyCalculatorProxy));

vm.startPrank(msg.sender);
dynamicDutyCalculator.grantRole(dynamicDutyCalculator.BOT(), bot);
vm.stopPrank();
}

function testRevert_initialize() public {
Expand Down Expand Up @@ -107,12 +116,12 @@ contract DynamicDutyCalculatorTest is Test {
dynamicDutyCalculator.setCollateralParams(collateral, beta, rate0, true);

vm.startPrank(admin);
vm.expectRevert("AggMonetaryPolicy/invalid-beta");
vm.expectRevert("AMO/invalid-beta");
dynamicDutyCalculator.setCollateralParams(collateral, 3e5, rate0, true);
vm.stopPrank();

vm.startPrank(admin);
vm.expectRevert("AggMonetaryPolicy/invalid-beta");
vm.expectRevert("AMO/invalid-beta");
dynamicDutyCalculator.setCollateralParams(collateral, 1e8, rate0, true);
vm.stopPrank();
}
Expand Down Expand Up @@ -677,7 +686,7 @@ contract DynamicDutyCalculatorTest is Test {
function testRevert_setDelta() public {
vm.startPrank(admin);
uint256 _delta = 20000000;
vm.expectRevert("AggMonetaryPolicy/delta-is-too-large");
vm.expectRevert("AMO/delta-is-too-large");
dynamicDutyCalculator.setDelta(_delta);
vm.stopPrank();
}
Expand All @@ -702,9 +711,101 @@ contract DynamicDutyCalculatorTest is Test {
dynamicDutyCalculator.file("interaction", address(0xAA));

vm.startPrank(admin);
vm.expectRevert("AggMonetaryPolicy/file-unrecognized-param");
vm.expectRevert("AMO/file-unrecognized-param");
dynamicDutyCalculator.file("proxy", address(0xAA));
vm.stopPrank();
}

function test_setCollateralRate0() public {
test_setCollateralParams();

vm.mockCall(
address(oracle),
abi.encodeWithSelector(ResilientOracle.peek.selector, lisUSD),
abi.encode(uint256(99500000)) // returns $0.995
);

vm.mockCall(
address(interaction),
abi.encodeWithSelector(Interaction.setCollateralDuty.selector),
abi.encode(uint256(0))
);

vm.startPrank(bot);
address[] memory collaterals = new address[](1);
collaterals[0] = collateral;
uint256[] memory rates = new uint256[](1);
rates[0] = rate0_15p;
dynamicDutyCalculator.setCollateralRate0(collaterals, rates);
vm.stopPrank();

(bool _enabled, uint256 _lastPrice, uint256 _rate0, uint256 _beta) = dynamicDutyCalculator.ilks(collateral);

assertEq(_beta, beta);
assertEq(_rate0, rate0_15p);
assertEq(_enabled, true);
assertEq(_lastPrice, 99500000);
}

function test_setCollateralRate0_price_overflow() public {
test_setCollateralParams();
vm.mockCall(
address(oracle),
abi.encodeWithSelector(ResilientOracle.peek.selector, lisUSD),
abi.encode(uint256(89000000)) // returns $0.89
);

vm.mockCall(
address(interaction),
abi.encodeWithSelector(Interaction.setCollateralDuty.selector),
abi.encode(uint256(0))
);

vm.startPrank(bot);
address[] memory collaterals = new address[](1);
collaterals[0] = collateral;
uint256[] memory rates = new uint256[](1);
rates[0] = rate0_15p;
dynamicDutyCalculator.setCollateralRate0(collaterals, rates);
vm.stopPrank();

(bool _enabled, uint256 _lastPrice, uint256 _rate0, uint256 _beta) = dynamicDutyCalculator.ilks(collateral);

assertEq(_beta, beta);
assertEq(_rate0, rate0_15p);
assertEq(_enabled, true);
assertEq(_lastPrice, 89000000);
}

function test_setCollateralRate0_revert() public {
test_setCollateralParams();

address[] memory collaterals = new address[](1);
collaterals[0] = collateral;
uint256[] memory rates = new uint256[](1);
rates[0] = rate0_15p;

vm.startPrank(bot);
vm.expectRevert("AMO/invalid-params");
dynamicDutyCalculator.setCollateralRate0(collaterals, new uint256[](2));
vm.stopPrank();

vm.startPrank(address(1));
vm.expectRevert("AccessControl: account 0x0000000000000000000000000000000000000001 is missing role 0x902cbe3a02736af9827fb6a90bada39e955c0941e08f0c63b3a662a7b17a4e2b");
dynamicDutyCalculator.setCollateralRate0(collaterals, rates);
vm.stopPrank();

collaterals[0] = address(0);
vm.startPrank(bot);
vm.expectRevert("AMO/invalid-address");
dynamicDutyCalculator.setCollateralRate0(collaterals, rates);
vm.stopPrank();

collaterals[0] = collateral1;
vm.startPrank(bot);
vm.expectRevert("AMO/invalid-status");
dynamicDutyCalculator.setCollateralRate0(collaterals, rates);
vm.stopPrank();
}
}

0 comments on commit d1bf1e4

Please sign in to comment.