Skip to content

broyeztony/ford-lang

Repository files navigation

The F0/rd Smart Contract Programming Language

Ford Playground Screenshot

Why F0/rd?

While Solidity and Vyper are currently the primary smart contract programming languages for the EVM, F0/rd aims to provide a fresh approach as an additional option for EVM development. Our vision is to simplify EVM development by offering:

  • A less verbose syntax
  • An easier learning curve
  • Modern language constructs and sensible defaults
  • Developer-friendly tooling, such as F0/rd SIMULΞ

Smart Contracts: .f0 and .meta Files

In F0/rd, a smart contract consists of two key components:

  1. .f0 file: Contains the main logic of your smart contract.
  2. .meta file: Specifies additional metadata for state variables, functions, and events, including:
    • Schema
    • Scope
    • Visibility
    • Modifiers

This separation allows for cleaner code organization and improved readability, making it easier to develop and maintain smart contracts.

Example: playground.f0

contract Playground;

let hashmap : address->u32;

def lambda {
    for let k: u8 = 0 to 10 {
        /* do something */
    }
}

def symbols (x: string, m: address->u256, y: i16): u8 {
    -> 42;
}

playground.meta

state:
    hashmap:
        - public 

defs:
  - name: lambda
    modifiers:
        - pure
        - nonpayable
        - public
  - name: symbols
    modifiers:
        - pure
        - nonpayable
        - private
    params:
        - name: x
          location: calldata
        - name: m
          location: storage
  • The .meta file is auto-generated by the compiler.
  • All state variables and functions are assumed to be public and non-payable unless otherwise specified in the .meta file.
  • pure and view modifiers are detected automatically by the F0/rd compiler and do not need to be specified explicitly in the code.
  • Function's parameters are assumed to be located in memory unless otherwise specified in the .meta file.
  • Functions that do not take input parameters (i.e the lambda function in the above code) do not need empty parenthesis. They can be simply written as
def lambda { ... }
  • for loops assume a default step of 1 as in
for let k: u8 = 0 to 10 /* step 1 (default) */ {
        /* do something */
} 

The above points solely based on the above playground contract example are just a few examples to illustrate how F0/rd envision to simplify the developer experience. There is much more to F0/rd than just those.

F0/rd types

Signed and unsigned integers

'i8', 'i16', 'i24', 'i32', 'i40', 'i48', 'i56', 'i64',
'i72', 'i80', 'i88', 'i96', 'i104', 'i112', 'i120', 'i128',
'i136', 'i144', 'i152', 'i160', 'i168', 'i176', 'i184', 'i192',
'i200', 'i208', 'i216', 'i224', 'i232', 'i240', 'i248', 'i256'
'u8', 'u16', 'u24', 'u32', 'u40', 'u48', 'u56', 'u64',
'u72', 'u80', 'u88', 'u96', 'u104', 'u112', 'u120', 'u128',
'u136', 'u144', 'u152', 'u160', 'u168', 'u176', 'u184', 'u192',
'u200', 'u208', 'u216', 'u224', 'u232', 'u240', 'u248', 'u256'

Example

let y0    : u8        = 200;
let y1    : u16       = 2000;
let x0    : i8        = -10;

or simply (x type will default to 256 bits unsigned integer)

let x = 256;

Strings

let s   : string    = "hello F0/rd!";

Addresses

let addr  : address   = "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c";

Booleans

let b     : bool      = false;

or simply (b type will default to boolean)

let b = false;

Lists

let li    : list[i32];

Hashmaps

let mm    : address->u256; 

Command line transpilation

You start with playground.f0

// Enter your F0/rd code here
contract Playground;

let hashmap : address->u32;

def lambda {
    for let k: u8 = 0 to 10 /* step 1 (default) */ {
        /* do something */
    }
}

def symbols (x: string, m: address->u256, y: i16): u8 {
    -> 42;
}

def externalDef: u16 {
    -> simpleInt();
}

def simpleInt: u16 {
    -> 2048;
}

def p(amount: i24): bool {
    -> true;
}

You do

npm run compile

You end up with

// playground.sol
/// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Playground {
    mapping(address => uint32) public hashmap;
    
    function lambda() public {
        for (uint8 k = 0; k < 10; k++) {}
    }
    
    function symbols(string calldata x, mapping(address => uint256) storage m, int16 y) private returns (uint8  res) {
        return 42;
    }
    
    function externalDef() external returns (uint16  res) {
        return simpleInt();
    }
    
    function simpleInt() private returns (uint16  res) {
        return 2048;
    }
    
    function p(int24 amount) public payable returns (bool  res) {
        return true;
    }
}

ABI

[{"inputs":[],"name":"addr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"b","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lambda","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int16","name":"amount","type":"int16"}],"name":"payableDef","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"s","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"somePublicDef","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"x0","outputs":[{"internalType":"int8","name":"","type":"int8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"x1","outputs":[{"internalType":"int16","name":"","type":"int16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"x2","outputs":[{"internalType":"int32","name":"","type":"int32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"x3","outputs":[{"internalType":"int8","name":"","type":"int8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"x4","outputs":[{"internalType":"int64","name":"","type":"int64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"y0","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"y1","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"y2","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"y3","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"y4","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"y5","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

Bytecode

0x6080604052348015600e575f5ffd5b5061030e8061001c5f395ff3fe60806040526004361061003e575f3560
e01c806355d788e11461004257806391432a3414610072578063dad0be611461009c578063f02be3b4146100b2
575b5f5ffd5b61005c60048036038101906100579190610189565b6100ee565b60405161006991906101ce565b
60405180910390f35b34801561007d575f5ffd5b506100866100f8565b6040516100939190610203565b604051
80910390f35b3480156100a7575f5ffd5b506100b0610106565b005b3480156100bd575f5ffd5b506100d86004
8036038101906100d39190610276565b610127565b6040516100e591906102bf565b60405180910390f35b5f60
019050919050565b5f610101610146565b905090565b5f5f90505b600a8160ff16101561012457808060010191
505061010b565b50565b5f602052805f5260405f205f915054906101000a900463ffffffff1681565b5f610800
905090565b5f5ffd5b5f8160020b9050919050565b61016881610153565b8114610172575f5ffd5b50565b5f81
3590506101838161015f565b92915050565b5f6020828403121561019e5761019d61014f565b5b5f6101ab8482
8501610175565b91505092915050565b5f8115159050919050565b6101c8816101b4565b82525050565b5f6020
820190506101e15f8301846101bf565b92915050565b5f61ffff82169050919050565b6101fd816101e7565b82
525050565b5f6020820190506102165f8301846101f4565b92915050565b5f73ffffffffffffffffffffffffff
ffffffffffffff82169050919050565b5f6102458261021c565b9050919050565b6102558161023b565b811461
025f575f5ffd5b50565b5f813590506102708161024c565b92915050565b5f6020828403121561028b5761028a
61014f565b5b5f61029884828501610262565b91505092915050565b5f63ffffffff82169050919050565b6102
b9816102a1565b82525050565b5f6020820190506102d25f8301846102b0565b9291505056fea2646970667358
2212202ecbeacf46e1b066916e9404ba6091031a3731f96f243555c110c761f9abf6a064736f6c634300081b00
33

server-side transpilation

npm run serve

=>

F0/rd transpiler server running on port 3000

Then send a POST request to the /transpile API endpoint

curl -X POST \
  http://localhost:3000/transpile \
  -H "Content-Type: application/json" \
  -d "{\"code\": $(cat playground.f0 | jq -Rs .)}"

About

Home repository of the F0/rd smart contract programming language

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors