Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions contracts/partners/tokenWrappers/MezoWrapper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.17;

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

import { PullTokenWrapperImmutableBase } from "./PullTokenWrapperImmutableBase.sol";
import { Errors } from "../../utils/Errors.sol";

interface IMezoStaking {
function createLockFor(uint256 _value, uint256 _lockDuration, address _to) external returns (uint256);
}

/// @title MezoWrapper
/// @notice Non-upgradeable wrapper that creates Mezo locks for claimed tokens
/// @dev On claim, underlying tokens are approved and locked via `createLockFor` on the Mezo staking contract
contract MezoWrapper is PullTokenWrapperImmutableBase {
using SafeERC20 for IERC20;

/// @notice Mezo staking contract
address public immutable mezoStaking;
/// @notice Duration used when creating locks
uint256 public lockDuration;

event LockDurationUpdated(uint256 newLockDuration);

constructor(
address _token,
address _distributionCreator,
address _holder,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please clarify who is _holder? Is it a contract with a pre-approved wrapper or a multisig?

address _mezoStaking,
uint256 _lockDuration,
string memory _name,
string memory _symbol
) ERC20(_name, _symbol) PullTokenWrapperImmutableBase(_token, _distributionCreator, _holder) {
if (_mezoStaking == address(0)) revert Errors.ZeroAddress();
mezoStaking = _mezoStaking;
lockDuration = _lockDuration;
IERC20(_token).safeApprove(_mezoStaking, type(uint256).max);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MEZO does not implement safeApprove. You can use approve

}

/// @notice Hook called before every transfer: on claim, pulls tokens from holder and creates a Mezo lock;
/// on fee transfer, pulls tokens from holder and sends them directly to the fee recipient
function _beforeTokenTransfer(address from, address to, uint256 amount) internal override {
if (to == feeRecipient) {
IERC20(token).safeTransferFrom(holder, to, amount);
} else if (from == distributor) {
if (lockDuration == 0) {
IERC20(token).safeTransferFrom(holder, to, amount);
} else {
IERC20(token).safeTransferFrom(holder, address(this), amount);
IMezoStaking(mezoStaking).createLockFor(amount, lockDuration, to);
}
}
}

// ================================= ADMIN =================================

/// @notice Updates the lock duration used for future claims
function setLockDuration(uint256 _newLockDuration) external onlyHolderOrGovernor {
lockDuration = _newLockDuration;
emit LockDurationUpdated(_newLockDuration);
}

/// @notice Resets the token allowance granted to the Mezo staking contract
function setStakingAllowance(uint256 _allowance) external onlyHolderOrGovernor {
IERC20(token).safeApprove(mezoStaking, 0);
IERC20(token).safeApprove(mezoStaking, _allowance);
}
}
3 changes: 3 additions & 0 deletions contracts/partners/tokenWrappers/PublicWrapperBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ abstract contract PublicWrapperBase is PullTokenWrapperImmutableBase {
if (_minting == 0 && isAllowed[to] == 0) _burn(to, amount);
}

/// @notice Disabled: minting happens automatically in `_beforeTokenTransfer`
function mint(address, uint256) external override {}

