EIP-712 使用详解
之前的文章我们介绍过如何对数据进行签名,利用签名技术我们可以实现一些功能例如白名单校验等。但是这种签名技术的应用场景比较简单,一般就是给一串字符串,或者一串哈希签名,如果我们想为更复杂的数据签名就无法实现了。 EIP-712 的出现就是为了解决这个问题,利用 EIP-712,我们可以对更大的数据集,例如对结构体进行签名。那么这种签名格式有什么实际的应用场景呢。使用过 Uniswap,PancakeSwap 等 DEX 的朋友应该有印象,在移除 LP 流动性的时候,我们需要先签名,然后再发送一笔交易移除流动性。正常情况下,其实应该我们先调用 LP 代币的授权方法,授权 DEX 合约可以转移我们的 LP,然后再去移除流动性。而这种二合一的实现正是应用了 EIP-712。它帮助我们仅仅签名一次,就可以将两步交易合并为一步交易,从而节省 Gas 费用。这篇文章我们就来看看 EIP-712 到底是怎么使用的。基本结构EIP712Domain顾名思义,是一个与域相关的结构体,总共包含五个字段:name,合约或者协议的名称version,合约的版本chainId,合约部署的链 Id,一般使用 ...
流动性挖矿-合约原理详解
流动性挖矿应该是上个牛市最火热的内容,基本上整个 DeFi 都是在围绕着流动性挖矿展开的,今天我们就来看看它到底是什么以及合约代码层面是怎么实现的。流动性挖矿简介首先我们先从用户的角度来理解一下流动性挖矿是什么,实际上就是用户通过在合约中质押一个 token 从而赚取另一个 token 的过程。例如,SushiSwap 最初推出的 DEX 流动性挖矿,用户可以通过将 SushiSwap 的 LP token 质押到合约中赚取 Sushi token。那么这个奖励具体是怎么发放以及如何实现的呢?我们今天就来研究一下这部分内容。 先来看几个例子: 一:假设有一个流动性挖矿的合约,可以质押 A token 赚取 B token。它在 0 秒时开始活动,每秒奖励 R 个 B token。此时有用户 Alice 在第 3 秒时质押了 2 个 A token,并且之后没有其他人参与,在第 8 秒时取出 token,图示:那么他在此时获得的收益就是:5R = (2 / 2) * (8 - 3) * R 其中,第一个 2 是用户 A 质押的数量,第二个 2 是合约中质押的总量,(8-3)是用户 ...
CREATE2 操作码使用方法详解
CREATE2 是一个可以在合约中创建合约的操作码。我们先来举个例子看看它能干什么:这段代码是 Uniswap v2-core 里面的工厂合约代码,使用 create2 操作码创建了 pair 合约,返回值是 pair 的地址,这样就可以逻辑中直接使用其地址进行接下来的操作。 那么 create2 到底是怎么使用呢,根据官方 EIP 文档,create2 一共接收四个参数,分别是:endowment(创建合约时往合约中打的 ETH 数量)memory_start(代码在内存中的起始位置,一般固定为 add(bytecode, 0x20) )memory_length(代码长度,一般固定为 mload(bytecode) )salt(随机数盐)这里要注意的是第一个参数如果大于 0 的话,需要待部署合约的构造方法带有 payable。随机数盐是由用户自定,须为 bytes32 格式,例如在上面 Uniswap 的例子中,salt 为:bytes32 salt = keccak256(abi.encodePacked(token0, token1)); create2 还有一个优点,相...
Smart Contract Developer
EIP-712 使用详解
之前的文章我们介绍过如何对数据进行签名,利用签名技术我们可以实现一些功能例如白名单校验等。但是这种签名技术的应用场景比较简单,一般就是给一串字符串,或者一串哈希签名,如果我们想为更复杂的数据签名就无法实现了。 EIP-712 的出现就是为了解决这个问题,利用 EIP-712,我们可以对更大的数据集,例如对结构体进行签名。那么这种签名格式有什么实际的应用场景呢。使用过 Uniswap,PancakeSwap 等 DEX 的朋友应该有印象,在移除 LP 流动性的时候,我们需要先签名,然后再发送一笔交易移除流动性。正常情况下,其实应该我们先调用 LP 代币的授权方法,授权 DEX 合约可以转移我们的 LP,然后再去移除流动性。而这种二合一的实现正是应用了 EIP-712。它帮助我们仅仅签名一次,就可以将两步交易合并为一步交易,从而节省 Gas 费用。这篇文章我们就来看看 EIP-712 到底是怎么使用的。基本结构EIP712Domain顾名思义,是一个与域相关的结构体,总共包含五个字段:name,合约或者协议的名称version,合约的版本chainId,合约部署的链 Id,一般使用 ...
流动性挖矿-合约原理详解
流动性挖矿应该是上个牛市最火热的内容,基本上整个 DeFi 都是在围绕着流动性挖矿展开的,今天我们就来看看它到底是什么以及合约代码层面是怎么实现的。流动性挖矿简介首先我们先从用户的角度来理解一下流动性挖矿是什么,实际上就是用户通过在合约中质押一个 token 从而赚取另一个 token 的过程。例如,SushiSwap 最初推出的 DEX 流动性挖矿,用户可以通过将 SushiSwap 的 LP token 质押到合约中赚取 Sushi token。那么这个奖励具体是怎么发放以及如何实现的呢?我们今天就来研究一下这部分内容。 先来看几个例子: 一:假设有一个流动性挖矿的合约,可以质押 A token 赚取 B token。它在 0 秒时开始活动,每秒奖励 R 个 B token。此时有用户 Alice 在第 3 秒时质押了 2 个 A token,并且之后没有其他人参与,在第 8 秒时取出 token,图示:那么他在此时获得的收益就是:5R = (2 / 2) * (8 - 3) * R 其中,第一个 2 是用户 A 质押的数量,第二个 2 是合约中质押的总量,(8-3)是用户 ...
CREATE2 操作码使用方法详解
CREATE2 是一个可以在合约中创建合约的操作码。我们先来举个例子看看它能干什么:这段代码是 Uniswap v2-core 里面的工厂合约代码,使用 create2 操作码创建了 pair 合约,返回值是 pair 的地址,这样就可以逻辑中直接使用其地址进行接下来的操作。 那么 create2 到底是怎么使用呢,根据官方 EIP 文档,create2 一共接收四个参数,分别是:endowment(创建合约时往合约中打的 ETH 数量)memory_start(代码在内存中的起始位置,一般固定为 add(bytecode, 0x20) )memory_length(代码长度,一般固定为 mload(bytecode) )salt(随机数盐)这里要注意的是第一个参数如果大于 0 的话,需要待部署合约的构造方法带有 payable。随机数盐是由用户自定,须为 bytes32 格式,例如在上面 Uniswap 的例子中,salt 为:bytes32 salt = keccak256(abi.encodePacked(token0, token1)); create2 还有一个优点,相...
Share Dialog
Share Dialog
Smart Contract Developer

