solidity实现ERC1155多代币标准

文章目录

  • 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 开源地址。


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

『 C++ 』BinarySearchTree搜索二叉树

文章目录 前言 &#x1f995;二叉搜索树的概念 &#x1f995;搜索二叉树的初始化 &#x1f995;Insert( )插入函数 &#x1f995;&#x1f47e; InsertR( ) 插入函数(递归) InOrder( ) 中序遍历打印 &#x1f995;Find( ) 查找函数 &#x1f995;&#x1f47e; Find( ) 查找函数…

【Ajax】发送get请求获取接口数据

编写html实现通过Ajax发送get请求并获取数据 代码实现 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title…

Libsvm中grid.py文件的解读

1.导入相关文件 这里重点讲一下 _ _all_ _ [find_parameters] &#xff1a; _all__ [find_parameters] 是 Python 中用于定义模块级别的变量 __all__ 的语法&#xff0c; __all__ 是一个包含模块中应该被公开&#xff08;即可以通过 from module import * 导入&#xff09;的…

【开源】基于Vue+SpringBoot的快乐贩卖馆管理系统

项目编号&#xff1a; S 064 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S064&#xff0c;文末获取源码。} 项目编号&#xff1a;S064&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 搞笑视频模块2.3 视…

CentOS上安装和配置Apache HTTP服务器

在CentOS系统上安装和配置Apache HTTP服务器可以为您的网站提供可靠的托管环境。Apache是开源的Web服务器软件&#xff0c;具有广泛的支持和强大的功能。下面是在CentOS上安装和配置Apache HTTP服务器的步骤&#xff1a; 步骤一&#xff1a;安装Apache HTTP服务器 打开终端&am…

总结一篇本地idea配合阿里云服务器使用docker

idea打包打镜像发到阿里云服务器 先说一下使用docker desktop软件怎么使用 1.下载docker desktop官网&#xff0c;先注册个账号吧&#xff0c;后面桌面软件登录会用到&#xff08;当然&#xff0c;配合这个软件使用需要科学上网&#xff09; 安装这个要配合wsl使用&#xf…

电脑搜不自己的手机热点,其余热点均可!

一、现象&#xff1a; 之前可正常连接&#xff0c;突然间发现收不到自己的WiFi信号&#xff0c;其余人均可收到。通过重复手机电脑关机、改变热点设置中的频段等方式均没解决&#xff0c;同事电脑和手机可搜索到我的WiFi。 二、问题&#xff1a; WiF驱动程序更新 三&#x…

ingress介绍和ingress通过LoadBalancer暴露服务配置

目录 一.ingress基本原理介绍 1.将原有用于暴露服务和负载均衡的服务的三四层负载均衡变为一个七层负载均衡 2.controller和ingress 3.通过下面这个图可能会有更直观的理解 二.为什么会出现ingress 1.NodePort存在缺点 2.LoadBalancer存在缺点 三.ingress三种暴露服务的…

『TypeScript』从零开始编写你的第一个TypeScript程序

&#x1f4e3;读完这篇文章里你能收获到 了解TypeScript及为什么使用TypeScriptTypeScript的安装过程编写第一个HelloTs程序 文章目录 一、TypeScript简介1. 什么是TypeScript&#xff1f;2. 为什么选择使用TypeScript&#xff1f;2.1 静态类型检查2.2 更好的代码维护性2.3 更…

Prometheus 配置文件和标签 Pmsql

1.Prometheus配置文件 Prometheus可以通过命令行或者配置文件的方式对服务进行配置。 命令行方式一般用于不可变的系统参数配置&#xff0c;例如存储位置、要保留在磁盘和内存中的数据量等&#xff1b;配置文件用于定义与数据动态获取相关的配置选项和文件等内容。命令行方式…

Sql Server关于表的建立、修改、删除

