Skip to content

Instantly share code, notes, and snippets.

@jtriley-eth
Last active January 12, 2022 15:22
Show Gist options
  • Save jtriley-eth/4d695d65c05be3da194d6ec718aa4b91 to your computer and use it in GitHub Desktop.
Save jtriley-eth/4d695d65c05be3da194d6ec718aa4b91 to your computer and use it in GitHub Desktop.
Simple Yul Contract Implementation
/// @title Simple read-write contract written in Yul
/// @author jtriley.eth
/// @notice This is designed to be functionally identical to ./SolidityContract.sol
/// This is for educational purposes only, do not use in production.
object "YulContract" {
code {
/// @dev Deploys the contract
datacopy(0, dataoffset("runtime"), datasize("runtime"))
return (0, datasize("runtime"))
}
object "runtime" {
code {
// ----- ----- -----
// VALUE CHECKER
// ----- ----- -----
/// @dev reverts if msg.value is greater than 0
if iszero(iszero(callvalue())) {
revert(0, 0)
}
// ----- ----- -----
// SELECTOR SWITCH
// ----- ----- -----
/// @dev switch evaluating the function selector
switch extractSelector()
// bytes4(keccak256("read()"))
case 0x57de26a4 {
returnUint(read())
}
// bytes4(keccak256("set(uint256)"))
case 0x60fe47b1 {
set(extractUint(0))
}
// fallback()
default {
returnUint(42)
}
// ----- ----- -----
// EXTERNAL
// ----- ----- -----
/// @notice Reads myNumber from state slot 0
/// @return _myNumber The number read from storage
function read() -> _myNumber {
_myNumber := sload(myNumberSlot())
}
/// @notice Writes new number to state
/// @dev emits MyNumberSet
/// @param newNumber to write to storage
function set(newNumber) {
let oldNumber := sload(myNumberSlot())
sstore(myNumberSlot(), newNumber)
emitMyNumberSet(caller(), oldNumber, newNumber)
}
// ----- ----- -----
// CALLDATA DECODERS
// ----- ----- -----
/// @dev extracts 4 byte selector from calldata
function extractSelector() -> _selector {
_selector := shr(0x28, calldataload(0))
}
/// @dev extracts uint256 from calldata
/// offset = parameter index, starting at zero
function extractUint(offset) -> _value {
let position := add(4, mul(offset, 0x20))
if lt(calldatasize(), add(position, 0x20)) {
revert(0, 0)
}
_value := calldataload(position)
}
// ----- ----- -----
// CALLDATA ENCODER
// ----- ----- -----
/// @dev Stores parameter in memory, calls return on first
/// 32 bytes of memory
function returnUint(value) {
mstore(0, value)
return(0, 0x20)
}
// ----- ----- -----
// STORAGE LAYOUT
// ----- ----- -----
/// @dev Returns storage slot for myNumber
function myNumberSlot() -> _slot {
_slot := 0
}
// ----- ----- -----
// EVENT EMITTER
// ----- ----- -----
/// @notice emits MyNumberSet
/// @dev Uses log2 with signatureHash and setter (indexed) as topics
/// @param setter Indexed address of number setter
/// @param oldNumber Not indexed old number
/// @param newNumber Not indexed new number
function emitMyNumberSet(setter, oldNumber, newNumber) {
// keccak256("MyNumberSet(address,uint256,uin256)");
let signatureHash := 0x29ad1af1374ae5de3c5b3f510eb08a7d10028e9dfe5aff6570c701c4177c76cb
mstore(0, oldNumber)
mstore(0x20, newNumber)
log2(0, 0x40, signatureHash, setter)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment