编译器
0.8.22+commit.4fc1097e
文件 1 的 7:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 2 的 7:IERC165.sol
pragma solidity ^0.8.20;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 3 的 7:IERC20.sol
pragma solidity ^0.8.20;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
文件 4 的 7:IERC721.sol
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
文件 5 的 7:MamiStakeV3.sol
pragma solidity ^0.8.22;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
interface RewardsToken {
function mint(address, uint256) external;
}
contract MamiStakeV3 is Ownable, ReentrancyGuard {
struct Pool {
address nftAddress;
address tokenAddress;
uint256 tokenAmount;
uint256 start;
uint256 rate;
address rewardsTokenAddress;
uint256 stakedAmount;
bool passRequired;
uint256[] sharePoolIds;
uint256 max;
}
struct User {
uint256 last;
uint256 amount;
uint256 remain;
}
mapping(uint256 => Pool) public poolInfos;
mapping(uint256 => address[]) public poolAddrs;
mapping(uint256 => mapping(address => User)) public poolUsers;
mapping(uint256 => mapping(uint256 => address)) public passUsed;
mapping(uint256 => mapping(uint256 => address)) public tokenUsed;
mapping(uint256 => mapping(uint256 => uint256)) public tokenPassRelation;
mapping(uint256 => uint256) public totalClaimed;
IERC721 public passAddress;
address public foundation;
constructor() Ownable(msg.sender) {
foundation = 0xB03167F37319F2C67Dd3062fc1482044205484d1;
address tokenAddress = 0x8983CF891867942d06AD6CEb9B9002de860E202d;
address nftAddress = 0xbc77f3A44f19113845B2870ce9E72f612D77DC17;
passAddress = IERC721(0xd6591bBb8A4867cEa5ec732f9c30379C4A8bE730);
uint256[] memory sharePoolIds0 = new uint256[](1);
sharePoolIds0[0] = 1;
uint256[] memory sharePoolIds1 = new uint256[](1);
sharePoolIds1[0] = 0;
setPool(
0,
nftAddress,
tokenAddress,
40000 ether,
block.number,
8.8 ether,
tokenAddress,
false,
sharePoolIds0,
50000000 ether
);
setPool(
1,
nftAddress,
tokenAddress,
40000 ether,
block.number,
10 ether,
tokenAddress,
true,
sharePoolIds1,
50000000 ether
);
}
function stake(
uint256 poolId,
uint256[] calldata stakeTokenIds,
uint256[] calldata passTokenIds
) external checkPool(poolId) {
Pool storage pool = poolInfos[poolId];
claim(poolId);
bool userExist;
for (uint256 i = 0; i < poolAddrs[poolId].length; i++) {
if (poolAddrs[poolId][i] == msg.sender) {
userExist = true;
break;
}
}
if (!userExist) {
genUser(poolId);
}
for (uint256 i = 0; i < stakeTokenIds.length; i++) {
require(
msg.sender ==
IERC721(pool.nftAddress).ownerOf(stakeTokenIds[i]),
"You dont owner this nft"
);
require(
tokenUsed[poolId][stakeTokenIds[i]] == address(0),
"Stake token id used"
);
for (uint256 y = 0; y < pool.sharePoolIds.length; y++) {
require(
tokenUsed[pool.sharePoolIds[y]][stakeTokenIds[i]] ==
address(0),
"The nft already staked"
);
}
tokenUsed[poolId][stakeTokenIds[i]] = msg.sender;
if (pool.passRequired) {
require(
passTokenIds.length == stakeTokenIds.length,
"Invalid length pass and token ids"
);
require(
msg.sender == IERC721(passAddress).ownerOf(passTokenIds[i]),
"You dont owner this pass"
);
require(
passUsed[poolId][passTokenIds[i]] == address(0),
"Stake pass id used"
);
for (uint256 y = 0; y < pool.sharePoolIds.length; y++) {
require(
passUsed[pool.sharePoolIds[y]][passTokenIds[i]] ==
address(0),
"The pass already staked"
);
}
passUsed[poolId][passTokenIds[i]] = msg.sender;
tokenPassRelation[poolId][stakeTokenIds[i]] = passTokenIds[i];
}
}
poolUsers[poolId][msg.sender].amount += stakeTokenIds.length;
pool.stakedAmount += stakeTokenIds.length;
IERC20(pool.tokenAddress).transferFrom(
msg.sender,
address(this),
pool.tokenAmount * stakeTokenIds.length
);
}
function genUser(uint256 poolId) private {
poolUsers[poolId][msg.sender] = User(block.number, 0, 0);
poolAddrs[poolId].push(msg.sender);
}
function unStake(
uint256 poolId,
uint256[] calldata unStakeTokenIds
) external checkPool(poolId) {
Pool storage pool = poolInfos[poolId];
claim(poolId);
for (uint256 i = 0; i < unStakeTokenIds.length; i++) {
require(
tokenUsed[poolId][unStakeTokenIds[i]] == msg.sender,
"Stake token id not used by you"
);
tokenUsed[poolId][unStakeTokenIds[i]] = address(0);
if (pool.passRequired) {
uint256 passTokenId = tokenPassRelation[poolId][
unStakeTokenIds[i]
];
passUsed[poolId][passTokenId] = address(0);
}
}
poolUsers[poolId][msg.sender].amount -= unStakeTokenIds.length;
pool.stakedAmount -= unStakeTokenIds.length;
IERC20(pool.tokenAddress).transfer(
msg.sender,
pool.tokenAmount * unStakeTokenIds.length
);
}
function claim(uint256 poolId) public checkPool(poolId) {
_sync(poolId);
Pool memory pool = poolInfos[poolId];
User storage user = poolUsers[poolId][msg.sender];
if (totalClaimed[poolId] + user.remain >= pool.max) {
user.remain = pool.max - totalClaimed[poolId];
}
if (user.remain > 0) {
uint256 fee = user.remain / 20;
RewardsToken(pool.rewardsTokenAddress).mint(foundation, fee);
RewardsToken(pool.rewardsTokenAddress).mint(
msg.sender,
user.remain - fee
);
totalClaimed[poolId] += user.remain;
user.remain = 0;
}
}
function claimAll(uint256[] calldata poolIds) public {
for (uint256 i = 0; i < poolIds.length; i++) {
claim(poolIds[i]);
}
}
function setPool(
uint256 poolId,
address nftAddress,
address tokenAddress,
uint256 tokenAmount,
uint256 start,
uint256 rate,
address rewardsTokenAddress,
bool passRequired,
uint256[] memory sharePoolIds,
uint256 max
) public onlyOwner {
poolInfos[poolId] = Pool(
nftAddress,
tokenAddress,
tokenAmount,
start,
rate,
rewardsTokenAddress,
0,
passRequired,
sharePoolIds,
max
);
}
modifier checkPool(uint256 poolId) {
Pool memory pool = poolInfos[poolId];
require(pool.tokenAddress != address(0), "Pool not exist");
require(pool.start <= block.number, "Pool not start");
_;
}
function _sync(uint256 poolId) private {
uint256 pendingRemain = getPendingRemain(poolId, msg.sender);
User storage user = poolUsers[poolId][msg.sender];
user.remain += pendingRemain;
user.last = block.number;
}
function getPendingRemain(
uint256 poolId,
address account
) public view returns (uint256) {
uint256 pendingRemain = 0;
Pool memory pool = poolInfos[poolId];
if (pool.stakedAmount > 0) {
User memory user = poolUsers[poolId][account];
uint256 last = pool.start > user.last ? pool.start : user.last;
pendingRemain =
((block.number - last) * user.amount * pool.rate) /
pool.stakedAmount;
}
return pendingRemain;
}
function getSharedTokenIds(
uint256 poolId
) public view returns (uint256[] memory) {
return poolInfos[poolId].sharePoolIds;
}
}
文件 6 的 7:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 7 的 7:ReentrancyGuard.sol
pragma solidity ^0.8.20;
abstract contract ReentrancyGuard {
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
_status = ENTERED;
}
function _nonReentrantAfter() private {
_status = NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
{
"compilationTarget": {
"contracts/MamiStakeV3.sol": "MamiStakeV3"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"poolIds","type":"uint256[]"}],"name":"claimAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"foundation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"getPendingRemain","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"}],"name":"getSharedTokenIds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"passAddress","outputs":[{"internalType":"contract IERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"passUsed","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"poolAddrs","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"poolInfos","outputs":[{"internalType":"address","name":"nftAddress","type":"address"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"rate","type":"uint256"},{"internalType":"address","name":"rewardsTokenAddress","type":"address"},{"internalType":"uint256","name":"stakedAmount","type":"uint256"},{"internalType":"bool","name":"passRequired","type":"bool"},{"internalType":"uint256","name":"max","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"poolUsers","outputs":[{"internalType":"uint256","name":"last","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"remain","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"},{"internalType":"address","name":"nftAddress","type":"address"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"rate","type":"uint256"},{"internalType":"address","name":"rewardsTokenAddress","type":"address"},{"internalType":"bool","name":"passRequired","type":"bool"},{"internalType":"uint256[]","name":"sharePoolIds","type":"uint256[]"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"setPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"},{"internalType":"uint256[]","name":"stakeTokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"passTokenIds","type":"uint256[]"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenPassRelation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenUsed","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"totalClaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"poolId","type":"uint256"},{"internalType":"uint256[]","name":"unStakeTokenIds","type":"uint256[]"}],"name":"unStake","outputs":[],"stateMutability":"nonpayable","type":"function"}]