在这周,NFT 租赁市场 Double Protocol 提交的可租赁 NFT 标准“EIP-4907”通过了以太坊开发团队的最终审核,成为第 30 个 ERC 标准“Final”的状态。
让我们一起来阅读这极简的源代码,来缕清其他是如何实现NFT租赁的?
1. 为什么需要租赁?
2. 源码解读
2.1 实现原理&数据结构
2.2 设置用户
2.3 查询用户&过期时间
2.4 租赁关系的强制性设计
3. 总结
NFT 的爆发毋庸置疑,而伴随普及引发了对其资产实用性的需求,尤其是在元宇宙和边玩边赚 (P2E) 的场景下,仅仅单纯的资产所有权并不足以支撑更多的应用玩法的诞生。
一直的痛点是供给不平衡,导致的好项目少,浮于金融玩法的韭菜项目多,既然如今web3有大量web2行业精英在涌入,也必然需要快速的新标准推出,来将创造力的供给释放。
NFT流动性的紧缺,使得他风风火火的同时也备受诟病,他之前产权一体化的金融化操作,市场各类安全问题欺诈、剽窃、钓鱼、跑路、资产被盗层出不穷。
而Eip-4907,想要解决的就是分离NFT的资产价值和使用价值,释放出NFT的市场流动性
由于Eip4907极其简单,因此对代码的解读无论是否技术同学均可来尝试理解其细节设计。
他作为 ERC-721 的扩展, EIP-4907 增加了一个变量UserInfo,让应用可以查询此NFT当前被租出去的目标地址“user”和出租时间”expires"。如果发现已经超出出租时间,则租赁关系宣告失效。
代码极为简单仅有72行,使用这个标准,就是在原来的ERC721之上新增
1个事件(用于通知链下应用称为事件)
3个方法(用于实现链上数据管理功能)
分别是
UpdateUser 事件:当NFT转移,租赁校色设置时,发出租赁用户改变的通知
setUser 方法:NFT所有者授权者可用,设置此NFTID的出租用户和过期时间
userOf 方法:任何人可用,查询此NFTID的出租用户
userExpires 方法:任何人可用,查询此NFTID的过期时间
理解ERC标准协议的最佳方式就是理解他管理数据的最底层数据结构
例如:前文【源码解读】你买的NFT到底是什么?
其实NFT只是通过2个映射(_owners,_balances),即一种字典形式的key-value对应关系的存储结构去记录数据
mapping(uint256 => address) _owners;// 记录每一个NFTID当前对应的所有者地址
mapping(address => uint256) _balances; //记录了当前所有者总计持有的NFT数量
而Eip-4907则是新增了一个数据对象UserInfo 在所有权的概念之外增加“用户”的维度
struct UserInfo {
address user; // 用户地址
uint64 expires; //用户到期时间
}
数据结构简单,就意味着管理的方法实现也非常简单
设置用户仅有3个步骤
function setUser(uint256 tokenId, address user, uint64 expires) public virtual{
require(_isApprovedOrOwner(msg.sender, tokenId),"ERC721: transfer caller is not owner nor approved");
UserInfo storage info = _users[tokenId];//新增存储登记信息
info.user = user;
info.expires = expires;
emit UpdateUser(tokenId,user,expires); //发出事件通知链下应用
}
媒体处处宣传的超时自动失效,而无需二次链上交易登记失效节约gas的逻辑就在这了。
查询的逻辑很简单,按指定的NFTID查询 _users的user信息即可,但他增加了,if判断,当前块的时间block.timestamp 是否会超过设置的过期时间expires,所以此查询仅在时间内有效。
function userOf(uint256 tokenId)public view virtual returns(address){
if( uint256(_users[tokenId].expires) >= block.timestamp){
return _users[tokenId].user;
};//执行此函数,在未到期的情况下,返回此ID的当前用户地址
else{return address(0);
}//到期情况下,则返回0地址,意未占用
}
此处无需类似user查询时候判断是否过期,因为得知上一次过期时间,也是加快NFT用户使用率的一种方式。
function userExpires(uint256 tokenId) public view virtual returns(uint256){
return _users[tokenId].expires;//执行此函数,返回此ID的用户过期时间
}
此eip4907的协议,对标准交易方法Transfer 增加了一部分内容,通过_beforeTokenTransfer 实现,就是强制在进行Transfer交易转移后就删除掉这部分对用户的信息,并且发出事件通知已经用户失效了。
function _beforeTokenTransfer(address from,address to,uint256 tokenId
) internal virtual override{
super._beforeTokenTransfer(from, to, tokenId);
//当交易不是自己转自己的情况下,如果有设置“用户”则删除他
if (from != to && _users[tokenId].user != address(0)) {
delete _users[tokenId];// 删除用户信息
emit UpdateUser(tokenId, address(0), 0);// 发出事件通知已删除
}
}
总结
没想到吧,这么快就代码讲完了,因为确实他写完了,对的,不像是之前的通过限制转移权的方法EIP-5058,见前文【EIP-5058 能否防止NFT项目方提桶跑路?】
其实他EIP-4907只是多了个变量,并称之为租赁用户而已,可以声明,但是其他应用认不认就是另一码事了,毕竟其强制性有限,转移就能强行终止出租授权
当然你或许会问,为什么这么简单反而火速成为了难得一见,且每次都能掀起一番波浪的Final标准呢?
这也就是web3的哲学:越简单,越优雅,剩下的交给共识。
我个人认可这样的哲学与趋势
web3过去爆发或是得益于金融操作,但是好的生态不能只是金融价值,还需要更有商业价值,需要更广大的用户融入于生活生产形成价值闭环,货币终究只是手段而不是目的本身
引用:
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4907.md
写在最后:
【解密】OpenSea免费创造的NFT都没上链竟能出现在我的钱包里?
欢迎各位同学从后台提交有趣的合约或交易哈希。
关注十四,用技术的眼光发现价值。
看到这里,帅气的你不点个赞吗?
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
如果我使用ruby版本2.5.1和Rails版本2.3.18会怎样?我有基于rails2.3.18和ruby1.9.2p320构建的rails应用程序,我只想升级ruby的版本,而不是rails,这可能吗?我必须面对哪些挑战? 最佳答案 GitHub维护apublicfork它有针对旧Rails版本的分支,有各种变化,它们一直在运行。有一段时间,他们在较新的Ruby版本上运行较旧的Rails版本,而不是最初支持的版本,因此您可能会发现一些关于需要向后移植的有用提示。不过,他们现在已经有几年没有使用2.3了,所以充其量只能让更
我想使用spawn(针对多个并发子进程)在Ruby中执行一个外部进程,并将标准输出或标准错误收集到一个字符串中,其方式类似于使用Python的子进程Popen.communicate()可以完成的操作。我尝试将:out/:err重定向到一个新的StringIO对象,但这会生成一个ArgumentError,并且临时重新定义$stdxxx会混淆子进程的输出。 最佳答案 如果你不喜欢popen,这是我的方法:r,w=IO.pipepid=Process.spawn(command,:out=>w,:err=>[:child,:out])
我正在尝试找到一种方法来规范化字符串以将其作为文件名传递。到目前为止我有这个:my_string.mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/n,'').downcase.gsub(/[^a-z]/,'_')但第一个问题:-字符。我猜这个方法还有更多问题。我不控制名称,名称字符串可以有重音符、空格和特殊字符。我想删除所有这些,用相应的字母('é'=>'e')替换重音符号,并将其余的替换为'_'字符。名字是这样的:“Prélèvements-常规”“健康证”...我希望它们像一个没有空格/特殊字符的文件名:“prelevements_routin
其实做自媒体的成本并不高,入门只需要一部手机即可!在手机上找视频素材、使用手机剪辑视频、最后使用手机发布视频作品获得收益!方法并不难,今天这期内容就来给粉丝们分享一种小方法,每天稳定收益100-300,抓紧点赞收藏!1、找素材(1)使用手机拍摄自己喜欢的经典段落,使用程序把文案内容提取出来(2)也可以在豆瓣、知乎、微博等网站中找一些自己需要的文案素材(3)把文案进行润色修改,可以加入一些自己的观点(4)视频素材可以使用软件中自带的素材,也可以在素材网站中下载完整版的素材2、文案配音(1)把复制好的文案直接导入小程序中(2)调整音色、音调后一键合成音频即可(3)可以选择自己朗读配音,需要花一点时
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o
一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg