[英]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.