[英]Getting saved block.timestamp in solidity Struct, but function is returning timestamp of current time
This is my 1st time asking a question on stackoverflow so i hope i provided all the information needed here这是我第一次在 stackoverflow 上提问,所以我希望我在这里提供了所有需要的信息
So i wrote a solidity smart contract to stake nfts, the staking part is working well but the unstaking part is not, im trying to write the unstaking function so that the owner will only be able to unstake their nfts if a certain time has passed, this time is a uint48 value stored in a solidity Struct of the stakeNft:所以我写了一个可靠的智能合约来质押 nfts,质押部分运行良好但解除质押部分不是,我试图编写解除质押功能,以便所有者只有在一定时间过去后才能解除质押他们的 nfts,这次是一个 uint48 值,存储在 stakeNft 的一个solidity Struct 中:
struct Stake {
uint24 tokenId;
uint48 timestamp; <------
address owner;
}
This is the staking function:这是质押功能:
function stake(uint256[] calldata tokenIds) external {
IERC721N nft = IERC721N(NftAddress);
uint256 tokenId;
totalStaked += tokenIds.length;
for (uint256 i = 0; i < tokenIds.length; i++) {
tokenId = tokenIds[i];
require(nft.ownerOf(tokenId) == msg.sender, "not your token");
require(vault[tokenId].tokenId == 0, "already staked");
nft.transferFrom(msg.sender, address(this), tokenId);
emit BlockStaked(msg.sender, tokenId, block.timestamp);
vault[tokenId] = Stake({
owner: msg.sender,
tokenId: uint24(tokenId),
timestamp: uint48(block.timestamp)
});
}
}
And this is the Unstaking function:这是Unstaking功能:
function _unstakeMany(address account, uint256[] calldata tokenIds)
internal
{
IERC721N nft = IERC721N(NftAddress);
// uint256 tokenId;
Stake memory staked;
totalStaked -= tokenIds.length;
for (uint256 i = 0; i < tokenIds.length; i++) {
// tokenId = tokenIds[i];
staked = vault[tokenIds[i]];
uint256 timeStamp = stakeStamp(tokenIds[i]);
require(staked.owner == msg.sender, "not an owner");
if(block.timestamp < timeStamp + 60){
revert timeError(timeStamp, tokenIds[i]);
}
delete vault[tokenIds[i]];
emit BlockUnstaked(account, tokenIds[i], block.timestamp);
nft.transferFrom(address(this), account, tokenIds[i]);
}
}
This is the full code:这是完整的代码:
// SPDX-License-Identifier: MIT LICENSE
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
interface IERC20N is IERC20 {
function mint(address to, uint256 amount) external;
}
interface IERC721N is IERC721 {
function totalSupply() external view returns (uint256);
}
contract Vault is Ownable, IERC721Receiver {
using SafeMath for uint256;
uint256 public totalStaked;
// struct to store a stake's token, owner, and earning values
struct Stake {
uint24 tokenId;
uint48 timestamp;
address owner;
}
event BlockStaked(address owner, uint256 tokenId, uint256 value);
event BlockUnstaked(address owner, uint256 tokenId, uint256 value);
event Claimed(address owner, uint256 amount);
// maps tokenId to stake
mapping(uint256 => Stake) public vault;
// initialising Nft cotract and coin contract
address public NftAddress;
address public TokenAddress;
// IERC721N nft1 = IERC721N(NftAddress);
// IERC20N token = IERC20N(TokenAddress);
error timeError(uint256 timeleft, uint256 tokenId);
// error timeError(uint256 timeleft, uint256 blockStamp, uint256 tokenId);
constructor() {}
function setNftAddress(address _address) public onlyOwner {
NftAddress = _address;
}
function setTokenAddress(address _address) public onlyOwner {
TokenAddress = _address;
}
function stake(uint256[] calldata tokenIds) external {
IERC721N nft = IERC721N(NftAddress);
uint256 tokenId;
totalStaked += tokenIds.length;
for (uint256 i = 0; i < tokenIds.length; i++) {
tokenId = tokenIds[i];
require(nft.ownerOf(tokenId) == msg.sender, "not your token");
require(vault[tokenId].tokenId == 0, "already staked");
nft.transferFrom(msg.sender, address(this), tokenId);
emit BlockStaked(msg.sender, tokenId, block.timestamp);
vault[tokenId] = Stake({
owner: msg.sender,
tokenId: uint24(tokenId),
timestamp: uint48(block.timestamp)
});
}
}
uint256 public TIMe;
function _unstakeMany(address account, uint256[] calldata tokenIds)
internal
{
// IERC721N nft = IERC721N(NftAddress);
// uint256 tokenId;
Stake memory staked;
totalStaked -= tokenIds.length;
for (uint256 i = 0; i < tokenIds.length; i++) {
// tokenId = tokenIds[i];
staked = vault[tokenIds[i]];
uint256 timeStamp = stakeStamp(tokenIds[i]);
require(staked.owner == msg.sender, "not an owner");
if(block.timestamp < timeStamp + 60){
revert timeError(timeStamp, tokenIds[i]);
}
delete vault[tokenIds[i]];
emit BlockUnstaked(account, tokenIds[i], block.timestamp);
// nft.transferFrom(address(this), account, tokenIds[i]);
}
}
function blockStamp() public view returns(uint256){
return block.timestamp;
}
function stakeStamp(uint256 id) public view returns(uint256){
return vault[id].timestamp;
}
function unstake(uint256[] calldata tokenIds) external {
_claim(msg.sender, tokenIds, true);
}
function _claim(
address account,
uint256[] calldata tokenIds,
bool _unstake
) internal {
uint256 tokenId;
uint256 earned = 0;
IERC20N token = IERC20N(TokenAddress);
for (uint256 i = 0; i < tokenIds.length; i++) {
tokenId = tokenIds[i];
Stake memory staked = vault[tokenId];
require(staked.owner == account, "not an owner");
uint256 stakedAt = staked.timestamp;
vault[tokenId] = Stake({
owner: account,
tokenId: uint24(tokenId),
timestamp: uint48(block.timestamp)
});
if (block.timestamp - stakedAt > 300) {
earned += 1000 ether;
}
}
if (earned > 0) {
token.mint(msg.sender, earned);
}
if (_unstake) {
_unstakeMany(account, tokenIds);
}
emit Claimed(account, earned);
}
function timeFromStaked(uint256[] calldata tokenIds)
public
view
returns (uint256[] memory)
{
uint256[] memory list = new uint256[](tokenIds.length);
for (uint256 i = 0; i < tokenIds.length; i++) {
uint256 tokenId = tokenIds[i];
Stake memory staked = vault[tokenId];
uint256 stakedAt = staked.timestamp;
list[i] = uint48(block.timestamp) - stakedAt;
}
return list;
}
// should never be used inside of transaction because of gas fee
function balanceOf(address account) public view returns (uint256) {
IERC721N nft = IERC721N(NftAddress);
uint256 balance = 0;
uint256 supply = nft.totalSupply();
for (uint256 i = 1; i <= supply; i++) {
if (vault[i].owner == account) {
balance += 1;
}
}
return balance;
}
// should never be used inside of transaction because of gas fee
function tokensOfOwner(address account)
public
view
returns (uint256[] memory ownerTokens)
{
IERC721N nft = IERC721N(NftAddress);
uint256 supply = nft.totalSupply();
uint256[] memory tmp = new uint256[](supply);
uint256 index = 0;
for (uint256 tokenId = 1; tokenId <= supply; tokenId++) {
if (vault[tokenId].owner == account) {
tmp[index] = vault[tokenId].tokenId;
index += 1;
}
}
uint256[] memory tokens = new uint256[](index);
for (uint256 i = 0; i < index; i++) {
tokens[i] = tmp[i];
}
return tokens;
}
function onERC721Received(
address,
address from,
uint256,
bytes calldata
) external pure override returns (bytes4) {
require(from == address(0x0), "Cannot send nfts to Vault directly");
return IERC721Receiver.onERC721Received.selector;
}
}
after i run it on ganache-cli and do the steps required to initialize the contracts i stake one nft在我在 ganache-cli 上运行它并执行初始化合约所需的步骤后,我质押了一个 nft
then after sometime i do another transaction on my local blockchain to update the block.timestamp value and try to unstake然后一段时间后,我在本地区块链上进行另一笔交易以更新 block.timestamp 值并尝试取消抵押
when i try to unstake before the time passes the reverted timeError returns the value of the timestamps of the corresponding stake, but its not the right value because its always changing everytime i run the unstake function, and its always equal to the block.timestamp value当我尝试在时间过去之前取消抵押时,还原的 timeError 返回相应抵押的时间戳的值,但它不是正确的值,因为每次我运行取消抵押函数时它总是在变化,并且它总是等于 block.timestamp 值
this time stamp value is fetched using a function called stakeStamp, the stakeStamp function always returns the right value from the struct, but whenever i use it in the unstake function it returns the block.timestamp value instead of the saved timeestamp in the struct这个时间戳值是使用一个名为 stakeStamp 的函数获取的,stakeStamp 函数总是从结构中返回正确的值,但是每当我在 unstake 函数中使用它时,它都会返回 block.timestamp 值而不是结构中保存的时间戳
this is the stakeStamp function:这是 stakeStamp 函数:
function stakeStamp(uint256 id) public view returns(uint256){
return vault[id].timestamp;
}
u can check how i used it in the unstake function up above in the 3rd code block你可以在第三个代码块的上面的 unstake 函数中检查我是如何使用它的
i hope i provided good information on the problem.我希望我提供了有关该问题的良好信息。
First of all, I think you should make some automated tests in order to identify what is causing this timestamp bug.首先,我认为您应该进行一些自动化测试,以确定导致此时间戳错误的原因。 I've made some for you already that I will be appending to this answer.
我已经为你做了一些,我将附加到这个答案中。
There are some things that could be better in your contract.在你的合同中有些事情可能会更好。
For example, in line 99 (at least in my editor after some linting)例如,在第 99 行(至少在我的编辑器中经过一些 linting)
if(block.timestamp < timeStamp + 60){
revert timeError(timeStamp, tokenIds[i]);
}
block.timestamp
is a value that keeps growing. block.timestamp
是一个不断增长的值。 It's the timestamp of the EVM, so it's increasing by the seconds.它是 EVM 的时间戳,所以它以秒为单位增加。 checking
block.timestamp < timeStamp + 60
will throw error after 60 seconds of calling stake()
.检查
block.timestamp < timeStamp + 60
将在调用stake()
60 秒后抛出错误。 Maybe you should rewrite it to也许您应该将其重写为
require(block.timestamp > timeStamp + 60,"not past due time");
As the blocking timers seem to be small, I assume you are not using a testing environment and testing the contract on Remix or something else.由于阻塞计时器似乎很小,我假设您没有使用测试环境并在 Remix 或其他东西上测试合约。
I've made some testing on your contract using Hardhat and made a gist that you can access.我已经使用 Hardhat 对您的合同进行了一些测试,并制作了一个您可以访问的要点。 Take a look:
看一看:
https://remix.ethereum.org/#version=soljson-v0.8.7+commit.e28d00a7.js&optimize=false&runs=200&gist=3ad1a14ab5ad4ec5aca16ae6414ffb67 https://remix.ethereum.org/#version=soljson-v0.8.7+commit.e28d00a7.js&optimize=false&runs=200&gist=3ad1a14ab5ad4ec5aca16ae6414ffb67
It basically tests if the Vault can stake and unstake.它基本上测试了 Vault 是否可以质押和取消质押。
Regarding to your question in this point:关于你在这一点上的问题:
when i try to unstake before the time passes the reverted timeError returns the value of the timestamps of the corresponding stake, but its not the right value because its always changing everytime i run the unstake function, and its always equal to the block.timestamp value
当我尝试在时间过去之前取消抵押时,还原的 timeError 返回相应抵押的时间戳的值,但它不是正确的值,因为每次我运行取消抵押函数时它总是在变化,并且它总是等于 block.timestamp 值
You stake to a specific block.timestamp
in the moment the function was called.在调用函数的那一刻,您将抵押到特定的
block.timestamp
。 It is static, doesn't change.它是静态的,不会改变。 I quite doesn't understand what you mean there.
我完全不明白你的意思。 Also,
timeStamp
returned from the stakeStamp()
is always different from block.timestmap
.此外,从
timeStamp
stakeStamp()
返回的 timeStamp 始终与block.timestmap
不同。 More specifically, it's always lower than block.timestmap
since the intention is to set it stake()
and check it on unstake()
更具体地说,它总是低于
block.timestmap
,因为目的是设置它stake()
并在unstake()
上检查它
Also, is there any specific reason to use uint48
as the timestamp?此外,是否有任何特定原因使用
uint48
作为时间戳? Given that it is stated in the Solidity documentation that it is an uint256
I don't know if this cast has any unwanted behavior.鉴于 Solidity 文档中声明它是一个
uint256
,我不知道这个演员表是否有任何不需要的行为。 Probably not, given that the timestamp is in seconds and it would perfectly fit in a uint48
.可能不会,因为时间戳以秒为单位,并且完全适合
uint48
。
Import safeTransferFrom
function from IERC721.从 IERC721 导入
safeTransferFrom
函数。 use safeTransferFrom
function in stake and _unstakeMany
functions.在 Stake 和
_unstakeMany
函数中使用safeTransferFrom
函数。 if you dont get solution , just ping me in twitter(@bhargav_dasappa).如果您没有得到解决方案,只需在推特上联系我(@bhargav_dasappa)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.