Blockchain engineer
从入门到实战:我的全套 Web3 学习路径(2025版)
你好,我是 Keegan 小钢。 如果你刚开始接触 Web3、准备从 Web2 转型,或者正在寻找一套真正系统、可落地、能带你从入门做到链上实战的学习路径,那么这篇文章会对你非常有帮助。 我是一名有 16 年经验的互联网从业者,过去 8 年专注于 Web3 技术方向,同时持续做个人 IP 超过 13 年。长期以来,我在公众号、知乎、B 站持续输出 Web3 的学习路线、开发知识、工程实践以及真实链上项目的研发过程。如果你对我的经历好奇,可以阅读这篇文章:《复盘我的 13 年个人 IP 之路》。 这几年,越来越多同学加我咨询,他们大多会问:我应该从哪里开始学 Web3?我有一些基础,但做不出完整项目怎么办?有没有适合“从入门到能做项目”的学习路径?我想顺利找到 Web3 工作,该怎么准备?本质上,这些问题可以归结成一句话:“我现在这个水平,下一步该学什么?”为了让不同阶段的同学都能快速找到最适合自己的学习路径,我把过去几年输出的所有 Web3 内容——免费课程、付费课程、AI+Web3 实战营、以及深度服务——做了一次系统性的梳理。 这篇文章是你最清晰、最完整的 「Web3 学习路...
万字长文聊聊Web3的现状与趋势
整体数据现状与趋势首先,先来看看 Web3 的搜索热度情况,我们可以从 GoogleTrends 中看到一些数据。下图是关于 Web3 在全球过去 5 年内的搜索热度趋势图:从图中可以看出,前面几年的搜索热度一直很低,热度值一直保持在 10 以下,但从 2021 年下半年开始逐渐飙升,在 2021 年 12 月底达到了顶峰。虽然随后开始有所回落,但依然保持在很高的热度。 如果再按区域来看搜索热度,就会发现,搜索热度最高的竟然是在中国,且与其他区域的搜索热度差距很大,如下图所示这说明,中国依然是 Web3 最大的潜在市场。 接着,再来看看整个加密货币总市值的趋势图,某种程度上,这也代表了整个 Web3 行业的总市值。下图的数据来自 CoinMarketCap:从图中可以看到,总市值也是在 2021 年出现大幅度飙升,2021 年底到达顶峰,达到了将近 3 万亿美元的总市值。随后不断回落,在 2022 年底跌到了最低点,总市值降到低于 1 万亿,相比高点,跌去了三分之二。但是,就算是最低点也依然比 2021 年之前那些年的总市值高得多。 加密货币的总市值看上去好像不低,但如果跟全球股...
万字长文聊聊Web3的组成架构
Web3 发展至今,生态已然初具雏形,如果将当前阶段的 Web3 生态组成架构抽象出一个鸟瞰图,由下而上可划分为四个层级:区块链网络层、中间件层、应用层、访问层。下面我们来具体看看每一层级都有什么。另外,此章节会涉及到很多项目的名称,因为篇幅原因不会一一进行介绍,有兴趣的可以另外去查阅相关资料进行深入了解。区块链网络层最底层是「区块链网络层」,也是 Web3 的基石层,主要由各区块链网络所组成。 组成该层级的区块链网络还不少,Bitcoin、Ethereum、BNB Chain(BSC)、Polygon、Arbitrum、Polkadot、Cosmos、Celestia、Avalanche、Aptos、Sui 等等,还有很多。根据 Blockchain-Comparison 的统计,截止撰文之日的区块链至少有 150 条。这里我们主要说的是公链,联盟链不包括在内。因为区块链实在太多,会有些眼花缭乱,所以有必要进行分门别类。 首先,不同区块链之间存在着分层结构,有 Layer0、Layer1、Layer2 之分。其次,Web3 的繁荣发展,依赖于智能合约技术,而智能合约的运行环境为...
从入门到实战:我的全套 Web3 学习路径(2025版)
你好,我是 Keegan 小钢。 如果你刚开始接触 Web3、准备从 Web2 转型,或者正在寻找一套真正系统、可落地、能带你从入门做到链上实战的学习路径,那么这篇文章会对你非常有帮助。 我是一名有 16 年经验的互联网从业者,过去 8 年专注于 Web3 技术方向,同时持续做个人 IP 超过 13 年。长期以来,我在公众号、知乎、B 站持续输出 Web3 的学习路线、开发知识、工程实践以及真实链上项目的研发过程。如果你对我的经历好奇,可以阅读这篇文章:《复盘我的 13 年个人 IP 之路》。 这几年,越来越多同学加我咨询,他们大多会问:我应该从哪里开始学 Web3?我有一些基础,但做不出完整项目怎么办?有没有适合“从入门到能做项目”的学习路径?我想顺利找到 Web3 工作,该怎么准备?本质上,这些问题可以归结成一句话:“我现在这个水平,下一步该学什么?”为了让不同阶段的同学都能快速找到最适合自己的学习路径,我把过去几年输出的所有 Web3 内容——免费课程、付费课程、AI+Web3 实战营、以及深度服务——做了一次系统性的梳理。 这篇文章是你最清晰、最完整的 「Web3 学习路...
万字长文聊聊Web3的现状与趋势
整体数据现状与趋势首先,先来看看 Web3 的搜索热度情况,我们可以从 GoogleTrends 中看到一些数据。下图是关于 Web3 在全球过去 5 年内的搜索热度趋势图:从图中可以看出,前面几年的搜索热度一直很低,热度值一直保持在 10 以下,但从 2021 年下半年开始逐渐飙升,在 2021 年 12 月底达到了顶峰。虽然随后开始有所回落,但依然保持在很高的热度。 如果再按区域来看搜索热度,就会发现,搜索热度最高的竟然是在中国,且与其他区域的搜索热度差距很大,如下图所示这说明,中国依然是 Web3 最大的潜在市场。 接着,再来看看整个加密货币总市值的趋势图,某种程度上,这也代表了整个 Web3 行业的总市值。下图的数据来自 CoinMarketCap:从图中可以看到,总市值也是在 2021 年出现大幅度飙升,2021 年底到达顶峰,达到了将近 3 万亿美元的总市值。随后不断回落,在 2022 年底跌到了最低点,总市值降到低于 1 万亿,相比高点,跌去了三分之二。但是,就算是最低点也依然比 2021 年之前那些年的总市值高得多。 加密货币的总市值看上去好像不低,但如果跟全球股...
万字长文聊聊Web3的组成架构
Web3 发展至今,生态已然初具雏形,如果将当前阶段的 Web3 生态组成架构抽象出一个鸟瞰图,由下而上可划分为四个层级:区块链网络层、中间件层、应用层、访问层。下面我们来具体看看每一层级都有什么。另外,此章节会涉及到很多项目的名称,因为篇幅原因不会一一进行介绍,有兴趣的可以另外去查阅相关资料进行深入了解。区块链网络层最底层是「区块链网络层」,也是 Web3 的基石层,主要由各区块链网络所组成。 组成该层级的区块链网络还不少,Bitcoin、Ethereum、BNB Chain(BSC)、Polygon、Arbitrum、Polkadot、Cosmos、Celestia、Avalanche、Aptos、Sui 等等,还有很多。根据 Blockchain-Comparison 的统计,截止撰文之日的区块链至少有 150 条。这里我们主要说的是公链,联盟链不包括在内。因为区块链实在太多,会有些眼花缭乱,所以有必要进行分门别类。 首先,不同区块链之间存在着分层结构,有 Layer0、Layer1、Layer2 之分。其次,Web3 的繁荣发展,依赖于智能合约技术,而智能合约的运行环境为...
Blockchain engineer

