Uniswap v4 Permissioned Pools解析:把合规控制嵌入 AMM 交易路径

作者:Beosin 发布:2026-08-05 21:40 收录:2026-09-03 09:16 1 次阅读 约 1490 字
摘要:Permissioned Pools 将合规控制嵌入链上执行路径,为 RWA 项目提供更标准的链上流动性基础设施,获得更多流动性,但项目团队需满足更复杂的权限治理和更高的合约审计要求。
推荐理由:本文涵盖「RWA」、「Uniswap v4」、「Permissioned Pools」等多个主题,重点关注 RWA。

Uniswap v4 把 AMM 从固定池子合约推进到可扩展的池子执行框架。v4 通过单例 PoolManager 管理所有池子,并允许每个池子挂载一个 hook,在初始化、swap、增减流动性等生命周期节点插入自定义逻辑。

Permissioned Pools 正是基于 hook 能力构建的。Uniswap Labs 在 7 月 23 日联合Securitize和Superstate发布了 Permissioned Pools,旨在让受监管资产、RWA(如股票代币、基金代币)等资产可以在 AMM 上交易,同时把白名单和权限检查放到链上执行,而不是只依赖前端或链下合规判断。

一、Permissioned Pools 机制解析


传统 AMM Pool 默认是 permissionless 的,任何地址只要持有代币、授权代币,就可以进行交易或提供流动性。这一设计难以满足RWA资产的合规要求,而如果直接把 permissioned token 放入Uniswap v4 AMM Pool,未获得授权的地址可能通过内部 claim 或组合路径绕过原 token 白名单的流动性入口。为此,Uniswap 的 Permissioned Pools 采用了 adapter + hook 的设计:池子里交易的不是原始 
permissioned token,而是由 PermissionsAdapter 管理的虚拟表示。

1.1 Permissioned Pools 架构

Permissioned Pools 主要由以下4个核心组件:

(1) PermissionsAdapter

核心包装合约。它持有底层 permissioned token,并铸造池子内部使用的 virtual token。用户通常不直接管理 virtual token,路由器和 position manager 会在交互中自动处理包装与解包(unwrap)。


(2) IAllowlistChecker

发行方自行实现的白名单检查合约。它返回某个地址对某个 token 的权限,例如是否允许 swap、是否允许提供流动性。


(3) PermissionedHooks

挂在 v4 pool 上的 hook。它在 beforeInitialize、beforeSwap、beforeAddLiquidity 等阶段执行检查:池子是否使用已验证 adapter、swapper 是否有 SWAP_ALLOWED、LP 是否有 LIQUIDITY_ALLOWED。


(4) PermissionedPositionManager / Universal Router

负责用户实际交互。permissioned swap routing 需要通过 Universal Router,而 LP position NFT 也不可转让,以避免已授权地址把 LP 仓位转给未授权地址。这一限制能够对获得做市收益或间接持有敞口的场景进行监管。

 

1.2 交易流程如何被限制


以 swap 为例,路径基本为:


(1) 用户持有底层 permissioned token;

(2) 用户通过 Universal Router 发起 swap;

(3) Router 检查并把底层 token 转入 PermissionsAdapter;

(4) Adapter 创建池子内部使用的 virtual token;

(5) v4 PoolManager 执行 swap;

(6) PermissionedHooks.beforeSwap 检查用户是否有 SWAP_ALLOWED;

(7) swap 完成后,virtual token 被转换回底层 token。


二、代码案例分析


2.1 Allowlist Checker


下面是一个简化版 allowlist checker,实际生产环境应使用多签、更细致的角色权限管理、可审计的 KYC provider 更新流程。


// SPDX-License-Identifier: MITpragma solidity ^0.8.24; import {ERC165IERC165from "@openzeppelin/contracts/utils/introspection/ERC165.sol";import {Ownablefrom "@openzeppelin/contracts/access/Ownable.sol"; import {    IAllowlistCheckerfrom "@uniswap/v4-periphery/src/hooks/permissionedPools/interfaces/IAllowlistChecker.sol"; import {    PermissionFlag,    PermissionFlagsfrom "@uniswap/v4-periphery/src/hooks/permissionedPools/libraries/PermissionFlags.sol"; contract IssuerAllowlistChecker is IAllowlistCheckerERC165Ownable {    mapping(address account => PermissionFlag permission) private _permissions;     constructor(address initialOwnerOwnable(initialOwner) {}     function checkAllowlist(        address account,        address /* tokenAddress */    ) external view returns (PermissionFlag) {        return _permissions[account];    }     function setSwapAllowed(address account, bool allowed) external onlyOwner {        PermissionFlag current = _permissions[account];         if (allowed) {            _permissions[account] = current | PermissionFlags.SWAP_ALLOWED;        } else {            _permissions[account] = current & ~PermissionFlags.SWAP_ALLOWED;        }    }     function setLiquidityAllowed(address account, bool allowed) external onlyOwner {        PermissionFlag current = _permissions[account];         if (allowed) {            _permissions[account] = current | PermissionFlags.LIQUIDITY_ALLOWED;        } else {            _permissions[account] = current & ~PermissionFlags.LIQUIDITY_ALLOWED;        }    }     function setFullPermission(address account) external onlyOwner {        _permissions[account] =            PermissionFlags.SWAP_ALLOWED |            PermissionFlags.LIQUIDITY_ALLOWED;    }     function clearPermission(address account) external onlyOwner {        delete _permissions[account];    }     function supportsInterface(        bytes4 interfaceId    ) public view override(ERC165IERC165returns (bool) {        return            interfaceId == type(IAllowlistChecker).interfaceId ||            super.supportsInterface(interfaceId);    }}

