├── 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 | Root logo 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 --------------------------------------------------------------------------------