区块链web3 基础知识,包括ABI、EIP、ERC等

区块链web3 基础知识,包括ABI、EIP、ERC等

ABI

一个是 solidity 提供了ABI的相关API函数:

  • abi.encode(...) returns (bytes):计算参数的ABI编码
  • abi.encodePacked(...) returns (bytes):计算参数的紧密打包编码 `
  • abi. encodeWithSelector(bytes4 selector, ...) returns (bytes): 计算函数选择器和参数的ABI编码
  • abi.encodeWithSignature(string signature, ...) returns (bytes):
    等价于* abi.encodeWithSelector(bytes4(keccak256(signature), ...)

用 "1" 作为参数调用set,如下图:

然后我们打开etherscan查看交易详情数据, 可以看到其附加数据如下图:

这个数据就是ABI的编码数据:

javascript 复制代码
0x60fe47b10000000000000000000000000000000000000000000000000000000000000001
javascript 复制代码
function abiEncode() public constant returns (bytes) {
        abi.encode(1);  // 计算1的ABI编码
        return abi.encodeWithSignature("set(uint256)", 1); //计算函数set(uint256) 及参数1 的ABI 编码
    }

EIP和ERC

EIP 是为以太坊生态提出的改进建议,涵盖协议升级、核心规则和智能合约标准等;
ERC 是一种特殊类型的 EIP,用于定义智能合约的标准,通常是应用层的开发者所使用。

简单来说,ERC 是以 EIP 提案形式提出并被广泛采用的智能合约标准。ERC 的编号是对应的 EIP 编号。例如:ERC-20 实际是 EIP-20。

ERC-20 (代币标准)

  • 功能:ERC-20 是一种广泛使用的代币标准,定义了代币的交互接口,包括转账、余额查询、供应量查询等。它允许智能合约表现得像标准化的、可预测的货币。
  • 内容 :ERC-20 标准包括基本的代币功能,如 transfer(), balanceOf(), approve(), 和 transferFrom(),这些函数确保了代币能够在整个以太坊网络中被统一和可靠地处理。

功能代码:

solidity 复制代码
function transfer(address to, uint256 value)
function approve(address owner,address spender,uint256 value)
function transferFrom(address from,address to,uint256 value)//由被授权者调用此方法,from为授权者
function mint(address account, uint256 value)
function burn(address account, uint256 value)

tips:

transfer和safeTransfer函数的区别?

  • _safeTransfer函数在发送代币之前会检查接收方地址是否是一个合约。如果是,它会调用该合约的onERC20Received函数(这是一个接口,由接收代币的合约实现)来确认合约能够接收代币。
  • 确保了接收代币的合约能够正确处理接收到的代币,从而避免了代币永久锁定在无法接收的合约地址中。
ERC20Permit.sol

该合约提供了一个新的授权操作,permit()函数的作用便是完成 owner对spender的授权,个人理解是,因为原ERC20中的approve函数必须是owner亲自去调用才能完成授权,这比较麻烦owner,而permit则是可以通过owner提供的签名来验证,并执行owner对spender的授权操作。

ERC20Burnable.sol

该合约提供了销币功能。

  • burn(uint256 value):销毁msg.sender的value个代币。
  • burnFrom(address account, uint256 value) :msg.sender销毁account的value个代币,前提是account给予msg.sender权限。
ERC20Capped.sol

给ERC20代币的的totalsupply设置上限,设置某个ERC20代币的发行量不能超过cap。限制的逻辑在这里(from==0,则被检测为铸币操作):

solidity 复制代码
if (from == address(0)) {
         uint256 maxSupply = cap();
         uint256 supply = totalSupply();
         if (supply > maxSupply) {
             revert ERC20ExceededCap(supply, maxSupply);
         }
     }
ERC20FlashMint.sol

该合约提供了一个借贷功能,只能借该合约生成的代币,且最大接待额为:token == address(this) ? type(uint256).max - totalSupply() : 0;还需要支付fee,这个借贷函数不需要主动还款,因为ta采用的是burn操作,直接将你手中借来的token全部销毁。

ERC20Pausable.sol

该合约提供了一个紧急停止功能,在_update()函数加上whenNotPaused修饰符

ERC20Snapshot.sol

通过快照机制记录当时的余额和总供应量,以备以后查阅。

solidity 复制代码
 		function _snapshot() internal virtual returns (uint256) 
    function _getCurrentSnapshotId() internal view virtual returns (uint256)
    function balanceOfAt(address account, uint256 snapshotId) public view virtual returns (uint256) 
    function totalSupplyAt(uint256 snapshotId) public view virtual returns (uint256)
    function _beforeTokenTransfer(address from,address to,uint256 amount) 
    function _valueAt(uint256 snapshotId, Snapshots storage snapshots) private view returns (bool, uint256) 
    function _updateAccountSnapshot(address account) 
    function _updateTotalSupplySnapshot()
    function _updateSnapshot(Snapshots storage snapshots, uint256 currentValue) 
    function _lastSnapshotId(uint256[] storage ids) private view returns (uint256)
ERC20Votes.sol

该合约支持DAO的投票和授权,正常投票前需要先授权代币

ERC20Wrapper.sol

该合约支持代币的包装,用户可以存入和取出_underlying代币,存入多少_underlying代币,就可以铸造多少ERC20Wrapper代币,同理取出多少 _underlying代币,便会销毁多少ERC20Wrapper代币代币。

ERC-721 (非同质化代币标准)

  • 功能:ERC-721 定义了非同质化代币(NFT)的标准,每个代币都是独一无二的,可以代表不同的资产,如艺术品、收藏品、房产等。
  • 内容 :ERC-721 包括transferFrom, approve, 和ownerOf等方法,每个方法都处理具有唯一标识符的独立代币。

代码功能:

solidity 复制代码
function transfer(address to, uint256 tokenId)
function setApprove(address to,uint256 tokenId)
function transferFrom(address to,uint256 tokenId)//由被授权者调用此方法,from为nft的owner
function mint(address account,uint256 tokenId,string memory tokenURI)
function burn(uint256 tokenId)

ERC165

ERC165 的核心方法

supportsInterface(bytes4 interfaceID) external view returns (bool)

用于检测合约是否支持某个接口。

interfaceID 是接口函数签名的 XOR(异或)结果。

参考链接:理解ERC165标准

ERC165的使用

目录结构

javascript 复制代码
contracts/
    ├─ MyContract.sol
    ├─ IMyInterface.sol
test/
    ├─ MyContract.t.sol
合约代码
javascript 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IMyInterface {
    function myFunction() external view returns (uint256);
}
javascript 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "./IMyInterface.sol";

contract MyContract is ERC165, IMyInterface {
    // 实现接口方法
    function myFunction() external pure override returns (uint256) {
        return 42;
    }

    // 声明支持的接口
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IMyInterface).interfaceId || super.supportsInterface(interfaceId);
    }
}
测试脚本
javascript 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import "forge-std/Test.sol";
import "../contracts/MyContract.sol";
import "../contracts/IMyInterface.sol";

contract MyContractTest is Test {
    MyContract myContract; // 实例化待测试的合约

    function setUp() public {
        // 部署 MyContract 合约
        myContract = new MyContract();
    }
    function testSupportsInterface() public {
        // 计算接口 ID
        bytes4 interfaceId = type(IMyInterface).interfaceId;
        console.logBytes32(interfaceId);

        // 测试是否支持接口
        bool isSupport = myContract.supportsInterface(interfaceId);

        console.log(" isSupport: ", isSupport);
        assertTrue(isSupport, "MyContract should support IMyInterface");
    }

    function testDoesNotSupportOtherInterfaces() public {
        // 一个不存在的接口 ID
        bytes4 fakeInterfaceId = 0x12345678;

        // 测试不支持的接口
        bool isSupport = myContract.supportsInterface(fakeInterfaceId);

        console.log("interfaceId= 0x12345678 isSupport: ", isSupport);
        assertFalse(isSupport, "MyContract should not support fake interface");
    }

    function testMyFunction() public {
        // 测试接口函数是否正常工作
        uint256 result = myContract.myFunction();
        console.log("myFunction() result: ", result);
        assertEq(result, 42, "myFunction should return 42");
    }
}

ERC-4626 (资产托管合约标准)

  • 功能:ERC4626 协议是一种用于代币化保险库的标准,它可以优化和统一收益保险库的技术参数。收益保险库是指使用不同策略来为用户提供最佳收益的合约,例如借贷市场、聚合器或本身具有利息的代币。
  • 内容:ERC4626 协议提供了一个标准的 API,用于表示单个底层 ERC-20 代币的收益保险库份额。,包括存入、取出资产的方法,并计算每个代币代表的底层资产份额。

份额计算公式shares = assets * totalSupply / totalAssets),计算结果是有小数点的,一般是向下取整。

代码功能:

preview 函数提供了一种方式,用于在不实际执行交易的情况下预估交易结果。这可以帮助用户了解如果他们进行特定的金库操作(如存款、提款、铸币或赎回),将会得到多少份额或资产。

solidity 复制代码
asset() //返回保险库使用的底层代币的地址,必须是一个 ERC-20 合约。
totalAssets() //返回保险库持有的底层资产的总量,应该包括任何由收益产生的复利。
convertToShares() //返回保险库为给定数量的底层资产兑换的份额数量。
convertToAssets() //返回保险库为给定数量的份额兑换的底层资产数量。
maxDeposit() //返回接收者在单次存款调用中可以存入的底层资产的最大数量。
previewDeposit() //允许用户在当前区块模拟他们的存款效果。
deposit() //将底层资产存入保险库,并将份额授予接收者。
maxMint() //返回接收者在单次铸造调用中可以铸造的份额的最大数量。
previewMint() //允许用户在当前区块模拟他们的铸造效果。
mint() //将份额铸造给接收者,并从保险库中取出相应数量的底层资产。
maxWithdraw() //返回接收者在单次取款调用中可以取出的底层资产的最大数量。
previewWithdraw() //允许用户在当前区块模拟他们的取款效果。
withdraw() //将份额从接收者处销毁,并将相应数量的底层资产从保险库中取出。
maxRedeem() //返回接收者在单次赎回调用中可以赎回的份额的最大数量。
previewRedeem() //允许用户在当前区块模拟他们的赎回效果。
redeem() //将份额从接收者处销毁,并将相应数量的底层资产从保险库中取出。

redeem 函数在 ERC-4626 标准中用于按份额赎回资产,允许用户根据持有的份额数量直接提取相应的资产。而 withdraw 函数则按用户指定的资产数量直接提取,计算并赎回所需的份额以匹配这一资产请求。

ERC-777 (高级代币标准)

  • 功能:ERC-777 介绍了一个新的代币标准,提供了比 ERC-20 更多的功能,如发送者和接收者挂钩功能,更灵活的交易处理。

  • 内容 :该标凈支持ERC-20的基本功能,并增加了操作员机制,允许用户授权第三方在其代币账户上执行操作,以及sendreceive钩子,这些钩子可以在代币被发送和接收时触发函数。

  • 原因:由于ERC20 标准没有一个转账通知机制 ,很多ERC20代币误转到合约之后,再也没有办法把币转移出来,已经有大量的ERC20 因为这个原因被锁死,如锁死的QTUM锁死的EOS

    另外一个问题是ERC20 转账时,无法携带额外的信息,例如:我们有一些客户希望让用户使用 ERC20 代币购买商品,因为转账没法携带额外的信息, 用户的代币转移过来,不知道用户具体要购买哪件商品,从而展加了线下额外的沟通成本。

    ERC777 在 ERC20的基础上定义了 send(dest, value, data) 来转移代币, send函数额外的参数用来携带其他的信息,send函数会检查持有者和接收者是否实现了相应的钩子函数,如果有实现(不管是普通用户地址还是合约地址都可以实现钩子函数),则调用相应的钩子函数。

  • **详细解释:**ERC777与ERC20兼容(兼容的意思就是ERC777的功能包括了ERC20的所有功能,实现兼容的方式就是,让ERC777直接继承IERC20接口。),同时引入了operator操作员的概念,操作员可以代表另一个地址(合约或者普通账户)发送代币,这个操作员的身份类似始于ERC20中被 某地址执行 approve操作后的身份,可以托管授权这的资产。同时还引进了sender和receiver的钩子函数(hooks)让代币持有者和代币接收者能有更多的处理。而且ERC777还采用了ERC1820标准的优点,可以判断某合约是否实现ERC777协议的相关接口,更重要的是还可以将sender/receiver的钩子函数放到 地址的 implement去处理,这样一来,使得整个代币体系更丰富,拓展性也大大增强。

    ERC-777是在ERC20的基础上增加了操作员机制

    • 普通操作员:即是代币的 holder亲自给 operator授权的,该 operator只能操作 该holder的代币。
    • 默认操作员:即合约初始化设置的操作员,ta的权限是最大的,默认情况下它可以操作所有人的代币,类似于管理员。但是用户可以自行将其移除权限,这样一来ta就不能操作自己的tokens了。
    solidity 复制代码
        // Immutable, but accounts may revoke them (tracked in __revokedDefaultOperators).
        mapping(address => bool) private _defaultOperators;
        // For each account, a mapping of its operators and revoked default operators.
        mapping(address => mapping(address => bool)) private _operators;
        mapping(address => mapping(address => bool)) private _revokedDefaultOperators;
关键代码,token转移 send函数
solidity 复制代码
function _send(
        address operator,
        address from,
        address to,
        uint256 amount,
        bytes memory userData,
        bytes memory operatorData,
        bool requireReceptionAck
    )
        private
    {
        require(from != address(0), "ERC777: send from the zero address");
        require(to != address(0), "ERC777: send to the zero address");
        _callTokensToSend(operator, from, to, amount, userData, operatorData);
        _move(operator, from, to, amount, userData, operatorData);
        _callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck);
    }
  • _callTokensToSend函数 ,先通过注册表getInterfaceImplementer(from, TOKENS_SENDER_INTERFACE_HASH)查看 tokenHolder 用来实现IERC777TokensSender接口的合约地址ADDRESS,如果有则调用ADDRESS中的tokensToSend函数。

    solidity 复制代码
            address implementer = _erc1820.getInterfaceImplementer(from, TOKENS_SENDER_INTERFACE_HASH);
            if (implementer != address(0)) {
                IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData);
            }
  • _move函数则是负责更新余额,修改Holder和receiver的代币余额

    solidity 复制代码
      _balances[from] = _balances[from].sub(amount);
      _balances[to] = _balances[to].add(amount);
  • _callTokensReceived函数 ,先通过注册表getInterfaceImplementer(from, TOKENS_RECIPIENT_INTERFACE_HASH)查看 tokenHolder 用来实现IERC777Recipient接口的合约地址ADDRESS,如果有则调用ADDRESS中的tokensReceived函数,如果没有还需要判断传入的参数requireReceptionAck,如果参数为 true,那么则需要检测接收者to是否为合约地址,如果是合约地址则revert(其目的即使为了保证contract receiver必须实现 ERC777TokensRecipient)。

    solidity 复制代码
            address implementer = _erc1820.getInterfaceImplementer(to, TOKENS_RECIPIENT_INTERFACE_HASH);
            if (implementer != address(0)) {
                IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData);
            } else if (requireReceptionAck) {
                require(!to.isContract(), "ERC777: token recipient contract has no implementer for ERC777TokensRecipient");
            }

EIP-2612 (代币许可)

  • 功能:EIP-2612 允许代币持有者通过签名方式授权他人在他们的账户上进行交易,而无需实际发送交易,从而节约了Gas成本。
  • 内容 :它引入了permit方法,使得代币的批准(通常通过approve完成)可以通过签名方式离线完成,这种方式与ERC-20的approve/transferFrom模式相兼容。

ERC-1155 (多代币标准)

  • 功能:ERC-1155 允许在单一合约中定义和管理多种代币类型,包括可替代和非可替代代币。
  • 内容:该标准提供了一种高效的代币管理方式,通过一个合约调用来批量处理多种代币类型的转移,减少了交易成本。
solidity 复制代码
balanceOf() //单币种余额查询,返回account拥有的id种类的代币的持仓量。
balanceOfBatch() //多币种余额查询,查询的地址accounts数组和代币种类ids数组的长度要相等。
setApprovalForAll() //批量授权,将调用者的代币授权给operator地址。。
isApprovedForAll() //查询批量授权信息,如果授权地址operator被account授权,则返回true。
safeTransferFrom() //安全单币转账,将amount单位id种类的代币从from地址转账给to地址。如果to地址是合约,则会验证是否实现了onERC1155Received()接收函数。
safeBatchTransferFrom() //安全多币转账,与单币转账类似,只不过转账数量amounts和代币种类ids变为数组,且长度相等。如果to地址是合约,则会验证是否实现了onERC1155BatchReceived()接收函数。

SBT (Soulbound Tokens,灵魂绑定代币)

  • 功能:SBT是一种不能转让的代币,用于代表用户身份、资质、经历或信誉等个人属性或成就。
  • 内容 :实际上是一个特殊类型的 NFT,它被设计为不可转让或交易。
solidity 复制代码
1) 铸造 NFT
mint 函数允许合约所有者为指定的地址创建一个新的 SBT。
2)禁止转移
通过重写 transferFrom 方法,禁止 SBT 的转移。任何尝试转移 SBT 的操作都会失败。
3)禁止授权
通过重写 approve 和 setApprovalForAll 方法来禁止授权操作。
相关推荐
qiquandongkh3 小时前
期权懂|深度虚值期权合约有哪些特性?
大数据·人工智能·区块链
木西14 小时前
实现一个简洁版的NFT交易所
web3·智能合约·solidity
选择不变14 小时前
操盘训练营实战之-福龙马四板是如何布局和操作的
大数据·区块链·通达信指标公式·炒股技巧·炒股指标
风染yeye14 小时前
区块链期末复习3:跨链原子交换&其他加密货币
算法·区块链·密码学·分布式账本
投资小箱子1 天前
算力股开盘大涨,电光科技7连板
大数据·python·科技·区块链·fastapi
学术会议2 天前
【科技塑造智能未来】2025年大数据、遥感与图像处理国际会议 (BDRSIP 2025)
大数据·人工智能·科技·区块链
koko爱英语2 天前
区块链安全常见的攻击分析——私有数据泄露 (Private Data Exposure)【7】
安全·区块链
Footprint_Analytics2 天前
2024 年公链行业年度报告:从基建竞赛到应用突破
区块链
陀螺财经2 天前
回顾2024加密市场的“得与失”,2025路在何方?
区块链
风染yeye2 天前
区块链期末复习3.2:比特币脚本
算法·区块链·密码学·分布式账本