This week, I mainly focused on integrating the smart contracts I deployed last week into our website. I didn't learn much new, so I'll share some useful knowledge I gained in the past that I haven't shared yet.
Solidity Immutable Variable
While I was skimming through Base's tutorials on gas optimization, I noticed the usage of an immutable variable. Since I was curious, I did a bit of research on what it does.
What is it?
As the name suggests, an immutable variable cannot be changed after the contract is deployed. You can assign a value while declaring it or within the constructor, but it cannot be accessed until the contract is deployed. In other words, it is not accessible within the constructor; it can only be assigned a value. Here's an example of how it's used:
contract Example {
address immutable addressOne;
address immutable addressTwo = address(0);
address immutable addressThree;
address immutable addressFour;
constructor(address _anotherOwner) {
addressOne = msg.sender;
addressThree = _anotherOwner;
// The line below will throw an error
// addressFour = addressOne;
}
}
Vs. Constant Variable
Here's a brief table that explains the differences:
Category | Constant | Immutable |
Possible Values | Any values other than external ones (ex. msg.sender, block.timestamp, etc.) | Any values other than dynamic ones (ex. string, bytes, etc.) |
Gas Efficiency | Most efficient | Less efficient than a constant variable |
Value Assignment | Must be assigned at compilation | Can be assigned at runtime (i.e. within the constructor) |
Stuck Transaction
When using Hardhat to interact with the Polygon PoS network, I frequently faced issues with transactions getting stuck due to low gas prices. This occurred because Polygon Labs set a minimum gas price of 30 gwei to mitigate spam transactions. To prevent transactions from getting stuck, you can use one of the following methods:
Set an explicit gas price for all transactions: If you're using Hardhat, you can set it in
hardhat.config.ts
:const config: HardhatUserConfig = { // ... matic: { // ... gasPrice: Number(ethers.parseUnits("30", "gwei")) } }
Set an explicit gas price for a specific transaction:
contract Example { address immutable owner; constructor() { owner = msg.sender; } function setOwner(address newOwner) external { owner = newOwner; } }
async function main() { const example = await ethers.deployContract("Example"); await example.waitForDeployment(); const txOptions = {gasPrice: ethers.parseUnits("30", "gwei")}; const tx = await example.setOwner(ethers.ZeroAddress, txOptions); await tx.wait(); // ... } main();
If you want to resolve a stuck transaction, you need to provide the nonce of the stuck transaction as well.
async function main() {
const example = await ethers.deployContract("Example");
await example.waitForDeployment();
const signer = new ethers.Wallet(
`<account-private-key>`,
ethers.provider,
);
const nonce = await ethers.provider.getTransactionCount(
signer.address,
"latest",
);
const txOptions = {gasPrice: ethers.parseUnits("30", "gwei"), nonce};
const tx = await example.setOwner(ethers.ZeroAddress, txOptions);
await tx.wait();
// ...
}
main();
That wraps up this week's TWIL. Happy hacking! ☕️