Solidity #
- Etherum contracts: write code in Solidity (most common) or other frontend languages
- Compiles to EVM bytecode
- Validators use EVM to execute contract bytecode in response to Tx
- An example contract: NameCoin
contract nameCoin { struct nameEntry { address owner; bytes32 value; } mapping (bytes32 => nameEntry) data; // insecure: front-running bug, can be solved using committments function nameNew(bytes32 name) { // registration cost is 100 Wei if (data[name] == 0 && msg.value >= 100) { data[name].owner = msg.sender; // record domain owner emit Register(msg.sender, name); // log event } } function nameUpdate(bytes32 name, bytes32 newValue, address newOwner) { if (data[name].owner == msg.sender && msg.value >= 10) { data[name].value = newValue; data[name].owner = newOwner; } } // humans do not need this (i.e. etherscan.io) // used by other contracts function nameLookup(bytes32 name) { return data[name]; } }
Contract structure #
Inheritance #
- Everything is a contract, but some can be degenerate contracts (i.e., those that only contain interfaces and no implementations)
interface IERC20 {
function transfer(address _to, uint256 _value) external returns (bool);
function totalSupply() external view returns (uint256);
}
contract ERC20 is IERC20 { // inheritance
address owner;
constructor() public {
owner = msg.sender;
}
function transfer(address _to, uint256 _value) external returns (bool) {
// implementation
}
}
Value types #
uint256
bytes32
address (bytes32)
- Functions:
_address.balance
,_address.send(value)
,_address.transfer(value)
- Calling other contracts to send Tx:
bool success = _address.call{value: msg.value/2, gas: 100}(args);
delegatecall
: load code from other contract into current context
- Functions:
bool
Reference types #
- structs
- arrays
- bytes
- strings
- mapping (associative array):
- Declaration:
mapping (address => uint256) balances;
- Assignment:
balances[addr] = value;
- All initialize at 0
- Declaration:
Globally available variables #
block
; fields:blockHash
coinbase
gaslimit
number
timestamp
gasLeft()
msg
(Tx data); fields:data
(raw data)sender
sig
value
tx
; fields:gasprice
origin
abi
; fields:encode
encodePacked
encodeWithSelector
encodeWithSignature
- Cryptographic functions:
keccack256()
sha256()
sha3()
require
- e.g.
require(msg.value > 100, "insufficient funds sent")
- required run conditions
- e.g.
assert
- Should never fail in prod: this is just for dev code testing
Function visibilities #
external
: function can only be called from outside contract- Arguments read from calldata (costs 16 gas/byte)
public
: function can be called externally and internally- If called externally: args copied from calldata to memory (expensive)
private
: only visible inside contractinternal
: only visible in this contract and contracts deriving from itview
: only read storage (no writes)pure
: does not touch storage
Note: difference between tx.origin
and msg.sender
:
- Imagine Tx chain
A->B->C->D
, then atD
msg.sender == C
buttx.origin == A
Notes on publicity of Solidity contracts #
- Despite compiling to EVM bytecode (only bytecode stored on-chain) - people want to see the source and verify the compiled contract is doing what it should be
- Therefore - Solidity code submitted to sites like Etherscan, which verify the compiled contract matches the on-chain bytecode
- Solidity variables stored in
S[]
array on chain- This means that contracts cannot keep secrets!!