Subscribe to Keegan小钢

Subscribe to Keegan小钢
Share Dialog
Share Dialog
<100 subscribers
<100 subscribers
前一篇文章已经对 UniswapV4 做了简单的概述,了解了其主要特性。从本篇开始,我们要深入合约实现了,先看看其合约结构。
UniswapV4 的合约项目,还是和之前的版本一样,分为了 v4-core 和 v4-periphery 两个 repo。另外,之前的版本,合约项目框架是用 Hardhat 搭建的,而这回,你会发现改用 Foundry 了。Foundry 正在慢慢变成开发新合约项目的主流框架,因为 Foundry 相比 Hardhat,写单元测试和脚本都和写合约一样,可以统一用 solidity 来编写,这对于不太精通 JavaScript/TypeScript 的合约工程师来说就会更方便了。
还有,目前的合约实现其实还不是最终版,近期依然在不断提交更新。
当前,v4-core 的合约目录结构如下图所示:

interfaces 定义了所有接口合约,libraries 存放的是所有库合约,test 目录是测试用的,我们不用关心。types 值得介绍一下,在以前的版本中没有这个。其实就是封装了几种特定类型,包括 4 种类型:
BalanceDelta
Currency
PoolId
PoolKey
PoolKey 最容易理解,我们来看看其代码实现:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Currency} from "./Currency.sol";
import {IHooks} from "../interfaces/IHooks.sol";
/// @notice Returns the key for identifying a pool
struct PoolKey {
/// @notice The lower currency of the pool, sorted numerically
Currency currency0;
/// @notice The higher currency of the pool, sorted numerically
Currency currency1;
/// @notice The pool swap fee, capped at 1_000_000. The upper 4 bits determine if the hook sets any fees.
uint24 fee;
/// @notice Ticks that involve positions must be a multiple of tick spacing
int24 tickSpacing;
/// @notice The hooks of the pool
IHooks hooks;
}
其实就是定义了一个结构体,包含了五个字段,这些字段加在一起就是一个池子的唯一标识。其中,currency0 和 currency1 就是之前版本的 token0 和 token1,只是变成了 Currency 类型。Currency 类型其实本质上也是地址类型,是由地址类型声明的用户自定义值类型。待会我们再展开介绍什么是用户自定义值类型。
PoolKey 相比 UniswapV3 时多了一个 hooks,这其实就是要指定的 Hooks 合约地址。
PoolId 就是一种用户自定义值类型,我们来看看其代码实现:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {PoolKey} from "./PoolKey.sol";
type PoolId is bytes32;
/// @notice Library for computing the ID of a pool
library PoolIdLibrary {
function toId(PoolKey memory poolKey) internal pure returns (PoolId) {
return PoolId.wrap(keccak256(abi.encode(poolKey)));
}
}
最关键的一行就是 type PoolId is bytes32。这就是用户自定义值类型的用法,使用 type C is V 的方式进行定义。V 被称为基础类型,可以是布尔型、整型、地址型、字节型等值类型的一种,但不能是 mapping、数据、结构体等引用类型。C 就是所要定义的新类型名称,有点类似于是 V 的别名,但会有严格的类型检查。
用户自定义值类型有两个内置函数可用于与基础类型之间进行转换。wrap() 函数可以将基础类型转为自定义类型,比如上面代码通过调用 PoolId.wrap() 函数就将一个 bytes32 类型的值转为了 PoolId 类型。还有个 unwrap() 函数则可以将自定义类型转为基础类型。
这种自定义类型是在 solidity 0.8.8 开始引入的,所以也只能在 0.8.8 及以上的编译版本中使用。
PoolId 其实就是用于定义一个池子的唯一 ID,从 PoolIdLibrary 的 toId() 函数可以看出,其实就是将 poolKey 进行编码后计算得出的哈希值,然后通过 wrap 函数将这个 bytes32 类型的哈希值转为了 PoolId 类型。
Currency 和 BalanceDelta 也是和 PoolId 一样的用户自定义值类型。Currency 的基础类型是 address 类型,用来表示池子里的资产。BalanceDelta 的基础类型是 int256,用来表示净余额。
Currency 的实现不只是简单地用 type 定义了其类型,还定义了一些函数,如下所示:
type Currency is address;
using {greaterThan as >, lessThan as <, greaterThanOrEqualTo as >=, equals as ==} for Currency global;
function equals(Currency currency, Currency other) pure returns (bool) {
return Currency.unwrap(currency) == Currency.unwrap(other);
}
function greaterThan(Currency currency, Currency other) pure returns (bool) {
return Currency.unwrap(currency) > Currency.unwrap(other);
}
function lessThan(Currency currency, Currency other) pure returns (bool) {
return Currency.unwrap(currency) < Currency.unwrap(other);
}
function greaterThanOrEqualTo(Currency currency, Currency other) pure returns (bool) {
return Currency.unwrap(currency) >= Currency.unwrap(other);
}
自定义类型虽然是基于基础值类型而定义的,但因为类型检查,是没办法直接使用基础类型本身的用法的,包括比较符和基础类型本身的内置函数。虽然 Currency 类型的基础类型是 address,而我们知道 address 类型的两个变量是可以直接使用 >、<、>=、== 这些比较符去比较两个地址类型的大小的。但 Currency 类型则不能直接使用,类型检查无法通过。因此,需要再额外定义四个函数,分别用于对应的四个比较符,再通过 using 语句把这四个函数作为各自的比较符进行使用。如此一来,就可以把 Currency 类型用于大小比较了。
另外,与 Currency 配置使用的还有库合约 CurrencyLibrary,其封装了转账、查询余额、是否原生代币等函数。需要对自定义类型添加额外的功能函数时,通常都是为其封装对应的库合约,PoolId 对应的有 PoolIdLibrary,BalanceDelta 对应的有 BalanceDeltaLibrary。
BalanceDelta 需要说明一下,它是用于表示净余额的,它其实是将两个代币的数额组装到一起的。在 BalanceDelta 中有定义了以下函数:
function toBalanceDelta(int128 _amount0, int128 _amount1) pure returns (BalanceDelta balanceDelta) {
/// @solidity memory-safe-assembly
assembly {
balanceDelta :=
or(shl(128, _amount0), and(0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff, _amount1))
}
}
该函数就是将两个代币的金额一起转成 BalanceDelta 类型。可看到其实现使用了内联汇编,其实就是前 128 位用于存放 amount0,后 128 位用于存放 amount1。
BalanceDeltaLibrary 库合约中则封装了 amount0() 和 amount1(),可从 BalanceDelta 中分别读取出 amount0 和 amount1。
至此,关于 types 目录的就讲解这么多了。回到 v4-core 的合约目录结构,可看到根目录下有 5 个合约文件:
Claims.sol
Fees.sol
NoDelegateCall.sol
Owned.sol
PoolManager.sol
最核心的就是 PoolManager.sol,也就是统一管理所有池的单例合约。其他几个合约都是被 PoolManager 所继承的子合约。关于 PoolManager 合约的具体实现我们下一篇文章再讲解。
关于 Claims 合约,很有必要说明一下。其实两个星期前,即 11 月中旬之前,PoolManager 还是继承了 ERC1155 的,用于额外的代币记账。但是,我发现 11 月 14 号有一个提交,移除了 ERC1155 部分,改为了继承自 Claims 合约。
所以 Claims 合约其实就是用于替代 ERC1155 来实现额外记账功能的。其实现了 balanceOf 和 transfer 两个开放函数,以及 _mint 和 _burn 两个内部函数。具体实现比较简单,这里就不贴代码了。
Fees 封装了费用相关的函数和状态变量,包括获取协议费用、获取 Hook 费用、获取动态交易费用,以及提取协议费用、提取 Hoos 费用等。
NoDelegateCall 和 UniswapV3 中使用的 NoDelegateCall 一样的,是为了防止代理调用。
Owned 则是用于设置和检查 owner 权限的。
接着,来看看 v4-periphery 的合约代码结构。其根目录下有三个目录和一个文件:
hooks/examples
interfaces
libraries
BaseHook.sol
hooks/examples 里是几个实现不同应用场景的示例代码,目前包括:
FullRange
LimitOrder
TWAMM
GeomeanOracle
VolatilityOracle
后面会用其他篇章一一剖析这几个实现,目前我们就不展开了。
BaseHook 是所有 Hooks 的基础合约,封装了最简单的实现。
实际上,我个人觉得这个 v4-periphery 应该是还没完成全部实现的,因为目前该 repo 还缺少了关键的路由合约。
前一篇文章已经对 UniswapV4 做了简单的概述,了解了其主要特性。从本篇开始,我们要深入合约实现了,先看看其合约结构。
UniswapV4 的合约项目,还是和之前的版本一样,分为了 v4-core 和 v4-periphery 两个 repo。另外,之前的版本,合约项目框架是用 Hardhat 搭建的,而这回,你会发现改用 Foundry 了。Foundry 正在慢慢变成开发新合约项目的主流框架,因为 Foundry 相比 Hardhat,写单元测试和脚本都和写合约一样,可以统一用 solidity 来编写,这对于不太精通 JavaScript/TypeScript 的合约工程师来说就会更方便了。
还有,目前的合约实现其实还不是最终版,近期依然在不断提交更新。
当前,v4-core 的合约目录结构如下图所示:

