YYSuni
cover

Solidity More

Delve deeper into advanced Solidity knowledge

Can a Public Contract Know All Its Content?

No.

  1. Contracts have private variables. The term "private" means that the information is not intended to be disclosed, and outsiders cannot access it.
  2. The creation code contains all the content, but it is very difficult to parse into readable information. For example, it is hard to extract initialization parameters:
    • This includes whether the developer intended it to be this way.
    • Compiler optimizations and encryption also play a role.

How to Deploy a Contract with Multiple Files

A contract is generally a single file with a clear contract name representing the main contract. It can import multiple files, and methods from different files may also be parsed into callable ABIs.

To call an externally deployed contract, you can set the address of the external contract in the current contract and use an interface to link to and call the contract.

A library can be deployed as a separate contract. When deploying a library, you only need to link it without deploying it separately.

"libraries": {
    "contracts/libraries/NFTDescriptor.sol": {
        "NFTDescriptor": "0x42b24a95702b9986e82d421cc3568932790a48ec"
    }
}

In Solidity, when a contract references a library, the compiler generates a placeholder address to mark the location of the library in the contract code. Before deploying the contract, this placeholder address must be replaced with the actual address of the deployed library. This process is called "library linking."

Keywords

Exposing contract methods:

  • public
  • external (callable only from outside)

memory indicates that a variable is not stored persistently but exists only in memory.

abi is a built-in library provided by Solidity.

  • abi.encode encodes multiple values into a byte array.
  • abi.encodePacked encodes multiple values into a tightly packed byte array without alignment or padding.
  • abi.encodeWithSelector encodes a function selector (4 bytes) and parameters into a byte array. It is commonly used for calling contract functions.
  • abi.encodeWithSignature encodes a function signature (in string form) and parameters into a byte array. The compiler automatically parses the function signature to generate the selector.
  • abi.decode decodes a byte array into variables of specified types. The types to be decoded must be specified.
  • abi.encodeCall encodes a function call into a byte array. This method is available in Solidity version 0.8.12 and above, making it easier to encode function calls.
  • abi.selector retrieves the function selector (4 bytes). This method is available in Solidity version 0.8.12 and above.
  • abi.functionSelector retrieves the function selector (4 bytes). This method is available in Solidity version 0.8.12 and above, similar to abi.selector.

Compiler Optimization

In the Solidity compilation process, optimization is an important option that helps optimize the generated bytecode, thereby reducing gas consumption during deployment and execution. Enabling optimization typically involves a series of improvements to the code generated by the compiler to enhance efficiency.

Optimization Enabled: Yes with 200 runs

solc --optimize --optimize-runs 200 your_contract.sol

Uniswap tokenURI

tokenURI is dynamically generated data that wraps position data into a JSON data encoded in Base64. When parsing, WETH9 is handled specially.

Base64

Base64 converts characters into 8-bit binary (0/1), then reassembles them into 6-bit binary, mapping to ASCII characters. The length is a multiple of 4, with = used for padding at the end.

Parent Class Initialization

constructor([parameter list]) [parent contract constructor call] {
    // Initialization logic
}

constructor(
    address _factory,
    address _WETH9,
    address _tokenDescriptor_
) ERC721Permit('Uniswap V3 Positions NFT-V1', 'UNI-V3-POS', '1') PeripheryImmutableState(_factory, _WETH9) {
    _tokenDescriptor = _tokenDescriptor_;
}

Modifiers

function collect(CollectParams calldata params)
    external
    payable
    override
    isAuthorizedForToken(params.tokenId)
    returns (uint256 amount0, uint256 amount1)
{
    // ...
}

modifier isAuthorizedForToken(uint256 tokenId) {
    require(_isApprovedOrOwner(msg.sender, tokenId), 'Not approved');
    _;
}

modifier myModifier() {
    // Logic to be executed before the function body
    _; // Indicates the function's main logic
    // Logic to be executed after the function body
}

In Solidity, isAuthorizedForToken is a modifier. It is used to execute additional logic before or after a function. Modifiers are commonly used to implement permission checks, state validation, or other preconditions.