From 9d762b9661e64607d68d976586d989f7bd0c708f Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Sat, 2 Nov 2024 21:07:02 -0400 Subject: [PATCH] Back to old style account conversions --- README.md | 67 ++------------------------------------------ src/token/OUSD.sol | 69 +++++++++++++++++++++++++++++----------------- test/OUSD.t.sol | 58 +++++++++++++++++++++++++++++++++----- 3 files changed, 96 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index 9265b45..877340a 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,3 @@ -## Foundry +# YOLO Spike, OUSD Token -**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** - -Foundry consists of: - -- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). -- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. -- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. -- **Chisel**: Fast, utilitarian, and verbose solidity REPL. - -## Documentation - -https://book.getfoundry.sh/ - -## Usage - -### Build - -```shell -$ forge build -``` - -### Test - -```shell -$ forge test -``` - -### Format - -```shell -$ forge fmt -``` - -### Gas Snapshots - -```shell -$ forge snapshot -``` - -### Anvil - -```shell -$ anvil -``` - -### Deploy - -```shell -$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key -``` - -### Cast - -```shell -$ cast -``` - -### Help - -```shell -$ forge --help -$ anvil --help -$ cast --help -``` +In which I take a swing at a balance centric approach to see if it results in simpiler code. diff --git a/src/token/OUSD.sol b/src/token/OUSD.sol index a39e04c..7f32b47 100644 --- a/src/token/OUSD.sol +++ b/src/token/OUSD.sol @@ -259,21 +259,26 @@ contract OUSD is Governable { } function _adjustAccount(address account, int256 balanceChange) internal returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff) { + RebaseOptions state = rebaseState[account]; int256 currentBalance = int256(balanceOf(account)); - int256 newBalance = currentBalance + balanceChange; + uint256 newBalance = uint256(int256(currentBalance) + int256(balanceChange)); if(newBalance < 0){ revert("Transfer amount exceeds balance"); // Should never trigger } + if (state == RebaseOptions.YieldDelegationTarget) { + newBalance += aintMoney[account]; + } + if(_isNonRebasingAccount(account)){ nonRebasingSupplyDiff = balanceChange; if(nonRebasingCreditsPerToken[account]!=1e27){ nonRebasingCreditsPerToken[account] = 1e27; } - _creditBalances[account] = uint256(newBalance) * 1e9; + _creditBalances[account] = newBalance * 1e9; } else { - int256 newCredits = ((newBalance) * int256(_rebasingCreditsPerToken) + 1e18 - 1) / 1e18; - rebasingCreditsDiff = newCredits - int256(_creditBalances[account]); - _creditBalances[account] = uint256(newCredits); + uint256 newCredits = _balanceToRebasingCredits(newBalance); + rebasingCreditsDiff = int256(newCredits) - int256(_creditBalances[account]); + _creditBalances[account] = newCredits; } } @@ -484,6 +489,12 @@ contract OUSD is Governable { } } + function _balanceToRebasingCredits(uint256 balance) internal view returns (uint256) { + // Rounds up, because we need to ensure that accounts allways have + // at least the balance that they should have. + return ((balance) * _rebasingCreditsPerToken + 1e18 - 1) / 1e18; + } + /** * @notice Enable rebasing for an account. * @dev Add a contract address to the non-rebasing exception list. The @@ -510,37 +521,36 @@ contract OUSD is Governable { function _rebaseOptIn(address _account) internal { require(_isNonRebasingAccount(_account), "Account has not opted out"); - uint256 balance = balanceOf(msg.sender); - (int256 beforeRebasingCreditsDiff, int256 beforeNonRebasingSupplyDiff) - = _adjustAccount(msg.sender, -int256(balance)); - nonRebasingCreditsPerToken[msg.sender] = 0; + uint256 balance = balanceOf(msg.sender); + + // Acount rebaseState[msg.sender] = RebaseOptions.OptIn; + nonRebasingCreditsPerToken[msg.sender] = 0; + _creditBalances[msg.sender] = _balanceToRebasingCredits(balance); + + // Globals + nonRebasingSupply -= balance; + _rebasingCredits += _creditBalances[msg.sender]; - (int256 afterRebasingCreditsDiff, int256 afterNonRebasingSupplyDiff) - = _adjustAccount(msg.sender, int256(balance)); - _adjustGlobals( - beforeRebasingCreditsDiff - afterRebasingCreditsDiff, - beforeNonRebasingSupplyDiff - afterNonRebasingSupplyDiff - ); emit AccountRebasingEnabled(_account); } function rebaseOptOut() public nonReentrant { require(!_isNonRebasingAccount(msg.sender), "Account has not opted in"); + + uint256 oldCredits = _creditBalances[msg.sender]; uint256 balance = balanceOf(msg.sender); - (int256 beforeRebasingCreditsDiff, int256 beforeNonRebasingSupplyDiff) - = _adjustAccount(msg.sender, -int256(balance)); - - nonRebasingCreditsPerToken[msg.sender] = 1e27; + + // Acount rebaseState[msg.sender] = RebaseOptions.OptOut; + nonRebasingCreditsPerToken[msg.sender] = 1e27; + _creditBalances[msg.sender] = balance * 1e9; + + // Globals + nonRebasingSupply += balance; + _rebasingCredits -= oldCredits; - (int256 afterRebasingCreditsDiff, int256 afterNonRebasingSupplyDiff) - = _adjustAccount(msg.sender, int256(balance)); - _adjustGlobals( - afterRebasingCreditsDiff - beforeRebasingCreditsDiff, - afterNonRebasingSupplyDiff - beforeNonRebasingSupplyDiff - ); emit AccountRebasingDisabled(msg.sender); } @@ -599,7 +609,14 @@ contract OUSD is Governable { yieldFrom[to] = from; rebaseState[from] = RebaseOptions.YieldDelegationSource; rebaseState[to] = RebaseOptions.YieldDelegationTarget; - // Todo: accounting changes + + uint256 fromAmount = balanceOf(from); + nonRebasingSupply -= fromAmount; + aintMoney[to] = fromAmount; + (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff) + = _adjustAccount(to, int256(fromAmount)); + _adjustGlobals(rebasingCreditsDiff, nonRebasingSupplyDiff); + } function undelegateYield(address from) external onlyGovernor() { diff --git a/test/OUSD.t.sol b/test/OUSD.t.sol index ee8427e..ca3fac5 100644 --- a/test/OUSD.t.sol +++ b/test/OUSD.t.sol @@ -8,11 +8,11 @@ contract CounterTest is Test { OUSD public ousd; address public matt = makeAddr("Matt"); - address public labs = makeAddr("NonRebasing"); + address public nonrebasing = makeAddr("NonRebasing"); address public pool = makeAddr("Pool"); address public collector = makeAddr("Collector"); address public attacker = makeAddr("Attacker"); - address[] accounts = [matt, attacker, labs, pool, collector]; + address[] accounts = [matt, attacker, nonrebasing, pool, collector]; function setUp() public { ousd = new OUSD(); @@ -20,13 +20,13 @@ contract CounterTest is Test { ousd.mint(matt, 1000 ether); assertEq(ousd.totalSupply(), 1000 ether); - ousd.mint(labs, 1000 ether); + ousd.mint(nonrebasing, 1000 ether); ousd.mint(pool, 1000 ether); ousd.mint(collector, 1000 ether); ousd.mint(attacker, 1000 ether); assertEq(ousd.totalSupply(), 5000 ether); - vm.prank(labs); + vm.prank(nonrebasing); ousd.rebaseOptOut(); vm.prank(pool); @@ -36,21 +36,61 @@ contract CounterTest is Test { ousd.rebaseOptOut(); vm.prank(collector); ousd.rebaseOptIn(); + assertEq(ousd.nonRebasingSupply(), 2000 ether); ousd.delegateYield(pool, collector); - assertEq(ousd.nonRebasingSupply(), 1000 ether, "delegate should decrease rebasing"); + assertEq(ousd.nonRebasingSupply(), 1000 ether, "delegate should decrease nonrebasing"); assertEq(ousd.totalSupply(), 5000 ether); + assertEq(ousd.balanceOf(pool), 1000 ether); + assertEq(ousd.balanceOf(collector), 1000 ether); + } + + function _show() internal { + console.log(" ..totalSupply: ", ousd.totalSupply()); + console.log(" ..nonRebasingSupply: ", ousd.nonRebasingSupply()); + console.log(" ..rebasingCredits: ", ousd.rebasingCreditsHighres()); + console.log(" ..rebasingCreditsPerToken: ", ousd.rebasingCreditsPerTokenHighres()); } function test_ChangeSupply() public { - assertEq(ousd.totalSupply(), 6000 ether); + assertEq(ousd.totalSupply(), 5000 ether); assertEq(ousd.nonRebasingSupply(), 1000 ether); ousd.changeSupply(7000 ether); assertEq(ousd.totalSupply(), 7000 ether); assertEq(ousd.nonRebasingSupply(), 1000 ether); } + function test_SimpleRebasingCredits() public { + // Create an OUSD with a very simple credits to token ratio. + // This doesn't test rouding, but does make for nice human readable numbers + // to check the directions of things + + ousd = new OUSD(); + ousd.initialize("", "", address(this), 1e27 / 2); + + ousd.mint(matt, 1000 ether); + assertEq(ousd.rebasingCredits(), 500 ether); + + ousd.mint(nonrebasing, 1000 ether); + assertEq(ousd.rebasingCredits(), 1000 ether); + + vm.prank(nonrebasing); + ousd.rebaseOptOut(); + assertEq(ousd.rebasingCredits(), 500 ether, "rebaseOptOut should reducing total rebasing credits"); + + ousd.burn(matt, 500 ether); + assertEq(ousd.rebasingCredits(), 250 ether, "rebasing burn should reduce rebasing credits"); + + ousd.burn(nonrebasing, 500 ether); + assertEq(ousd.rebasingCredits(), 250 ether); + + // Add yield + assertEq(ousd.balanceOf(matt), 500 ether, "matt should have 500 OUSD"); + ousd.changeSupply(2000 ether); + assertEq(ousd.balanceOf(matt), 1500 ether, "all yield should go to matt"); + } + function test_CanDelegateYield() public { vm.prank(matt); ousd.rebaseOptOut(); @@ -80,8 +120,12 @@ contract CounterTest is Test { function testDelegateYield() public { + console.log(ousd.totalSupply()/1e18); + console.log(ousd.balanceOf(matt)*1/1e18); + ousd.changeSupply(ousd.totalSupply() + 1000 ether); - assertEq(ousd.balanceOf(matt), 100); + assertEq(ousd.balanceOf(matt), 1250 ether); + assertEq(ousd.balanceOf(collector), 1500 ether, "Collecter should have earned both yields"); }