Solidity Non-Authoritative Development Guide (2): Syntax Summary

Contact

Similar to class, it can be: abstract, inherited and called by other contracts.

Typical use:

  • Create a new contract:new MyContract(...)
  • Use the deployed contract:MyContract($address)

visibility

visibility similar apply to externally accessible Subcontracts are accessible
external function
public public function + state variable
internal protected function + state variable
private private function + state variable

Note: For public variables, the corresponding getter will be automatically generated (see: Ethers.js non-authoritative development guide (continued) for details ).

Key elements

element illustrate example
State variables Stored permanently on the chain, it takes gas uint data;
function There are two types of read/write, and the write method needs to consume gas; it can exist inside and outside the contract function func() public {...}
fallback() Cannot be called directly from the outside, it will be executed when there is no function in the request contract fallback() external { ... }
receive() Cannot be directly called externally, it is executed when receiving eth receive() external payable {...}
modifier Reusable declarative constraints, executed before function calls. Declaration: modifier onlyOwner(){...}use:function func() public onlyOwner {...}
event The execution log on the chain can be queried in the future. emit Event1(data);
structure custom type struct MyType { uint item1; bool item2; }
error custom exception Declaration: error MyError(unit reason);use:revert MyError(200);
enumerate Best choice for finite constant values enum State { Created, Locked, Inactive }

Note:

  • payable, the function receiving eth must add

  • view or pure, indicating that the function will not change the Ethereum state

  • fallback and receive functions

    • Neither can have a function name, so there are no  function keywords.
    • must use external
    • The fallback function can also be payable, but it is recommended to use the receive function first.
    • The payable fallback and receive functions can consume up to 2300 gas, which needs to be tested here.
    • Ordinary fallback does not have this limitation, as long as the gas is sufficient, any complex operation can be performed.

For details on how to use event and query logs in dapps, see: Ethers.js Non-Authoritative Development Guide (Part 2)

Interface

Similar to interfaces in other languages, you can:

  • Inherit other interfaces
  • Only method declarations, nothing else
  • All methods are external

Library

Similar to contract, but:

  • cannot use state variables
  • cannot inherit or be inherited
  • cannot receive eth
  • It cannot be executed independently and must be referenced by other contracts.

The relationship between the two is similar: contract, executable file; library, dynamic link library

type of data

Value Types and Reference Types

similar
value type bool、uint / int、address、byte、enum
reference type Array (such as bytes / string), structure, mapping

Note:

  • byte arrays and strings

    • keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2))
    • bytes bs = 'bytes';
    • string str = 'string';
    • byte1 b1 = 'a';
    • byte2 b2 = 256;, 256 exceeds the single-byte size
    • Fixed-length byte array: byte1 ~ byte32
    • Dynamic byte data: bytes and string, where string is encoded in utf-8.
    • Solidity does not provide a method for string comparison, but it can be done with the help of a hash function.
  • array

  • int[] age = [10, 20, 30, 40, 50];

  • int[] age = new int[](5);

  • int[5] age = [10, 20, 30, 40, 50];

  • Fixed-length arrays cannot be initialized with new

  • Dynamic arrays can use new or initial assignment at the same time

  • There are two types of address: address and  address payable, compared with the former, the latter has more transfer functions.

storage location

similar example
storage Persistence, global memory in the contract State variables
memory Function's local memory, non-persistent function input
calldata Function input parameters, non-persistent function input
stack EVM call stack

Relevant rules:

  • Prioritize calldata because it can avoid copying and cannot be modified

  • Function local variables:

    • mapping (uint => address) storage localNames = names;
    • When it is storage, it needs to point to the external state variable
    • value type, memory
    • Reference type, the default is storage, but it can be specified as memory.
    • mapping, storage, always point to external state variables. The following example  names is a state variable defined in a contract, and its type is also mapping.
  • Assignment rules

  • value type, making an independent copy

  • reference type, copy reference

  • Assignment between storage and memory / calldata always produces an independent copy.

  • Assignment between memory variables

  • storage Assigns a value to a local storage variable, copying the reference.

  • Other storage assignments always generate independent copies.

Global Variables and Methods

illustrate example
eth unit wei, wei, ether 1 gwei
time unit seconds、minutes、hours、days、weeks 1 minutes
block block object
blockhash() If the input parameter is one of the latest 256 blocks, it will be its hash. Otherwise, 0.
msg msg object
tx tx object
gasleft() remaining gas
abi abi object
address address object
this The current contract object, which can be explicitly converted to address address(this).balance
type() type information
addmod (a + b) % k
mulmod (a * b) % k
hash function keccak256、sha256、ripemd160
ecrecover Recover address from signature

详见:Units and Globally Available Variables — Solidity 0.8.18 documentation

Note:

  • The difference between tx.orgin and msg.sender

    • tx.orgin is the first account to initiate tx, and its value is always eoa.
    • msg.sender is the direct calling account of the current function, which may be eoa or contract address.
  • Make sure that the first parameter of ecrecover is a valid eth message signature hash, which can be done with the help of openzepplin's ecdsa tool class.

  • It is used first  address.transfer, and when it fails, transfer it throws an exception and  sender returns  false.

  • The low-level methods (call, delegatecall, staticcall, send, transfer) on address have two sides:

  • They are cheap to execute due to the lack of runtime checks like type, existence, etc.

  • Therefore, it is not safe.

  • The use of call, delegatecall, and staticcall on address is similar, but the application scenarios are different:

  • call, applied to the contract

  • delegatecall, applied to library

  • staticcall, applied to contract read-only methods, namely view or pure methods, otherwise an exception will be thrown.

  • A typical address.call call:

bytes memory payload = abi.encodeWithSignature("register(string)", "MyName");
(bool success, bytes memory returnData) = address(nameReg).call(payload);
require(success);
  • If you need to adjust gas and send eth, then:
address(nameReg).call{gas: 1000000, value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));

exception handling

Exception type:

  • Panic, internal error, such as division by zero.
  • Error, general exception.

After the contract throws an exception, the state is rolled back. There are currently 3 ways:

  • require(expression), if the expression is false, an exception is thrown, and unused gas is returned

    • It is suitable for the input parameter of the verification function and throws Error.
    • The current version of require cannot be used with custom Error types. If necessary, use the combination of "conditional statement + revert".
  • assert(expression), same as above, but the unused gas will not be refunded and will be consumed in full

  • Suitable for verifying internal state and throwing Panic.

  • revert(), throw Error directly or customize Error, similar to throw in other languages.

Example of a try...catch statement:

try feed.getData(token) returns (uint v) {
    return (v, true);
} catch Error(string memory /*reason*/) {
    // require 导致
    errorCount++;
    return (0, false);
} catch Panic(uint /*errorCode*/) {
    // assert 导致
    errorCount++;
    return (0, false);
} catch (bytes memory /*lowLevelData*/) {
    // revert 导致
    errorCount++;
    return (0, false);
}

Guess you like

Origin blog.csdn.net/smartContractXH/article/details/128202072