该合约只负责检查某地址对某类资产有什么权限,真正的执行操作发生在adapter 和 hook 中。


2.2 部署 adapter 并创建 pool 


// SPDX-License-Identifier: MITpragma solidity ^0.8.24;import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";import {    IAllowlistCheckerfrom "@uniswap/v4-periphery/src/hooks/permissionedPools/interfaces/IAllowlistChecker.sol"; interface IPermissionsAdapterFactory {    function createPermissionsAdapter(        IERC20 token,        address owner,        IAllowlistChecker checker    ) external returns (address adapter);     function verifyPermissionsAdapter(address adapterexternal;} interface IPermissionsAdapter {    function depositForVerification(uint256 amountexternal;    function updateAllowedWrapper(address wrapper, bool allowedexternal;    function updateSwappingEnabled(bool enabledexternal;} interface IPermissionedPositionManager {    function setAllowedHook(        Currency currency,        IHooks hook,        bool allowed    ) external;} contract PermissionedPoolOnboardingExample {    function onboard(        address permissionedToken,        address issuerAdmin,        address checker,        address factory,        address permissionedPositionManager,        address universalRouter,        address v4Quoter,        address mixedRouteQuoterV2,        address permissionedHooks,        address pairedAsset    ) external returns (PoolKey memory key) {        address adapter = IPermissionsAdapterFactory(factory)            .createPermissionsAdapter(                IERC20(permissionedToken),                issuerAdmin,                IAllowlistChecker(checker)            );
        IPermissionsAdapter(adapter).depositForVerification(1);        IPermissionsAdapterFactory(factory).verifyPermissionsAdapter(adapter);        IPermissionsAdapter(adapter).updateAllowedWrapper(permissionedPositionManager, true);        IPermissionsAdapter(adapter).updateAllowedWrapper(universalRouter, true);        IPermissionsAdapter(adapter).updateAllowedWrapper(v4Quoter, true);        IPermissionsAdapter(adapter).updateAllowedWrapper(mixedRouteQuoterV2, true);        IPermissionedPositionManager(permissionedPositionManager).setAllowedHook(            Currency.wrap(adapter),            IHooks(permissionedHooks),            true        );         key = PoolKey({            currency0: Currency.wrap(adapter),            currency1: Currency.wrap(pairedAsset),            fee3000,            tickSpacing60,            hooksIHooks(permissionedHooks)        });         IPermissionsAdapter(adapter).updateSwappingEnabled(true);    }}

Permissioned Pools 将合规控制嵌入链上执行路径,但需要注意其中的潜在安全点:PermissionsAdapter的owner地址可以更换 allowlist checker、可以暂停或开启swap、批准或撤销wrapper,allowlist checker的逻辑实现,资产发行方管理的用户资格变化。

结语

Uniswap v4 Permissioned Pools 的意义在于它让 permissioned assets 可以进入 AMM流动性池。v4 的 hook 架构提供了插入点,PermissionsAdapter 解决了底层 token 不能直接进入共享 PoolManager 的问题,PermissionedHooks 则把 swap 和 LP 的资格检查变成链上约束。这一设计将为 RWA 项目提供更标准的链上流动性基础设施,获得更多流动性,但项目团队需满足更复杂的权限治理和更高的合约审计要求

拓展阅读:Uniswap v4 Hook解析:架构设计、常见漏洞与防护实践

Beosin是一家领先的区块链安全与监管合规科技公司,专注于项目上线前的智能合约安全审计、项目运行时的安全风险监控与阻断、被盗追回、虚拟资产反洗钱(AML)以及调查追踪。Beosin已为全球20多个国家和地区的监管与执法机构、200多家虚拟资产服务商以及4500多家Web3项目提供“一站式”区块链合规产品+安全服务。欢迎点击公众号留言框,与我们联系。
































转载声明:本文转载自原发布平台 (作者:Beosin), 原文标题《Uniswap v4 Permissioned Pools解析:把合规控制嵌入 AMM 交易路径》, 查看原文。 版权归原作者及原发布平台所有,本站仅作收录与展示,未对正文内容作实质性修改; 若涉及侵权请联系本站处理。