What is the salt keyword in Solidity?

In cryptography, a salt is a piece of randomness introduced into a transaction to enhance security. Specifically, a salt is employed in Ethereum and Solidity when generating unique contract addresses through a hashing algorithm.

Cryptography generates a secret code for each transaction by applying a hashing algorithm. The transaction serves as an input to the hashing function, which, in turn, contributes to the derivation of a unique value—often referred to as a hash or digest. This unique value determines the contract address in contract deployment on a blockchain like Ethereum.

Let’s explore the intricacies of using the salt keyword and its application.

Application of salt keyword

The use of salt in cryptography serves the following two primary purposes:

  • Security enhancement: Adding the salt keyword in the transaction helps generate a secret code that is hard to determine/decipher, which makes it more challenging for attackers to guess or crack the secret code through cryptographic attacks.

  • Predictable contract address: Every time a contract gets deployed on the Ethereum blockchain, it is given an address. Usually, the algorithm calculates this address by combining the contract’s creator’s address with the creator’s nonce (the number of transactions sent from that address). The problem with this new contract’s address may be unpredictable, making some interactions with the contract challenging. Solidity addresses this case by including a deterministic value in the address computation through the salt keyword, a 32-byte keyword, hence generating deterministic addresses.

Address calculation with salt

Generating this new contract’s address uses a different technique if the option salt (a 32-byte value) is specified. It will calculate the address using the formed contract’s creation bytecode, the salt value provided, the address of the contract that is constructing it, and the constructor’s argument.
Using the unique salt value while deploying a contract on the blockchain, we can easily predict the resultant address. This has applications in establishing contracts that interact in specified ways with one another or generating a group of contracts with predictable addresses for testing purposes.
Consequently, the counter or “nonce” is not utilized to provide more flexibility in contract creation. This design choice enables the derivation of the new contract’s address before its creation. Additionally, this derived address remains reliable even if the creating contract generates other contracts in the interim.

Example

Here’s an example of how to use the salt keyword in Solidity. In this example, we’ll create a contract, CContract, from an existing contract, DContract. While creating CContract, we’ll compute its predicted address, compare it with its actual address, and see if they both match.

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract DContract {
uint public value;
constructor(uint _value) {
value = _value;
}
}
contract CContract {
function createDerivedContract(bytes32 salt, uint arg) public {
// Predicting the address
address predictedAddress = address(uint160(uint(keccak256(abi.encodePacked(
bytes1(0xff),
address(this),
salt,
keccak256(abi.encodePacked(
type(DContract).creationCode,
abi.encode(arg)
))
)))));
// Creating a new instance of DContract with the specified salt and argument
DContract d = new DContract{salt: salt}(arg);
// Verifying the deployed address matches the predicted address
require(address(d) == predictedAddress, "Address Mismatch");
}
}

Explanation

  • Line 2: Specify the version of the Solidity compiler that should be used to compile the smart contract code.

    • >=0.7.0 indicates that the code is expected to be compatible with Solidity version 0.7.0 or any later version.

    • <0.9.0 indicates that the code is not guaranteed to be compatible with Solidity version 0.9.0 or any later version.

  • Lines 4–10: The DContract contract is defined, which represents a contract with a single state variable value of type uint. This variable will store the value passed to the constructor during contract deployment. It will be publicly accessible, meaning other contracts or users can read its value.

  • Lines 12–31: The CContract contract is defined. It contains the createDerivedContract function, which allows creating a new instance of the DContract contract with a provided salt and argument. This function is marked as public, meaning it can be invoked from outside the contract.

    • Line 15: The predictedAddress variable is declared to store the predicted address of the new instance of DContract. It represents the expected address of the contract instance created with the given salt and argument.

    • Lines 15–23: The predictedAddress is calculated using a combination of hashing and encoding operations. It includes the salt, which helps generate a unique address, the current contract’s address (address(this)), the Keccak256 hashA cryptographic function that takes in any amount of inputs and returns a unique 32 byte hash. of the creation code of DContract (which represents the contract’s bytecode), and the encoded arg value (which will be passed to the constructor of DContract). The complicated expression of predictedAddress just tells us how the address can be precomputed. It is just there for illustration. We actually only need new DContract{salt: salt}(arg).

    • Line 26: The new keyword is used to create a new instance of the DContract contract named d. The salt is passed as an argument to the constructor, along with the arg value. This deployment step triggers the execution of the constructor function within DContract and creates a new instance of DContract on the blockchain.

    • Line 29: The require statement is used to ensure that the address of the deployed d contract matches the predicted address. It verifies that the actual address of the deployed contract is the same as the expected predictedAddress. If the addresses do not match, the transaction will revert, indicating a mismatch between the predicted and actual addresses. This provides a way to verify the integrity of the contract creation process.

Note: Solidity compiler returns a binary output. For better interactivity, refer to the Remix IDE and paste the code above in a .sol file. After compilation and deploying the code on the blockchain, we’ll be able to see the successful deployment message similar to the one shown below:

Successful deployment message for CContract on Remix IDE
Successful deployment message for CContract on Remix IDE

Interacting with the deployed contract

Follow the steps below to test the expected behavior:

  • Deployment: Ensure that the contract has been successfully deployed on the Ethereum blockchain. Note the contract address provided during deployment.

  • Select the deployed contract: In Remix, navigate to the “Deployed Contracts” section.

  • Interact with createDerivedContract:

    • Select your deployed contract from the list.

    • Locate the createDerivedContract function, which takes a bytes32 salt and a uint256 arg as parameters.

  • Enter values: Enter desired values for bytes32 salt and uint256 arg in the input fields. For example:

    • bytes32 salt: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef

    • uint256 arg: 42

Deployed contract section of Remix IDE
Deployed contract section of Remix IDE
  • Execute the transaction: Click the “Transact” button to execute the transaction.

  • Check output: Monitor the Remix console for transaction details.

If the require statement in the contract is satisfied (predicted and actual addresses match), the transaction will be successful.

Successful transaction message with the deployed contract
Successful transaction message with the deployed contract

In case of errors or an address mismatch (try this by removing the salt keyword from the predictedAddress definition, redeploy, and interact with this new contract), we’ll see an “Address Mismatch” error message.

Failed transaction message with the deployed contract
Failed transaction message with the deployed contract

By following the steps above, we can test the createDerivedContract function with different salt and arg values to ensure the proper deployment and address prediction functionality of the contract.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved