# Learn Solidity Series 8: contract event

By [Renaissance Labs](https://paragraph.com/@renaissance-labs) · 2022-03-01

---

1\. create contract instance
----------------------------

创建合约的语法:

    new 合约名();
    
    示例：
    contract A {}
    contract B {
        function test() public { 
            A a = new A();
        }
    }
    

创建合约实例的时候，默认会调用构造函数，并执行构造函数中的代码。如果没有定义构造函数，EVM编译器会自动生成一个默认的构造函数。如果构造函数指定了参数，那么创建合约实例时候需要传入相同数量的实参。

    contract A {
        int num;
        constructor(int _num) public {
            num = _num;
        }
    }
    
    contract B { 
        function test() public { 
            A a = new A(10);
        }
    }
    

> 注意：合约构造函数是可选的。solidity不支持构造函数的重载，也就是一个合约最多只有一个构造函数。

2\. contract inherit
--------------------

> 一个合约可以继承另外一个合约，也可以同时继承多个合约

    contract A {}
    contract B is A {}
    

如果同时继承了多个父合约，最远的派生函数会被调用。

    
    contract C is A, B {}
    
    contract SimpleContract {
        function test() public returns(uint) {
            C c = new C();
            c.a(); // 20
            c.f(); // 20
        }
    }
    

上面合约C同时继承了A和B，因为B合约比A合约距离C合约远，所以在`test`函数中调用父合约状态变量和函数，实际调用的是B合约。

父合约的函数可以被派生合约中具有相同名称、相同参数的函数重载。如果派生合约的重载函数的返回值类型或数量不相同，会导致错误。

如果父合约构造函数带参数，那么可以在派生合约声明时候指定参数，或者也可以在构造函数位置以修饰器方式提供。

    contract A {
        int num;
        constructor(int _num) public {
            num = _num;
        }
    }
    
    contract B is A {}
    contract B is A(10) {}
    contract B is A {
        constructor() public A(10) {}
    }
    

3\. abstract contract
---------------------

如果一个派生合约没有指定所有父合约的构造函数参数，那么该合约是一个抽象合约。

    
    contract A {
        uint a；
        constructor(uint _a) internal {
            a = _a;
        }
    }
    
    contract B is A {}
    
    contract SimpleContract {
        function test() public { 
            //B a = new B();
        }
    }
    

因为合约B继承合约A时候，没有指定构造函数参数，因此合约B是一个抽象合约。抽象合约不能够实例化，因此上面`test`函数中注释代码会报错。

另外，如果一个合约包含未实现的函数，那么该合约也是一个抽象合约。

    contract A {
        function test() public;
    }
    

4\. interface
-------------

> 接口主要用于规范合约的实现，其定义格式为：
> 
> 定义接口使用`interface`关键字。接口里面只能够声明函数，不能定义状态变量和构造函数，也不能够对函数提供实现。

    interface InterfaceA {
        function f() external;
    }
    

上面接口函数`f()`必须要使用`external`修饰符。与Java不同，solidity接口之间无法继承。

![](https://storage.googleapis.com/papyrus_images/bfe3ead9dc54fbca81aeb626b93471e1210d9bf9c718e0451bc4b4e88975dc73.png)

一个合约可以继承多个接口。

    interface InterfaceA {}
    interface InterfaceB {}
    interface Impl is InterfaceA, InterfaceB {}
    

如果一个合约没有实现接口里面的所有函数，那么该合约是一个抽象合约。

👉抽象合约与接口的相同点和不同点？

1）抽象合约可以定义状态变量和构造函数，而接口不可以；

2）抽象合约中的函数可以不实现，也可以实现，而接口中只能包含未实现的函数；

3）抽象合约中的函数可以使用任意修饰符，但接口中函数只能使用external修饰符；

4）抽象合约可以继承其他合约或接口，而接口不能；

5）抽象合约和接口都不能实例化；

6）抽象合约和接口都可以声明结构体和枚举；

5\. lib合约
---------

库合约不同于一般合约，它更像Java里的工具类，负责为其他合约提供有用的工具函数。

定义语法：

    library 库合约 {}
    

库合约可以定义结构体、枚举，也可以声明状态变量，但是状态变量必须是常量。

像一般函数调用一样，库合约中的成员可以通过`库合约.成员`方式访问。

    library Utils {
        function sum(uint a, uint b) public pure returns(uint) {
            return a + b;
        }
    }
    
    library SimpleContract {
        function test() public pure {
            uint result = Utils.sum(10, 20);
        }
    }
    

如果在合约中包含了库合约，那么部署合约时候，EVM会自动将库合约代码包含进来，无需额外部署库合约。

除了上面调用方式以外，还可以通过`using...for`语法访问库合约里面的函数。该语法运行将库合约中的函数附加到任意类型上，从而实现类似原生函数调用的效果。

    library Utils {
        function get(int[] storage self, uint index) internal view returns(int) {
            require(index >= 0);
            return self[index];
        }
        
        function get(mapping(address => uint128) storage self) internal view returns(uint128) {
            require(msg.sender > address(0));
            return self[msg.sender];
        }
    }
    
    contract SimpleContract {
        int[] ids;
        mapping(address => uint128) balances;
        using Utils for int[];
        using Utils for mapping(address => uint128);
        
        function test() public view  {
            int id = ids.get(0);
            uint128 bal = balances.get();
        }
    }
    

上面库合约定义了两个重载方法。当使用`using..for`语法访问库合约时候，第一个参数为对应类型的状态变量。

上面代码通过using...for语法，将库合约绑定到uint\[\]和mapping(address => uint128)类型。因此，该合约中所有uint\[\]和mapping(address => uint128)类型的变量都可以库函数，这时候库函数的第一个参数引用了调用该函数的变量。

6.Event
-------

事件使得开发者可以访问EVM的日志系统，帮助开发者了解智能合约运行过程的状态信息。开发者可以在dapp中监听solidity事件，当智能合约中触发了某个事件，EVM的日志系统会反过来调用dapp中定义的事件回调函数，从而在回调函数中将日志信息打印出来。

记录在区块链上的日志信息可以被外部检索出来。solidity规定一个事件里面最多有3个参数可以设置为indexed，表示用户可以通过该参数对搜索结果进行过滤。所有非indexed参数将保存在日志的数据部分，而indexed参数不会被保存。

    contract SimpleContract {
        event LogSave(
        address indexed _addr,
        uint _value
        );
    
        function saveMoney() public payable {
        if (msg.value <= 100 wei) {
            emit LogSave(msg.sender, msg.value);
            }
        }
    }
    

上面合约定义了一个`LogSave`事件，其中第一个参数是可索引参数。在`saveMoney`函数中，判断如果`msg.value`小于等于100 wei，则触发`LogSave`事件。

---

*Originally published on [Renaissance Labs](https://paragraph.com/@renaissance-labs/learn-solidity-series-8-contract-event)*
