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
37237de
commit 9e55cec
Showing
10 changed files
with
248 additions
and
106 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
Submodule openzeppelin-contracts
added at
a241f0
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,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 { IMTokenYield } from "./interfaces/IMTokenYield.sol"; | ||
import { IWrappedM } from "./interfaces/IWrappedM.sol"; | ||
|
||
contract MTokenYield is IMTokenYield, 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("MYield by M^0", "ysM") { | ||
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
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,35 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
|
||
pragma solidity 0.8.23; | ||
|
||
import { IERC721Metadata } from "../../lib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Metadata.sol"; | ||
|
||
interface IMTokenYield is IERC721Metadata { | ||
/* ============ Events ============ */ | ||
|
||
/* ============ Custom Errors ============ */ | ||
|
||
error NotWrappedM(); | ||
|
||
error NotOwner(); | ||
|
||
error LengthMismatch(); | ||
|
||
error ExcessAmount(); | ||
|
||
/* ============ Interactive Functions ============ */ | ||
|
||
function mint(address account, uint256 amount) external returns (uint256 tokenId); | ||
|
||
function burn(address account, uint256 tokenId) external returns (uint256 baseAmount, uint256 yield); | ||
|
||
function claim(address account_, uint256 tokenId) external returns (uint256 yield); | ||
|
||
function reshape( | ||
address account, | ||
uint256[] calldata tokenIds, | ||
uint256[] calldata amounts | ||
) external returns (uint256[] memory newTokenIds, uint256 yield); | ||
|
||
/* ============ View/Pure Functions ============ */ | ||
} |
Oops, something went wrong.