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
31 changes: 19 additions & 12 deletions contracts/core/MultiGauge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ contract MultiGauge is StakelessMultiPoolBase, IGauge {
//region ---------------------- Constants

/// @dev Version of this contract. Adjust manually on each code modification.
string public constant VERSION = "1.0.0";
/// 1.0.1 - check left() in updatePeriod
string public constant VERSION = "1.0.1";

// keccak256(abi.encode(uint256(keccak256("erc7201:myrd.MultiGauge")) - 1)) & ~bytes32(uint256(0xff))
bytes32 internal constant MULTI_GAUGE_STORAGE_LOCATION = 0x56fe937432a4b636174f357965a052660eeb836d7a87be456fd784604b733000; // erc7201:myrd.MultiGauge
Expand Down Expand Up @@ -64,22 +65,28 @@ contract MultiGauge is StakelessMultiPoolBase, IGauge {
$.activePeriod = _activePeriod;

address _xMyrd = $.xMyrd;
// get MYRD balance before calling rebase()
address _myrd = defaultRewardToken;

// get MYRD balance before calling rebase()
uint balanceBefore = IERC20(_myrd).balanceOf(address(this));

// receive penalties from xMyrd (if any)
// xMyrd will transfer penalties directly to this contract
IXMyrd(_xMyrd).rebase();
// min amount that is allowed to be send to the gauge according implementation of _notifyRewardAmount()
uint _left = left(_xMyrd, _myrd);

// notify reward amount if necessary
// min required amount of the rebase to satisfy requirements in _notifyRewardAmount()
uint minAllowedRebase = amount_ > _left ? 0 : _left - amount_ + 1;

// xMyrd transfers penalties directly to this contract
// assume here that XMyrd won't transfer anything if available penalties are less than minAllowedRebase
IXMyrd(_xMyrd).rebase(minAllowedRebase);

// ensure that rebase haven't sent any penalties OR it sent at least minAllowedRebase
uint balanceAfter = IERC20(_myrd).balanceOf(address(this));
if (
balanceAfter > balanceBefore // penalties received
|| amount_ != 0 // additional amount provided
) {
// received penalties will be added to the amount_ inside _notifyRewardAmount
_notifyRewardAmount(_S().xMyrd, _myrd, amount_, true, balanceBefore);
if (balanceAfter != balanceBefore && balanceAfter < minAllowedRebase + balanceBefore) revert IAppErrors.IncorrectBalance();

// total amount for _notifyRewardAmount must exceed left amount
if (amount_ + balanceAfter > _left + balanceBefore) {
_notifyRewardAmount(_xMyrd, _myrd, amount_, true, balanceBefore);
}
}
//endregion ---------------------- Operator actions
Expand Down
1 change: 1 addition & 0 deletions contracts/interfaces/IAppErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface IAppErrors {
error IncorrectArrayLength();
error IncorrectZeroArgument();
error IncorrectZeroAddress();
error IncorrectBalance();
error AlreadySet();
error ZeroAddress();

Expand Down
3 changes: 2 additions & 1 deletion contracts/interfaces/IXMyrd.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ interface IXMyrd {
function setExemptionTo(address[] calldata exemptee, bool[] calldata exempt) external;

/// @notice Function called by the gauge to send the rebases once a week
function rebase() external;
/// @param minAmount If available penalties < minAmount function won't send anything
function rebase(uint minAmount) external;


// ------------------------------ View functions
Expand Down
20 changes: 17 additions & 3 deletions contracts/test/mocks/XMyrdMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,30 @@ contract XMyrdMock is MockToken {
address gauge;
bool private rebaseCalled;
mapping(address => uint) private _enterForCalled;
uint private rebaseAmountToTransfer;

constructor(address myrd_, address gauge_) MockToken("x", "y") {
myrd = myrd_;
gauge = gauge_;
}

/// @dev Transfer all MYRD balance to the gauge
function rebase() external {
function rebase(uint minAmount) external {
rebaseCalled = true;
if (myrd != address(0) && gauge != address(0)) {
uint256 myrdBalance = IERC20(myrd).balanceOf(address(this));

if (myrdBalance != 0) {
IERC20(myrd).transfer(gauge, myrdBalance);
if (myrdBalance != 0 && myrdBalance >= minAmount && myrdBalance >= 10_000) {
uint amount = rebaseAmountToTransfer == 0 ? myrdBalance : rebaseAmountToTransfer;
IERC20(myrd).transfer(gauge, amount);
}
}
}

function setRebaseAmountToTransfer_(uint rebaseAmountToTransfer_) external {
rebaseAmountToTransfer = rebaseAmountToTransfer_;
}

function isRebaseCalled() external view returns (bool) {
return rebaseCalled;
}
Expand All @@ -38,4 +44,12 @@ contract XMyrdMock is MockToken {
function enterForAmount(address receiver) external view returns (uint) {
return _enterForCalled[receiver];
}

function pendingRebase() external view returns (uint) {
return IERC20(myrd).balanceOf(address(this));
}

function BASIS() external pure returns (uint) {
return 10_000;
}
}
9 changes: 6 additions & 3 deletions contracts/token/XMyrd.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ contract XMyrd is Controllable, ERC20Upgradeable, IXMyrd {

//region ------------------------ Constants

/// @inheritdoc IControllable
string public constant VERSION = "1.0.0";
/// @dev Version of this contract. Adjust manually on each code modification.
/// 1.0.1: add minAmount param to rebase
string public constant VERSION = "1.0.1";

/// @inheritdoc IXMyrd
uint public constant BASIS = 10_000;
Expand Down Expand Up @@ -73,7 +74,7 @@ contract XMyrd is Controllable, ERC20Upgradeable, IXMyrd {
//region ------------------------ Restricted actions

/// @inheritdoc IXMyrd
function rebase() external {
function rebase(uint minAmount) external {
MainStorage storage $ = _S();

address _gauge = $.gauge;
Expand All @@ -90,6 +91,8 @@ contract XMyrd is Controllable, ERC20Upgradeable, IXMyrd {
if (
/// @dev if the rebase is greater than the Basis
period > $.lastDistributedPeriod && _pendingRebase >= BASIS
/// @dev if the rebase is greater than the given threshold
&& _pendingRebase >= minAmount
) {
$.lastDistributedPeriod = period;

Expand Down
10 changes: 8 additions & 2 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,25 @@ export default {
accountsBalance: '100000000000000000000000000000',
},
loggingEnabled: argv.loggingEnabled,
// chains: {
chains: {
// 778877: {
// hardforkHistory: {
// istanbul: 0,
// },
// }
// },
146: {
hardforkHistory: {
shanghai: 0,
},
}
},
},
sonic: {
chainId: 146,
url: argv.sonicRpcUrl,
accounts: [argv.privateKey],
timeout: 99999,
hardfork: "shanghai",
verify: {
etherscan: {
apiKey: argv.networkScanKeySonic,
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@
"@graphql-codegen/cli": "^5.0.0",
"@graphql-codegen/typescript": "^4.0.1",
"@graphql-codegen/typescript-operations": "^4.0.1",
"@nomicfoundation/hardhat-chai-matchers": "^2.0.2",
"@nomicfoundation/hardhat-ethers": "^3.0.5",
"@nomicfoundation/hardhat-chai-matchers": "^2.0.8",
"@nomicfoundation/hardhat-ethers": "^3.0.8",
"@nomicfoundation/hardhat-network-helpers": "^1.0.10",
"@nomicfoundation/hardhat-verify": "^2.0.3",
"@nomicfoundation/hardhat-verify": "^2.0.13",
"@openzeppelin/contracts": "^5.3.0",
"@typechain/ethers-v6": "^0.5.1",
"@typechain/hardhat": "^9.1.0",
Expand All @@ -66,7 +66,7 @@
"ethereumjs-tx": "^2.1.2",
"ethers": "^6.9.1",
"graphql": "^16.8.1",
"hardhat": "^2.19.3",
"hardhat": "^2.23.0",
"hardhat-abi-exporter": "^2.10.1",
"hardhat-contract-sizer": "^2.10.0",
"hardhat-deploy": "^0.11.45",
Expand Down
10 changes: 6 additions & 4 deletions test/core/MultiGaugeFTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ describe('MultiGaugeFTest', function() {
let user3: SignerWithAddress;
let user4: SignerWithAddress;
let deployerInController: SignerWithAddress;
let controllerGovernance: SignerWithAddress;

let controller: Controller;
let xMyrd: XMyrd;
Expand Down Expand Up @@ -73,7 +74,8 @@ describe('MultiGaugeFTest', function() {
usdcMock = await DeployerUtils.deployMockToken(signer, 'USDC', 6, false);
wethMock = await DeployerUtils.deployMockToken(signer, 'WETH', 18, false);

await controller.connect(governance).changeDeployer(deployerInController, false);
controllerGovernance = await DeployUtils.impersonate(await controller.governance());
await controller.connect(controllerGovernance).changeDeployer(deployerInController, false);
});

after(async function () {
Expand All @@ -90,7 +92,7 @@ describe('MultiGaugeFTest', function() {

describe("Check initialization of deployed proxy contracts", () => {
it("should return expected values", async () => {
expect((await controller.governance()).toLowerCase()).eq(governance.address.toLowerCase());
expect((await controller.governance()).toLowerCase()).eq(controllerGovernance.address.toLowerCase());

expect((await gauge.controller()).toLowerCase()).eq((await controller.getAddress()).toLowerCase());
expect((await gauge.xMyrd()).toLowerCase()).eq((await xMyrd.getAddress()).toLowerCase());
Expand Down Expand Up @@ -450,8 +452,8 @@ describe('MultiGaugeFTest', function() {

// ------------ try to remove reward tokens
await expect(await gauge.left(xMyrd, usdcMock)).gt(0n);
await expect(gauge.connect(governance).removeRewardToken(xMyrd, usdcMock)).rejectedWith("Rewards not ended", "there is not empty amount of rewards for the current period");
await expect(gauge.connect(governance).removeRewardToken(xMyrd, wethMock)).rejectedWith("Not reward token", "default token cannot be removed");
await expect(gauge.connect(controllerGovernance).removeRewardToken(xMyrd, usdcMock)).rejectedWith("Rewards not ended", "there is not empty amount of rewards for the current period");
await expect(gauge.connect(controllerGovernance).removeRewardToken(xMyrd, wethMock)).rejectedWith("Not reward token", "default token cannot be removed");

// ------------ register WETH as reward
await gauge.connect(deployerInController).registerRewardToken(xMyrd, wethMock);
Expand Down
Loading