solidity实现ERC1155多代币标准

news2024/9/27 5:59:39

文章目录

  • 1、NFT - 维基百科
  • 2、IERC1155MetadataURI
  • 3、IERC1155Receiver
  • 4、IERC1155
  • 5、ERC1155
  • 6、NFT1155
  • 7、开源地址

1、NFT - 维基百科

ERC-1155 标准于2018年6月由Witek Radomski、Andrew Cooke、Philippe Castonguay、James Therien、Eric Binet及Ronan Sandford提出。此标准可针对单一应用合约,提供同质代币、非同质代币(用ERC-1155建构ERC-721资产)及其他结构类型(例如半同质代币,semi-fungible tokens)的多元组合。相较于ERC-721每创立一个代币ID就对应一个单独智能合约资产,在ERC-1155多元代币标准下,每个代币ID可以代表一系列不同类型的资产,并附加量化区块以标示钱包中各类型资产的数量;同类型资产可以互换,要转让多少数量也可以。
ERC-1155 标准下的NFT结构上更有弹性:拥有元数据、供应量与其他属性;新功能包括:可一次传输多种代币类型以节省交易成本、也可同时进行多个代币交易(托管/原子交换)而无需逐一批准每个合约、可在单一应用合约中注明并混搭多种FT、NFT及SFT。

2、IERC1155MetadataURI

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;


// ERC1155的可选接口,加入了uri()函数查询元数据
interface IERC1155MetadataURI  {
    // 返回第`id`种类代币的URI
    function uri(uint256 id) external view returns (string memory);
}

3、IERC1155Receiver

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "./IERC165.sol";

interface IERC1155Receiver is IERC165 {
    // 接受ERC1155安全转账`safeTransferFrom`     
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    // 接受ERC1155批量安全转账`safeBatchTransferFrom` 
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}

4、IERC1155

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "./IERC165.sol";

// https://eips.ethereum.org/EIPS/eip-1155[EIP].
interface IERC1155 is IERC165 {
    // 单类代币转账事件
    event TransferSingle(
        address indexed operator, 
        address indexed from, 
        address indexed to, 
        uint256 id, 
        uint256 value
    );

    // 批量代币转账事件, ids和values为转账的代币种类和数量数组
    event TransferBatch(
        address indexed operator, 
        address indexed from, 
        address indexed to, 
        uint256[] ids, 
        uint256[] values
    );

    // 批量授权事件, 当`account`将所有代币授权给`operator`时释放
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    // 当`id`种类的代币的URI发生变化时释放,`value`为新的URI
    event URI(string value, uint256 indexed id);

    // 持仓查询,返回`account`拥有的`id`种类的代币的持仓量
    function balanceOf(address account, uint256 id) external view returns (uint256);

    // 批量持仓查询,`accounts`和`ids`数组的长度要想等。
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);

    // 批量授权,将调用者的代币授权给`operator`地址。释放{ApprovalForAll}事件.
    function setApprovalForAll(address operator, bool approved) external;

    // 批量授权查询,如果授权地址`operator`被`account`授权,则返回`true`
    function isApprovedForAll(address account, address operator) external view returns (bool);

    // 安全转账,将`amount`单位`id`种类的代币从`from`转账给`to`.  释放{TransferSingle}事件.
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;

    // 批量安全转账, 释放{TransferBatch}事件
    function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
}

5、ERC1155

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "./IERC1155.sol";
import "./IERC1155Receiver.sol";
import "./IERC1155MetadataURI.sol";
import "./Address.sol";
import "./Strings.sol";
import "./IERC165.sol";

