)
WTF-Solidity 第24讲在智能合约中用create创建新合约极简 Uniswap 实战【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity本讲基于 WTF-Solidity 极简入门教程的西语版第 24 讲Languages/es/24_Crear_es/readme.md讲解如何在已部署的智能合约内部再创建新的智能合约。以太坊上不仅外部账户EOA可以部署合约合约本身也可以作为工厂动态铸造新合约——去中心化交易所 Uniswap 正是靠Factory合约创建了无数个Pair币对合约。读完本文你将掌握create关键字的完整用法、极简 Uniswap 的Pair/PairFactory双合约架构并能在 Remix 中亲手验证合约创建流程与底层CREATE操作码。背景合约也能创建合约在以太坊链上账户体系分为外部账户EOA即用户钱包和合约账户。用户可以通过部署交易创建智能合约同样一个智能合约在其执行期间也可以通过 EVM 的创建指令再创建新的智能合约。这种合约造合约的能力是链上模块化架构的基础典型代表就是Uniswap它的工厂合约PairFactory按需铸造无数的币对合约Pair每个币对独立管理两种代币的地址、流动性与兑换逻辑。在 Solidity 中有两种在合约内创建新合约的方式create本讲主角用法简单创建出的合约地址由创建者地址与 nonce 决定不可预先预测create2下一讲Languages/es/25_Crear2_es/readme.md介绍可以通过 salt 预先计算合约地址。本讲将用create实现一个极简版 Uniswap完整代码位于 24_Create/Create.sol西语版同款源码见 Languages/es/24_Crear_es/Create.sol。create语法详解create的用法非常直观用new关键字实例化目标合约并传入新合约构造函数所需的参数Contract x new Contract{value: _value}(params)各部分的含义Contract要创建的合约类型名称x创建的合约对象其地址即新合约地址value: _value可选。若新合约构造函数声明为payable创建者可以在创建时向新合约转入_value数量的ETHparams传递给新合约构造函数的参数列表。底层上new Contract(...)会触发 EVM 的CREATE操作码EVM 以创建者地址 创建者 nonce为输入计算新合约地址执行合约初始化代码构造函数并将返回的运行时字节码部署到该地址。极简 Uniswap 架构Uniswap V2的核心合约主要包含两个部分UniswapV2Pair币对合约负责管理币对中两种代币的地址、流动性以及买卖swapUniswapV2Factory工厂合约负责创建新的币对合约并统一管理所有币对地址。下面我们用create实现极简版拆成两个合约Pair币对合约负责管理代币地址PairFactory工厂合约负责创建新币对并维护地址索引。Pair币对合约contract Pair{ address public factory; // 工厂合约地址 address public token0; // 代币1 address public token1; // 代币2 constructor() payable { factory msg.sender; } // 部署时由工厂合约调用一次 function initialize(address _token0, address _token1) external { require(msg.sender factory, UniswapV2: FORBIDDEN); // 充分的权限校验 token0 _token0; token1 _token1; } }Pair合约非常精简只包含 3 个状态变量factory部署它的工厂合约地址token0/token1币对中的两种代币地址。其关键逻辑有两点构造函数记录创建者constructor() payable在部署瞬间执行将msg.sender即调用new Pair()的工厂合约写入factory。这里构造函数声明为payable是为了允许创建时附带 ETH。initialize二次初始化由于构造函数里拿不到币对参数initialize(address _token0, address _token1)由工厂合约在创建完成后手动调用把两种代币地址写入token0、token1。其中require(msg.sender factory, UniswapV2: FORBIDDEN)是权限校验确保只有创建该币对的工厂合约才能初始化防止被任意账户篡改。提问为什么 Uniswap 不直接在constructor里设置好token0和token1的地址回答因为 Uniswap 实际使用的是create2创建合约。create2在创建时不允许在构造函数中携带参数其地址由 creator、salt 与 bytecode 唯一确定因此只能部署空壳币对再通过initialize填充代币地址。而使用create时Pair构造函数是可以携带参数的理论上也能在构造函数中直接设置token0、token1——本讲为贴近 Uniswap 的真实设计仍采用initialize模式。PairFactory工厂合约contract PairFactory{ mapping(address mapping(address address)) public getPair; // 通过两个代币地址查Pair地址 address[] public allPairs; // 保存所有Pair地址 function createPair(address tokenA, address tokenB) external returns (address pairAddr) { // 创建新合约 Pair pair new Pair(); // 调用新合约的initialize方法 pair.initialize(tokenA, tokenB); // 更新地址map pairAddr address(pair); allPairs.push(pairAddr); getPair[tokenA][tokenB] pairAddr; getPair[tokenB][tokenA] pairAddr; } }工厂合约维护两个状态变量getPairmapping(address mapping(address address))嵌套映射以两种代币地址为键返回对应币对合约地址方便按代币快速反查币对allPairsaddress[]动态数组按创建顺序保存所有币对合约地址。PairFactory只有一个对外函数createPair(address tokenA, address tokenB)它完整串起了合约创建合约的四个步骤创建合约Pair pair new Pair();仅这一行就在当前合约内部触发了CREATE操作码部署出一个新的Pair合约构造函数把factory设为msg.sender即PairFactory自身初始化币对调用pair.initialize(tokenA, tokenB)写入两种代币地址登记地址将address(pair)追加进allPairs建立双向索引getPair[tokenA][tokenB]与getPair[tokenB][tokenA]同时指向新币对地址保证无论调用方以何种顺序传入代币都能查到。整个创建流程中真正造合约的只有一行new Pair()这正是 Solidity 合约工厂模式的核心魅力。动手验证用真实代币地址创建币对部署好PairFactory合约后可以用下面两个地址作为参数调用createPair观察新币对的地址WBNB地址: 0x2c44b726ADF1963cA47Af88B284C06f30380fC78 BSC链上的PEOPLE地址: 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c按教程西语版实测调用createPair后得到的Pair合约地址为0x5C9eb5D6a6C2c1B3EFc52255C0b356f116f6f66D。由于create生成的地址取决于工厂地址与工厂 nonce不同部署环境下结果可能不同读者应以自己环境中的实测返回值为准。在 Remix 上验证合约创建流程1. 调用createPair获取币对地址部署PairFactory后在 Remix 的 DEPLOY RUN TRANSACTIONS 面板展开createPair函数将WBNB与PEOPLE两个地址填入tokenA、tokenB并发送交易。decoded output中的pairAddr即为新创建的Pair合约地址同时allPairs数组中也会出现该地址。2. 核对Pair合约状态变量将 Remix 的 Contract 下拉框切换为Pair在 At Address 输入框中填入上一步返回的币对地址即可生成针对该已部署合约的调用接口。展开后可以查到三个状态变量factory指向PairFactory地址token0、token1分别对应传入的两种代币地址证明工厂创建 initialize初始化的链路执行正确。3. 用 Debug 查看底层CREATE操作码在 Remix 的交易日志中打开 Debugger进入Step details的vm trace可以看到CREATE操作码以及新合约实例的创建过程——这从 EVM 层面印证了new Pair()实际触发的是底层CREATE指令Solidity Locals中也能看到新创建的pair合约实例。源码佐证与运行环境本讲完整可编译源码24_Create/Create.solpragma solidity ^0.8.34;Pair与PairFactory两个合约同文件定义西语版同款源码Languages/es/24_Crear_es/Create.sol中文原版教程24_Create/readme.md英文版见 Languages/en/24_Create_en/readme.md。仓库根目录的 foundry.toml 配置了统一编译环境solc 0.8.34libs [lib, node_modules]并配置了forge-std与 OpenZeppelin 的 remapping可用于对本讲合约执行编译与测试。读者也可以直接在 Remix 中粘贴源码运行无需额外依赖。需要说明的是本讲演示的是create其生成的合约地址不可预先预测。若希望像真实 Uniswap 那样通过(tokenA, tokenB)直接推算出币对地址供 Router 离线计算、避免跨合约查询getPair则需要使用create2其原理、salt 计算与地址预计算函数在下一讲 Languages/es/25_Crear2_es/readme.md 中有完整实现与验证步骤。总结本讲借助极简 Uniswap 的例子完整介绍了使用create在合约内创建合约的方法Pair合约通过构造函数记录工厂地址、通过initialize填充代币地址PairFactory用一行new Pair()创建币对并维护getPair双向映射与allPairs地址数组。配合 Remix 的调用、状态变量核对与 Debugger 操作码查看可以直观理解合约工厂模式从 Solidity 语法到 EVM 底层的完整链路。下一讲将用create2实现同样的极简 Uniswap并重点讲解如何预先计算合约地址。【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考