// SPDX-License-Identifier: BSUL-1.1
pragma solidity ^0.8.19;
interface NotionalProxy {
function rebalance(uint16 currencyId) external;
function checkRebalance() external view returns (uint16[] memory currencyId);
}
contract RebalanceHelper {
event RebalancingFailed(uint16 currencyId, bytes reason);
event RebalancingSucceeded(uint16 currencyId);
error CurrencyIdsLengthCannotBeZero();
error CurrencyIdsNeedToBeSorted();
error Unauthorized();
address public constant RELAYER_ADDRESS = 0x745915418D8B70f39ce9e61A965cBB0C87f9f7Ed;
uint256 public constant DELAY_AFTER_FAILURE = 10 minutes;
NotionalProxy public immutable NOTIONAL;
mapping(uint16 currencyId => uint32 lastFailedRebalanceTimestamp) public failedRebalanceMap;
constructor(NotionalProxy notionalAddress) {
NOTIONAL = notionalAddress;
}
function checkAndRebalance() external {
uint16[] memory currencyIdsToProcess = checkRebalance();
if (currencyIdsToProcess.length > 0) {
rebalanceCurrencyIds(currencyIdsToProcess);
}
}
function rebalanceCurrencyIds(uint16[] memory currencyIds) public {
if (currencyIds.length == 0) {
revert CurrencyIdsLengthCannotBeZero();
}
if (msg.sender != RELAYER_ADDRESS) {
revert Unauthorized();
}
for (uint256 i = 0; i < currencyIds.length; i++) {
uint16 currencyId = currencyIds[i];
// ensure currency ids are unique and sorted
if (i != 0 && currencyIds[i - 1] >= currencyId) {
revert CurrencyIdsNeedToBeSorted();
}
// Rebalance each of the currencies provided.
try NOTIONAL.rebalance(currencyId) {
emit RebalancingSucceeded(currencyId);
} catch (bytes memory reason) {
failedRebalanceMap[currencyId] = uint32(block.timestamp);
emit RebalancingFailed(currencyId, reason);
}
}
}
function checkRebalance()
public
view
returns (uint16[] memory currencyIdsToProcess)
{
uint16[] memory currencyIds = NOTIONAL.checkRebalance();
// skip any currency that failed in previous rebalance that
// happened after block.timestamp - DELAY_AFTER_FAILURE period
uint16 numOfCurrencyIdsToProcess = 0;
for (uint256 i = 0; i < currencyIds.length; i++) {
if (
failedRebalanceMap[currencyIds[i]] + DELAY_AFTER_FAILURE <
block.timestamp
) {
currencyIds[numOfCurrencyIdsToProcess++] = currencyIds[i];
}
}
if (numOfCurrencyIdsToProcess > 0) {
currencyIdsToProcess = new uint16[](
numOfCurrencyIdsToProcess
);
for (uint256 i = 0; i < numOfCurrencyIdsToProcess; i++) {
currencyIdsToProcess[i] = currencyIds[i];
}
}
}
}
{
"compilationTarget": {
"contracts/bots/RebalanceHelper.sol": "RebalanceHelper"
},
"evmVersion": "cancun",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin-4.6/=node_modules/@openzeppelin-4.6/",
":@openzeppelin/=node_modules/@openzeppelin/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/",
":hardhat/=node_modules/hardhat/",
":murky/=lib/murky/",
":openzeppelin-contracts/=lib/murky/lib/openzeppelin-contracts/"
]
}
[{"inputs":[{"internalType":"contract NotionalProxy","name":"notionalAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CurrencyIdsLengthCannotBeZero","type":"error"},{"inputs":[],"name":"CurrencyIdsNeedToBeSorted","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"currencyId","type":"uint16"},{"indexed":false,"internalType":"bytes","name":"reason","type":"bytes"}],"name":"RebalancingFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"currencyId","type":"uint16"}],"name":"RebalancingSucceeded","type":"event"},{"inputs":[],"name":"DELAY_AFTER_FAILURE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NOTIONAL","outputs":[{"internalType":"contract NotionalProxy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RELAYER_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkAndRebalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkRebalance","outputs":[{"internalType":"uint16[]","name":"currencyIdsToProcess","type":"uint16[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"currencyId","type":"uint16"}],"name":"failedRebalanceMap","outputs":[{"internalType":"uint32","name":"lastFailedRebalanceTimestamp","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16[]","name":"currencyIds","type":"uint16[]"}],"name":"rebalanceCurrencyIds","outputs":[],"stateMutability":"nonpayable","type":"function"}]