// ERC1155多代币标准 https://eips.ethereum.org/EIPS/eip-1155
contract ERC1155 is IERC165, IERC1155, IERC1155MetadataURI {
    using Address for address; 
    using Strings for uint256; 
    // Token名称
    string public name;
    // Token代号
    string public symbol;
    // 代币种类id 到 账户account 到 余额balances 的映射
    mapping(uint256 => mapping(address => uint256)) private _balances;
    // address 到 授权地址 的批量授权映射
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // 构造函数,初始化`name` 和`symbol`, uri_
    constructor(string memory name_, string memory symbol_) {
        name = name_;
        symbol = symbol_;
    }

    function supportsInterface(bytes4 interfaceId) 
        public view virtual override returns (bool) {
        return
            interfaceId == type(IERC1155).interfaceId ||
            interfaceId == type(IERC1155MetadataURI).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }

    // 持仓查询 实现IERC1155的balanceOf,返回account地址的id种类代币持仓量。
    function balanceOf(address account, uint256 id) 
        public view virtual override returns (uint256) {
        require(account != address(0), "ERC1155: address zero is not a valid owner");
        return _balances[id][account];
    }

    // @dev 批量持仓查询   
    function balanceOfBatch(address[] memory accounts, uint256[] memory ids) 
        public view virtual override returns (uint256[] memory) {
        require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
        uint256[] memory batchBalances = new uint256[](accounts.length);

        for (uint256 i = 0; i < accounts.length; ++i) {
            batchBalances[i] = balanceOf(accounts[i], ids[i]);
        }
        
        return batchBalances;
    }

    //  批量授权,调用者授权operator使用其所有代币
    function setApprovalForAll(address operator, bool approved) 
        public virtual override {
        require(msg.sender != operator, "ERC1155: setting approval status for self");
        _operatorApprovals[msg.sender][operator] = approved;
        emit ApprovalForAll(msg.sender, operator, approved);
    }
                                    
    // 查询批量授权.
    function isApprovedForAll(address account, address operator) 
        public view virtual override returns (bool) {
        return _operatorApprovals[account][operator];
    }

    // 安全转账,将`amount`单位的`id`种类代币从`from`转账到`to`
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes memory data) 
        public virtual override {
        address operator = msg.sender;
        // 调用者是持有者或是被授权
        require(
            from == operator || isApprovedForAll(from, operator),
            "ERC1155: caller is not token owner nor approved"
        );
        require(to != address(0), "ERC1155: transfer to the zero address");
        // from地址有足够持仓
        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
        // 更新持仓量
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }
        _balances[id][to] += amount;
        // 释放事件
        emit TransferSingle(operator, from, to, id, amount);
        // 安全检查
        _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);    
    }

    // 批量安全转账,将`amounts`数组单位的`ids`数组种类代币从`from`转账到`to`
    function safeBatchTransferFrom(
        address from, address to, uint256[] memory ids, 
        uint256[] memory amounts, bytes memory data) 
        public virtual override {
        address operator = msg.sender;
        // 调用者是持有者或是被授权
        require(
            from == operator || isApprovedForAll(from, operator),
            "ERC1155: caller is not token owner nor approved"
        );
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
        require(to != address(0), "ERC1155: transfer to the zero address");

        // 通过for循环更新持仓  
        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
        }

        emit TransferBatch(operator, from, to, ids, amounts);
        // 安全检查
        _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);    
    }

    // 锻造
    function _mint(address to, uint256 id, uint256 amount, bytes memory data) 
        internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");

        address operator = msg.sender;

        _balances[id][to] += amount;
        emit TransferSingle(operator, address(0), to, id, amount);

        _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
    }

    // 批量铸造
    function _mintBatch(address to, uint256[] memory ids, 
        uint256[] memory amounts, bytes memory data) 
        internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = msg.sender;

        for (uint256 i = 0; i < ids.length; i++) {
            _balances[ids[i]][to] += amounts[i];
        }

        emit TransferBatch(operator, address(0), to, ids, amounts);

        _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
    }

    // 销毁
    function _burn(address from, uint256 id, uint256 amount) internal virtual {
        require(from != address(0), "ERC1155: burn from the zero address");

        address operator = msg.sender;
        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");

        unchecked {
            _balances[id][from] = fromBalance - amount;
        }

        emit TransferSingle(operator, from, address(0), id, amount);
    }

    // 批量销毁
    function _burnBatch(address from, uint256[] memory ids, uint256[] memory amounts) 
        internal virtual {
        require(from != address(0), "ERC1155: burn from the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = msg.sender;

        for (uint256 i = 0; i < ids.length; i++) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
        }

        emit TransferBatch(operator, from, address(0), ids, amounts);
    }

    // 安全转账检查
    function _doSafeTransferAcceptanceCheck( address operator, address from, address to,
        uint256 id, uint256 amount, bytes memory data) private {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                if (response != IERC1155Receiver.onERC1155Received.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non-ERC1155Receiver implementer");
            }
        }
    }

    // ERC1155的批量安全转账检查
    function _doSafeBatchTransferAcceptanceCheck( 
        address operator, address from, address to,
        uint256[] memory ids, uint256[] memory amounts, bytes memory data) private {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                bytes4 response
            ) {
                if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non-ERC1155Receiver implementer");
            }
        }
    }

    // 返回id种类代币的uri,存储metadata,类似ERC721的tokenURI.
    function uri(uint256 id)  public view virtual override returns (string memory) {
        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, id.toString())) : "";
    }

    // 计算{uri}的BaseURI,uri就是把baseURI和tokenId拼接在一起,需要开发重写.
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }
}

