
Uniswap V3交易预计算技巧
Uniswap上做swap交易时,比如用usdt购买btc,会根据界面上输入的usdt数量,实时计算出可以swap到多少个btc,v2版本因为是应用了xy=k的公式,可以方便的计算出来。代码里通过getAmountOut和getAmountIn得到,这两个都是view函数,不需要消耗gas。而到了...
Meebits mint随机算法
先上代码function randomIndex() internal returns (uint) { uint totalSize = TOKEN_LIMIT - numTokens; uint index = uint(keccak256(abi.encodePacked(nonce, ms...

nftx闪电贷领取apecoin空投
已经是两个多月前的事件了,这几天才对这个tx进行了分析和fork重现,记录下来加深一下理解。apecoin的空投并没有限制调用方不能是合约地址,也不是用链下签名再到合约里验签的方式来领取空投,而是直接在合约里校验调用方的地址里有没有bayc/mayc,再加上apecoin当时的价格在8u左右,总体...
<100 subscribers

Uniswap V3交易预计算技巧
Uniswap上做swap交易时,比如用usdt购买btc,会根据界面上输入的usdt数量,实时计算出可以swap到多少个btc,v2版本因为是应用了xy=k的公式,可以方便的计算出来。代码里通过getAmountOut和getAmountIn得到,这两个都是view函数,不需要消耗gas。而到了...
Meebits mint随机算法
先上代码function randomIndex() internal returns (uint) { uint totalSize = TOKEN_LIMIT - numTokens; uint index = uint(keccak256(abi.encodePacked(nonce, ms...

nftx闪电贷领取apecoin空投
已经是两个多月前的事件了,这几天才对这个tx进行了分析和fork重现,记录下来加深一下理解。apecoin的空投并没有限制调用方不能是合约地址,也不是用链下签名再到合约里验签的方式来领取空投,而是直接在合约里校验调用方的地址里有没有bayc/mayc,再加上apecoin当时的价格在8u左右,总体...
Share Dialog
Share Dialog
这是大家最常提及的bytecode,用来生成runtime bytecode,包括了构造函数逻辑和逻辑函数参数。在solidity里可以用type(ContractName).creationCode来获取。当你编译合约时,creation bytecode就会生成。在Remix里可以用compilation details里查看,如下图,划红线的就是creation bytecode。

这是存储在链上用来描述智能合约的代码。这个代码不包括构造函数逻辑和参数,实际上是creation bytecode在evm里执行完后的返回的字节码。下文讲述的metamorphic合约就是利用了这个性质来实现用不同的bytecode部署相同地址的合约的。
合约的runtime bytecode可以在链上用extcodecopy获取,在solidity里可以用type(ContractName).runtimeCode来获取,runtime bytecode的hash可以用extcodehash获取。
这是一个概括性的术语,包括了runtime bytecode和creation byte,但更通常用来表示runtime bytecode。
实际上也是creation bytecode,这个术语在create2 opcode的文档里有提及。
在Constantinople硬分叉后,EVM增加了create2这个opcode,允许传入待创建合约的creation bytecode和salt,部署合约在指定的地址,这个地址可以用keccak256(0xff ++ deployersAddr ++ salt ++ keccak256(bytecode))[12:]计算出来。而EVM里还有另外一个opcode,叫做selfdestruct,可以用来销毁当前合约,销毁后合约的runtime code会变成0,而且storage也会全部清空。
Metamorphic合约利用了上述两个特性,非常巧妙的实现了合约的升级。
//SPDX-License-Identifier: MIT pragma solidity 0.8.1; contract Factory { mapping (address => address) _implementations; event Deployed(address _addr); function deploy(uint salt, bytes calldata bytecode) public { bytes memory implInitCode = bytecode; /* * Metamorphic contract initialization code (29 bytes): * * 0x5860208158601c335a63aaf10f428752fa158151803b80938091923cf3 * * Description: * * pc|op|name | [stack] | <memory> * * ** set the first stack item to zero - used later ** * 00 58 getpc [0] <> * * ** set second stack item to 32, length of word returned from staticcall ** * 01 60 push1 * 02 20 outsize [0, 32] <> * * ** set third stack item to 0, position of word returned from staticcall ** * 03 81 dup2 [0, 32, 0] <> * * ** set fourth stack item to 4, length of selector given to staticcall ** * 04 58 getpc [0, 32, 0, 4] <> * * ** set fifth stack item to 28, position of selector given to staticcall ** * 05 60 push1 * 06 1c inpos [0, 32, 0, 4, 28] <> * * ** set the sixth stack item to msg.sender, target address for staticcall ** * 07 33 caller [0, 32, 0, 4, 28, caller] <> * * ** set the seventh stack item to msg.gas, gas to forward for staticcall ** * 08 5a gas [0, 32, 0, 4, 28, caller, gas] <> * * ** set the eighth stack item to selector, "what" to store via mstore ** * 09 63 push4 * 10 aaf10f42 selector [0, 32, 0, 4, 28, caller, gas, 0xaaf10f42] <> * * ** set the ninth stack item to 0, "where" to store via mstore *** * 11 87 dup8 [0, 32, 0, 4, 28, caller, gas, 0xaaf10f42, 0] <> * * ** call mstore, consume 8 and 9 from the stack, place selector in memory ** * 12 52 mstore [0, 32, 0, 4, 0, caller, gas] <0xaaf10f42> * * ** call staticcall, consume items 2 through 7, place address in memory ** * 13 fa staticcall [0, 1 (if successful)] <address> * * ** flip success bit in second stack item to set to 0 ** * 14 15 iszero [0, 0] <address> * * ** push a third 0 to the stack, position of address in memory ** * 15 81 dup2 [0, 0, 0] <address> * * ** place address from position in memory onto third stack item ** * 16 51 mload [0, 0, address] <> * * ** place address to fourth stack item for extcodesize to consume ** * 17 80 dup1 [0, 0, address, address] <> * * ** get extcodesize on fourth stack item for extcodecopy ** * 18 3b extcodesize [0, 0, address, size] <> * * ** dup and swap size for use by return at end of init code ** * 19 80 dup1 [0, 0, address, size, size] <> * 20 93 swap4 [size, 0, address, size, 0] <> * * ** push code position 0 to stack and reorder stack items for extcodecopy ** * 21 80 dup1 [size, 0, address, size, 0, 0] <> * 22 91 swap2 [size, 0, address, 0, 0, size] <> * 23 92 swap3 [size, 0, size, 0, 0, address] <> * * ** call extcodecopy, consume four items, clone runtime code to memory ** * 24 3c extcodecopy [size, 0] <code> * * ** return to deploy final code in memory ** * 25 f3 return [] *deployed!* */ bytes memory metamorphicCode = ( hex"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3" ); // determine the address of the metamorphic contract. address metamorphicContractAddress = _getMetamorphicContractAddress(salt, metamorphicCode); // declare a variable for the address of the implementation contract. address implementationContract; // load implementation init code and length, then deploy via CREATE. /* solhint-disable no-inline-assembly */ assembly { let encoded_data := add(0x20, implInitCode) // load initialization code. let encoded_size := mload(implInitCode) // load init code's length. implementationContract := create( // call CREATE with 3 arguments. 0, // do not forward any endowment. encoded_data, // pass in initialization code. encoded_size // pass in init code's length. ) } /* solhint-enable no-inline-assembly */ //first we deploy the code we want to deploy on a separate address // store the implementation to be retrieved by the metamorphic contract. _implementations[metamorphicContractAddress] = implementationContract; address addr; assembly { let encoded_data := add(0x20, metamorphicCode) // load initialization code. let encoded_size := mload(metamorphicCode) // load init code's length. addr := create2(0, encoded_data, encoded_size, salt) } require( addr == metamorphicContractAddress, "Failed to deploy the new metamorphic contract." ); emit Deployed(addr); } /** * @dev Internal view function for calculating a metamorphic contract address * given a particular salt. */ function _getMetamorphicContractAddress( uint256 salt, bytes memory metamorphicCode ) internal view returns (address) { // determine the address of the metamorphic contract. return address( uint160( // downcast to match the address type. uint256( // convert to uint to truncate upper digits. keccak256( // compute the CREATE2 hash using 4 inputs. abi.encodePacked( // pack all inputs to the hash together. hex"ff", // start with 0xff to distinguish from RLP. address(this), // this contract will be the caller. salt, // pass in the supplied salt value. keccak256( abi.encodePacked( metamorphicCode ) ) // the init code hash. ) ) ) ) ); } //those two functions are getting called by the metamorphic Contract function getImplementation() external view returns (address implementation) { return _implementations[msg.sender]; } } contract Test1 { uint public myUint; function setUint(uint _myUint) public { myUint = _myUint; } function killme() public { selfdestruct(payable(msg.sender)); } } contract Test2 { uint public myUint; function setUint(uint _myUint) public { myUint = 2*_myUint; } function killme() public { selfdestruct(payable(msg.sender)); } }
5860208158601c335a63aaf10f428752fa158151803b80938091923cf3,这串bytecode的原理是staticcall调用getImplementation方法,获取implementation合约地址,再用extcodecopy把implementation合约的runtime bytecode复制到memory,做为当前部署合约的runtime bytecode,以此来动态替换合约的runtime bytecode,而合约地址又不变。
部署Factory合约
用Test1的bytecode和一个固定的salt,调用Factory合约的deploy方法部署Test1合约,Deployed事件的参数就是Test1合约的地址
调用Test1合约的killme方法,销毁合约
用Test2的bytecode和部署Test1时相同的salt,调用Factory合约的deploy方法部署Test2合约,Deployed事件的参数就是Test2合约的地址
这样,就实现了合约地址不变,存储和逻辑更新的升级合约模式,而且没有用代理的delegateCall
审计合约里有没有出现selfdestruct,或者通过delegatecall或者callcode去调用selfdestruct。因为不能销毁也就意味着不能重新部署。如果存在selfdestruct,那要检查合约的部署者是否用create2创建此合约,如果是用create2创建的,就要辨别一下部署器是不是metamorphic合约,如果是那要小心了。
https://ethereum-blockchain-developer.com/110-upgrade-smart-contracts/12-metamorphosis-create2/
这是大家最常提及的bytecode,用来生成runtime bytecode,包括了构造函数逻辑和逻辑函数参数。在solidity里可以用type(ContractName).creationCode来获取。当你编译合约时,creation bytecode就会生成。在Remix里可以用compilation details里查看,如下图,划红线的就是creation bytecode。

这是存储在链上用来描述智能合约的代码。这个代码不包括构造函数逻辑和参数,实际上是creation bytecode在evm里执行完后的返回的字节码。下文讲述的metamorphic合约就是利用了这个性质来实现用不同的bytecode部署相同地址的合约的。
合约的runtime bytecode可以在链上用extcodecopy获取,在solidity里可以用type(ContractName).runtimeCode来获取,runtime bytecode的hash可以用extcodehash获取。
这是一个概括性的术语,包括了runtime bytecode和creation byte,但更通常用来表示runtime bytecode。
实际上也是creation bytecode,这个术语在create2 opcode的文档里有提及。
在Constantinople硬分叉后,EVM增加了create2这个opcode,允许传入待创建合约的creation bytecode和salt,部署合约在指定的地址,这个地址可以用keccak256(0xff ++ deployersAddr ++ salt ++ keccak256(bytecode))[12:]计算出来。而EVM里还有另外一个opcode,叫做selfdestruct,可以用来销毁当前合约,销毁后合约的runtime code会变成0,而且storage也会全部清空。
Metamorphic合约利用了上述两个特性,非常巧妙的实现了合约的升级。
//SPDX-License-Identifier: MIT pragma solidity 0.8.1; contract Factory { mapping (address => address) _implementations; event Deployed(address _addr); function deploy(uint salt, bytes calldata bytecode) public { bytes memory implInitCode = bytecode; /* * Metamorphic contract initialization code (29 bytes): * * 0x5860208158601c335a63aaf10f428752fa158151803b80938091923cf3 * * Description: * * pc|op|name | [stack] | <memory> * * ** set the first stack item to zero - used later ** * 00 58 getpc [0] <> * * ** set second stack item to 32, length of word returned from staticcall ** * 01 60 push1 * 02 20 outsize [0, 32] <> * * ** set third stack item to 0, position of word returned from staticcall ** * 03 81 dup2 [0, 32, 0] <> * * ** set fourth stack item to 4, length of selector given to staticcall ** * 04 58 getpc [0, 32, 0, 4] <> * * ** set fifth stack item to 28, position of selector given to staticcall ** * 05 60 push1 * 06 1c inpos [0, 32, 0, 4, 28] <> * * ** set the sixth stack item to msg.sender, target address for staticcall ** * 07 33 caller [0, 32, 0, 4, 28, caller] <> * * ** set the seventh stack item to msg.gas, gas to forward for staticcall ** * 08 5a gas [0, 32, 0, 4, 28, caller, gas] <> * * ** set the eighth stack item to selector, "what" to store via mstore ** * 09 63 push4 * 10 aaf10f42 selector [0, 32, 0, 4, 28, caller, gas, 0xaaf10f42] <> * * ** set the ninth stack item to 0, "where" to store via mstore *** * 11 87 dup8 [0, 32, 0, 4, 28, caller, gas, 0xaaf10f42, 0] <> * * ** call mstore, consume 8 and 9 from the stack, place selector in memory ** * 12 52 mstore [0, 32, 0, 4, 0, caller, gas] <0xaaf10f42> * * ** call staticcall, consume items 2 through 7, place address in memory ** * 13 fa staticcall [0, 1 (if successful)] <address> * * ** flip success bit in second stack item to set to 0 ** * 14 15 iszero [0, 0] <address> * * ** push a third 0 to the stack, position of address in memory ** * 15 81 dup2 [0, 0, 0] <address> * * ** place address from position in memory onto third stack item ** * 16 51 mload [0, 0, address] <> * * ** place address to fourth stack item for extcodesize to consume ** * 17 80 dup1 [0, 0, address, address] <> * * ** get extcodesize on fourth stack item for extcodecopy ** * 18 3b extcodesize [0, 0, address, size] <> * * ** dup and swap size for use by return at end of init code ** * 19 80 dup1 [0, 0, address, size, size] <> * 20 93 swap4 [size, 0, address, size, 0] <> * * ** push code position 0 to stack and reorder stack items for extcodecopy ** * 21 80 dup1 [size, 0, address, size, 0, 0] <> * 22 91 swap2 [size, 0, address, 0, 0, size] <> * 23 92 swap3 [size, 0, size, 0, 0, address] <> * * ** call extcodecopy, consume four items, clone runtime code to memory ** * 24 3c extcodecopy [size, 0] <code> * * ** return to deploy final code in memory ** * 25 f3 return [] *deployed!* */ bytes memory metamorphicCode = ( hex"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3" ); // determine the address of the metamorphic contract. address metamorphicContractAddress = _getMetamorphicContractAddress(salt, metamorphicCode); // declare a variable for the address of the implementation contract. address implementationContract; // load implementation init code and length, then deploy via CREATE. /* solhint-disable no-inline-assembly */ assembly { let encoded_data := add(0x20, implInitCode) // load initialization code. let encoded_size := mload(implInitCode) // load init code's length. implementationContract := create( // call CREATE with 3 arguments. 0, // do not forward any endowment. encoded_data, // pass in initialization code. encoded_size // pass in init code's length. ) } /* solhint-enable no-inline-assembly */ //first we deploy the code we want to deploy on a separate address // store the implementation to be retrieved by the metamorphic contract. _implementations[metamorphicContractAddress] = implementationContract; address addr; assembly { let encoded_data := add(0x20, metamorphicCode) // load initialization code. let encoded_size := mload(metamorphicCode) // load init code's length. addr := create2(0, encoded_data, encoded_size, salt) } require( addr == metamorphicContractAddress, "Failed to deploy the new metamorphic contract." ); emit Deployed(addr); } /** * @dev Internal view function for calculating a metamorphic contract address * given a particular salt. */ function _getMetamorphicContractAddress( uint256 salt, bytes memory metamorphicCode ) internal view returns (address) { // determine the address of the metamorphic contract. return address( uint160( // downcast to match the address type. uint256( // convert to uint to truncate upper digits. keccak256( // compute the CREATE2 hash using 4 inputs. abi.encodePacked( // pack all inputs to the hash together. hex"ff", // start with 0xff to distinguish from RLP. address(this), // this contract will be the caller. salt, // pass in the supplied salt value. keccak256( abi.encodePacked( metamorphicCode ) ) // the init code hash. ) ) ) ) ); } //those two functions are getting called by the metamorphic Contract function getImplementation() external view returns (address implementation) { return _implementations[msg.sender]; } } contract Test1 { uint public myUint; function setUint(uint _myUint) public { myUint = _myUint; } function killme() public { selfdestruct(payable(msg.sender)); } } contract Test2 { uint public myUint; function setUint(uint _myUint) public { myUint = 2*_myUint; } function killme() public { selfdestruct(payable(msg.sender)); } }
5860208158601c335a63aaf10f428752fa158151803b80938091923cf3,这串bytecode的原理是staticcall调用getImplementation方法,获取implementation合约地址,再用extcodecopy把implementation合约的runtime bytecode复制到memory,做为当前部署合约的runtime bytecode,以此来动态替换合约的runtime bytecode,而合约地址又不变。
部署Factory合约
用Test1的bytecode和一个固定的salt,调用Factory合约的deploy方法部署Test1合约,Deployed事件的参数就是Test1合约的地址
调用Test1合约的killme方法,销毁合约
用Test2的bytecode和部署Test1时相同的salt,调用Factory合约的deploy方法部署Test2合约,Deployed事件的参数就是Test2合约的地址
这样,就实现了合约地址不变,存储和逻辑更新的升级合约模式,而且没有用代理的delegateCall
审计合约里有没有出现selfdestruct,或者通过delegatecall或者callcode去调用selfdestruct。因为不能销毁也就意味着不能重新部署。如果存在selfdestruct,那要检查合约的部署者是否用create2创建此合约,如果是用create2创建的,就要辨别一下部署器是不是metamorphic合约,如果是那要小心了。
https://ethereum-blockchain-developer.com/110-upgrade-smart-contracts/12-metamorphosis-create2/
No comments yet