Subscribe to xyyme.eth

Subscribe to xyyme.eth
<100 subscribers
<100 subscribers
这篇文章开始,我将用一个系列的文章详细介绍智能合约升级原理,尽量会以 Solidity 初学者的角度来探究,同时也能帮助自己更加巩固对这个知识点的了解。
我们知道,区块链上的内容都是不可变的,一旦数据上链之后,那么便是不可修改的。智能合约也是一样,部署在链上之后,合约本身的代码便是不可修改的,如果部署之后发现了合约中有 bug,那就完全没办法,只能另外部署一份合约了。其实这样也是有好处的,因为如果一份合约没有问题的话,那么也就代表它能够一直正确运行,而没有人能够修改合约的运行逻辑,真正达到的 Code is law 的理念。
但是,作为项目开发而言,这样就不是很友好了。首先,如果代码中有 bug,无法修改,如果这个 bug 是关乎用户 token 转账等场景,可能会造成用户的资产永久锁死在合约中。其次,项目发展是一个长期的过程,不可能一开始就能把所有的功能都考虑到。如果后期想要添加功能,那么就只能更换合约,并且需要用户手动操作,将旧合约中的数据(资产)转移到新合约中,这样可能会造成用户的困扰。
那么我们自然想到,合约开发能不能也可以像传统互联网开发那样,有问题随时修改,并且对于用户是无感知的呢?这时就引入了我们要介绍的合约升级的概念。
首先,我们来看传统的合约调用逻辑:

