Skip to content

Commit

Permalink
Merge pull request #104 from lista-dao/psm
Browse files Browse the repository at this point in the history
fea: add psm
  • Loading branch information
qingyang-lista authored Nov 25, 2024
2 parents 7614c88 + a20e67f commit 9e99fa9
Show file tree
Hide file tree
Showing 44 changed files with 3,862 additions and 18 deletions.
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ coverage*
gasReporterOutput.json
lib/
contracts/*
!contracts/psm/
scripts/*
test/*
!test/psm/
Binary file added audits/blocksec_psm_241122.pdf
Binary file not shown.
Binary file added audits/salus_PSM_241122.pdf
Binary file not shown.
8 changes: 8 additions & 0 deletions contracts/hMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,12 @@ library hMath {
}
}
}

function rmul(uint x, uint y) internal pure returns (uint z) {
unchecked {
z = x * y;
require(y == 0 || z / y == x);
z = z / hMath.ONE;
}
}
}
4 changes: 4 additions & 0 deletions contracts/interfaces/HayLike.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ interface HayLike is IERC20{
function transferFrom(address, address, uint256) external returns (bool);

function approve(address, uint256) external returns (bool);

function mint(address, uint256) external;

function burn(address, uint256) external;
}
14 changes: 14 additions & 0 deletions contracts/interfaces/IAdapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IAdapter {
function deposit(uint256 amount) external;

function withdraw(address account, uint256 amount) external;

function totalAvailableAmount() external returns (uint256);

function withdrawAll() external returns (uint256);

function netDepositAmount() external view returns (uint256);
}
6 changes: 6 additions & 0 deletions contracts/interfaces/IEarnPool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IEarnPool {
function deposit(address account, uint256 gemAmount, uint256 lisUSDAmount) external;
}
6 changes: 6 additions & 0 deletions contracts/interfaces/ILisUSDPool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface ILisUSDPool {
function depositFor(address pool, address account, uint256 amount) external;
}
10 changes: 10 additions & 0 deletions contracts/interfaces/IPSM.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IPSM {
function buy(uint256 amount) external;

function sell(uint256 amount) external;

function token() external view returns (address);
}
12 changes: 12 additions & 0 deletions contracts/interfaces/IVBep20Delegate.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IVBep20Delegate {
function mint(uint256 mintAmount) external returns (uint256);

function redeem(uint256 redeemTokens) external returns (uint256);

function redeemUnderlying(uint256 redeemAmount) external returns (uint256);

function balanceOfUnderlying(address owner) external returns (uint256);
}
10 changes: 10 additions & 0 deletions contracts/interfaces/IVaultManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IVaultManager {
function deposit(uint256 amount) external;

function withdraw(address receiver, uint256 amount) external;

function getTotalNetDepositAmount() external view returns (uint256);
}
2 changes: 2 additions & 0 deletions contracts/interfaces/VatLike.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,6 @@ interface VatLike {
function cage() external;

function uncage() external;

function debt() external view returns (uint256);
}
11 changes: 11 additions & 0 deletions contracts/mock/MockUSDC.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.10;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MockUSDC is ERC20 {
constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) {
uint256 initialSupply = 1e18 * 1e9;
_mint(msg.sender, initialSupply);
}
}
39 changes: 39 additions & 0 deletions contracts/mock/psm/MockVenus.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

contract MockVenus is ERC20 {
using SafeERC20 for IERC20;
address public underlying;

constructor(address _underlying) ERC20("MockVenus", "MockVenus") {
underlying = _underlying;
}

function mint(uint256 amount) external returns (uint256) {
IERC20(underlying).safeTransferFrom(msg.sender, address(this), amount);

_mint(msg.sender, amount);
return amount;
}

function redeem(uint256 amount) external returns (uint256) {
IERC20(underlying).safeTransfer(msg.sender, amount);

_burn(msg.sender, amount);
return amount;
}

function redeemUnderlying(uint256 amount) external returns (uint256) {
IERC20(underlying).safeTransfer(msg.sender, amount);

_burn(msg.sender, amount);
return amount;
}

function balanceOfUnderlying(address owner) external view returns (uint256) {
return balanceOf(owner);
}
}
132 changes: 132 additions & 0 deletions contracts/psm/EarnPool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "../interfaces/ILisUSDPool.sol";
import "../interfaces/IPSM.sol";

contract EarnPool is AccessControlUpgradeable, PausableUpgradeable, UUPSUpgradeable {
using SafeERC20 for IERC20;

// token => psm
mapping(address => address) public psm;

address public lisUSDPool; // lisUSD pool address
address public lisUSD; // lisUSD address

bytes32 public constant MANAGER = keccak256("MANAGER"); // manager role
bytes32 public constant PAUSER = keccak256("PAUSER"); // pause role

event SetLisUSDPool(address lisUSDPool);
event SetLisUSD(address lisUSD);
event SetPSM(address token, address psm);
event RemovePSM(address token);

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

/**
* @dev initialize contract
* @param _admin admin address
* @param _manager manager address
* @param _lisUSDPool lisUSD pool address
* @param _lisUSD lisUSD address
*/
function initialize(
address _admin,
address _manager,
address _pauser,
address _lisUSDPool,
address _lisUSD
) public initializer {
require(_admin != address(0), "admin cannot be zero address");
require(_manager != address(0), "manager cannot be zero address");
require(_pauser != address(0), "pauser cannot be zero address");
require(_lisUSDPool != address(0), "lisUSDPool cannot be zero address");
require(_lisUSD != address(0), "lisUSD cannot be zero address");
__AccessControl_init();
__Pausable_init();
__UUPSUpgradeable_init();

_setupRole(DEFAULT_ADMIN_ROLE, _admin);
_setupRole(MANAGER, _manager);
_setupRole(PAUSER, _pauser);

lisUSDPool = _lisUSDPool;
lisUSD = _lisUSD;

emit SetLisUSDPool(_lisUSDPool);
emit SetLisUSD(_lisUSD);
}

