According to Foundry, “Solidity scripting is a declarative way to deploy contracts using solidity, instead of the more limiting and less user-friendly forge create”.
Foundry is a fast modular framework for smart contract development and it is the only framework that provides a way to write scripts directly in solidity, frameworks like hardhat make use of Javascript or Typescript.
Foundry provides a way to run Solidity scripts on its EVM backend. In this article, we will explore this feature and deploy a Solidity script while we are at it.
How does Solidity Scripting work?
In Foundry, scripting can be divided into four phases;
Phase 1: Local Simulation
Context: The script is executed within a local Ethereum Virtual Machine (EVM).
RPC/Fork URL: If provided, the EVM will simulate the script's execution in the context of a remote blockchain network (e.g., Ethereum mainnet, a testnet).
Transaction Collection: Any external calls (not static, not internal) made using vm.broadcast or vm.startBroadcast are added to a list of pending transactions.
Phase 2: Onchain Simulation
Condition: This only occurs if an RPC/Fork URL is provided in Phase 1.
Sequential Execution: The collected transactions from Phase 1 are executed sequentially on the remote blockchain. This allows for simulating the script's behavior in a more realistic environment.
Phase 3: Broadcasting
Condition: Requires the --broadcast flag and successful completion of previous phases.
Transaction Submission: The collected transactions are sent to the remote blockchain network for execution.
Phase 4: Verification
Condition: Requires the --verify flag, an API key, and successful completion of previous phases.
Contract Verification: The deployed contract is verified on a blockchain explorer (e.g., Etherscan) using the provided API key.
In the next section, we’d be deploying a solidity contract to see this in practice.
Solidity Scripting in Practice
Writing the smart contract
The contract below is used to store the favourite number of different individuals;
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract SimpleStorage {
uint256 myFavoriteNumber;
struct Person {
uint256 favoriteNumber;
string name;
}
Person[] public listOfPeople;
mapping(string => uint256) public nameToFavoriteNumber;
function store(uint256 _favoriteNumber) public {
myFavoriteNumber = _favoriteNumber;
}
function retrieve() public view returns (uint256) {
return myFavoriteNumber;
}
function addPerson(string memory _name, uint256 _favoriteNumber) public {
listOfPeople.push(Person(_favoriteNumber, _name));
nameToFavoriteNumber[_name] = _favoriteNumber;
}
}
Run forge build to compile the code;
Your result should look like this.
Writing the script
Create a script with s.sol ending the name of the file in the script folder
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {Script} from "forge-std/Script.sol";
import {SimpleStorage} from "../src/SimpleStorage.sol";
contract DeploySimpleStorage is Script {
function run() external returns (SimpleStorage){
vm.startBroadcast();
SimpleStorage simpleStorage = new SimpleStorage();
vm.stopBroadcast();
return simpleStorage;
}
}
This is a simple script to deploy the simple storage contract let’s take a look at how it works;
1. Import
import {Script} from "forge-std/Script.sol";
import {SimpleStorage} from "../src/SimpleStorage.sol";
import {Script} from "forge-std/Script.sol"; Imports the Script base contract from Foundry's standard library (forge-std). The Script contract provides utilities and functions needed to write deployment scripts.
import {SimpleStorage} from "../src/SimpleStorage.sol"; Imports the SimpleStorage contract from a file located in the src directory. This is the contract that will be deployed by the script.
2. Deployment definition
contract DeploySimpleStorage is Script {
- DeploySimpleStorage: This is a new contract that extends the Script base contract. It contains the logic needed to deploy the SimpleStorage contract.
3. Run Function
function run() external returns (SimpleStorage){
vm.startBroadcast();
SimpleStorage simpleStorage = new SimpleStorage();
vm.stopBroadcast();
return simpleStorage;
}
- Function Signature: The run() function is the entry point of the script. It is marked as external, meaning it can be called from outside the contract, and it returns an instance of the SimpleStorage contract.
4. Starting the Broadcast:
vm.startBroadcast();
This line uses Foundry's special cheat code vm.startBroadcast() to start broadcasting transactions. Everything that follows this line will be sent as a transaction to the blockchain, meaning it will be executed on the Ethereum network (or a local test environment). Essentially, it tells Foundry to start recording the operations that will be sent as transactions.
- Deploying the SimpleStorage Contract:
SimpleStorage simpleStorage = new SimpleStorage();
This line deploys a new instance of the SimpleStorage contract. The new keyword is used to create a new contract instance. The deployment is recorded as a transaction because it is wrapped within vm.startBroadcast() and vm.stopBroadcast().
- The deployed contract instance is stored in the variable simpleStorage.
5. Stopping the Broadcast:
vm.stopBroadcast();
This line stops broadcasting transactions. It indicates the end of the series of operations that should be sent as transactions.
- Returning the Deployed Contract:
return simpleStorage;
The function returns the instance of the deployed SimpleStorage contract. This can be useful for interacting with the contract immediately after deployment.
Deploying our script
Now that we understand how the script works, let’s deploy it.
Run forge script script/DeploySimpleStorage.s.sol
If you did it correctly, this is what you should see;
To simulate onchain transactions, you would have to pass an RPC URL and also provide your private key.
In the next article, I will be showing you how to safely simulate onchain transactions without exposing your private details.
To learn more about solidity scripting with foundry check out this piece.
Top comments (0)