-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e802aaa
commit 1d62e95
Showing
3 changed files
with
471 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity >=0.8.0 <0.9.0; | ||
|
||
import { stdJson } from "forge-std/StdJson.sol"; | ||
import { BaseScript } from ".//BaseScript.s.sol"; | ||
import { XERC20PufferVault } from "../src/l2/XERC20PufferVault.sol"; | ||
import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; | ||
import { Initializable } from "openzeppelin/proxy/utils/Initializable.sol"; | ||
import { NoImplementation } from "../src/NoImplementation.sol"; | ||
import { Timelock } from "../src/Timelock.sol"; | ||
import { ERC1967Proxy } from "openzeppelin/proxy/ERC1967/ERC1967Proxy.sol"; | ||
import { AccessManager } from "openzeppelin/access/manager/AccessManager.sol"; | ||
|
||
/** | ||
* @title DeployL2XPufETH | ||
* @author Puffer Finance | ||
* @notice Deploy XPufETH | ||
* @dev | ||
* | ||
* | ||
* NOTE: | ||
* | ||
* If you ran the deployment script, but did not `--broadcast` the transaction, it will still update your local chainId-deployment.json file. | ||
* Other scripts will fail because addresses will be updated in deployments file, but the deployment never happened. | ||
* | ||
* BaseScript.sol holds the private key logic, if you don't have `PK` ENV variable, it will use the default one PK from `makeAddr("pufferDeployer")` | ||
* | ||
* PK=${deployer_pk} forge script script/DeployL2XPufETH.s.sol:DeployL2XPufETH -vvvv --rpc-url=... --broadcast | ||
*/ | ||
contract DeployL2XPufETH is BaseScript { | ||
address operationsMultisig = vm.envOr("OPERATIONS_MULTISIG", makeAddr("operationsMultisig")); | ||
address pauserMultisig = vm.envOr("PAUSER_MULTISIG", makeAddr("pauserMultisig")); | ||
address communityMultisig = vm.envOr("COMMUNITY_MULTISIG", makeAddr("communityMultisig")); | ||
|
||
function run() public broadcast { | ||
AccessManager accessManager = new AccessManager(_broadcaster); | ||
|
||
Timelock timelock = new Timelock({ | ||
accessManager: address(accessManager), | ||
communityMultisig: communityMultisig, | ||
operationsMultisig: operationsMultisig, | ||
pauser: pauserMultisig, | ||
initialDelay: 7 days + 1 | ||
}); | ||
|
||
address noImpl = address(new NoImplementation()); | ||
|
||
bytes32 xPufETHSalt = bytes32("xPufETH"); | ||
|
||
ERC1967Proxy xPufETH = new ERC1967Proxy{ salt: xPufETHSalt }(noImpl, ""); | ||
vm.label(address(xPufETH), "xPufETH"); | ||
|
||
XERC20PufferVault newImplementation = new XERC20PufferVault(); | ||
|
||
vm.expectEmit(true, true, true, true); | ||
emit Initializable.Initialized(1); | ||
NoImplementation(payable(address(xPufETH))).upgradeToAndCall( | ||
address(newImplementation), abi.encodeCall(XERC20PufferVault.initialize, (address(accessManager))) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,285 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity >=0.8.4 <0.9.0; | ||
|
||
import { IXERC20 } from "./interface/IXERC20.sol"; | ||
import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; | ||
import { AccessManagedUpgradeable } from | ||
"@openzeppelin-contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; | ||
import { ERC20PermitUpgradeable } from | ||
"@openzeppelin-contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; | ||
|
||
contract XERC20PufferVault is IXERC20, AccessManagedUpgradeable, ERC20PermitUpgradeable, UUPSUpgradeable { | ||
/** | ||
* @notice The duration it takes for the limits to fully replenish | ||
*/ | ||
uint256 private constant _DURATION = 1 days; | ||
|
||
/** | ||
* @notice The address of the lockbox contract | ||
*/ | ||
address public lockbox; | ||
|
||
/** | ||
* @notice Maps bridge address to bridge configurations | ||
*/ | ||
mapping(address => Bridge) public bridges; | ||
|
||
constructor() { | ||
_disableInitializers(); | ||
} | ||
|
||
function initialize(address accessManager) public initializer { | ||
__AccessManaged_init(accessManager); | ||
__ERC20_init("xPufETH", "xPufETH"); | ||
__ERC20Permit_init("xPufETH"); | ||
} | ||
|
||
/** | ||
* @notice Mints tokens for a user | ||
* @dev Can only be called by a bridge | ||
* @param _user The address of the user who needs tokens minted | ||
* @param _amount The amount of tokens being minted | ||
*/ | ||
function mint(address _user, uint256 _amount) public { | ||
_mintWithCaller(msg.sender, _user, _amount); | ||
} | ||
|
||
/** | ||
* @notice Burns tokens for a user | ||
* @dev Can only be called by a bridge | ||
* @param _user The address of the user who needs tokens burned | ||
* @param _amount The amount of tokens being burned | ||
*/ | ||
function burn(address _user, uint256 _amount) public { | ||
if (msg.sender != _user) { | ||
_spendAllowance(_user, msg.sender, _amount); | ||
} | ||
|
||
_burnWithCaller(msg.sender, _user, _amount); | ||
} | ||
|
||
/** | ||
* @notice Sets the lockbox address | ||
* | ||
* @param _lockbox The address of the lockbox | ||
*/ | ||
function setLockbox(address _lockbox) public restricted { | ||
// if (msg.sender != FACTORY) revert IXERC20_NotFactory(); | ||
lockbox = _lockbox; | ||
|
||
emit LockboxSet(_lockbox); | ||
} | ||
|
||
/** | ||
* @notice Updates the limits of any bridge | ||
* @dev Can only be called by the owner | ||
* @param _mintingLimit The updated minting limit we are setting to the bridge | ||
* @param _burningLimit The updated burning limit we are setting to the bridge | ||
* @param _bridge The address of the bridge we are setting the limits too | ||
*/ | ||
function setLimits(address _bridge, uint256 _mintingLimit, uint256 _burningLimit) external restricted { | ||
if (_mintingLimit > (type(uint256).max / 2) || _burningLimit > (type(uint256).max / 2)) { | ||
revert IXERC20_LimitsTooHigh(); | ||
} | ||
|
||
_changeMinterLimit(_bridge, _mintingLimit); | ||
_changeBurnerLimit(_bridge, _burningLimit); | ||
emit BridgeLimitsSet(_mintingLimit, _burningLimit, _bridge); | ||
} | ||
|
||
/** | ||
* @notice Returns the max limit of a bridge | ||
* | ||
* @param _bridge the bridge we are viewing the limits of | ||
* @return _limit The limit the bridge has | ||
*/ | ||
function mintingMaxLimitOf(address _bridge) public view returns (uint256 _limit) { | ||
_limit = bridges[_bridge].minterParams.maxLimit; | ||
} | ||
|
||
/** | ||
* @notice Returns the max limit of a bridge | ||
* | ||
* @param _bridge the bridge we are viewing the limits of | ||
* @return _limit The limit the bridge has | ||
*/ | ||
function burningMaxLimitOf(address _bridge) public view returns (uint256 _limit) { | ||
_limit = bridges[_bridge].burnerParams.maxLimit; | ||
} | ||
|
||
/** | ||
* @notice Returns the current limit of a bridge | ||
* | ||
* @param _bridge the bridge we are viewing the limits of | ||
* @return _limit The limit the bridge has | ||
*/ | ||
function mintingCurrentLimitOf(address _bridge) public view returns (uint256 _limit) { | ||
_limit = _getCurrentLimit( | ||
bridges[_bridge].minterParams.currentLimit, | ||
bridges[_bridge].minterParams.maxLimit, | ||
bridges[_bridge].minterParams.timestamp, | ||
bridges[_bridge].minterParams.ratePerSecond | ||
); | ||
} | ||
|
||
/** | ||
* @notice Returns the current limit of a bridge | ||
* | ||
* @param _bridge the bridge we are viewing the limits of | ||
* @return _limit The limit the bridge has | ||
*/ | ||
function burningCurrentLimitOf(address _bridge) public view returns (uint256 _limit) { | ||
_limit = _getCurrentLimit( | ||
bridges[_bridge].burnerParams.currentLimit, | ||
bridges[_bridge].burnerParams.maxLimit, | ||
bridges[_bridge].burnerParams.timestamp, | ||
bridges[_bridge].burnerParams.ratePerSecond | ||
); | ||
} | ||
|
||
/** | ||
* @notice Uses the limit of any bridge | ||
* @param _bridge The address of the bridge who is being changed | ||
* @param _change The change in the limit | ||
*/ | ||
function _useMinterLimits(address _bridge, uint256 _change) internal { | ||
uint256 _currentLimit = mintingCurrentLimitOf(_bridge); | ||
bridges[_bridge].minterParams.timestamp = block.timestamp; | ||
bridges[_bridge].minterParams.currentLimit = _currentLimit - _change; | ||
} | ||
|
||
/** | ||
* @notice Uses the limit of any bridge | ||
* @param _bridge The address of the bridge who is being changed | ||
* @param _change The change in the limit | ||
*/ | ||
function _useBurnerLimits(address _bridge, uint256 _change) internal { | ||
uint256 _currentLimit = burningCurrentLimitOf(_bridge); | ||
bridges[_bridge].burnerParams.timestamp = block.timestamp; | ||
bridges[_bridge].burnerParams.currentLimit = _currentLimit - _change; | ||
} | ||
|
||
/** | ||
* @notice Updates the limit of any bridge | ||
* @dev Can only be called by the owner | ||
* @param _bridge The address of the bridge we are setting the limit too | ||
* @param _limit The updated limit we are setting to the bridge | ||
*/ | ||
function _changeMinterLimit(address _bridge, uint256 _limit) internal { | ||
uint256 _oldLimit = bridges[_bridge].minterParams.maxLimit; | ||
uint256 _currentLimit = mintingCurrentLimitOf(_bridge); | ||
bridges[_bridge].minterParams.maxLimit = _limit; | ||
|
||
bridges[_bridge].minterParams.currentLimit = _calculateNewCurrentLimit(_limit, _oldLimit, _currentLimit); | ||
|
||
bridges[_bridge].minterParams.ratePerSecond = _limit / _DURATION; | ||
bridges[_bridge].minterParams.timestamp = block.timestamp; | ||
} | ||
|
||
/** | ||
* @notice Updates the limit of any bridge | ||
* @dev Can only be called by the owner | ||
* @param _bridge The address of the bridge we are setting the limit too | ||
* @param _limit The updated limit we are setting to the bridge | ||
*/ | ||
function _changeBurnerLimit(address _bridge, uint256 _limit) internal { | ||
uint256 _oldLimit = bridges[_bridge].burnerParams.maxLimit; | ||
uint256 _currentLimit = burningCurrentLimitOf(_bridge); | ||
bridges[_bridge].burnerParams.maxLimit = _limit; | ||
|
||
bridges[_bridge].burnerParams.currentLimit = _calculateNewCurrentLimit(_limit, _oldLimit, _currentLimit); | ||
|
||
bridges[_bridge].burnerParams.ratePerSecond = _limit / _DURATION; | ||
bridges[_bridge].burnerParams.timestamp = block.timestamp; | ||
} | ||
|
||
/** | ||
* @notice Updates the current limit | ||
* | ||
* @param _limit The new limit | ||
* @param _oldLimit The old limit | ||
* @param _currentLimit The current limit | ||
* @return _newCurrentLimit The new current limit | ||
*/ | ||
function _calculateNewCurrentLimit(uint256 _limit, uint256 _oldLimit, uint256 _currentLimit) | ||
internal | ||
pure | ||
returns (uint256 _newCurrentLimit) | ||
{ | ||
uint256 _difference; | ||
|
||
if (_oldLimit > _limit) { | ||
_difference = _oldLimit - _limit; | ||
_newCurrentLimit = _currentLimit > _difference ? _currentLimit - _difference : 0; | ||
} else { | ||
_difference = _limit - _oldLimit; | ||
_newCurrentLimit = _currentLimit + _difference; | ||
} | ||
} | ||
|
||
/** | ||
* @notice Gets the current limit | ||
* | ||
* @param _currentLimit The current limit | ||
* @param _maxLimit The max limit | ||
* @param _timestamp The timestamp of the last update | ||
* @param _ratePerSecond The rate per second | ||
* @return _limit The current limit | ||
*/ | ||
function _getCurrentLimit(uint256 _currentLimit, uint256 _maxLimit, uint256 _timestamp, uint256 _ratePerSecond) | ||
internal | ||
view | ||
returns (uint256 _limit) | ||
{ | ||
_limit = _currentLimit; | ||
if (_limit == _maxLimit) { | ||
return _limit; | ||
} else if (_timestamp + _DURATION <= block.timestamp) { | ||
_limit = _maxLimit; | ||
} else if (_timestamp + _DURATION > block.timestamp) { | ||
uint256 _timePassed = block.timestamp - _timestamp; | ||
uint256 _calculatedLimit = _limit + (_timePassed * _ratePerSecond); | ||
_limit = _calculatedLimit > _maxLimit ? _maxLimit : _calculatedLimit; | ||
} | ||
} | ||
|
||
/** | ||
* @notice Internal function for burning tokens | ||
* | ||
* @param _caller The caller address | ||
* @param _user The user address | ||
* @param _amount The amount to burn | ||
*/ | ||
function _burnWithCaller(address _caller, address _user, uint256 _amount) internal { | ||
if (_caller != lockbox) { | ||
uint256 _currentLimit = burningCurrentLimitOf(_caller); | ||
if (_currentLimit < _amount) revert IXERC20_NotHighEnoughLimits(); | ||
_useBurnerLimits(_caller, _amount); | ||
} | ||
_burn(_user, _amount); | ||
} | ||
|
||
/** | ||
* @notice Internal function for minting tokens | ||
* | ||
* @param _caller The caller address | ||
* @param _user The user address | ||
* @param _amount The amount to mint | ||
*/ | ||
function _mintWithCaller(address _caller, address _user, uint256 _amount) internal { | ||
if (_caller != lockbox) { | ||
uint256 _currentLimit = mintingCurrentLimitOf(_caller); | ||
if (_currentLimit < _amount) revert IXERC20_NotHighEnoughLimits(); | ||
_useMinterLimits(_caller, _amount); | ||
} | ||
_mint(_user, _amount); | ||
} | ||
|
||
/** | ||
* @dev Authorizes an upgrade to a new implementation | ||
* Restricted access | ||
* @param newImplementation The address of the new implementation | ||
*/ | ||
// slither-disable-next-line dead-code | ||
function _authorizeUpgrade(address newImplementation) internal virtual override restricted { } | ||
} |
Oops, something went wrong.