保存块。固体结构中的timestamp,但功能正在返回当前时间的时间戳

发布于 2025-01-27 21:32:21 字数 9191 浏览 5 评论 0 原文

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

所以我写了一份坚固的智能合约来维持nfts好吧,但是脱落的部分不是,我试图编写拆卸功能,以便所有者只有一定时间过去了,只能释放其NFT,这次是存储在固定结构中的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)
            });
        }
    }

这是放下的功能:

    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放在一个NFT

之后,然后一段时间后,我在本地区块链上进行另一次交易要更新块。Timestamp值并尝试

在我尝试在时间通过之前取消销售时尝试删除,返回的TimeError会返回相应股份的时间戳记的值,而不是正确的值,因为它总是每次运行Unbestake函数时它总是更改,并且它始终等于block.timestamp value

此时间邮票值使用称为stakestamp的函数获取,stakestamp函数始终从结构中返回正确的值block.timestamp值,而不是struct 中保存的timeStamp

这是stakestamp函数:

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

您可以在第三代码块中检查我如何在上面的unainsest函数中使用它,

我希望我提供有关问题的良好信息。

This is my 1st time asking a question on stackoverflow so i hope i provided all the information needed here

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:

    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:

    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

then after sometime i do another transaction on my local blockchain to update the block.timestamp value and try to unstake

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

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

this is the stakeStamp function:

    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

i hope i provided good information on the problem.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

ゃ人海孤独症 2025-02-03 21:32:21

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

您的合同中有些事情可能会更好。

例如,在第99行中(至少在我的编辑中,在我的编辑中)

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

block.timestamp 是一个不断增长的值。这是EVM的时间戳,因此它增加了几秒钟。检查 block.timestamp&lt; Timestamp + 60 将在60秒钟调用 stake()后会丢弃错误。也许您应该将其重写为,

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

因为阻止计时器似乎很小,我认为您没有使用测试环境并在混音或其他内容上测试合同。

我使用Hardhat对您的合同进行了一些测试,并提出了可以访问的要点。看看:

https://remix.ethereum.org/#version = soljson-v0.8.7+commit.e28d00a7.js&amptimize = false=false&amp = false&amp =

200股份和抢劫。

关于您的问题,这一点:

当我试图在时间过去的时间之前撤销时
返回相应股份的时间戳的值,但
它不是正确的价值,因为每次我运行时总是改变
删除功能,并且始终等于块。Timestamp

在调用函数的那一刻,您 赌注降低到特定的 block.timestamp 。它是静态的,不会改变。我完全不明白你在那里的意思。另外, timestamp stakestamp()返回的始终不同于 block.timestmap 。 ,它始终低于 block.timestmap ,因为目的是设置 stake()并在 unstake()上检查它

更具体地说 是否有使用 uint48 作为时间戳的特定原因?鉴于在坚固的文档中指出的是 uint256 我不知道这种铸件是否有任何不必要的行为。鉴于时间戳已在几秒钟内,并且它将完全适合 uint48

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)

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

block.timestamp is a value that keeps growing. It's the timestamp of the EVM, so it's increasing by the seconds. checking block.timestamp < timeStamp + 60 will throw error after 60 seconds of calling stake(). 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.

I've made some testing on your contract using Hardhat and made a gist that you can access. Take a look:

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.

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

You stake to a specific block.timestamp in the moment the function was called. 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. More specifically, it's always lower than block.timestmap since the intention is to set it stake() and check it on unstake()

Also, is there any specific reason to use uint48 as the timestamp? 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. Probably not, given that the timestamp is in seconds and it would perfectly fit in a uint48.

少钕鈤記 2025-02-03 21:32:21

导入 safetransferfrom 功能从IERC721发出。使用 safetransfrom stake中的功能, _UNSTAKEMANY 函数。如果您没有得到解决方案,只需在Twitter(@bhargav_dasappa)中ping我即可。

Import safeTransferFrom function from IERC721. use safeTransferFrom function in stake and _unstakeMany functions. if you dont get solution , just ping me in twitter(@bhargav_dasappa).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文