获取保存块.时间戳在固体结构中,但函数返回当前时间的时间戳



这是我第一次在stackoverflow上提问,所以我希望我提供了这里需要的所有信息

所以我写了一个坚实的智能合约来押注nft,押注部分工作良好,但取消押注部分不是,我试图编写取消押注函数,以便所有者只有在一定时间过去后才能取消押注他们的nft,这一次是存储在stakeNft的坚实结构体中的uint48值:

struct Stake {
uint24 tokenId;
uint48 timestamp; <------
address owner;
}

这是赌注函数:

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)
});
}
}

这是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]);

}
}

这是完整的代码:

// 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;
}
}

之后,我在ganache-cli上运行它,并执行初始化合同所需的步骤,我下注一个nft

然后过一段时间,我在我的本地区块链上做另一个交易来更新块。时间戳值,并尝试解锁

当我尝试在时间过去之前取消利害关系时,恢复的timeError返回相应利害关系的时间戳的值,但它不是正确的值,因为每次我运行unstake函数时它总是在变化,并且它总是等于块。时间戳值

这个时间戳值是使用一个名为stakeStamp的函数获取的,stakeStamp函数总是从结构中返回正确的值,但是每当我在unstake函数中使用它时,它都会返回块。时间戳值,而不是结构体

中保存的时间戳。这是一个函数:

function stakeStamp(uint256 id) public view returns(uint256){
return vault[id].timestamp;
}

你可以看看我是如何在上面第三个代码块的unstake函数中使用它的

我希望我对这个问题提供了有用的信息。

首先,我认为您应该进行一些自动化测试,以确定是什么导致了这个时间戳错误。我已经为你做了一些,我将追加到这个答案。

你的合同中有些地方还可以做得更好。

例如,在第99行(至少在我的编辑器中经过一些检查后)

if(block.timestamp < timeStamp + 60){
revert timeError(timeStamp, tokenIds[i]);
}

block.timestamp是一个持续增长的值。这是EVM的时间戳,所以它以秒为单位增加。检查block.timestamp < timeStamp + 60会在调用stake()60秒后抛出错误。也许你应该重写成

require(block.timestamp > timeStamp + 60,"not past due time");

由于阻塞计时器似乎很小,我假设您没有使用测试环境并在Remix或其他东西上测试合同。

我已经用Hardhat对你的合同做了一些测试,并做了一个要点,你可以访问。看一下:

https://remix.ethereum.org/版本= soljson-v0.8.7 + commit.e28d00a7.js&优化= false&运行= 200,要点= 3 ad1a14ab5ad4ec5aca16ae6414ffb67

它基本上测试Vault是否可以桩和拆桩。

关于你在这一点上的问题:

当我试图在时间过去之前解除赌注时,会出现恢复的timeError返回相应利害关系的时间戳的值,但是它不是正确的值,因为每次运行它都在变化unstake函数,它总是等于block.timestamp值

您在调用函数的那一刻将赌注押到一个特定的block.timestamp。它是静态的,不会改变。我不太明白你的意思。另外,从stakeStamp()返回的timeStamp总是不同于block.timestmap。更具体地说,它总是低于block.timestmap,因为意图是将其设置为stake()并在unstake()上检查它

还有,使用uint48作为时间戳有什么特殊的原因吗?考虑到在Solidity文档中声明它是uint256,我不知道这个cast是否有任何不想要的行为。可能不会,因为时间戳是以秒为单位的,它完全适合uint48

从IERC721导入safeTransferFrom函数。在利害关系中使用safeTransferFrom函数和_unstakeMany函数。如果你没有得到解决方案,只要在twitter上联系我(@bhargav_dasappa)。