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 #
uint256bytes32address (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:blockHashcoinbasegaslimitnumbertimestamp
gasLeft()msg(Tx data); fields:data(raw data)sendersigvalue
tx; fields:gaspriceorigin
abi; fields:encodeencodePackedencodeWithSelectorencodeWithSignature
- 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 atDmsg.sender == Cbuttx.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!!