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 9be838f
Showing
14 changed files
with
355 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
a241f0
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,86 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
|
||
pragma solidity 0.8.23; | ||
|
||
import { UIntMath } from "../lib/common/src/libs/UIntMath.sol"; | ||
|
||
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 mYieldTokenId_) { | ||
emit Transfer(address(0), account_, amount_); | ||
|
||
balanceOf[account_] += amount_; | ||
totalSupply += amount_; | ||
|
||
mYieldTokenId_ = IWrappedMYield(wrappedMYield).mint(account_, amount_); | ||
|
||
IERC20(mToken).transferFrom(msg.sender, address(this), amount_); | ||
} | ||
|
||
function withdraw(address account_, uint256 mYieldTokenId_) external returns (uint256 baseAmount_, uint256 yield_) { | ||
(baseAmount_, yield_) = IWrappedMYield(wrappedMYield).burn(msg.sender, mYieldTokenId_); | ||
|
||
emit Transfer(account_, address(0), baseAmount_); | ||
|
||
balanceOf[account_] -= baseAmount_; | ||
totalSupply -= baseAmount_; | ||
|
||
IERC20(mToken).transfer(account_, baseAmount_ + yield_); | ||
} | ||
|
||
function extract(address account_, uint256 amount_) external onlyWrappedMYield { | ||
IERC20(mToken).transfer(account_, amount_); | ||
} | ||
|
||
/* ============ 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,155 @@ | ||
// 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 { | ||
// TODO: Might be a way to make this a uint112 and uint128 for one slot. | ||
struct YieldBase { | ||
uint240 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 => YieldBase yieldBase) 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_) { | ||
tokenId_ = ++_tokenCount; | ||
|
||
_yieldBases[tokenId_] = YieldBase({ | ||
amount: UIntMath.safe240(amount_), | ||
index: IMTokenLike(mToken).currentIndex() | ||
}); | ||
|
||
_mint(account_, tokenId_); | ||
} | ||
|
||
function burn( | ||
address account_, | ||
uint256 tokenId_ | ||
) external onlyWrappedM returns (uint256 baseAmount_, uint256 yield_) { | ||
if (ownerOf(tokenId_) != account_) revert NotOwner(); | ||
|
||
_burn(tokenId_); | ||
|
||
YieldBase storage yieldBase_ = _yieldBases[tokenId_]; | ||
|
||
baseAmount_ = yieldBase_.amount; | ||
|
||
yield_ = _multiplyDown(yieldBase_.amount, yieldBase_.index - IMTokenLike(mToken).currentIndex()); | ||
|
||
delete _yieldBases[tokenId_]; | ||
} | ||
|
||
function claim(address account_, uint256 tokenId_) external returns (uint256 yield_) { | ||
if (ownerOf(tokenId_) != msg.sender) revert NotOwner(); | ||
|
||
YieldBase storage yieldBase_ = _yieldBases[tokenId_]; | ||
|
||
uint128 currentIndex_ = IMTokenLike(mToken).currentIndex(); | ||
|
||
yield_ = _multiplyDown(yieldBase_.amount, yieldBase_.index - currentIndex_); | ||
|
||
yieldBase_.index = currentIndex_; | ||
|
||
IWrappedM(wrappedM).extract(account_, yield_); | ||
} | ||
|
||
function reshape( | ||
address account_, | ||
uint256[] calldata tokenIds_, | ||
uint256[] calldata amounts_ | ||
) external returns (uint256[] memory newTokenIds_, uint256 yield_) { | ||
if (tokenIds_.length != amounts_.length) revert LengthMismatch(); | ||
|
||
uint128 currentIndex_ = IMTokenLike(mToken).currentIndex(); | ||
|
||
uint240 total_; | ||
|
||
for (uint256 index_; index_ < tokenIds_.length; ++index_) { | ||
uint256 tokenId_ = tokenIds_[index_]; | ||
|
||
if (ownerOf(tokenId_) != msg.sender) revert NotOwner(); | ||
|
||
_burn(tokenId_); | ||
|
||
YieldBase storage yieldBase_ = _yieldBases[tokenId_]; | ||
|
||
total_ += yieldBase_.amount; | ||
yield_ += _multiplyDown(yieldBase_.amount, yieldBase_.index - currentIndex_); | ||
|
||
delete _yieldBases[tokenId_]; | ||
} | ||
|
||
newTokenIds_ = new uint256[](tokenIds_.length); | ||
|
||
for (uint256 index_; index_ < newTokenIds_.length; ++index_) { | ||
uint256 tokenId_ = newTokenIds_[index_] = ++_tokenCount; | ||
uint240 amount_ = UIntMath.safe240(amounts_[index_]); | ||
|
||
_yieldBases[tokenId_] = YieldBase({ | ||
amount: amount_, | ||
index: currentIndex_ | ||
}); | ||
|
||
total_ -= amount_; | ||
|
||
_mint(account_, tokenId_); | ||
} | ||
|
||
if (total_ > 0) revert ExcessAmount(); | ||
|
||
IWrappedM(wrappedM).extract(account_, yield_); | ||
} | ||
|
||
/* ============ View/Pure Functions ============ */ | ||
|
||
function getYieldBase(uint256 tokenId_) external view returns (uint240 amount_, uint128 index_) { | ||
YieldBase storage yieldBase_ = _yieldBases[tokenId_]; | ||
|
||
amount_ = yieldBase_.amount; | ||
index_ = yieldBase_.index; | ||
} | ||
|
||
/* ============ Internal Interactive Functions ============ */ | ||
|
||
/* ============ Internal View/Pure Functions ============ */ | ||
|
||
function _multiplyDown(uint240 x_, uint128 index_) internal pure returns (uint240) { | ||
unchecked { | ||
return uint240((uint256(x_) * index_) / _EXP_SCALED_ONE); | ||
} | ||
} | ||
} |
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.