Add Yul smart contract language (#5861)

* Add Yul smart contract langauge (intermediate language for Ethereum Virtual Machine)

* add yul to grammars.yml

* remove yulp extension

Co-authored-by: Colin Seymour <colin@github.com>
This commit is contained in:
davidhq 2022-04-26 11:27:59 +02:00 коммит произвёл GitHub
Родитель 01cda72d46
Коммит cbb23aef82
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 264 добавлений и 0 удалений

Просмотреть файл

@ -127,6 +127,7 @@ vendor/grammars/SublimeClarion:
vendor/grammars/SublimeEthereum:
- source.solidity
- source.vyper
- source.yul
vendor/grammars/SublimeGDB/:
- source.disasm
- source.gdb

Просмотреть файл

@ -7225,6 +7225,14 @@ Yacc:
ace_mode: text
color: "#4B6C4B"
language_id: 409
Yul:
type: programming
color: "#794932"
ace_mode: text
tm_scope: source.yul
extensions:
- ".yul"
language_id: 237469033
ZAP:
type: programming
color: "#0d665e"

68
samples/Yul/contract.yul Normal file
Просмотреть файл

@ -0,0 +1,68 @@
// A contract consists of a single object with sub-objects representing
// the code to be deployed or other contracts it can create.
// The single "code" node is the executable code of the object.
// Every (other) named object or data section is serialized and
// made accessible to the special built-in functions datacopy / dataoffset / datasize
// The current object, sub-objects and data items inside the current object
// are in scope.
object "Contract1" {
// This is the constructor code of the contract.
code {
function allocate(size) -> ptr {
ptr := mload(0x40)
if iszero(ptr) { ptr := 0x60 }
mstore(0x40, add(ptr, size))
}
// first create "Contract2"
let size := datasize("Contract2")
let offset := allocate(size)
// This will turn into codecopy for EVM
datacopy(offset, dataoffset("Contract2"), size)
// constructor parameter is a single number 0x1234
mstore(add(offset, size), 0x1234)
pop(create(offset, add(size, 32), 0))
// now return the runtime object (the currently
// executing code is the constructor code)
size := datasize("Contract1_deployed")
offset := allocate(size)
// This will turn into a memory->memory copy for Ewasm and
// a codecopy for EVM
datacopy(offset, dataoffset("Contract1_deployed"), size)
return(offset, size)
}
data "Table2" hex"4123"
object "Contract1_deployed" {
code {
function allocate(size) -> ptr {
ptr := mload(0x40)
if iszero(ptr) { ptr := 0x60 }
mstore(0x40, add(ptr, size))
}
// runtime code
mstore(0, "Hello, World!")
return(0, 0x20)
}
}
// Embedded object. Use case is that the outside is a factory contract,
// and Contract2 is the code to be created by the factory
object "Contract2" {
code {
// code here ...
}
object "Contract2_deployed" {
code {
// code here ...
}
}
data "Table1" hex"4123"
}
}

186
samples/Yul/erc20.yul Normal file
Просмотреть файл

@ -0,0 +1,186 @@
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) }
}
}
}
}

1
vendor/README.md поставляемый
Просмотреть файл

@ -559,6 +559,7 @@ This is a list of grammars that Linguist selects to provide syntax highlighting
- **YARA:** [infosec-intern/vscode-yara](https://github.com/infosec-intern/vscode-yara)
- **YASnippet:** [Alhadis/language-emacs-lisp](https://github.com/Alhadis/language-emacs-lisp)
- **Yacc:** [Alhadis/language-grammars](https://github.com/Alhadis/language-grammars)
- **Yul:** [davidhq/SublimeEthereum](https://github.com/davidhq/SublimeEthereum)
- **ZAP:** [tclem/vscode-zil-language](https://github.com/tclem/vscode-zil-language)
- **ZIL:** [tclem/vscode-zil-language](https://github.com/tclem/vscode-zil-language)
- **Zeek:** [zeek/zeek-sublime](https://github.com/zeek/zeek-sublime)