Skip to content

Commit

Permalink
Integrate $BB in YieldDistributor (#94)
Browse files Browse the repository at this point in the history
* chore: adding  voting power pulling

* fix: adding buttered bread init to tests

* fix: adding buttered bread init to tests

* chore: adding buttered bread to initalizer in yd

* fix: reordering variable for upgrade safety

* fix: adding validation of init variables

* fix: amending variable name to conform with style guide

* fix: line break after conditional"

* fix: fixing implication of token symbol

* fix: amend init values to align with init validations

* chore: adding integration test

---------

Co-authored-by: Ron Turetzky <[email protected]>
  • Loading branch information
RonTuretzky and Ron Turetzky authored Sep 22, 2024
1 parent 5bbb335 commit 1989246
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 6 deletions.
2 changes: 2 additions & 0 deletions script/deploy/DeployYieldDistributor.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ contract DeployYieldDistributor is Script {
string public deployConfigPath = string(bytes("./script/deploy/config/deployYD.json"));
string config_data = vm.readFile(deployConfigPath);
address _bread = stdJson.readAddress(config_data, "._bread");
address _butteredBread = stdJson.readAddress(config_data, "._butteredBread");
uint256 _minRequiredVotingPower = stdJson.readUint(config_data, "._minRequiredVotingPower");
uint256 _cycleLength = stdJson.readUint(config_data, "._cycleLength");
uint256 _maxPoints = stdJson.readUint(config_data, "._maxPoints");
Expand All @@ -24,6 +25,7 @@ contract DeployYieldDistributor is Script {
bytes initData = abi.encodeWithSelector(
YieldDistributor.initialize.selector,
_bread,
_butteredBread,
_precision,
_minRequiredVotingPower,
_maxPoints,
Expand Down
8 changes: 6 additions & 2 deletions script/deploy/config/deployBB.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"_owner": "0x918dEf5d593F46735f74F9E2B280Fe51AF3A99ad",
"_liquidityPools": ["0xa555d5344f6FB6c65da19e403Cb4c1eC4a1a5Ee3"],
"_scalingFactors": ["100"],
"_liquidityPools": [
"0xf3d8f3de71657d342db60dd714c8a2ae37eac6b4"
],
"_scalingFactors": [
"100"
],
"_name": "ButteredBread",
"_symbol": "BB"
}
3 changes: 2 additions & 1 deletion script/deploy/config/deployYD.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"_bread": "0xa555d5344f6FB6c65da19e403Cb4c1eC4a1a5Ee3",
"_butteredbread": "0x123456789abcdef123456789abcdef123456789a",
"_owner": "0x918dEf5d593F46735f74F9E2B280Fe51AF3A99ad",
"_projectNames": [
"laborDao",
Expand All @@ -19,7 +20,7 @@
"_maxPoints": 10000,
"_cycleLength": 518400,
"_minRequiredVotingPower": 1728000000000000000000000,
"_lastClaimedBlockNumber": 0,
"_lastClaimedBlockNumber": 1,
"_precision": 1000000000000000000,
"_yieldFixedSplitDivisor": 2
}
27 changes: 25 additions & 2 deletions src/YieldDistributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ contract YieldDistributor is IYieldDistributor, OwnableUpgradeable {
mapping(address => uint256[]) voterDistributions;
/// @notice How much of the yield is divided equally among projects
uint256 public yieldFixedSplitDivisor;
/// @notice The address of the `ButteredBread` token contract
ERC20VotesUpgradeable public BUTTERED_BREAD;

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
Expand All @@ -57,6 +59,7 @@ contract YieldDistributor is IYieldDistributor, OwnableUpgradeable {

function initialize(
address _bread,
address _butteredBread,
uint256 _precision,
uint256 _minRequiredVotingPower,
uint256 _maxPoints,
Expand All @@ -66,8 +69,16 @@ contract YieldDistributor is IYieldDistributor, OwnableUpgradeable {
address[] memory _projects
) public initializer {
__Ownable_init(msg.sender);
if (
_bread == address(0) || _butteredBread == address(0) || _precision == 0 || _minRequiredVotingPower == 0
|| _maxPoints == 0 || _cycleLength == 0 || _yieldFixedSplitDivisor == 0 || _lastClaimedBlockNumber == 0
|| _projects.length == 0
) {
revert MustBeGreaterThanZero();
}

BREAD = Bread(_bread);
BUTTERED_BREAD = ERC20VotesUpgradeable(_butteredBread);
PRECISION = _precision;
minRequiredVotingPower = _minRequiredVotingPower;
maxPoints = _maxPoints;
Expand Down Expand Up @@ -97,8 +108,12 @@ contract YieldDistributor is IYieldDistributor, OwnableUpgradeable {
* @return uint256 The voting power of the user
*/
function getCurrentVotingPower(address _account) public view returns (uint256) {
return
this.getVotingPowerForPeriod(BREAD, lastClaimedBlockNumber - cycleLength, lastClaimedBlockNumber, _account);
return this.getVotingPowerForPeriod(
BREAD, lastClaimedBlockNumber - cycleLength, lastClaimedBlockNumber, _account
)
+ this.getVotingPowerForPeriod(
BUTTERED_BREAD, lastClaimedBlockNumber - cycleLength, lastClaimedBlockNumber, _account
);
}

/**
Expand Down Expand Up @@ -373,4 +388,12 @@ contract YieldDistributor is IYieldDistributor, OwnableUpgradeable {

yieldFixedSplitDivisor = _yieldFixedSplitDivisor;
}

/**
* @notice Set the ButteredBread token contract
* @param _butteredBread Address of the ButteredBread token contract
*/
function setButteredBread(address _butteredBread) public onlyOwner {
BUTTERED_BREAD = ERC20VotesUpgradeable(_butteredBread);
}
}
77 changes: 77 additions & 0 deletions test/ButteredBread.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {ButteredBread, IButteredBread} from "src/ButteredBread.sol";
import {ICurveStableSwap} from "src/interfaces/ICurveStableSwap.sol";
import {IERC20Votes} from "src/interfaces/IERC20Votes.sol";
import {YieldDistributorTestWrapper} from "src/test/YieldDistributorTestWrapper.sol";
import {YieldDistributor, IYieldDistributor} from "src/YieldDistributor.sol";
import {IBread} from "bread-token/src/interfaces/IBread.sol";
import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol";
import {ERC20Mock} from "openzeppelin-contracts/contracts/mocks/token/ERC20Mock.sol";

uint256 constant XDAI_FACTOR = 700; // 700% scaling factor; 7X
uint256 constant TOKEN_AMOUNT = 1000 ether;
Expand All @@ -23,6 +28,21 @@ contract ButteredBreadTest is Test {

uint256 public fixedPointPercent;
address[] public userList;
YieldDistributorTestWrapper public yieldDistributor;
string public deployConfigPath = string(bytes("./test/test_deploy.json"));
string config_data = vm.readFile(deployConfigPath);
bytes projectsRaw = stdJson.parseRaw(config_data, "._projects");
address[] projects = abi.decode(projectsRaw, (address[]));
uint256 _blocktime = stdJson.readUint(config_data, "._blocktime");
uint256 _maxPoints = stdJson.readUint(config_data, "._maxPoints");
uint256 _precision = stdJson.readUint(config_data, "._precision");
uint256 _minVotingAmount = stdJson.readUint(config_data, "._minVotingAmount");
uint256 _cycleLength = stdJson.readUint(config_data, "._cycleLength");
uint256 _minHoldingDuration = stdJson.readUint(config_data, "._minHoldingDuration");
uint256 _lastClaimedBlockNumber = stdJson.readUint(config_data, "._lastClaimedBlockNumber");
uint256 _yieldFixedSplitDivisor = stdJson.readUint(config_data, "._yieldFixedSplitDivisor");
// See test/YieldDistributor.t.sol for explanation of these values
uint256 _minRequiredVotingPower = stdJson.readUint(config_data, "._minRequiredVotingPower");

function setUp() public virtual {
vm.createSelectFork(vm.rpcUrl("gnosis"));
Expand Down Expand Up @@ -54,6 +74,25 @@ contract ButteredBreadTest is Test {
vm.label(GNOSIS_CURVE_POOL_XDAI_BREAD, "CurveLP_XDAI_BREAD");

userList.push(ALICE);

YieldDistributorTestWrapper yieldDistributorImplementation = new YieldDistributorTestWrapper();
address[] memory projects1 = new address[](1);
projects1[0] = address(this);
bytes memory ydinitData = abi.encodeWithSelector(
YieldDistributor.initialize.selector,
address(GNOSIS_BREAD),
address(bb),
_precision,
_minRequiredVotingPower,
_maxPoints,
_cycleLength,
_yieldFixedSplitDivisor,
_lastClaimedBlockNumber,
projects1
);
yieldDistributor = YieldDistributorTestWrapper(
address(new TransparentUpgradeableProxy(address(yieldDistributorImplementation), address(this), ydinitData))
);
}

function _helperAddLiquidity(address _account, uint256 _amountT0, uint256 _amountT1) internal {
Expand Down Expand Up @@ -490,3 +529,41 @@ contract ButteredBreadTest_Delegation is ButteredBreadTest {
assertEq(bb.delegates(ALICE), DELEGATEE);
}
}

interface Mintable {
function mint(address account) external payable;
}

contract ButteredBreadTest_Integration is ButteredBreadTest {
uint256 public start = 32_323_232_323;

function setUp() public virtual override {
super.setUp();
_helperAddLiquidity(ALICE, TOKEN_AMOUNT, TOKEN_AMOUNT);
}

function setUpForCycle(YieldDistributorTestWrapper _yieldDistributor) public {
vm.roll(start - (_cycleLength));
address yieldDistributorOwner = OwnableUpgradeable(_yieldDistributor).owner();
vm.prank(yieldDistributorOwner);
_yieldDistributor.setLastClaimedBlockNumber(vm.getBlockNumber());
address breadOwner = OwnableUpgradeable(GNOSIS_BREAD).owner();
vm.prank(breadOwner);
IBread(GNOSIS_BREAD).setYieldClaimer(address(_yieldDistributor));
vm.roll(start - (_cycleLength + 1));
vm.deal(ALICE, _minVotingAmount);
vm.startPrank(ALICE);
bb.deposit(GNOSIS_CURVE_POOL_XDAI_BREAD, TOKEN_AMOUNT);
Mintable(GNOSIS_BREAD).mint{value: _minVotingAmount}(ALICE);
vm.roll(start);
}

function testIntegration() public {
setUpForCycle(yieldDistributor);
assertEq(bb.balanceOf(ALICE), TOKEN_AMOUNT * XDAI_FACTOR / fixedPointPercent);
assertEq(bb.accountToLPBalance(ALICE, GNOSIS_CURVE_POOL_XDAI_BREAD), TOKEN_AMOUNT);
uint256 bbBalance = bb.balanceOf(ALICE);
uint256 bBalance = IERC20(GNOSIS_BREAD).balanceOf(ALICE);
assertEq(yieldDistributor.getCurrentVotingPower(ALICE), bbBalance + bBalance);
}
}
5 changes: 5 additions & 0 deletions test/YieldDistributor.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {TransparentUpgradeableProxy} from
import {YieldDistributor, IYieldDistributor} from "src/YieldDistributor.sol";
import {YieldDistributorTestWrapper} from "src/test/YieldDistributorTestWrapper.sol";

import {ButteredBread} from "src/ButteredBread.sol";

abstract contract Bread is ERC20VotesUpgradeable, OwnableUpgradeable {
function claimYield(uint256 amount, address receiver) public virtual;
function yieldAccrued() external view virtual returns (uint256);
Expand Down Expand Up @@ -43,6 +45,7 @@ contract YieldDistributorTest is Test {
uint256 _lastClaimedBlockNumber = stdJson.readUint(config_data, "._lastClaimedBlockNumber");
uint256 _yieldFixedSplitDivisor = stdJson.readUint(config_data, "._yieldFixedSplitDivisor");
Bread public bread = Bread(address(_bread));
ButteredBread public butteredBread = ButteredBread(address(_bread));
uint256 minHoldingDurationInBlocks = _minHoldingDuration / _blocktime;

// For testing purposes, these values were used in the following way to configure _minRequiredVotingPower
Expand All @@ -61,6 +64,7 @@ contract YieldDistributorTest is Test {
bytes memory initData = abi.encodeWithSelector(
YieldDistributor.initialize.selector,
address(bread),
address(butteredBread),
_precision,
_minRequiredVotingPower,
_maxPoints,
Expand All @@ -79,6 +83,7 @@ contract YieldDistributorTest is Test {
initData = abi.encodeWithSelector(
YieldDistributor.initialize.selector,
address(bread),
address(butteredBread),
_precision,
_minRequiredVotingPower,
_maxPoints,
Expand Down
3 changes: 2 additions & 1 deletion test/test_deploy.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"_bread": "0xa555d5344f6FB6c65da19e403Cb4c1eC4a1a5Ee3",
"_butteredbread": "0x123456789abcdef123456789abcdef123456789a",
"_projectNames": [
"laborDao",
"Dandelion",
Expand All @@ -17,7 +18,7 @@
"_blocktime": 5,
"_cycleLength": 10,
"_maxPoints": 10000,
"_lastClaimedBlockNumber": 0,
"_lastClaimedBlockNumber": 1,
"_minRequiredVotingPower": 10000000000000000000,
"_minVotingAmount": 10000000000000000000,
"_minHoldingDuration": 864000,
Expand Down

0 comments on commit 1989246

Please sign in to comment.