/**
* @dev deposit token to earn pool
* @param token token address
* @param amount token amount
*/
function deposit(address token, uint256 amount) external whenNotPaused {
require(amount > 0, "amount must be greater than zero");
require(psm[token] != address(0), "psm not set");

address account = msg.sender;
// transfer token to earn pool
IERC20(token).safeTransferFrom(account, address(this), amount);

// convert token to lisUSD by psm
IERC20(token).safeIncreaseAllowance(psm[token], amount);
uint256 before = IERC20(lisUSD).balanceOf(address(this));
IPSM(psm[token]).sell(amount);
uint256 lisUSDAmount = IERC20(lisUSD).balanceOf(address(this)) - before;

// deposit lisUSD to lisUSD pool
IERC20(lisUSD).safeIncreaseAllowance(lisUSDPool, lisUSDAmount);
ILisUSDPool(lisUSDPool).depositFor(token, account, lisUSDAmount);
}

/**
* @dev pause contract
*/
function pause() external onlyRole(PAUSER) {
_pause();
}

/**
* @dev unpause contract
*/
function unpause() external onlyRole(MANAGER) {
_unpause();
}

/**
* @dev set psm
* @param _token token address
* @param _psm psm address
*/
function setPSM(address _token, address _psm) external onlyRole(MANAGER) {
require(_token != address(0), "token cannot be zero address");
require(_psm != address(0), "psm cannot be zero address");
require(psm[_token] == address(0), "psm already set");
require(IPSM(_psm).token() == _token, "psm token not match");
psm[_token] = _psm;

emit SetPSM(_token, _psm);
}

/**
* @dev remove psm
* @param _token token address
*/
function removePSM(address _token) external onlyRole(MANAGER) {
require(psm[_token] != address(0), "psm is not set");
delete psm[_token];

emit RemovePSM(_token);
}

function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE) {}
}
Loading

0 comments on commit 9e99fa9

Please sign in to comment.