Michael.W基于Foundry精读Openzeppelin第23期——ERC165Checker.sol

Michael.W基于Foundry精读Openzeppelin第23期——ERC165Checker.sol

      • 0. 版本
        • 0.1 ERC165Checker.sol
      • 1. 目标合约
      • 2. 代码精读
        • 2.1 supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId)
        • 2.2 supportsERC165(address account)
        • 2.3 supportsInterface(address account, bytes4 interfaceId)
        • 2.4 getSupportedInterfaces(address account, bytes4[] memory interfaceIds)
        • 2.5 supportsAllInterfaces(address account, bytes4[] memory interfaceIds)

0. 版本

[openzeppelin]:v4.8.3,[forge-std]:v1.5.6

0.1 ERC165Checker.sol

Github: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.3/contracts/utils/introspection/ERC165Checker.sol

ERC165Checker库是用来查询已实现IERC165的目标合约自身实现了哪些interface的工具库。在使用时需要注意:利用ERC165Checker提供的查询方法进行查询的过程不会因为目标合约没有实现待查询interface而发生revert。而是通过bool变量来标识目标合约对待查询interface的实现情况,查询方可以通过该bool值进行判断从而进行下一步的逻辑处理。

1. 目标合约

封装ERC165Checker library成为一个可调用合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/src/utils/introspection/MockERC165Checker.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;import "openzeppelin-contracts/contracts/utils/introspection/ERC165Checker.sol";contract MockERC165Checker {using ERC165Checker for address;function supportsERC165(address account) external view returns (bool){return account.supportsERC165();}function supportsInterface(address account, bytes4 interfaceId) external view returns (bool){return account.supportsInterface(interfaceId);}function getSupportedInterfaces(address account, bytes4[] memory interfaceIds)externalviewreturns (bool[] memory){return account.getSupportedInterfaces(interfaceIds);}function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) external view returns (bool){return account.supportsAllInterfaces(interfaceIds);}function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) external view returns (bool){return account.supportsERC165InterfaceUnchecked(interfaceId);}
}

全部foundry测试合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/test/utils/introspection/ERC165Checker.t.sol

2. 代码精读

2.1 supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId)

查询目标地址account是否继承了interface id对应的interface。即只做对应interface id的显式支持interface检查,而不做对IERC165 interface的支持检查。

