generated from m0-foundation/foundry-template
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
939ca6e
commit 82e61af
Showing
14 changed files
with
358 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,17 @@ | ||
{ | ||
"plugins": ["prettier-plugin-solidity"], | ||
"plugins": [ | ||
"prettier-plugin-solidity" | ||
], | ||
"overrides": [ | ||
{ | ||
"files": "*.sol", | ||
"options": { | ||
"bracketSpacing": true, | ||
"compiler": "0.8.25", | ||
"compiler": "0.8.23", | ||
"parser": "solidity-parse", | ||
"printWidth": 120, | ||
"tabWidth": 4, | ||
} | ||
} | ||
] | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule openzeppelin-contracts
added at
dd1e89
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
|
||
pragma solidity 0.8.23; | ||
|
||
import { IERC20 } from "../lib/common/src/interfaces/IERC20.sol"; | ||
|
||
import { ERC20Extended } from "../lib/common/src/ERC20Extended.sol"; | ||
|
||
import { IMTokenLike } from "./interfaces/IMTokenLike.sol"; | ||
import { IWrappedMYield } from "./interfaces/IWrappedMYield.sol"; | ||
import { IWrappedM } from "./interfaces/IWrappedM.sol"; | ||
|
||
contract WrappedM is IWrappedM, ERC20Extended { | ||
/* ============ Variables ============ */ | ||
|
||
address public immutable mToken; | ||
address public immutable wrappedMYield; | ||
|
||
uint256 public totalSupply; | ||
|
||
mapping(address account => uint256 balance) public balanceOf; | ||
|
||
/* ============ Modifiers ============ */ | ||
|
||
modifier onlyEarner() { | ||
if (!IMTokenLike(mToken).isEarning(msg.sender)) revert NotEarner(); | ||
|
||
_; | ||
} | ||
|
||
modifier onlyWrappedMYield() { | ||
if (msg.sender != wrappedMYield) revert NotWrappedMYield(); | ||
|
||
_; | ||
} | ||
|
||
/* ============ Constructor ============ */ | ||
|
||
constructor(address mToken_, address mYield_) ERC20Extended("Wrapped M by M^0", "wM", 6) { | ||
mToken = mToken_; | ||
wrappedMYield = mYield_; | ||
} | ||
|
||
/* ============ Interactive Functions ============ */ | ||
|
||
function deposit(address account_, uint256 amount_) external onlyEarner returns (uint256 wrappedMYieldTokenId_) { | ||
emit Transfer(address(0), account_, amount_); | ||
|
||
balanceOf[account_] += amount_; | ||
totalSupply += amount_; | ||
|
||
wrappedMYieldTokenId_ = IWrappedMYield(wrappedMYield).mint(account_, amount_); | ||
|
||
IERC20(mToken).transferFrom(msg.sender, address(this), amount_); | ||
} | ||
|
||
function withdraw( | ||
address account_, | ||
uint256 wrappedMYieldTokenId_ | ||
) external returns (uint256 amount_, uint256 yield_) { | ||
( amount_, yield_ ) = IWrappedMYield(wrappedMYield).burn(msg.sender, wrappedMYieldTokenId_); | ||
|
||
emit Transfer(account_, address(0), amount_); | ||
|
||
balanceOf[account_] -= amount_; | ||
totalSupply -= amount_; | ||
|
||
IERC20(mToken).transfer(account_, amount_ + yield_); | ||
} | ||
|
||
/* ============ View/Pure Functions ============ */ | ||
|
||
/* ============ Internal Interactive Functions ============ */ | ||
|
||
function _transfer(address sender_, address recipient_, uint256 amount_) internal override { | ||
emit Transfer(sender_, recipient_, amount_); | ||
|
||
balanceOf[sender_] -= amount_; | ||
balanceOf[recipient_] += amount_; | ||
} | ||
|
||
/* ============ Internal View/Pure Functions ============ */ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
|
||
pragma solidity 0.8.23; | ||
|
||
import { UIntMath } from "../lib/common/src/libs/UIntMath.sol"; | ||
|
||
import { ERC721 } from "../lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol"; | ||
|
||
import { IMTokenLike } from "./interfaces/IMTokenLike.sol"; | ||
import { IWrappedMYield } from "./interfaces/IWrappedMYield.sol"; | ||
import { IWrappedM } from "./interfaces/IWrappedM.sol"; | ||
|
||
contract WrappedMYield is IWrappedMYield, ERC721 { | ||
struct YieldBasis { | ||
uint112 amount; | ||
uint128 index; | ||
} | ||
|
||
/* ============ Variables ============ */ | ||
|
||
uint56 internal constant _EXP_SCALED_ONE = 1e12; | ||
|
||
address public immutable mToken; | ||
address public immutable wrappedM; | ||
|
||
uint256 internal _tokenCount; | ||
|
||
mapping(uint256 tokenId => YieldBasis yieldBasis) internal _yieldBases; | ||
|
||
/* ============ Modifiers ============ */ | ||
|
||
modifier onlyWrappedM() { | ||
if (msg.sender != wrappedM) revert NotWrappedM(); | ||
|
||
_; | ||
} | ||
|
||
/* ============ Constructor ============ */ | ||
|
||
constructor(address mToken_, address wrappedM_) ERC721("Wrapped M Yield by M^0", "wyM") { | ||
mToken = mToken_; | ||
wrappedM = wrappedM_; | ||
} | ||
|
||
/* ============ Interactive Functions ============ */ | ||
|
||
function mint(address account_, uint256 amount_) external onlyWrappedM returns (uint256 tokenId_) { | ||
return _mint(account_, UIntMath.safe240(amount_), IMTokenLike(mToken).currentIndex()); | ||
} | ||
|
||
function burn( | ||
address account_, | ||
uint256 tokenId_ | ||
) external onlyWrappedM returns (uint256 amount_, uint256 yield_) { | ||
return _burn(account_, tokenId_, IMTokenLike(mToken).currentIndex()); | ||
} | ||
|
||
function reshape( | ||
address account_, | ||
uint256[] calldata tokenIds_, | ||
uint256[] calldata amounts_ | ||
) external returns (uint256[] memory newTokenIds_) { | ||
uint128 currentIndex_ = IMTokenLike(mToken).currentIndex(); | ||
|
||
uint240 totalAmount_; | ||
uint240 totalYield_; | ||
|
||
for (uint256 index_; index_ < tokenIds_.length; ++index_) { | ||
(uint240 amount_, uint240 yield_) = _burn(msg.sender, tokenIds_[index_], currentIndex_); | ||
|
||
totalAmount_ += amount_; | ||
totalYield_ += yield_; | ||
} | ||
|
||
uint128 blendedIndex_ = _getIndex(totalAmount_, _getPrincipalAmount(totalAmount_ + totalYield_, currentIndex_)); | ||
|
||
newTokenIds_ = new uint256[](amounts_.length + 1); | ||
|
||
for (uint256 index_; index_ < amounts_.length; ++index_) { | ||
uint240 amount_ = (index_ == amounts_.length - 1) | ||
? totalAmount_ | ||
: UIntMath.safe240(amounts_[index_]); | ||
|
||
newTokenIds_[index_] = _mint(account_, amount_, blendedIndex_); | ||
|
||
totalAmount_ -= amount_; | ||
} | ||
} | ||
|
||
/* ============ View/Pure Functions ============ */ | ||
|
||
function getYieldBasis(uint256 tokenId_) public view returns (uint112 amount_, uint128 index_) { | ||
YieldBasis storage yieldBasis_ = _yieldBases[tokenId_]; | ||
|
||
return (yieldBasis_.amount, yieldBasis_.index); | ||
} | ||
|
||
/* ============ Internal Interactive Functions ============ */ | ||
|
||
function _mint(address account_, uint240 amount_, uint128 currentIndex_) internal returns (uint256 tokenId_) { | ||
_yieldBases[tokenId_ = ++_tokenCount] = YieldBasis({ | ||
amount: _getPrincipalAmount(UIntMath.safe240(amount_), currentIndex_), | ||
index: currentIndex_ | ||
}); | ||
|
||
_mint(account_, tokenId_); | ||
} | ||
|
||
function _burn( | ||
address account_, | ||
uint256 tokenId_, | ||
uint128 currentIndex_ | ||
) internal returns (uint240 amount_, uint240 yield_) { | ||
_revertIfNotOwner(account_, tokenId_); | ||
|
||
_burn(tokenId_); | ||
|
||
(uint112 basisAmount_, uint128 basisIndex_) = getYieldBasis(tokenId_); | ||
|
||
amount_ = _getPresentAmount(basisAmount_, basisIndex_); | ||
yield_ = _getPresentAmount(basisAmount_, currentIndex_) - amount_; | ||
|
||
delete _yieldBases[tokenId_]; | ||
} | ||
|
||
/* ============ Internal View/Pure Functions ============ */ | ||
|
||
function _revertIfNotOwner(address account_, uint256 tokenId_) internal view { | ||
if (ownerOf(tokenId_) != account_) revert NotOwner(); | ||
} | ||
|
||
function _multiplyDown(uint112 x_, uint128 y_) internal pure returns (uint240) { | ||
unchecked { | ||
return uint240((uint256(x_) * y_) / _EXP_SCALED_ONE); | ||
} | ||
} | ||
|
||
function _divideDown128(uint240 x_, uint128 y_) internal pure returns (uint112) { | ||
if (y_ == 0) revert DivisionByZero(); | ||
|
||
unchecked { | ||
return UIntMath.safe112((uint256(x_) * _EXP_SCALED_ONE) / y_); | ||
} | ||
} | ||
|
||
function _divideDown112(uint240 x_, uint112 y_) internal pure returns (uint128) { | ||
if (y_ == 0) revert DivisionByZero(); | ||
|
||
unchecked { | ||
return UIntMath.safe128((uint256(x_) * _EXP_SCALED_ONE) / y_); | ||
} | ||
} | ||
|
||
function _getIndex(uint240 presentAmount_, uint112 principalAmount) internal pure returns (uint128) { | ||
return _divideDown112(presentAmount_, principalAmount); | ||
} | ||
|
||
function _getPresentAmount(uint112 principalAmount_, uint128 index_) internal pure returns (uint240) { | ||
return _multiplyDown(principalAmount_, index_); | ||
} | ||
|
||
function _getPrincipalAmount(uint240 presentAmount_, uint128 index_) internal pure returns (uint112) { | ||
return _divideDown128(presentAmount_, index_); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
|
||
pragma solidity 0.8.23; | ||
|
||
interface IMTokenLike { | ||
/* ============ View/Pure Functions ============ */ | ||
|
||
function currentIndex() external view returns (uint128); | ||
|
||
function isEarning(address account) external view returns (bool); | ||
} |
Oops, something went wrong.