Skip to content

Commit

Permalink
add natspec, change token logic
Browse files Browse the repository at this point in the history
  • Loading branch information
bxmmm1 committed Jun 14, 2024
1 parent db253b4 commit 04649f5
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 71 deletions.
45 changes: 36 additions & 9 deletions mainnet-contracts/src/PufLocker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,23 @@ import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { PufLockerStorage } from "./PufLockerStorage.sol";
import { IPufLocker } from "./interface/IPufLocker.sol";
import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Permit } from "./structs/Permit.sol";

/**
* @title PufLocker
* @author Puffer Finance
* @custom:security-contact security@puffer.fi
*/
contract PufLocker is AccessManagedUpgradeable, IPufLocker, PufLockerStorage {
using SafeERC20 for IERC20;

constructor() {
_disableInitializers();
}

function initialize(address accessManager) external initializer {
require(accessManager != address(0));
__AccessManaged_init(accessManager);
}

Expand All @@ -23,32 +36,40 @@ contract PufLocker is AccessManagedUpgradeable, IPufLocker, PufLockerStorage {
_;
}

function setAllowedToken(address token, bool allowed) external restricted {
/**
* @notice Creates a new staking token contract
* @dev Restricted to Puffer DAO
*/
function setIsAllowedToken(address token, bool allowed) external restricted {
PufLockerData storage $ = _getPufLockerStorage();
$.allowedTokens[token] = allowed;
emit TokenAllowanceChanged(token, allowed);
emit SetTokenIsAllowed(token, allowed);
}

function setLockPeriods(uint40 minLock, uint40 maxLock) external restricted {
/**
* @notice Creates a new staking token contract
* @dev Restricted to Puffer DAO
*/
function setLockPeriods(uint128 minLock, uint128 maxLock) external restricted {
if (minLock > maxLock) {
revert InvalidLockPeriod();
}
PufLockerData storage $ = _getPufLockerStorage();
emit LockPeriodsChanged($.minLockPeriod, minLock, $.maxLockPeriod, maxLock);
$.minLockPeriod = minLock;
$.maxLockPeriod = maxLock;
}

function deposit(address token, uint40 lockPeriod, Permit calldata permitData) external isAllowedToken(token) {
function deposit(address token, uint128 lockPeriod, Permit calldata permitData) external isAllowedToken(token) {
if (permitData.amount == 0) {
revert InvalidAmount();
}
PufLockerData storage $ = _getPufLockerStorage();

if (lockPeriod < $.minLockPeriod || lockPeriod > $.maxLockPeriod) {
revert InvalidLockPeriod();
}

uint40 releaseTime = uint40(block.timestamp) + lockPeriod;

// https://docs.openzeppelin.com/contracts/5.x/api/token/erc20#security_considerations
try ERC20Permit(token).permit({
owner: msg.sender,
Expand All @@ -60,7 +81,10 @@ contract PufLocker is AccessManagedUpgradeable, IPufLocker, PufLockerStorage {
r: permitData.r
}) { } catch { }

IERC20(token).transferFrom(msg.sender, address(this), permitData.amount);
IERC20(token).safeTransferFrom(msg.sender, address(this), permitData.amount);

uint128 releaseTime = uint128(block.timestamp) + lockPeriod;

$.deposits[msg.sender][token].push(Deposit(uint128(permitData.amount), releaseTime));

emit Deposited(msg.sender, token, uint128(permitData.amount), releaseTime);
Expand All @@ -82,7 +106,7 @@ contract PufLocker is AccessManagedUpgradeable, IPufLocker, PufLockerStorage {
}

Deposit storage userDeposit = userDeposits[index];
if (userDeposit.releaseTime > uint40(block.timestamp)) {
if (userDeposit.releaseTime > uint128(block.timestamp)) {
revert DepositStillLocked();
}

Expand Down Expand Up @@ -124,7 +148,10 @@ contract PufLocker is AccessManagedUpgradeable, IPufLocker, PufLockerStorage {
return depositPage;
}

function getLockPeriods() external view returns (uint40, uint40) {
/**
* @inheritdoc IPufLocker
*/
function getLockPeriods() external view returns (uint128, uint128) {
PufLockerData storage $ = _getPufLockerStorage();
return ($.minLockPeriod, $.maxLockPeriod);
}
Expand Down
15 changes: 10 additions & 5 deletions mainnet-contracts/src/PufLockerStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,23 @@ import { IPufLocker } from "./interface/IPufLocker.sol";

/**
* @title PufLockerStorage
* @author Puffer Finance
* @dev Storage contract for PufLocker to support upgradability
* @custom:security-contact security@puffer.fi
*/
abstract contract PufLockerStorage {
// Storage slot location for PufLocker data

// keccak256(abi.encode(uint256(keccak256("PufLocker.storage")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant _PUF_LOCKER_STORAGE_SLOT =
0xed4b58c94786491f32821dd56ebc03d5f67df2b901c79c3e972343a4fbb3dfed; // keccak256("PufLocker.storage");
0xaf4bf4b31f04ca259733013a412d3e67552036ab2d2af267876ad7f9110e5d00;

/// @custom:storage-location erc7201:PufLocker.storage
struct PufLockerData {
mapping(address => bool) allowedTokens;
mapping(address => mapping(address => IPufLocker.Deposit[])) deposits;
uint40 minLockPeriod;
uint40 maxLockPeriod;
mapping(address token => bool isAllowed) allowedTokens;
mapping(address token => mapping(address depositor => IPufLocker.Deposit[])) deposits;
uint128 minLockPeriod;
uint128 maxLockPeriod;
}

function _getPufLockerStorage() internal pure returns (PufLockerData storage $) {
Expand Down
38 changes: 18 additions & 20 deletions mainnet-contracts/src/PufToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/Sig
import { PufferL2Depositor } from "./PufferL2Depositor.sol";
import { IMigrator } from "./interface/IMigrator.sol";
import { IPufStakingPool } from "./interface/IPufStakingPool.sol";
import { Permit } from "./structs/Permit.sol";

/**
* @title Puf token
Expand Down Expand Up @@ -46,6 +45,10 @@ contract PufToken is IPufStakingPool, ERC20, ERC20Permit {
*/
ERC20 public immutable TOKEN;

/**
* @notice The maximum deposit amount.
* @dev Deposit cap is in wei
*/
uint256 public totalDepositCap;

constructor(address token, string memory tokenName, string memory tokenSymbol, uint256 depositCap)
Expand Down Expand Up @@ -97,24 +100,15 @@ contract PufToken is IPufStakingPool, ERC20, ERC20Permit {
_;
}

function depositFrom(address sender, address account, uint256 amount)
external
whenNotPaused
validateAddressAndAmount(account, amount)
onlyPufferFactory
{
_deposit(sender, account, amount);
}

/**
* @notice Deposits the underlying token to receive pufToken to the `account`
*/
function deposit(address account, uint256 amount)
function deposit(address from, address account, uint256 amount)
external
whenNotPaused
validateAddressAndAmount(account, amount)
{
_deposit(msg.sender, account, amount);
_deposit(from, account, amount);
}

/**
Expand Down Expand Up @@ -189,21 +183,25 @@ contract PufToken is IPufStakingPool, ERC20, ERC20Permit {
totalDepositCap = newDepositCap;
}

function _deposit(address sender, address account, uint256 amount) internal {
TOKEN.safeTransferFrom(sender, address(this), amount);
uint256 deNormalizedTotalSupply = _denormalizeAmount(totalSupply());
function _deposit(address depositor, address account, uint256 amount) internal {
TOKEN.safeTransferFrom(msg.sender, address(this), amount);

uint256 normalizedAmount = _normalizeAmount(amount);

if (deNormalizedTotalSupply + amount > totalDepositCap) {
if (totalSupply() + normalizedAmount > totalDepositCap) {
revert TotalDepositCapReached();
}

uint256 normalizedAmount = _normalizeAmount(amount);

// Mint puffToken to the account
_mint(account, normalizedAmount);

// Using the original deposit amount in the event
emit Deposited(sender, account, amount);
// If the user is deposiing using the factory, we emit the `depositor` from the parameters
if (msg.sender == address(PUFFER_FACTORY)) {
emit Deposited(depositor, account, amount);
} else {
// If it is a direct deposit not comming from the depositor, we use msg.sender

Check failure on line 202 in mainnet-contracts/src/PufToken.sol

View workflow job for this annotation

GitHub Actions / unit-tests

comming ==> coming
emit Deposited(msg.sender, account, amount);
}
}

/**
Expand Down
25 changes: 14 additions & 11 deletions mainnet-contracts/src/PufferL2Depositor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,19 @@ contract PufferL2Depositor is IPufferL2Depositor, AccessManaged {
r: permitData.r
}) { } catch { }

_deposit({ token: token, sender: msg.sender, account: account, amount: permitData.amount });
IERC20(token).safeTransferFrom(msg.sender, address(this), permitData.amount);

_deposit({ token: token, depositor: msg.sender, account: account, amount: permitData.amount });
}

/**
* @inheritdoc IPufferL2Depositor
* @dev Restricted in this context is like `whenNotPaused` modifier from Pausable.sol
*/
function depositETH(address account) external payable onlySupportedTokens(WETH) restricted {
function depositETH(address account) external payable restricted {
IWETH(WETH).deposit{ value: msg.value }();

_deposit({ token: WETH, sender: address(this), account: account, amount: msg.value });
_deposit({ token: WETH, depositor: msg.sender, account: account, amount: msg.value });
}

/**
Expand All @@ -93,12 +95,14 @@ contract PufferL2Depositor is IPufferL2Depositor, AccessManaged {
emit SetIsMigratorAllowed(migrator, allowed);
}

/**
* @notice Changes the status of `migrator` to `allowed`
* @dev Restricted to Puffer DAO
*/
function setDepositCap(address token, uint256 newDepositCap) external onlySupportedTokens(token) restricted {
PufToken pufToken = PufToken(tokens[token]);
uint256 oldDepositCap = pufToken.totalDepositCap();
emit DepositCapUpdated(token, pufToken.totalDepositCap(), newDepositCap);
pufToken.setDepositCap(newDepositCap);

emit DepositCapUpdated(token, oldDepositCap, newDepositCap);
}

/**
Expand All @@ -107,13 +111,12 @@ contract PufferL2Depositor is IPufferL2Depositor, AccessManaged {
*/
function revertIfPaused() external restricted { }

function _deposit(address token, address sender, address account, uint256 amount) internal {
function _deposit(address token, address depositor, address account, uint256 amount) internal {
PufToken pufToken = PufToken(tokens[token]);
if (sender == address(this)) {
IERC20(token).safeIncreaseAllowance(address(pufToken), amount);
}

pufToken.depositFrom(sender, account, amount);
IERC20(token).safeIncreaseAllowance(address(pufToken), amount);

pufToken.deposit(depositor, account, amount);

emit DepositedToken(token, msg.sender, account, amount);
}
Expand Down
16 changes: 8 additions & 8 deletions mainnet-contracts/src/interface/IPufLocker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { Permit } from "../structs/Permit.sol";

interface IPufLocker {
// Custom error messages
error NotAdmin();
error TokenNotAllowed();
error InvalidAmount();
error InvalidLockPeriod();
Expand All @@ -15,27 +14,28 @@ interface IPufLocker {
error InvalidRecipientAddress();

// Events
event TokenAllowanceChanged(address indexed token, bool allowed);
event Deposited(address indexed user, address indexed token, uint128 amount, uint40 releaseTime);
event SetTokenIsAllowed(address indexed token, bool allowed);
event Deposited(address indexed user, address indexed token, uint128 amount, uint128 releaseTime);
event Withdrawn(address indexed user, address indexed token, uint128 amount, address recipient);
event LockPeriodsChanged(uint128 previousMinLock, uint128 newMinLock, uint128 previousMaxLock, uint128 newMaxLock);

// Functions
function setAllowedToken(address token, bool allowed) external;
function setIsAllowedToken(address token, bool allowed) external;

function setLockPeriods(uint40 minLockPeriod, uint40 maxLockPeriod) external;
function setLockPeriods(uint128 minLockPeriod, uint128 maxLockPeriod) external;

function deposit(address token, uint40 lockPeriod, Permit calldata permitData) external;
function deposit(address token, uint128 lockPeriod, Permit calldata permitData) external;

function withdraw(address token, uint256[] calldata depositIndexes, address recipient) external;

function getDeposits(address user, address token, uint256 start, uint256 limit)
external
view
returns (Deposit[] memory);
function getLockPeriods() external view returns (uint40, uint40);
function getLockPeriods() external view returns (uint128, uint128);

struct Deposit {
uint128 amount;
uint40 releaseTime;
uint128 releaseTime;
}
}
8 changes: 7 additions & 1 deletion mainnet-contracts/src/interface/IPufStakingPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ interface IPufStakingPool {
address indexed depositor, address indexed destination, address indexed migratorContract, uint256 amount
);

function deposit(address account, uint256 amount) external;
/**
* @notice Function used to deposit the undelrying token
* @param depositor is the msg.sender or a parameter passed from the PufferL2Depositor
* @param account is the recipient of the deposit
* @param amount is the deposit amount
*/
function deposit(address depositor, address account, uint256 amount) external;

function withdraw(address recipient, uint256 amount) external;

Expand Down
Loading

0 comments on commit 04649f5

Please sign in to comment.