

This is the first article in a series about deterministic deployments: how can we deploy a contract at the same address in multiple chains? This apparently simple problem has many possible solutions, and new ideas continue to emerge.
In this entry we’ll go over three possible approaches: carefully managing a private key, using Nick’s method, or pre-signing transactions. The rest of the series will cover other approaches like CREATE2 factories, CREATE3, ERC-7955, and more.
Before starting, let’s review some basic concepts that are necessary to understand the rest of the discussion. Feel free to skip this section if you are familiar with the following:
How deployment transactions work
The difference between init and runtime code
How contract addresses are derived
The way in which the CREATE opcode behaves like deployment transactions
We can think of Ethereum transactions as falling into two groups:
Transactions that have a recipient address. This recipient can be an EOA[1] or a contract. If it’s a contract, its code is executed. Let’s call these normal transactions.
Transactions that don’t have a recipient. These are used to create a new contract, so we’ll call them deployment transactions.

When we use a normal transaction to call a contract, the code of this contract is executed and the data field in the transaction is used as the input. In that context, we call the contract’s code its runtime code. But when we send a deployment transaction something quite different happens: it’s the data field that gets executed. The result of this execution is used as the runtime code of the deployed contract.[2] In that context, we say that the data of the deployment transaction is the init code of the contract.

It’s important to emphasize how different these two scenarios are. When we call a contract, the data field usually encodes the function we are calling and the arguments we are passing. That is, it’s just data. But in a deployment transaction, the data is valid EVM code.
We now know that the runtime code of a newly created contract comes from executing its init code. But where is the contract deployed? Its address is determined by the following formula:
address = keccak256(rlp([sender, nonce]))[12:]That is: we RLP-encode the address and nonce of the account sending the deployment transaction, hash the result, and take the last 20 bytes of that hash. But the details don’t matter that much. What’s important for our purposes is that the address of the deployed contract depends only on the address and nonce of the sender.
Transactions are not the only way to create new contracts. Existing contracts can also make deployments. One of the two ways they can do it is with the CREATE opcode (the other way is with the CREATE2 opcode, which we’ll discuss in the next entry of this series).
For this discussion, there isn’t that much difference between a deployment transaction and an execution of the CREATE opcode:
Both execute some init code, but the CREATE opcode takes it from the EVM memory of the current execution, not from the transaction data.
In both cases, the address of the created contract depends on the deployer's address and its nonce. When a contract makes a deployment, its address and nonce are used. The only nuance here is that contracts only increment their nonce when they deploy contracts, and that their starting nonce is 1.
We want to figure out how to deploy a contract at the same address in multiple chains, and we explained that the address of a contract depends only on the address and nonce of the account that sends the deployment transaction. This means that there is a straightforward solution to our problem: deploy the contract using the same private key on every chain, making sure it's the first transaction sent from that address (or the second, or the fifth; it doesn’t matter as long as the nonce is always the same).
This is the simplest answer to the problem of deterministic deployments, but it has a few downsides:
The private key needs to be kept secure in perpetuity. If you lose access to it, then there's no way to deploy at the target address in new chains.
If for any reason the deployment transaction reverts, the nonce gets consumed and the possibility to deploy at the desired address is lost forever for that chain.
The deployment can’t be done by anyone. Only the people with access to the private key can deploy the contract.
Nick’s method is a technique to create valid transactions without using a private key. To understand how it works, we first need to have a proper view of what a transaction is made of.
In our first diagram, a transaction was represented by an object like the following:
{
"from": EOA,
"to": Foo,
"data": "0x1234...",
// ...other fields
}
While this is a useful simplification, it’s not technically correct. A more accurate representation is:
{
"to": Foo,
"data": "0x1234...",
// ...other fields...
"r": "0xabcd...",
"s": "0xfedc...",
"v": 27,
}
The r/s/v fields are the values of the transaction signature. Notice we have removed the from field: since the sender address can be recovered from the signature values, including it in the transaction is redundant and wasteful.
The normal way signature values are obtained is by encoding the transaction and signing it with a private key. But here’s the thing: it’s easy to use arbitrary r/s/v values and get a valid signature for a given unsigned transaction.[3] The from that corresponds to this signed transaction will be effectively random, but if we fund that address and then propagate the signed transaction, it will be mined.
We can use this method to manufacture a signed deployment transaction and propagate it in multiple chains. The address of the deployed contract will be the same in every chain, because the sender is the same and the nonce is always 0 (we have to use a nonce of 0 for the transaction to be valid, because the sender address, being effectively random, is completely unlikely to have a non-zero nonce).
This method, normally called Nick’s method[4], improves upon two of the downsides of the managed private key approach of the previous section:
There’s no need to keep a PK secure because there’s none involved.
Since the manufactured transaction is public, anyone can deploy the contract in a new chain by funding the sender and propagating the transaction.
But the method has a couple of downsides that the previous approach doesn’t have:
The transaction can never be changed, because that would invalidate the signature. This means that all the fields have to be valid for every possible chain. If, for example, the gas limit is too tight and we propagate the transaction in a chain where opcodes are pricier, then the execution will revert with an out-of-gas error. In that case, the possibility of deploying to that address will be lost forever.
Nick’s method only works on chains that don’t enforce replay protection[5], because we are intentionally replaying the same transaction in every target chain.
This approach can be seen as an extension to the managed private key approach rather than its own thing. I’m explaining it in a separate section because it’s easier to understand its trade-offs after having gone over Nick’s method.
We said that the managed private key approach means keeping a PK secure forever, and that only those with access to it can deploy the contract. But, as we saw in the previous section, anyone can propagate a signed transaction as long as it’s valid. This means that a deployment transaction can be signed (without replay protection) and shared publicly so that anyone can use it in other chains.
In a way, this approach combines the best aspects of the previous two methods:
As with Nick’s method, anyone can make the deployment, even if the PK gets lost.
Unlike Nick’s method, we can use the managed PK as a fallback if the pre-signed transaction doesn’t work (for example, in chains that enforce replay protection).
And we can sign and share more than one transaction that deploys the same contract. This is useful to have pre-signed transactions with different gas limits.[6]
In the next article of the series we’ll look into two other approaches to the deterministic deployments problem: CREATE2 factories and CREATE3. You can subscribe to the blog to get notified when the second part is published.
Externally Owned Accounts. These are accounts that don't have code and are controlled by a private key.
More precisely: if the execution doesn’t revert, the return data contains the runtime code that will be used for the deployed contract. For example, the code 0x620102035f526003601df3 returns 0x010203. If you sent a deployment transaction with that init code, a contract with the (nonsense) runtime code 0x010203 would be created. If you want to see how that works in more depth, check this EVM playground.
Basically: if we pick random r and s values, we have an almost 50% chance of getting a valid signature. v can then be derived from r and s.
Named after Nick Johnson, who explained in this 2016 article how the method was used for a rather different problem. I’ve also seen this method called “one-time-account transactions”, which is more self-descriptive but less cool. Actually, I think the only person that uses that term is Nick himself, whom I guess is too modest to call it Nick’s method.
This is the first article in a series about deterministic deployments: how can we deploy a contract at the same address in multiple chains? This apparently simple problem has many possible solutions, and new ideas continue to emerge.
In this entry we’ll go over three possible approaches: carefully managing a private key, using Nick’s method, or pre-signing transactions. The rest of the series will cover other approaches like CREATE2 factories, CREATE3, ERC-7955, and more.
Before starting, let’s review some basic concepts that are necessary to understand the rest of the discussion. Feel free to skip this section if you are familiar with the following:
How deployment transactions work
The difference between init and runtime code
How contract addresses are derived
The way in which the CREATE opcode behaves like deployment transactions
We can think of Ethereum transactions as falling into two groups:
Transactions that have a recipient address. This recipient can be an EOA[1] or a contract. If it’s a contract, its code is executed. Let’s call these normal transactions.
Transactions that don’t have a recipient. These are used to create a new contract, so we’ll call them deployment transactions.

