├── IBeanstalk.sol
├── IDelegation.sol
├── README.md
├── Root.sol
├── deploy.js
├── proposals.md
└── root.pdf
/IBeanstalk.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.17;
3 | pragma experimental ABIEncoderV2;
4 |
5 | import "@openzeppelin/contracts-upgradeable-8/token/ERC20/IERC20Upgradeable.sol";
6 |
7 | enum ConvertKind {
8 | BEANS_TO_CURVE_LP,
9 | CURVE_LP_TO_BEANS,
10 | UNRIPE_BEANS_TO_UNRIPE_LP,
11 | UNRIPE_LP_TO_UNRIPE_BEANS,
12 | LAMBDA_LAMBDA
13 | }
14 |
15 | enum From {
16 | EXTERNAL,
17 | INTERNAL,
18 | EXTERNAL_INTERNAL,
19 | INTERNAL_TOLERANT
20 | }
21 | enum To {
22 | EXTERNAL,
23 | INTERNAL
24 | }
25 |
26 | interface IBeanstalk {
27 | function balanceOfSeeds(address account) external view returns (uint256);
28 |
29 | function balanceOfStalk(address account) external view returns (uint256);
30 |
31 | function transferDeposits(
32 | address sender,
33 | address recipient,
34 | address token,
35 | uint32[] calldata seasons,
36 | uint256[] calldata amounts
37 | ) external payable returns (uint256[] memory bdvs);
38 |
39 | function permitDeposit(
40 | address owner,
41 | address spender,
42 | address token,
43 | uint256 value,
44 | uint256 deadline,
45 | uint8 v,
46 | bytes32 r,
47 | bytes32 s
48 | ) external payable;
49 |
50 | function permitDeposits(
51 | address owner,
52 | address spender,
53 | address[] calldata tokens,
54 | uint256[] calldata values,
55 | uint256 deadline,
56 | uint8 v,
57 | bytes32 r,
58 | bytes32 s
59 | ) external payable;
60 |
61 | function plant() external payable returns (uint256);
62 |
63 | function update(address account) external payable;
64 |
65 | function transferInternalTokenFrom(
66 | IERC20Upgradeable token,
67 | address from,
68 | address to,
69 | uint256 amount,
70 | To toMode
71 | ) external payable;
72 |
73 | function transferToken(
74 | IERC20Upgradeable token,
75 | address recipient,
76 | uint256 amount,
77 | From fromMode,
78 | To toMode
79 | ) external payable;
80 |
81 | function permitToken(
82 | address owner,
83 | address spender,
84 | address token,
85 | uint256 value,
86 | uint256 deadline,
87 | uint8 v,
88 | bytes32 r,
89 | bytes32 s
90 | ) external payable;
91 |
92 | function convert(
93 | bytes calldata convertData,
94 | uint32[] memory crates,
95 | uint256[] memory amounts
96 | ) external payable returns (uint32 toSeason, uint256 fromAmount, uint256 toAmount, uint256 fromBdv, uint256 toBdv);
97 |
98 | function getDeposit(
99 | address account,
100 | address token,
101 | uint32 season
102 | ) external view returns (uint256, uint256);
103 | }
104 |
--------------------------------------------------------------------------------
/IDelegation.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.17;
3 | pragma experimental ABIEncoderV2;
4 |
5 | interface IDelegation{
6 | function clearDelegate(bytes32 _id) external;
7 | function setDelegate(bytes32 _id, address _delegate) external;
8 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Root
4 |
5 | A fungible wrapper for Beanstalk Silo Deposits: [roottoken.org](https://roottoken.org)
6 |
7 | Code Version: `1.0.1`
8 | Whitepaper Version: `1.0.2`
9 |
10 | Root is an Ethereum-native permissionless wrapper that implements the ERC-20 token Standard to
11 | create fungibility and composability for Beanstalk Silo Deposits.
12 |
13 | Root is deployed at [0x77700005BEA4DE0A78b956517f099260C2CA9a26](https://etherscan.io/address/0x77700005BEA4DE0A78b956517f099260C2CA9a26) (full list of contract addresses [here](https://docs.roottoken.org/resources/contracts)).
14 |
15 | ## Documentation
16 |
17 | * [Root Whitepaper](https://roottoken.org/root.pdf) ([Version History](https://github.com/RootToken/Root-Whitepaper/tree/main/version-history))
18 | * [Root Docs](https://docs.roottoken.org/)
19 |
20 | ## Audits
21 |
22 | * [Root Audits](https://github.com/RootToken/Root-Audits)
23 |
24 | ## Bug Bounty Program
25 |
26 | The Root token contract is considered in-scope of the Beanstalk Immunefi Bug Bounty Program.
27 |
28 | You can find the program and submit bug reports [here](https://immunefi.com/bounty/beanstalk).
29 |
--------------------------------------------------------------------------------
/Root.sol:
--------------------------------------------------------------------------------
1 | /**
2 | * SPDX-License-Identifier: MIT
3 | **/
4 |
5 | pragma solidity ^0.8.17;
6 | pragma experimental ABIEncoderV2;
7 |
8 | import "@openzeppelin/contracts-upgradeable-8/token/ERC20/extensions/draft-ERC20PermitUpgradeable.sol";
9 | import "@openzeppelin/contracts-upgradeable-8/token/ERC20/IERC20Upgradeable.sol";
10 | import "@openzeppelin/contracts-upgradeable-8/proxy/utils/UUPSUpgradeable.sol";
11 | import "@openzeppelin/contracts-upgradeable-8/access/OwnableUpgradeable.sol";
12 | import "@openzeppelin/contracts-upgradeable-8/utils/math/MathUpgradeable.sol";
13 |
14 | import "./interfaces/IBeanstalk.sol";
15 | import "./interfaces/IDelegation.sol";
16 |
17 | /// @notice Silo deposit transfer
18 | /// @param token a whitelisted silo token address
19 | /// @param seasons a list of deposit season
20 | /// @param amounts a list of deposit amount
21 | struct DepositTransfer {
22 | address token;
23 | uint32[] seasons;
24 | uint256[] amounts;
25 | }
26 |
27 | /// @title Root FDBDV
28 | /// @author 0xkokonut, mistermanifold, publius
29 | contract Root is UUPSUpgradeable, ERC20PermitUpgradeable, OwnableUpgradeable {
30 | using MathUpgradeable for uint256;
31 |
32 | /// @notice This event will emit after the user mint Root token
33 | /// @param account minting user
34 | /// @param deposits silo deposits transferred into contract
35 | /// @param bdv total bdv used for deposits
36 | /// @param stalk total stalk for deposits
37 | /// @param seeds total seeds for deposits
38 | /// @param shares total shares minted
39 | event Mint(
40 | address indexed account,
41 | DepositTransfer[] deposits,
42 | uint256 bdv,
43 | uint256 stalk,
44 | uint256 seeds,
45 | uint256 shares
46 | );
47 |
48 | /// @notice This event will emit after the user redeem Root token
49 | /// @param account redeeming user
50 | /// @param deposits silo deposits transferred to the user
51 | /// @param bdv total bdv for deposits
52 | /// @param stalk total stalk for deposits
53 | /// @param seeds total seeds for deposits
54 | /// @param shares total shares burned
55 | event Redeem(
56 | address indexed account,
57 | DepositTransfer[] deposits,
58 | uint256 bdv,
59 | uint256 stalk,
60 | uint256 seeds,
61 | uint256 shares
62 | );
63 |
64 | /// @notice This event will emit after the owner whitelist a silo token
65 | /// @param token address of a silo token
66 | event AddWhitelistToken(address indexed token);
67 |
68 | /// @notice This event will emit after the owner remove a silo token from whitelist
69 | /// @param token address of a silo token
70 | event RemoveWhitelistToken(address indexed token);
71 |
72 | /// @notice Beanstalk address
73 | address public constant BEANSTALK_ADDRESS =
74 | 0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5;
75 |
76 | /// @notice Decimal precision of this contract token
77 | uint256 private constant PRECISION = 1e18;
78 |
79 | /// @notice A mapping of whitelisted token
80 | /// @return whitelisted mapping of all whitelisted token
81 | mapping(address => bool) public whitelisted;
82 |
83 | /// @notice The total bdv of the silo deposits in the contract
84 | /// @dev only get updated on mint/earn/redeem
85 | /// @return underlyingBdv total bdv of the silo deposit(s) in the contract
86 | uint256 public underlyingBdv;
87 |
88 | /// @notice Nominated candidate to be the owner of the contract
89 | /// @dev The nominated candidate need to call the claimOwnership function
90 | /// @return ownerCandidate The nomindated candidate to become the new owner of the contract
91 | address public ownerCandidate;
92 |
93 | /// @custom:oz-upgrades-unsafe-allow constructor
94 | constructor() {
95 | _disableInitializers();
96 | }
97 |
98 | /// @notice Initialize the contract
99 | /// @param name The name of this ERC-20 contract
100 | /// @param symbol The symbol of this ERC-20 contract
101 | function initialize(string calldata name, string calldata symbol)
102 | external
103 | initializer
104 | {
105 | __ERC20_init(name, symbol);
106 | __ERC20Permit_init(name);
107 | __Ownable_init();
108 | }
109 |
110 | /// @notice Renounce ownership of contract
111 | /// @dev Not possible with this smart contract
112 | function renounceOwnership() public virtual override onlyOwner {
113 | revert("Ownable: Can't renounceOwnership here");
114 | }
115 |
116 | /// @notice Nominate a candidate to become the new owner of the contract
117 | /// @dev The nominated candidate need to call claimOwnership function
118 | function transferOwnership(address newOwner)
119 | public
120 | virtual
121 | override
122 | onlyOwner
123 | {
124 | require(
125 | newOwner != address(0),
126 | "Ownable: Non-zero owner address required"
127 | );
128 | ownerCandidate = newOwner;
129 | }
130 |
131 | /// @notice Nominated candidate claim ownership
132 | function claimOwnership() external {
133 | require(
134 | msg.sender == ownerCandidate,
135 | "Ownable: sender must be ownerCandidate to accept ownership"
136 | );
137 | _transferOwnership(ownerCandidate);
138 | ownerCandidate = address(0);
139 | }
140 |
141 | function _authorizeUpgrade(address) internal override onlyOwner {}
142 |
143 | /// @notice Owner whitelist a silo token
144 | /// @param token Silo token to be add to the whitelist
145 | function addWhitelistToken(address token) external onlyOwner {
146 | require(token != address(0), "Non-zero token address required");
147 | whitelisted[token] = true;
148 | emit AddWhitelistToken(token);
149 | }
150 |
151 | /// @notice Remove silo token from the whitelist
152 | /// @param token Silo token to be remove from the whitelist
153 | function removeWhitelistToken(address token) external onlyOwner {
154 | require(token != address(0), "Non-zero token address required");
155 | delete whitelisted[token];
156 | emit RemoveWhitelistToken(token);
157 | }
158 |
159 | /// @notice Delegate snapshot voting power
160 | /// @param _delegateContract snapshot delegate contract
161 | /// @param _delegate account to delegate voting power
162 | /// @param _snapshotId snapshot space key
163 | function setDelegate(
164 | address _delegateContract,
165 | address _delegate,
166 | bytes32 _snapshotId
167 | ) external onlyOwner {
168 | require(
169 | _delegateContract != address(0),
170 | "Non-zero delegate address required"
171 | );
172 | if (_delegate == address(0)) {
173 | IDelegation(_delegateContract).clearDelegate(_snapshotId);
174 | } else {
175 | IDelegation(_delegateContract).setDelegate(_snapshotId, _delegate);
176 | }
177 | }
178 |
179 | /// @notice Update bdv of a silo deposit and underlyingBdv
180 | /// @dev Will revert if bdv doesn't increase
181 | function updateBdv(address token, uint32 season) external {
182 | _updateBdv(token, season);
183 | }
184 |
185 | /// @notice Update Bdv of multiple silo deposits and underlyingBdv
186 | /// @dev Will revert if the bdv of the deposits doesn't increase
187 | function updateBdvs(address[] calldata tokens, uint32[] calldata seasons)
188 | external
189 | {
190 | for (uint256 i; i < tokens.length; ++i) {
191 | _updateBdv(tokens[i], seasons[i]);
192 | }
193 | }
194 |
195 | /// @notice Update silo deposit bdv and underlyingBdv
196 | /// @dev Will revert if the BDV doesn't increase
197 | function _updateBdv(address token, uint32 season) internal {
198 | require(token != address(0), "Bdv: Non-zero token address required");
199 | (uint256 amount, ) = IBeanstalk(BEANSTALK_ADDRESS).getDeposit(
200 | address(this),
201 | token,
202 | season
203 | );
204 | uint32[] memory seasons = new uint32[](1);
205 | seasons[0] = season;
206 | uint256[] memory amounts = new uint256[](1);
207 | amounts[0] = amount;
208 | (, , , uint256 fromBdv, uint256 toBdv) = IBeanstalk(BEANSTALK_ADDRESS)
209 | .convert(
210 | abi.encode(ConvertKind.LAMBDA_LAMBDA, amount, token),
211 | seasons,
212 | amounts
213 | );
214 | underlyingBdv += toBdv - fromBdv;
215 | }
216 |
217 | /// @notice Return the ratio of underlyingBdv per ROOT token
218 | function bdvPerRoot() external view returns (uint256) {
219 | return (underlyingBdv * PRECISION) / totalSupply();
220 | }
221 |
222 | /// @notice Call plant function on Beanstalk
223 | /// @dev Anyone can call this function on behalf of the contract
224 | function earn() external {
225 | uint256 beans = IBeanstalk(BEANSTALK_ADDRESS).plant();
226 | underlyingBdv += beans;
227 | }
228 |
229 | /// @dev return the min value of the three input values
230 | function _min(
231 | uint256 num1,
232 | uint256 num2,
233 | uint256 num3
234 | ) internal pure returns (uint256) {
235 | num1 = MathUpgradeable.min(num1, num2);
236 | return MathUpgradeable.min(num1, num3);
237 | }
238 |
239 | /// @dev return the max value of the three input values
240 | function _max(
241 | uint256 num1,
242 | uint256 num2,
243 | uint256 num3
244 | ) internal pure returns (uint256) {
245 | num1 = MathUpgradeable.max(num1, num2);
246 | return MathUpgradeable.max(num1, num3);
247 | }
248 |
249 | /// @notice Mint ROOT token using silo deposit(s) with a silo deposit permit
250 | /// @dev Make sure any token inside of DepositTransfer have sufficient approval either via permit in the arg or existing approval
251 | /// @param depositTransfers silo deposit(s) to mint ROOT token
252 | /// @param mode Transfer ROOT token to
253 | /// @param minRootsOut Minimum number of ROOT token to receive
254 | /// @param token a silo deposit token address
255 | /// @param value a silo deposit amount
256 | /// @param deadline permit expiration
257 | /// @param v permit signature
258 | /// @param r permit signature
259 | /// @param s permit signature
260 | function mintWithTokenPermit(
261 | DepositTransfer[] calldata depositTransfers,
262 | To mode,
263 | uint256 minRootsOut,
264 | address token,
265 | uint256 value,
266 | uint256 deadline,
267 | uint8 v,
268 | bytes32 r,
269 | bytes32 s
270 | ) external virtual returns (uint256) {
271 | IBeanstalk(BEANSTALK_ADDRESS).permitDeposit(
272 | msg.sender,
273 | address(this),
274 | token,
275 | value,
276 | deadline,
277 | v,
278 | r,
279 | s
280 | );
281 |
282 | return _transferAndMint(depositTransfers, mode, minRootsOut);
283 | }
284 |
285 | /// @notice Mint ROOT token using silo deposit(s) with silo deposit tokens and values permit
286 | /// @param depositTransfers silo deposit(s) to mint ROOT token
287 | /// @param mode Transfer ROOT token to
288 | /// @param minRootsOut Minimum number of ROOT token to receive
289 | /// @param tokens a list of silo deposit token address
290 | /// @param values a list of silo deposit amount
291 | /// @param deadline permit expiration
292 | /// @param v permit signature
293 | /// @param r permit signature
294 | /// @param s permit signature
295 | function mintWithTokensPermit(
296 | DepositTransfer[] calldata depositTransfers,
297 | To mode,
298 | uint256 minRootsOut,
299 | address[] calldata tokens,
300 | uint256[] calldata values,
301 | uint256 deadline,
302 | uint8 v,
303 | bytes32 r,
304 | bytes32 s
305 | ) external virtual returns (uint256) {
306 | IBeanstalk(BEANSTALK_ADDRESS).permitDeposits(
307 | msg.sender,
308 | address(this),
309 | tokens,
310 | values,
311 | deadline,
312 | v,
313 | r,
314 | s
315 | );
316 |
317 | return _transferAndMint(depositTransfers, mode, minRootsOut);
318 | }
319 |
320 | /// @notice Mint ROOT token using silo deposit(s)
321 | /// @param depositTransfers silo deposit(s) to mint ROOT token
322 | /// @param mode Transfer ROOT token to
323 | /// @param minRootsOut Minimum number of ROOT token to receive
324 | function mint(
325 | DepositTransfer[] calldata depositTransfers,
326 | To mode,
327 | uint256 minRootsOut
328 | ) external virtual returns (uint256) {
329 | return _transferAndMint(depositTransfers, mode, minRootsOut);
330 | }
331 |
332 | /// @notice Redeem ROOT token for silo deposit(s) with farm balance permit
333 | /// @param depositTransfers silo deposit(s) receive
334 | /// @param mode Burn ROOT token from
335 | /// @param maxRootsIn Maximum number of ROOT token to burn
336 | /// @param token ROOT address
337 | /// @param value amount of ROOT approved
338 | /// @param deadline permit expiration
339 | /// @param v permit signature
340 | /// @param r permit signature
341 | /// @param s permit signature
342 | function redeemWithFarmBalancePermit(
343 | DepositTransfer[] calldata depositTransfers,
344 | From mode,
345 | uint256 maxRootsIn,
346 | address token,
347 | uint256 value,
348 | uint256 deadline,
349 | uint8 v,
350 | bytes32 r,
351 | bytes32 s
352 | ) external virtual returns (uint256) {
353 | IBeanstalk(BEANSTALK_ADDRESS).permitToken(
354 | msg.sender,
355 | address(this),
356 | token,
357 | value,
358 | deadline,
359 | v,
360 | r,
361 | s
362 | );
363 | return _transferAndRedeem(depositTransfers, mode, maxRootsIn);
364 | }
365 |
366 | /// @notice Redeem ROOT token for silo deposit(s)
367 | /// @param depositTransfers silo deposit(s) receive
368 | /// @param mode Burn ROOT token from
369 | /// @param maxRootsIn Maximum number of ROOT token to burn
370 | function redeem(
371 | DepositTransfer[] calldata depositTransfers,
372 | From mode,
373 | uint256 maxRootsIn
374 | ) external virtual returns (uint256) {
375 | return _transferAndRedeem(depositTransfers, mode, maxRootsIn);
376 | }
377 |
378 | /// @notice Burn ROOT token to exchange for silo deposit(s)
379 | function _transferAndRedeem(
380 | DepositTransfer[] calldata depositTransfers,
381 | From mode,
382 | uint256 maxRootsIn
383 | ) internal returns (uint256) {
384 | (
385 | uint256 shares,
386 | uint256 bdv,
387 | uint256 stalk,
388 | uint256 seeds
389 | ) = _transferDeposits(depositTransfers, false);
390 |
391 | require(
392 | shares <= maxRootsIn,
393 | "Redeem: shares is greater than maxRootsIn"
394 | );
395 |
396 | // Default mode is EXTERNAL
397 | address burnAddress = msg.sender;
398 | // Transfer token from beanstalk internal to this contract and burn
399 | if (mode == From.INTERNAL) {
400 | burnAddress = address(this);
401 | IBeanstalk(BEANSTALK_ADDRESS).transferInternalTokenFrom(
402 | this,
403 | msg.sender,
404 | burnAddress,
405 | shares,
406 | To.EXTERNAL
407 | );
408 | }
409 | _burn(burnAddress, shares);
410 | emit Redeem(msg.sender, depositTransfers, bdv, stalk, seeds, shares);
411 | return shares;
412 | }
413 |
414 | /// @notice Transfer silo deposit(s) to exchange ROOT token
415 | function _transferAndMint(
416 | DepositTransfer[] calldata depositTransfers,
417 | To mode,
418 | uint256 minRootsOut
419 | ) internal returns (uint256) {
420 | (
421 | uint256 shares,
422 | uint256 bdv,
423 | uint256 stalk,
424 | uint256 seeds
425 | ) = _transferDeposits(depositTransfers, true);
426 |
427 | require(shares >= minRootsOut, "Mint: shares is less than minRootsOut");
428 |
429 | // Transfer mint tokens to beanstalk internal balance
430 | if (mode == To.INTERNAL) {
431 | _mint(address(this), shares);
432 | _approve(address(this), BEANSTALK_ADDRESS, shares);
433 | IBeanstalk(BEANSTALK_ADDRESS).transferToken(
434 | this,
435 | msg.sender,
436 | shares,
437 | From.EXTERNAL,
438 | To.INTERNAL
439 | );
440 | } else if (mode == To.EXTERNAL) {
441 | _mint(msg.sender, shares);
442 | }
443 |
444 | emit Mint(msg.sender, depositTransfers, bdv, stalk, seeds, shares);
445 | return shares;
446 | }
447 |
448 | /// @notice Transfer Silo Deposit(s) between user/ROOT contract and update
449 | /// @return shares number of shares will be mint/burn
450 | /// @return bdv total bdv of depositTransfers
451 | /// @return stalk total stalk of depositTransfers
452 | /// @return seeds total seeds of depositTransfers
453 | function _transferDeposits(
454 | DepositTransfer[] calldata depositTransfers,
455 | bool isDeposit
456 | )
457 | internal
458 | returns (
459 | uint256 shares,
460 | uint256 bdv,
461 | uint256 stalk,
462 | uint256 seeds
463 | )
464 | {
465 | IBeanstalk(BEANSTALK_ADDRESS).update(address(this));
466 | uint256 balanceOfSeedsBefore = IBeanstalk(BEANSTALK_ADDRESS)
467 | .balanceOfSeeds(address(this));
468 | uint256 balanceOfStalkBefore = IBeanstalk(BEANSTALK_ADDRESS)
469 | .balanceOfStalk(address(this));
470 |
471 | for (uint256 i; i < depositTransfers.length; ++i) {
472 | require(
473 | whitelisted[depositTransfers[i].token],
474 | "Token is not whitelisted"
475 | );
476 |
477 | uint256[] memory bdvs = _transferDeposit(
478 | depositTransfers[i],
479 | isDeposit
480 | );
481 | for (uint256 j; j < bdvs.length; ++j) {
482 | bdv += bdvs[j];
483 | }
484 | }
485 |
486 | uint256 balanceOfSeedsAfter = IBeanstalk(BEANSTALK_ADDRESS)
487 | .balanceOfSeeds(address(this));
488 | uint256 balanceOfStalkAfter = IBeanstalk(BEANSTALK_ADDRESS)
489 | .balanceOfStalk(address(this));
490 |
491 | uint256 underlyingBdvAfter;
492 | if (isDeposit) {
493 | underlyingBdvAfter = underlyingBdv + bdv;
494 | stalk = balanceOfStalkAfter - balanceOfStalkBefore;
495 | seeds = balanceOfSeedsAfter - balanceOfSeedsBefore;
496 | } else {
497 | underlyingBdvAfter = underlyingBdv - bdv;
498 | stalk = balanceOfStalkBefore - balanceOfStalkAfter;
499 | seeds = balanceOfSeedsBefore - balanceOfSeedsAfter;
500 | }
501 | uint256 supply = totalSupply();
502 | if (supply == 0) {
503 | shares = stalk * 1e8; // Stalk is 1e10 so we want to initialize the initial supply to 1e18
504 | } else if (isDeposit) {
505 | shares =
506 | supply.mulDiv(
507 | _min(
508 | underlyingBdvAfter.mulDiv(
509 | PRECISION,
510 | underlyingBdv,
511 | MathUpgradeable.Rounding.Down
512 | ),
513 | balanceOfStalkAfter.mulDiv(
514 | PRECISION,
515 | balanceOfStalkBefore,
516 | MathUpgradeable.Rounding.Down
517 | ),
518 | balanceOfSeedsAfter.mulDiv(
519 | PRECISION,
520 | balanceOfSeedsBefore,
521 | MathUpgradeable.Rounding.Down
522 | )
523 | ),
524 | PRECISION,
525 | MathUpgradeable.Rounding.Down
526 | ) -
527 | supply;
528 | } else {
529 | shares =
530 | supply -
531 | supply.mulDiv(
532 | _min(
533 | underlyingBdvAfter.mulDiv(
534 | PRECISION,
535 | underlyingBdv,
536 | MathUpgradeable.Rounding.Up
537 | ),
538 | balanceOfStalkAfter.mulDiv(
539 | PRECISION,
540 | balanceOfStalkBefore,
541 | MathUpgradeable.Rounding.Up
542 | ),
543 | balanceOfSeedsAfter.mulDiv(
544 | PRECISION,
545 | balanceOfSeedsBefore,
546 | MathUpgradeable.Rounding.Up
547 | )
548 | ),
549 | PRECISION,
550 | MathUpgradeable.Rounding.Up
551 | );
552 | }
553 |
554 | underlyingBdv = underlyingBdvAfter;
555 | }
556 |
557 | /// @notice Transfer silo deposit(s) between contract/user
558 | function _transferDeposit(
559 | DepositTransfer calldata depositTransfer,
560 | bool isDeposit
561 | ) internal returns (uint256[] memory bdvs) {
562 | bdvs = IBeanstalk(BEANSTALK_ADDRESS).transferDeposits(
563 | isDeposit ? msg.sender : address(this),
564 | isDeposit ? address(this) : msg.sender,
565 | depositTransfer.token,
566 | depositTransfer.seasons,
567 | depositTransfer.amounts
568 | );
569 | }
570 | }
571 |
--------------------------------------------------------------------------------
/deploy.js:
--------------------------------------------------------------------------------
1 |
2 | async function deploy(mock = true) {
3 | const signer = await hre.ethers.getSigner();
4 | if (mock) {
5 | await hre.network.provider.request({
6 | method: "hardhat_impersonateAccount",
7 | params: ['0xa6542Ba5588d275e2e7d0fB2b0aa295a56003B72'],
8 | });
9 | await hre.network.provider.send("hardhat_setBalance", [signer.address, "0x3635C9ADC5DEA00000"]);
10 | }
11 |
12 | let i = 0;
13 | while (i < 7) {
14 | await signer.sendTransaction({
15 | nonce: i,
16 | to: signer.address,
17 | gasLimit: 21000,
18 | value: "0",
19 | });
20 | i += 1;
21 | }
22 |
23 | const RootToken = await ethers.getContractFactory("Root", {
24 | signer,
25 | });
26 | this.rootToken = await upgrades.deployProxy(RootToken, ["Root", "ROOT"], {
27 | initializer: "initialize",
28 | });
29 | console.log("Root token deployed at: ", this.rootToken.address);
30 |
31 | // Transfer ownership
32 | const t = await this.rootToken.transferOwnership('0xb7774ec5031e1d903152E96BbC1601e5D0D83Ca2');
33 | await t.wait();
34 | console.log("New owner: ", await this.rootToken.ownerCandidate());
35 | }
36 |
37 | exports.deployRoot = deploy;
38 |
--------------------------------------------------------------------------------
/proposals.md:
--------------------------------------------------------------------------------
1 | # Proposals
2 |
3 | All past Root governance proposals can be found [here](https://github.com/RootToken/Root-Governance-Proposals).
4 |
5 | ## Root Improvement Proposal (RIP)
6 |
7 | Root Improvement Proposals, or BIPs, are proposals to change the Root token or change Root governance.
8 |
9 | You can read more about BIPs [here](https://docs.roottoken.org/governance/proposals#rip).
10 |
11 | * No RIPs yet!
12 |
13 | ## Emergency Root Improvement Proposal (ERIP)
14 |
15 | Emergency Root Improvement Proposals, or ERIPs, are emergency proposals to change Root that are decided on and committed by the [Root DAO Multisig (RDM)](https://docs.bean.money/almanac/governance/beanstalk/bcm-process).
16 |
17 | You can read about the BCM's Emergency Response Procedures [here](https://docs.roottoken.org/governance/root-token/rdm-process#emergency-response-procedures).
18 |
19 | * [ERIP-0](https://github.com/RootToken/Root-Governance-Proposals/blob/main/rip/erip/erip-0-redeem-fix.md): Redeem Fix
20 |
21 | ## Root Stalk Proposal (RSP)
22 |
23 | Root Stalk Proposals, or RSPs, are proposals for the Root DAO to determine how Root should use its Stalk in each governance process, and distribute yield, if ever.
24 |
25 | You can read more about RSPs [here](https://docs.roottoken.org/governance/proposals#rsp).
26 |
27 | * [RSP-1](https://github.com/RootToken/Root-Governance-Proposals/blob/main/rsp/rsp-1-vote-on-bip-30.md#rsp-process-note): Vote on BIP-30
28 |
--------------------------------------------------------------------------------
/root.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RootToken/Root/4d1f6b44671e54a1b36e2274021748f55e3a59c8/root.pdf
--------------------------------------------------------------------------------