[英]Getting saved block.timestamp in solidity Struct, but function is returning timestamp of current time
这是我第一次在 stackoverflow 上提问,所以我希望我在这里提供了所有需要的信息
所以我写了一个可靠的智能合约来质押 nfts,质押部分运行良好但解除质押部分不是,我试图编写解除质押功能,以便所有者只有在一定时间过去后才能解除质押他们的 nfts,这次是一个 uint48 值,存储在 stakeNft 的一个solidity Struct 中:
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
然后一段时间后,我在本地区块链上进行另一笔交易以更新 block.timestamp 值并尝试取消抵押
当我尝试在时间过去之前取消抵押时,还原的 timeError 返回相应抵押的时间戳的值,但它不是正确的值,因为每次我运行取消抵押函数时它总是在变化,并且它总是等于 block.timestamp 值
这个时间戳值是使用一个名为 stakeStamp 的函数获取的,stakeStamp 函数总是从结构中返回正确的值,但是每当我在 unstake 函数中使用它时,它都会返回 block.timestamp 值而不是结构中保存的时间戳
这是 stakeStamp 函数:
function stakeStamp(uint256 id) public view returns(uint256){
return vault[id].timestamp;
}
你可以在第三个代码块的上面的 unstake 函数中检查我是如何使用它的
我希望我提供了有关该问题的良好信息。
首先,我认为您应该进行一些自动化测试,以确定导致此时间戳错误的原因。 我已经为你做了一些,我将附加到这个答案中。
在你的合同中有些事情可能会更好。
例如,在第 99 行(至少在我的编辑器中经过一些 linting)
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 对您的合同进行了一些测试,并制作了一个您可以访问的要点。 看一看:
它基本上测试了 Vault 是否可以质押和取消质押。
关于你在这一点上的问题:
当我尝试在时间过去之前取消抵押时,还原的 timeError 返回相应抵押的时间戳的值,但它不是正确的值,因为每次我运行取消抵押函数时它总是在变化,并且它总是等于 block.timestamp 值
在调用函数的那一刻,您将抵押到特定的block.timestamp
。 它是静态的,不会改变。 我完全不明白你的意思。 此外,从timeStamp
stakeStamp()
返回的 timeStamp 始终与block.timestmap
不同。 更具体地说,它总是低于block.timestmap
,因为目的是设置它stake()
并在unstake()
上检查它
此外,是否有任何特定原因使用uint48
作为时间戳? 鉴于 Solidity 文档中声明它是一个uint256
,我不知道这个演员表是否有任何不需要的行为。 可能不会,因为时间戳以秒为单位,并且完全适合uint48
。
从 IERC721 导入safeTransferFrom
函数。 在 Stake 和_unstakeMany
函数中使用safeTransferFrom
函数。 如果您没有得到解决方案,只需在推特上联系我(@bhargav_dasappa)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.