用户最初与旧合约进行交互,如果更新了新的合约,那么用户需要更换交互地址,迁移数据。新旧合约分别保存了各自的数据,两者互不关联。那么如果我们想要做成前面说的无感知升级,该怎么操作呢?
我们知道,合约本身是不能更改的,那么我们考虑,能不能用一种代理人的方式,也就是说,我们去调用一个代理合约,代理合约将我们发送的请求,也就是函数调用,转发给实际执行逻辑的合约,当需要更换新合约的时候,只需要在代理合约转发请求时,将请求发送给新的合约地址就可以了。而用户对这一切是不知道的,因为用户自始至终都是与代理合约做交互。来看看一个简单的图示:

如图中所示,我们在实际执行逻辑的合约前面加了一个代理合约,用户一直是与代理合约交互,开始时代理合约将请求转发给旧合约,当需要升级时,部署一个新的合约,同时代理合约会将请求转发给新合约,这样对于用户来说就是无感知的。
但是这里还有一个问题,我们前面说到,在传统合约模式中,用户是需要将数据从旧合约迁移到新合约中的,现在加了一个代理合约,请求是转发过去了,那么数据怎么办呢,用户的 token 不是还在旧合约中吗?此时,我们应该想到,其实代理合约不仅仅是起到转发请求的作用,而且承载了所有的合约数据,包括用户的 token 也都是存放在代理合约中,右边的逻辑合约也就仅仅是实现逻辑而已,不保存任何数据:

这样一来,我们就能随意更换逻辑合约,而不用担心数据迁移问题。也就是说,我们已经做到了和传统互联网开发一样,能够对合约进行升级。这样对于用户体验和项目方维护来说都是一个很大的优点。
那么既然合约升级这么好,为什么不是所有的项目都采用可升级合约呢?前面我们说到,可升级与不可升级之间,各自都是有利有弊的,这取决于项目方的权衡。
对于可升级合约来说,好处是优化用户体验,利于项目方维护。而坏处就是项目方可以随时更改后面的逻辑合约作恶,将用户的 token 转走,这对于用户来说是无能为力的。(现在有很多项目的升级权限都是由多签管理,但是仍然存在风险)
相对应的,对于不可升级合约,一切都写在了代码中,真正实现了 Code is law 的理念,即使项目方自己也不能随意更改合约,操纵用户资产。例如 Uniswap 的合约都是不可升级的,用户在与其进行交互时,是可以放心将资产放在上面的。但是代价就是项目开发时要做到各种测试,包括项目审计也要做很多遍,因为一旦部署,就无法更改了。(这里并不是说使用了可升级合约就可以省去这些安全步骤,安全一定是合约开发的重中之重,只是说可升级合约即使发现了 bug,也可以后期修改)
综合而言,两者都有各自的优缺点,取决于项目方的考虑与权衡。但要注意的是,并不是说不可升级合约就一定是安全的项目方,而可升级的合约就一定是是不安全的项目方。项目方之间差距也很大,例如 USDC 就是可升级合约,但是 USDC 几乎可以说是不会作恶的。
通过代理合约模式可以实现合约升级,对于用户是无感知的。同时合约升级也是有利有弊,需要针对具体场景判断选择哪种模式。
欢迎和我交流
这篇文章开始,我将用一个系列的文章详细介绍智能合约升级原理,尽量会以 Solidity 初学者的角度来探究,同时也能帮助自己更加巩固对这个知识点的了解。
我们知道,区块链上的内容都是不可变的,一旦数据上链之后,那么便是不可修改的。智能合约也是一样,部署在链上之后,合约本身的代码便是不可修改的,如果部署之后发现了合约中有 bug,那就完全没办法,只能另外部署一份合约了。其实这样也是有好处的,因为如果一份合约没有问题的话,那么也就代表它能够一直正确运行,而没有人能够修改合约的运行逻辑,真正达到的 Code is law 的理念。
但是,作为项目开发而言,这样就不是很友好了。首先,如果代码中有 bug,无法修改,如果这个 bug 是关乎用户 token 转账等场景,可能会造成用户的资产永久锁死在合约中。其次,项目发展是一个长期的过程,不可能一开始就能把所有的功能都考虑到。如果后期想要添加功能,那么就只能更换合约,并且需要用户手动操作,将旧合约中的数据(资产)转移到新合约中,这样可能会造成用户的困扰。
那么我们自然想到,合约开发能不能也可以像传统互联网开发那样,有问题随时修改,并且对于用户是无感知的呢?这时就引入了我们要介绍的合约升级的概念。
首先,我们来看传统的合约调用逻辑:

