Uniswap v3 无常损失分析
Sep 17, 202228 min. readUniswap v3 无常损失分析目标对 Uniswap v3 无常损失的定量分析;如何使用策略让 Uniswap v3 LP 获得更大的收益。Uniswap 概览基于恒定乘积的自动化做市商(AMM),去中心化交易所。 v1 版本:2018年11月解决了什么问题:传统交易所 order book 买卖双方不活跃导致的长时间挂单,交易效率低下功能:ETH ←→ ERC20 token 兑换带来的问题:token1 与 token2 之间的兑换需要借助 ETHUSDT → ETH → USDCv2 版本:2020年5月新功能自由组合交易对:token1 ←→ token2token1-token2 交易池LPers 提供流动性并赚取费用价格预言机(时间加权平均价格,TWAP)、闪电贷、最优化交易路径等带来的问题资金利用率低:在 x*y=k 的情况下,做市的价格区间在 (0, +∞) 的分布,当用户交易时,交易的量相比我们的流动性来说是很小的假设 ETH/DAI 交易对的实时价格为 1500 DAI/ETH,交易对的流动性池中共有资金:4...
使用 Merkle 树做 NFT 白名单验证
使用 Merkle 树做 NFT 白名单验证Merkle 树现在普遍用来做线上数据验证。这篇文章主要解释和实现使用 Merkle 树做 NFT 白名单验证。 使用 Merkle 树做 NFT 白名单验证,简单来说就是将所有的白名单钱包地址做为 Merkle 树的叶节点生成一棵 Merkle 树,在部署的NFT 合约中只存储 Merkle 树的 root hash,这样避免了在合约中存储所有白名单地址带来的高额 gas 费用。在 mint 时,前端生成钱包地址的 Merkle proof,调用合约进行验证即可。 一次验证过程前端和合约运行过程如图:图片来自 [3]Merkle 树详情请参见:https://en.wikipedia.org/wiki/Merkle_tree图片来自 [1]比如,以水果单词作为叶节点,生成 Merkle 树的结构如下:图片来自 [2]合约实现我们简单实现 Merkle 验证的过程,此合约包含以下功能:设置 Merkle 根哈希: setSaleMerkleRoot验证 Merkle proof: isValidMerkleProofmint 并记录是否...
Sui 数据类型讲解
这篇文章中,我们将介绍 Sui 中常见的数据结构,这些结构包含 Sui Move 和 Sui Framework 中提供的基础类型和数据结构,理解和熟悉这些数据结构对于 Sui Move 的理解和应用大有裨益。 首先,我们先快速复习一下 Sui Move 中使用到的基础类型。无符号整型(Integer)Move 包含六种无符号整型:u8,u16 u32,u64,u128和 u256。值的范围从 0 到 与类型大小相关的最大值。 这些类型的字面值为数字序列(例如 112)或十六进制文字,例如 0xFF。 字面值的类型可以选择添加为后缀,例如 112u8。 如果未指定类型,编译器将尝试从使用文字的上下文中推断类型。 如果无法推断类型,则假定为 u64。 对无符号整型支持的运算包括:算数运算: + - * % /位运算: & | ^ >> <<比较运算: > < >= <= == !=类型转换: as注意,类型转换不会截断,因此如果结果对于指定类型而言太大,转换将中止。简单示例:let a: u64 = 4; let b = 2u64; let hex_u64: u64 = 0xCAF...
<100 subscribers
Uniswap v3 无常损失分析
Sep 17, 202228 min. readUniswap v3 无常损失分析目标对 Uniswap v3 无常损失的定量分析;如何使用策略让 Uniswap v3 LP 获得更大的收益。Uniswap 概览基于恒定乘积的自动化做市商(AMM),去中心化交易所。 v1 版本:2018年11月解决了什么问题:传统交易所 order book 买卖双方不活跃导致的长时间挂单,交易效率低下功能:ETH ←→ ERC20 token 兑换带来的问题:token1 与 token2 之间的兑换需要借助 ETHUSDT → ETH → USDCv2 版本:2020年5月新功能自由组合交易对:token1 ←→ token2token1-token2 交易池LPers 提供流动性并赚取费用价格预言机(时间加权平均价格,TWAP)、闪电贷、最优化交易路径等带来的问题资金利用率低:在 x*y=k 的情况下,做市的价格区间在 (0, +∞) 的分布,当用户交易时,交易的量相比我们的流动性来说是很小的假设 ETH/DAI 交易对的实时价格为 1500 DAI/ETH,交易对的流动性池中共有资金:4...
使用 Merkle 树做 NFT 白名单验证
使用 Merkle 树做 NFT 白名单验证Merkle 树现在普遍用来做线上数据验证。这篇文章主要解释和实现使用 Merkle 树做 NFT 白名单验证。 使用 Merkle 树做 NFT 白名单验证,简单来说就是将所有的白名单钱包地址做为 Merkle 树的叶节点生成一棵 Merkle 树,在部署的NFT 合约中只存储 Merkle 树的 root hash,这样避免了在合约中存储所有白名单地址带来的高额 gas 费用。在 mint 时,前端生成钱包地址的 Merkle proof,调用合约进行验证即可。 一次验证过程前端和合约运行过程如图:图片来自 [3]Merkle 树详情请参见:https://en.wikipedia.org/wiki/Merkle_tree图片来自 [1]比如,以水果单词作为叶节点,生成 Merkle 树的结构如下:图片来自 [2]合约实现我们简单实现 Merkle 验证的过程,此合约包含以下功能:设置 Merkle 根哈希: setSaleMerkleRoot验证 Merkle proof: isValidMerkleProofmint 并记录是否...
Sui 数据类型讲解
这篇文章中,我们将介绍 Sui 中常见的数据结构,这些结构包含 Sui Move 和 Sui Framework 中提供的基础类型和数据结构,理解和熟悉这些数据结构对于 Sui Move 的理解和应用大有裨益。 首先,我们先快速复习一下 Sui Move 中使用到的基础类型。无符号整型(Integer)Move 包含六种无符号整型:u8,u16 u32,u64,u128和 u256。值的范围从 0 到 与类型大小相关的最大值。 这些类型的字面值为数字序列(例如 112)或十六进制文字,例如 0xFF。 字面值的类型可以选择添加为后缀,例如 112u8。 如果未指定类型,编译器将尝试从使用文字的上下文中推断类型。 如果无法推断类型,则假定为 u64。 对无符号整型支持的运算包括:算数运算: + - * % /位运算: & | ^ >> <<比较运算: > < >= <= == !=类型转换: as注意,类型转换不会截断,因此如果结果对于指定类型而言太大,转换将中止。简单示例:let a: u64 = 4; let b = 2u64; let hex_u64: u64 = 0xCAF...
Share Dialog
Share Dialog
假设地址 A 发送了 100 ETH 给地址 B,由于在区块链上所有的数据都是公开的,所以全世界都知道地址 A 和地址 B 进行了一次交易,如果地址A和地址 B 属于同一个用户 Alice,则大家知道Alice仍然拥有 100 ETH,如果地址B属于用户 Bob,则大家知道 Bob 现在有 100ETH 了。一个问题就是:如何在交易的过程中保持隐蔽呢,或者说隐藏发送用户与接收用户之前的练习?那就要用到 Tornado Cash。
用户将资金存入Tornado Cash,然后将资金提取到另一个地址中,在区块链上记录上,这两个地址之间的联系就大概率断开了。那 Tornado Cash 是如何做到的呢?
首先我们看一下存款过程。用户在存款时需要生产两个随机数 secret 和 nullifier,并计算这两个数的一个哈希 commitment = hash(secret, nullifier),然后用户将需要混币的金额(比如 1 ETH)和 commitment 发送给 TC 合约的 deposit 函数,TC合约将保存这两个数据,commitment之后会用于提取存入的资金。
同时,用户会得到一个凭证,通过这个凭证,用户(或者任何人)就可以提取存入的资金。
如果不同的用户会存入不同的金额,比如 Alice 和 Bob 存入 1 ETH,Chris 存入 73 ETH,当取出存款时,某个地址提取了 73 ETH,我们会有很大程度怀疑这个地址属于 Chris。因此,在TC 合约中规定了每次存入的金额为 1 ETH,这样就不会有地址与其他地址不一致。
实际上,TC 有不同金额的 ETH 存款池,分别为 0.1,1,10,100,以满足不同数量的存取款需求。
当进行取款时,一种错误方法是将之前随机生成的 secret 和 nullifier 作为参数发送给合约的取款函数,合约检查 hash(secret,nullifier) 是否等于之前保存的 commitment,如果相等就发送 1 ETH给取款者。但是这个过程就使得取款者的身份暴露了,因为 hash 过程是不可逆的,当我们从存款日志中找到相等的commitment时,我们就可以通过 commitment 建立存款者和取款者之间的联系,因为只有这个存款者知道获得 commitment 的 secret 和 nullifier。
如果解决这个过程呢?如果有人有一种方法可以证明他知道一组(secret, nullifier) 使得 hash(secret, nullifier) 在合约记录的commitment列表中,但是却不公开这组(secret, nullifier) ,那这个人就可以只用发送这个证明给合约进行验证,就可以证明他拥有之前存入过资金,当却不知道对应于哪一组存入的资金,所以仍然保持匿名。
这个证明就是零知识证明,它可以证明你知道某个信息但却不用公开这个信息。TC 使用的零知识证明称为 zk-SNARK。
我们注意到当用户存款和取款时,使用了两个随机数 secret 和 nullifier,nullifier 的作用是什么呢?当用户取款时,合约其实不知道到底是谁在取款,为了避免用户存入 1 ETH 然后进行多次提取,TC要求当用户发送证明的同时发送 nullifier 的哈希nullifierHash,在zk-SNARK的证明中,他会检查两件事情:一是检查 hash(secret, nullifier) 在 commitment 的列表中,二是 nullifierHash 等于 hash(nullifier),一旦验证成功,合约就会记录这个哈希。当同一个证明第二次被提交时就会失败,因为对应的 nullifier 哈希已经使用过了,这样就避免了二次提款。
使用 Merkle 树。Merkle树具体参见之前的介绍文章。
TC 会首先初始化一组叶子节点为 keccak256("tornado"),并以这些叶子节点构建一颗 Merkle 树。当用户存款时,对应的 commitment 存入 Merkle 树的第一个叶子节点,然后合约更新整棵 Merkle 树,然后是第二个用户的commitment 存入第二个叶子节点并更新整棵 Merkle 树,依次类推。

