Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
bip-6: Soil Efficiency (#14)
  • Loading branch information
publiuss committed Dec 6, 2021
1 parent dfda4a6 commit 160a367
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 52 deletions.
12 changes: 6 additions & 6 deletions protocol/contracts/C.sol
Expand Up @@ -56,8 +56,8 @@ library C {
uint256 private constant ROOTS_BASE = 1e12;

// Field
uint256 private constant SOIL_MAX_RATIO_CAP = 25e16; // 25%
uint256 private constant SOIL_MIN_RATIO_CAP = 1e15; // 0.1%
uint256 private constant MAX_SOIL_DENOMINATOR = 4; // 25%
uint256 private constant COMPLEX_WEATHER_DENOMINATOR = 1000; // 0.1%


/**
Expand Down Expand Up @@ -104,12 +104,12 @@ library C {
return WITHDRAW_TIME;
}

function getMinSoilRatioCap() internal pure returns (uint256) {
return SOIL_MIN_RATIO_CAP;
function getComplexWeatherDenominator() internal pure returns (uint256) {
return COMPLEX_WEATHER_DENOMINATOR;
}

function getMaxSoilRatioCap() internal pure returns (uint256) {
return SOIL_MAX_RATIO_CAP;
function getMaxSoilDenominator() internal pure returns (uint256) {
return MAX_SOIL_DENOMINATOR;
}

function getHarvestPercentage() internal pure returns (uint256) {
Expand Down
3 changes: 1 addition & 2 deletions protocol/contracts/farm/facets/FieldFacet/Dibbler.sol
Expand Up @@ -64,8 +64,7 @@ contract Dibbler {

function saveSowTime() private {
uint256 totalBeanSupply = bean().totalSupply();
uint256 minTotalSoil = C.getMinSoilRatioCap().mul(bean().totalSupply()).div(1e18);
if (s.f.soil >= minTotalSoil) return;
if (totalSoil() >= totalBeanSupply.div(C.getComplexWeatherDenominator())) return;

uint256 sowTime = block.timestamp.sub(s.season.timestamp);
s.w.nextSowTime = uint32(sowTime);
Expand Down
49 changes: 33 additions & 16 deletions protocol/contracts/farm/facets/SeasonFacet/Life.sol
Expand Up @@ -106,33 +106,42 @@ contract Life {
**/

function increaseSoil(uint256 amount) internal returns (int256) {
uint256 maxTotalSoil = C.getMaxSoilRatioCap().mul(bean().totalSupply()).div(1e18);
uint256 minTotalSoil = C.getMinSoilRatioCap().mul(bean().totalSupply()).div(1e18);
if (s.f.soil > maxTotalSoil) {
amount = s.f.soil.sub(maxTotalSoil);
uint256 maxTotalSoil = getMaxSoil();
uint256 soil = s.f.soil;
if (soil > maxTotalSoil) {
amount = soil.sub(maxTotalSoil);
decrementTotalSoil(amount);
return -int256(amount);
}
uint256 newTotalSoil = s.f.soil + amount;
amount = newTotalSoil <= maxTotalSoil ? amount : maxTotalSoil.sub(s.f.soil);
amount = newTotalSoil >= minTotalSoil ? amount : minTotalSoil.sub(s.f.soil);

uint256 newTotalSoil = soil.add(amount);
if (newTotalSoil > maxTotalSoil) amount = maxTotalSoil.sub(soil);
incrementTotalSoil(amount);
return int256(amount);
}

function decreaseSoil(uint256 amount) internal {
function decreaseSoil(uint256 amount, uint256 harvested) internal returns (int256) {
uint256 minTotalSoil = getMinSoil(harvested);
uint256 soil = s.f.soil;
if (soil < minTotalSoil) {
amount = minTotalSoil.sub(soil);
incrementTotalSoil(amount);
return int256(amount);
}
if (amount > soil) {
amount = soil.sub(minTotalSoil);
} else {
uint256 newTotalSoil = soil.sub(amount);
uint256 maxTotalSoil = getMaxSoil();
if (newTotalSoil > maxTotalSoil) amount = soil.sub(maxTotalSoil);
else if (newTotalSoil < minTotalSoil) amount = soil.sub(minTotalSoil);
}

decrementTotalSoil(amount);
return -int256(amount);
}

function ensureSoilBounds() internal returns (int256) {
uint256 minTotalSoil = C.getMinSoilRatioCap().mul(bean().totalSupply()).div(1e18);
if (s.f.soil < minTotalSoil) {
uint256 amount = minTotalSoil.sub(s.f.soil);
incrementTotalSoil(amount);
return int256(amount);
}
uint256 maxTotalSoil = C.getMaxSoilRatioCap().mul(bean().totalSupply()).div(1e18);
uint256 maxTotalSoil = getMaxSoil();
if (s.f.soil > maxTotalSoil) {
uint256 amount = s.f.soil.sub(maxTotalSoil);
decrementTotalSoil(amount);
Expand All @@ -141,6 +150,14 @@ contract Life {
return 0;
}

function getMaxSoil() internal view returns (uint256 maxSoil) {
maxSoil = bean().totalSupply().div(C.getMaxSoilDenominator());
}

function getMinSoil(uint256 amount) internal view returns (uint256 minSoil) {
minSoil = amount.mul(100).div(100 + s.w.yield);
}

function incrementTotalSoil(uint256 amount) internal {
s.f.soil = s.f.soil.add(amount);
}
Expand Down
2 changes: 1 addition & 1 deletion protocol/contracts/farm/facets/SeasonFacet/Sun.sol
Expand Up @@ -68,7 +68,7 @@ contract Sun is Weather {

function growSupply(uint256 beans, uint256 price) private returns (uint256) {
(uint256 newHarvestable, uint256 newSilo) = increaseSupply(beans);
int256 newSoil = ensureSoilBounds();
int256 newSoil = decreaseSoil(beans, newHarvestable);
emit SupplyIncrease(season(), price, newHarvestable, newSilo, newSoil);
return newSilo;
}
Expand Down
25 changes: 0 additions & 25 deletions protocol/contracts/mocks/mockFacets/MockFieldFacet.sol
Expand Up @@ -16,15 +16,6 @@ contract MockFieldFacet is FieldFacet {

using SafeMath for uint256;

function incrementTotalSoilE(uint256 amount) public {
incrementTotalSoil(amount);
ensureSoilBounds();
}

function incrementTotalSoilEE(uint256 amount) public {
incrementTotalSoil(amount);
}

function incrementTotalHarvestableE(uint256 amount) public {
bean().mint(address(this), amount);
s.f.harvestable = s.f.harvestable.add(amount);
Expand Down Expand Up @@ -52,20 +43,4 @@ contract MockFieldFacet is FieldFacet {
s.f.soil = s.f.soil.add(amount);
}

function ensureSoilBounds() internal returns (int256) {
uint256 minTotalSoil = C.getMinSoilRatioCap().mul(bean().totalSupply()).div(100);
if (s.f.soil < minTotalSoil) {
uint256 amount = minTotalSoil.sub(s.f.soil);
incrementTotalSoil(amount);
return int256(amount);
}
uint256 maxTotalSoil = C.getMaxSoilRatioCap().mul(bean().totalSupply()).div(100);
if (s.f.soil > maxTotalSoil) {
uint256 amount = s.f.soil.sub(maxTotalSoil);
s.f.soil = s.f.soil.sub(amount, "MockField: Not enough Soil.");
return -int256(amount);
}
return 0;
}

}
28 changes: 28 additions & 0 deletions protocol/contracts/mocks/mockFacets/MockSeasonFacet.sol
Expand Up @@ -138,6 +138,34 @@ contract MockSeasonFacet is SeasonFacet {
s.w.lastSoilPercent = number;
}

function incrementTotalSoilE(uint256 amount) public {
incrementTotalSoil(amount);
}

function decrementTotalSoilE(uint256 amount) public {
decrementTotalSoil(amount);
}

function increaseSoilE(uint256 amount) public {
increaseSoil(amount);
}

function decreaseSoilE(uint256 amount, uint256 harvested) public {
decreaseSoil(amount, harvested);
}

function ensureSoilBoundsE() public {
ensureSoilBounds();
}

function minSoil(uint256 amount) public view returns (uint256) {
return getMinSoil(amount);
}

function maxSoil() public view returns (uint256) {
return getMaxSoil();
}

function resetAccount(address account) public {
uint32 _s = season();
for (uint32 j = 0; j <= _s; j++) {
Expand Down
13 changes: 12 additions & 1 deletion protocol/coverage_data/sun.json
@@ -1 +1,12 @@
{"columns":["season","beansInSilo","harvestablePods","unripenedPods","soil","twapBeans","twapUSDC","divisor","beansInPool","ethInPool","newTotalSupply","newBeansInSilo","newSupplyofSoil","newHarvestablePods","newUnripenedPods","newTotalPods","supplyIncrease","seasonOfLastSupplyIncrease","currentSeason","currentTWAP","deltaHarvestablePods","deltaBeansInSilo","deltaSoil"],"data":[["1","1","0","0","0","8","10","10000","1","110","2","1","0","0","0","0","0","0","1","800000000000000000","0","0","0"],["1","1","0","0","0","12","10","10000","1","110","31","30","0","0","0","0","29","1","1","1200000000000000000","0","29","0"],["1","1","0","0","100","12","10","10000","1","110","31","30","7","0","0","0","29","1","1","1200000000000000000","0","29","-93"],["1","1","0","1","100","12","10","10000","1","110","31","29","7","1","0","1","28","1","1","1200000000000000000","1","28","-93"],["1","1","0","1","1","12","10","10000","10000","110","12891","2890","12","1","0","1","2889","1","1","1200000000000000000","1","2889","11"]]}
{"columns":
["season","beansInSilo","harvestablePods","unripenedPods","soil","twapBeans","twapUSDC","divisor","beansInPool","ethInPool","newTotalSupply","newBeansInSilo","newSupplyofSoil","newHarvestablePods","newUnripenedPods","newTotalPods","supplyIncrease","seasonOfLastSupplyIncrease","currentSeason","currentTWAP","deltaHarvestablePods","deltaBeansInSilo","deltaSoil"],
"data":[
["1","1","0","0","0","8","10","10000","1","110","2","1","0","0","0","0","0","0","1","800000000000000000","0","0","0"],
["1","1","0","0","2","8","10","10000","1","110","2","1","0","0","0","0","0","0","1","800000000000000000","0","0","-2"],
["1","1","0","0","0","12","10","10000","1","110","31","30","0","0","0","0","29","1","1","1200000000000000000","0","29","0"],
["1","1","0","0","100","12","10","10000","1","110","31","30","7","0","0","0","29","1","1","1200000000000000000","0","29","-93"],
["1","1","0","1","100","12","10","10000","1","110","31","29","7","1","0","1","28","1","1","1200000000000000000","1","28","-93"],
["1","1","0","2","1","12","10","10000","10000","110","12891","2889","2","2","0","2","2889","1","1","1200000000000000000","2","2888","1"],
["1","1","0","2","3000","12","10","10000","10000","110","12891","2889","110","2","0","2","2889","1","1","1200000000000000000","2","2888","-2890"]
]
}
170 changes: 169 additions & 1 deletion protocol/test/Sun.test.js
Expand Up @@ -31,6 +31,7 @@ describe('Sun', function () {
describe(testStr.concat((v)), function () {
testData = {}
columns.forEach((key, i) => testData[key] = tests[v][i])
console.log(testData);
before(async function () {
await this.season.resetState()
await this.pair.burnBeans(this.bean.address)
Expand All @@ -43,7 +44,7 @@ describe('Sun', function () {
await this.bean.mint(this.silo.address, this.testData.beansInSilo)
await this.bean.mint(this.pair.address, this.testData.beansInPool)
await this.silo.incrementDepositedBeansE(this.testData.beansInSilo)
await this.field.incrementTotalSoilEE(this.testData.soil)
await this.season.incrementTotalSoilE(this.testData.soil)
await this.silo.depositSiloAssetsE(userAddress, '1', '100000')
await this.field.incrementTotalPodsE((parseInt(this.testData.unripenedPods) + parseInt(this.testData.harvestablePods)).toString())
await this.field.incrementTotalHarvestableE(this.testData.harvestablePods)
Expand Down Expand Up @@ -83,3 +84,170 @@ describe('Sun', function () {
})
})
})


describe('Sun Soil', function () {

before(async function () {
[owner,user,user2] = await ethers.getSigners()
userAddress = user.address
user2Address = user2.address
const contracts = await deploy("Test", false, true)
ownerAddress = contracts.account
this.diamond = contracts.beanstalkDiamond
this.season = await ethers.getContractAt('MockSeasonFacet', this.diamond.address)
this.field = await ethers.getContractAt('MockFieldFacet', this.diamond.address)
this.silo = await ethers.getContractAt('MockSiloFacet', this.diamond.address)
this.bean = await ethers.getContractAt('MockToken', contracts.bean)
this.pair = await ethers.getContractAt('MockUniswapV2Pair', contracts.pair)
this.pegPair = await ethers.getContractAt('MockUniswapV2Pair', contracts.pegPair)
await this.bean.mint(this.silo.address, '100000')
await this.season.setYieldE('100')
});

this.beforeEach(async function () {
await this.season.decrementTotalSoilE(await this.field.totalSoil())
})

it("Properly sets the soil bounds", async function () {
expect(await this.season.minSoil('100')).to.be.equal('50')
expect(await this.season.maxSoil()).to.be.equal('25000')
});

// Increase Soil

describe("Increase above max Soil", async function () {
beforeEach(async function () {
await this.season.increaseSoilE('100000')
});

it("Properly sets the soil", async function () {
expect(await this.field.totalSoil()).to.be.equal('25000')
});
});

describe("Increase when already above above max Soil", async function () {
beforeEach(async function () {
await this.season.incrementTotalSoilE('100000')
await this.season.increaseSoilE('100000')
});

it("Properly sets the soil", async function () {
expect(await this.field.totalSoil()).to.be.equal('25000')
});
});

describe("Increase little Soil", async function () {
beforeEach(async function () {
await this.season.increaseSoilE('1')
});

it("Properly sets the soil", async function () {
expect(await this.field.totalSoil()).to.be.equal('1')
});
});

describe("Increase to normal Soil", async function () {
beforeEach(async function () {
await this.season.increaseSoilE('100')
});

it("Properly sets the soil", async function () {
expect(await this.field.totalSoil()).to.be.equal('100')
});
});

// Decrease Soil

describe("Decrease above max Soil", async function () {
beforeEach(async function () {
await this.season.incrementTotalSoilE('100000')
await this.season.decreaseSoilE('1', '100')
});

it("Properly sets the max soil", async function () {
expect(await this.field.totalSoil()).to.be.equal('25000')
});
});

describe("Decrease already below min Soil", async function () {
beforeEach(async function () {
await this.season.incrementTotalSoilE('2')
await this.season.decreaseSoilE('1', '100')
});

it("Properly sets the soil", async function () {
expect(await this.field.totalSoil()).to.be.equal('50')
});
});

describe("Decrease below min Soil", async function () {
beforeEach(async function () {
await this.season.incrementTotalSoilE('110')
await this.season.decreaseSoilE('100', '100')
});

it("Properly sets the soil", async function () {
expect(await this.field.totalSoil()).to.be.equal('50')
});
});

describe("Decrease to normal Soil", async function () {
beforeEach(async function () {
await this.season.incrementTotalSoilE('110')
await this.season.decreaseSoilE('10', '100')
});

it("Properly sets the soil", async function () {
expect(await this.field.totalSoil()).to.be.equal('100')
});
});

describe("Decrease below 0 Soil", async function () {
beforeEach(async function () {
await this.season.incrementTotalSoilE('100')
await this.season.decreaseSoilE('110', '10')
});

it("Properly sets the soil", async function () {
expect(await this.field.totalSoil()).to.be.equal('5')
});
});

describe("Decrease to 0 Soil", async function () {
beforeEach(async function () {
await this.season.incrementTotalSoilE('100')
await this.season.decreaseSoilE('110', '0')
});

it("Properly sets the soil", async function () {
expect(await this.field.totalSoil()).to.be.equal('0')
});
});

// Ensure soil bounds

// Decrease Soil

describe("When above max soil", async function () {
beforeEach(async function () {
await this.season.incrementTotalSoilE('100000')
await this.season.ensureSoilBoundsE()
});

it("Properly sets the max soil", async function () {
expect(await this.field.totalSoil()).to.be.equal('25000')
});
});

describe("When normal soil", async function () {
beforeEach(async function () {
await this.season.incrementTotalSoilE('2')
await this.season.ensureSoilBoundsE()
});

it("Properly sets the soil", async function () {
expect(await this.field.totalSoil()).to.be.equal('2')
});
});
});

0 comments on commit 160a367

Please sign in to comment.