用户最初与旧合约进行交互,如果更新了新的合约,那么用户需要更换交互地址,迁移数据。新旧合约分别保存了各自的数据,两者互不关联。那么如果我们想要做成前面说的无感知升级,该怎么操作呢?
我们知道,合约本身是不能更改的,那么我们考虑,能不能用一种代理人的方式,也就是说,我们去调用一个代理合约,代理合约将我们发送的请求,也就是函数调用,转发给实际执行逻辑的合约,当需要更换新合约的时候,只需要在代理合约转发请求时,将请求发送给新的合约地址就可以了。而用户对这一切是不知道的,因为用户自始至终都是与代理合约做交互。来看看一个简单的图示:

如图中所示,我们在实际执行逻辑的合约前面加了一个代理合约,用户一直是与代理合约交互,开始时代理合约将请求转发给旧合约,当需要升级时,部署一个新的合约,同时代理合约会将请求转发给新合约,这样对于用户来说就是无感知的。
但是这里还有一个问题,我们前面说到,在传统合约模式中,用户是需要将数据从旧合约迁移到新合约中的,现在加了一个代理合约,请求是转发过去了,那么数据怎么办呢,用户的 token 不是还在旧合约中吗?此时,我们应该想到,其实代理合约不仅仅是起到转发请求的作用,而且承载了所有的合约数据,包括用户的 token 也都是存放在代理合约中,右边的逻辑合约也就仅仅是实现逻辑而已,不保存任何数据:

这样一来,我们就能随意更换逻辑合约,而不用担心数据迁移问题。也就是说,我们已经做到了和传统互联网开发一样,能够对合约进行升级。这样对于用户体验和项目方维护来说都是一个很大的优点。
那么既然合约升级这么好,为什么不是所有的项目都采用可升级合约呢?前面我们说到,可升级与不可升级之间,各自都是有利有弊的,这取决于项目方的权衡。
对于可升级合约来说,好处是优化用户体验,利于项目方维护。而坏处就是项目方可以随时更改后面的逻辑合约作恶,将用户的 token 转走,这对于用户来说是无能为力的。(现在有很多项目的升级权限都是由多签管理,但是仍然存在风险)
相对应的,对于不可升级合约,一切都写在了代码中,真正实现了 Code is law 的理念,即使项目方自己也不能随意更改合约,操纵用户资产。例如 Uniswap 的合约都是不可升级的,用户在与其进行交互时,是可以放心将资产放在上面的。但是代价就是项目开发时要做到各种测试,包括项目审计也要做很多遍,因为一旦部署,就无法更改了。(这里并不是说使用了可升级合约就可以省去这些安全步骤,安全一定是合约开发的重中之重,只是说可升级合约即使发现了 bug,也可以后期修改)
综合而言,两者都有各自的优缺点,取决于项目方的考虑与权衡。但要注意的是,并不是说不可升级合约就一定是安全的项目方,而可升级的合约就一定是是不安全的项目方。项目方之间差距也很大,例如 USDC 就是可升级合约,但是 USDC 几乎可以说是不会作恶的。
通过代理合约模式可以实现合约升级,对于用户是无感知的。同时合约升级也是有利有弊,需要针对具体场景判断选择哪种模式。
欢迎和我交流
No activity yet