# OpenSea未上链的NFT，为何会出现在链上钱包？

By [shisi.eth](https://paragraph.com/@shisi-eth) · 2022-10-10

---

内容概要
----

粉丝反馈，他在opensea无gas免费创建的NFT资产，竟能出现在小狐狸钱包里，而opensea官方声明的此NFT在正式交易前是未上链铸造的，这就很奇怪，中心化应用的资产出现在为去中心化应用服务的钱包里，到底是官方代付gas上链还是乌龙，随我一文揭晓！

### 目录大纲

1.  背景简述
    
2.  破案历程
    
    2.1 小狐狸网络抓包
    
    2.2 链上的交易统计
    
    2.3 合约反编译分析
    
3.  破案总结
    

**面向对象**

Web3新手，有无技术背景均可理解此文解密过程

背景简述
----

### 1.1 opensea 是什么？

全球最大的公链NFT交易平台，也是目前除以太坊链底层外营收最大的web3平台，下文简述为os

![](https://storage.googleapis.com/papyrus_images/a31f34a6db6fa57d0927c14fbe7f501b8f1e7662aa4628b33b73886fd154f615.png)

### 1.2 NFT是什么？

可见前文：[【源码解读】你买的NFT到底是什么？](https://mp.weixin.qq.com/s?__biz=MzIyMTQ5MTg5Mw==&mid=2247483815&idx=1&sn=5f91df631b450944739419be185e597c&chksm=e83aa67ddf4d2f6bf24b9f6139bd685db9b5f3ff5a131f84c179a5166ad42337f0b2aabe0bf0&scene=21#wechat_redirect)

### 1.3 NFT铸造是什么？

可见前文：[当奈飞的NFT忘记了web2的业务安全](https://mp.weixin.qq.com/s?__biz=MzIyMTQ5MTg5Mw==&mid=2247483761&idx=1&sn=0ebe04a3611d8ad2bde5045a862324e3&chksm=e83aa6abdf4d2fbd476a402c6fb697b6d3086721a66f217d659e45ddd1835a45885b969b5161&scene=21#wechat_redirect)

大意是，只有在链上_Mint_后的才能认为是资产，且一般用户需要付gas

### 1.4 os的免费创造NFT服务是什么?

NFT上链成本高，标准ERC721的mint需要8W的gas，约5刀

NFT定制合约难，虽有标准但顶级NFT项目会定制且部分强化功能，例如azuki（出品721A，降低批量mint成本）

![](https://storage.googleapis.com/papyrus_images/b03d9ce459fda37331571d0aabbe6e0437fbec329a3855a8a9723c84f511681e.png)

因此，易用性一直就是市场的痛点，毕竟艺术家不是合约工程师，需更低成本的试错来探索有价值的艺术品，官方也特别说明过（原文见附录）

在opensea.io上create的NFT，符合ERC1155标准，即使是它们存在于链上之前，这些 NFT 也可以在任何平台上出售，在交易时才上链铸造

破案历程
----

### 2.1 案例来源

某粉丝很惊奇发现，自己确实可以在os上按流程进行create，但按官方说法此时是未上链的，但是他尝试在小狐狸钱包里导入资产时发现， 竟然已经可见，甚至尝试导入一个自己未create的NFTID时也能导入成功，因此反馈我寻找技术维度的全貌解读

### 2.2 还原方式

1：os上 createNFT（附录有官方教程）得到合约地址与ID

2：小狐狸上开启NFT检测后，再手动添加收藏品，即出现Shared Storefront

![](https://storage.googleapis.com/papyrus_images/48d1570520d0b7e854a6ee381354cdee837db9e3fba0512dc297fd414cb046e5.png)

### 2.3 调查不易

其实查询资产是否上链是个非常容易的过程，用区块链浏览器即可

见前文：[当我们在看Etherscan的时候,到底在看什么?](https://mp.weixin.qq.com/s?__biz=MzIyMTQ5MTg5Mw==&mid=2247483782&idx=1&sn=ff4604617e9409f844bf60a37f96543e&chksm=e83aa65cdf4d2f4ad58a90069649bce2dc1678c3bf216cbe44205d5f0f0632186a6fdfb92587&scene=21#wechat_redirect)

但是由于此os的NFT共享商店的合约并没有做验证。所以无法直接看源码查数据

但使用未经开源以及安全审计的合约，着实让我这从事多年安全行业的职业强迫症有些难受

如果真存在风险，其危害是巨大的

*   如果真未上链，小狐狸钱包去读取中心化平台数据？放在我的资产里？
    
*   如果真上了链，用户无需gas，则可能官方付费上链，雷同羊毛有被攻击风险
    
*   未上链的资产，如产生冲突风险，那此NFT属于谁的？
    

### 2.4 小狐狸app抓包

首先咱们通过对照实验做手机抓包，发现整体小狐狸会做的事情很多

拉取地址余额，交易，最新块内容，指定地址合约字节码等等

![](https://storage.googleapis.com/papyrus_images/011cd17278e06cfe1e7ab57c934bc286ae5338b02837941a7d534c1e08cd8a09.png)

对数百个网络包逐个分析后，发现其小狐狸会使用infura（提供api接口同以太坊节点做交互的平台）的eth-call方法，功能是查询指定NFT合约中标准的balanceof方法

为何我如此肯定？此请求包里没有balanceof呀？

咱们用实验说话双向校验，标准的1155协议的标准函数名及参数为：

    function balanceOf(address,uint256)
    

由于data构建指定函数调用，其生成Mothod-ID的原理是名字+参数类型组合，因此他构建的call方法，data字段的开头必然为00fdd58e

我输 入的要添加资产是123号，其data参数末尾对应是7b=(7\*16+11=123) 也对应上。

同时发现其缺乏防重放的措施，所以我可以直接编辑参数

重发请求查询其他NFT合约所有权来对比:

*   azuki：721标准，无得到0,有则非0
    
*   爱死机：1155标准，无得到0,有则得1
    
*   查os共享商店，修改参数尾数（刚才的123）确实会得到值但意义不明
    

所以这里我得出的一大惊奇猜想是（是错误的）：

**链上可查得NFT所有权的值，难道真的上链了吗？**

### 2.5 链上交易统计

如果真上链其实也可以不由用户支付gas，有种“元交易”的技术手法，就是交易发起方和gas支付者不同的形式，并且openzepplin里特地有个content 合约就是用于额外支持元交易产生的msg.sender 可能特殊化的问题

但是，我通过对此合约的链上交易数据统计（etherscan+dune），抽取23号一天出现的交易频率可见下图，都证明了好像并没有固定由官方发起的元交易类型交易

### 2.6 破局得靠合约反编译

由于官方没有验证合约，导致分析起来费劲多了，但可通过合约反编译来大致摸清逻辑

如果只是openzepplin的标准1155库进行反编译的话，行数是170行

但是此1155则反编译后是1000行，因此必然有较多自定义实现

反编译后整体是吻合1155的数据标准

    mapping (uint256 => [uint256]) _balanceOf;
    mapping (uint256 => [uint256]) _setApprovalForAll; 
    uint256[] _name; 
    uint256[] _symbol;
    mapping (uint256 => [uint256]) _totalSupply;
    

但是也显著有不是标准数据部分

    mapping (uint256 => [uint256]) owner_a; 
    mapping (uint256 => [uint256]) _creator; 
    

由于链上数据读取返回了结果，所以重点分析balanceOf 函数，他整体实现用了30多行，这是很明显**重写了原先的标准函数**

    //此为标准1155协议的balanceOf 函数实现，只是读取指定id和地址的数值而已
    function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
            require(account != address(0), "ERC1155: balance query for the zero address");
            return _balances[id][account];}
    

由于os他依旧是沿用标准，所以他的两个参数是固定的可以理解为：

*   Varg0 = 待查询NFTid所有权的用户地址
    
*   varg1 = 待查询的NFTID 数字
    

从反编译程序里看逻辑，他会读取待查地址的\_balanceOf总余额数，以及此NFTID是否被\_creator等等

    //此为反编译os合约的片段（太长不粘来，反编译网址在附录）
    function balanceOf(address varg0, uint256 varg1) public payable { 
        require(msg.data.length - 4 >= 64);
        v0 = v1 = _balanceOf[varg0];
        if (address(_creator[varg1]) == 0) {
            v2 = v3 = address(varg1 >> 96) == varg0;
            if (address(varg1 >> 96) != varg0) {
                if (!(0xff & owner_a[varg0])) { 。。。
    

最关键的一句address(varg1 >> 96) != varg0

由于反编译不会完全按照solidity的语法，所以原本不支持的位移运算符就出现了

不讲复杂的，总之这里将varg1 （NFTID）的其中一部分，和varg0（用户地址）做比较

这也意味着，原来此NFT的ID包含了用户的地址

我顿然醒悟，写个进制转换，将我在os上create得到的NFTid传入，解密得出

![](https://storage.googleapis.com/papyrus_images/58c116279ddf80cbd457ed5de657b1949184d4b5fe45fbf6d7f2120d5e33f477.png)

这个数值也和我在小狐狸抓包看到的data后半段内容是一致

这里还可以继续研读代码来挖掘细节

*   比如balanceOf 返回结果为2个值，会审计编号是否会超出\_totalSupply 限定发行量
    
*   比如safeTransferFrom 专门设置\_mint 防重放攻击，在其中若未mint则进行首次铸造
    

在解读出NFTID的组成后，我也顿时明白此合约的核心逻辑了

**此ID由3部分构成，用户地址+系列编号+指定ID** ，这也意味着无论我铸造多少次，前xx位都是固定的，即我的钱包地址转为10进制而已，而他查询的balanceof函数也因为我前缀统一，从而判别我是未铸造前的所有者，因此理论上我的铸造空间极其大。

因此小狐狸确实能导入，因为前缀一致，balanceof 在没有所有者的情况下，会默认依据此NFTID对应的空间的所有者返回结果，如果发生铸造转移，也有合约中配套的owner\_a 和\_creator 来证明这个创作者和当前所有者的关系。

至于为何小狐狸能出现NFT图，这点是他官方声明过的设置，出于读取更多维度的描述说明、稀有度、原图uri等信息，且用户可关闭这个钱包去查询os中心化数据库的功能。

![](https://storage.googleapis.com/papyrus_images/68caa3487eb6ffc03a753b418c96b024a4bfe020bedd77e07e34452c28ce4622.png)

破案总结
----

*   小狐狸是无辜的，他只是用标准方法走infura读取链上数据而已，并没有特别加工并修改返回数据
    
*   os不去验证合约，有一定自我市场竞争保护的目的，但是不能阻挡妙手玩家对合约分析，却给不少用户带来无法证明资产所有的困境
    
*   最终，确实其NFT资产在交易转移前未被铸造上链，但由NFTID定义的空间已经被特殊设计可查得余额结果，所以理论上其他交易平台也可买卖此NFT
    

欢迎各位同学从后台提交有趣的合约或交易哈希。

关注十四，用技术的眼光发现价值。

看到这里，帅气的你不点个赞吗?

![](https://storage.googleapis.com/papyrus_images/b48f70bbbea572ee015d69b3cc0cb9b004349b41bd23b1e91ba285c352944d08.png)

---

*Originally published on [shisi.eth](https://paragraph.com/@shisi-eth/opensea-nft)*