6、NFT1155

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;


import "./ERC1155.sol"; 

contract NFT1155 is ERC1155 {
    uint256 constant MAX_ID = 10000;

    constructor() ERC1155("NFT1155", "NFT1155"){

    }

    function _baseURI() internal pure override returns (string memory) {
        return "ipfs://QmcPGA3kecSMfHcv8i6wpgB2RkPA1FSVqu6AZY3rKNmxuj/";
    }

    function mint(address to, uint256 id, uint256 amount) external {
        require(id < MAX_ID, "id overflow");
        _mint(to, id, amount, "");
    }

    function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts) external {
        for(uint256 i = 0; i < ids.length;i++){
            require(ids[i] < MAX_ID, "id overflow");
        }
        _mintBatch(to, ids, amounts, "");
    }
}

测试运行:
在这里插入图片描述

7、开源地址

gitee 开源地址。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1284601.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RK3568平台开发系列讲解(Linux系统篇)device_node 转换成 platform_device

🚀返回专栏总目录 文章目录 一、DTB转换规则二、转换源码分析沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍通过设备树 device_node 转换成 platform_device 一、DTB转换规则 device 部分是用 platform_device 结构体来描述硬件资源的, 所以内核最终会将…

LeetCode(47)合并区间【区间】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 合并区间 1.题目 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中…

VMware安装Ubuntu系统(Server端,Desktop端步骤一样)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

【C++】开源:Boost进程间通信库InterProcess配置使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍Boost进程间通信库InterProcess配置使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一…

IntelliJ IDEA图形安装教程

IntelliJ IDEA图形安装教程 之前开始Java程序&#xff0c;一直用的eclipse&#xff0c;觉得还可以。一直听说IntelliJ IDEA比eclipse好用很多&#xff0c;但因为比较懒&#xff0c;也没有学习使用。机缘巧合下&#xff0c;尝试用了下&#xff0c;顿时有种相见恨晚的感觉&#…

翻译: 生成式人工智能的工作原理How Generative AI works

ChatGPT 和 Bard 等系统生成文本的能力几乎像魔法一样。它们确实代表了 AI 技术的一大步进。但是文本生成到底是如何工作的呢&#xff1f;在这个视频中&#xff0c;我们将看看生成式 AI 技术的底层原理&#xff0c;这将帮助你理解你可以如何使用它&#xff0c;以及何时可能不想…

掌控安全 -- POST 注入

POST注入第一题 既然是POST注入&#xff0c;那么再用GET传参的方式就不能用了 万能密码登录一下试试 这里显示成功登录&#xff0c;然后就可以知道登录框这里存在注入&#xff0c;也就是可以构造sql语句进行注入&#xff0c;然后就是正常的注入手法了。 or 11 order by 1# 这…

【算法专题】前缀和

前缀和 前缀和1. 前缀和【模板】2. 二维前缀和【模板】3. 寻找数组的中心下标4. 除自身以外数组的乘积5. 和为K的子数组6. 和可被K整除的子数组7. 连续数组8. 矩阵区域和 前缀和 1. 前缀和【模板】 题目链接 -> Nowcoder -DP34.前缀和【模板】 Nowcoder -DP34.前缀和【模…

【Vulnhub 靶场】【Prime (2021): 2】【简单 - 中等】【20210509】

1、环境介绍 靶场介绍&#xff1a;https://www.vulnhub.com/entry/prime-2021-2,696/ 靶场下载&#xff1a;https://download.vulnhub.com/prime-2021/Prime-2.ova 靶场难度&#xff1a;简单 - 中等 发布日期&#xff1a;2021年5月9日 文件大小&#xff1a;3.7 GB 靶场作者&am…

菜鸟学习日记(python)——运算符

