I am part of the EthIndia fellowship 3.0 as a Wei fellow. I'll share a blog of my overall experience and more about the fellowship in a wrap-up blog after my eight weeks are up. I'll use this blog as a weekly log of my experience going through the speedrunethereum challenges. It'll also help keep me accountable
In the first four weeks of the fellowship, I'll be going through the speedrunethereum challenges and I will use this blog to posting weekly logs to help keep me accountable.
Table of contents
Week One
I'll share the challenges I faced while working on the challenges and on Ethereum and how I found the experience coming from a Solana background (yes I am that guy 🤙).
challenge zero
The first challenge is a simple walk-through to get you acquainted with how speedrunethereum
works and help you set up your dev-tooling.
Some errors I faced working on the first challenge.
Node version
should be the current LTS i.ev18.14.0
.- The first link I got after
deploying
my frontend to surge.sh did not work. So I redeployed and the second link worked.
After going through the first challenge, I did some self-learning about NFTs, wrote and deployed my contract using hardhat. The indexing tools(plus much more) provided by Alchemy made the process so so easy. Plus, I didn't know this but, apparently there's a whole specification on how to write comments Solidity comments.
Link to NFT I minted (Kobeni Supremacy 🛐).
Comparison to Solana
Working with NFTs in Solana is different to Ethereum. Lets start from the very beginning.
Firstly, on Solana 'everything is an account.' That's it. Everything you need to know about building on Solana in one simple sentence. On Solana smart contracts are referred to as programs and there is the notion of native
and on chain programs
. native programs
are programs-built into the core of the chain. For example the system program
address 11111111111111111111111111111111
enables creation of new accounts and transfer of tokens. on chain programs
on the other hand are deployed to the blockchain by anyone.
So how does this differ with Ethereum you might ask? On Ethereum you have the ERC721 specification you have to follows to mint an NFT. That means, implementing your own contracts with all of the specifications or using a battle-tested library like OpenZeppelin. After implementation you HAVE to deploy your contract which is the biggest difference. In Solana there exists a native token program, address TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
, that you can use to mint NFTs and create tokens. To make it an NFT there's also the metaplex token metadata program to manage metadata.
Secondly, accounts on Solana are stateless. That means your programs/smart-contracts do not store data directly on them. By doing this, some gnarly optimizations such as the parallelized processingof transactions can be enabled, one of the reasons the network is so fast.. This means that if you are creating a program that has to store some data on-chain you'll have to create another account to do this. Unlike in ethereum where a smart contract which has all of its data in one place.
Finishing it off, there is then the concept of associated token account
(I'm not talking about this).
challenge one
The second challenge dealt with building a Decentralized staking app.
Fun new things I learned about.
msg
- global variable that exists globally to provide more information about Ethereum. Read more about it in this blog.- Testing whether an
event
was emitted is possible as described here. - Modifiers are really cool.
- emojis and unicode characters are supported in Solidity with the use of the
unicode
keyword.
console.log(unicode'\t 📤 sending to external contract ...');
Some modifications I made.
- Added test to check whether stake event was fired when staking.
- Used modifiers to hold reusable code like checking whether the deadline was met.
Changed the execute
function, from this,
function execute() public {
uint256 contractBalance = address(this).balance;
//check whether time constraints were met
uint256 remTime = timeLeft();
if (remTime != 0) {
revert('Stake period has not expired yet');
}
//check threshold
require(contractBalance >= THRESHOLD, 'threshold not reached');
//? send to external contract
console.log(unicode'\n\t 📤 sending to external contract...\n');
exampleExternalContract.complete{value: address(this).balance}(); //wasn't able to do validation with this
}
to this
modifier checkEpoch(bool isOpenForWithdrawal) {
uint256 remTime = timeLeft();
if (isOpenForWithdrawal) {
require(remTime >= 0, 'The deadline has already passed');
} else {
require(remTime == 0, 'There is still some time left');
}
_;
}
function execute() public checkEpoch(false) {
uint256 contractBalance = address(this).balance;
//check threshold
require(contractBalance >= THRESHOLD, 'threshold not reached');
//? send to external contract
(bool sent, ) = address(exampleExternalContract)
.call{value: contractBalance}(abi.encodeWithSignature('complete()'));
console.log(unicode'\n\t 📤 sending to external contract...\n');
require(sent, 'unable to send funds to external contract');
}
Started testing for revert error e.g When the threshold isn't reached, calling execute
shouldn't be possible.
await expect(stakerContract.execute()).to.be.revertedWith(
"threshold not reached"
);
await expect(stakerContract.execute()).to.be.reverted;
Some Errors I faced while working on the challenge.
- Spent almost three days trying to figure out why the artefacts for the
Staker
contracts weren't being generated. Turns out the latest update to vs-code had turned it off. - Came across an overflow error trying to convert the balances using to
toNumber()
method for BigNumber, and instead I should have - While trying to deploy the contract using
hard-hat deploy
and NOT the deploy script, I got this error,nonce has already been used
. It occurred when I chose to increase the gas fees to get my contracts deployed quicker. Choosingcontinue waiting
got rid of the error - The tests in do no reflect the 72hrs change suggested at the end of the challenge.
const balAfterStaking = await stakerContract.balances(owner.address);
console.log(
"\n\tbalance after staking",
ethers.utils.formatEther(balAfterStaking)
);
Week two
Week two of the fellowship and I learned about ERC20 Tokens and How not Random block hashes are given the deterministic nature of Ethereum.
challenge two
Neat things I took note of.
-
Testing of emitted events can be done like so.
console.log("\t", " 💸 Buying..."); const buyTokensResult = await vendor.buyTokens({ value: ethers.utils.parseEther("0.001"), }); console.log("\t", " 🏷 buyTokens Result: ", buyTokensResult.hash); console.log("\t", " ⏳ Waiting for confirmation..."); const txResult = await buyTokensResult.wait(); expect(txResult.status).to.equal(1); await expect(buyTokensResult).to.emit(vendor, "BuyTokens");
-
Learnt about the
override
keyword which allows the return type of an inherited function to be changed. To allow the function in the parent contract to be overridden, it needs to have thevirtual
keyword. -
transfer
moves the tokens from your Token contract whiletransferFrom
movesallowed
tokens that have beenapproved
from one address to another.
For further reading check the open zeppelin implementation of the ERC20 contract.
challenge three
This challenge was relatively easy to work out as I just needed to copy the functions from the Dice game onto my exploitive contract.
Check out the verified contract here