When we use a normal transaction to call a contract, the code of this contract is executed and the data field in the transaction is used as the input. In that context, we call the contract’s code its runtime code. But when we send a deployment transaction something quite different happens: it’s the data field that gets executed. The result of this execution is used as the runtime code of the deployed contract.[2] In that context, we say that the data of the deployment transaction is the init code of the contract.

It’s important to emphasize how different these two scenarios are. When we call a contract, the data field usually encodes the function we are calling and the arguments we are passing. That is, it’s just data. But in a deployment transaction, the data is valid EVM code.
We now know that the runtime code of a newly created contract comes from executing its init code. But where is the contract deployed? Its address is determined by the following formula:
address = keccak256(rlp([sender, nonce]))[12:]That is: we RLP-encode the address and nonce of the account sending the deployment transaction, hash the result, and take the last 20 bytes of that hash. But the details don’t matter that much. What’s important for our purposes is that the address of the deployed contract depends only on the address and nonce of the sender.
Transactions are not the only way to create new contracts. Existing contracts can also make deployments. One of the two ways they can do it is with the CREATE opcode (the other way is with the CREATE2 opcode, which we’ll discuss in the next entry of this series).
For this discussion, there isn’t that much difference between a deployment transaction and an execution of the CREATE opcode:
Both execute some init code, but the CREATE opcode takes it from the EVM memory of the current execution, not from the transaction data.
In both cases, the address of the created contract depends on the deployer's address and its nonce. When a contract makes a deployment, its address and nonce are used. The only nuance here is that contracts only increment their nonce when they deploy contracts, and that their starting nonce is 1.
We want to figure out how to deploy a contract at the same address in multiple chains, and we explained that the address of a contract depends only on the address and nonce of the account that sends the deployment transaction. This means that there is a straightforward solution to our problem: deploy the contract using the same private key on every chain, making sure it's the first transaction sent from that address (or the second, or the fifth; it doesn’t matter as long as the nonce is always the same).
This is the simplest answer to the problem of deterministic deployments, but it has a few downsides:
The private key needs to be kept secure in perpetuity. If you lose access to it, then there's no way to deploy at the target address in new chains.
If for any reason the deployment transaction reverts, the nonce gets consumed and the possibility to deploy at the desired address is lost forever for that chain.
The deployment can’t be done by anyone. Only the people with access to the private key can deploy the contract.
Nick’s method is a technique to create valid transactions without using a private key. To understand how it works, we first need to have a proper view of what a transaction is made of.
In our first diagram, a transaction was represented by an object like the following:
{
"from": EOA,
"to": Foo,
"data": "0x1234...",
// ...other fields
}
While this is a useful simplification, it’s not technically correct. A more accurate representation is:
{
"to": Foo,
"data": "0x1234...",
// ...other fields...
"r": "0xabcd...",
"s": "0xfedc...",
"v": 27,
}
The r/s/v fields are the values of the transaction signature. Notice we have removed the from field: since the sender address can be recovered from the signature values, including it in the transaction is redundant and wasteful.
The normal way signature values are obtained is by encoding the transaction and signing it with a private key. But here’s the thing: it’s easy to use arbitrary r/s/v values and get a valid signature for a given unsigned transaction.[3] The from that corresponds to this signed transaction will be effectively random, but if we fund that address and then propagate the signed transaction, it will be mined.
We can use this method to manufacture a signed deployment transaction and propagate it in multiple chains. The address of the deployed contract will be the same in every chain, because the sender is the same and the nonce is always 0 (we have to use a nonce of 0 for the transaction to be valid, because the sender address, being effectively random, is completely unlikely to have a non-zero nonce).
This method, normally called Nick’s method[4], improves upon two of the downsides of the managed private key approach of the previous section:
There’s no need to keep a PK secure because there’s none involved.
Since the manufactured transaction is public, anyone can deploy the contract in a new chain by funding the sender and propagating the transaction.
But the method has a couple of downsides that the previous approach doesn’t have:
The transaction can never be changed, because that would invalidate the signature. This means that all the fields have to be valid for every possible chain. If, for example, the gas limit is too tight and we propagate the transaction in a chain where opcodes are pricier, then the execution will revert with an out-of-gas error. In that case, the possibility of deploying to that address will be lost forever.
Nick’s method only works on chains that don’t enforce replay protection[5], because we are intentionally replaying the same transaction in every target chain.
This approach can be seen as an extension to the managed private key approach rather than its own thing. I’m explaining it in a separate section because it’s easier to understand its trade-offs after having gone over Nick’s method.
We said that the managed private key approach means keeping a PK secure forever, and that only those with access to it can deploy the contract. But, as we saw in the previous section, anyone can propagate a signed transaction as long as it’s valid. This means that a deployment transaction can be signed (without replay protection) and shared publicly so that anyone can use it in other chains.
In a way, this approach combines the best aspects of the previous two methods:
As with Nick’s method, anyone can make the deployment, even if the PK gets lost.
Unlike Nick’s method, we can use the managed PK as a fallback if the pre-signed transaction doesn’t work (for example, in chains that enforce replay protection).
And we can sign and share more than one transaction that deploys the same contract. This is useful to have pre-signed transactions with different gas limits.[6]
In the next article of the series we’ll look into two other approaches to the deterministic deployments problem: CREATE2 factories and CREATE3. You can subscribe to the blog to get notified when the second part is published.
Externally Owned Accounts. These are accounts that don't have code and are controlled by a private key.
More precisely: if the execution doesn’t revert, the return data contains the runtime code that will be used for the deployed contract. For example, the code 0x620102035f526003601df3 returns 0x010203. If you sent a deployment transaction with that init code, a contract with the (nonsense) runtime code 0x010203 would be created. If you want to see how that works in more depth, check this EVM playground.
Basically: if we pick random r and s values, we have an almost 50% chance of getting a valid signature. v can then be derived from r and s.
Named after Nick Johnson, who explained in this 2016 article how the method was used for a rather different problem. I’ve also seen this method called “one-time-account transactions”, which is more self-descriptive but less cool. Actually, I think the only person that uses that term is Nick himself, whom I guess is too modest to call it Nick’s method.
Transactions with replay protection include the chain id as part of the signed transaction. If the chain id is different from the current chain, the transaction is considered invalid.
Notice that this has a griefing risk. If the pre-signed transaction with the lowest gas limit doesn’t work on a given chain, then someone could fund the sender and propagate it so that it reverts with an out-of-gas error, preventing the contract from being deployed to the target address.
Transactions with replay protection include the chain id as part of the signed transaction. If the chain id is different from the current chain, the transaction is considered invalid.
Notice that this has a griefing risk. If the pre-signed transaction with the lowest gas limit doesn’t work on a given chain, then someone could fund the sender and propagate it so that it reverts with an out-of-gas error, preventing the contract from being deployed to the target address.
<100 subscribers
<100 subscribers
Share Dialog
Share Dialog
Franco Victorio
Franco Victorio
No comments yet