0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

create2によるスマートコントラクトのデプロイ (foundry)

Posted at

これに書いてあるcreate2によるデプロイをやってみました。
Foundryを使用します。

  • create2はコントラクトからしか呼び出せない
  • create2を呼び出すファクトリーコントラクトがまず必要
  • ファクトリーコントラクトを呼び出して、実際のコントラクトをデプロイする
異なるチェーンで同じコントラクトアドレスを使うためには、、、
  • 通常のデプロイの場合にはnonceを同じにしたいといけない。
  • create2の場合はファクトリーコントラクトのアドレスが異なるチェーンで同じでないといけない。
とりあえずやってみます。

ファクトリーコントラクトFactory
https://github.com/miguelmota/solidity-create2-example
create2のところはassemblyとなっています。solidityでは書けないようです。

pragma solidity ^0.8.13;

contract Factory {
  event Deployed(address addr, uint256 salt);

  function deploy(bytes memory code, uint256 salt) public {
    address addr;
    assembly {
      addr := create2(0, add(code, 0x20), mload(code), salt)
      if iszero(extcodesize(addr)) {
        revert(0, 0)
      }
    }

    emit Deployed(addr, salt);
  }
}

anvilでテストネット起動

anvil 

$RPCにはhttp://127.0.0.1:8545, $SEC1にはプライベートキーを入れてFactory.solをデプロイします。ここであとで使用するためコントラクトアドレスをメモしておきます。

forge create --rpc-url $RPC  --private-key $SEC1  src/Factory.sol:Factory

実際にデプロイするコントラクトはこちら。

src/NFT.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;

import {ERC721} from "solmate/tokens/ERC721.sol";
import {Strings} from "openzeppelin-contracts/utils/Strings.sol";
import {Ownable} from "openzeppelin-contracts/access/Ownable.sol";

error MintPriceNotPaid();
error MaxSupply();
error NonExistentTokenURI();
error WithdrawTransfer();

event NewMinted(uint256 tokenId);

contract NFT is ERC721, Ownable {
	using Strings for uint256;
	string public baseURI;
	uint256 public currentTokenId;
	uint256 public constant TOTAL_SUPPLY = 10_000; 
	uint256 public constant MINT_PRICE = 0.08 ether; 

	constructor(
		string memory _name,
		string memory _symbol,
		string memory _baseURI
	) ERC721(_name, _symbol) Ownable(msg.sender) {
		baseURI = _baseURI;
	}

	function mintTo(address recipient) public payable returns (uint256) {
		if(msg.value !=MINT_PRICE) {
			revert MintPriceNotPaid();
		}
		uint256 newTokenId = ++currentTokenId;
		if (newTokenId > TOTAL_SUPPLY) {
			revert MaxSupply();
		}
		_safeMint(recipient, newTokenId);
        emit  NewMinted(newTokenId);  
		return newTokenId;
	}

	function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
		if(ownerOf(tokenId) == address(0)) {
			revert NonExistentTokenURI();
		}
		return bytes(baseURI).length > 0 
		? string(abi.encodePacked(baseURI, tokenId.toString())) 
		:"";
	}

	function withdrawPayments(address payable payee) external onlyOwner {
		uint256 balance = address(this).balance;
		(bool transferTx, ) = payee.call{value: balance}("");
		if(!transferTx) {
			revert WithdrawTransfer();
		}
	}
}

Foundryではデプロイのスクリプトをsolidityかけるので、以下のとおり

  • DETERMINISTIC_CREATE2_FACTORYにファクトリコントラクトのfactoryのアドレスをいれます。
  • 実際にデプロイするコントラクトのコンストラクターに引数がある場合はencodePackedでコントラクトのバイナリと共に実際に使用する引数をいれる必要がある。
  • このデプロイスクリプトが汎用的に記述できるかどうかは要検証。(引数の数が可変、引数の型の可変など)
  • 事前にアドレスを計算してログに出すようにしてある。デプロイする前に実施することでアドレスを事前に計算できる。
import {Script} from "forge-std/Script.sol";
import {NFT} from "src/NFT.sol";
import {Factory} from "src/Factory.sol";
import "forge-std/console.sol";

