In some scenarios, it’s desirable to run the Kurtosis CDK package with a real testnet rather than using a local chain. This document will show how to modify the configuration of the Kurtosis CDK package in order to deploy to Sepolia.
The first thing we should do is make sure that Kurtosis doesn’t bother doing the L1 deployment. We’re going to use yq to do carry this out.
yq -Y --in-place '.deploy_l1 = false' params.yml
This will stop Kurtosis from using spinning up the L1 Ethereum package. Since we’re using Sepolia, there will be no need.
If this isn’t the first time deploying the CDK contracts to Sepolia with a specific address, the salt must be changed.
The timeout for deploying the CDK Node must be increased since we are deploying to Sepolia which will take longer to verify batches than a local L1.
- args["zkevm_aggregator_port"], application_protocol="grpc"
+ args["zkevm_aggregator_port"], application_protocol="grpc", wait = "60m"
By default, the CDK package has some hard coded keys. This is fine for local testing, but if you run on sepolia, you’re likely to get your funds stolen even if it’s inadvertent. First, let’s create a new mnemonic seed phrase to derive our application keys.
cast wallet new-mnemonic
Successfully generated a new mnemonic. Phrase: multiply smoke dash galaxy priority speak sell decade diamond crew charge because Accounts: - Account 0: Address: 0x3C3Ade761F53bdf4F3f40B46D0AbDA286C2aCDcC Private key: 0x8a4758bf2a9c144e4421327f6155f52e31c12ed4684e6c852b91a7d7f7e6c75f
Great! Now we have a new seed phrase to use. Now, we’ll take that seed
phrase and write generate the settings for params.yml
.
seed="multiply smoke dash galaxy priority speak sell decade diamond crew charge because"
polycli wallet inspect --mnemonic "$seed" --addresses 9 | \
jq -r '.Addresses[] | [.ETHAddress, .HexPrivateKey] | @tsv' | \
awk 'BEGIN{split("sequencer,aggregator,claimtxmanager,timelock,admin,loadtest,agglayer,dac,proofsigner",roles,",")} {print "zkevm_l2_" roles[NR] "_address: \"" $1 "\""; print "zkevm_l2_" roles[NR] "_private_key: \"0x" $2 "\"\n"}'
zkevm_l2_sequencer_address: "0x47eAFc647e0EE13F920fdF03F1DB012173452F96" zkevm_l2_sequencer_private_key: "a79cd51949914b6fa6a49f65669c7b9b81ff231a4320b49b3ce3218ed1cdb656" zkevm_l2_aggregator_address: "0x2F24237B5a1635eb708e1945ff299c08f2728399" zkevm_l2_aggregator_private_key: "77057cbeefd49d81edb539850f23dcf62001e7150c0078364820efb13153df52" zkevm_l2_claimtxmanager_address: "0x07c394df412978586fD9A66bf08E3F955cf00db9" zkevm_l2_claimtxmanager_private_key: "7ff66927e40127a400ba36e68a43385d47561c4c4b7fcda1533e39724868ff60" zkevm_l2_timelock_address: "0x46f943D43b60e03f87e1F1959C0D313D5cBBD1C2" zkevm_l2_timelock_private_key: "a5b838802b4367047ee63f8857b122092fbc277932919d50f7e56c3a20093941" zkevm_l2_admin_address: "0x886380dC1C51A2482b58f48617E74e80aECf0742" zkevm_l2_admin_private_key: "768c8a9d126ce9d2c80313736c66da90d4e911ee6cbdf7dc0749ae1be3a72d96" zkevm_l2_loadtest_address: "0x81457240ff5b49CaF176885ED07e3E7BFbE9Fb81" zkevm_l2_loadtest_private_key: "0xd7df6d64c569ffdfe7c56e6b34e7a2bdc7b7583db74512a9ffe26fe07faaa5de" zkevm_l2_agglayer_address: "0x8C1b9edeB66082fFbB75Ac0D2fdcC391AA002997" zkevm_l2_agglayer_private_key: "047b918007c2e6614f9575bd14aaa44dd7d8a1315791ebb5888fb04c3f64440c" zkevm_l2_dac_address: "0x392Ecbb8cF99D402BD2A788D871161CC06974652" zkevm_l2_dac_private_key: "99b3d65616a126798563d9e0dfd692d43540b9cfca4c6bdf3c3df2c263c98a3c" zkevm_l2_proofsigner_address: "0xDE5E0Ca08F7A1CDc9651Df0865dcf47A1337830E" zkevm_l2_proofsigner_private_key: "b5ee3080a448b95f64efb459ff3e9783f28daa1de0c4e5d07a52b48e47d92885"
We’re going to take these generated values and place them into the ../params.yml file. Each one of these settings should already exist and you’re simply replacing the keys that are already in this file.
Now we need to adjust the parameters for L1 specifically for Sepolia. First, let’s create a new mnemonic specifically for running contract deployment.
cast wallet new-mnemonic
Phrase: proof slam law choose steak beach fee cousin rate soup bunker undo Accounts: - Account 0: Address: 0x2eEfbBbbb4344CAa1180593EBCDFC20a60f54bae Private key: 0xff4eacda829d92184a5f385097daf968fd0730e48a7ed4ab321f79e5154d5096
We’ll take that mnemonic and configure l1_preallocated_mnemonic
with
this value.
yq -Y --in-place '.args.l1_preallocated_mnemonic = "proof slam law choose steak beach fee cousin rate soup bunker undo"' params.yml
This account is used for two things. It sends funds to the accounts that need funds on L1 (e.g. sequencer, aggregator, admin). This account also does the contract deployment. Accordingly, we’ll need to send funds to this account.
There are a few other values we need to configure for L1.
yq -Y --in-place '.args.l1_chain_id = 11155111' params.yml
yq -Y --in-place '.args.l1_funding_amount = "5ether"' params.yml
yq -Y --in-place '.args.l1_rpc_url = "https://YOUR-SEPOLIA-RPC.invalid"' params.yml
yq -Y --in-place '.args.l1_ws_url = "wss://YOUR-SEPOLIA-RPC.invalid"' params.yml
All of the configuration should be set. Let’s run things:
kurtosis run --enclave cdk-v1 --args-file params.yml .
While the deployment process is ongoing, you may notice that the Kurtosis deployment is stuck at
Starting the cdk node components
This is because now that the L1 is Sepolia, it takes longer to verify the batches. In the process, when looking at the CDK Node’s container you should see it exited.
docker ps -a | grep ghcr.io/0xpolygon/cdk
We need to monitor the logs of the Erigon Sequencer to make sure the first batch has been verified on L1 before restarting the exited CDK Node container.
kurtosis service logs cdk-v1 cdk-erigon-sequencer-001 -f
While waiting for the batches to verify on Sepolia, you should see the below logs.
[cdk-erigon-sequencer-001] [INFO] [08-29|02:22:20.487] [5/13 Execution] Starting sequencing stage
[cdk-erigon-sequencer-001] [WARN] [08-29|02:22:20.487] [5/13 Execution] ForkId is 0. Waiting for L1 to finalise a block...
You should expect to see the below logs once the batches have been verified on Sepolia.
[cdk-erigon-sequencer-001] [INFO] [08-29|02:11:56.349] [5/13 Execution] Starting sequencing stage
[cdk-erigon-sequencer-001] [INFO] [08-29|02:11:56.363] [5/13 Execution] Starting batch 361...
[cdk-erigon-sequencer-001] [INFO] [08-29|02:11:56.363] [5/13 Execution] Starting block 911 (forkid 9)...
[cdk-erigon-sequencer-001] [INFO] [08-29|02:11:56.363] [5/13 Execution] Waiting for txs from the pool...
[cdk-erigon-sequencer-001] [INFO] [08-29|02:12:02.376] [5/13 Execution] Increment trie hashes started previousRootHeight=910 calculatingRootHeight=911
[cdk-erigon-sequencer-001] [INFO] [08-29|02:12:02.377] [5/13 Execution] Regeneration trie hashes finished. Commiting batch
[cdk-erigon-sequencer-001] [INFO] [08-29|02:12:02.377] [5/13 Execution] Increment ended
[cdk-erigon-sequencer-001] [INFO] [08-29|02:12:02.377] [5/13 Execution] Finish block 911 with 0 transactions...
[cdk-erigon-sequencer-001] [INFO] [08-29|02:12:02.380] [5/13 Execution] Starting block 912 (forkid 9)...
[cdk-erigon-sequencer-001] [INFO] [08-29|02:12:02.380] [5/13 Execution] Waiting for txs from the pool...
Now the CDK Node container can be restarted
docker start <CDK_Node_Container_ID>
You should see the Kurtosis deployment continuing and finishing shortly after.
To make sure that the network is functional, you can query the below commands, and after approximately 20 minutes, you should see the virtualBatchNumber and verifiedBatchNumber incrementing.
cast rpc zkevm_batchNumber
cast rpc zkevm_virtualBatchNumber
cast rpc zkevm_verifiedBatchNumber
Once this completes, you’ll probably want to see your deployed contracts an on-chain activity.
kurtosis service exec cdk-v1 contracts-001 'cat /opt/zkevm/combined.json'
{
"polygonRollupManagerAddress": "0xcaf5779919bD483d03ef4b7a9590a963b987F0B6",
"polygonZkEVMBridgeAddress": "0x2A7Abc10A8c826d687e228880812DaBcc6ebD213",
"polygonZkEVMGlobalExitRootAddress": "0xE288A20FE8F51A75cf835477393618aBbCb9D187",
"polTokenAddress": "0x5C51B0CdD92a012b647fccfc6b36DE34B2C4fb12",
"zkEVMDeployerContract": "0x406C4282c165A25c4dDf95CE74c566017beD8Daf",
"deployerAddress": "0x886380dC1C51A2482b58f48617E74e80aECf0742",
"timelockContractAddress": "0xd8B6F8b113c7F6716459A9818f7d8DDf483a5537",
"deploymentRollupManagerBlockNumber": 6590703,
"upgradeToULxLyBlockNumber": 6590703,
"admin": "0x886380dC1C51A2482b58f48617E74e80aECf0742",
"trustedAggregator": "0x2F24237B5a1635eb708e1945ff299c08f2728399",
"proxyAdminAddress": "0xeBd2DA2a8e18FB11DA5D6F4C59583E9bA6D57121",
"salt": "0xabcde00000000000000000000000000000000000000000000000000000000015",
"polygonDataCommitteeAddress": "0x897d08b9bCd510eeAaDDbCd35Fc20fd52D0C0ECD",
"firstBatchData": {
"transactions": "0xf9010380808401c9c380942a7abc10a8c826d687e228880812dabcc6ebd21380b8e4f811bff7000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a40d5f56745a118d0906a34e69aec8c0db1cb8fa000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005ca1ab1e0000000000000000000000000000000000000000000000000000000005ca1ab1e1bff",
"globalExitRoot": "0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5",
"timestamp": 1724892384,
"sequencer": "0x47eAFc647e0EE13F920fdF03F1DB012173452F96"
},
"genesis": "0xc8200f2886f4aa6622ce018e354041c036f321cfc70af63f7da9ff389032051b",
"createRollupBlockNumber": 6590707,
"rollupAddress": "0x9Ed8D0046723111F7212331D07Ba02c344a9d955",
"verifierAddress": "0x3D574d0c4B4d39eEB2B2cb8B16c983723e957Fb6",
"consensusContract": "PolygonValidiumEtrog",
"polygonZkEVML2BridgeAddress": "0x2A7Abc10A8c826d687e228880812DaBcc6ebD213",
"polygonZkEVMGlobalExitRootL2Address": "0xa40d5f56745a118d0906a34e69aec8c0db1cb8fa",
"bridgeGenBlockNumber": 6590707
}
These are all of the generated details from the network. We can see some of the contracts in Etherscan / Blockscout now:
- Sequenced batches should show up here https://sepolia.etherscan.io/address/0x9Ed8D0046723111F7212331D07Ba02c344a9d955
- Verified batches should show up here https://sepolia.etherscan.io/address/0xcaf5779919bD483d03ef4b7a9590a963b987F0B6
If we followed the steps above, we would have local test environment that using Sepolia as the L1 chain, but the environment itself would not be persistent and there wouldn’t be a straight forward way to use a separate contract deployment.
kurtosis service exec cdk-v1 contracts-001 'cat /opt/zkevm/combined.json' | tail -n +2 | jq '.' > templates/contract-deploy/combined.json
kurtosis service exec cdk-v1 contracts-001 'cat /opt/zkevm/genesis.json' | tail -n +2 | jq '.' > templates/contract-deploy/genesis.json
One piece of general advice: if your kurtosis run
fails for some
reason in the middle of the run, you’ll usually want to do a full
cleanup of the enclave before trying to run again.
kurtosis clean --all
After kurtosis run
you might see an error like this:
ProviderError: only replay-protected (EIP-155) transactions allowed over RPC
The deployment of the zkevm-contracts uses a specific method to
maintain consistent addresses across chains. If you’re seeing this
error, it means your RPC provider is blocking these transactions. If
you’re running your own node you’ll need to make some configuration
changes. E.g. in geth you would set rpc.allow-unprotected-txs
:
--rpc.allow-unprotected-txs (default: false) ($GETH_RPC_ALLOW_UNPROTECTED_TXS) Allow for unprotected (non EIP155 signed) transactions to be submitted via RPC
If you’re not running your own node, you’ll need to use an RPC provider that allows unprotected transactions. In the example for this guide, I’ve used Alchmey.
If you’ve been running into issues or potentially run kurtosis run
multiple times, it’s possible that you run into an issue like this:
Proxy admin was already deployed to: 0xCde3964eB272b209706141029e0782A7576Af5cD Error: Proxy admin was deployed, but the owner is not the deployer, deployer address: 0x17C8c765f260e146673f90B11C7fbBC76AE6E35A, proxyAdmin: 0x38d59F21cdd8B837E1EC6d4b03472b6FEEdF736a at main (/opt/zkevm-contracts/deployment/v2/3_deployContracts.ts:190:15) at processTicksAndRejections (node:internal/process/task_queues:95:5) [2024-06-11 20:28:54] Step 5: Creating rollup > @0xpolygonhermez/[email protected] npx > hardhat run deployment/v2/4_createRollup.ts --network localhost Error: Cannot find module './deploy_output.json' Require stack: - /opt/zkevm-contracts/deployment/v2/4_createRollup.ts at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1145:15) at Function.Module._resolveFilename.sharedData.moduleResolveFilenameHook.installedValue [as _resolveFilename] (/opt/zkevm-contracts/node_modules/@cspotcode/source-map-support/source-map-support.js:811:30) at Function.Module._load (node:internal/modules/cjs/loader:986:27) at Module.require (node:internal/modules/cjs/loader:1233:19) at require (node:internal/modules/helpers:179:18) at Object.<anonymous> (/opt/zkevm-contracts/deployment/v2/4_createRollup.ts:17:22) at Module._compile (node:internal/modules/cjs/loader:1358:14) at Module.m._compile (/opt/zkevm-contracts/node_modules/ts-node/src/index.ts:1618:23) at Module._extensions..js (node:internal/modules/cjs/loader:1416:10) at Object.require.extensions.<computed> [as .ts] (/opt/zkevm-contracts/node_modules/ts-node/src/index.ts:1621:12) { code: 'MODULE_NOT_FOUND', requireStack: [ '/opt/zkevm-contracts/deployment/v2/4_createRollup.ts' ] }
This can happen if the same address is used to deploy the contracts multiple times with the same salt. In order to do a clean up there are a few things you should do.
First, you should make sure you’ve cleaned up your local environment properly:
kurtosis clean --all
Once you’ve cleaned up your environment, you should either rotate your
l1_preallocated_mnemonic
to a new address in order to avoid the
conflict or you can update the salt
value in the
deploy_parameters.json file. Each run of the contract deployment needs
to be a new combination of l1_preallocated_mnemonic
and
salt
. Swapping the salt is probably the easiest way to do another
deployment.
We don’t encounter this issue in local testing because we reset the entire L1 chain between tests / contract deployments.