Skip to content

Commit

Permalink
[PDE-410] Integrate NestTeller and NestAtomicQueue as BoringVault mod…
Browse files Browse the repository at this point in the history
…ule (#137)
  • Loading branch information
ungaro authored Jan 1, 2025
1 parent 0d10386 commit f485bf4
Show file tree
Hide file tree
Showing 14 changed files with 880 additions and 276 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@
[submodule "nest/lib/solmate"]
path = nest/lib/solmate
url = https://github.com/transmissions11/solmate
[submodule "nest/lib/nucleus-boring-vault"]
path = nest/lib/nucleus-boring-vault
url = https://github.com/plumenetwork/nucleus-boring-vault
1 change: 1 addition & 0 deletions nest/lib/nucleus-boring-vault
Submodule nucleus-boring-vault added at 5b9eec
3 changes: 2 additions & 1 deletion nest/remappings.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
@solmate/=lib/solmate/src/
@solmate/=lib/solmate/src/
@boringvault/=lib/nucleus-boring-vault/
6 changes: 4 additions & 2 deletions nest/src/interfaces/IBoringVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
pragma solidity ^0.8.25;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

interface IBoringVault is IERC20, IERC20Metadata {
interface IBoringVault is IERC20 {

/**
* @notice Deposits assets into the vault in exchange for shares
Expand All @@ -26,4 +25,7 @@ interface IBoringVault is IERC20, IERC20Metadata {
*/
function exit(address to, address asset, uint256 assetAmount, address from, uint256 shareAmount) external;

// ERC20 interface functions
function decimals() external view returns (uint8);

}
17 changes: 17 additions & 0 deletions nest/src/mocks/MockERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MockERC20 is ERC20 {

// small hack to be excluded from coverage report
function test() public { }

constructor(string memory name, string memory symbol) ERC20(name, symbol) { }

function mint(address to, uint256 amount) public {
_mint(to, amount);
}

}
83 changes: 83 additions & 0 deletions nest/src/mocks/MockLayerZeroEndpoint.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import { MessageLibManager } from "@layerzerolabs/lz-evm-protocol-v2/contracts/MessageLibManager.sol";
import { MessagingChannel } from "@layerzerolabs/lz-evm-protocol-v2/contracts/MessagingChannel.sol";
import { MessagingComposer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/MessagingComposer.sol";
import { MessagingContext } from "@layerzerolabs/lz-evm-protocol-v2/contracts/MessagingContext.sol";
import {
ILayerZeroEndpointV2,
MessagingFee,
MessagingParams,
MessagingReceipt,
Origin
} from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

contract MockLayerZeroEndpoint is
MessagingChannel,
MessageLibManager,
MessagingComposer,
MessagingContext,
ILayerZeroEndpointV2
{

address public lzToken;
mapping(address oapp => address delegate) public delegates;

constructor(
uint32 _eid,
address _owner
) MessagingChannel(_eid) MessageLibManager() MessagingComposer() MessagingContext() Ownable(_owner) { }

function quote(MessagingParams calldata, address) external pure returns (MessagingFee memory) {
return MessagingFee(0, 0);
}

function send(MessagingParams calldata _params, address) external payable returns (MessagingReceipt memory) {
bytes32 guid = keccak256(abi.encodePacked(msg.sender, _params.dstEid, _params.receiver));
return MessagingReceipt(guid, 1, MessagingFee(0, 0));
}

function verify(Origin calldata, address, bytes32) external pure { }

function lzReceive(Origin calldata, address, bytes32, bytes calldata, bytes calldata) external payable { }

function clear(address, Origin calldata, bytes32, bytes calldata) external { }

function setLzToken(
address _lzToken
) external {
lzToken = _lzToken;
emit LzTokenSet(_lzToken);
}

function setDelegate(
address _delegate
) external {
delegates[msg.sender] = _delegate;
emit DelegateSet(msg.sender, _delegate);
}

function nativeToken() external pure returns (address) {
return address(0);
}

function initializable(Origin calldata, address) external pure returns (bool) {
return true;
}

function verifiable(Origin calldata, address) external pure returns (bool) {
return true;
}

// Override required internal functions
function _assertAuthorized(
address _oapp
) internal view override(MessagingChannel, MessageLibManager) {
if (msg.sender != _oapp && msg.sender != delegates[_oapp]) {
revert("Unauthorized");
}
}

}
170 changes: 97 additions & 73 deletions nest/src/mocks/MockVault.sol
Original file line number Diff line number Diff line change
@@ -1,117 +1,141 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import { IBoringVault } from "../interfaces/IBoringVault.sol";
import { BeforeTransferHook } from "@boringvault/src/interfaces/BeforeTransferHook.sol";
import { ERC1155Holder } from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { ERC721Holder } from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import { Auth, Authority } from "@solmate/auth/Auth.sol";
import { ERC20 } from "@solmate/tokens/ERC20.sol";
import { SafeTransferLib } from "@solmate/utils/SafeTransferLib.sol";

import { IBoringVault } from "../interfaces/IBoringVault.sol";

contract MockVault is ERC20, Auth, ERC721Holder, ERC1155Holder, IBoringVault {

using SafeERC20 for IERC20;
contract MockVault is IBoringVault, Auth, ERC721Holder, ERC1155Holder {

// token => account => balance
mapping(address => mapping(address => uint256)) private _balances;
using SafeTransferLib for ERC20;

mapping(address => mapping(address => mapping(address => uint256))) private _allowances;
// State variables
string public name;
string public symbol;
uint8 public decimals;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
BeforeTransferHook public hook;

IERC20 public asset;
IERC20 public immutable usdc;
IERC20 public immutable usdt;
address public beforeTransferHook;
// Events
event DebugCall(string functionName, bytes data);
event Enter(address indexed from, address indexed asset, uint256 amount, address indexed to, uint256 shares);
event Exit(address indexed to, address indexed asset, uint256 amount, address indexed from, uint256 shares);

constructor(
address _owner,
string memory _name,
string memory _symbol,
address _usdc
) ERC20(_name, _symbol) Auth(_owner, Authority(address(0))) {
usdc = IERC20(_usdc);
uint8 _decimals
) Auth(_owner, Authority(address(0))) {
name = _name;
symbol = _symbol;
decimals = _decimals;
}

function enter(address from, address asset_, uint256 assetAmount, address to, uint256 shareAmount) external {
if (assetAmount > 0) {
IERC20(asset_).safeTransferFrom(from, address(this), assetAmount);
}
_balances[asset_][to] += shareAmount;
_allowances[asset_][to][msg.sender] = type(uint256).max;
function approve(address spender, uint256 amount) public returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}

function exit(address to, address asset_, uint256 assetAmount, address from, uint256 shareAmount) external {
// Change from checking 'from' balance to checking the actual owner's balance
address owner = from == msg.sender ? to : from;
require(_balances[asset_][owner] >= shareAmount, "MockVault: insufficient balance");
function transfer(address to, uint256 amount) public returns (bool) {
_callBeforeTransfer(msg.sender);
return _transfer(msg.sender, to, amount);
}

uint256 allowed = _allowances[asset_][owner][msg.sender];
function transferFrom(address from, address to, uint256 amount) public returns (bool) {
_callBeforeTransfer(from);
uint256 allowed = allowance[from][msg.sender];
if (allowed != type(uint256).max) {
require(allowed >= shareAmount, "MockVault: insufficient allowance");
_allowances[asset_][owner][msg.sender] = allowed - shareAmount;
}

_balances[asset_][owner] -= shareAmount;

// Changed: Transfer to 'to' instead of msg.sender, and always transfer if we have shares
if (shareAmount > 0) {
IERC20(asset_).safeTransfer(to, shareAmount);
require(allowed >= amount, "MockVault: insufficient allowance");
allowance[from][msg.sender] = allowed - amount;
}
return _transfer(from, to, amount);
}

function transferFrom(address asset_, address from, address to, uint256 amount) external returns (bool) {
require(_balances[asset_][from] >= amount, "MockVault: insufficient balance");
function _transfer(address from, address to, uint256 amount) internal returns (bool) {
require(from != address(0), "MockVault: transfer from zero address");
require(to != address(0), "MockVault: transfer to zero address");
require(balanceOf[from] >= amount, "MockVault: insufficient balance");

uint256 allowed = _allowances[asset_][from][msg.sender];
if (allowed != type(uint256).max) {
require(allowed >= amount, "MockVault: insufficient allowance");
_allowances[asset_][from][msg.sender] = allowed - amount;
}
balanceOf[from] -= amount;
balanceOf[to] += amount;

_balances[asset_][from] -= amount;
_balances[asset_][to] += amount;
emit Transfer(from, to, amount);
return true;
}

function approve(address asset_, address spender, uint256 amount) external returns (bool) {
_allowances[asset_][msg.sender][spender] = amount;
return true;
function _mint(address to, uint256 amount) internal {
require(to != address(0), "MockVault: mint to zero address");
totalSupply += amount;
balanceOf[to] += amount;
emit Transfer(address(0), to, amount);
}

function balanceOf(
address account
) public view virtual override(ERC20, IERC20) returns (uint256) {
// Return total balance across all assets
return _balances[address(usdc)][account] + _balances[address(usdt)][account];
function _burn(address from, uint256 amount) internal {
require(from != address(0), "MockVault: burn from zero address");
require(balanceOf[from] >= amount, "MockVault: insufficient balance");
totalSupply -= amount;
balanceOf[from] -= amount;
emit Transfer(from, address(0), amount);
}

function totalSupply() public view virtual override(ERC20, IERC20) returns (uint256) {
// Return total supply across all assets
return _balances[address(usdc)][address(this)] + _balances[address(usdt)][address(this)];
}
function enter(
address from,
address asset,
uint256 assetAmount,
address to,
uint256 shareAmount
) external override requiresAuth {
emit DebugCall("enter", abi.encode(from, asset, assetAmount, to, shareAmount));

function decimals() public pure virtual override(ERC20, IERC20Metadata) returns (uint8) {
return 6;
}
if (assetAmount > 0) {
ERC20(asset).safeTransferFrom(from, address(this), assetAmount);
}

function tokenBalance(address token, address account) external view returns (uint256) {
return _balances[token][account];
}
_mint(to, shareAmount);

function setBalance(address token, uint256 amount) external {
_balances[token][address(this)] = amount;
emit Enter(from, asset, assetAmount, to, shareAmount);
}

function allowance(address asset_, address owner, address spender) external view returns (uint256) {
return _allowances[asset_][owner][spender];
function exit(
address to,
address asset,
uint256 assetAmount,
address from,
uint256 shareAmount
) external override requiresAuth {
emit DebugCall("exit", abi.encode(to, asset, assetAmount, from, shareAmount));

_burn(from, shareAmount);

if (assetAmount > 0) {
ERC20(asset).safeTransfer(to, assetAmount);
}

emit Exit(to, address(asset), assetAmount, from, shareAmount);
}

function setBeforeTransferHook(
address hook
) external {
beforeTransferHook = hook;
address _hook
) external requiresAuth {
hook = BeforeTransferHook(_hook);
}

function _callBeforeTransfer(
address from
) internal view {
if (address(hook) != address(0)) {
hook.beforeTransfer(from);
}
}

receive() external payable { }

}
Loading

0 comments on commit f485bf4

Please sign in to comment.