Skip to content

Commit

Permalink
feat: add sonic fragment
Browse files Browse the repository at this point in the history
  • Loading branch information
sogipec committed Jan 14, 2025
1 parent 929830e commit d24952f
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 3 deletions.
6 changes: 3 additions & 3 deletions contracts/partners/tokenWrappers/PufferPointTokenWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,17 @@ contract PufferPointTokenWrapper is UUPSHelper, ERC20Upgradeable {
function initialize(
address _underlying,
uint32 _cliffDuration,
IAccessControlManager _core,
IAccessControlManager _accessControlManager,
address _distributionCreator
) public initializer {
__ERC20_init(
string.concat("Merkl Token Wrapper - ", IERC20Metadata(_underlying).name()),
string.concat("mtw", IERC20Metadata(_underlying).symbol())
);
__UUPSUpgradeable_init();
if (address(_core) == address(0)) revert Errors.ZeroAddress();
if (address(_accessControlManager) == address(0)) revert Errors.ZeroAddress();
underlying = _underlying;
core = _core;
core = _accessControlManager;
cliffDuration = _cliffDuration;
distributionCreator = _distributionCreator;
distributor = IDistributionCreator(_distributionCreator).distributor();
Expand Down
80 changes: 80 additions & 0 deletions contracts/partners/tokenWrappers/SonicFragment.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// 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 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

import { IAccessControlManager } from "../../interfaces/IAccessControlManager.sol";
import { Errors } from "../../utils/Errors.sol";

/// @title SonicFragment
/// @notice Contract for Sonic fragments which can be converted upon activation into S tokens
/// @author Angle Labs, Inc.
contract SonicFragment is ERC20 {
using SafeERC20 for IERC20;

/// @notice Contract handling access control
IAccessControlManager public immutable accessControlManager;
/// @notice Address for the S token
address public immutable sToken;

/// @notice Amount of S tokens sent on the contract at the activation of redemption
/// @dev Used to compute the exchange rate between fragments and S tokens
uint128 public sTokenAmount;
/// @notice Total supply of the contract
/// @dev Needs to be stored to compute the exchange rate between fragments and sTokens
uint120 public supply;
/// @notice Whether redemption for S tokens has been activated or not
uint8 public contractSettled;

constructor(
address _accessControlManager,
address recipient,
address _sToken,
uint256 _totalSupply,
string memory _name,
string memory _symbol
) ERC20(_name, _symbol) {
// Zero address check
if (_sToken == address(0)) revert Errors.ZeroAddress();
IAccessControlManager(_accessControlManager).isGovernor(msg.sender);
sToken = _sToken;
accessControlManager = IAccessControlManager(_accessControlManager);
supply = uint120(_totalSupply);
_mint(recipient, _totalSupply);
}

// ================================= MODIFIERS =================================

/// @notice Checks whether the `msg.sender` has the governor role
modifier onlyGovernor() {
if (!accessControlManager.isGovernor(msg.sender)) revert Errors.NotAllowed();
_;
}

/// @notice Activates the contract settlement and enables redemption of fragments into S
/// @dev Can only be called once
function settleContract(uint256 _sTokenAmount) external onlyGovernor {
if (contractSettled > 0) revert Errors.NotAllowed();
contractSettled = 1;
IERC20(sToken).safeTransferFrom(msg.sender, address(this), sTokenAmount);
sTokenAmount = uint128(_sTokenAmount);
}

/// @notice Recovers leftover tokens after sometime
function recover(uint256 amount, address recipient) external onlyGovernor {
IERC20(sToken).safeTransfer(recipient, amount);
sTokenAmount = 0;
}

/// @notice Redeems fragments against S based on a predefined exchange rate
function redeem(uint256 amount, address recipient) external returns (uint256 amountToSend) {
uint128 _sTokenAmount = sTokenAmount;
if (_sTokenAmount == 0) revert Errors.NotAllowed();
_burn(msg.sender, amount);
amountToSend = (amount * _sTokenAmount) / supply;
IERC20(sToken).safeTransfer(recipient, amountToSend);
}
}

0 comments on commit d24952f

Please sign in to comment.