如何证明 commitment 在这棵 Merkle 树中呢?

假设需要证明c3在这棵Merkel中,我们需要找到从叶子节点 c3 到根的路径过程中的哈希,使得他们与 c3 依次进行 hash 可以得到根哈希,即图中绿色节点的哈希列表。
Tornado Cash 中,我们需要提供这些节点哈希,并通过 zk-SNARK 生成零知识证明,以此证明 c3 在这棵以 root(=h(h(h(c0,c1),h(c2,c3)),h(h(c4,c5), z1)))为根的 Merkle 中,但却不用告诉大家 c3 的值。

因此我们将证明 proof 和 Merkle 树根 root 发送给合约,一旦合约验证成功,我们就可以取出之前存入的存款。
TC 合约包含三个部分:
存款和取款合约,用于与用户交互;
Merkle 树,用于记录存款哈希;
zk-SNARK 验证器合约,用于验证取款时证明合法。
zk-SNARK 验证器合约由 circom 编写的验证电路通过 snarkjs 库生成。
假设地址 A 发送了 100 ETH 给地址 B,由于在区块链上所有的数据都是公开的,所以全世界都知道地址 A 和地址 B 进行了一次交易,如果地址A和地址 B 属于同一个用户 Alice,则大家知道Alice仍然拥有 100 ETH,如果地址B属于用户 Bob,则大家知道 Bob 现在有 100ETH 了。一个问题就是:如何在交易的过程中保持隐蔽呢,或者说隐藏发送用户与接收用户之前的练习?那就要用到 Tornado Cash。
用户将资金存入Tornado Cash,然后将资金提取到另一个地址中,在区块链上记录上,这两个地址之间的联系就大概率断开了。那 Tornado Cash 是如何做到的呢?
首先我们看一下存款过程。用户在存款时需要生产两个随机数 secret 和 nullifier,并计算这两个数的一个哈希 commitment = hash(secret, nullifier),然后用户将需要混币的金额(比如 1 ETH)和 commitment 发送给 TC 合约的 deposit 函数,TC合约将保存这两个数据,commitment之后会用于提取存入的资金。
同时,用户会得到一个凭证,通过这个凭证,用户(或者任何人)就可以提取存入的资金。
如果不同的用户会存入不同的金额,比如 Alice 和 Bob 存入 1 ETH,Chris 存入 73 ETH,当取出存款时,某个地址提取了 73 ETH,我们会有很大程度怀疑这个地址属于 Chris。因此,在TC 合约中规定了每次存入的金额为 1 ETH,这样就不会有地址与其他地址不一致。
实际上,TC 有不同金额的 ETH 存款池,分别为 0.1,1,10,100,以满足不同数量的存取款需求。
当进行取款时,一种错误方法是将之前随机生成的 secret 和 nullifier 作为参数发送给合约的取款函数,合约检查 hash(secret,nullifier) 是否等于之前保存的 commitment,如果相等就发送 1 ETH给取款者。但是这个过程就使得取款者的身份暴露了,因为 hash 过程是不可逆的,当我们从存款日志中找到相等的commitment时,我们就可以通过 commitment 建立存款者和取款者之间的联系,因为只有这个存款者知道获得 commitment 的 secret 和 nullifier。
如果解决这个过程呢?如果有人有一种方法可以证明他知道一组(secret, nullifier) 使得 hash(secret, nullifier) 在合约记录的commitment列表中,但是却不公开这组(secret, nullifier) ,那这个人就可以只用发送这个证明给合约进行验证,就可以证明他拥有之前存入过资金,当却不知道对应于哪一组存入的资金,所以仍然保持匿名。
这个证明就是零知识证明,它可以证明你知道某个信息但却不用公开这个信息。TC 使用的零知识证明称为 zk-SNARK。
我们注意到当用户存款和取款时,使用了两个随机数 secret 和 nullifier,nullifier 的作用是什么呢?当用户取款时,合约其实不知道到底是谁在取款,为了避免用户存入 1 ETH 然后进行多次提取,TC要求当用户发送证明的同时发送 nullifier 的哈希nullifierHash,在zk-SNARK的证明中,他会检查两件事情:一是检查 hash(secret, nullifier) 在 commitment 的列表中,二是 nullifierHash 等于 hash(nullifier),一旦验证成功,合约就会记录这个哈希。当同一个证明第二次被提交时就会失败,因为对应的 nullifier 哈希已经使用过了,这样就避免了二次提款。
使用 Merkle 树。Merkle树具体参见之前的介绍文章。
TC 会首先初始化一组叶子节点为 keccak256("tornado"),并以这些叶子节点构建一颗 Merkle 树。当用户存款时,对应的 commitment 存入 Merkle 树的第一个叶子节点,然后合约更新整棵 Merkle 树,然后是第二个用户的commitment 存入第二个叶子节点并更新整棵 Merkle 树,依次类推。

如何证明 commitment 在这棵 Merkle 树中呢?

假设需要证明c3在这棵Merkel中,我们需要找到从叶子节点 c3 到根的路径过程中的哈希,使得他们与 c3 依次进行 hash 可以得到根哈希,即图中绿色节点的哈希列表。
Tornado Cash 中,我们需要提供这些节点哈希,并通过 zk-SNARK 生成零知识证明,以此证明 c3 在这棵以 root(=h(h(h(c0,c1),h(c2,c3)),h(h(c4,c5), z1)))为根的 Merkle 中,但却不用告诉大家 c3 的值。

因此我们将证明 proof 和 Merkle 树根 root 发送给合约,一旦合约验证成功,我们就可以取出之前存入的存款。
TC 合约包含三个部分:
存款和取款合约,用于与用户交互;
Merkle 树,用于记录存款哈希;
zk-SNARK 验证器合约,用于验证取款时证明合法。
zk-SNARK 验证器合约由 circom 编写的验证电路通过 snarkjs 库生成。
No comments yet