注:方法内部使用了内联汇编的staticcall,如果account合约没有实现IERC165 interface 也不会revert。

    function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {// 拼接调用supportsInterface(bytes4 interfaceId)的calldatabytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId);// 内联汇编中static call目标合约的supportsInterface方法bool success;uint256 returnSize;uint256 returnValue;assembly {// 内联汇编中的staticcall的参数为:staticcall(g, a, in, insize, out, outsize):// - g: 本次调用时设置的gas上限为30000// - a: call的合约地址为account// - in: memory中的staticcall的calldata的起始位置,即encodedParams指针+32个字节// - insize: memory中的staticcall的calldata的长度,即encodedParams指针指向内存中的值// - out: memory中存放返回数据的起始位置,即memory中第一个字位置0x00// - outsize: memory中存放返回数据的长度——0x20个字节(因为supportsInterface方法返回值为bool,内存中用一个字来存储)// 注: 内联汇编staticcall详解参见博文:https://blog.csdn.net/michael_wgy_/article/details/132196437success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)// static call的returndata的字节长度returnSize := returndatasize()// 从memory中取出staticcall的返回值,即bool返回值内容returnValue := mload(0x00)}// 返回true的条件(与关系):// 1. success为true —— static call成功调用;// 2. static call的return data字节长度>=32,即staticcall有返回数据;// 3. static call的返回值为truereturn success && returnSize >= 0x20 && returnValue > 0;}

foundry代码验证

import "openzeppelin-contracts/contracts/utils/introspection/ERC165.sol";
import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";// all kinds of target contracts for test// no method `supportsInterface(bytes4 interfaceId)`
contract SupportNone {}contract SupportERC165 is ERC165 {}contract SupportERC165ButNotInvalidInterfaceId is ERC165 {function supportsInterface(bytes4 interfaceId) public view override returns (bool) {return interfaceId == 0xffffffff || super.supportsInterface(interfaceId);}
}interface ICustomized {function helloMichael() external view returns (string memory);
}contract SupportManyInterfacesButNotERC165 is ERC20, ICustomized {string _str = "michael.w";constructor()ERC20("", ""){}function helloMichael() external view returns (string memory){return _str;}function supportsInterface(bytes4 interfaceId) public pure returns (bool) {return interfaceId == type(IERC20).interfaceId ||interfaceId == type(IERC20Metadata).interfaceId ||interfaceId == type(ICustomized).interfaceId;}
}contract SupportManyInterfacesWithERC165 is ERC165, ERC20, ICustomized {string _str = "michael.w";constructor()ERC20("", ""){}function helloMichael() external view returns (string memory){return _str;}function supportsInterface(bytes4 interfaceId) public view override returns (bool) {return interfaceId == type(IERC20).interfaceId ||interfaceId == type(IERC20Metadata).interfaceId ||interfaceId == type(ICustomized).interfaceId ||super.supportsInterface(interfaceId);}
}contract ERC165CheckerTest is Test {MockERC165Checker testing = new MockERC165Checker();SupportNone supportNone = new SupportNone();SupportERC165 supportERC165 = new SupportERC165();SupportERC165ButNotInvalidInterfaceId supportERC165ButNotInvalidInterfaceId = new SupportERC165ButNotInvalidInterfaceId();SupportManyInterfacesButNotERC165 supportManyInterfacesButNotERC165 = new SupportManyInterfacesButNotERC165();SupportManyInterfacesWithERC165 supportManyInterfacesWithERC165 = new SupportManyInterfacesWithERC165();bytes4 constant INTERFACE_ID_INVALID = 0xffffffff;function test_SupportsERC165InterfaceUnchecked() external {// case 1: query to contract SupportNone without revert and return falseassertFalse(testing.supportsERC165InterfaceUnchecked(address(supportNone), type(IERC165).interfaceId));assertFalse(testing.supportsERC165InterfaceUnchecked(address(supportNone), INTERFACE_ID_INVALID));// case 2: query to contract SupportERC165assertTrue(testing.supportsERC165InterfaceUnchecked(address(supportERC165), type(IERC165).interfaceId));assertFalse(testing.supportsERC165InterfaceUnchecked(address(supportERC165), INTERFACE_ID_INVALID));// case 3: query to contract SupportERC165ButNotInvalidInterfaceIdassertTrue(testing.supportsERC165InterfaceUnchecked(address(supportERC165ButNotInvalidInterfaceId), type(IERC165).interfaceId));assertTrue(testing.supportsERC165InterfaceUnchecked(address(supportERC165ButNotInvalidInterfaceId), INTERFACE_ID_INVALID));// case 4: query to contract SupportManyInterfacesButNotERC165assertFalse(testing.supportsERC165InterfaceUnchecked(address(supportManyInterfacesButNotERC165), type(IERC165).interfaceId));assertFalse(testing.supportsERC165InterfaceUnchecked(address(supportManyInterfacesButNotERC165), INTERFACE_ID_INVALID));assertTrue(testing.supportsERC165InterfaceUnchecked(address(supportManyInterfacesButNotERC165), type(IERC20).interfaceId));assertTrue(testing.supportsERC165InterfaceUnchecked(address(supportManyInterfacesButNotERC165), type(IERC20Metadata).interfaceId));assertTrue(testing.supportsERC165InterfaceUnchecked(address(supportManyInterfacesButNotERC165), type(ICustomized).interfaceId));// case 5: query to contract SupportManyInterfacesWithERC165assertTrue(testing.supportsERC165InterfaceUnchecked(address(supportManyInterfacesWithERC165), type(IERC165).interfaceId));assertFalse(testing.supportsERC165InterfaceUnchecked(address(supportManyInterfacesWithERC165), INTERFACE_ID_INVALID));assertTrue(testing.supportsERC165InterfaceUnchecked(address(supportManyInterfacesWithERC165), type(IERC20).interfaceId));assertTrue(testing.supportsERC165InterfaceUnchecked(address(supportManyInterfacesWithERC165), type(IERC20Metadata).interfaceId));assertTrue(testing.supportsERC165InterfaceUnchecked(address(supportManyInterfacesWithERC165), type(ICustomized).interfaceId));}
}

2.2 supportsERC165(address account)

检查目标地址account是否实现了IERC165 interface。

注:任何实现了IERC165的合约都必须支持对IERC165 interface id的显式支持查询和对invalid interface id (即0xffffffff)的显式不支持查询。

    // 按照EIP-165规范,任何接口的interface id都不应该是0xffffffffbytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;function supportsERC165(address account) internal view returns (bool) {// 分别用IERC165 interface id和invalid interface id作为参数去static call目标合约的supportsInterface()方法。当返回值依次为true和false表示目标合约account完全支持IERC165 interfacereturnsupportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) &&!supportsERC165InterfaceUnchecked(account, _INTERFACE_ID_INVALID);}

foundry代码验证

contract ERC165CheckerTest is Test {MockERC165Checker testing = new MockERC165Checker();SupportNone supportNone = new SupportNone();SupportERC165 supportERC165 = new SupportERC165();SupportERC165ButNotInvalidInterfaceId supportERC165ButNotInvalidInterfaceId = new SupportERC165ButNotInvalidInterfaceId();SupportManyInterfacesButNotERC165 supportManyInterfacesButNotERC165 = new SupportManyInterfacesButNotERC165();SupportManyInterfacesWithERC165 supportManyInterfacesWithERC165 = new SupportManyInterfacesWithERC165();bytes4 constant INTERFACE_ID_INVALID = 0xffffffff;function test_SupportsERC165() external {// case 1: query to contract SupportNone without revert and return falseassertFalse(testing.supportsERC165(address(supportNone)));// case 2: query to contract SupportERC165assertTrue(testing.supportsERC165(address(supportERC165)));// case 3: query to contract SupportERC165ButNotInvalidInterfaceIdassertFalse(testing.supportsERC165(address(supportERC165ButNotInvalidInterfaceId)));// case 4: query to contract SupportManyInterfacesButNotERC165assertFalse(testing.supportsERC165(address(supportManyInterfacesButNotERC165)));// case 5: query to contract SupportManyInterfacesWithERC165assertTrue(testing.supportsERC165(address(supportManyInterfacesWithERC165)));}
}

2.3 supportsInterface(address account, bytes4 interfaceId)

检查目标地址account是否实现了输入interface id对应的interface。

注:该函数会同时检查目标地址是否支持IERC165 interface。

    function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {// 当目标地址同时支持IERC165 interface和对应interface id才被认为是已支持对应interface id对应的interfacereturn supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);}

foundry代码验证

contract ERC165CheckerTest is Test {MockERC165Checker testing = new MockERC165Checker();SupportNone supportNone = new SupportNone();SupportERC165 supportERC165 = new SupportERC165();SupportERC165ButNotInvalidInterfaceId supportERC165ButNotInvalidInterfaceId = new SupportERC165ButNotInvalidInterfaceId();SupportManyInterfacesButNotERC165 supportManyInterfacesButNotERC165 = new SupportManyInterfacesButNotERC165();SupportManyInterfacesWithERC165 supportManyInterfacesWithERC165 = new SupportManyInterfacesWithERC165();bytes4 constant INTERFACE_ID_INVALID = 0xffffffff;function test_SupportsInterface() external {// case 1: query to contract SupportNoneassertFalse(testing.supportsInterface(address(supportNone), INTERFACE_ID_INVALID));assertFalse(testing.supportsInterface(address(supportNone), type(IERC165).interfaceId));// case 2: query to contract SupportERC165assertFalse(testing.supportsInterface(address(supportERC165), INTERFACE_ID_INVALID));assertTrue(testing.supportsInterface(address(supportERC165), type(IERC165).interfaceId));// case 3: query to contract SupportERC165ButNotInvalidInterfaceIdassertFalse(testing.supportsInterface(address(supportERC165ButNotInvalidInterfaceId), INTERFACE_ID_INVALID));assertFalse(testing.supportsInterface(address(supportERC165ButNotInvalidInterfaceId), type(IERC165).interfaceId));// case 4: query to contract SupportManyInterfacesButNotERC165assertFalse(testing.supportsInterface(address(supportManyInterfacesButNotERC165), INTERFACE_ID_INVALID));assertFalse(testing.supportsInterface(address(supportManyInterfacesButNotERC165), type(IERC165).interfaceId));assertFalse(testing.supportsInterface(address(supportManyInterfacesButNotERC165), type(IERC20).interfaceId));assertFalse(testing.supportsInterface(address(supportManyInterfacesButNotERC165), type(IERC20Metadata).interfaceId));assertFalse(testing.supportsInterface(address(supportManyInterfacesButNotERC165), type(ICustomized).interfaceId));// case 5: query to contract SupportManyInterfacesWithERC165assertFalse(testing.supportsInterface(address(supportManyInterfacesWithERC165), INTERFACE_ID_INVALID));assertTrue(testing.supportsInterface(address(supportManyInterfacesWithERC165), type(IERC165).interfaceId));assertTrue(testing.supportsInterface(address(supportManyInterfacesWithERC165), type(IERC20).interfaceId));assertTrue(testing.supportsInterface(address(supportManyInterfacesWithERC165), type(IERC20Metadata).interfaceId));assertTrue(testing.supportsInterface(address(supportManyInterfacesWithERC165), type(ICustomized).interfaceId));}
}

2.4 getSupportedInterfaces(address account, bytes4[] memory interfaceIds)

批量检查目标地址account是否实现了输入interface ids对应的interface数组。返回值为一个bool数组,分别对应对相同index的interface ids数组成员的支持与否。

    function getSupportedInterfaces(address account, bytes4[] memory interfaceIds)internalviewreturns (bool[] memory){// 在内存中创建与传入interface ids数组相同长度的bool数组bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);if (supportsERC165(account)) {// 如果目标地址account已经完全支持IERC165 interface,才会进一步去检查传入的interface ids数组的支持情况。否则,直接认作全不支持for (uint256 i = 0; i < interfaceIds.length; i++) {// 遍历查询目标account对各个interfaceId的支持情况,并将检查结果依次存入bool数组interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);}}// 返回bool数组return interfaceIdsSupported;}

foundry代码验证

contract ERC165CheckerTest is Test {MockERC165Checker testing = new MockERC165Checker();SupportNone supportNone = new SupportNone();SupportERC165 supportERC165 = new SupportERC165();SupportERC165ButNotInvalidInterfaceId supportERC165ButNotInvalidInterfaceId = new SupportERC165ButNotInvalidInterfaceId();SupportManyInterfacesButNotERC165 supportManyInterfacesButNotERC165 = new SupportManyInterfacesButNotERC165();SupportManyInterfacesWithERC165 supportManyInterfacesWithERC165 = new SupportManyInterfacesWithERC165();bytes4 constant INTERFACE_ID_INVALID = 0xffffffff;function test_GetSupportedInterfaces() external {bytes4[] memory interfaceIds = new bytes4[](4);interfaceIds[0] = type(IERC165).interfaceId;interfaceIds[1] = type(IERC20).interfaceId;interfaceIds[2] = type(IERC20Metadata).interfaceId;interfaceIds[3] = type(ICustomized).interfaceId;// case 1: query to contract SupportNonebool[] memory supported = testing.getSupportedInterfaces(address(supportNone), interfaceIds);assertEq(supported.length, 4);// all false because of not supporting ERC165 completelyassertFalse(supported[0]);assertFalse(supported[1]);assertFalse(supported[2]);assertFalse(supported[3]);// case 2: query to contract SupportERC165supported = testing.getSupportedInterfaces(address(supportERC165), interfaceIds);assertEq(supported.length, 4);assertTrue(supported[0]);assertFalse(supported[1]);assertFalse(supported[2]);assertFalse(supported[3]);// case 3: query to contract SupportERC165ButNotInvalidInterfaceIdsupported = testing.getSupportedInterfaces(address(supportERC165ButNotInvalidInterfaceId), interfaceIds);assertEq(supported.length, 4);// all false because of not supporting ERC165 completelyassertFalse(supported[0]);assertFalse(supported[1]);assertFalse(supported[2]);assertFalse(supported[3]);// case 4: query to contract SupportManyInterfacesButNotERC165supported = testing.getSupportedInterfaces(address(supportManyInterfacesButNotERC165), interfaceIds);assertEq(supported.length, 4);// all false because of not supporting ERC165 completelyassertFalse(supported[0]);assertFalse(supported[1]);assertFalse(supported[2]);assertFalse(supported[3]);// case 5: query to contract SupportManyInterfacesWithERC165supported = testing.getSupportedInterfaces(address(supportManyInterfacesWithERC165), interfaceIds);assertEq(supported.length, 4);// all trueassertTrue(supported[0]);assertTrue(supported[1]);assertTrue(supported[2]);assertTrue(supported[3]);}
}

2.5 supportsAllInterfaces(address account, bytes4[] memory interfaceIds)

检查目标地址account是否全部实现了输入interface ids对应的interface数组。如果全部实现返回true,否则返回false。

注:每次调用调用supportsInterface(address account, bytes4 interfaceId)检查目标地址account对某一interface id的支持情况都会进行一次对IERC165的支持检查。而本方法中无论数组interfaceIds的长度是多少都只会进行一次对IERC165的支持检查,非常节约gas。

    function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {// 如果目标地址account已经完全支持IERC165 interface,才会进一步去检查传入的interface ids数组的支持情况。否则,直接返回falseif (!supportsERC165(account)) {return false;}// 遍历查询目标地址account对各个interface id的支持情况。只要出现任意一个interface id不支持的情况就直接返回falsefor (uint256 i = 0; i < interfaceIds.length; i++) {if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {return false;}}// 当目标地址account对传入的各个interface id都支持时,返回truereturn true;}

foundry代码验证

contract ERC165CheckerTest is Test {MockERC165Checker testing = new MockERC165Checker();SupportNone supportNone = new SupportNone();SupportERC165 supportERC165 = new SupportERC165();SupportERC165ButNotInvalidInterfaceId supportERC165ButNotInvalidInterfaceId = new SupportERC165ButNotInvalidInterfaceId();SupportManyInterfacesButNotERC165 supportManyInterfacesButNotERC165 = new SupportManyInterfacesButNotERC165();SupportManyInterfacesWithERC165 supportManyInterfacesWithERC165 = new SupportManyInterfacesWithERC165();bytes4 constant INTERFACE_ID_INVALID = 0xffffffff;function test_SupportsAllInterfaces() external {bytes4[] memory interfaceIds = new bytes4[](4);interfaceIds[0] = type(IERC165).interfaceId;interfaceIds[1] = type(IERC20).interfaceId;interfaceIds[2] = type(IERC20Metadata).interfaceId;interfaceIds[3] = type(ICustomized).interfaceId;// case 1: query to contract SupportNoneassertFalse(testing.supportsAllInterfaces(address(supportNone), interfaceIds));// case 2: query to contract SupportERC165assertFalse(testing.supportsAllInterfaces(address(supportERC165), interfaceIds));// case 3: query to contract SupportERC165ButNotInvalidInterfaceIdassertFalse(testing.supportsAllInterfaces(address(supportERC165ButNotInvalidInterfaceId), interfaceIds));// case 4: query to contract SupportManyInterfacesButNotERC165assertFalse(testing.supportsAllInterfaces(address(supportManyInterfacesButNotERC165), interfaceIds));// case 5: query to contract SupportManyInterfacesWithERC165assertTrue(testing.supportsAllInterfaces(address(supportManyInterfacesWithERC165), interfaceIds));}
}

ps:
本人热爱图灵,热爱中本聪,热爱V神。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!
在这里插入图片描述
公众号名称:后现代泼痞浪漫主义奠基人

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

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

相关文章

springboot整合JMH做优化实战

这段时间接手项目出现各种问题&#xff0c;令人不胜烦扰。吐槽下公司做项目完全靠人堆&#xff0c;大上快上风格注定留下一地鸡毛&#xff0c;修修补补不如想如何提升同事代码水准免得背锅。偶然看到关于JMH对于优化java代码的直观性&#xff0c;于是有了这篇文章&#xff0c;希…

11款UML/SysML建模工具更新(2023.7)Papyrus、UModel……

DDD领域驱动设计批评文集 欢迎加入“软件方法建模师”群 《软件方法》各章合集 最近一段时间更新的工具有&#xff1a; 工具最新版本&#xff1a;drawio-desktop 21.6.5 更新时间&#xff1a;2023年7月22日 工具简介 开源绘图工具&#xff0c;用Electron编写&#xff0c;…

uniapp文件下载并预览

大概就是这样的咯&#xff0c;文件展示到页面上&#xff0c;点击文件下载并预览该文件&#xff1b; 通过点击事件handleDownLoad(file.path)&#xff0c;file.path为文件的地址&#xff1b; <view class"files"><view class"cont" v-for"(…

PPO和文本生成

策略梯度 策略梯度&#xff08;Policy Gradient&#xff09;方法梯度的计算如下&#xff1a; E ( a t , s t ) ∈ π θ [ A ^ t ∇ θ log ⁡ π θ ( a t ∣ s t ) ] \mathbb E_{(a_t,s_t) \in \pi_\theta}[\hat A_t \nabla_ \theta \log \pi_\theta(a_t | s_t)] E(at​,st…

Nginx的重定向

URI&#xff1a;统一资源标识符&#xff0c;是一种字符串标识&#xff0c;主要是用于标识抽象的或者是物理资源&#xff08;主要是指一些文件视频等等&#xff09; 常用的Nginx正则表达式 ^ 匹配输入字符串的起始位置&#xff08;以......开头&#xff09; $ 匹配输入…

FreeRTOS( 任务与中断优先级,临界保护)

资料来源于硬件家园&#xff1a;资料汇总 - FreeRTOS实时操作系统课程(多任务管理) 目录 一、中断优先级 1、NVIC基础知识 2、FreeRTOS配置NVIC 3、SVC、PendSV、Systick中断 4、不受FreeRTOS管理的中断 5、STM32CubeMX配置 二、任务优先级 1、任务优先级说明 2、任务…

【LeetCode】144. 二叉树的前序遍历、94. 二叉树的中序遍历、145. 二叉树的后序遍历

作者&#xff1a;小卢 专栏&#xff1a;《Leetcode》 喜欢的话&#xff1a;世间因为少年的挺身而出&#xff0c;而更加瑰丽。 ——《人民日报》 144. 二叉树的前序遍历 144. 二叉树的前序遍历 题目&#xff1a; 给你二叉树的根节点 root &…

保姆级Arcgis安装图文安装教程

参考视频&#xff1a;【钟老师arcGIS从放弃到入门】02软件下载与安装_哔哩哔哩_bilibili 安装包在视频简介中有 注释&#xff1a;安装过程中有犯错误&#xff0c;请耐心看完一遍再跟着操作 &#xff08;一&#xff09;安装包下载 下载视频中分享的压缩包(压缩包密码&#x…

window下部署Yapi接口管理系统部署总结

window下部署Yapi接口管理系统部署总结 YApi 是高效、易用、功能强大的 api 管理平台&#xff0c;旨在为开发、产品、测试人员提供更优雅的接口管理服务。可以帮助开发者轻松创建、发布、维护 API&#xff0c;YApi 还为用户提供了优秀的交互体验&#xff0c;开发人员只需利用平…

后端开发8.品牌模块

概述 简介 效果图 数据库设计 DROP TABLE IF EXISTS `goods_brand`;CREATE TABLE `goods_brand` ( `goodsBrandId` int(11) NOT NULL AUTO_IN

04-4_Qt 5.9 C++开发指南_时间日期与定时器

文章目录 1. 时间日期相关的类2. 源码2.1 可视化UI设计2.2 dialog.h2.3 dialog.cpp 1. 时间日期相关的类 时间日期是经常遇到的数据类型&#xff0c;Qt 中时间日期类型的类如下。 QTime:时间数据类型&#xff0c;仅表示时间&#xff0c;如 15:23:13。 QDate:日期数据类型&…

【资料分享】全志科技T507-H工业核心板规格书

1 核心板简介 创龙科技SOM-TLT507是一款基于全志科技T507-H处理器设计的4核ARM Cortex-A53全国产工业核心板&#xff0c;主频高达1.416GHz。核心板CPU、ROM、RAM、电源、晶振等所有元器件均采用国产工业级方案&#xff0c;国产化率100%。 核心板通过邮票孔连接方式引出MIPI C…

QGIS开发五:使用UI文件

前面我们说了在创建项目时创建的是一个空项目&#xff0c;即不使用 Qt 提供的综合开发套件 Qt Creator&#xff0c;也不使用 Qt Visual Studio Tools 这类工具。 但是后面发现&#xff0c;如果我想要有更加满意的界面布局&#xff0c;还是要自己写一个UI文件&#xff0c;如果不…

深度对话|如何设计合适的网络经济激励措施

近日&#xff0c;我们与Mysten Labs的首席经济学家Alonso de Gortari进行了对话&#xff0c;讨论了如何在网络运营商和参与者之间找到激励措施的平衡&#xff0c;以及Sui的经济如何不断发展。 是什么让您选择将自己的经济学背景应用于区块链和Web3领域&#xff1f; 起初&…

微信个人小程序申请 (AppID 和 AppSecret)

1. 登录微信公众平台 https://mp.weixin.qq.com/cgi-bin/loginpage?url%2Fcgi-bin%2Fhome%3Ft%3Dhome%2Findex%26lang%3Dzh_CN%26token%3D47421820 2. 右上角立即注册 3. 注册类型选择小程序 4. 账号信息 5. 邮箱激活 6. 小程序发布流程 7. 小程序信息 (前往填写) 8. 获取小程…

【一】初步认识数据库

数据库概览数据库 缘起表(Table)的理解用表来定义数据库数据库系统的理解概念层次的理解实例层次的理解 数据库管理系统的理解从用户角度看从系统实现角度看典型的数据库管理系统 数据库语言数据库定义、操纵、控制语言数据库语言 VS 高级语言 内容回顾练习 数据库概览 走马观…

QT笔记——QT自定义事件

我们有时候想发送自定义事件 1&#xff1a;创建自定义事件&#xff0c;首先我们需要知道它的条件 1&#xff1a;自定义事件需要继承QEvent 2&#xff1a;事件的类型需要在 QEvent::User 和 QEvent::MaxUser 范围之间&#xff0c;在QEvent::User之前 是预留给系统的事件 3&#…

前端先行模拟接口(mock+expres+json)

目录 mock模拟数据&#xff1a;data/static.js 路由&#xff1a;index.js 服务器&#xff1a;server.js yarn /node 启动服务器&#xff1a;yarn start 客户端&#xff1a;修改代理路径(修改设置后都要重启才生效) 示例 后端框架express构建服务器 前端发起请求 静态数…

smtplib.SMTPHeloError: (500, b‘Error: bad syntax‘)

如果你编写邮件收发工具的时候,有可能会遇到这个问题。这里直接给出解决办法。 目录 1、检查系统版本 2、点击右侧的更改适配器选项

0基础学C#笔记08:插入排序法

文章目录 前言一、过程简单描述&#xff1a;二、代码总结 前言 我们在玩打牌的时候&#xff0c;你是怎么整理那些牌的呢&#xff1f;一种简单的方法就是一张一张的来&#xff0c;将每一张牌插入到其他已经有序的牌中的适当位置。当我们给无序数组做排序的时候&#xff0c;为了…