Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enable rebaseOptIn by the governor #1961

Merged
merged 3 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
35 changes: 27 additions & 8 deletions contracts/contracts/token/OUSD.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ contract OUSD is Initializable, InitializableERC20Detailed, Governable {
uint256 rebasingCredits,
uint256 rebasingCreditsPerToken
);
event RebaseOptIn(address account);
event RebaseOptOut(address account);

enum RebaseOptions {
NotSet,
Expand Down Expand Up @@ -488,32 +490,48 @@ contract OUSD is Initializable, InitializableERC20Detailed, Governable {
}
}

/**
* @notice Enable rebasing for an account.
* @dev Add a contract address to the non-rebasing exception list. The
* address's balance will be part of rebases and the account will be exposed
* to upside and downside.
* @param _account Address of the account.
*/
function governanceRebaseOptIn(address _account) public nonReentrant onlyGovernor {
_rebaseOptIn(_account);
}

/**
* @dev Add a contract address to the non-rebasing exception list. The
* address's balance will be part of rebases and the account will be exposed
* to upside and downside.
*/
function rebaseOptIn() public nonReentrant {
require(_isNonRebasingAccount(msg.sender), "Account has not opted out");
_rebaseOptIn(msg.sender);
}

function _rebaseOptIn(address _account) internal {
require(_isNonRebasingAccount(_account), "Account has not opted out");

// Convert balance into the same amount at the current exchange rate
uint256 newCreditBalance = _creditBalances[msg.sender]
uint256 newCreditBalance = _creditBalances[_account]
.mul(_rebasingCreditsPerToken)
.div(_creditsPerToken(msg.sender));
.div(_creditsPerToken(_account));

// Decreasing non rebasing supply
nonRebasingSupply = nonRebasingSupply.sub(balanceOf(msg.sender));
nonRebasingSupply = nonRebasingSupply.sub(balanceOf(_account));

_creditBalances[msg.sender] = newCreditBalance;
_creditBalances[_account] = newCreditBalance;

// Increase rebasing credits, totalSupply remains unchanged so no
// adjustment necessary
_rebasingCredits = _rebasingCredits.add(_creditBalances[msg.sender]);
_rebasingCredits = _rebasingCredits.add(_creditBalances[_account]);

rebaseState[msg.sender] = RebaseOptions.OptIn;
rebaseState[_account] = RebaseOptions.OptIn;

// Delete any fixed credits per token
delete nonRebasingCreditsPerToken[msg.sender];
delete nonRebasingCreditsPerToken[_account];
emit RebaseOptIn(_account);
}

/**
Expand All @@ -533,6 +551,7 @@ contract OUSD is Initializable, InitializableERC20Detailed, Governable {

// Mark explicitly opted out of rebasing
rebaseState[msg.sender] = RebaseOptions.OptOut;
emit RebaseOptOut(msg.sender);
}

/**
Expand Down
44 changes: 44 additions & 0 deletions contracts/deploy/082_upgrade_oeth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const { deploymentWithGovernanceProposal } = require("../utils/deploy");

module.exports = deploymentWithGovernanceProposal(
{
deployName: "082_upgrade_oeth",
forceDeploy: false,
reduceQueueTime: true,
deployerIsProposer: true
},
async ({ ethers, deployWithConfirmation }) => {
const cOETHProxy = await ethers.getContract("OETHProxy");
const eigenLayerStrategyContract = "0xa4c637e0f704745d182e4d38cab7e7485321d059";

// Deploy new version of OETH contract
const dOETHImpl = await deployWithConfirmation(
"OETH",
[]
);

const cOETH = await ethers.getContractAt(
"OETH",
cOETHProxy.address
);

// Governance Actions
// ----------------
return {
name: "Upgrade the OETH AMO strategy with peg keeping functions.",
actions: [
// Upgrade the OETH token proxy contract to the new implementation
{
contract: cOETHProxy,
signature: "upgradeTo(address)",
args: [dOETHImpl.address],
},
{
contract: cOETH,
signature: "governanceRebaseOptIn(address)",
args: [eigenLayerStrategyContract],
},
],
};
}
);
1 change: 0 additions & 1 deletion contracts/node.sh
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ main()
printf "\n"
echo "🟢 Node initialized"

FORK=true npm run copy-interface-artifacts
FORK=true npx hardhat fund --amount 100000 --network localhost --accountsfromenv true &

# wait for subprocesses to finish
Expand Down
42 changes: 42 additions & 0 deletions contracts/test/token/oeth.fork-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const { expect } = require("chai");

const { loadDefaultFixture } = require("./../_fixture");
const {
isCI,
} = require("./../helpers");

/**
* Regarding hardcoded addresses:
* The addresses are hardcoded in the test files (instead of
* using them from addresses.js) intentionally. While importing and
* using the variables from that file increases readability, it may
* result in it being a single point of failure. Anyone can update
* the addresses.js file and it may go unnoticed.
*
* Points against this: The on-chain data would still be correct,
* making the tests to fail in case only addresses.js is updated.
*
* Still open to discussion.
*/

describe("ForkTest: OETH", function () {
this.timeout(0);

// Retry up to 3 times on CI
this.retries(isCI ? 3 : 0);

let fixture;
beforeEach(async () => {
fixture = await loadDefaultFixture();
});

describe("verify state", () => {
// These tests use a transaction to call a view function so the gas usage can be reported.
it("Should get total value", async () => {
const { oeth } = fixture;
const eigenLayerStrategyContract = "0xa4c637e0f704745d182e4d38cab7e7485321d059";
// 2 equals OptIn
expect(await oeth.rebaseState(eigenLayerStrategyContract)).to.be.equal(2);
});
});
});
13 changes: 11 additions & 2 deletions contracts/test/token/ousd.js
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,12 @@ describe("Token", function () {

const totalSupplyBefore = await ousd.totalSupply();
await expect(mockNonRebasing).has.an.approxBalanceOf("99.50", ousd);
await mockNonRebasing.rebaseOptIn();

const rebaseTx = await mockNonRebasing.rebaseOptIn();
await expect(rebaseTx)
.to.emit(ousd, "RebaseOptIn")
.withArgs(mockNonRebasing.address);

await expect(mockNonRebasing).has.an.approxBalanceOf("99.50", ousd);
expect(await ousd.totalSupply()).to.equal(totalSupplyBefore);

Expand Down Expand Up @@ -470,7 +475,11 @@ describe("Token", function () {
const initialrebasingCreditsPerTokenHighres =
await ousd.rebasingCreditsPerTokenHighres();

await ousd.connect(matt).rebaseOptOut();
const rebaseTx = await ousd.connect(matt).rebaseOptOut();
await expect(rebaseTx)
.to.emit(ousd, "RebaseOptOut")
.withArgs(matt.address);

// Received 100 from the rebase, the 200 simulated yield was split between
// Matt and Josh
await expect(matt).has.an.approxBalanceOf("200.00", ousd);
Expand Down
Loading