/**
*Submitted for verification at BscScan.com on 2022-02-19
*/
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol
/**
* @title ERC20 interface
* @dev see https://eips.ethereum.org/EIPS/eip-20
*/
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function burnbyContract(uint256 _amount) external;
function withdrawStakingReward(address _address,uint256 _amount) external;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: openzeppelin-solidity/contracts/math/SafeMath.sol
/**
* @title SafeMath
* @dev Unsigned math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two unsigned integers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two unsigned integers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
// File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* https://eips.ethereum.org/EIPS/eip-20
*
* This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for
* all accounts just by listening to said events. Note that this isn't required by the specification, and other
* compliant implementations may not do it.
*/
interface IERC721 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool _approved) external;
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
}
contract Ownable {
address public _owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(_owner == msg.sender, "Ownable: caller is not the owner");
_;
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(
newOwner != address(0),
"Ownable: new owner is the zero address"
);
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// File: contracts/EDM.sol
contract SYAC_NFT_Staking is Ownable{
//-----------------------------------------
//Variables
using SafeMath for uint256;
IERC721 NFTToken;
IERC20 token;
//-----------------------------------------
//Structs
struct userInfo
{
uint256 totlaWithdrawn;
uint256 withdrawable;
uint256 totalStaked;
uint256 availableToWithdraw;
}
//-----------------------------------------
//Mappings
mapping(address => mapping(uint256 => uint256)) public stakingTime;
mapping(address => userInfo ) public User;
mapping(address => uint256[] ) public Tokenid;
mapping(address=>uint256) public totalStakedNft;
mapping(uint256=>bool) public alreadyAwarded;
mapping(address=>mapping(uint256=>uint256)) public depositTime;
uint256 time= 1 days;
uint256 lockingtime= 1 days;
uint256 public firstReward =300 ether;
//-----------------------------------------
//constructor
constructor(IERC721 _NFTToken,IERC20 _token)
{
NFTToken =_NFTToken;
token=_token;
}
//-----------------------------------------
//Stake NFTS to earn Reward in coca coin
function Stake(uint256[] memory tokenId) external
{
for(uint256 i=0;i<tokenId.length;i++){
require(NFTToken.ownerOf(tokenId[i]) == msg.sender,"nft not found");
NFTToken.transferFrom(msg.sender,address(this),tokenId[i]);
Tokenid[msg.sender].push(tokenId[i]);
stakingTime[msg.sender][tokenId[i]]=block.timestamp;
if(!alreadyAwarded[tokenId[i]]){
depositTime[msg.sender][tokenId[i]]=block.timestamp;
}
}
User[msg.sender].totalStaked+=tokenId.length;
totalStakedNft[msg.sender]+=tokenId.length;
}
//-----------------------------------------
//check your Reward By this function
function rewardOfUser(address Add) public view returns(uint256)
{
uint256 RewardToken;
for(uint256 i = 0 ; i < Tokenid[Add].length ; i++){
if(Tokenid[Add][i] > 0)
{
if((block.timestamp>depositTime[Add][Tokenid[Add][i]]+1 days)&&!alreadyAwarded[Tokenid[Add][i]]){
RewardToken+=firstReward;
}
RewardToken += (((block.timestamp - (stakingTime[Add][Tokenid[Add][i]])).div(time)))*15 ether;
}
}
return RewardToken+User[Add].availableToWithdraw;
}
//-----------------------------------------
//Returns all NFT user staked
function userStakedNFT(address _staker)public view returns(uint256[] memory)
{
return Tokenid[_staker];
}
//-----------------------------------------
//Withdraw your reward
function WithdrawReward() public
{
uint256 reward = rewardOfUser(msg.sender);
require(reward > 0,"you don't have reward yet!");
require(token.balanceOf(address(token))>=reward,"Contract Don't have enough tokens to give reward");
token.withdrawStakingReward(msg.sender,reward);
for(uint8 i=0;i<Tokenid[msg.sender].length;i++){
stakingTime[msg.sender][Tokenid[msg.sender][i]]=block.timestamp;
}
User[msg.sender].totlaWithdrawn += reward;
User[msg.sender].availableToWithdraw = 0;
for(uint256 i = 0 ; i < Tokenid[msg.sender].length ; i++){
alreadyAwarded[Tokenid[msg.sender][i]]=true;
}
}
//-----------------------------------------
//Get index by Value
function find(uint value) internal view returns(uint) {
uint i = 0;
while (Tokenid[msg.sender][i] != value) {
i++;
}
return i;
}
//-----------------------------------------
//User have to pass tokenID to unstake token
function unstake(uint256[] memory _tokenId) external
{
User[msg.sender].availableToWithdraw+=rewardOfUser(msg.sender);
for(uint256 i=0;i<_tokenId.length;i++){
if(rewardOfUser(msg.sender)>0)alreadyAwarded[_tokenId[i]]=true;
uint256 _index=find(_tokenId[i]);
require(Tokenid[msg.sender][_index] ==_tokenId[i] ,"NFT with this _tokenId not found");
NFTToken.transferFrom(address(this),msg.sender,_tokenId[i]);
delete Tokenid[msg.sender][_index];
Tokenid[msg.sender][_index]=Tokenid[msg.sender][Tokenid[msg.sender].length-1];
stakingTime[msg.sender][_tokenId[i]]=0;
Tokenid[msg.sender].pop();
}
User[msg.sender].totalStaked-=_tokenId.length;
totalStakedNft[msg.sender]>0?totalStakedNft[msg.sender]-=_tokenId.length:totalStakedNft[msg.sender]=0;
}
function isStaked(address _stakeHolder)public view returns(bool){
if(totalStakedNft[_stakeHolder]>0){
return true;
}else{
return false;
}
}
//-----------------------------------------
//Only Owner
function WithdrawToken()public onlyOwner{
require(token.transfer(msg.sender,token.balanceOf(address(this))),"Token transfer Error!");
}
function changeFirstReward(uint256 _reward)external onlyOwner{
firstReward=_reward;
}
}
{
"compilationTarget": {
"SYAC_NFT_Staking.sol": "SYAC_NFT_Staking"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"contract IERC721","name":"_NFTToken","type":"address"},{"internalType":"contract IERC20","name":"_token","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"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":"tokenId","type":"uint256[]"}],"name":"Stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"Tokenid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"User","outputs":[{"internalType":"uint256","name":"totlaWithdrawn","type":"uint256"},{"internalType":"uint256","name":"withdrawable","type":"uint256"},{"internalType":"uint256","name":"totalStaked","type":"uint256"},{"internalType":"uint256","name":"availableToWithdraw","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WithdrawReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"WithdrawToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"_owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"alreadyAwarded","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_reward","type":"uint256"}],"name":"changeFirstReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"depositTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"firstReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_stakeHolder","type":"address"}],"name":"isStaked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"Add","type":"address"}],"name":"rewardOfUser","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakingTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"totalStakedNft","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":"_tokenId","type":"uint256[]"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"}],"name":"userStakedNFT","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"}]