Version |
$ solc --versionVersion: 0.6.12 |
$ vyper --version0.2.3 |
General notes on syntax |
Solidity loosely borrows its syntax from Javascript and C |
Vyper syntax is valid Python 3 syntax (but the opposite is not true) |
Block delimiters |
{ } |
: # Vyper uses Python's off-side rule |
Statement separator |
; |
\n' and : |
End of line comment |
// comment |
# comment |
Multiple line comment |
/* multiple linecomment */ |
# Multiple line# comment |
Constant |
uint constant TOTAL_SUPPLY = 10000000; |
TOTAL_SUPPLY: constant(uint256) = 10000000 |
Assignment |
v = 1; |
v = 1 |
Parallel assignment |
(x, y) = (0, 1); |
Tuple to tuple assignment not supported |
Swap |
(x, y) = (y, x); |
|
Compound assignment |
-=, *=, /=, %=, |=, &=, ^= |
-=, *=, /=, %=, |=, &=, ^= |
Increment and decrement |
i++, ++i, i--, --i |
i += 1, i -= 1 |
Null |
null doesn't exist in Solidity but any unitialized variables take a default value represented by 0 in memory |
null doesn't exist in Vyper but any unitialized variables take a default value represented by 0 in memory |
Set variable to default value |
delete v // doesn't work with mappings |
v = empty(uint256) |
Null test |
v == 0 |
v == 0 |
Conditional expression |
x > 0 ? x : -x |
Conditional expression not supported |
Contract lifecycle |
|
|
Feature |
Solidity |
Vyper |
Contract creation |
Contract c = new Contract(args); |
|
Contract creation with funding |
Contract c = new Contract{value: amount}(args); |
|
Salted contract creation (CREATE2) |
Contract c = new Contract{salt: salt}(args); |
|
Create forwarder contract |
|
contract: address = create_forwarder_to(other_contract, value) |
Selfdestruct (Avoid) |
selfdestruct(refundAddr) |
selfdestruct(refund_addr) |
Interfaces |
|
|
Feature |
Solidity |
Vyper |
Interfaces |
interface HelloWorld { function hello() external pure; function world(int) external pure;} |
interface HelloWorld: def hello(): nonpayable def world(uint256): nonpayable |
Interface type |
interface HelloWorldWithEvent { event Event(); function hello() external pure; function world(int) external pure;}contract Test { bytes4 public hello_world_with_event = type(HelloWorldWithEvent).interfaceId;} |
|
Operators |
|
|
Feature |
Solidity |
Vyper |
True and false |
true false |
True False |
Falsehoods |
FALSE |
FALSE |
Logical operators |
&& || ! |
and or not |
Relational operators |
#ERROR! |
#ERROR! |
Min and max |
|
max(x, y) |
Arithmetic operators |
#ERROR! |
#ERROR! |
Integer division |
/ |
/ |
Bit operators |
<< >> & | ^ ~ |
<< >> & | ^ ~ |
Binary & hex literals |
uint x = 0x52string memory s = hex"52" |
a: address= 0x14d465376c051Cbcd80Aa2d35Fd5df9910f80543b: Bytes[32]= b'\x01\x02\x03\x04\x05\x06... (32 bytes)d: Bytes[1] = 0b00010001 |
Data structures |
|
|
Feature |
Solidity |
Vyper |
String type |
string |
String[N] # N is a fixed number |
Bytes type |
bytes // dynamicbytes1, bytes2, ..., bytes32 // packedbytes[N] // N is a fixed number, unpacked |
Bytes[N] # N is a fixed number |
String literal |
"don't \"no\""'don"t \'no\''� |
"don't \"no\""'don"t \'no\'' |
String length |
bytes(s).length |
len(s) |
String literal escapes |
\ (escapes an actual newline)\\ (backslash)\' (single quote)\" (double quote)\b (backspace)\f (form feed)\n (newline)\r (carriage return)\t (tab)\v (vertical tab)\xNN (hex escape)\uNNNN (unicode escape) |
\ (escapes an actual newline)\\ (backslash)\' (single quote)\" (double quote)\a (bell)\b (backspace)\f (form feed)\n (newline)\r (carriage return)\t (tab)\v (vertical tab)\ooo (octal escape)\xNN (hex escape)\uNNNN (unicode escape)\uNNNNNNNN (unicode escape) |
Are strings mutable? |
Yes |
Yes |
Slice |
abi.decode(_payload[:4], (bytes4))// array slices only implemented for calldata arrays |
slice(x, _start, _len) |
String comparison |
keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2)) |
keccak256(s1) == keccak256(s2) |
String concatenation |
abi.encodePacked(s1, s2) |
concat(s1, s2) |
Array literal |
[1, 2, 3] |
[1, 2, 3] |
Length |
a.length |
len(a) |
Empty test |
a.length == 0 |
|
Lookup |
a[0] |
a[0] |
Update |
a[0] = 1; |
a[0] = 1 |
Out of bounds access |
Failing assertion |
Failing assertion |
Add new element |
a.push(3); # Dynamic arrays |
|
Remove element |
a.pop(); # Dynamic arrays |
|
Struct |
struct Pair { uint x; uint y;} // Creating a structPair memory pair = Pair(2, 3); // Instantiating a struct variablerequire(pair.y > pair.x); // Accessing elements |
struct Pair: x: uint256 y: uint256 # Creating a structpair: Pair = Pair({x: 2, y: 3}) # Instantiating a struct variableassert pair.y > pair.x # Accessing elements |
Mapping size |
Impossible to know |
Impossible to know |
Lookup |
m[2] |
m[2] |
Update |
m[2] = 1; |
m[2] = 1 |
Missing key behaviour |
A mapping has no concept of set keys, a mapping always refers to a hashed value that is the same for a given mapping and key |
A mapping has no concept of set keys, a mapping always refers to a hashed value that is the same for a given mapping and key |
Delete key |
m[2] = 0; |
m[2] = empty(uint256) |
Immutable variables |
uint immutable x; // have to be assigned in the constructor |
|
Functions |
|
|
Feature |
Solidity |
Vyper |
Define function |
function add2(uint x, uint y) public pure returns (uint) { return x + y;} |
@externaldef add2(x: uint256, y: uint256) -> uint256: return x + y |
Function argument storage location |
function first(uint[] calldata x) public pure returns (uint) { // this function doesn't copy x to memory return x[0];}function first(uint[] memory x) public pure returns (uint) { // this function first copies x to memory return x[0];} |
|
Invoke function |
add2(x, y) |
add2(x, y) |
External function calls |
c.f{gas: 1000, value: 4 ether}() |
c.f()raw_call(address, data, outsize, gas, value, is_delegate_call) |
Control flow |
|
|
Feature |
Solidity |
Vyper |
If statement |
if (a > 2) { ...else if (a == 0) { ...} else { ...} |
if a > 2: ...elif a == 0: ...else: ... |
For loop |
for (uint i = 0; i < 3; i++) { ...} |
for i in range(3): ... |
While loop |
while (a > 0) { ...} |
|
Do-While loop |
do { ...} while (a > 0); |
|
Return value |
return x + y; |
return x + y |
Break |
break; |
break |
Continue |
continue; |
continue |
Assert |
assert(x > y); |
assert x > y |
Require |
require(x > y); |
|
Revert |
require(false, "revert reason") |
raise "revert reason" |
Exception handling |
interface DataFeed { function getData(address token) external returns (uint value); }contract FeedConsumer { DataFeed feed; uint errorCount; function rate(address token) public returns (uint value, bool success) { // Permanently disable the mechanism if there are // more than 10 errors. require(errorCount < 10); try feed.getData(token) returns (uint v) { return (v, true); } catch Error(string memory /*reason*/) { // This is executed in case // revert was called inside getData // and a reason string was provided. errorCount++; return (0, false); } catch (bytes memory /*lowLevelData*/) { // This is executed in case revert() was used // or there was a failing assertion, division // by zero, etc. inside getData. errorCount++; return (0, false); } }} |
|
Misc |
|
|
Feature |
Solidity |
Vyper |
Comments |
NatSpec conventions for functions:/// @author Mary A. Botanist/// @notice Calculate tree age in years, rounded up, for live trees/// @dev The Alexandr N. Tetearing algorithm could increase precision/// @param rings The number of rings from dendrochronological sample/// @return age in years, rounded up for partial yearsEvents:/// The address `participant` just registered for the gathering.event Registered(address participant);Special inheritance syntax for contracts:/// @inheritdoc OtherContract |
def foo(): """ @author Mary A. Botanist @notice Calculate tree age in years, rounded up, for live trees @dev The Alexandr N. Tetearing algorithm could increase precision @param rings The number of rings from dendrochronological sample @return age in years, rounded up for partial years """ ... |
Payment with error on failure (Avoid for Solidity) |
address.transfer() |
send(address, value) |
Payment with false on failure (Avoid for Solidity) |
address.send() |
|
Payment with gas forwarding (WARNING) |
address.call.value().gas()() |
raw_call(address, data, outsize, gas, value, is_delegate_call) |
Event logging |
event Deposit( address indexed _from, bytes32 indexed _id, uint _value);emit Deposit(msg.sender, _id, msg.value); |
event Deposit: _from: indexed(address) _id: indexed(bytes32) _value: uint256log Deposit(msg.sender, _id, msg.value) |
Units, global constants and type ranges |
1 ether1 finney1 szabo1 wei1 gwei1 seconds1 minutes1 hours1 days1 weeks1 years // deprecatedtype(uint).mintype(uint).maxtype(int8).mintype(int8).max... |
ZERO_ADDRESSas_wei_value(1, "finney")as_wei_value(1, "szabo")as_wei_value(1, "wei")as_wei_value(1, "babbage")as_wei_value(1, "shannon")EMPTY_BYTES32MAX_INT128MIN_INT128MAX_DECIMALMIN_DECIMALMAX_UINT256ZERO_WEI |
Block and transaction properties |
blockhash(blockNumber)block.coinbaseblock.difficultyblock.gaslimitblock.numberblock.timestampnow // alias for block.timestampgasleft()msg.datamsg.gasmsg.sendermsg.sigmsg.valuetx.gaspricetx.origin |
blockhash(blockNumber)block.coinbaseblock.difficultyblock.numberblock.prevhash # Same as blockhash(block.number - 1)block.timestampmsg.gasmsg.sendermsg.valuetx.origin |