interfaces 定义了所有接口合约,libraries 存放的是所有库合约,test 目录是测试用的,我们不用关心。types 值得介绍一下,在以前的版本中没有这个。其实就是封装了几种特定类型,包括 4 种类型:
BalanceDelta
Currency
PoolId
PoolKey
PoolKey 最容易理解,我们来看看其代码实现:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Currency} from "./Currency.sol";
import {IHooks} from "../interfaces/IHooks.sol";
/// @notice Returns the key for identifying a pool
struct PoolKey {
/// @notice The lower currency of the pool, sorted numerically
Currency currency0;
/// @notice The higher currency of the pool, sorted numerically
Currency currency1;
/// @notice The pool swap fee, capped at 1_000_000. The upper 4 bits determine if the hook sets any fees.
uint24 fee;
/// @notice Ticks that involve positions must be a multiple of tick spacing
int24 tickSpacing;
/// @notice The hooks of the pool
IHooks hooks;
}
其实就是定义了一个结构体,包含了五个字段,这些字段加在一起就是一个池子的唯一标识。其中,currency0 和 currency1 就是之前版本的 token0 和 token1,只是变成了 Currency 类型。Currency 类型其实本质上也是地址类型,是由地址类型声明的用户自定义值类型。待会我们再展开介绍什么是用户自定义值类型。
PoolKey 相比 UniswapV3 时多了一个 hooks,这其实就是要指定的 Hooks 合约地址。
PoolId 就是一种用户自定义值类型,我们来看看其代码实现:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {PoolKey} from "./PoolKey.sol";
type PoolId is bytes32;
/// @notice Library for computing the ID of a pool
library PoolIdLibrary {
function toId(PoolKey memory poolKey) internal pure returns (PoolId) {
return PoolId.wrap(keccak256(abi.encode(poolKey)));
}
}
最关键的一行就是 type PoolId is bytes32。这就是用户自定义值类型的用法,使用 type C is V 的方式进行定义。V 被称为基础类型,可以是布尔型、整型、地址型、字节型等值类型的一种,但不能是 mapping、数据、结构体等引用类型。C 就是所要定义的新类型名称,有点类似于是 V 的别名,但会有严格的类型检查。
用户自定义值类型有两个内置函数可用于与基础类型之间进行转换。wrap() 函数可以将基础类型转为自定义类型,比如上面代码通过调用 PoolId.wrap() 函数就将一个 bytes32 类型的值转为了 PoolId 类型。还有个 unwrap() 函数则可以将自定义类型转为基础类型。
这种自定义类型是在 solidity 0.8.8 开始引入的,所以也只能在 0.8.8 及以上的编译版本中使用。
PoolId 其实就是用于定义一个池子的唯一 ID,从 PoolIdLibrary 的 toId() 函数可以看出,其实就是将 poolKey 进行编码后计算得出的哈希值,然后通过 wrap 函数将这个 bytes32 类型的哈希值转为了 PoolId 类型。
Currency 和 BalanceDelta 也是和 PoolId 一样的用户自定义值类型。Currency 的基础类型是 address 类型,用来表示池子里的资产。BalanceDelta 的基础类型是 int256,用来表示净余额。
Currency 的实现不只是简单地用 type 定义了其类型,还定义了一些函数,如下所示:
type Currency is address;
using {greaterThan as >, lessThan as <, greaterThanOrEqualTo as >=, equals as ==} for Currency global;
function equals(Currency currency, Currency other) pure returns (bool) {
return Currency.unwrap(currency) == Currency.unwrap(other);
}
function greaterThan(Currency currency, Currency other) pure returns (bool) {
return Currency.unwrap(currency) > Currency.unwrap(other);
}
function lessThan(Currency currency, Currency other) pure returns (bool) {
return Currency.unwrap(currency) < Currency.unwrap(other);
}
function greaterThanOrEqualTo(Currency currency, Currency other) pure returns (bool) {
return Currency.unwrap(currency) >= Currency.unwrap(other);
}
自定义类型虽然是基于基础值类型而定义的,但因为类型检查,是没办法直接使用基础类型本身的用法的,包括比较符和基础类型本身的内置函数。虽然 Currency 类型的基础类型是 address,而我们知道 address 类型的两个变量是可以直接使用 >、<、>=、== 这些比较符去比较两个地址类型的大小的。但 Currency 类型则不能直接使用,类型检查无法通过。因此,需要再额外定义四个函数,分别用于对应的四个比较符,再通过 using 语句把这四个函数作为各自的比较符进行使用。如此一来,就可以把 Currency 类型用于大小比较了。
另外,与 Currency 配置使用的还有库合约 CurrencyLibrary,其封装了转账、查询余额、是否原生代币等函数。需要对自定义类型添加额外的功能函数时,通常都是为其封装对应的库合约,PoolId 对应的有 PoolIdLibrary,BalanceDelta 对应的有 BalanceDeltaLibrary。
BalanceDelta 需要说明一下,它是用于表示净余额的,它其实是将两个代币的数额组装到一起的。在 BalanceDelta 中有定义了以下函数:
function toBalanceDelta(int128 _amount0, int128 _amount1) pure returns (BalanceDelta balanceDelta) {
/// @solidity memory-safe-assembly
assembly {
balanceDelta :=
or(shl(128, _amount0), and(0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff, _amount1))
}
}
该函数就是将两个代币的金额一起转成 BalanceDelta 类型。可看到其实现使用了内联汇编,其实就是前 128 位用于存放 amount0,后 128 位用于存放 amount1。
BalanceDeltaLibrary 库合约中则封装了 amount0() 和 amount1(),可从 BalanceDelta 中分别读取出 amount0 和 amount1。
至此,关于 types 目录的就讲解这么多了。回到 v4-core 的合约目录结构,可看到根目录下有 5 个合约文件:
Claims.sol
Fees.sol
NoDelegateCall.sol
Owned.sol
PoolManager.sol
最核心的就是 PoolManager.sol,也就是统一管理所有池的单例合约。其他几个合约都是被 PoolManager 所继承的子合约。关于 PoolManager 合约的具体实现我们下一篇文章再讲解。
关于 Claims 合约,很有必要说明一下。其实两个星期前,即 11 月中旬之前,PoolManager 还是继承了 ERC1155 的,用于额外的代币记账。但是,我发现 11 月 14 号有一个提交,移除了 ERC1155 部分,改为了继承自 Claims 合约。
所以 Claims 合约其实就是用于替代 ERC1155 来实现额外记账功能的。其实现了 balanceOf 和 transfer 两个开放函数,以及 _mint 和 _burn 两个内部函数。具体实现比较简单,这里就不贴代码了。
Fees 封装了费用相关的函数和状态变量,包括获取协议费用、获取 Hook 费用、获取动态交易费用,以及提取协议费用、提取 Hoos 费用等。
NoDelegateCall 和 UniswapV3 中使用的 NoDelegateCall 一样的,是为了防止代理调用。
Owned 则是用于设置和检查 owner 权限的。
接着,来看看 v4-periphery 的合约代码结构。其根目录下有三个目录和一个文件:
hooks/examples
interfaces
libraries
BaseHook.sol
hooks/examples 里是几个实现不同应用场景的示例代码,目前包括:
FullRange
LimitOrder
TWAMM
GeomeanOracle
VolatilityOracle
后面会用其他篇章一一剖析这几个实现,目前我们就不展开了。
BaseHook 是所有 Hooks 的基础合约,封装了最简单的实现。
实际上,我个人觉得这个 v4-periphery 应该是还没完成全部实现的,因为目前该 repo 还缺少了关键的路由合约。
No activity yet