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

Fork System #1042

Open
wants to merge 70 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
8ab4f74
skeleton of lineage system
funderbrker Jul 4, 2024
74bf9d0
fert out logic
funderbrker Jul 4, 2024
14f385f
migrate in fert
funderbrker Jul 7, 2024
2fef3f8
migrate out deposits logic
funderbrker Jul 7, 2024
6abcd16
fert migrate out patch
funderbrker Jul 7, 2024
9b2e80a
mow on deposit migration
funderbrker Jul 7, 2024
54991fa
migrate in deposits logic
funderbrker Jul 8, 2024
a2a22c3
compilation fixes
funderbrker Jul 8, 2024
2437b86
Merge branch 'test-3' into lineage-extension
funderbrker Jul 8, 2024
17bf0d1
shuffle init diamond, fix typos
funderbrker Jul 9, 2024
5cceabe
migration patches. executes
funderbrker Jul 9, 2024
59e7705
lineage migration test. wip.
funderbrker Jul 9, 2024
50b4358
migration grown stalk fix
funderbrker Jul 9, 2024
de9be79
update field to handle holes via Slashing
funderbrker Jul 15, 2024
9f7d9da
pod migration out
funderbrker Jul 15, 2024
40a643b
migrate in plots
funderbrker Jul 19, 2024
fafcd7b
readme update
funderbrker Jul 27, 2024
6d7a778
remove fn to add new sources
funderbrker Aug 12, 2024
84fca25
partial revert of contracts/beanstalk/init/InitalizeDiamond.sol
funderbrker Aug 12, 2024
14da241
rm old init file
funderbrker Aug 13, 2024
a1cb901
update deposits tests and supporting logic
funderbrker Aug 13, 2024
52c83ec
cleap up foundry test helpers
funderbrker Aug 13, 2024
67977a2
update + fix fert migration logic. add fert migration test
funderbrker Aug 13, 2024
8d34f2b
finalized plot migration design. patch implementation. add plot migra…
funderbrker Aug 13, 2024
b120426
dust cleanup
funderbrker Aug 13, 2024
3851ae9
test - migrate deposits to new
funderbrker Aug 15, 2024
5cafece
parent migrate out events. bug fixes.
funderbrker Aug 15, 2024
9f63558
refactor foundry tests to allow multiple beanstalks
funderbrker Aug 15, 2024
41c95dd
test alternative beanstalk constants
funderbrker Aug 15, 2024
441dc85
plots descendants to new instance, tests & fixes
funderbrker Aug 16, 2024
00a733b
rename migrate -> transmit
funderbrker Aug 16, 2024
332584a
--lineage--
funderbrker Aug 16, 2024
f6e6f12
fert transmit to new addr tests
funderbrker Aug 16, 2024
d27db02
placeholder functions for children
funderbrker Aug 16, 2024
c2494ae
refactor foundry tests inheritence of constants
funderbrker Aug 16, 2024
7f5bf18
remove unused invariant tests
funderbrker Aug 16, 2024
059a47f
transmit test constant fix
funderbrker Aug 16, 2024
e54daba
more fert transmission test checks
funderbrker Aug 17, 2024
9c59d5e
lp transmit to child test
funderbrker Aug 17, 2024
7b1c64a
source index check
funderbrker Aug 17, 2024
a77e3fb
push plot to back of child line if missed harvesting or deployment
funderbrker Aug 19, 2024
a1762f9
transmit tests for reverts and for combo transmit
funderbrker Aug 19, 2024
d0c15ff
fin
funderbrker Aug 19, 2024
77b1919
better setting and testing of source pods
funderbrker Aug 19, 2024
222f952
Merge branch 'beanstalk3-codehawks-remediations' of https://github.co…
funderbrker Aug 20, 2024
da4639a
Merge branch 'beanstalk3-codehawks-remediations' of https://github.co…
funderbrker Aug 20, 2024
5ef5ba8
fix bad merge
funderbrker Oct 16, 2024
b05c9da
return a sun test
funderbrker Oct 16, 2024
0e9f4ff
fix harvestableBean amt
funderbrker Oct 16, 2024
80d5579
fix merge
funderbrker Oct 16, 2024
3129031
track harvested in addition to processed
funderbrker Oct 16, 2024
0aa27e6
harvested and slashed getters
funderbrker Oct 16, 2024
9c6927e
Merge branch 'master' into transmission-merge-master-2
pizzaman1337 Oct 18, 2024
3d1be76
Test fixes wip
pizzaman1337 Oct 18, 2024
14a91d8
WIP test fixing
pizzaman1337 Oct 18, 2024
56cef6b
All tests passing but 1
pizzaman1337 Oct 21, 2024
be07d9d
All tests passing
pizzaman1337 Oct 21, 2024
b7562a2
prettier auto formatting changes
pizzaman1337 Oct 21, 2024
d025170
Update transmit in facet to accept bytes array for asset flexibility
pizzaman1337 Oct 21, 2024
acabd72
prettier auto formatting changes
pizzaman1337 Oct 21, 2024
acd0a45
Modify transmitOut to take asset bytes array
pizzaman1337 Oct 21, 2024
763102a
Update comment
pizzaman1337 Oct 21, 2024
50913a4
Merge branch 'transmission-bytes-array' of github.com:BeanstalkFarms/…
pizzaman1337 Oct 21, 2024
2e2a769
Test total harvested and total slashed
pizzaman1337 Oct 21, 2024
463740e
Don't allow transfer of germinating deposits
pizzaman1337 Oct 22, 2024
310496f
Update requires to asserts
pizzaman1337 Oct 23, 2024
0100ffb
Fix compiler warning
pizzaman1337 Oct 23, 2024
1d47631
Verify deposits that are still germinating after one season cannot be…
pizzaman1337 Oct 24, 2024
a83210c
Merge master into transmission branch (#1162)
Brean0 Oct 26, 2024
304fdf6
Update transmit in facet to accept bytes array for asset flexibility …
Brean0 Oct 26, 2024
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
4 changes: 4 additions & 0 deletions protocol/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,13 @@ _Note: The Beanstalk repo is a monorepo with induvidual projects inside it. See

1. Clone the repository and install dependencies

*If using Mac on Apple Silicon*
`/usr/sbin/softwareupdate --install-rosetta --agree-to-license`

```bash
git clone https://github.com/BeanstalkFarms/Beanstalk
cd Beanstalk/protocol
export YARN_IGNORE_NODE=1
yarn
```

Expand Down
13 changes: 13 additions & 0 deletions protocol/contracts/C.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "./interfaces/IFertilizer.sol";
import "./interfaces/IProxyAdmin.sol";
import "./libraries/Decimal.sol";
import "./interfaces/IPipeline.sol";
import {AppStorage, LibAppStorage} from "contracts/libraries/LibAppStorage.sol";

/**
* @title C
Expand Down Expand Up @@ -56,6 +57,13 @@ library C {
// Special index to indicate the data to copy is the operator address.
uint80 internal constant OPERATOR_COPY_INDEX = type(uint80).max - 1;

//////////////////// Fork ////////////////////
uint256 internal constant DEST_FIELD = 1;

function getSeasonPeriod() internal pure returns (uint256) {
return CURRENT_SEASON_PERIOD;
}

function getRootsBase() internal pure returns (uint256) {
return ROOTS_BASE;
}
Expand All @@ -79,4 +87,9 @@ library C {
function pipeline() internal pure returns (IPipeline) {
return IPipeline(PIPELINE);
}

function bean() internal view returns (address) {
AppStorage storage s = LibAppStorage.diamondStorage();
return s.sys.tokens.bean;
}
}
40 changes: 40 additions & 0 deletions protocol/contracts/beanstalk/ForkSystem/TransmitInFacet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* SPDX-License-Identifier: MIT
**/

pragma solidity ^0.8.20;

import {AppStorage} from "contracts/beanstalk/storage/AppStorage.sol";
import {Invariable} from "contracts/beanstalk/Invariable.sol";
import {LibTransmitIn} from "contracts/libraries/ForkSystem/LibTransmitIn.sol";

/**
* @title TransmitInFacet
* @author funderbrker
* @notice Destination instance logic for receiving transmitted assets from another version.
* @notice Destination has knowledge of valid Sources and their configurations at deployment time.
**/
contract TransmitInFacet is Invariable {
AppStorage internal s;

/**
* @notice Process the inbound migration locally.
* @dev Reverts if failure to mint assets or handle migration in.
* @dev Arguments are bytes because different sources may use different encodings.
*/
function transmitIn(
address user,
bytes[][] calldata assets,
bytes calldata //data
) external fundsSafu {
require(s.sys.supportedSourceForks[msg.sender], "Unsupported source");
require(assets.length >= 2, "Insufficient data provided");

LibTransmitIn.transmitInDeposits(user, assets[0]);
LibTransmitIn.transmitInPlots(user, assets[1]);
if (assets.length > 2) {
LibTransmitIn.transmitInFertilizer(user, assets[2]);
}
// additional assets can be processed here in the future
}
}
54 changes: 54 additions & 0 deletions protocol/contracts/beanstalk/ForkSystem/TransmitOutFacet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* SPDX-License-Identifier: MIT
**/

pragma solidity ^0.8.20;

import {Invariable} from "contracts/beanstalk/Invariable.sol";
import {LibTractor} from "contracts/libraries/LibTractor.sol";
import {LibTransmitOut} from "contracts/libraries/ForkSystem/LibTransmitOut.sol";
import {ITransmitInFacet} from "contracts/interfaces/ITransmitInFacet.sol";

/**
* @title TransmitOutFacet
* @author funderbrker
* @notice Source instance logic for migrating assets to new version.
* @notice Source instance has no knowledge of possible Destinations or their configurations.
**/
contract TransmitOutFacet is Invariable {
/**
* @notice Process the outbound migration and transfer necessary assets to destination.
* @dev Reverts if failure to burn assets or destination fails.
* @param assets Contains abi encoded deposits, plots, and fertilizer.
* @param data Currently unused but remains available for paramaters such as minimum output requirements.
*/
function transmitOut(
address destination,
bytes[] calldata assets,
bytes calldata data
) external fundsSafu {
require(assets.length >= 3, "Missing asset data");

bytes[] memory deposits = LibTransmitOut.transmitOutDeposits(
LibTractor._user(),
destination,
abi.decode(assets[0], (LibTransmitOut.SourceDeposit[]))
);
bytes[] memory plots = LibTransmitOut.transmitOutPlots(
LibTractor._user(),
abi.decode(assets[1], (LibTransmitOut.SourcePlot[]))
);
bytes[] memory fertilizer = LibTransmitOut.transmitOutFertilizer(
LibTractor._user(),
abi.decode(assets[2], (LibTransmitOut.SourceFertilizer[]))
);

bytes[][] memory processedAssets = new bytes[][](3);
processedAssets[0] = deposits;
processedAssets[1] = plots;
processedAssets[2] = fertilizer;

// Reverts if Destination fails to handle transmitted assets.
ITransmitInFacet(destination).transmitIn(LibTractor._user(), processedAssets, data);
}
}
2 changes: 1 addition & 1 deletion protocol/contracts/beanstalk/Invariable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ abstract contract Invariable {
s.sys.silo.unripeSettings[s.sys.tokens.urBean].balanceOfUnderlying + // unchopped underlying beans
s.sys.orderLockedBeans;
for (uint256 j; j < s.sys.fieldCount; j++) {
entitlements[i] += (s.sys.fields[j].harvestable - s.sys.fields[j].harvested); // unharvested harvestable beans
entitlements[i] += (s.sys.fields[j].harvestable - s.sys.fields[j].processed); // unharvested harvestable beans
}
} else if (tokens[i] == LibUnripe._getUnderlyingToken(s.sys.tokens.urLp)) {
entitlements[i] += s.sys.silo.unripeSettings[s.sys.tokens.urLp].balanceOfUnderlying;
Expand Down
8 changes: 1 addition & 7 deletions protocol/contracts/beanstalk/barn/FertilizerFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,7 @@ contract FertilizerFacet is Invariable, ReentrancyGuard {
uint256[] calldata ids,
LibTransfer.To mode
) external payable fundsSafu noSupplyChange oneOutFlow(s.sys.tokens.bean) nonReentrant {
uint256 amount = IFertilizer(s.sys.tokens.fertilizer).beanstalkUpdate(
LibTractor._user(),
ids,
s.sys.fert.bpf
);
s.sys.fert.fertilizedPaidIndex += amount;
LibTransfer.sendToken(BeanstalkERC20(s.sys.tokens.bean), amount, LibTractor._user(), mode);
LibFertilizer.claimFertilized(ids, mode);
}

/**
Expand Down
80 changes: 54 additions & 26 deletions protocol/contracts/beanstalk/field/FieldFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {ReentrancyGuard} from "../ReentrancyGuard.sol";
import {Invariable} from "contracts/beanstalk/Invariable.sol";
import {LibDiamond} from "contracts/libraries/LibDiamond.sol";
import {LibMarket} from "contracts/libraries/LibMarket.sol";
import {LibField} from "contracts/libraries/LibField.sol";
import {BeanstalkERC20} from "contracts/tokens/ERC20/BeanstalkERC20.sol";

interface IBeanstalk {
Expand Down Expand Up @@ -145,6 +146,7 @@ contract FieldFacet is Invariable, ReentrancyGuard {
*
* Pods are "burned" when the corresponding Plot is deleted from
* `s.accts[account].fields[fieldId].plots`.
* @dev If Plot has been Slashed, burn the Plot. Anyone can burn Slashed Plots.
*/
function harvest(
uint256 fieldId,
Expand All @@ -163,6 +165,7 @@ contract FieldFacet is Invariable, ReentrancyGuard {
/**
* @dev Ensure that each Plot is at least partially harvestable, burn the Plot,
* update the total harvested, and emit a {Harvest} event.
* @dev If Plot has been Slashed, burn the Plot.
*/
function _harvest(
uint256 fieldId,
Expand All @@ -172,48 +175,55 @@ contract FieldFacet is Invariable, ReentrancyGuard {
// The Plot is partially harvestable if its index is less than
// the current harvestable index.
require(plots[i] < s.sys.fields[fieldId].harvestable, "Field: Plot not Harvestable");
uint256 harvested = _harvestPlot(LibTractor._user(), fieldId, plots[i]);
beansHarvested += harvested;
beansHarvested += _harvestPlot(LibTractor._user(), fieldId, plots[i]);
}
s.sys.fields[fieldId].harvested += beansHarvested;
s.sys.fields[fieldId].totalHarvested += beansHarvested;
emit Harvest(LibTractor._user(), fieldId, plots, beansHarvested);
}

/**
* @dev Check if a Plot is at least partially Harvestable; calculate how many
* Pods are Harvestable, create a new Plot if necessary.
* @dev If Plot has been Slashed, burn the Plot.
*/
function _harvestPlot(
address account,
uint256 fieldId,
uint256 index
) private returns (uint256 harvestablePods) {
// Check that `account` holds this Plot.
uint256 pods = s.accts[account].fields[fieldId].plots[index];
require(pods > 0, "Field: no plot");
) private returns (uint256 beansHarvested) {
// If Plot held by null address, it has been Slashed. Burn Plot.
if (s.accts[address(0)].fields[fieldId].plots[index] > 0) {
account = address(0);
}
uint256 plotPods = s.accts[account].fields[fieldId].plots[index];
require(plotPods > 0, "Field: no plot");

// Calculate how many Pods are harvestable.
// The upstream _harvest function checks that at least some Pods
// are harvestable.
harvestablePods = s.sys.fields[fieldId].harvestable.sub(index);
uint256 harvestablePods = s.sys.fields[fieldId].harvestable.sub(index);

LibMarket._cancelPodListing(account, fieldId, index);

LibMarket._cancelPodListing(LibTractor._user(), fieldId, index);
LibField.deletePlot(account, fieldId, index);

delete s.accts[account].fields[fieldId].plots[index];
LibDibbler.removePlotIndexFromAccount(account, fieldId, index);
beansHarvested = plotPods <= harvestablePods ? plotPods : harvestablePods;
s.sys.fields[fieldId].processed += beansHarvested;

// If burning a Slashed Plot, amount harvestable does not change.
if (account == address(0)) {
s.sys.fields[fieldId].harvestable += beansHarvested;
return 0;
}

// If the entire Plot was harvested, exit.
if (harvestablePods >= pods) {
return pods;
if (beansHarvested == plotPods) {
return beansHarvested;
}

// Create a new Plot with remaining Pods.
uint256 newIndex = index.add(harvestablePods);
s.accts[account].fields[fieldId].plots[newIndex] = pods.sub(harvestablePods);
s.accts[account].fields[fieldId].plotIndexes.push(newIndex);
s.accts[account].fields[fieldId].piIndex[newIndex] =
s.accts[account].fields[fieldId].plotIndexes.length -
1;
uint256 newIndex = index.add(beansHarvested);
LibField.createPlot(account, fieldId, newIndex, plotPods.sub(beansHarvested));
}

//////////////////// CONFIG /////////////////////
Expand Down Expand Up @@ -270,42 +280,60 @@ contract FieldFacet is Invariable, ReentrancyGuard {

/**
* @notice Returns the number of outstanding Pods. Includes Pods that are
* currently Harvestable but have not yet been Harvested.
* currently Harvestable or Burnable but have not yet been Harvested.
* @param fieldId The index of the Field to query.
*/
function totalPods(uint256 fieldId) public view returns (uint256) {
return s.sys.fields[fieldId].pods - s.sys.fields[fieldId].harvested;
return s.sys.fields[fieldId].pods - s.sys.fields[fieldId].processed;
}

/**
* @notice Returns the number of Pods that have ever been Harvested.
* @notice Returns the number of Pods that have ever been Harvested or Burned.
* @param fieldId The index of the Field to query.
*/
function totalProcessed(uint256 fieldId) public view returns (uint256) {
funderbrker marked this conversation as resolved.
Show resolved Hide resolved
return s.sys.fields[fieldId].processed;
}

/**
* @notice Returns the number of Pods that have been Harvested. Not including Slashed plots.
* @param fieldId The index of the Field to query.
*/
function totalHarvested(uint256 fieldId) public view returns (uint256) {
return s.sys.fields[fieldId].harvested;
return s.sys.fields[fieldId].totalHarvested;
}

/**
* @notice Returns the number of Pods that have been Slashed.
* @param fieldId The index of the Field to query.
*/
function totalSlashed(uint256 fieldId) public view returns (uint256) {
return s.sys.fields[fieldId].processed - s.sys.fields[fieldId].totalHarvested;
}

/**
* @notice Returns the number of Pods that are currently Harvestable but
* have not yet been Harvested.
* @dev This is the number of Pods that Beanstalk is prepared to pay back,
* but that haven’t yet been claimed via the `harvest()` function.
* @dev Cannot use this number as an index, as there is no accounting for Slashed plots.
* @param fieldId The index of the Field to query.
*/
function totalHarvestable(uint256 fieldId) public view returns (uint256) {
return s.sys.fields[fieldId].harvestable - s.sys.fields[fieldId].harvested;
return s.sys.fields[fieldId].harvestable - s.sys.fields[fieldId].processed;
}

/**
* @notice Returns the number of Pods that are currently Harvestable for the active Field.
* @dev Cannot use this number as an index, as there is no accounting for Slashed plots.
*/
function totalHarvestableForActiveField() public view returns (uint256) {
return
s.sys.fields[s.sys.activeField].harvestable - s.sys.fields[s.sys.activeField].harvested;
return totalHarvestable(s.sys.activeField);
}

/**
* @notice Returns the number of Pods that are not yet Harvestable. Also known as the Pod Line.
* @dev Includes Slashed Pods.
* @param fieldId The index of the Field to query.
*/
function totalUnharvestable(uint256 fieldId) public view returns (uint256) {
Expand Down
28 changes: 28 additions & 0 deletions protocol/contracts/beanstalk/init/InitDiamond.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
SPDX-License-Identifier: MIT
*/

pragma solidity ^0.8.20;

import {InitializeDiamond} from "contracts/beanstalk/init/InitializeDiamond.sol";
import {C} from "contracts/C.sol";
import {IBean} from "contracts/interfaces/IBean.sol";
import {LibConstant} from "test/foundry/utils/LibConstant.sol";

/**
* @author Publius, Brean
* @title InitDiamond
* @notice InitDiamond initializes the Beanstalk Diamond.
* A new bean token and bean:TOKEN well are deployed.
*
**/
contract InitDiamond is InitializeDiamond {
// initial reward for deploying beanstalk.
uint256 constant INIT_SUPPLY = 100e6;

function init() external {
initializeDiamond(LibConstant.BEAN, LibConstant.BEAN_ETH_WELL);

IBean(s.sys.tokens.bean).mint(msg.sender, INIT_SUPPLY);
}
}
6 changes: 4 additions & 2 deletions protocol/contracts/beanstalk/init/InitDistribution.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ contract InitDistribution {
AppStorage internal s;
IBeanstalk beanstalk;

uint256 internal constant ACTIVE_FIELD = 0;

function init(address shipmentPlanner) external {
beanstalk = IBeanstalk(address(this));
require(
Expand All @@ -48,7 +50,7 @@ contract InitDistribution {
shipmentPlanner,
ShipmentPlanner.getFieldPlan.selector,
ShipmentRecipient.FIELD,
abi.encode(uint256(0))
abi.encode(ACTIVE_FIELD)
);

shipmentRoutes[2] = ShipmentRoute(
Expand All @@ -60,7 +62,7 @@ contract InitDistribution {

beanstalk.setShipmentRoutes(shipmentRoutes);
beanstalk.addField();
beanstalk.setActiveField(0, 1);
beanstalk.setActiveField(ACTIVE_FIELD, 1);

// TODO: Initialize Field values from priors.
}
Expand Down
Loading