{"id":21936164,"url":"https://github.com/learnweb3dao/advanced-solidity-topics","last_synced_at":"2025-07-31T14:12:47.022Z","repository":{"id":39975566,"uuid":"445246384","full_name":"LearnWeb3DAO/Advanced-Solidity-Topics","owner":"LearnWeb3DAO","description":null,"archived":false,"fork":false,"pushed_at":"2022-06-21T10:00:47.000Z","size":224,"stargazers_count":8,"open_issues_count":1,"forks_count":7,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-08T10:30:50.452Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LearnWeb3DAO.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-01-06T17:00:40.000Z","updated_at":"2024-01-08T10:23:08.000Z","dependencies_parsed_at":"2022-09-17T17:04:15.908Z","dependency_job_id":null,"html_url":"https://github.com/LearnWeb3DAO/Advanced-Solidity-Topics","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/LearnWeb3DAO/Advanced-Solidity-Topics","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LearnWeb3DAO%2FAdvanced-Solidity-Topics","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LearnWeb3DAO%2FAdvanced-Solidity-Topics/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LearnWeb3DAO%2FAdvanced-Solidity-Topics/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LearnWeb3DAO%2FAdvanced-Solidity-Topics/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LearnWeb3DAO","download_url":"https://codeload.github.com/LearnWeb3DAO/Advanced-Solidity-Topics/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LearnWeb3DAO%2FAdvanced-Solidity-Topics/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268053783,"owners_count":24188094,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-31T02:00:08.723Z","response_time":66,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-29T01:13:26.005Z","updated_at":"2025-07-31T14:12:47.004Z","avatar_url":"https://github.com/LearnWeb3DAO.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Advanced Solidity Topics\n\nIn the Freshman track, we looked at some basic Solidity syntax. We covered variables, data types, functions, loops, conditional flows, and arrays. \n\nHowever, Solidity has a few more things, things which will be important through the coding assignments of the Sophomore track and beyond. In this tutorial, we will cover some more important Solidity topics.\n\n## Prefer a Video?\nIf you would rather learn from a video, we have a recording available of this tutorial on our YouTube. It is split into 2 parts, and there are timestamps. Watch the video by clicking on the screenshot below, or go ahead and read the tutorial!\n\n#### Part 1\n[![Part \n#1](https://raw.githubusercontent.com/LearnWeb3DAO/Advanced-Solidity-Topics/main/assets/video-preview1.webp)](https://www.youtube.com/watch?v=Z5P3rKBRmEM)\n#### Part 2\n[![Part \n#2](https://raw.githubusercontent.com/LearnWeb3DAO/Advanced-Solidity-Topics/main/assets/video-preview2.webp)](https://www.youtube.com/watch?v=ILY3fIbwjk0)\n\n\n## Index\n- [Mappings](#mappings)\n- [Enums](#enums)\n- [Structs](#structs)\n- [View and Pure Functions](#view-and-pure-functions)\n- [Function Modifiers](#function-modifiers)\n- [Events](#events)\n- [Constructors](#constructors)\n- [Inheritance](#inheritance)\n- [Transferring ETH](#transferring-eth)\n- [Calling external contracts](#calling-external-contracts)\n- [Import statements](#import-statements)\n- [Solidity Libraries](#solidity-libraries)\n\n## Mappings\nMappings in Solidity act like hashmaps or dictionaries in other programming languages. They are used to store the data in key-value pairs. \n\nMappings are created with the syntax `mapping (keyType =\u003e valueType)`\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ncontract Mapping {\n    // Mapping from address to uint\n    mapping(address =\u003e uint) public myMap;\n    \n    function get(address _addr) public view returns (uint) {\n        // Mapping always returns a value.\n        // If the value was never set, it will return the default value.\n        // The default value for uint is 0\n        return myMap[_addr];\n    }\n\n    function set(address _addr, uint _i) public {\n        // Update the value at this address\n        myMap[_addr] = _i;\n    }\n\n    function remove(address _addr) public {\n        // Reset the value to the default value.\n        delete myMap[_addr];\n    }\n}\n```\n\nWe can also create nested mappings, where the `key` points to a second nested mapping. To do this, we set the `valueType` to a mapping itself.\n\n```solidity\ncontract NestedMappings {\n    // Mapping from address =\u003e (mapping from uint to bool)\n    mapping(address =\u003e mapping(uint =\u003e bool)) public nestedMap;\n    \n    function get(address _addr1, uint _i) public view returns (bool) {\n        // You can get values from a nested mapping\n        // even when it is not initialized\n        // The default value for a bool type is false\n        return nestedMap[_addr1][_i];\n    }\n\n    function set(\n        address _addr1,\n        uint _i,\n        bool _boo\n    ) public {\n        nestedMap[_addr1][_i] = _boo;\n    }\n\n    function remove(address _addr1, uint _i) public {\n        delete nestedMap[_addr1][_i];\n    }\n}\n```\n\n\u003cQuiz questionId=\"dc7e7818-b3bc-4e10-8cbd-1c2e497fb270\" /\u003e\n\n## Enums\nThe word `Enum` stands for `Enumerable`. They are user defined types that contain human readable names for a set of constants, called members. They are commonly used to restrict a variable to only have one of a few predefined values. Since they are just an abstraction for human readable constants, in actuality, they are internally represented as `uint`s.\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ncontract Enum {\n    // Enum representing different possible shipping states\n    enum Status {\n        Pending,\n        Shipped,\n        Accepted,\n        Rejected,\n        Canceled\n    }\n    \n    // Declare a variable of the type Status\n    // This can only contain one of the predefined values\n    Status public status;\n    \n    // Since enums are internally represented by uints\n    // This function will always return a uint\n    // Pending = 0\n    // Shipped = 1\n    // Accepted = 2\n    // Rejected = 3\n    // Canceled = 4\n    // Value higher than 4 cannot be returned\n    function get() public view returns (Status) {\n        return status;\n    }\n    \n    // Pass a uint for input to update the value\n    function set(Status _status) public {\n        status = _status;\n    }\n    \n    // Update value to a specific enum members\n    function cancel() public {\n        status = Status.Canceled; // Will set status = 4\n    }\n}\n```\n\n\u003cQuiz questionId=\"50b8f51f-5ee7-4e4d-b9c6-4e2ad54bf5a3\" /\u003e\n\n## Structs\nThe concept of structs exists in many high level programming languages. They are used to define your own data types which group together related data. \n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ncontract TodoList {\n    // Declare a struct which groups together two data types\n    struct TodoItem {\n        string text;\n        bool completed;\n    }\n    \n    // Create an array of TodoItem structs\n    TodoItem[] public todos;\n    \n    function createTodo(string memory _text) public {\n        // There are multiple ways to initialize structs\n        \n        // Method 1 - Call it like a function\n        todos.push(TodoItem(_text, false));\n        \n        // Method 2 - Explicitly set its keys\n        todos.push(TodoItem({ text: _text, completed: false }));\n        \n        // Method 3 - Initialize an empty struct, then set individual properties\n        TodoItem memory todo;\n        todo.text = _text;\n        todo.completed = false;\n        todos.push(todo);\n    }\n    \n    // Update a struct value\n    function update(uint _index, string memory _text) public {\n        todos[_index].text = _text;\n    }\n    \n    // Update completed\n    function toggleCompleted(uint _index) public {\n        todos[_index].completed = !todos[_index].completed;\n    }\n}\n```\n\n\u003cQuiz questionId=\"f1d1f4fd-7b3c-4749-a13c-5773cbff8915\" /\u003e\n\n## View and Pure Functions\nYou might have noticed that some of the functions we have been writing specify one of either a `view` or `pure` keyword in the function header. These are special keywords which indicate specific behavior for the function.\n\nGetter functions (those which return values) can be declared either `view` or `pure`. \n- `View`: Functions which do not change any state values\n- `Pure`: Functions which do not change any state values, but also don't read any state values\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ncontract ViewAndPure {\n    // Declare a state variable\n    uint public x = 1;\n    \n    // Promise not to modify the state (but can read state)\n    function addToX(uint y) public view returns (uint) {\n        return x + y;\n    }\n    \n    // Promise not to modify or read from state\n    function add(uint i, uint j) public pure returns (uint) {\n        return i + j;\n    }\n}\n```\n\n\u003cQuiz questionId=\"b80ae48a-1064-4b65-b3ce-eed33af1b22a\" /\u003e\n\u003cQuiz questionId=\"b0b996d9-67df-415c-8083-0da0021c7212\" /\u003e\n\n## Function Modifiers\nModifiers are code that can be run before and/or after a function call. They are commonly used for restricting access to certain functions, validating input parameters, protecting against certain types of attacks, etc.\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ncontract Modifiers {\n    address public owner;\n    \n    constructor() {\n        // Set the contract deployer as the owner of the contract\n        owner = msg.sender;\n    }\n    \n    // Create a modifier that only allows a function to be called by the owner\n    modifier onlyOwner() {\n        require(msg.sender == owner, \"You are not the owner\");\n        \n        // Underscore is a special character used inside modifiers\n        // Which tells Solidity to execute the function the modifier is used on\n        // at this point\n        // Therefore, this modifier will first perform the above check\n        // Then run the rest of the code\n        _;\n    }\n    \n    // Create a function and apply the onlyOwner modifier on it\n    function changeOwner(address _newOwner) public onlyOwner {\n        // We will only reach this point if the modifier succeeds with its checks\n        // So the caller of this transaction must be the current owner\n        owner = _newOwner;\n    }\n}\n```\n\n\u003cQuiz questionId=\"39766f33-9b10-49ec-9215-464161846f63\" /\u003e\n\n## Events\nEvents allow contracts to perform logging on the Ethereum blockchain. Logs for a given contract can be parsed later to perform updates on the frontend interface, for example. They are commonly used to allow frontend interfaces to listen for specific events and update the user interface, or used as a cheap form of storage.\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ncontract Events {\n    // Declare an event which logs an address and a string\n    event TestCalled(address sender, string message);\n    \n    function test() public {\n        // Log an event \n        emit TestCalled(msg.sender, \"Someone called test()!\");\n    }\n}\n```\n\n\u003cQuiz questionId=\"0a8eb990-d7c8-4bb6-bd33-eb02691e21c5\" /\u003e\n\n## Constructors\nA `constructor` is an optional function that is executed when the contract is first deployed. You can also pass arguments to constructors.\n\nP.S. - If you remember, we actually used constructors in the Freshman track Cryptocurrency and NFT tutorials!\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ncontract X {\n    string public name;\n    \n    // You will need to provide a string argument when deploying the contract\n    constructor(string memory _name) {\n        // This will be set immediately when the contract is deployed\n        name = _name;\n    }\n}\n```\n\n\u003cQuiz questionId=\"bcb76e7c-f4dd-4be3-b73d-78ab3701e973\" /\u003e\n\n## Inheritance\nInheritance is the procedure by which one contract can inherit the attributes and methods of another contract. Solidity supports multiple inheritance. Contracts can inherit other contract by using the `is` keyword. \n\nNote: We actually also did Inheritance in the Freshman Track Cryptocurrency and NFT tutorials - where we inherited from the `ERC20` and `ERC721` contracts respectively.\n\nA parent contract which has a function that can be overridden by a child contract must be declared as a `virtual` function.\n\nA child contract that is going to override a parent function must use the `override` keyword.\n\nThe order of inheritance matters if parent contracts share methods or attributes by the same name.\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\n/* Graph of inheritance\n    A\n   / \\\n  B   C\n /   /\nD   E\n\n*/\n\ncontract A {\n    // Declare a virtual function foo() which can be overridden by children\n    function foo() public pure virtual returns (string memory) {\n        return \"A\";\n    }\n}\n\ncontract B is A {\n    // Override A.foo();\n    // But also allow this function to be overridden by further children\n    // So we specify both keywords - virtual and override\n    function foo() public pure virtual override returns (string memory) {\n        return \"B\";\n    }\n}\n\ncontract C is A {\n    // Similar to contract B above\n    function foo() public pure virtual override returns (string memory) {\n        return \"C\";\n    }\n}\n\n// When inheriting from multiple contracts, if a function is defined multiple times, the right-most parent contract's function is used.\ncontract D is B, C {\n    // D.foo() returns \"C\"\n    // since C is the right-most parent with function foo();\n    // override (B,C) means we want to override a method that exists in two parents\n    function foo() public pure override (B, C) returns (string memory) {\n        // super is a special keyword that is used to call functions\n        // in the parent contract\n        return super.foo();\n    }\n}\n\ncontract E is C, B {\n    // E.foo() returns \"B\"\n    // since B is the right-most parent with function foo();\n    function foo() public pure override (C, B) returns (string memory) {\n        return super.foo();\n    }\n}\n\n```\n\n\u003cQuiz questionId=\"c89ae39d-611a-40e6-a506-c4a047e2f951\" /\u003e\n\n## Transferring ETH\nThere are three ways to transfer ETH from a contract to some other address. However, two of them are no longer recommended methods by Solidity in latest versions, therefore we shall skip those.\n\nCurrently, the recommended way to transfer ETH from a contract is to use the `call` function. The `call` function returns a `bool` indicating success or failure of the transfer.\n\n### How to receive Ether in a regular Ethereum account address\nIf transferring ETH to a regular account (like a Metamask address), you do not need to do anything special as all such accounts can automatically accept ETH transfers.\n\n### How to receive Ether in a contract\nHowever, if you are writing a contract that you want to be able to receive ETH transfers directly, you must have at least one of the functions below\n- `receive() external payable`\n- `fallback() external payable`\n\n`receive()` is called if `msg.data` is an empty value, and `fallback()` is used otherwise.\n\n\u003e `msg.data` is a way to specify arbitrary data along with a transaction. You will usually not be using it manually.\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ncontract ReceiveEther {\n    /*\n    Which function is called, fallback() or receive()?\n\n           send Ether\n               |\n         msg.data is empty?\n              / \\\n            yes  no\n            /     \\\nreceive() exists?  fallback()\n         /   \\\n        yes   no\n        /      \\\n    receive()   fallback()\n    */\n\n    // Function to receive Ether. msg.data must be empty\n    receive() external payable {}\n\n    // Fallback function is called when msg.data is not empty\n    fallback() external payable {}\n\n    function getBalance() public view returns (uint) {\n        return address(this).balance;\n    }\n}\n\ncontract SendEther {\n    function sendEth(address payable _to) public payable {\n        // Just forward the ETH received in this payable function\n        // to the given address\n        uint amountToSend = msg.value;\n        // call returns a bool value specifying success or failure\n        (bool success, bytes memory data) = _to.call{value: msg.value}(\"\");\n        require(success == true, \"Failed to send ETH\");\n    }\n}\n```\n\n\u003cQuiz questionId=\"5c21f649-71a8-4216-a36e-e342a75c16cc\" /\u003e\n\u003cQuiz questionId=\"7852870f-423e-4265-a144-cef24af6d15e\" /\u003e\n\n## Calling External Contracts\nContracts can call other contracts by just calling functions on an instance of the other contract like `A.foo(x, y, z)`. To do so, you must have an interface for `A` which tells your contract which functions exist. Interfaces in Solidity behave like header files, and serve similar purposes to the ABI we have been using when calling contracts from the frontend. This allows a contract to know how to encode and decode function arguments and return values for calling external contracts.\n\nNote: Interfaces you use do not need to be extensive. i.e. they do not need to necessarily contain all the functions that exist in the external contract - only those which you might be calling at some point. \n\nAssume there is an external `ERC20` contract, and we are interested in calling the `balanceOf` function to check the balance of a given address from our contract. \n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ninterface MinimalERC20 {\n    // Just include the functions we are interested in\n    // in the interface\n    function balanceOf(address account) external view returns (uint256);\n}\n\ncontract MyContract {\n    MinimalERC20 externalContract;\n    \n    constructor(address _externalContract) {\n        // Initialize a MinimalERC20 contract instance\n        externalContract = MinimalERC20(_externalContract);\n    }\n    \n    function mustHaveSomeBalance() public {\n        // Require that the caller of this transaction has a non-zero\n        // balance of tokens in the external ERC20 contract\n        uint balance = externalContract.balanceOf(msg.sender);\n        require(balance \u003e 0, \"You don't own any tokens of external contract\");\n    }\n}\n```\n\n## Import Statements\nTo maintain code readability, you can split your Solidity code over multiple files. Solidity allows importing both local and external files.\n\n### Local Imports\nAssume we have a folder structure like this:\n```\n├── Import.sol\n└── Foo.sol\n```\n\nwhere `Foo.sol` is \n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\ncontract Foo {\n    string public name = \"Foo\";\n}\n```\n\nWe can import `Foo` and use it in `Import.sol` as such\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\n// import Foo.sol from current directory\nimport \"./Foo.sol\";\n\ncontract Import {\n    // Initialize Foo.sol\n    Foo public foo = new Foo();\n\n    // Test Foo.sol by getting it's name.\n    function getFooName() public view returns (string memory) {\n        return foo.name();\n    }\n}\n```\n\nNOTE: When we use Hardhat, we can also install contracts as node modules through `npm`, and then import contracts from the `node_modules` folder. These also count as local imports, as technically when you install a package you are downloading the contracts to your local machine.\n\n### External Imports\nYou can also import from Github by simply copying the URL. We did this in the Cryptocurrency and NFT tutorials in the Freshman track.\n\n```solidity\n// https://github.com/owner/repo/blob/branch/path/to/Contract.sol\nimport \"https://github.com/owner/repo/blob/branch/path/to/Contract.sol\";\n\n// Example import ERC20.sol from openzeppelin-contract repo\n// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol\nimport \"https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol\";\n\n```\n\n## Solidity Libraries\nLibraries are similar to contracts in Solidity, with a few limitations. Libraries cannot contain any state variables, and cannot transfer ETH.\n\nTypically, libraries are used to add helper functions to your contracts. An extremely commonly used library in Solidity world is `SafeMath` - which ensures that mathematical operations do not cause an integer underflow or overflow.\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nlibrary SafeMath {\n    function add(uint x, uint y) internal pure returns (uint) {\n        uint z = x + y;\n        // If z overflowed, throw an error\n        require(z \u003e= x, \"uint overflow\");\n        return z;\n    }\n}\n\ncontract TestSafeMath {\n    function testAdd(uint x, uint y) public pure returns (uint) {\n        return SafeMath.add(x, y);\n    }\n}\n```\n\n\u003cQuiz questionId=\"297f987e-5948-413a-84ec-96499db914f2\" /\u003e\n\nThat's all folks! Congratulations on making it this far :D\n\n\u003cSubmitQuiz /\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flearnweb3dao%2Fadvanced-solidity-topics","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flearnweb3dao%2Fadvanced-solidity-topics","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flearnweb3dao%2Fadvanced-solidity-topics/lists"}