contract DeterministicDeploy is Script {

    // 先ほどの`Factory`コントラクトのアドレスを入れます。
    address internal constant DETERMINISTIC_CREATE2_FACTORY = 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9;
    NFT nft;
    function run() public {
        uint salt;
        salt=3;
        vm.broadcast();
        //ここからネットワーク
        Factory fact = Factory(DETERMINISTIC_CREATE2_FACTORY); 
        bytes memory creationBytecode = type(NFT).creationCode;
        
        // デプロイするコントラクトのconstructorに引数が必要な場合も加える。
        // 引数がない場合はinitCode= type(NFT).creationCode;でよい
        
        bytes memory initCode = abi.encodePacked(creationBytecode, abi.encode("NFT contract","NFT","http://mynft.org"));
        
        // ここはアドレス計算してログに出してるだけ
        // この部分を使う事で事前にアドレスを計算できる
        bytes memory bytecode = abi.encodePacked(bytes1(0xff),address(DETERMINISTIC_CREATE2_FACTORY),salt,keccak256(initCode));
        address newaddr = address(uint160(uint(keccak256(bytecode))));
        console.logAddress(newaddr);
        
        // デプロイ
        fact.deploy(initCode, salt);
    }
}

スクリプト実行します ($SEC1はプライベートキーをいれてます。)

 forge script script/NFT.s.sol:DeterministicDeploy  --rpc-url $RPC --broadcast --private-key  $SEC1

実行すると以下のログがでます。
== Logs ==の下がデプロイされるコントラクトのアドレスになります。(事前に計算したアドレス)

root@DESKTOP-N2O3OSL:~/nft# forge script script/NFT.s.sol:DeterministicDeploy  --rpc-url $RPC --broadcast --private-key  $SEC1
[⠰] Compiling...
No files changed, compilation skipped
Script ran successfully.

== Logs ==
  0xeb1d23b5cf6da2cefD99bF4d6AC0430b7A0BDBeF

EIP-3855 is not supported in one or more of the RPCs used.
Unsupported Chain IDs: 31337.
Contracts deployed with a Solidity version equal or higher than 0.8.20 might not work properly.
For more information, please see https://eips.ethereum.org/EIPS/eip-3855

## Setting up 1 EVM.

==========================

Chain 31337

Estimated gas price: 3.102308024 gwei

Estimated total gas used for script: 1812436

Estimated amount required: 0.005622734745786464 ETH

==========================

