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`. | ||||
| 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