Last active
January 12, 2022 15:22
-
-
Save jtriley-eth/4d695d65c05be3da194d6ec718aa4b91 to your computer and use it in GitHub Desktop.
Simple Yul Contract Implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// @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