mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Example ERC20 contract in Yul.
This commit is contained in:
parent
1bb07e268b
commit
38486f47a7
197
docs/yul.rst
197
docs/yul.rst
@ -104,6 +104,8 @@ less-than comparison.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
At the :ref:`end of the section <erc20yul>`, a complete implementation of
|
||||||
|
the ERC-20 standard can be found.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1087,3 +1089,198 @@ d `VarDeclInitializer`
|
|||||||
|
|
||||||
Some steps depend on properties ensured by `BlockFlattener`, `FunctionGrouper`, `ForLoopInitRewriter`.
|
Some steps depend on properties ensured by `BlockFlattener`, `FunctionGrouper`, `ForLoopInitRewriter`.
|
||||||
For this reason the Yul optimizer always applies them before applying any steps supplied by the user.
|
For this reason the Yul optimizer always applies them before applying any steps supplied by the user.
|
||||||
|
|
||||||
|
|
||||||
|
.. _erc20yul:
|
||||||
|
|
||||||
|
Complete ERC20 Example
|
||||||
|
======================
|
||||||
|
|
||||||
|
.. code-block:: yul
|
||||||
|
|
||||||
|
object "Token" {
|
||||||
|
code {
|
||||||
|
// Store the creator in slot zero.
|
||||||
|
sstore(0, caller())
|
||||||
|
|
||||||
|
// Deploy the contract
|
||||||
|
datacopy(0, dataoffset("runtime"), datasize("runtime"))
|
||||||
|
return(0, datasize("runtime"))
|
||||||
|
}
|
||||||
|
object "runtime" {
|
||||||
|
code {
|
||||||
|
// Protection against sending Ether
|
||||||
|
require(iszero(callvalue()))
|
||||||
|
|
||||||
|
// Dispatcher
|
||||||
|
switch selector()
|
||||||
|
case 0x70a08231 /* "balanceOf(address)" */ {
|
||||||
|
returnUint(balanceOf(decodeAsAddress(0)))
|
||||||
|
}
|
||||||
|
case 0x18160ddd /* "totalSupply()" */ {
|
||||||
|
returnUint(totalSupply())
|
||||||
|
}
|
||||||
|
case 0xa9059cbb /* "transfer(address,uint256)" */ {
|
||||||
|
transfer(decodeAsAddress(0), decodeAsUint(1))
|
||||||
|
returnTrue()
|
||||||
|
}
|
||||||
|
case 0x23b872dd /* "transferFrom(address,address,uint256)" */ {
|
||||||
|
transferFrom(decodeAsAddress(0), decodeAsAddress(1), decodeAsUint(2))
|
||||||
|
returnTrue()
|
||||||
|
}
|
||||||
|
case 0x095ea7b3 /* "approve(address,uint256)" */ {
|
||||||
|
approve(decodeAsAddress(0), decodeAsUint(1))
|
||||||
|
returnTrue()
|
||||||
|
}
|
||||||
|
case 0xdd62ed3e /* "allowance(address,address)" */ {
|
||||||
|
returnUint(allowance(decodeAsAddress(0), decodeAsAddress(1)))
|
||||||
|
}
|
||||||
|
case 0x40c10f19 /* "mint(address,uint256)" */ {
|
||||||
|
mint(decodeAsAddress(0), decodeAsUint(1))
|
||||||
|
returnTrue()
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
revert(0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function mint(account, amount) {
|
||||||
|
require(calledByOwner())
|
||||||
|
|
||||||
|
mintTokens(amount)
|
||||||
|
addToBalance(account, amount)
|
||||||
|
emitTransfer(0, account, amount)
|
||||||
|
}
|
||||||
|
function transfer(to, amount) {
|
||||||
|
executeTransfer(caller(), to, amount)
|
||||||
|
}
|
||||||
|
function approve(spender, amount) {
|
||||||
|
revertIfZeroAddress(spender)
|
||||||
|
setAllowance(caller(), spender, amount)
|
||||||
|
emitApproval(caller(), spender, amount)
|
||||||
|
}
|
||||||
|
function transferFrom(from, to, amount) {
|
||||||
|
decreaseAllowanceBy(from, caller(), amount)
|
||||||
|
executeTransfer(from, to, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeTransfer(from, to, amount) {
|
||||||
|
revertIfZeroAddress(to)
|
||||||
|
deductFromBalance(from, amount)
|
||||||
|
addToBalance(to, amount)
|
||||||
|
emitTransfer(from, to, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- calldata decoding functions ----------- */
|
||||||
|
function selector() -> s {
|
||||||
|
s := div(calldataload(0), 0x100000000000000000000000000000000000000000000000000000000)
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeAsAddress(offset) -> v {
|
||||||
|
v := decodeAsUint(offset)
|
||||||
|
if iszero(iszero(and(v, not(0xffffffffffffffffffffffffffffffffffffffff)))) {
|
||||||
|
revert(0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function decodeAsUint(offset) -> v {
|
||||||
|
let pos := add(4, mul(offset, 0x20))
|
||||||
|
if lt(calldatasize(), add(pos, 0x20)) {
|
||||||
|
revert(0, 0)
|
||||||
|
}
|
||||||
|
v := calldataload(pos)
|
||||||
|
}
|
||||||
|
/* ---------- calldata encoding functions ---------- */
|
||||||
|
function returnUint(v) {
|
||||||
|
mstore(0, v)
|
||||||
|
return(0, 0x20)
|
||||||
|
}
|
||||||
|
function returnTrue() {
|
||||||
|
returnUint(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------- events ---------- */
|
||||||
|
function emitTransfer(from, to, amount) {
|
||||||
|
let signatureHash := 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
|
||||||
|
emitEvent(signatureHash, from, to, amount)
|
||||||
|
}
|
||||||
|
function emitApproval(from, spender, amount) {
|
||||||
|
let signatureHash := 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925
|
||||||
|
emitEvent(signatureHash, from, spender, amount)
|
||||||
|
}
|
||||||
|
function emitEvent(signatureHash, indexed1, indexed2, nonIndexed) {
|
||||||
|
mstore(0, nonIndexed)
|
||||||
|
log3(0, 0x20, signatureHash, indexed1, indexed2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------- storage layout ---------- */
|
||||||
|
function ownerPos() -> p { p := 0 }
|
||||||
|
function totalSupplyPos() -> p { p := 1 }
|
||||||
|
function accountToStorageOffset(account) -> offset {
|
||||||
|
offset := add(0x1000, account)
|
||||||
|
}
|
||||||
|
function allowanceStorageOffset(account, spender) -> offset {
|
||||||
|
offset := accountToStorageOffset(account)
|
||||||
|
mstore(0, offset)
|
||||||
|
mstore(0x20, spender)
|
||||||
|
offset := keccak256(0, 0x40)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------- storage access ---------- */
|
||||||
|
function owner() -> o {
|
||||||
|
o := sload(ownerPos())
|
||||||
|
}
|
||||||
|
function totalSupply() -> supply {
|
||||||
|
supply := sload(totalSupplyPos())
|
||||||
|
}
|
||||||
|
function mintTokens(amount) {
|
||||||
|
sstore(totalSupplyPos(), safeAdd(totalSupply(), amount))
|
||||||
|
}
|
||||||
|
function balanceOf(account) -> bal {
|
||||||
|
bal := sload(accountToStorageOffset(account))
|
||||||
|
}
|
||||||
|
function addToBalance(account, amount) {
|
||||||
|
let offset := accountToStorageOffset(account)
|
||||||
|
sstore(offset, safeAdd(sload(offset), amount))
|
||||||
|
}
|
||||||
|
function deductFromBalance(account, amount) {
|
||||||
|
let offset := accountToStorageOffset(account)
|
||||||
|
let bal := sload(offset)
|
||||||
|
require(lte(amount, bal))
|
||||||
|
sstore(offset, sub(bal, amount))
|
||||||
|
}
|
||||||
|
function allowance(account, spender) -> amount {
|
||||||
|
amount := sload(allowanceStorageOffset(account, spender))
|
||||||
|
}
|
||||||
|
function setAllowance(account, spender, amount) {
|
||||||
|
sstore(allowanceStorageOffset(account, spender), amount)
|
||||||
|
}
|
||||||
|
function decreaseAllowanceBy(account, spender, amount) {
|
||||||
|
let offset := allowanceStorageOffset(account, spender)
|
||||||
|
let currentAllowance := sload(offset)
|
||||||
|
require(lte(amount, currentAllowance))
|
||||||
|
sstore(offset, sub(currentAllowance, amount))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- utility functions ---------- */
|
||||||
|
function lte(a, b) -> r {
|
||||||
|
r := iszero(gt(a, b))
|
||||||
|
}
|
||||||
|
function gte(a, b) -> r {
|
||||||
|
r := iszero(lt(a, b))
|
||||||
|
}
|
||||||
|
function safeAdd(a, b) -> r {
|
||||||
|
r := add(a, b)
|
||||||
|
if or(lt(r, a), lt(r, b)) { revert(0, 0) }
|
||||||
|
}
|
||||||
|
function calledByOwner() -> cbo {
|
||||||
|
cbo := eq(owner(), caller())
|
||||||
|
}
|
||||||
|
function revertIfZeroAddress(addr) {
|
||||||
|
require(addr)
|
||||||
|
}
|
||||||
|
function require(condition) {
|
||||||
|
if iszero(condition) { revert(0, 0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user