我们进行运算时&#xff0c;需要两类数据&#xff0c;操作数和运算符&#xff0c;例如&#xff1a;ab就是一个运算&#xff0c;它的操作数是a和b&#xff0c;运算符是‘’ 在python中运算符包括以下几大类&#xff1a; 算数运算符比较&#xff08;关系&#xff09;运算符赋值…

图像万物分割——Segment Anything算法解析与模型推理

一、概述 在视觉任务中&#xff0c;图像分割任务是一个很广泛的领域&#xff0c;应用于交互式分割&#xff0c;边缘检测&#xff0c;超像素化&#xff0c;感兴趣目标生成&#xff0c;前景分割&#xff0c;语义分割&#xff0c;实例分割&#xff0c;泛视分割等。 交互式分割&am…

二、DVP摄像头调试笔记(dvp外设驱动篇、寄存器篇)

问题描述点 &#xff08; 存在MCK&#xff0c;I2C数据都正常发送情况排查&#xff09; 一、首先排查行同步信号和场同步信号是否与芯片的有效电平是否一致 1、确认当前camera寄存器的VSYNC寄存器设置是高有效还是低有效 2、确认当前camear寄存器的HSYNC寄存器设置是高有效还是…

12.4_黑马MybatisPlus笔记(下)

目录 11 12 thinking&#xff1a;关于Mybatis Plus中BaseMapper和IService&#xff1f; 13 ​编辑 thinking&#xff1a;CollUtil.isNotEmpty? 14 thinking&#xff1a;Collection、Collections、Collector、Collectors&#xff1f; thinking&#xff1a;groupBy&#…

手机升级到iOS15.8后无法在xcode(14.2)上真机调试

之前手机是iOS14.2的系统,在xcode上进行真机测试运行良好&#xff0c;因为想要使用Xcode的Instruments功能&#xff0c;今天将系统更新到了iOS15.8 &#xff0c;结果崩了 说是Xcode和手机系统不兼容不能进行真机测试。在网上查不好些方法&#xff0c;靠谱的就是下载相关版本的…

配置攻击防范示例

1、组网需求。 如果局域网内存在Hacker向SwitchA发起畸形报文攻击、分片报文攻击和泛洪攻击&#xff0c;将会造成SwitchA瘫痪。为了预防这种情况&#xff0c;管理员希望通过在SwitchA上部署各种攻击防范措施来为用户提供安全的网络环境&#xff0c;保障正常的网络服务。 2、配…

[读论文]BK-SDM: A Lightweight, Fast, and Cheap Version of Stable Diffusion

github: GitHub - Nota-NetsPresso/BK-SDM: A Compressed Stable Diffusion for Efficient Text-to-Image Generation [ICCV23 Demo] [ICML23 Workshop] ICML 2023 Workshop on ES-FoMo 简化方式 蒸馏方式&#xff08;训练Task蒸馏outKD-FeatKD&#xff09; 训练数据集 评测指标…

今日实施|解读新国标对数据库审计的能力要求

数据库审计是数据安全建设不可或缺的技术工具之一&#xff0c;无论是国家级的法律或标准&#xff0c;还是等保以及行业级的安全标准均对使用数据库审计有明确要求。据相关数据统计显示&#xff0c;数据库审计产品的市场需求已占据中国数据库安全市场容量的6成以上。 12月1日&am…

openGauss学习笔记-141 openGauss 数据库运维-例行维护-例行重建索引

文章目录 openGauss学习笔记-141 openGauss 数据库运维-例行维护-例行重建索引141.1 背景信息141.2 重建索引141.3 操作步骤 openGauss学习笔记-141 openGauss 数据库运维-例行维护-例行重建索引 141.1 背景信息 数据库经过多次删除操作后&#xff0c;索引页面上的索引键将被…

Spring Session介绍

Spring SessionSession储存到Redis1&#xff1a;添加依赖2&#xff1a;配置信息3:Spring Session存String3:Spring Session存对象 项目改造-Redis储存Session Spring Session Spring Session储存在Redis和取的执行流程&#xff1a; 1&#xff1a;request.getSession() 方法时&…

微软 Power Platform 零基础 Power Pages 网页搭建实际案例实践(三)

微软 Power Platform 零基础 Power Pages 网页搭建教程之案例实践学习&#xff08;三&#xff09;结合Power Apps和Power Automate Power Pages 实际案例学习 微软 Power Platform 零基础 Power Pages 网页搭建教程之案例实践学习&#xff08;三&#xff09;结合Power Apps和Po…