###
Finding wallets for all the necessary addresses...
##
Sending transactions [0 - 0].
⠁ [00:00:00] [##############################################################################################################################################################] 1/1 txes (0.0s)
Transactions saved to: /root/nft/broadcast/NFT.s.sol/31337/run-latest.json

Sensitive values saved to: /root/nft/cache/NFT.s.sol/31337/run-latest.json

##
Waiting for receipts.
⠉ [00:00:00] [##########################################################################################################################################################] 1/1 receipts (0.0s)
##### anvil-hardhat
✅  [Success]Hash: 0x8ddf14a9cce21184d4af5a0bf3cc1a870928f73b2b1c8ca971708d5ad95fe377
Block: 25
Paid: 0.003996530526966366 ETH (1312582 gas * 3.044785413 gwei)


Transactions saved to: /root/nft/broadcast/NFT.s.sol/31337/run-latest.json

Sensitive values saved to: /root/nft/cache/NFT.s.sol/31337/run-latest.json



==========================

ONCHAIN EXECUTION COMPLETE & SUCCESSFUL.
Total Paid: 0.003996530526966366 ETH (1312582 gas * avg 3.044785413 gwei)

Transactions saved to: /root/nft/broadcast/NFT.s.sol/31337/run-latest.json

Sensitive values saved to: /root/nft/cache/NFT.s.sol/31337/run-latest.json

また Factory.solでは event Deployed(address addr, uint256 salt);を送出してるので以下でもアドレスを確認できます。dataのの前半部分がコントラクトアドレスです。

root@DESKTOP-N2O3OSL:~/nft# cast logs
- address: 0xeb1d23b5cf6da2cefD99bF4d6AC0430b7A0BDBeF
  blockHash: 0x305fe3c367d386d35f9518fc74447f0546fea3b5189bf458177ea6302acb3d79
  blockNumber: 25
  data: 0x
  logIndex: 0
  removed: false
  topics: [
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0
        0x0000000000000000000000000000000000000000000000000000000000000000
        0x000000000000000000000000cf7ed3acca5a467e9e704c703e8d87f634fb0fc9
  ]
  transactionHash: 0x8ddf14a9cce21184d4af5a0bf3cc1a870928f73b2b1c8ca971708d5ad95fe377
  transactionIndex: 0
- address: 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
  blockHash: 0x305fe3c367d386d35f9518fc74447f0546fea3b5189bf458177ea6302acb3d79
  blockNumber: 25
  data: 0x000000000000000000000000eb1d23b5cf6da2cefd99bf4d6ac0430b7a0bdbef0000000000000000000000000000000000000000000000000000000000000003
  logIndex: 1
  removed: false
  topics: [
        0xb03c53b28e78a88e31607a27e1fa48234dce28d5d9d9ec7b295aeb02e674a1e1
  ]
  transactionHash: 0x8ddf14a9cce21184d4af5a0bf3cc1a870928f73b2b1c8ca971708d5ad95fe377
  transactionIndex: 0

実際のデプロイしたNFTコントラクトでミントできました。

root@DESKTOP-N2O3OSL:~/nft# cast send $NFT_CON "mintTo(address)" $ADR2 --private-key $SEC1 --value "0.08 ether"

blockHash               0x785a3714d77f343dcfbc4469f344065fb2fb29d2984b948c86d90517ee1b1c59
blockNumber             26
contractAddress
cumulativeGasUsed       94376
effectiveGasPrice       3039677108
gasUsed                 94376
logs                    [{"address":"0xeb1d23b5cf6da2cefd99bf4d6ac0430b7a0bdbef","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000000000000000000000000000000000000000000000","0x00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x","blockHash":"0x785a3714d77f343dcfbc4469f344065fb2fb29d2984b948c86d90517ee1b1c59","blockNumber":"0x1a","transactionHash":"0xf808721c61db2b2a9301e3dc6c5adff354ba6007f3f211428a7a5eb48e208d07","transactionIndex":"0x0","logIndex":"0x0","transactionLogIndex":"0x0","removed":false},{"address":"0xeb1d23b5cf6da2cefd99bf4d6ac0430b7a0bdbef","topics":["0x0f7088101dc997e74a352357ee3a53389b5957179a421a220c5258546478fe47"],"data":"0x0000000000000000000000000000000000000000000000000000000000000001","blockHash":"0x785a3714d77f343dcfbc4469f344065fb2fb29d2984b948c86d90517ee1b1c59","blockNumber":"0x1a","transactionHash":"0xf808721c61db2b2a9301e3dc6c5adff354ba6007f3f211428a7a5eb48e208d07","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":"0x1","removed":false}]
logsBloom               0x00000000000000000002000000000020000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000040000000000000000000000000008000000000000000000040000000000800000000000000800020000000000000000000800000000000000000000000010000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082000000000000000000000000000000000000000000000000000060000000000000000000000000000000000001000000000000000800000000000000
root
status                  1
transactionHash         0xf808721c61db2b2a9301e3dc6c5adff354ba6007f3f211428a7a5eb48e208d07
transactionIndex        0
type                    2
root@DESKTOP-N2O3OSL:~/nft#

anvilのDefault CREATE2 Deployer

Foundryに同梱してあるテストネットツールanvilをみてみたら、

Create2を使用するDefault CREATE2 Deployer ってコントラクトが0x4e59b44847b379578588920ca78fbf26c0b4956c既に入っているのだけど、使い方がわからなかった。

そのコントラクトはこれらしいんだけど、わからなかった。
https://github.com/Arachnid/deterministic-deployment-proxy/tree/master
わかったらアップデートします。

他参考サイト

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?