文件 1 的 5:Context.sol
pragma solidity ^0.8.0;
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 的 5:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 3 的 5:IERC721.sol
pragma solidity ^0.8.0;
import "../../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);
}
文件 4 的 5:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 5 的 5:WoAStaking.sol
pragma solidity ^0.8.7;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract WoAStaking is Ownable {
bool public stakingEnabled = false;
uint256 public totalStaked;
uint256 public baseReward = 5;
struct Stake {
address owner;
uint128 timestamp;
}
mapping(address => mapping(uint256 => Stake)) public vault;
mapping(address => mapping(address => uint256[])) public userStakeTokens;
mapping(address => bool) public isVaultContract;
mapping(address => uint256) public vaultMultiplier;
event NFTStaked(
address owner,
address tokenAddress,
uint256 tokenId,
uint256 value
);
event NFTUnstaked(
address owner,
address tokenAddress,
uint256 tokenId,
uint256 value
);
function stakeNfts(address _contract, uint256[] calldata tokenIds)
external
{
require(stakingEnabled == true, "Staking is not enabled yet.");
require(isVaultContract[_contract] == true, "Contract not allowed");
IERC721 nftContract = IERC721(_contract);
for (uint256 i; i < tokenIds.length; i++) {
require(
nftContract.ownerOf(tokenIds[i]) == msg.sender,
"You do not own this token"
);
nftContract.transferFrom(msg.sender, address(this), tokenIds[i]);
vault[_contract][tokenIds[i]] = Stake(
msg.sender,
uint128(block.timestamp)
);
userStakeTokens[msg.sender][_contract].push(tokenIds[i]);
emit NFTStaked(msg.sender, _contract, tokenIds[i], block.timestamp);
totalStaked++;
}
}
function unstakeNfts(address _contract, uint256[] calldata tokenIds)
external
{
require(stakingEnabled == true, "Staking is not enabled yet.");
require(isVaultContract[_contract] == true, "Contract not allowed");
IERC721 nftContract = IERC721(_contract);
for (uint256 i; i < tokenIds.length; i++) {
bool isTokenOwner = false;
uint256 tokenIndex = 0;
for (
uint256 j;
j < userStakeTokens[msg.sender][_contract].length;
j++
) {
if (userStakeTokens[msg.sender][_contract][j] == tokenIds[i]) {
isTokenOwner = true;
tokenIndex = j;
}
}
require(isTokenOwner == true, "You do not own this Token");
nftContract.transferFrom(address(this), msg.sender, tokenIds[i]);
delete vault[_contract][tokenIds[i]];
totalStaked--;
userStakeTokens[msg.sender][_contract][
tokenIndex
] = userStakeTokens[msg.sender][_contract][
userStakeTokens[msg.sender][_contract].length - 1
];
userStakeTokens[msg.sender][_contract].pop();
emit NFTUnstaked(
msg.sender,
_contract,
tokenIds[i],
block.timestamp
);
}
}
function getStakedTokens(address _user, address _contract)
external
view
returns (uint256[] memory)
{
return userStakeTokens[_user][_contract];
}
function getRewards(address _user, address[] calldata vaultContracts)
external
view
returns (uint256)
{
uint256 reward = 0;
uint256 i;
for (i = 0; i < vaultContracts.length; i++) {
reward += _calculateReward(_user, vaultContracts[i]);
}
return reward;
}
function addVault(address _contract, uint256 _multiplier) public onlyOwner {
require(isVaultContract[_contract] == false, "Contract already added");
isVaultContract[_contract] = true;
vaultMultiplier[_contract] = _multiplier;
}
function setStakingEnabled(bool _enabled) external onlyOwner {
stakingEnabled = _enabled;
}
function setBaseReward(uint256 _reward) external onlyOwner {
baseReward = _reward;
}
function setMultiplier(address _contract, uint256 _multiplier)
external
onlyOwner
{
require(isVaultContract[_contract] == true, "Contract not added");
vaultMultiplier[_contract] = _multiplier;
}
function _calculateReward(address _user, address _contract)
internal
view
returns (uint256)
{
uint256 reward = 0;
for (uint256 i; i < userStakeTokens[_user][_contract].length; i++) {
uint256 token = userStakeTokens[_user][_contract][i];
uint256 timeSinceStake = block.timestamp -
vault[_contract][token].timestamp;
uint256 rewardPerToken = baseReward * vaultMultiplier[_contract];
reward += timeSinceStake * rewardPerToken * 1e18;
}
return reward / 86400;
}
}
{
"compilationTarget": {
"WoAStaking.sol": "WoAStaking"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"NFTStaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"NFTUnstaked","type":"event"},{"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":"address","name":"_contract","type":"address"},{"internalType":"uint256","name":"_multiplier","type":"uint256"}],"name":"addVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"baseReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address[]","name":"vaultContracts","type":"address[]"}],"name":"getRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_contract","type":"address"}],"name":"getStakedTokens","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isVaultContract","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_reward","type":"uint256"}],"name":"setBaseReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"},{"internalType":"uint256","name":"_multiplier","type":"uint256"}],"name":"setMultiplier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_enabled","type":"bool"}],"name":"setStakingEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"stakeNfts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStaked","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":"address","name":"_contract","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"unstakeNfts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userStakeTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"vault","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint128","name":"timestamp","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"vaultMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]