Comment thread
sogipec marked this conversation as resolved.
/// @notice Burns wrapper tokens from a given address
function burn(address from, uint256 amount) external onlyHolderOrGovernor {
_burn(from, amount);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ abstract contract PullTokenWrapperImmutableBase is ERC20 {
/// @notice Mints wrapper tokens to a recipient and allows them to hold wrapper tokens
/// @param recipient Address to receive the minted wrapper tokens
/// @param amount Amount of wrapper tokens to mint
function mint(address recipient, uint256 amount) external onlyHolderOrGovernor {
function mint(address recipient, uint256 amount) external virtual onlyHolderOrGovernor {
isAllowed[recipient] = 1;
_mint(recipient, amount);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ contract PullTokenWrapperVaultImmutable is PullTokenWrapperImmutableBase {
address _holder,
address _vault
)
ERC20(string(abi.encodePacked(IERC20Metadata(_token).name(), " (wrapped)")), IERC20Metadata(_token).symbol())
ERC20(string(abi.encodePacked(IERC20Metadata(_vault).name(), " (wrapped)")), IERC20Metadata(_token).symbol())
Comment thread
sogipec marked this conversation as resolved.
PullTokenWrapperImmutableBase(_token, _distributionCreator, _holder)
{
if (_vault == address(0)) revert Errors.ZeroAddress();
Expand Down
9 changes: 3 additions & 6 deletions scripts/MockToken.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,14 @@ contract MockTokenScript is BaseScript {}
// Deploy script
contract Deploy is MockTokenScript {
function run() external broadcast {
// forge script scripts/MockToken.s.sol:Deploy --rpc-url avalanche --sender 0xA9DdD91249DFdd450E81E1c56Ab60E1A62651701 --broadcast
// MODIFY THESE VALUES TO SET YOUR DESIRED TOKEN PARAMETERS
string memory name = "Mock Token";
string memory symbol = "MOCK";
string memory name = "USDe";
string memory symbol = "USDe";
uint8 decimals = 18;
_run(name, symbol, decimals);
}

function run(string calldata name, string calldata symbol, uint8 decimals) external broadcast {
_run(name, symbol, decimals);
}

function _run(string memory name, string memory symbol, uint8 decimals) internal {
console.log("DEPLOYER_ADDRESS:", broadcaster);

Expand Down
42 changes: 42 additions & 0 deletions scripts/deployMezoWrapper.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.17;

import { console } from "forge-std/console.sol";

import { BaseScript } from "./utils/Base.s.sol";

import { MezoWrapper } from "../contracts/partners/tokenWrappers/MezoWrapper.sol";

contract DeployMezoWrapper is BaseScript {
// forge script scripts/deployMezoWrapper.s.sol --rpc-url mezo --sender 0xA9DdD91249DFdd450E81E1c56Ab60E1A62651701 --broadcast --verify
function run() public {
uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);

address distributionCreator = 0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd;

// ------------------------------------------------------------------------
// TO EDIT
address underlying = 0x7B7c000000000000000000000000000000000001;
address holder = 0x6b57b0Ef5594a5820fD473353180442764d8601D;
address mezoStaking = 0xb90fdAd3DFD180458D62Cc6acedc983D78E20122;
uint256 lockDuration = 0;
string memory name = "veMEZO (wrapped)";
string memory symbol = "veMEZO";
// ------------------------------------------------------------------------

MezoWrapper wrapper = new MezoWrapper(
underlying,
distributionCreator,
holder,
mezoStaking,
lockDuration,
name,
symbol
);

console.log("MezoWrapper:", address(wrapper));

vm.stopBroadcast();
}
}
6 changes: 3 additions & 3 deletions scripts/deployWETH9.s.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.17;

import {Script} from "forge-std/Script.sol";
import {WETH9} from "../contracts/partners/tokenWrappers/weth9.sol";
import { Script } from "forge-std/Script.sol";
import { WETH9 } from "../contracts/partners/tokenWrappers/weth9.sol";

contract DeployWETH9 is Script {
WETH9 public weth9;
Expand All @@ -12,7 +12,7 @@ contract DeployWETH9 is Script {
DEPLOYER_PRIVATE_KEY = vm.envUint("DEPLOYER_PRIVATE_KEY");
vm.startBroadcast(DEPLOYER_PRIVATE_KEY);

weth9 = new WETH9();
weth9 = new WETH9();

vm.stopBroadcast();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,17 +166,15 @@ contract Test_ClaimFeeTokenWrapperImmutable_ClaimPath is ClaimFeeTokenWrapperImm

contract Test_ClaimFeeTokenWrapperImmutable_Burn is ClaimFeeTokenWrapperImmutableTest {
function test_RevertWhen_NotHolderOrGovernor() public {
vm.prank(alice);
wrapper.mint(alice, 10 ether);
deal(address(wrapper), alice, 10 ether);

vm.expectRevert(Errors.NotAllowed.selector);
vm.prank(bob);
wrapper.burn(alice, 5 ether);
}

function test_Success_Holder() public {
vm.prank(alice);
wrapper.mint(alice, 10 ether);
deal(address(wrapper), alice, 10 ether);

vm.prank(alice);
wrapper.burn(alice, 5 ether);
Expand All @@ -185,8 +183,7 @@ contract Test_ClaimFeeTokenWrapperImmutable_Burn is ClaimFeeTokenWrapperImmutabl
}

function test_Success_Governor() public {
vm.prank(alice);
wrapper.mint(alice, 10 ether);
deal(address(wrapper), alice, 10 ether);

vm.prank(governor);
wrapper.burn(alice, 3 ether);
Expand Down
Loading
Loading