表的创建&#xff1a; &#xff08;1&#xff09;在“对象资源管理器”面板中展开“数据库”节点&#xff0c;可以看到自己创建的数据库&#xff0c;比如Product。展开Product节点&#xff0c;右击“表”节点&#xff0c;在弹出的快捷菜单中选择“新建表”项&#xff0c;进入“…

【keil备忘录】2. stm32 keil仿真时的时间测量功能

配置仿真器Trace内核时钟为单片机实际的内核时钟&#xff0c;需要勾选Enable设置&#xff0c;设置完成后Enable取消勾选也可以&#xff0c;经测试时钟频率配置仍然生效&#xff0c;此处设置为48MHZ: 时间测量时必须打开register窗口&#xff0c;否则可能不会计数 右下角有计…

AMD 发布新芯片MI300,支持训练和运行大型语言模型

AMD 宣布推出 MI300 芯片&#xff0c;其 Ryzen 8040移动处理器将于2024年用于笔记本电脑。 AMD官方网站&#xff1a;AMD ׀ together we advance_AI AMD——美国半导体公司专门为计算机、通信和消费电子行业设计和制造各种创新的微处理器&#xff08;CPU、GPU、主板芯片组、电…

【AIGC】prompt工程从入门到精通

注&#xff1a;本文示例默认“文心大模型3.5”演示&#xff0c;表示为>或w>&#xff08;wenxin)&#xff0c;有时为了对比也用百川2.0展示b>&#xff08;baichuan) 有时候为了模拟错误输出&#xff0c;会用到m>&#xff08;mock)表示&#xff08;因为用的大模型都会…

SLAM算法与工程实践——SLAM基本库的安装与使用(3):Pangolin库

SLAM算法与工程实践系列文章 下面是SLAM算法与工程实践系列文章的总链接&#xff0c;本人发表这个系列的文章链接均收录于此 SLAM算法与工程实践系列文章链接 下面是专栏地址&#xff1a; SLAM算法与工程实践系列专栏 文章目录 SLAM算法与工程实践系列文章SLAM算法与工程实践…

网络管理相关

管理功能分为管理站manager和代理agent两部分。 网络管理&#xff1a; 网络管理系统中&#xff0c;每一个网络节点都包含有一组与管理有关的软件&#xff0c;叫做网络管理实体NME。 管理站的另外一组软件叫做网络管理应用NMA&#xff0c;提供用户接口&#xff0c;根据用户命令显…

http与apache

目录 1.http相关概念 2.http请求的完整过程 3.访问浏览器背后的原理过程 4.动态页面与静态页面区别 静态页面&#xff1a; 动态页面&#xff1a; 5.http协议版本 6.http请求方法 7.HTTP协议报文格式 8.http响应状态码 1xx&#xff1a;提示信息 2xx&#xff1a;成功…

css 十字分割线(含四等分布局)

核心技术 伪类选择器含义li:nth-child(2)第2个 lili:nth-child(n)所有的lili:nth-child(2n)所有的第偶数个 lili:nth-child(2n1)所有的第奇数个 lili:nth-child(-n5)前5个 lili:nth-last-child(-n5)最后5个 lili:nth-child(7n)选中7的倍数 border-right: 3px solid white;borde…

ssh安装和Gitee(码云)源码拉取

文章目录 安装ssh服务注册码云公钥设置码云账户SSH公钥安装git客户端和git-lfs源码获取 安装ssh服务 更新软件源&#xff1a; sudo apt-get update安装ssh服务 sudo apt-get install openssh-server检查ssh是否安装成功 which ssh输出&#xff1a; /usr/bin/ssh启动ssh 服…

AI并行计算:CUDA和ROCm

1 介绍 1.1 CUDA CUDA&#xff08;Compute Unified Device Architecture&#xff09;是Nvidia于2006年推出的一套通用并行计算架构&#xff0c;旨在解决在GPU上的并行计算问题。其易用性和便捷性能够方便开发者方便的进行GPU编程&#xff0c;充分利用GPU的并行能力&#xff0…