以太坊应用层标准提案NFT之ERC-4907

一 为什么需要ERC-4907

当有时候,NFT价格太贵或者没有对应的NFT所有权,但是又想使用NFT,用来从事一些商业活动。这时候,ERC-721是没办法来解决这个问题,对于ERC-721来说,NFT只能发生所有权转移,不能再所有权未转移的前提下使用别人的NFT。

因此,为了适配这种场景,提出来了ERC-4907提案,该提案允许用户从拥有NFT所有权的用户中租赁NFT一定时间的使用权

二 ERC-4907标准提案

2.1 什么是ERC-4907提案

第一: ERC-4907是一个以太坊NFT 租赁协议标准提案

第二: 它提出了所有权和使用权的概念,并且所有权和使用权是分离的

第三: 它允许用户将NFT的使用权租赁给别人,并且可以设置租赁到期时间,到期后租赁自动归还给NFT的所有者

2.2 ERC-4907提案具有哪些特点

2.2.1 灵活的租赁机制

ERC-4907允许NFT所有者在不转让完全所有权的情况下,租赁其NFT给他人。这种灵活性使得NFT可以在不同的时间段内被多个用户使用,从而提升了其经济效益和使用率

2.2.2 增加了NFT的流动性

通过租赁模型,ERC-4907使得NFT更易于流动。持有者可以选择将其资产租给需要的用户,而非必须完全出售,从而拓展了NFT在市场上的应用方式

2.2.3 租赁流程简单

ERC-4907定义了明确的用户角色和使用权期限,通过智能合约自动管理使用权的到期和更新,从而简化了租赁过程,减少了操作成本和风险。

2.2.4 促进了新的商业模式

该标准为NFT租赁和订阅模型开辟了新的商业机会,例如游戏内物品租赁、虚拟资产租赁等,增加了数字资产的多样化利用方式。

2.3 ERC-4907提案具有哪些使用场景

2.3.1 资产抵押和借贷

NFT所有者可以使用ERC-4907将其资产租赁给他人,并将租金作为抵押品用于借贷或其他金融交易

2.3.2 订阅和服务模型

ERC-4907允许创建基于订阅的NFT服务模型,例如内容订阅或专业服务订阅。用户可以支付租金以获取NFT的临时使用权,这种模式适用于数字内容市场、教育资源或其他服务领域

2.3.3 艺术品和数字创作的临时使用权

艺术家或数字创作者可以使用ERC-4907来提供其作品的临时使用权,例如数字艺术品或音乐作品。这使得艺术品可以在限定时间内展示或使用,而所有者仍然保留作品的完全控制权

2.3.4 虚拟地产租赁

在虚拟世界或元宇宙中,用户可以通过ERC-4907标准租赁虚拟地产或其他数字资产。这种模式允许用户在特定时间内使用虚拟空间,进行展示、活动或其他商业用途,而不需要长期的资产拥有权。

2.3.5 游戏内物品租赁

游戏开发者可以使用ERC-4907标准实现在游戏中租赁虚拟物品,如武器、服装或其他游戏资产。玩家可以通过租赁获取对这些稀有或高价值物品的临时使用权,而不必购买它们的完整所有权

三 ERC-4970标准提案规范

3.1 函数

3.1.1 setUser

function setUser(uint256 tokenId, address user, uint64 expires) external;

设置哪一个用户享有使用权,到期时间是多长

3.1.2 userOf

function userOf(uint256 tokenId) external view returns (address);

根据tokenId获取当前哪一个用户持有对该NFT的使用权

3.1.3 userExpires

function userExpires(uint256 tokenId) external view returns (uint256);

获取一个tokenId对应的NFT的如果有人持有使用权,那么到期时间是多少?

3.2 事件

3.2.1 UpdateUser

event UpdateUser(

    uint256 indexed tokenId,

    address indexed user,

    uint64 expires

);

四 手动实现ERC-4907

4.1 IERC4907.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

interface IERC4907 {
    // Emitted when the `user` of an NFT or the `expires` of the `user` is changed
    event UpdateUser(
        uint256 indexed tokenId,
        address indexed user,
        uint64 expires
    );

    /**
     * Set the user and expires of a NFT
     * @param tokenId token id
     * @param user  The new user of the NFT
     * @param expires  UNIX timestamp, The new user could use the NFT before expires
     */
    function setUser(uint256 tokenId, address user, uint64 expires) external;

    /**
     * Get the user address of an NFT
     * @param tokenId  The NFT to get the user address for
     */
    function userOf(uint256 tokenId) external view returns (address);

    /**
     * Get the user expires of an NFT
     * @param tokenId The NFT to get the user expires for
     */
    function userExpires(uint256 tokenId) external view returns (uint256);
}

4.2 ERC4907.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "./IERC4907.sol";
import "./IERC4907Errors.sol";
import "./Counter.sol";

