Last active
June 3, 2023 21:23
-
-
Save jtriley2p/2643ae1b534d8c1be7d529311816e68d to your computer and use it in GitHub Desktop.
Revisions
-
jtriley2p revised this gist
Oct 21, 2022 . 1 changed file with 174 additions and 15 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -6,8 +6,6 @@ pragma solidity 0.8.17; bytes32 constant nameLength = 0x0000000000000000000000000000000000000000000000000000000000000009; bytes32 constant nameData = 0x59756c20546f6b656e0000000000000000000000000000000000000000000000; // Used in the `symbol()` function // "YUL" bytes32 constant symbolLength = 0x0000000000000000000000000000000000000000000000000000000000000003; @@ -19,10 +17,16 @@ bytes32 constant insufficientBalanceSelector = 0xf4d678b800000000000000000000000 // `bytes4(keccak256("InsufficientAllowance(address,address)"))` bytes32 constant insufficientAllowanceSelector = 0xf180d8f900000000000000000000000000000000000000000000000000000000; // max uint256 value, used to mint EVERYTHING to the deployer lol uint256 constant maxUint256 = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; error InsufficientBalance(); error InsufficientAllowance(address owner, address spender); // `keccak256("Transfer(address,address,uint256)")` bytes32 constant transferHash = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; // `keccak256("Approval(address,address,uint256)")` bytes32 constant approvalHash = 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925; /// @title Yul ERC20 @@ -33,205 +37,360 @@ contract YulERC20 { event Transfer(address indexed sender, address indexed receiver, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); // account -> balance // `slot = keccak(account, 0x00))` mapping(address => uint256) internal _balances; // owner -> spender -> allowance // `slot = keccak256(owner, keccak256(spender, 0x01))` mapping(address => mapping(address => uint256)) internal _allowances; // `slot = 0x02` uint256 internal _totalSupply; // Mint maxUint256 tokens to the `msg.sender`. constructor() { assembly { // store the caller address at memory index zero mstore(0x00, caller()) // store zero (storage index) at memory index 32 mstore(0x20, 0x00) // hash the first 64 bytes of memory to generate the balance slot let slot := keccak256(0x00, 0x40) // store maxUint256 as caller's balance sstore(slot, maxUint256) // store maxUint256 as total supply sstore(0x02, maxUint256) // store maxUint256 in memory to log mstore(0x00, maxUint256) // log transfer event log3(0x00, 0x20, transferHash, 0x00, caller()) } } function name() public pure returns (string memory) { assembly { // get free memory pointer from memory index `0x40` let memptr := mload(0x40) // store string pointer (0x20) in memory mstore(memptr, 0x20) // store string length in memory 32 bytes after the pointer mstore(add(memptr, 0x20), nameLength) // store string data 32 bytes after the length mstore(add(memptr, 0x40), nameData) // return from memory the three 32 byte slots (ptr, len, data) return(memptr, 0x60) } } function symbol() public pure returns (string memory) { assembly { // get free memory pointer from memory index `0x40` let memptr := mload(0x40) // store string pointer (0x20) in memory mstore(memptr, 0x20) // store string length in memory 32 bytes after the pointer mstore(add(memptr, 0x20), symbolLength) // store string data 32 bytes after the length mstore(add(memptr, 0x40), symbolData) // return from memory the three 32 byte slots (ptr, len, data) return(memptr, 0x60) } } function decimals() public pure returns (uint8) { assembly { // store `18` in memory at slot zero mstore(0, 18) // return 32 bytes from memory at slot zero return(0x00, 0x20) } } function totalSupply() public view returns (uint256) { assembly { // load the total supply from storage slot 0x02 and store in memory mstore(0x00, sload(0x02)) // return 32 bytes from memory at index zero return(0x00, 0x20) } } function balanceOf(address) public view returns (uint256) { assembly { // load calldata offset 4 (first arg after selector) and store in memory at index zero mstore(0x00, calldataload(4)) // store zero (storage index) at memory index 32 mstore(0x20, 0x00) // load from storage the hash of the first 64 bytes of memory, // then store the value in memory at offset zero mstore(0x00, sload(keccak256(0x00, 0x40))) // return the first 32 bytes from memory (loaded balance) return(0x00, 0x20) } } function transfer(address receiver, uint256 amount) public returns (bool) { assembly { // load free memory pointer from index 64 let memptr := mload(0x40) // store the caller address at the free memory pointer mstore(memptr, caller()) // store zero (storage index) in the next memory index mstore(add(memptr, 0x20), 0x00) // hash 64 bytes of memory to generate the caller's balance slot let callerBalanceSlot := keccak256(memptr, 0x40) // load the caller's balance let callerBalance := sload(callerBalanceSlot) // if the caller's balance is less than the amount if lt(callerBalance, amount) { // store the insufficient balance selector in memory at slot zero mstore(0x00, insufficientBalanceSelector) // revert with the 4 byte selector from memory revert(0x00, 0x04) } // if the caller == receiver, revert if eq(caller(), receiver) { // we should have a better error message here, // but we were short on time revert(0x00, 0x00) } // decrease the caller's balance let newCallerBalance := sub(callerBalance, amount) // store the caller's balance in its slot sstore(callerBalanceSlot, newCallerBalance) // store the receiver address in memory at the memory pointer // (overwrites some of the memory we have written to, but we don't need it anymore) mstore(memptr, receiver) // store zero (storage index) at a 32 byte offset mstore(add(memptr, 0x20), 0x00) // hash 64 bytes of memory to generate the receiver's balance slot let receiverBalanceSlot := keccak256(memptr, 0x40) // load the receiver's balance let receiverBalance := sload(receiverBalanceSlot) // increase receiver balance let newReceiverBalance := add(receiverBalance, amount) // store the receiver's balance sstore(receiverBalanceSlot, newReceiverBalance) // store the amount in memory to be logged mstore(0x00, amount) // log the transfer event log3(0x00, 0x20, transferHash, caller(), receiver) // store `true` in memory at index zero mstore(0x00, 0x01) // return the first 32 byte word of memory return(0x00, 0x20) } } function allowance(address owner, address spender) public view returns (uint256) { assembly { // store owner address at memory index zero mstore(0x00, owner) // store one (storage index) at memory index 32 mstore(0x20, 0x01) // hash the first 64 bytes of memory to generate the inner hash let innerHash := keccak256(0x00, 0x40) // store the spender address at memory index zero mstore(0x00, spender) // store the inner hash at memory index 32 mstore(0x20, innerHash) // hash the first 64 bytes of memory to generate the allowance slot let allowanceSlot := keccak256(0x00, 0x40) // load the allowance from storage let allowanceAmount := sload(allowanceSlot) // store the allowance at memory index zero mstore(0x00, allowanceAmount) // return the first 32 byte word from memory return(0x00, 0x20) } } function approve(address spender, uint256 amount) public returns (bool) { assembly { // store the caller address mstore(0x00, caller()) // store one (storage index) at memory index 32 mstore(0x20, 0x01) // hash the first 64 bytes of memory to generate the inner hash let innerHash := keccak256(0x00, 0x40) // store the spender address at memory index zero mstore(0x00, spender) // store the inner hash at memory index 32 mstore(0x20, innerHash) // hash the first 64 bytes of memory to generate the allowance slot let allowanceSlot := keccak256(0x00, 0x40) // store the new allowance in the allowance slot sstore(allowanceSlot, amount) // store the amount at memory index zero to be logged mstore(0x00, amount) // log the approval event log3(0x00, 0x20, approvalHash, caller(), spender) // store `true` at memory index zero mstore(0x00, 0x01) // return the first 32 byte word from memory return(0x00, 0x20) } } function transferFrom(address sender, address receiver, uint256 amount) public returns (bool) { assembly { // load the free memory pointer from memory index 64 let memptr := mload(0x40) // store the sender address at memory index zero mstore(0x00, sender) // store one (storage index) at memory index 32 mstore(0x20, 0x01) // hash the first 64 bytes of memory to generate the inner hash let innerHash := keccak256(0x00, 0x40) // store the caller (spender) at memory index zero mstore(0x00, caller()) // store the inner hash at memory index 32 mstore(0x20, innerHash) // hash the first 64 bytes of memory to generate the allowance slot let allowanceSlot := keccak256(0x00, 0x40) // load the caller's allowance to spend on behalf of the sender let callerAllowance := sload(allowanceSlot) // if the caller's allowance is less than the amount if lt(callerAllowance, amount) { // store the insufficient allowance error selector at the free memory pointer mstore(memptr, insufficientAllowanceSelector) // store the sender in memory after the four byte selector mstore(add(memptr, 0x04), sender) // store the caller in memory after the sender mstore(add(memptr, 0x24), caller()) // revert with 68 (4 + 32 + 32) bytes of memory revert(memptr, 0x44) } // if the caller allowance is less than the max uint256 value (infinite) if lt(callerAllowance, maxUint256) { // subtract the amount from the allowance and store it in storage sstore(allowanceSlot, sub(callerAllowance, amount)) } // store the sender address in memory at the free memory pointer mstore(memptr, sender) // store zero (storage index) after the sender address mstore(add(memptr, 0x20), 0x00) // hash 64 bytes of memory starting at the free memory pointer to generate the balance slot let senderBalanceSlot := keccak256(memptr, 0x40) // load the sender balance let senderBalance := sload(senderBalanceSlot) // if the sender balance is less than the amount if lt(senderBalance, amount) { // store the insufficient balance selector in memory mstore(0x00, insufficientBalanceSelector) // revert with the error selector revert(0x00, 0x04) } // subtract the amount from the sender balance and store it sstore(senderBalanceSlot, sub(senderBalance, amount)) // store the receiver address in memory at the free memory pointer mstore(memptr, receiver) // store zero (storage index) after the receiver address mstore(add(memptr, 0x20), 0x00) // hash 64 bytes of memory starting at the free memory pointer to generate the balance slot let receiverBalanceSlot := keccak256(memptr, 0x40) // load the sender balance let receiverBalance := sload(receiverBalanceSlot) // add the amount and the receiver balance and store it sstore(receiverBalanceSlot, add(receiverBalance, amount)) // store the amount in memory to be logged mstore(0x00, amount) // log the transfer event log3(0x00, 0x20, transferHash, sender, receiver) // store `true` in memory at slot zero mstore(0x00, 0x01) // return the first 32 byte word from memory return(0x00, 0x20) } } -
jtriley2p created this gist
Oct 20, 2022 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,238 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; // Used in the `name()` function // "Yul Token" bytes32 constant nameLength = 0x0000000000000000000000000000000000000000000000000000000000000009; bytes32 constant nameData = 0x59756c20546f6b656e0000000000000000000000000000000000000000000000; uint256 constant maxUint256 = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; // Used in the `symbol()` function // "YUL" bytes32 constant symbolLength = 0x0000000000000000000000000000000000000000000000000000000000000003; bytes32 constant symbolData = 0x59554c0000000000000000000000000000000000000000000000000000000000; // `bytes4(keccak256("InsufficientBalance()"))` bytes32 constant insufficientBalanceSelector = 0xf4d678b800000000000000000000000000000000000000000000000000000000; // `bytes4(keccak256("InsufficientAllowance(address,address)"))` bytes32 constant insufficientAllowanceSelector = 0xf180d8f900000000000000000000000000000000000000000000000000000000; error InsufficientBalance(); error InsufficientAllowance(address owner, address spender); bytes32 constant transferHash = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; bytes32 constant approvalHash = 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925; /// @title Yul ERC20 /// @author <your name here> /// @notice For demo purposes ONLY. /// @dev Some optimizations and best practices omitted here for the sake of demonstration. contract YulERC20 { event Transfer(address indexed sender, address indexed receiver, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); // owner -> balance mapping(address => uint256) internal _balances; // owner -> spender -> allowance mapping(address => mapping(address => uint256)) internal _allowances; uint256 internal _totalSupply; constructor() { assembly { mstore(0x00, caller()) mstore(0x20, 0x00) let slot := keccak256(0x00, 0x40) sstore(slot, maxUint256) sstore(0x02, maxUint256) mstore(0x00, maxUint256) log3(0x00, 0x20, transferHash, 0x00, caller()) } } function name() public pure returns (string memory) { assembly { let memptr := mload(0x40) mstore(memptr, 0x20) mstore(add(memptr, 0x20), nameLength) mstore(add(memptr, 0x40), nameData) return(memptr, 0x60) } } function symbol() public pure returns (string memory) { assembly { let memptr := mload(0x40) mstore(memptr, 0x20) mstore(add(memptr, 0x20), symbolLength) mstore(add(memptr, 0x40), symbolData) return(memptr, 0x60) } } function decimals() public pure returns (uint8) { assembly { mstore(0, 18) return(0x00, 0x20) } } function totalSupply() public view returns (uint256) { assembly { mstore(0x00, sload(0x02)) return(0x00, 0x20) } } function balanceOf(address) public view returns (uint256) { assembly { mstore(0x00, calldataload(4)) mstore(0x20, 0x00) mstore(0x00, sload(keccak256(0x00, 0x40))) return(0x00, 0x20) } } function transfer(address receiver, uint256 amount) public returns (bool) { assembly { // mem stuff let memptr := mload(0x40) // load caller balance, assert sufficient mstore(memptr, caller()) mstore(add(memptr, 0x20), 0x00) let callerBalanceSlot := keccak256(memptr, 0x40) let callerBalance := sload(callerBalanceSlot) if lt(callerBalance, amount) { mstore(0x00, insufficientBalanceSelector) revert(0x00, 0x04) } if eq(caller(), receiver) { revert(0x00, 0x00) } // decrease caller bal let newCallerBalance := sub(callerBalance, amount) sstore(callerBalanceSlot, newCallerBalance) // load receiver balance mstore(memptr, receiver) mstore(add(memptr, 0x20), 0x00) let receiverBalanceSlot := keccak256(memptr, 0x40) let receiverBalance := sload(receiverBalanceSlot) // increase receiver bal let newReceiverBalance := add(receiverBalance, amount) // store sstore(receiverBalanceSlot, newReceiverBalance) // log mstore(0x00, amount) log3(0x00, 0x20, transferHash, caller(), receiver) // ret mstore(0x00, 0x01) return(0x00, 0x20) } } function allowance(address owner, address spender) public view returns (uint256) { // keccak256(spender, keccak256(owner, slot))) assembly { mstore(0x00, owner) mstore(0x20, 0x01) let innerHash := keccak256(0x00, 0x40) mstore(0x00, spender) mstore(0x20, innerHash) let allowanceSlot := keccak256(0x00, 0x40) let allowanceAmount := sload(allowanceSlot) mstore(0x00, allowanceAmount) return(0x00, 0x20) } } function approve(address spender, uint256 amount) public returns (bool) { assembly { mstore(0x00, caller()) mstore(0x20, 0x01) let innerHash := keccak256(0x00, 0x40) mstore(0x00, spender) mstore(0x20, innerHash) let allowanceSlot := keccak256(0x00, 0x40) sstore(allowanceSlot, amount) mstore(0x00, amount) log3(0x00, 0x20, approvalHash, caller(), spender) mstore(0x00, 0x01) return(0x00, 0x20) } } function transferFrom(address sender, address receiver, uint256 amount) public returns (bool) { assembly { let memptr := mload(0x40) mstore(0x00, sender) mstore(0x20, 0x01) let innerHash := keccak256(0x00, 0x40) mstore(0x00, caller()) mstore(0x20, innerHash) let allowanceSlot := keccak256(0x00, 0x40) let callerAllowance := sload(allowanceSlot) if lt(callerAllowance, amount) { mstore(memptr, insufficientAllowanceSelector) mstore(add(memptr, 0x04), sender) mstore(add(memptr, 0x24), caller()) revert(memptr, 0x44) } if lt(callerAllowance, maxUint256) { sstore(allowanceSlot, sub(callerAllowance, amount)) } // load sender balance, assert sufficient mstore(memptr, sender) mstore(add(memptr, 0x20), 0x00) let senderBalanceSlot := keccak256(memptr, 0x40) let senderBalance := sload(senderBalanceSlot) if lt(senderBalance, amount) { mstore(0x00, insufficientBalanceSelector) revert(0x00, 0x04) } sstore(senderBalanceSlot, sub(senderBalance, amount)) // receiver balance mstore(memptr, receiver) mstore(add(memptr, 0x20), 0x00) let receiverBalanceSlot := keccak256(memptr, 0x40) let receiverBalance := sload(receiverBalanceSlot) sstore(receiverBalanceSlot, add(receiverBalance, amount)) mstore(0x00, amount) log3(0x00, 0x20, transferHash, sender, receiver) mstore(0x00, 0x01) return(0x00, 0x20) } } }