gpt4 book ai didi

struct - 在 solidity 结构中保存 block.timestamp,但函数返回当前时间的时间戳

转载 作者:行者123 更新时间:2023-12-05 08:10:38 25 4
gpt4 key购买 nike

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

所以我写了一个 solidity 智能合约来质押 nfts,质押部分运行良好但解除质押部分却不是,我正在尝试编写解除质押功能,以便所有者只能在特定时间解除质押他们的 nfts已经过去了,这次是存储在 stakeNft 的 solidity Struct 中的 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

然后过了一段时间我在我的本地区 block 链上做了另一笔交易来更新 block.timestamp 值并尝试取消质押

当我尝试在时间过去之前取消质押时,还原的 timeError 返回相应质押的时间戳值,但它不是正确的值,因为每次我运行取消质押函数时它总是在变化,并且它总是等于 block .时间戳值

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

这是 stakeStamp 函数:

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

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

我希望我提供了有关该问题的有用信息。

最佳答案

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

有些事情可以在您的契约(Contract)中做得更好。

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

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

block.timestamp是一个不断增长的值(value)。它是 EVM 的时间戳,所以它以秒为单位递增。检查 block.timestamp < timeStamp + 60调用 60 秒后将抛出错误 stake() .也许你应该将它重写为

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

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

我已经使用 Hardhat 对您的契约(Contract)进行了一些测试,并制作了一个您可以访问的要点。看一看:

https://remix.ethereum.org/#version=soljson-v0.8.7+commit.e28d00a7.js&optimize=false&runs=200&gist=3ad1a14ab5ad4ec5aca16ae6414ffb67

它主要测试 Vault 是否可以质押和取消质押。

关于您此时的问题:

when i try to unstake before the time passes the reverted timeErrorreturns the value of the timestamps of the corresponding stake, butits not the right value because its always changing everytime i runthe unstake function, and its always equal to the block.timestampvalue

您质押到一个特定的 block.timestamp在函数被调用的那一刻。它是静态的,不会改变。我完全不明白你的意思。另外,timeStampstakeStamp() 返回总是不同于 block.timestmap .更具体地说,它总是低于 block.timestmap因为打算设置它stake()并在 unstake() 上查看

此外,使用 uint48 有什么具体原因吗?作为时间戳?鉴于它在 Solidity 文档中说明它是一个 uint256我不知道这个类型转换是否有任何不良行为。可能不是,因为时间戳以秒为单位并且它完全适合 uint48 .

关于struct - 在 solidity 结构中保存 block.timestamp,但函数返回当前时间的时间戳,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72193055/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com