• solidity开发讲解


    solidity 教程链接

    1. 源文件结构

    源文件中可以包含任意多个 合约定义 、导入源文件指令 、 版本标识 指令、 结构体 、 枚举 和 函数 定义.

    SPDX许可标识
    SPDX:The Software Package Data Exchange
    常见开源:
    // SPDX-License-Identifier: MIT
    私有或者无授权:
    // SPDX-License-Identifier: UNLICENSED
    版本标识
    pragma solidity ^0.8.4;或者 pragma solidity >=0.4.16 <0.9.0;
    ABICoder Pragma
    Solidity 0.7.4 之前:pragma experimental ABIEncoderV2
    Solidity 0.7.4 之后:pragma abicoder v2

    导入文件

    import "filename";
    示例:
    import "./helper.sol";
    
    • 1
    • 2
    • 3

    注释

    // This is a single-line comment.
    
    /*
    This is a
    multi-line comment.
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2. 合约结构

    状态变量

    // SPDX-License-Identifier: GPL-3.0
    pragma solidity >=0.4.0 <0.9.0;
    
    contract SimpleStorage {
        uint storedData; // State variable
        // ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    函数

    / SPDX-License-Identifier: GPL-3.0
    pragma solidity >0.7.0 <0.9.0;
    
    contract TinyAuction {
        function Mybid() public payable { // 定义函数
            // ...
        }
    }
    
    // Helper function defined outside of a contract
    function helper(uint x) pure returns (uint) {
        return x * 2;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    函数修饰器

    // SPDX-License-Identifier: GPL-3.0
    pragma solidity >=0.4.22 <0.9.0;
    
    contract Purchase {
        address public seller;
    
        modifier onlySeller() { // Modifier
            require(
                msg.sender == seller,
                "Only seller can call this."
            );
            _;
        }
    
        function abort() public view onlySeller { // Modifier usage
            // ...
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    事件

    // SPDX-License-Identifier: GPL-3.0
    pragma solidity >=0.4.21 <0.9.0;
    
    contract SimpleAuction {
        event HighestBidIncreased(address bidder, uint amount); // Event
    
        function bid() public payable {
            // ...
            emit HighestBidIncreased(msg.sender, msg.value); // Triggering event
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    异常处理

    使用revert或者require(推荐)

    // SPDX-License-Identifier: GPL-3.0
    pragma solidity ^0.8.4;
    
    /// Not enough funds for transfer. Requested `requested`,
    /// but only `available` available.
    error NotEnoughFunds(uint requested, uint available);
    
    contract Token {
        mapping(address => uint) balances;
        function transfer(address to, uint amount) public {
            uint balance = balances[msg.sender];
            if (balance < amount)
                revert NotEnoughFunds(amount, balance);
            balances[msg.sender] -= amount;
            balances[to] += amount;
            // ...
        }
        
         function transfer2(address to, uint amount) public {
            uint balance = balances[msg.sender];
            require(balance > amount," balance must be greater than amount");
            balances[msg.sender] -= amount;
            balances[to] += amount;
            // ...
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    结构

    pragma solidity >=0.4.0 <0.9.0;
    
    contract Ballot {
        struct Voter { // 结构体
            uint weight;
            bool voted;
            address delegate;
            uint vote;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    枚举

    // SPDX-License-Identifier: GPL-3.0
    pragma solidity >=0.4.25 <0.9.0;
    
    contract Purchase {
        enum State { Created, Locked } // Enum
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3. 常用信息

    数据类型

    • 值类型:Booleans(true&false)、Integers(int8 to int256,uint8 to uint256)、Address(长度40位,20字节)、byte(byte1 to byte32)。
    • 引用类型:struct、bytes(byte[])、string、mapping。
      引用类型可以通过多个不同的名称修改它的值,而值类型的变量,每次都有独立的副本。因此,必须比值类型更谨慎地处理引用类型。 目前,引用类型包括结构,数组和映射,如果使用引用类型,则必须明确指明数据存储哪种类型的位置(空间)里:
      a. memory :数据在内存中,因此数据仅在其生命周期内(函数调用期间)有效。不能用于外部调用。
      b. storage :状态变量保存的位置,只要合约存在就一直存储.
      c. calldata:调用数据时用来保存函数参数的特殊数据位置,是一个只读位置,无需copy变量值到其他存储位置。
    • 类型修饰符:私有(private)、内部(internal)、公共(public)、外部(external);函数默认为internal,状态变量默认为private。另外函数还包含view(读取账本)和pure(不读取也不更改账本)。
      详解:
      private:仅内部访问。
      internal: 仅内部以及派生类访问。
      external:仅外部访问。
      public:内部和外部都可以访问。

    特殊变量和函数

    abi.encode(...) returns (bytes): ABI - 对给定参数进行编码
    abi.encodePacked(...) returns (bytes):对给定参数执行 紧打包编码
    abi.encodeWithSelector(bytes4 selector, ...) returns (bytes): ABI- 对给定参数进行编码,并以给定的函数选择器作为起始的 4 字节数据一起返回
    abi.encodeWithSignature(string signature, ...) returns (bytes):等价于 abi.encodeWithSelector(bytes4(keccak256(signature), ...)
    block.blockhash(uint blockNumber) returns (bytes32):指定区块的区块哈希——仅可用于最新的 256 个区块且不包括当前区块
    block.chainid (uint): 当前链 id
    block.coinbase ( address ): 挖出当前区块的矿工地址
    block.difficulty ( uint ): 当前区块难度
    block.gaslimit ( uint ): 当前区块 gas 限额
    block.number ( uint ): 当前区块号
    block.timestamp ( uint): 自 unix epoch 起始当前区块以秒计的时间戳
    gasleft() returns (uint256) :剩余的 gas
    msg.data ( bytes ): 完整的 calldata
    msg.sender ( address ): 消息发送者(当前调用)
    msg.sig ( bytes4 ): calldata 的前 4 字节(也就是函数标识符)
    msg.value ( uint ): 随消息发送的 wei 的数量
    tx.gasprice (uint): 交易的 gas 价格
    tx.origin (address payable): 交易发起者(完全的调用链)
    address(this):当前合约的地址
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    内联汇编

    通常我们通过库代码,来增强语言我,实现一些精细化的控制,Solidity为我们提供了一种接近于EVM底层的语言,内联汇编,允许与Solidity结合使用。由于EVM是栈式的,所以有时定位栈比较麻烦,Solidty的内联汇编为我们提供了下述的特性,来解决手写底层代码带来的各种问题:
    
    允许函数风格的操作码:mul(1, add(2, 3))等同于push1 3 push1 2 add push1 1 mul
    内联局部变量:let x := add(2, 3) let y := mload(0x40) x := add(x, y)
    可访问外部变量:function f(uint x) { assembly { x := sub(x, 1) } }
    标签:let x := 10 repeat: x := sub(x, 1) jumpi(repeat, eq(x, 0))
    循环:for { let i := 0 } lt(i, x) { i := add(i, 1) } { y := mul(2, y) }
    switch语句:switch x case 0 { y := mul(x, 2) } default { y := 0 }
    函数调用:function f(x) -> y { switch x case 0 { y := 1 } default { y := mul(x, f(sub(x, 1))) } }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    pragma solidity ^0.4.0;
    
    library GetCode {
        function at(address _addr) returns (bytes o_code) {
            assembly {
                // retrieve the size of the code, this needs assembly
                let size := extcodesize(_addr)
                // allocate output byte array - this could also be done without assembly
                // by using o_code = new bytes(size)
                o_code := mload(0x40)
                // new "memory end" including padding
                mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
                // store length in memory
                mstore(o_code, size)
                // actually retrieve the code, this needs assembly
                extcodecopy(_addr, add(o_code, 0x20), 0, size)
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    合约

    Solidity中合约有点类似面向对象语言中的类。合约中有用于数据持久化的状态变量(state variables),和可以操作他们的函数。调用另一个合约实例的函数时,会执行一个EVM函数调用,这个操作会切换执行时的上下文,这样,前一个合约的状态变量(state variables)就不能访问了。

    创建合约

    合约可以通过Solidity,或不通过Solidity创建。当合约创建时,一个和合约同名的函数(构造器函数)会调用一次,用于初始化。
    构造器函数是可选的。仅能有一个构造器,所以不支持重载。
    如果不通过Solidity,我们可以通过web3.js,使用JavaScript的API来完成合约创建:

    pragma solidity ^0.4.0;
    
    contract OwnedToken {
        // TokenCreator is a contract type that is defined below.
        // It is fine to reference it as long as it is not used
        // to create a new contract.
        TokenCreator creator;
        address owner;
        bytes32 name;
    
        // This is the constructor which registers the
        // creator and the assigned name.
        function OwnedToken(bytes32 _name) {
            // State variables are accessed via their name
            // and not via e.g. this.owner. This also applies
            // to functions and especially in the constructors,
            // you can only call them like that ("internall"),
            // because the contract itself does not exist yet.
            owner = msg.sender;
            // We do an explicit type conversion from `address`
            // to `TokenCreator` and assume that the type of
            // the calling contract is TokenCreator, there is
            // no real way to check that.
            creator = TokenCreator(msg.sender);
            name = _name;
        }
    
        function changeName(bytes32 newName) {
            // Only the creator can alter the name --
            // the comparison is possible since contracts
            // are implicitly convertible to addresses.
            if (msg.sender == address(creator))
                name = newName;
        }
    
        function transfer(address newOwner) {
            // Only the current owner can transfer the token.
            if (msg.sender != owner) return;
            // We also want to ask the creator if the transfer
            // is fine. Note that this calls a function of the
            // contract defined below. If the call fails (e.g.
            // due to out-of-gas), the execution here stops
            // immediately.
            if (creator.isTokenTransferOK(owner, newOwner))
                owner = newOwner;
        }
    }
    
    contract TokenCreator {
        function createToken(bytes32 name)
           returns (OwnedToken tokenAddress)
        {
            // Create a new Token contract and return its address.
            // From the JavaScript side, the return type is simply
            // "address", as this is the closest type available in
            // the ABI.
            return new OwnedToken(name);
        }
    
        function changeName(OwnedToken tokenAddress, bytes32 name) {
            // Again, the external type of "tokenAddress" is
            // simply "address".
            tokenAddress.changeName(name);
        }
    
        function isTokenTransferOK(
            address currentOwner,
            address newOwner
        ) returns (bool ok) {
            // Check some arbitrary condition.
            address tokenAddress = msg.sender;
            return (keccak256(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    库(Libraries)

    库与合约类似,但它的目的是在一个指定的地址,且仅部署一次,然后通过EVM的特性DELEGATECALL(Homestead之前是用CALLCODE)来复用代码。这意味着库函数调用时,它的代码是在调用合约的上下文中执行。使用this将会指向到调用合约,而且可以访问调用合约的存储(storage)。因为一个合约是一个独立的代码块,它仅可以访问调用合约明确提供的状态变量(state variables),否则除此之外,没有任何方法去知道这些状态变量。

    使用库合约的合约,可以将库合约视为隐式的父合约(base contracts),当然它们不会显式的出现在继承关系中。但调用库函数的方式非常类似,如库L有函数f(),使用L.f()即可访问。此外,internal的库函数对所有合约可见,如果把库想像成一个父合约就能说得通了。当然调用内部函数使用的是internal的调用惯例,这意味着所有internal类型可以传进去,memory类型则通过引用传递,而不是拷贝的方式。为了在EVM中实现这一点,internal的库函数的代码和从其中调用的所有函数将被拉取(pull into)到调用合约中,然后执行一个普通的JUMP来代替DELEGATECALL。

    pragma solidity ^0.4.0;
    
    library Set {
      // We define a new struct datatype that will be used to
      // hold its data in the calling contract.
      struct Data { mapping(uint => bool) flags; }
    
      // Note that the first parameter is of type "storage
      // reference" and thus only its storage address and not
      // its contents is passed as part of the call.  This is a
      // special feature of library functions.  It is idiomatic
      // to call the first parameter 'self', if the function can
      // be seen as a method of that object.
      function insert(Data storage self, uint value)
          returns (bool)
      {
          if (self.flags[value])
              return false; // already there
          self.flags[value] = true;
          return true;
      }
    
      function remove(Data storage self, uint value)
          returns (bool)
      {
          if (!self.flags[value])
              return false; // not there
          self.flags[value] = false;
          return true;
      }
    
      function contains(Data storage self, uint value)
          returns (bool)
      {
          return self.flags[value];
      }
    }
    
    
    contract C {
        Set.Data knownValues;
    
        function register(uint value) {
            // The library functions can be called without a
            // specific instance of the library, since the
            // "instance" will be the current contract.
            if (!Set.insert(knownValues, value))
                throw;
        }
        // In this contract, we can also directly access knownValues.flags, if we want.
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    接口

    /*接口与抽象合约类似,与之不同的是,接口内没有任何函数是已实现的,同时还有如下限制:
    不能继承其它合约,或接口。
    不能定义构造器
    不能定义变量
    不能定义结构体
    不能定义枚举类*/
    interface Token {
        function transfer(address recipient, uint amount);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    抽象(Abstract Contracts)

    pragma solidity ^0.4.0;
    
    contract Feline {
        function utterance() returns (bytes32);
        
        function getContractName() returns (string){
            return "Feline";
        }
    }
    
    
    contract Cat is Feline {
        function utterance() returns (bytes32) { return "miaow"; }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4. 示例

    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.4;
    
    // pragma experimental ABIEncoderV2;
    
    import "./helper.sol";
    
    /**
     * @title Storage
     * @dev Store & retrieve value in a variable
     */
    contract Storage {
    
        address public seller;
        constructor(){
            seller = msg.sender;
        }
        
        modifier onlySeller() { // Modifier
            require(
                msg.sender == seller,
                "Only seller can call this."
            );
            _;
        }
    
        uint256 number;
        
        event storeEvent(string name,uint256 value);
        /**
         * @dev Store value in variable
         * @param num value to store
         */
        function store(uint256 num) public {
            number = num;
            emit storeEvent("number",number);
        }
    
        /**
         * @dev Return value 
         * @return value of 'number'
         */
        function retrieve() public view returns (uint256){
            return number;
        }
        
        
        // only call by seller
        function storeOnlySeller(uint256 num) public onlySeller {
            // number = num;
            number = Helper.add(num,1);
            emit storeEvent("number",number);
        }
        
         // destory the contract
        function destory() public onlySeller {
            selfdestruct(payable(msg.sender));
        }
        
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.4;
    
    library Helper {
    
        function add(uint256 a,uint256 b) public pure returns(uint256) {
            return (a+b);
        }
        
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    5.合约编写

    pragma solidity >=0.4.22 <0.5.0;
    
    
    //ERC20协议接口
    contract ERC20Interface{
    
        function totalSupply() public constant returns(uint);
        
        function balanceOf(address tokenOwner) public constant returns(uint balance) ;
        
        function allowance(address tokenOwner,address spender) public constant returns(uint remainIng);
        
        function transfer(address to,uint tokens) public returns(bool success);
        
        function approve(address spender,uint tokens) public returns(bool success);
        
        function transferFrom(address from,address to,uint tokens) public returns(bool success);
        
        event Transfer(address indexed from,address indexed to, uint tokens);
        
        event Approval(address indexed tokenOwner,address indexed spender,uint tokens);
        
    }
    
    /**
     * @title SafeMath 安全计算,避免溢出
     * @dev Math operations with safety checks that throw on error
     */
    library SafeMath {
    
      /**
      * @dev Multiplies two numbers, throws on overflow.
      */
      function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) {
          return 0;
        }
        uint256 c = a * b;
        assert(c / a == b);
        return c;
      }
    
      /**
      * @dev Integer division of two numbers, truncating the quotient.
      */
      function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // assert(b > 0); // Solidity automatically throws when dividing by 0
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        return c;
      }
    
      /**
      * @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
      */
      function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
      }
    
      /**
      * @dev Adds two numbers, throws on overflow.
      */
      function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        assert(c >= a);
        return c;
      }
    }
    
    /**
     * @title Ownable 权限控制
     * @dev The Ownable contract has an owner address, and provides basic authorization control
     * functions, this simplifies the implementation of "user permissions".
     */
    contract Ownable {
      address public owner;
       //这里是个事件,供前端监听
      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
      /**
       * @dev The Ownable constructor sets the original `owner` of the contract to the sender
       * account.这是一个构造函数,在合约启动时只运行一次,将合约的地址赋给地址owner
       */
      function Ownable() public {
        owner = msg.sender;
      }
    
    
      /**
       * @dev Throws if called by any account other than the owner。这里就是modifier onlyOwner的修饰符,用来判定是否是合约的发布者
       * 修饰器,通常用于校验函数权限
       只有合约的拥有者才能执行函数
       */
      modifier onlyOwner() {
        require(msg.sender == owner);
        _;
      }
    
    
      /**
       * @dev Allows the current owner to transfer control of the contract to a newOwner.
       * @param newOwner The address to transfer ownership to.
       * 让合约拥有者修改指定新的合约拥有者,并调用事件来监听
       */
      function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0));
        OwnershipTransferred(owner, newOwner);
        owner = newOwner;
      }
    
    }
    
    
    contract bmtToken is ERC20Interface , Ownable{
        
        using SafeMath for uint;
        //代币名称
        string public name;
        //代币符号 ,比如 BTC
        string public symbol;
        //代币精度,比如 ETC 有18位
        uint8 public decimals;
        //发行总额
        uint public totalSupply;
        //地址 => 余额 映射关系
        mapping(address => uint) balances;
        //授权 地址A 授权给 地址B  多少个代币
        mapping(address => mapping(address => uint)) allowed;
        
        
        constructor() public {
            name = "BMT Token";
            
            symbol = "BMT";
            
            decimals = 18;
            
            totalSupply = 1000000000000000000;
            
            balances[msg.sender] = totalSupply;
            
            Transfer(address(0),msg.sender,totalSupply);
            
        }
        
        
        function totalSupply() public constant returns(uint){
            return totalSupply;
        }
        
        //获取账户余额
        function balanceOf(address tokenOwner) public constant returns(uint){
            return balances[tokenOwner];
        }
        
        
        //转账给地址to
        function transfer(address to, uint tokens) public returns (bool){
            
            balances[msg.sender] = balances[msg.sender].sub(tokens);
            balances[to] = balances[to].add(tokens);
            //触发事件
            Transfer(msg.sender,to,tokens);
            return true;
        }
        
        
         //被授权账户余额
        function allowance (address tokenOwner,address spender) public constant returns(uint){
            return allowed[tokenOwner][spender];
        }
        
        //授权给账户address tokens个代币
        function approve(address spender,uint tokens) public returns(bool){
            
            allowed[msg.sender][spender] = tokens;
            
            //触发事件
            Approval(msg.sender,spender,tokens);
            
            return true;
            
        }
        //from 转账给 to 
        function transferFrom(address from,address to,uint tokens) public returns(bool){
            
            allowed[from][to] = allowed[from][to].sub(tokens);
            
            balances[from] = balances[from].sub(tokens);
            
            balances[to] = balances[to].add(tokens);
            //触发事件
            Transfer(from,to,tokens);
            
            return true;
        }
        
        //niming匿名 huigun回滚 hanshu函数
        function () payable{
            //hanshu函数buneng不能 zhixing执行,hui会 huigun回滚 
            revert();
        }
        
        
        //合约 与 合约 之间交互,代币 与 代币 交易 
        function transferAnyERC20Token(address tokenAddr,uint tokens)public onlyOwner returns(bool) {
            
            //nenggou能够 jieshou接收接收renhe接收 shix实现ERCjiekou接口 d的 daibi代币 zhuangzhang转账 
    
            ERC20Interface(tokenAddr).transfer(msg.sender,tokens);
        }
        
       
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
  • 相关阅读:
    libevent源码跨平台编译(windows/macos/linux)
    java毕业设计教学平台(附源码、数据库)
    docker真实IP解决
    单片机学到什么程度才可以去工作?
    shell 脚本部署 helm
    SpringCloud - Spring Cloud Netflix 之 Zuul网关;路由(十一)
    科技“蝶变”,两轮电动车下一个五年的“新动力”
    UE4 C++ ActionRoguelike开发记录
    KVM虚拟化常见问题汇总
    禅道bug统计并发送钉钉通知
  • 原文地址:https://blog.csdn.net/weixin_47174945/article/details/125436534