abstract contract ERC4907 is IERC4907, ERC721 {
    struct UserInfo {
        address user; // address of user role
        uint64 expires; // unix timestamp, user expires
    }
    mapping(uint256 tokenId => UserInfo) private _users;
    Counter private _counter;

    event NFTMinted(address indexed user, uint256 indexed tokenId);

    constructor(string memory name, string memory symbol) ERC721(name,symbol) {

    }

    function setUser(
        uint256 tokenId,
        address user,
        uint64 expires
    ) external override {
        // check caller if has permission to set user, only owner or authorized spender could do this
        require(_isApprovedOrOwner(msg.sender, tokenId), "transfer caller is not owner or not approved");
        // check token if has rent to user, if not, the user address should be zero address
        require(userOf(tokenId) == address(0), "User already assigned");
        // check expires if greater than the current block timestamp
        require(expires > block.timestamp, "expires should be in future");

        ExpiresLessThanCurrentTime
        UserInfo storage info = _users[tokenId];
        info.user = user;
        info.expires = expires;

        emit UpdateUser(tokenId, user, expires);
    }

    /**
     * Check the assigned user of token, if no any assigned or expired, return address(0)
     * @param tokenId token id
     */
    function userOf(uint256 tokenId) public view override returns (address) {
        if (_users[tokenId].expires <= block.timestamp) {
            return address(0);
        }
        return _users[tokenId].user;
    }

    function userExpires(uint256 tokenId) external view override returns (uint256) {
        return _users[tokenId].expires;
    }

    function transferFrom(address from, address to, uint256 tokenId) public virtual {
        _beforeTokenTransfer(from, to, tokenId);
        super.transferFrom(from, to, tokenId);
    }

    function mint() public returns (uint256) {
        _counter.increment();
        uint256 tokenId = _counter.current();
        _safeMint(msg.sender, tokenId);
        emit NFTMinted(msg.sender, tokenId);
        return tokenId;
    }

    function _isApprovedOrOwner(
        address sender,
        uint256 tokenId
    ) internal view virtual returns (bool) {
        address owner = _ownerOf(tokenId);
        address spender = _getApproved(tokenId);
        return (sender == owner || sender == spender);
    }

    function supportsInterface(bytes4 interfaceId ) public view virtual override(ERC721) returns (bool) {
        return interfaceId == type(IERC4907).interfaceId || super.supportsInterface(interfaceId);
    }

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual override {
        if (
            from != to &&
            _users[tokenId].user != address(0) && //user present
            block.timestamp >= _users[tokenId].expires //user expired
        ) {
            delete _users[tokenId];
            emit UpdateUser(tokenId, address(0), 0);
        }
    }
}

4.3 Counter.sol

contract Counter {
    uint256 public number;

    function setNumber(uint256 newNumber) public {
        number = newNumber;
    }

    function increment() public {
        number++;
    }

    function current() public returns (uint256){
        return number;
    }
}

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ERC721是以太坊上面的一个智能合约标准,它定义了非同质化代币(NFT)的标准接口,使得开发者能够创建和交易独特的数字资产,如游戏道具、收藏品等。 要实现ERC721标准,需要编写智能合约代码,可以使用Solidity语言来实现。以下是一个简单的ERC721智能合约实现: ``` pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract MyNFT is ERC721 { using Counters for Counters.Counter; Counters.Counter private _tokenIds; constructor() ERC721("MyNFT", "NFT") {} function mintNFT(address recipient, string memory tokenURI) public returns (uint256) { _tokenIds.increment(); uint256 newItemId = _tokenIds.current(); _mint(recipient, newItemId); _setTokenURI(newItemId, tokenURI); return newItemId; } } ``` 上述代码实现了一个简单的ERC721智能合约,包含了一个名为`MyNFT`的合约,继承自`ERC721`,并实现了`mintNFT`函数用于创建NFT。 接下来,需要搭建一个NFT交易所,可以使用Web3.js和React.js来实现。以下是一个简单的NFT交易所前端代码实现: ``` import React, { useState, useEffect } from "react"; import Web3 from "web3"; import MyNFT from "./contracts/MyNFT.json"; const web3 = new Web3(Web3.givenProvider || "http://localhost:8545"); function App() { const [account, setAccount] = useState(""); const [nfts, setNFTs] = useState([]); useEffect(() => { async function loadBlockchainData() { const accounts = await web3.eth.getAccounts(); setAccount(accounts[0]); const networkId = await web3.eth.net.getId(); const deployedNetwork = MyNFT.networks[networkId]; const myNFT = new web3.eth.Contract( MyNFT.abi, deployedNetwork && deployedNetwork.address ); const totalSupply = await myNFT.methods.totalSupply().call(); const nftPromises = []; for (let i = 1; i <= totalSupply; i++) { nftPromises.push(myNFT.methods.tokenURI(i).call()); } const nftURIs = await Promise.all(nftPromises); setNFTs(nftURIs); } loadBlockchainData(); }, []); async function mintNFT() { const myNFT = new web3.eth.Contract(MyNFT.abi, MyNFT.networks[5777].address); await myNFT.methods .mintNFT(account, "https://example.com/my-nft.json") .send({ from: account }); setNFTs([...nfts, "https://example.com/my-nft.json"]); } return ( <div className="App"> <h1>My NFTs</h1> <p>Account: {account}</p> <button onClick={mintNFT}>Mint NFT</button> <ul> {nfts.map((nft) => ( <li> <a href={nft}>{nft}</a> </li> ))} </ul> </div> ); } export default App; ``` 上述代码实现了一个简单的NFT交易所前端,包含了一个名为`App`的React组件,用于显示用户的NFT和创建新的NFT。在`loadBlockchainData`函数中,使用Web3.js和智能合约ABI来获取用户账户和NFT列表。在`mintNFT`函数中,使用Web3.js和智能合约ABI来创建新的NFT,并更新NFT列表。 最后,需要将智能合约部署到以太坊网络上,可以使用Remix或Truffle等工具进行部署。部署成功后,将智能合约地址更新到前端代码中即可。 以上是一个简单的ERC721标准实现和NFT交易所搭建过程,具体实现过程可能会因为实际应用场景的不同而略有差异,需要根据实际情况进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫言静好、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值