精华内容
下载资源
问答
  • Solidity学习常用网址

    2021-01-07 21:19:37
    Solidity 语法学习 学习网站:solidity官网,V0.6.2 https://solidity.readthedocs.io/en/v0.6.2/miscellaneous.html#index-6 新手编译网站:Remix:https://remix.ethereum.org/ 代码的安全性问题举例:...
  • Solidity学习杂记

    2020-11-09 15:30:45
    pragma solidity ^0.4.23; contract Hello{ uint[5] arr = [1,2,3,4,5]; function Init(){ arr[0] = 100; arr[2] = 200; } function getArr() view returns(uint[5]){ return arr; } function add() ...

    收藏

    数组

    pragma solidity ^0.4.23;
    
    contract Hello{
    
       uint[5] arr = [1,2,3,4,5];
       function Init(){
           arr[0] = 100;
           arr[2] = 200;
       }
       
       function getArr() view returns(uint[5]){
           return arr;
       }
       
       function add() view returns(uint){
           uint sum = 0;
           for(uint i = 0;i<5;i++){
               sum += arr[i]; 
           }
           return sum;
       }
    }
    

    结构体

    pragma solidity ^0.4.23;
    
    contract Hello{
            // struct 结构体里面不能包含本身,但是可以是动态长度的数组,也可是映射
        struct boy{
            uint money;
            string name;
            mapping(uint=>string) map;
        }
        boy my;  //默认为storage类型,只能够用storage类型来操作mapping
        //mapping 不能直接获取
        function init() view returns(uint,string,string){
            // boy b 是 storage 类型【在链上】  boy(1000,"zy") 是 memory类型【在内存里】 所以要转成memory才能使用 
            boy memory b = boy(1000,"zy");
            my.map[0] = "Hello";
            return(b.money,b.name,my.map[0]); 
        }
        
         //结构体作为参数必须是internal
        function test(boy memory b) internal{
        //注意 memory 到 memory是引用指针
            
        }
        function test2(boy storage b) internal{
            
        }
     
    }
    

    传址

    pragma solidity ^0.4.23;
    
    contract Hello{
    
        uint[] arrx;   //储存在区块链的网络上
        function test(uint[] arry) returns(uint){
            arrx = arry;  //将内存的array拷贝给区块链上的arrx
            
            //当我们在函数体内部定义了一个可变长度的数组时,实际上他默认类型是storage类型,他指向了区块链上的arrx,所以当我修改Z的元素的时候实际上我们在操作的是量上的arrx的值
            uint[] storage Z  = arrx;
            
            //通过指针实际上修改了区块量上的arrx的值
            Z[0] = 100;
            
            //修改了链上arrx的长度
            Z.length = 100;
        }
        
        function test2() returns(uint){
            return arrx[0];
        }
        
        function test3() returns(uint){
            return arrx.length;
        }
     
    }
    
    

    合约销毁

    pragma solidity ^0.4.23;
    
    contract Hello{
       address owner;
       constructor(){
        
         owner = msg.sender;
     }
     
     
     function kill(){
         if(msg.sender == owner){
             selfdestruct(owner);
         }
     }
     
    }
    
    

    继承

    • public 可以继承内外部都可以使用
    pragma solidity ^0.4.23;
    contract Hello{
        uint private money = 1000;   
      function echoStr() public pure returns(string){
       return 'str';
      }
      function echoExternal() external pure returns(string){
          return 'external';
          
      }
      
      function externalTest() public view returns(string){
         return this.echoExternal();
          
      }
    }
    
    
    contract Hello2 is Hello{
    
      
    
      function test() public view returns(string){
         return echoStr();
      }
    }
    
    
    • internal 只能在继承的合约内部或合约内部使用,不能在合约的外部使用
    • external 只能用this用来外部调用 或者 Hello h = new Hello();引入调用
    • private 只能被自己使用,不能被继承
    pragma solidity ^0.4.23;
    contract Hello{
    
      function echoStr() internal pure returns(string){
       return 'str';
      }
    
    }
    
    
    contract Hello2 is Hello{
    
      
    
      function test() public view returns(string){
         return echoStr();
      }
       function test2() public view returns(string){
            return '22222';
       }
       function test3()  public view returns(string){
            return  this. echoExternal();
       }
    
    }
    
    
    contract Hello3{
    
            Hello h = new Hello();
            
            function externalEcho() view returns(string){
              return  h.echoExternal() ;
            }
    
    
    }
    
    
    

    modifier

    
    pragma solidity ^0.4.23;
    contract Hello{
        
        address public owner;
        
         uint public num;    
         uint lv = 10;   
         
          constructor(){
            
             owner = msg.sender;
         }
         
     
             
         modifier OnlyOwner{
             require(msg.sender == owner);  //如果不是原来的合约拥有者就停掉
             
             _;
         }
         
        modifier OwnerLv( uint _lv){
             require(lv >= _lv);  
             
             _;
         }
         
        //合约的所有者才可以修改
        function chabgeIt(uint _num) OnlyOwner OwnerLv(10){
            num = _num;
        }
    
    }
    
    
    

    mapping

    pragma solidity ^0.4.0;
    contract Hello{
        //映射
        mapping(address => uint) IdMapping;
        mapping(uint => string) NameMapping;
        
        uint public sum = 0;
        
        //防止重复注册
        modifier conrol{
               require(IdMapping[msg.sender] == 0);
               _;
        }
    
        function register(string name) conrol{
            address account = msg.sender;
            sum++;
            IdMapping[account] = sum;
            NameMapping[sum] = name;
            
        }
        
        function getIdByAddress(address are) view returns(uint){
            return IdMapping[are];
        }
        
            
        function getNameById(uint id) view returns(string){
            
            return NameMapping[id];
            
        }
        
    }
    
    

    构造函数

    pragma solidity ^0.4.23;
    contract Hello{
        
        uint public a;
    
    //   function Hello(){
    //       a = 100;
    //   }
      
      
    //   function Hello(uint _a){
    //       a = _a;
    //   }
       
    //  constructor(){
    //       a = 100;
    //  }
     
     
      constructor(uint _a){
    
          a = _a;
     }
    }
    
    
    pragma solidity ^0.4.23;
    contract Hello{
        
          address public owner;
    
    
     
     
      constructor(){
        
         owner = msg.sender;
     }
    }
    
    

    函数命名参数

    pragma solidity ^0.4.23;
    contract Hello{
      uint public num;
      string public name;
      
      function setData(uint _num,string _name){
          num = _num;
          name = _name;
      }
      function test(){
          setData({_name:"zy",_num:5});
      }
    }
    
    

    函数的重载

        函数的重载
        1,函数的名字相同
        2,函数的类型数量不同。
            参数数量不同
                function func(uint num){}
                function func(){}
            参数类型不同
                function func(uint num){}
                function func(uint8 num){}
    
        3,不考虑函数的返回值是否相同
        
        address 和 uint160 要注意,虽然能通过编译,但是不能够被调用( address = uint160 无法判断该调用谁)
            function func(uint160 num){}
            function func(address num){}
    

    一些语法

    pragma solidity ^0.4.0;
    contract Hello{
        
        string Myname = "zy";  //    string 没有 length 属性  需要  bytes(_name).length;强转获得长度
        bool _a ;
        //  int 可正可负 int256     uint 只能正数  uint256
        int _num1 = 1 ;
        int _num2 = 200 ;
        uint _num3 = 2;
         
    
    
    
        /**
        * 权限     private internal external public
        * 标识符   pure constant view payable
        * view     可以自由调用,因为它只是“查看”区块链的状态而不改变它。不消耗燃料
        * pure     也可以自由调用,不写入区块链 不消耗燃料
        * payable  交易操作需要使用
        **/
        function getName() public view returns(string)
        {
            return Myname;
            
        }
        
        // 修改 消耗燃料
        function changeMyName(string _newName) public{
            Myname = _newName;
        }
    
        function pureTest(string _name) pure public returns(string){
            return _name;
        }
        
        
        function getBool() view public returns(bool){
            return _a;
        }
        
        
       function getBool2() view public returns(bool){
            return !_a;
        }
    
    
        function panduan() view public  returns(bool){
            return _num1 == _num2;
        }
        
        function panduan2() view public  returns(bool){
            return _num1 != _num2;
        }
        
        function yu() view public  returns(bool){
             return (_num1 == _num2) && true;
         }
            
        function huo() view public  returns(bool){
             return (_num1 == _num2)||true;
         }
         
        function add(uint a,uint b)pure public returns(uint){
             return a+b;
         }
         
         function fan() view public  returns(uint){
             return ~_num3;
         }
         
         function lyu() view public returns(int){
             return _num1<<1;
         }
         
         function flow() view public returns(uint){
             
             uint8 mm = 255;
             mm++;
             return mm;
             
         } 
         function flow2() view public returns(uint){
             
             uint8 mm = 0;
             mm--;
             return mm;
             
         } 
         
         function interTest() returns(uint){
             return 2/4*1000;
         }
         
         
         
         
         
         
        bytes1 public bynum1 = 0x7a; //    定义的时候加public会默认加个公共方法   1个字节  8位  bytes 一旦固定长度不得修改长度
            
        function getLength() returns(uint){
            return bynum1.length;
        }
        
        bytes public name = new bytes(2);
        
        function IniName(){
            name[0] = 0x7a;
            name[1] = 0x68; 
        }
        
        function getnameLength() view returns(uint){
            return name.length;
        } 
        
        function changeName(){
            name[0] = 0x88;
            
        }
        
        function changeLength(){
            name.length = 5; //设置了长度后值会右边填充0    
        }
        
        // 数组后面添加元素
        function pushtest(){
            name.push(0x99);
        }
    }
    

    payable

    pragma solidity ^0.4.0;
    contract Hello{
        
        address public account;  //   0x5B38Da6a701c568545dCfcB03FcB875f56beddC4  uint160
           // payable 关键字用来给合约地址充值转账
        function pay() payable{
            
        }
        
        function getBalance() view returns(uint){
            return this.balance;
        }
        
         // 获取合约地址
        function getThis() view returns(address){
            return this;
        }
        
        function getOneBalance() view returns(uint){
            address account = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
            return account.balance;
        }
        
        
          //  transfer转账 到某个账户  
        function transfer() payable{
                address account = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
                account.transfer(msg.value);
        }
        
             //  transfer转账 到合约中
        function transfer2() payable{
            
           this.transfer(msg.value);
        }
        //转账 到合约中 需要一个回滚函数,不然会报错
        function () payable{
            
        }
        
       function transfer3() payable{
                address account = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
                account.transfer(10 ether);
        }
        //send 不会报错 指会返回 true 或 false
         function sends() payable{
                address account = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
                account.send(10 ether);
        }
        
    }
    
    

    solstring

    pragma solidity ^0.4.0;
    contract Hello{
        
        string name = "zy";  //    string 没有 length 属性  需要  bytes(_name).length;强转获得长度
        string name2 = "!$%^&#@$ada";
        function getLenght() view returns(uint){
            return bytes(name).length;
        }
        function changeName() view returns (bytes1){
            return bytes(name)[1];
        }
        function getName() view returns(bytes){
            return bytes(name);
        } 
        
        function changeName2(){
            bytes(name)[0] = 'C';
        }
        
           function getLenght2() view returns(uint){
            return bytes(name2).length;
        }
    }
    

    发布测试代币

    pragma solidity ^0.4.24;
    
    // ----------------------------------------------------------------------------
    // 'FIXED' 'Example Fixed Supply Token' token contract
    //
    // Symbol      : USDT
    // Name        : USDT
    // Total supply: 1,000,000,000.0000  发型代币数量 
    // Decimals    : 4    小数点 
    //
    // Enjoy.
    //
    // (c) BokkyPooBah / Bok Consulting Pty Ltd 2018. The MIT Licence.
    // ----------------------------------------------------------------------------
    
    
    // ----------------------------------------------------------------------------
    // Safe maths
    // ----------------------------------------------------------------------------
    library SafeMath {
        function add(uint a, uint b) internal pure returns (uint c) {
            c = a + b;
            require(c >= a);
        }
        function sub(uint a, uint b) internal pure returns (uint c) {
            require(b <= a);
            c = a - b;
        }
        function mul(uint a, uint b) internal pure returns (uint c) {
            c = a * b;
            require(a == 0 || c / a == b);
        }
        function div(uint a, uint b) internal pure returns (uint c) {
            require(b > 0);
            c = a / b;
        }
    }
    
    
    // ----------------------------------------------------------------------------
    // ERC Token Standard #20 Interface
    // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
    // ----------------------------------------------------------------------------
    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);
    }
    
    
    // ----------------------------------------------------------------------------
    // Contract function to receive approval and execute function in one call
    //
    // Borrowed from MiniMeToken
    // ----------------------------------------------------------------------------
    contract ApproveAndCallFallBack {
        function receiveApproval(address from, uint256 tokens, address token, bytes data) public;
    }
    
    
    // ----------------------------------------------------------------------------
    // Owned contract
    // ----------------------------------------------------------------------------
    contract Owned {
        address public owner;
        address public newOwner;
    
        event OwnershipTransferred(address indexed _from, address indexed _to);
    
        constructor() public {
            owner = msg.sender;
        }
    
        modifier onlyOwner {
            require(msg.sender == owner);
            _;
        }
    
        function transferOwnership(address _newOwner) public onlyOwner {
            newOwner = _newOwner;
        }
        function acceptOwnership() public {
            require(msg.sender == newOwner);
            emit OwnershipTransferred(owner, newOwner);
            owner = newOwner;
            newOwner = address(0);
        }
    }
    
    
    // ----------------------------------------------------------------------------
    // ERC20 Token, with the addition of symbol, name and decimals and a
    // fixed supply
    // ----------------------------------------------------------------------------
    contract FixedSupplyToken is ERC20Interface, Owned {
        using SafeMath for uint;
    
        string public symbol;
        string public  name;
        uint8 public decimals;
        uint _totalSupply;
    
        mapping(address => uint) balances;
        mapping(address => mapping(address => uint)) allowed;
    
    
        // ------------------------------------------------------------------------
        // Constructor
        // ------------------------------------------------------------------------
        constructor() public {
            symbol = "USDT"; 
            name = "USDT";  //放代币名称
            decimals = 4;
            _totalSupply = (1*10**9) * 10**uint(decimals);
            balances[owner] = _totalSupply;
            emit Transfer(address(0), owner, _totalSupply);
        }
    
    
        // ------------------------------------------------------------------------
        // Total supply
        // ------------------------------------------------------------------------
        function totalSupply() public view returns (uint) {
            return _totalSupply.sub(balances[address(0)]);
        }
    
    
        // ------------------------------------------------------------------------
        // Get the token balance for account `tokenOwner`
        // ------------------------------------------------------------------------
        function balanceOf(address tokenOwner) public view returns (uint balance) {
            return balances[tokenOwner];
        }
    
    
        // ------------------------------------------------------------------------
        // Transfer the balance from token owner's account to `to` account
        // - Owner's account must have sufficient balance to transfer
        // - 0 value transfers are allowed
        // ------------------------------------------------------------------------
        function transfer(address to, uint tokens) public returns (bool success) {
            balances[msg.sender] = balances[msg.sender].sub(tokens);
            balances[to] = balances[to].add(tokens);
            emit Transfer(msg.sender, to, tokens);
            return true;
        }
    
    
        // ------------------------------------------------------------------------
        // Token owner can approve for `spender` to transferFrom(...) `tokens`
        // from the token owner's account
        //
        // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
        // recommends that there are no checks for the approval double-spend attack
        // as this should be implemented in user interfaces 
        // ------------------------------------------------------------------------
        function approve(address spender, uint tokens) public returns (bool success) {
            allowed[msg.sender][spender] = tokens;
            emit Approval(msg.sender, spender, tokens);
            return true;
        }
    
    
        // ------------------------------------------------------------------------
        // Transfer `tokens` from the `from` account to the `to` account
        // 
        // The calling account must already have sufficient tokens approve(...)-d
        // for spending from the `from` account and
        // - From account must have sufficient balance to transfer
        // - Spender must have sufficient allowance to transfer
        // - 0 value transfers are allowed
        // ------------------------------------------------------------------------
        function transferFrom(address from, address to, uint tokens) public returns (bool success) {
            balances[from] = balances[from].sub(tokens);
            allowed[from][msg.sender] = allowed[from][msg.sender].sub(tokens);
            balances[to] = balances[to].add(tokens);
            emit Transfer(from, to, tokens);
            return true;
        }
    
    
        // ------------------------------------------------------------------------
        // Returns the amount of tokens approved by the owner that can be
        // transferred to the spender's account
        // ------------------------------------------------------------------------
        function allowance(address tokenOwner, address spender) public view returns (uint remaining) {
            return allowed[tokenOwner][spender];
        }
    
    
        // ------------------------------------------------------------------------
        // Token owner can approve for `spender` to transferFrom(...) `tokens`
        // from the token owner's account. The `spender` contract function
        // `receiveApproval(...)` is then executed
        // ------------------------------------------------------------------------
        function approveAndCall(address spender, uint tokens, bytes data) public returns (bool success) {
            allowed[msg.sender][spender] = tokens;
            emit Approval(msg.sender, spender, tokens);
            ApproveAndCallFallBack(spender).receiveApproval(msg.sender, tokens, this, data);
            return true;
        }
    
    
        // ------------------------------------------------------------------------
        // Don't accept ETH
        // ------------------------------------------------------------------------
        function () public payable {
            revert();
        }
    
    
        // ------------------------------------------------------------------------
        // Owner can transfer out any accidentally sent ERC20 tokens
        // ------------------------------------------------------------------------
        function transferAnyERC20Token(address tokenAddress, uint tokens) public onlyOwner returns (bool success) {
            return ERC20Interface(tokenAddress).transfer(owner, tokens);
        }
    }
    

    web3.php

    • git https://github.com/sc0Vu/web3.php/blob/master/src/Contract.php
    • 测试链
    $this->web3 = new Web3('https://ropsten.infura.io/v3/8fa45462ba4d438ab7a1aff99a0787b2');
    
    • 测试chainId
        $this->chainId = 3;
    
    • 查询状态 以及日志
    <?php
    /**
     * Created by PhpStorm.
     * User: dee
     * Date: 2016/8/6
     * Time: 12:44
     */
    namespace app\index\controller;
    // use Think\Controller;
    
    use app\index\model\User;
    use \think\Request; 
    use \think\Db; 
    use EthTool\KeyStore;
    use kornrunner\Keccak;
    
    
    require('../vendor/autoload.php');
    use Web3\Web3;
    use EthTool\Callback;
    use Web3\Contract;
    use Web3\Utils;
    use Web3\Providers\HttpProvider;
    use Web3\Personal;
    use Web3\RequestManagers\HttpRequestManager;
    use EthTool\Credential;
    use app\index\model\Accesscontract as Acc;
    
    
    class Checktransfer {
    
        public $web3;
    
        public function __construct(){
    
            $this->web3 = new Web3('https://ropsten.infura.io/v3/8fa45462ba4d438ab7a1aff99a0787b2'); //测试
            
            $this->chainId = 3;
    
            $this->model = new \app\index\model\Waitforreceipt();
    
        }
        
    	//  查询日志
        public function CheckLogs(){
                $hash = input('hash');
                $hash = trim($hash);
    
                if(empty($hash)){
                     return json(['code' => 0 ,'info'=>'缺失参数!']);
                }
                
                $type = input('types');
    
                $type = trim($type);
                 if($type == 'eth'){
            
                $type_name = 'NewDeposit';
    
                }elseif ($type == 'usdt') {
               
                    $type_name = 'NewDepositUSDT';
    
                }elseif ($type == 'xxx') {
               
                    $type_name = 'NewDepositxxx';
    
                }else{
                    return json(['code' => 0 , 'info' => '无效参数' ]);     
                }
    
    
                $model = new Acc();
    
                $contract = $model ->loadContract($this->web3,'xxx');//获取代币合约
    
                $is_hs = $model->checkEvent($contract,$type_name,$hash);
       
                if($is_hs){
                    
                    $is_hs['index'] -= 1;
               
                    return json(['code' => 1 , 'info' =>  $is_hs]);
                }
    
                return json(['code' => 0 ,'info'=>'无日志信息']);
          
               
        
                 // dd($receipt);
        }
    
        //查询订单
        public function CheckHash(){
                $hash = input('hash');
                $hash = trim($hash);
    
                if(empty($hash)){
                     return json(['code' => 0 ,'info'=>'缺失参数!']);
                }
    
                $is_hs = $this->model->saveHs($this->web3,$hash);
    
                if($is_hs){
                    return json(['code' => 1 , 'info' =>  $is_hs]);
                }
    
                return json(['code' => 0 ,'info'=>'暂未上链成功']);
          
               
        
                 // dd($receipt);
        }
    }
    
    <?php
    namespace app\index\model;
    
    use app\index\model\User;
    use app\index\model\Apply;
    use \think\Request;
    use \think\Db;
    use Elliptic\EC;
    use kornrunner\Keccak;
    use EthTool\KeyStore;
    
    require('../vendor/autoload.php');
    use Web3\Web3;
    use EthTool\Callback;
    use Web3\Contract;
    use Web3\Utils;
    use Web3\Providers\HttpProvider;
    use Web3\RequestManagers\HttpRequestManager;
    use app\index\controller\Index;
    
    
    
    class Waitforreceipt
    {
    
        public static function waitForReceipt($web3,$txhash,$timeout=60,$interval=1){
    
    
            $cb = new Callback;
            $t0 = time();
            while(true){
                $web3->eth->getTransactionReceipt($txhash,$cb);
                if($cb->result) break;
                $t1 = time();
                if(($t1 - $t0) > $timeout) break;
                sleep($interval);
            }
            return $cb->result;
        }
        /**
         * 查询哈希值一次
         * [waitForReceiptOne description]
         * @param  [type] $web3   [description]
         * @param  [type] $txhash [description]
         * @return [type]         [description]
         */
        public static function waitForReceiptOne($web3,$txhash){
            $cb = new Callback;
            // dd($txhash);
            $web3->eth->getTransactionReceipt($txhash,$cb);
            return $cb->result;
        }
    
        /**
         * 查询保存哈希值
         * [saveHs description]
         * @param  [type] $web3 [description]
         * @param  [type] $hash [description]
         * @return [type]       [description]
         */
        public static function saveHs($web3,$hash,$type = 0){
        $receipt = self::waitForReceiptOne($web3,$hash);
        // dd($receipt);
            try{
            	$hash = trim($hash);
            	if(!$hash){
            		return false;
            	}
                $is_hs = Db::name('eth_log_list')
                    ->where('transactionHash',$hash)
                    ->field('status,from,to,transactionHash')
                    ->find();
              
                if($is_hs){
    
                    $receipt = self::waitForReceiptOne($web3,$hash);
    
                    if($is_hs['status'] == 0 && !$receipt){
                    	return false;
                    }
                    if(!$receipt){
                        return $is_hs;
                    }else{
                        $status = hexdec($receipt->status);
    
                        if($status == 1){
                            $save['status'] = 1;
                        }else{
                            $save['status'] = 2;
                            $save['update_time'] = time();
                        }
    
                        $save['blockHash'] = $receipt->blockHash;
                        $save['blockNumber'] = $receipt->blockNumber;
                        $save['contractAddress'] = $receipt->contractAddress;
                        $save['cumulativeGasUsed'] = $receipt->cumulativeGasUsed;
                        $save['from'] = $receipt->from;
                        $save['gasUsed'] = hexdec($receipt->gasUsed) * 0.00000001;
                        $save['logs'] = json_encode($receipt->logs);
                        $save['to'] = $receipt->to;
    
                        $save['transactionIndex'] = $receipt->transactionIndex;
    
                     
    
                        $is_hs['status'] = $save['status'];
                        Db::name('eth_log_list')
                            ->where('transactionHash',$hash)
                            ->update($save);
    
                        $is_hs = Db::name('eth_log_list')
        		            ->where('transactionHash',$hash)
        		            ->field('status,from,to,transactionHash')
        		            ->find();    
                        return $is_hs;
                    }
    
                }else{
                	$save['type'] = $type;   
                    $receipt = self::waitForReceiptOne($web3,$hash);
                    if($receipt){
                        $status = hexdec($receipt->status);
                        if($status == 1){
    
                            $save['status'] = 1;
                        }else{
    
                            $save['status'] = 2;
                            $save['update_time'] = time();
                        }
                        $save['create_time'] = time();
                        $save['blockHash'] = $receipt->blockHash;
                        $save['blockNumber'] = $receipt->blockNumber;
                        $save['contractAddress'] = $receipt->contractAddress;
                        $save['cumulativeGasUsed'] = $receipt->cumulativeGasUsed;
                        $save['from'] = $receipt->from;
                        $save['gasUsed'] = hexdec($receipt->gasUsed) * 0.00000001;
                        $save['logs'] = json_encode($receipt->logs);
                        $save['to'] = $receipt->to;
                        $save['transactionHash'] = $receipt->transactionHash;
                        $save['transactionIndex'] = $receipt->transactionIndex;
                        Db::name('eth_log_list')->insert($save);
    
                        $re['status'] =  $save['status'];
                        $re['from'] = $save['from'];
                        $re['to'] = $save['to'];
                        $re['transactionHash'] = $save['transactionHash'];
    
                        return $re;
    
                    }else{
    
                    	$save['status'] = 0;
                    	$save['create_time'] = time();
                    	$save['transactionHash'] = $hash;
                    	Db::name('eth_log_list')->insert($save);
                        return false;
                    }
    
    
                }
            } catch (\Exception $e){
                return false;
            }
        }
    
    
        public static function waitForReceiptOnet($web3,$txhash){
            $cb = new Callback;
            // dd($txhash);
            $web3->eth->getTransactionReceipt($txhash,$cb);
            return $cb->result;
        }
    
    
    }
    
    <?php
    namespace app\index\model;
    
    use think\Db;
    
    use Web3\Contract;
    use Web3\Web3;
    use EthTool\Callback;
    use Web3\Utils;
    
    
    class Accesscontract
    {
        /**
         * 访问代币合约
         * @param $web3
         * @param $artifact   币种
         * @return Contract
         */
        function loadContract($web3,$artifact){
            $dir = \config('contractBuild');
            $abi = file_get_contents($dir . $artifact . '.abi');
            $addr = file_get_contents($dir . $artifact . '.addr');
            // dd($addr);
            $contract = new Contract($web3->provider,$abi);
            $contract->at($addr);
    
            return $contract;
        }
        /**
         * [checkEvent description] 得到事件日志
         * @param  [type] $contract [description]
         * @param  string $name     [description]  事件名字
         * @param  string $hash     [description]
         * @return [type]           [description]
         */
        function checkEvent($contract,$name='',$hash=''){
    
            if(!$contract|| !$name || !$hash )return false;
    
            try {
                $abi = $contract->abi;
    
                if(!$abi) return false;
    
                $event_arr = [];
    //        拼接事件
                foreach($abi as $k => $v){
                    if($v['type'] == 'event' && $name == $v['name'] ){
    
                        $v['checkHs'] = $v['name'].'(';
                        $arr = [];
                        foreach ($v['inputs'] as $key => $value) {
                            $arr[] = $value['type'];
                        }
                        $v['checkHs'] .= implode($arr,',').')';
                        $event_arr = $v;
                        break;
    
                    }
    
                }
    
                if(!$event_arr) return false;
    
                $ethabi = $contract->ethabi;
                $mytopic = $ethabi->encodeEventSignature($event_arr['checkHs']); //得到事件hash
    
                $cb = new Callback;
                $contract->eth->getTransactionReceipt($hash,$cb); //得到账单
                $is_hs = $cb->result;
    
                if(!$is_hs)return false;
    
                $logs =$is_hs->logs;
                foreach ($logs as $key => $value) {
    
                    if($value->topics){
    
                        if($value->topics['0'] == $mytopic){
                            $str = 	$value->data;
                            $len = mb_strlen($str);
    
                            if($len <= 2) return false;
    
                            $div = ($len-2)%64;
    
                            if($div) return false;
                            // dump($str);    
                            $str = mb_substr($str,2,$len);     //去除0x
                            $data_arr = str_split($str,64);
                            $re_arr = [];
                            // dump($data_arr);
                            // dd($event_arr['inputs']);
                            foreach ($event_arr['inputs'] as $i_k => $i_v) {
                                $tmp = $ethabi->decodeParameter($i_v['type'],$data_arr[$i_k]);  //解密
                                if(isset($tmp->value)){
                                    $re_arr[$i_v['name']] = gmp_strval($tmp->value);
                                }else{
                                    $re_arr[$i_v['name']] = $tmp;
                                }
                            }
                            return 	$re_arr;
                        }
                    }
                }
    
            } catch (error $e) {
                return false;
            }
            return false;
        }
    
        /**
         * 是否部署成功
         * @param $eth
         * @param $txhash
         * @param int $timeout
         * @param int $interval
         * @return mixed
         */
        function waitForReceipt($eth,$txhash,$timeout=60,$interval=1){
            // echo 'waiting for tx receipt...' . PHP_EOL;
            $cb = new Callback;
            $t0 = time();
            while(true){
                $eth->getTransactionReceipt($txhash,$cb);
                if($cb->result) break;
                $t1 = time();
                if(($t1 - $t0) > $timeout) break;
                sleep($interval);
            }
            return $cb->result;
        }
    
    
    
    
        /**
         * 转账
         * @param $contract
         * @param $initiator
         * @param $to
         * @param $value
         * @return mixed
         */
        function transfer($contract,$initiator,$to,$value){
            $cb = new Callback;
            $opts = [
                'from' => $initiator,
                'gas' => Utils::toHex(100000,true)
            ];
            $contract->send('transfer',$to,$value,$opts,$cb);
            $txhash = $cb->result;
    
            $receipt = $this -> waitForReceipt($contract->eth,$txhash);
    
            return $receipt;
        }
    
    
        function CheckData($contract,$name){
    
            $cb = new Callback;
            $contract->call($name,$cb);
            return $cb->result;
        }
    
    
    }	
    
    • 裸交易
    <?php
    namespace app\index\controller;
    
    use app\index\model\User;
    use app\index\model\Apply;
    use \think\Request;
    use \think\Db;
    use Elliptic\EC;
    use kornrunner\Keccak;
    use EthTool\KeyStore;
    use app\index\model\Accesscontract as Acc;
    
    require('../vendor/autoload.php');
    use Web3\Web3;
    use EthTool\Callback;
    use Web3\Contract;
    use Web3\Utils;
    use Web3\Providers\HttpProvider;
    use Web3\RequestManagers\HttpRequestManager;
    use EthTool\Credential;
    
    
    
    class Feetransfer
    {
    
    	public $web3;
    
    	public $admin;
    
    	public $hyaddress;
    	
    	public $chainId;
    
    	public $waitfModel;
    
    	public $keystore;
    
    	public $pwd;
    
        public function __construct(){
    
            $this->web3 = new Web3('https://ropsten.infura.io/v3/8fa45462ba4d438ab7a1aff99a0787b2'); //测试
            
            $this->chainId = 3;
    
        	$this->admin = '0x9Cx787xd5exxxxxxd6FAEf';//矿工费来源
    
        	$this->hyaddress = '0x510xxxxxxxx10f5xfd';  //合约地址
    
        	$this->waitfModel = new \app\index\model\Waitforreceipt();
    
        	$this->keystore = '/www/wwwroot/xxxhy/keystore/137cd6e542d6faef.json';
    
        	$this->pwd = 'ddd121231231';
        }
    
    
    
        /**
         * 释放
         * [autoWithdraw description]
         * @return [type] [description]
         */
    public function autoWithdraw(){
    
    	$cb = new Callback;
    
    	$admin = $this->admin;//总账户地址
    
    	$hyaddress = $this->hyaddress;
    
    	$this->web3->eth->getBalance($admin,$cb);//转出地址余额
    
    	$a = bcdiv($cb->result,pow(10,18),8);    //余额
    	
    	$val = Utils::toHex(0,true);
    		
    	$gasLimit = Utils::toHex(100000,true);	
       
    	$point = 0.0005;//预留数量以扣除旷工费
       
    	if($a <= $point ){//判断是否足够余额以转出
        //  dd($a);
    		return json(['code' => 0 , 'info' =>  $admin  . ' 转出地址ETH余额不足支付矿工费']);
    		exit;
    	}
    	
    	$model = new Acc();
    
    	$contract = $model -> loadContract($this->web3,'xxxx');//获取代币合约
    
    	$dizhi = substr($admin,2);//截取0x之后
    
    	$credential = Credential::fromWallet($this->pwd,$this->keystore);//获取对应json文件件
    
    	$walletAddress = $credential->getAddress();//重新获取地址
    
    	$this->web3->eth->getTransactionCount($walletAddress,'latest',$cb); //返回指定地址发生的交易数量
    	
    	$nonce =  $cb->result;
    
    	$opts = [
    		'from' => $walletAddress,
    		'gas' => Utils::toHex(30000,true),
    	];
    		
    	$contract = $model -> loadContract($this->web3,'xxxx');//获取代币合约
    	
    	$raw = $contract->sends('autoWithdraw',$opts,$cb);
    
    	$raw['from'] = $walletAddress;
    		
    	$raw['gasPrice'] = '0x' . Utils::toWei('100','gwei')->toHex();
    		
    	$raw['gasLimit'] = $gasLimit;
    		
    	$raw['value'] = $val;
    		
    	$raw['nonce'] = Utils::toHex($nonce,true);
    		
    	$raw['chainId'] = $this->chainId;
    		
    	$raw['to'] = $hyaddress;
    
    	$signed = $credential->signTransaction($raw);//获取签名
    
    	$this->web3->eth->sendRawTransaction($signed,$cb);//发起裸交易
     
    	if($cb->error){
                
    		return json(['code' => 0 , 'info' => $cb->error.PHP_EOL ]);		
    
    		}else{
    			$waitfModel = $this->waitfModel;
    		
    			$is_save = $waitfModel->saveHs($this->web3,$cb->result,2);
    		
    			return json(['code' => 1 , 'info' => $cb->result ]);
    	}
    
    
    }
    
    /**
     * 更新金额
     * [updateEther description]
     * @return [type] [description]
     */
    public function updateMoney(){
    
    	$num = input('num');
    	$type = input('type','trim');
    	if($type == 'updateEther'){
    		$type_num = 3;
    		$type_name = 'updateEther';
    	}}else{
    		return json(['code' => 0 , 'info' => '无效参数' ]);		
    	}
    	$cb = new Callback;
    
    	$admin = $this->admin;//总账户地址
    
    	$hyaddress = $this->hyaddress;
    
    	$this->web3->eth->getBalance($admin,$cb);//转出地址余额
    
    	$a = bcdiv($cb->result,pow(10,18),8);    //余额
    	
    	$val = Utils::toHex(0,true);
    		
    	$gasLimit = Utils::toHex(100000,true);	
       
    	$point = 0.0005;//预留数量以扣除旷工费
       
    	if($a <= $point ){//判断是否足够余额以转出
        //  dd($a);
    		return json(['code' => 0 , 'info' =>  $admin  . ' 转出地址ETH余额不足支付矿工费']);
    		exit;
    	}
    	
    	$model = new Acc();
    
    	$contract = $model -> loadContract($this->web3,'xxx');//获取代币合约
    
    	$dizhi = substr($admin,2);//截取0x之后
    
    	$credential = Credential::fromWallet($this->pwd,$this->keystore);//获取对应json文件件
    
    	$walletAddress = $credential->getAddress();//重新获取地址
    
    	$this->web3->eth->getTransactionCount($walletAddress,'latest',$cb); //返回指定地址发生的交易数量
    	
    	$nonce =  $cb->result;
    
    	$opts = [
    		'from' => $walletAddress,
    		'gas' => Utils::toHex(30000,true),
    	];
    		
    	$contract = $model -> loadContract($this->web3,'xxx');//获取代币合约
    	
    	$raw = $contract->sends($type_name,$num,$opts,$cb);
    
    	$raw['from'] = $walletAddress;
    		
    	$raw['gasPrice'] = '0x' . Utils::toWei('100','gwei')->toHex();
    		
    	$raw['gasLimit'] = $gasLimit;
    		
    	$raw['value'] = $val;
    		
    	$raw['nonce'] = Utils::toHex($nonce,true);
    		
    	$raw['chainId'] = $this->chainId;
    		
    	$raw['to'] = $hyaddress;
    	// dd($raw);
    	$signed = $credential->signTransaction($raw);//获取签名
    
    	$this->web3->eth->sendRawTransaction($signed,$cb);//发起裸交易
     
    	if($cb->error){
                
    		return json(['code' => 0 , 'info' => $cb->error.PHP_EOL ]);		
    
    		}else{
    			$waitfModel = $this->waitfModel;
    		
    			$is_save = $waitfModel->saveHs($this->web3,$cb->result,$type_num);
    		
    			return json(['code' => 1 , 'info' => $cb->result ]);
    	}
    
    
    }
    
    
    
    
    
    //  导入私钥 
        public function addAdminKey($privateKey){
    
         	$wfn = KeyStore::save($privateKey,'asdasdas','/www/wwwroot/xxxhy/keystore');
    
         }
    
    
    
    }
    
    • \vendor\sc0vu\web3.php\src\Contract.php 里添加sends()方法
    	public function sends()
        {
            if (isset($this->functions)) {
                $arguments = func_get_args();
                $method = array_splice($arguments, 0, 1)[0];
                $callback = array_pop($arguments);
    
                if (!is_string($method)) {
                    throw new InvalidArgumentException('Please make sure the method is string.');
                }
    
                $functions = [];
                foreach ($this->functions as $function) {
                    if ($function["name"] === $method) {
                        $functions[] = $function;
                    }
                };
                if (count($functions) < 1) {
                    throw new InvalidArgumentException('Please make sure the method exists.');
                }
                if (is_callable($callback) !== true) {
                    throw new \InvalidArgumentException('The last param must be callback function.');
                }
    
                // check the last one in arguments is transaction object
                $argsLen = count($arguments);
                $transaction = [];
                $hasTransaction = false;
    
                if ($argsLen > 0) {
                    $transaction = $arguments[$argsLen - 1];
                }
                if (
                    isset($transaction["from"]) ||
                    isset($transaction["to"]) ||
                    isset($transaction["gas"]) ||
                    isset($transaction["gasPrice"]) ||
                    isset($transaction["value"]) ||
                    isset($transaction["data"]) ||
                    isset($transaction["nonce"])
                ) {
                    $hasTransaction = true;
                } else {
                    $transaction = [];
                }
    
                $params = [];
                $data = "";
                $functionName = "";
                foreach ($functions as $function) {
                    if ($hasTransaction) {
                        if ($argsLen - 1 !== count($function['inputs'])) {
                            continue;
                        } else {
                            $paramsLen = $argsLen - 1;
                        }
                    } else {
                        if ($argsLen !== count($function['inputs'])) {
                            continue;
                        } else {
                            $paramsLen = $argsLen;
                        }
                    }
                    try {
                        $params = array_splice($arguments, 0, $paramsLen);
                        // print_r($params);
                        $data = $this->ethabi->encodeParameters($function, $params);
                        $functionName = Utils::jsonMethodToString($function);
                    } catch (InvalidArgumentException $e) {
                        continue;
                    }
                    break;
                }
                if (empty($data) || empty($functionName)) {
                    throw new InvalidArgumentException('Please make sure you have put all function params and callback.');
                }
                $functionSignature = $this->ethabi->encodeFunctionSignature($functionName);
                $transaction['to'] = $this->toAddress;
                $transaction['data'] = $functionSignature . Utils::stripZero($data);
    			
    			return $transaction;
    			
     
            }
        }
    
    展开全文
  • Solidity学习(一)

    2021-01-06 21:13:07
    Solidity入门,基本语法,整数溢出以及异常处理,字节数组,动态字节数组,字符串,字符串与字节数组的相互转换

    1.Solidity基础

    友情提示:每一句话都非常重要,每一个点都是细节。再者本教程适合有一定编程基础的小伙伴快速入门
    基本语法,整数溢出以及异常处理,字节数组,动态字节数组,字符串,字符串与字节数组的相互转换,帮助大家快速入门Solidity

    这里直接给一段代码

    pragma solidity ^0.4.16;
    
    contract Helloworld {
        string Myname = "张三";
        function getName() public view returns(string) {
            return  Myname;
        }
        function setName(string newname) public {
            Myname = newname;
        }
         function pureTest(string _name) public pure returns (string) {
            return _name;
        }
    }
    

    这里使用的编辑器是remix网页编辑器:点击进入remix编辑器

    下面我们对这段代码进行分析

    在java中,已经有很多的小我们大致了解,所以我们学习solidity只需要学习它与其他语言不同的地方

    1. 第一行的prama,是solidity的编译控制指令,那我们这行代码的意思是,源文件不允许被0.4.16以下,以及大于等于0.5.0版本的编译器所编译,简单来说,允许的范围就是左闭右开

      0.4.16<=编译器版本<0.5.0

      在此区间的编译器版本是可以编译的。

    2. contract关键字,相当于java中的class,定义一个合约

    3. view关键字,这个是Solidity特有的关键字,因为solidity最终是用在合约上,所以会有相关的功能,而view就是这些功能的其中一个,view只能用来修饰方法,而被修饰的方法不能修改状态变量,也就是在java中的类的属性,在这里这个状态变量就是Myname,在这里我们可以看到getName()方法并没有修改Myname的值

    4. 运行这个代码,我们先调用getName函数,结果如下Redmix代码运行结果这张图片显示运行结果,运行成功
      油费1
      这张图片显示油费消耗,记住这个数字

    5. 运行setName()方法,这个方法需要传入一个参数,我们在这里传入"李四",点击运行,之后再运行getName()重新获取值,结果如下
      在这里插入图片描述
      在这里插入图片描述
      但是我们发现油费改变了,这说明了什么?

    6. 当我们继续点击getName()运行多次的时候,油费依旧不变,而当我们再次运行setName()方法的时候油费又会改变。

    7. 这说明了被view修饰的方法不会消耗油费,因为它不需要改变状态变量,所以view关键字还有节约油费的作用

    8. pure关键字,被这个关键字修饰的方法不允许访问状态变量,所以也不需要花费油费。

    9. bool,&& ,||,用法与java中完全一致,在这里不过多解释。

    10. uint关键字,代表非负整型,默认是uint256,256是指256个比特位,

    8bit=1byte

    1. solidity中也支持方法重载
    2. 位运算符:由于solidity是用来部署合约,所以对内存要求很高,为了节约内存,位运算符显得相当重要

    &:按位与,同时为1,得到1,有一个及以上为0,就是0
    |:按位或,同时为0,得到0,有一个及以上为1,就是1
    ~:取反,0变成1,1变成0(只需要一串数字就可以)
    ^:按位异或,相同为0,不相同为1
    <<:左移
    .>>:右移

    将上面的符号全部测试一遍,这里不给出结果,有兴趣的可以自己去运行一下

    
    pragma solidity ^0.4.16;
       
       contract BoolTest {
           uint8 a = 4;
           uint8 b = 2;
           
           function plus() public view returns(uint8){
               return a + b;
           }
           function minus() public view returns(uint8){
               return a - b;
           }
           function multiply() public view returns(uint8){
               return a * b;
           }
           function divide() public view returns(uint8){
               return a / b;
           }
           function power() public view returns(uint8){
               return a ** b;
           }
           
           function test1() public view returns(uint8){
               return a&b;
           }
           function test2() public view returns(uint8){
               return a|b;
           }
           function test3() public view returns(uint8){
               return ~b;
           }
           function test4() public view returns(uint8){
               return a^b;
           }
           function test5() public view returns(uint8){
               return a<<1;
           }
            function test6() public view returns(uint8){
               return a>>1;
           }
       }
    

    2.整数溢出以及异常处理

    1. 接下来我们要介绍非常危险的整数溢出问题,附上代码。
     function flow() public pure returns(uint8){
               uint8 mm = 255;
               return mm;
           }
    

    我们知道。8个比特位的数据范围是0~255,所以这个方法会返回一个mm的值,是255,运行结果没问题。在这里插入图片描述
    接下来我们修改一下代码,修改后的代码在下方

    function flow() public pure returns(uint8){
               uint8 mm = 255;
               mm++;
               return mm;
           }
    

    代码修改后,mm理应变成256,但是256已结超过了8个比特位的取值范围,所以会出现错误。运行结果:
    在这里插入图片描述
    可以看到,mm的值输出直接变成了0,为什么会有这种结果呢?

    1 1 1 1 , 1 1 1 1

    上面是我们的8个比特位,这个是255的二进制表示,当这个数加1的时候,最后一位加1,学过二进制的朋友都知道,依次从后往前满2进1,最后的结果是:

    1, 0 0 0 0, 0 0 0 0

    由于我们只能读取8个比特位,所以读取之后的值就是

    0 0 0 0, 0 0 0 0

    这个值就是0,所以m后8位读取变成了0。

    为了验证我们的结论,我们再次修改代码:

     function flow() public pure returns(uint8){
               uint8 mm = 255;
               mm++;
               mm++;
               return mm;
           }
    

    运行结果:
    在这里插入图片描述
    可以看到,当我们让mm在之前的基础上再加1之后,变成了

    0 0 0 0, 0 0 0 1

    所以mm的结果就是1,可以验证我们刚刚的结论是正确的。

    1. 我们再写一个函数,附上代码:
    function erroTest() public pure returns(int){
                int a1 = 1;
                int a2 = 0;
                return a1/a2;
            }
    

    注意观察上面的函数,很明显我们看到上面的函数发生了错误,因为0不能做除数,那我们编译这个代码是能通过的,但是当我们调用这个函数的时候就会看到以下字段
    在这里插入图片描述
    这就是发生了一个异常。

    注意:solidity目前不支持小数类型

    3.字节数组

    我们还是先给一段代码

    pragma solidity ^0.4.0;
     
     contract BytesArray {
         
         bytes1 public num1 = 0x7a;// 只有一个字节的数  二进制表示:0111 1010
         bytes2 public num2 = 0x7a68;// 有两个字节的数 二进制表示:0111 1010 0110 1000
         
         function getLength() public returns(uint256) {
              return num1.length;// length属性不可修改
         }
         
         function getLength2() public returns(uint256) {
              return num2.length;// length属性不可修改
         }
     }
    
    1. 字节数组虽然在存储上与int一致,但是不能直接进行加减乘除操作。但是可以进行数的比较(>,>=,==,<=,<),也可以进行位运算(&, |, ~, ^, <<, >>)。
    2. 当属性添加public后,会被默认生成一个获取该属性的get方法。
    3. 加上以下代码,验证上述说法,f分别求&, |, ~, ^, <<, >>位运算后的值,并返回。
     function and() public returns(bytes2 n1,bytes2 n2,bytes2 n3, bytes2 n4, bytes2 n5,bytes2 n6){
              return (num1&num2,num1|num2,~num1,num1^num2,num1<<1,num1>>1);
          }
    

    得到结果:
    在这里插入图片描述
    结果正确。

    4.动态字节数组

    上代码:

     pragma solidity ^0.4.0;
     
     contract BytesArray {
         
         bytes public name = new bytes(2);
         
         function initName() public {
             name[0] = 0x7a;
             name[1] = 0x68;
         }
         
         function getLength() public returns(uint256){
             return name.length;
         }
         
         function changeLength() public {
             name.length = 5;
         }
     }
    
    1. 运行initName之后,再查看name属性的值:
      在这里插入图片描述
      结果没问题

    2. 运行getLength()方法查看bytes数组的长度:

    在这里插入图片描述
    长度是2,结果正确

    1. 运行changeLength()方法修改bytes数组的长度:
      在这里插入图片描述
      在这里插入图片描述
      长度变成了5

    总结:动态字节数组能够修改数组的长度。

    1. name.push(0x99)向name动态字节数组中添加一个0x99的元素。

    4.string类型

    1. string操作与java基本一致,string余额bytes之间可以强制转换,这一特性可以用来访问string中特定位置的值。
      代码测试
    pragma solidity ^0.4.0;
    
    contract StringTest {
        string public name = "zhangsan";
        
        function getLength() public returns(uint256){
            return bytes(name).length;
        }
        
        function changeName() public {
            bytes(name)[0] = 'L';
        }
        
        function getCName() public returns(bytes1){
            return bytes(name)[0];
        }
    }
    
    1. 获取name:
      在这里插入图片描述
    2. 获取长度为8个字节:
      在这里插入图片描述
    3. 调用getCName()获取第一个字母:
      在这里插入图片描述
      6.调用changeName修改第一个字母之后再获取name:
      在这里插入图片描述
    展开全文
  • Solidity学习教程

    千次阅读 2018-05-18 11:22:18
    为了方便广大区块链技术学习者和爱好者研究智能合约与Solidity语言,博主开始整理和翻译Solidity官方文档,并将本文作为索引页方便研究者针对性学习

    前言

    截止2018年5月18日撰写此文时,Solidity最新版本为0.4.24,官方中文文档还没有翻译完毕。网上现存的一些民间中文版:

    • 有些已经过时
    • 有些机翻严重
    • 有些翻译质量还可以,但是部分内容偷工减料
    • 有些需要付费订阅

    为了方便广大区块链技术学习者和爱好者研究智能合约Solidity语言,博主开始整理和翻译Solidity官方文档,并将本文作为索引页方便研究者针对性学习。

    索引

    进展

    • 从智能合约概述直到深入理解Solidity——合约结构的章节,来源自Solidity官方文档现已翻译的部分。
    • 其后的章节为博主翻译。
    展开全文
  • 一般这些内容理解了就能够进行相对简单的智能合约的编写了,同时会添加一些我认为也十分重要的内容学习文档为http://solidity-cn.readthedocs.io/zh/develop/layout-of-source-files.html()pragma solidity ^0.4.0;...

    在这里使用cryptoPunks为实例来进行solidity的介绍,一般这些内容理解了就能够进行相对简单的智能合约的编写了,同时会添加一些我认为也十分重要的内容

    学习文档为http://solidity-cn.readthedocs.io/zh/develop/layout-of-source-files.html

    ()pragma solidity ^0.4.0;

    这样,意味着源文件将既不允许低于 0.4.0 版本的编译器编译, 也不允许高于(包含) 0.5.0 版本的编译器编译(第二个条件因使用 ^ 被添加)

    还有pragma solidity >0.4.23 <0.5.0;

    ()声明状态变量

    string public name;

    一般我都会显示地将他们定义为public,虽然你不这么定义,调用合约时也能调用,但是用remix-ide的就知道区别,当你在run中deploy后,出现的变量按钮都是你声明为public

    ()变量声明后将有默认初始值,其初始值字节表示全部为零。任何类型变量的“默认值”是其对应类型的典型“零状态”,所以并不需要自己赋值,声明即可

    例如, bool 类型的默认值是 false 。 uint 或 int 类型的默认值是 0 。对于静态大小的数组和 bytes1 到 bytes32 ,每个单独的元素将被初始化为与其类型相对应的默认值。 最后,对于动态大小的数组, bytes 和 string 类型,其默认缺省值是一个空数组或字符串。

    所以其实代码中mapping (uint => Bid) public punkBids的punkBids被声明为{false,0,0x0,0}

    ()映射

    mapping (address => uint256) public balanceOf;

    当你想要将两个变量设定一定关系时,如该例子,想要通过账户的地址来查出账户拥有的token数量

    也可以写得复杂一点,比如:

    mapping (address => mapping (address => uint256)) public allowance;

    访问就是allowance[‘’][‘’]

    ()结构类型¶

    结构是可以将几个变量分组的自定义类型

    1》structure结构体

    struct Bid {

    bool hasBid;//是否正在竞标

    uint punkIndex;

    address bidder;

    uint value;

    }

    这样你可以将一组相关的信息写在一起,然后再结合语句:

    mapping (uint => Bid) public punkBids;

    这样你就可以通过punkBids[5]的映射方式去获取结构体Bid的信息了

    2》enum枚举类型

    枚举可用来创建由一定数量的“常量值”构成的自定义类型

    举例:enum Gender {Male,Female}

    Male = 0 , Female = 1

    访问枚举方式 Gender.Male 实际等于数字 0

    性别枚举的值就有男和女,所以当你声明一个枚举类型的变量的时候,它的值要么是男,要么是女这样,枚举下标定义从左至右从零开始

    ()event

    用于事件监听,比如

    event PunkTransfer(address indexed from, address indexed to, uint256 punkIndex);//声明

    function transferPunk(address to, uint punkIndex) {

    emit PunkTransfer(msg.sender, to, punkIndex); //监听事件

    }

    该event的作用就是你可以使用instance.PunkTransfer(filter,callback)函数,通过设置过滤条件filter,如{from:web3.eth.accounts[0]}来在调用transferPunk的同时监听该函数的特定条件的交易是否成功及其返回内容。

    监听时的emit要加上,没有回警告

    () if (msg.sender != owner) throw;

    这是在合约中我们常见到的条件判断语句,但是现在用这个的时候会有警告,现在都不用这种写法了,可以换成

    •     if(msg.sender != owner) { revert(); }

    •    assert(msg.sender == owner);

    •    require(msg.sender == owner);

    assert和require正确才可往下走

    revert() 和 require() 都会返还剩余 gas,而且允许返回一个message,比如:

    revert(‘Something bad happened’);

    或者

    require(condition, ‘Something bad happened’);

    就是如果当判断的条件不成立的时候就会输出的错误信息

    三者使用情况:

    •    一般地,尽量使用 require 函数

    •    一般地,require 应该在函数最开始的地方使用

    如果有复杂的 if/else 逻辑流,那么应该考虑使用 revert() 函数而不是require()。

    •    一般地,尽量少使用 assert 调用

    •    一般地,assert 应该在函数结尾处使用

    基本上,require() 应该被用于函数中检查条件,assert() 用于预防不应该发生的情况,但不应该使条件错误。

    另外,“除非认为之前的检查(用 if 或 require )会导致无法验证 overflow,否则不应该盲目使用 assert 来检查 overflow”——来自于@chriseth

    举例,在下例中,你可以看到如何轻松使用``require``检查输入条件以及如何使用``assert``检查内部错误,注意,你可以给 require 提供一个消息字符串,而 assert 不行:

    contract Sharer {

    function sendHalf(address addr) public payable returns (uint balance) {

    require(msg.value % 2 == 0, "Even value required.");

    uint balanceBeforeTransfer = this.balance;

    addr.transfer(msg.value / 2);

    //由于转移函数在失败时抛出异常并且不能在这里回调,因此我们应该没有办法仍然有一半的钱。

    assert(this.balance == balanceBeforeTransfer - msg.value / 2);

    return this.balance;

    }

    所以我一般就简单地记为,当想要在调用函数之前进行条件的限制时,就使用require或者if-revert,而且可以写message来输出错误信息以确定是哪里的判断限制了交易的进行。在remix-ide中,message会在触发后在控制台显示错误信息,但若是使用自己配置的私有链,就需要去自己查看了(没试过,之后有空试试)

    ()函数

    function () {internal|external} [pure|constant|view|payable] [returns ()]

    1》参数类型可以为空();返回类型与参数类型相反,返回类型不能为空 —— 如果函数类型不需要返回,则需要删除整个 returns () 部分。

    solidity与 Javascript 和 C 不同的是,它们可能返回任意数量的参数作为输出,但solidity不是,它返回多少个变量是定义死的,returns (uint a, uint b)。

    但是参数的名称是可以省略的。在函数中未使用参数的名称(特别是返回参数)可以省略。比如一个函数return(false,2),那它的返回参数就可以写为returns(bool,uint)。这些参数仍然存在于堆栈中,但它们无法访问。

    举例说明:

    contract C {

    // 省略参数名称

    function func(uint k, uint) public pure returns(uint) {

    return k;

    }

    2》被声明为internal|external,是内部函数和外部函数的区别

    有两种方法可以访问当前合约中的函数:一种是直接使用它的名字,f ,另一种是使用 this.f 。 前者适用于声明为内部函数internal,后者适用于声明为外部函数external的函数

    请注意,当前合约函数被声明为 public 函数(默认也为public),则该函数既可以被当作内部函数也可以被当作外部函数使用。 如果想将一个函数当作内部函数使用,就用 f 调用,如果想将其当作外部函数,使用 this.f

    3》pure|constant|view|payable

    1)带上payable关键字,说明这个函数需要接受msg.value,比如

    function buyPunk(uint punkIndex) payable {}

    那么在你部署好合约后,想要调用这个函数,你的调用形式一定是:

    contract.buyPunk(punkIndex,{from:web3.eth.accounts[0],value:30,gas:30000});

    这里的value传入的值就是在合约中使用的msg.value,from传入的值就是合约中的msg.sender

    这里的gas的传入也是一个十分重要的点,详细分析请看

    2)view是不能修改状态(相当于constant)

    1. 写入状态变量;

    2. (emitting??)发送事件;

    3. 创建其他合约;

    4. 使用selfdestruct;

    5. 通过调用发送Ether;

    6. 调用没有被声明为view和pure的函数

    7. 使用低级调用;

    8. 使用包含特定操作码的内联程序集。

    3)pure的限制更多一点,它是不能修改状态也不能读取状态

    1. 读取状态变量;

    2. 访问this.balance 或者

    .balance;

    3. 访问block,tx,msg(除了msg.sig和msg.data);

    4. 调用没有标记为pure的函数;

    5. 使用包含特定操作码的内联程序集。

    ()cryptoPunks函数withdraw函数中有一句msg.sender.transfer(amount)

    这句话的意思是,当我们在合同中有写payable的函数(如buyPunk)的时候,当调用这个函数时,msg.sender就需要付一定的msg.value,在这里,这个msg.value的去向to是合约的地址。

    但是一般在另一个函数(withdraw)中,我们收取这个value是希望它最终是会流向某一个账户的地址的,所以在这里我们一般都会定义一个数组,即一个临时账户pendingWithdrawals来记录你付的value的值, uint amount = pendingWithdrawals[msg.sender]。

    当最后想要把这个值给一个账户地址的时候,我们就会使用语句msg.sender.transfer(amount)来将合约地址上的这笔钱转到msg.sender这个账户地址中去

    ()地址类型address

    address:地址类型存储一个 20 字节的值(以太坊地址的大小)

    可以使用 balance 属性来查询一个地址的余额, 也可以使用 transfer 函数向一个地址发送 以太币(以 wei 为单位),即上文的msg.sender.transfer(amount)

    ()在remix-ide中编译合约的时候有出现过下面的警告:

    Warning: Variable is declared as a storage pointer. Use an explicit "storage" keyword to silence this warning.

    解决办法是: 加一个显式声明storage(搞清楚其与memory的区别)

    Bid storage exist = bidForPicture[pictureId];

    () 数据位置——详细说明:

    有三种类型,memory,storage和calldata,一般只有外部函数的参数(不包括返回参数)被强制指定为calldata。这种数据位置是只读的,不会持久化到区块链

    storage存储或memory内存

    memory存储位置同我们普通程序的内存类似,即分配,即使用,动态分配,越过作用域即不可被访问,等待被回收。

    而对于storage的变量,数据将永远存在于区块链上。

    总结¶

    强制指定的数据位置:

    •    外部函数的参数(不包括返回参数): calldata,效果跟 memory 差不多

    •    状态变量: storage

    默认数据位置:

    •    函数参数(包括返回参数): memory

    •    所有其它局部变量: storage

    举例说明:

    contract C {

    uint[] x;//x 的数据存储位置是 storage,状态变量

    //memoryArray 的数据存储位置是 memory,函数参数

    functionf(uint[] memoryArray) public {

    x= memoryArray; //将整个数组拷贝到 状态变量storage 中,可行

    var y = x; //分配一个指针(其中 y 的数据存储位置是 storage),可行,其他局部变量

    y[7]; //返回第 8 个元素,可行

    y.length = 2; //通过 y 修改 x,可行

    delete x; //清除数组,同时修改 y,可行

    //下面的就不可行了;需要在 storage 中创建新的未命名的临时数组

    //但 storage 是“静态”分配的:

    //y = memoryArray;

    //下面这一行也不可行,因为这会“重置”指针,

    //但并没有可以让它指向的合适的存储位置。

    //delete y;

    }

    }

    三种数据之间的相互赋值行为:

    (1)当我们把一个storage类型的变量赋值给另一个storage时,我们只是修改了它的指针,一个变,另一个也变

    struct S{string a;uint b;}

    S s;

    function convertStorage(S storage s) internal{

    S tmp = s;

    tmp.a = “Test”;//s的a的值也改变了

    }

    (2)memory转为memory,memory之间是引用传递,并不会拷贝数据,一个变,另一个也跟着变

    (3)memory转换为storage

    因为局部变量和状态变量的类型都可能是storage。所以我们要分开来说这两种情况:

    1. memory赋值给状态变量

    将一个memory类型的变量赋值给一个状态变量时,实际是将内存变量拷贝到存储中,后续两者不会有任何关系

    S s;

    function memoryToState(S memory tmp) internal{

    s = tmp;//从内存中复制到状态变量中。

    //修改旧memory中的值,并不会影响状态变量

    tmp.a = “Test”;//s的就不会变了

    }

    2.memory赋值给局部变量是不可以的

    由于在区块链中,storage必须是静态分配存储空间的。局部变量虽然是一个storage的,但它仅仅是一个storage类型的指针。如果进行这样的赋值,实际会产生一个错误。

    function assign(S s) internal{ //默认的变量是storage的指针

    //error:Type struct MemoryToLocalVar.S memory is not implicitly convertible to expected type struct MemoryToLocalVar.S storage pointer.

    S tmp = s; //修改变量为memory类型S memory tmp = s;即可

    }

    (4)storage转为memory,会创建一份独立的拷贝,两两互不影响

    ()在合约中有很多地方即有uint8/uint256/uint,区别是:

    声明一个类型为 uint (256位无符号整数)

    所以在solidity中,如果你声明了一个变量类型是uint的,其实就是声明了一个uint256的

    int / uint :分别表示有符号和无符号的不同位数的整型变量。 支持关键字 uint8 到 uint256 (无符号,从 8 位到 256 位)以及 int8 到 int256,以 8 位为步长递增。 uint 和 int 分别是 uint256 和 int256 的别名

    ()判断语句

    JavaScript 中的大部分控制结构在 Solidity 中都是可用的,除了 switch 和 goto。 因此 Solidity 中有 if,else,while,do,for,break,continue,return,``? :``这些与在 C 或者 JavaScript 中表达相同语义的关键词。

    用于表示条件的括号 不可以 被省略,单语句体两边的花括号可以被省略。

    注意,与 C 和 JavaScript 不同, Solidity 中非布尔类型数值不能转换为布尔类型,因此 if (1) { ... } 的写法在 Solidity 中 无效 。

    ()

    Gas requirement of function CryptoPicturesMarket.imageHash() high: infinite. If the gas requirement of a function is higher than the block gas limit, it cannot be executed. Please avoid loops in your functions or actions that modify large areas of storage (this includes clearing or copying arrays in storage)

    但是我查了一下资料,发现这个好像不是什么问题,希望以后能够看到可以解决它的方法吧

    以上是在cryptoPunks中需要知道的内容,下面是一些添加内容:

    其他内容:

    ()一个合约如何调用另一个合约

    contract MappingExample {

    mapping(address=>uint) public balances;functionupdate(uint newBalance) public {

    balances[msg.sender]=newBalance;

    }

    }

    contract MappingUser {functionf() public returns (uint) {

    MappingExample m= newMappingExample();

    m.update(100);return m.balances(this);//this是该合约的地址

    }

    }

    通过 new 创建合约

    使用关键字 new 可以创建一个新合约。待创建合约的完整代码必须事先知道,所以在MappingUser合约中创建合约MappingExample,MappingExample合约必须在MappingUser合约之前声明。因此递归的创建依赖是不可能的。

    即一个合约中创建另一个合约,另一个合约一定要在该合约之前就已经声明了

    ()合约相关¶

    this (current contract's type):

    当前合约,可以明确转换为 地址类型。

    selfdestruct(address recipient):

    销毁合约,并把余额发送到指定 地址类型。

    suicide(address recipient):

    与 selfdestruct 等价,但已不推荐使用。

    ()导入

    import * as symbolName from “filename”;//或“.sol”文件

    等同于import "filename" as symbolName;

    ()注释

    单行(//)、多行注释(/*…*/)和(/** ... */)在函数开头或内部做的注释

    /** @title 形状计算器。 */

    contract shapeCalculator {

    /** @dev 求矩形表明面积与周长。

    * @param w 矩形宽度。

    * @param h 矩形高度。

    * @return s 求得表面积。

    * @return p 求得周长。

    */

    function rectangle(uint w, uint h) returns (uint s, uint p) {

    s = w * h;

    p = 2 * (w + h);

    }

    }

    ()定长字节数组¶

    关键字有:bytes1, bytes2, bytes3, ..., bytes32。byte 是 bytes1 的别名。

    .length 表示这个字节数组的长度(只读)

    注解

    可以将 byte[] 当作字节数组使用,但这种方式非常浪费存储空间,准确来说,是在传入调用时,每个元素会浪费 31 字节。 更好地做法是使用 bytes。

    ()变长字节数组¶

    bytes:

    变长字节数组,参见 数组。它并不是值类型。

    string:

    变长 UTF-8 编码字符串类型,参见 数组。并不是值类型。

    ()字面常数-需要好好看看

    Solidity 中是没有八进制的,因此前置 0 是无效的

    注解

    数值字面常数表达式只要在非字面常数表达式中使用就会转换成非字面常数类型。 在下面的例子中,尽管我们知道 b 的值是一个整数,但 2.5 + a 这部分表达式并不进行类型检查,因此编译不能通过。

    //在remix中编译的时候的确是会报错

    browser/test.sol:4:17: TypeError: Operator + not compatible with types rational_const 5 / 2 and uint128

    uint128 b = 2.5 + a + 0.5;

    ^-----^

    uint128 a = 1;

    uint128 b = 2.5 + a + 0.5;

    意思应该就是说因为在字面上我们可以看见不进行类型检查时a是非字面常数类型

    十六进制字面常数以关键字 hex 打头,后面紧跟着用单引号或双引号引起来的字符串(例如,hex”001122FF")

    ()library ArrayUtils {

    // internal内部函数可以在内部库函数中使用,

    // 因为它们会成为同一代码上下文的一部分

    …}

    contract Pyramid {

    using ArrayUtils for *;//使用,然后通过ArrayUtils.函数名()调用

    }

    ()数组:

    可以在声明时指定长度,也可以动态调整大小。

    对于 存储storage的数组来说,元素类型可以是任意的(即元素也可以是数组类型,映射类型或者结构体)。

    对于 内存memory的数组来说,元素类型不能是映射类型,如果作为 public 函数的参数,它只能是 ABI 类型。

    (1)在memory中:一经创建,内存memory数组的大小就是固定的(但却是动态的,也就是说,它依赖于运行时的参数

    1.内存中的数组是不允许创建变长数组的

    uint[] memory a;//wrong

    2.数组字面常数是一种定长的 内存memory数组类型

    在memory中,声明定长数组:

    uint[] memory a = new uint[](2);

    在memory中,声明定长数组并初始化:

    uint[2] memory a = [uint(1),2];

    uint[] memory a = [uint(1),2];

    //wrong,定长的 内存memory数组并不能赋值给变长的 内存memory数组

    为什么要在第一个元素一前面加上uint的类型,这是因为我们前面声明的是uint的数组,而在solidity中有uint8,uint248等多种不同位数的无符号int型常量,在不声明的情况下,默认使用uint256,而我们初始化的数组元素是1和2,系统会判定为uint8,所以需要我们在第一个元素1前面强制转化为uint型

    当然,我们也可以如下的声明方式:

    uint8[2] memory a = [1,2];

    但要注意,那么a数组里面就不可以出现2进制里面大于8位的数了

    定长数组是无法通过length和push方法更改长度或插入的

    (2)在storage中

    1.在storage中,声明定长数组,并初始化变量:

    uint[2] a = [1,2];

    2.在storage中,声明变长数组,并初始化变量:

    uint[] a = [1,2];

    3.二维数组初始化,行列是反过来定义的:

    uint[2][3] a = [[1,1],[2,2],[3,3]];

    但访问是相同的:

    访问第三个动态数组的第二个元素,你应该使用 a[2][1]

    (3)

    bytes 和 string 类型的变量是特殊的数组。 bytes 类似于 byte[],但它在 calldata 中会被“紧打包”(译者注:将元素连续地存在一起,不会按每 32 字节一单元的方式来存放)。 string 与 bytes 相同,但(暂时)不允许用长度或索引来访问。

    (4)变长的 存储storage数组以及 bytes 类型(而不是 string 类型)都有一个叫做 push 的成员函数

    ()delete的作用:

    delete a 的结果是将 a 的类型在初始化时的值赋值给 a。即对于整型变量来说,相当于 a = 0, 但 delete 也适用于数组,对于动态数组来说,是将数组的长度设为 0,而对于静态数组来说,是将数组中的所有元素重置。 如果对象是结构体,则将结构体中的所有属性重置。

    delete 对整个映射是无效的(因为映射的键可以是任意的,通常也是未知的)。 因此在你删除一个结构体时,结果将重置所有的非映射属性,这个过程是递归进行的,除非它们是映射。 然而,单个的键及其映射的值是可以被删除的。

    contract DeleteExample {

    uint data;

    uint[] dataArray;functionf() public {

    uint x=data;delete x; //将 x 设为 0,并不影响数据

    delete data; //将 data 设为 0,并不影响 x,因为它仍然有个副本

    uint[] storage y =dataArray;//可以通过改变dataArray来改变y,也可以通过改变y来改变dataArray

    deletedataArray;//将 dataArray.length 设为 0,但由于 uint[] 是一个复杂的对象,y 也将受到影响,

    //因为它是一个存储位置是 storage 的对象的别名。

    //另一方面:"delete y" 是非法的,引用了 storage 对象的局部变量只能由已有的 storage 对象赋值。

    }

    }

    ()基本类型之间的转换

    (1)隐式转换

    int8 不能转换成 uint256(因为 uint256 不能涵盖某些值,例如,-1)。 更进一步来说,无符号整型可以转换成跟它大小相等或更大的字节类型,但反之不能。 任何可以转换成 uint160 的类型都可以转换成 address 类型。

    (2)显式转换

    但有些时候会出问题

    int8 y = -3;

    uint x = uint(y);

    这段代码的最后,x 的值将是 0xfffff..fd (64 个 16 进制字符),因为这是 -3 的 256 位补码形式。

    如果一个类型显式转换成更小的类型,相应的高位将被舍弃

    uint32 a = 0x12345678;

    uint16 b = uint16(a); // 此时 b 的值是 0x5678

    类型推断

    为了方便起见,没有必要每次都精确指定一个变量的类型,编译器会根据分配该变量的第一个表达式的类型自动推断该变量的类型

    uint24 x = 0x123;

    var y = x;

    这里 y 的类型将是 uint24。不能对函数参数或者返回参数使用 var。

    ()时间seconds、 minutes、 hours、 days、 weeks 和 years

    years 后缀已经不推荐使用了,因为从 0.5.0 版本开始将不再支持。

    这些后缀不能直接用在变量后边。如果想用时间单位(例如 days)来将输入变量换算为时间,你可以用如下方式来完成:

    function f(uint start, uint daysAfter) public {

    if (now >= start + daysAfter * 1 days) {

    // ...

    }

    }

    ()赋值¶

    解构赋值和返回多值¶

    Solidity 内部允许元组 (tuple) 类型,也就是一个在编译时元素数量固定的对象列表,列表中的元素可以是不同类型的对象。这些元组可以用来同时返回多个数值,也可以用它们来同时给多个新声明的变量或者既存的变量(或通常的 LValues):

    pragma solidity >0.4.23 <0.5.0;

    contract C {

    uint[] data;functionf() public pure returns (uint, bool, uint) {return (7, true, 2);

    }functiong() public {//基于返回的元组来声明变量并赋值

    (uint x, bool b, uint y) =f();//交换两个值的通用窍门——但不适用于非值类型的存储 (storage) 变量。

    (x, y) =(y, x);//元组的末尾元素可以省略(这也适用于变量声明)。

    (data.length,,) = f(); //将长度设置为 7

    //省略元组中末尾元素的写法,仅可以在赋值操作的左侧使用,除了这个例外:

    (x,) = (1,);//(1,) 是指定单元素元组的唯一方法,因为 (1)

    //相当于 1。

    }

    }

    ()错误处理:

    下列情况将会产生一个 assert 式异常:

    1    如果你访问数组的索引太大或为负数(例如 x[i] 其中 i >= x.length 或 i < 0)。

    2    如果你访问固定长度 bytesN 的索引太大或为负数。

    3    如果你用零当除数做除法或模运算(例如 5 / 0 或 23 % 0 )。

    4    如果你移位负数位。

    5    如果你将一个太大或负数值转换为一个枚举类型。

    6    如果你调用内部函数类型的零初始化变量。

    7    如果你调用 assert 的参数(表达式)最终结算为 false。

    下列情况将会产生一个 require 式异常:

    1    调用 throw 。

    2    如果你调用 require 的参数(表达式)最终结算为 false 。

    3    如果你通过消息调用调用某个函数,但该函数没有正确结束(它耗尽了 gas,没有匹配函数,或者本身抛出一个异常),上述函数不包括低级别的操作 call , send , delegatecall 或者 callcode 。低级操作不会抛出异常,而通过返回 false 来指示失败。

    4    如果你使用 new 关键字创建合约,但合约没有正确创建(请参阅上条有关”未正确完成“的定义)。

    5    如果你对不包含代码的合约执行外部函数调用。

    6    如果你的合约通过一个没有 payable 修饰符的公有函数(包括构造函数和 fallback 函数)接收 Ether。

    7    如果你的合约通过公有 getter 函数接收 Ether 。

    8    如果 .transfer() 失败。

    在内部, Solidity 对一个 require 式的异常执行回退操作(指令 0xfd )并执行一个无效操作(指令 0xfe )来引发 assert 式异常。 在这两种情况下,都会导致 EVM 回退对状态所做的所有更改。回退的原因是不能继续安全地执行,因为没有实现预期的效果。 因为我们想保留交易的原子性,所以最安全的做法是回退所有更改并使整个交易(或至少是调用)不产生效果。 请注意, assert 式异常消耗了所有可用的调用 gas ,而从 Metropolis 版本起 require 式的异常不会消耗任何 gas。

    下边的例子展示了如何在 revert 和 require 中使用错误字符串:

    pragma solidity ^0.4.22;

    contract VendingMachine {

    function buy(uint amount) payable {

    if (amount > msg.value / 2 ether)

    revert("Not enough Ether provided.");

    // 下边是等价的方法来做同样的检查:

    require(

    amount <= msg.value / 2 ether,

    "Not enough Ether provided."

    );

    // 执行购买操作

    }

    }

    这里提供的字符串应该是经过 ABI 编码 之后的,因为它实际上是调用了 Error(string) 函数。在上边的例子里,revert("Not enough Ether provided."); 会产生如下的十六进制错误返回值:

    0x08c379a0                                                         // Error(string) 的函数选择器

    0x0000000000000000000000000000000000000000000000000000000000000020 // 数据的偏移量(32)

    0x000000000000000000000000000000000000000000000000000000000000001a // 字符串长度(26)

    0x4e6f7420656e6f7567682045746865722070726f76696465642e000000000000 // 字符串数据("Not enough Ether provided.")

    ()可见性

    contract C {

    uint private data;

    function f(uint a) private returns(uint b) { return a + 1; }//不可继承

    function setData(uint a) public { data = a; }

    function getData() public returns(uint) { return data; }

    function compute(uint a, uint b) internal returns (uint) { return a+b; }//可继承

    }

    contract D {//这能访问public

    function readData() public {

    C c = new C();

    uint local = c.f(7); // error: member `f` is not visible

    c.setData(3);

    local = c.getData();

    local = c.compute(3, 5); // error: member `compute` is not visible

    }

    }

    contract E is C {//继承至C,所以可以访问internal,public

    function g() public {

    C c = new C();

    uint val = compute(3, 5); // access to internal member (from derived to parent contract)

    }

    }

    ()getter函数

    (1)就是在合约中声明的状态变量如data,其实都自动地生成了getter函数,就是可以像访问函数一样访问它的值,在合约外访问时就可以直接 合约名.data()

    pragma solidity ^0.4.0;

    contract C {

    uint public data = 42;

    }

    contract Caller {

    C c = new C();

    function f() public {

    uint local = c.data();

    }

    }

    (2)如果是在合约内部访问,它有两种访问的形式:internal和external

    internal则是直接变量名访问即可

    external则是使用this.data()

    pragma solidity ^0.4.0;

    contract C {

    uint public data;

    function x() public {

    data = 3; // internal access

    uint val = this.data(); // external access

    }

    }

    ()

    contract Complex {

    //Note that the mapping in the struct is omitted because there is no good way to provide the key for the mapping.

    struct Data {

    //如果在一个结构体中声明了一个映射,一般赋值时都先省略,然后在赋值mapping,因为它的key是不固定的

    uint a;

    bytes3 b;

    mapping (uint => uint) map;

    }

    mapping (uint => mapping(bool => Data[])) public data;

    //调用方法:data[arg1][arg2][arg3].a

    }

    ()修饰器modifier

    继承后可以直接使用被继承处的修饰器

    使用修改器实现的一个防重复进入的例子。

    pragma solidity ^0.4.0;

    contract Mutex {

    bool locked;

    modifier noReentrancy() {

    if (locked) throw;

    locked = true;

    _;//函数体f()return前的内容执行的区域

    locked = false;

    }

    /// This function is protected by a mutex, which means that

    /// reentrant calls from within msg.sender.call cannot call f again.

    /// The `return 7` statement assigns 7 to the return value but still

    /// executes the statement `locked = false` in the modifier.

    function f() noReentrancy returns (uint) {

    if (!msg.sender.call()) throw;

    return 7;

    }

    }

    例子中,由于call()方法有可能会调回当前方法,修改器实现了防重入的检查。

    如果同一个函数有多个修改器,他们之间以空格隔开,修饰器会依次检查执行。

    需要注意的是,在Solidity的早期版本中,有修改器的函数,它的return语句的行为有些不同。

    在修改器中和函数体内的显式的return语句,仅仅跳出当前的修改器和函数体。返回的变量会被赋值,但整个执行逻辑会在前一个修改器后面定义的”_"后继续执行。

    ()fallback function \ call()\send()

    (1)fallback function

    contract ExecuteFallback{//回退事件,会把调用的数据打印出来

    event FallbackCalled(bytes data);//fallback函数,注意是没有名字的,没有参数,没有返回值的

    function(){

    //msg.data其实就是你调用一个函数后,函数名,参数进行keccak256哈希后连接起来的data

    FallbackCalled(msg.data);//event

    }//调用已存在函数的事件,会把调用的原始数据,请求参数打印出来

    event ExistFuncCalled(bytes data, uint256 para);//一个存在的函数

    functionexistFunc(uint256 para){

    ExistFuncCalled(msg.data, para);

    }//模拟从外部对一个存在的函数发起一个调用,将直接调用函数

    functioncallExistFunc(){

    bytes4 funcIdentifier= bytes4(keccak256("existFunc(uint256)"));this.call(funcIdentifier, uint256(1));

    }//模拟从外部对一个不存在的函数发起一个调用,由于匹配不到函数,将调用回退函数

    functioncallNonExistFunc(){

    bytes4 funcIdentifier= bytes4(keccak256("functionNotExist()"));this.call(funcIdentifier);

    }

    }

    回退函数(fallback function):则是当调用一个合约里的某个函数时,如果该函数并不存在,那么就会去调用该回调函数,回调函数无参,无名,无返回值,你可以通过在里面返回个什么或者emit一个事件来显示调用了该回退函数,该函数还是很有用的

    (2)Call()

    Call():call()是一个底层的接口,用来向一个合约发送消息,进行合约之间的交互,但是很不安全,一般是不用的。这个函数是这样的,第一个参数是该访问的函数的名字,后面的参数则是该函数所需的参数,调用它的是一个合约的地址

    (3)send()

    send()函数发送ether

    当我们使用address.send(ether to send)向某个合约直接转帐时,由于这个行为没有发送任何数据,所以接收合约总是会调用fallback函数

    function() payable{fallbackTrigged(msg.data);}

    function deposit() payable{//用来给合约存点钱

    }

    在上述的代码中,我们先要使用deposit()合约存入一些ether,否则将会由于余额不足,调用send()函数将报错。

    查看事件fallbackTrigged得到:

    fallbackTrigged[

    "0x"

    ]

    可以看到,我们成功使用send()发送了1wei到合约,触发了fallback函数,附带的数据是0x(bytes类型的默认空值),空数据。

    这里需要特别注意的是:

    1.    如果我们要在合约中通过send()函数接收,就必须定义fallback函数,否则会抛异常。

    2.    fallback函数必须增加payable关键字,否则send()执行结果将会始终为false。

    fallback中的限制

    send()函数总是会调用fallback,这个行为非常危险,著名的DAO被黑也与这有关。如果我们在分红时,对一系列帐户进行send()操作,其中某个做恶意帐户中的fallback函数实现了一个无限循环,将因为gas耗尽,导致所有send()失败。为解决这个问题,send()函数当前即便gas充足,也只会附带限定的2300gas,故而fallback函数内除了可以进行日志操作外,你几乎不能做任何操作。

    下述行为消耗的gas都将超过fallback函数限定的gas值:

    •    向区块链中写数据

    •    创建一个合约

    •    调用一个external的函数

    •    发送ether

    所以一般,我们只能在fallback函数中进行一些日志操作:

    ()底层的日志接口(Low-level Interface to Logs):可以代替event

    通过函数log0,log1,log2,log3,log4,可以直接访问底层的日志组件。logi表示总共有带i + 1个参数(i表示的就是可带参数的数目,只是是从0开始计数的)。其中第一个参数会被用来做为日志的数据部分,其它的会做为主题(topics)。前面例子中的事件可改为如下:

    log3(

    msg.value,

    0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20,

    msg.sender,

    _id

    );

    其中的长16进制串是事件的签名,计算方式是keccak256("Deposit(address,hash256,uint256)")

    ()继承

    pragma solidity ^0.4.0;

    contract mortal is owned {functionkill() {if (msg.sender ==owner) selfdestruct(owner);

    }

    }

    contract Base1 is mortal {function kill() { /*do cleanup 1*/mortal.kill(); }

    }

    contract Base2 is mortal {function kill() { /*do cleanup 2*/mortal.kill(); }

    }

    contract Final is Base1, Base2 {

    }

    对Final.kill()的调用只会调用Base2.kill(),因为派生重写,会跳过Base1.kill,因为它根本就不知道有Base1。一个变通方法是使用super。

    pragma solidity ^0.4.0;

    contract mortal is owned {functionkill() {if (msg.sender ==owner) selfdestruct(owner);

    }

    }

    contract Base1 is mortal {function kill() { /*do cleanup 1*/super.kill(); }

    }

    contract Base2 is mortal {function kill() { /*do cleanup 2*/super.kill(); }

    }

    contract Final is Base2, Base1 {

    }

    如果Base1调用了函数super,它不会简单的调用基类的合约函数,它还会调用继承关系图谱上的下一个基类合约,所以会调用Base2.kill()。需要注意的最终的继承图谱将会是:Final,Base1,Base2,mortal,owned。使用super时会调用的实际函数在使用它的类的上下文中是未知的,尽管它的类型是已知的。这类似于普通虚函数查找(ordinary virtual method lookup)

    当基类的构造函数中如果需要传参,那么继承它时的方式是:

    contract Base {

    uint x;function Base(uint _x) { x =_x; }

    }

    contract Derived is Base(7) {function Derived(uint _y) Base(_y *_y) {

    }

    }

    继承写的顺序是很重要的,从继承少的到多的

    ()抽象(Abstract Contracts)

    抽象函数是没有函数体的的函数。如下:

    pragma solidity ^0.4.0;

    contract Feline {functionutterance() returns (bytes32);

    }

    这样的合约不能通过编译,即使合约内也包含一些正常的函数。但它们可以做为基合约被继承。

    pragma solidity ^0.4.0;

    contract Feline {functionutterance() returns (bytes32);functiongetContractName() returns (string){return "Feline";

    }

    }

    contract Cat is Feline {function utterance() returns (bytes32) { return "miaow"; }

    }

    如果一个合约从一个抽象合约里继承,但却没实现所有函数,那么它也是一个抽象合约。

    ()接口(interface)

    接口与抽象合约类似,与之不同的是,接口内没有任何函数是已实现的,同时还有如下限制:

    1.    不能继承其它合约,或接口。

    2.    不能定义构造器

    3.    不能定义变量

    4.    不能定义结构体

    5.    不能定义枚举类

    其中的一些限制可能在未来放开。

    接口基本上限制为合约ABI定义可以表示的内容,ABI和接口定义之间的转换应该是可能的,不会有任何信息丢失。

    接口用自己的关键词表示:

    interface Token {

    function transfer(address recipient, uint amount);

    }

    合约可以继承于接口,因为他们可以继承于其它的合约。

    pragma solidity ^0.4.11;

    interface tokenRecipient {functionreceiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }//使用则是在合同函数中直接使用:

    functionapproveAndCall(address _spender, uint256 _value, bytes _extraData)

    public

    returns (bool success) {

    tokenRecipient spender= tokenRecipient(_spender);

    ()library库

    使用库合约的合约,可以将库合约视为隐式的父合约(base contracts),当然它们不会显式的出现在继承关系中。意思就是不用写is来继承,直接可以在合约中使用:

    library Set {

    struct Data { mapping(uint => bool) flags; }

    }

    contract C {

    Set.Data knownValues;

    }

    但调用库函数的方式非常类似,如库L有函数f(),使用L.f()即可访问。此外,internal的库函数对所有合约可见,如果把库想像成一个父合约就能说得通了。当然调用内部函数使用的是internal的调用惯例,这意味着所有internal类型可以传进去,memory类型则通过引用传递,而不是拷贝的方式。

    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.

    functioninsert(Data storage self, uint value)

    returns (bool)

    {if(self.flags[value])return false; //already there

    self.flags[value] = true;return true;

    }functionremove(Data storage self, uint value)

    returns (bool)

    {if (!self.flags[value])return false; //not there

    self.flags[value] = false;return true;

    }functioncontains(Data storage self, uint value)

    returns (bool)

    {returnself.flags[value];

    }

    }

    contract C {

    Set.Data knownValues;functionregister(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.

    }

    上面的例子中:

    •    Library定义了一个数据结构体struct Data,用来在调用的合约中使用(库本身并未实际存储的数据)。如果函数需要操作数据,这个数据一般是通过库函数的第一个参数传入(Data storage self),按惯例会把参数名定为self。

    •    另外一个需要留意的是上例中self的类型是storage,那么意味着传入的会是一个引用,而不是拷贝的值,那么修改它的值,会同步影响到其它地方,俗称引用传递,非值传递。

    •    库函数的使用不需要实例化,c.register中可以看出是直接使用Set.insert。但实际上当前的这个合约本身就是它的一个实例。

    •    这个例子中,c可以直接访问,knownValues。虽然这个值主要是被库函数使用的

    对比普通合约来说,库的限制:

    •    无状态变量(state variables)。

    •    不能继承或被继承

    •    不能接收ether。

    附着库(Using for)

    指令using A for B;用来附着库里定义的函数(从库A)到任意类型B。这些函数将会默认接收调用函数对象的实例作为第一个参数。语法类似,python中的self变量一样。

    using A for *的效果是,库A中的函数被附着在做任意的类型上。

    在这两种情形中,所有函数,即使那些第一个参数的类型与调用函数的对象类型不匹配的,也被附着上了。类型检查是在函数被真正调用时,函数重载检查也会执行。

    using A for B;指令仅在当前的作用域有效,且暂时仅仅支持当前的合约这个作用域,后续也非常有可能解除这个限制,允许作用到全局范围。如果能作用到全局范围,通过引入一些模块(module),数据类型将能通过库函数扩展功能,而不需要每个地方都得写一遍类似的代码了。

    上面的例子就改成了:

    contract C {

    using Setfor Set.Data; //this is the crucial change

    Set.Data knownValues;functionregister(uint value) {//Here, all variables of type Set.Data have

    //corresponding member functions.

    //The following function call is identical to

    //Set.insert(knownValues, value)

    if (!knownValues.insert(value))throw;

    }

    }

    //其实就是本来要访问的话的语句是Set.insert(knownValues, value),现在是knownValues.insert(value),即将库中的函数都附着在了结构体struct Data上,因为之前库函数的第一个参数的声明其实也是这个结构体(self),附着后调用时就可以省略掉第一个参数了,就算没有结构体,那么附着的一定是函数的第一个参数声明的那个self的类型

    如:

    library Search {functionindexOf(uint[] storage self, uint value)

    }

    contract C {

    using Searchforuint[];

    uint[] data;

    }

    即 library 库,有两种作用

    《1》定义 library,使用结构体,调用函数

    pragma solidity ^0.4.20;//This is the same code as before, just without comments

    library Set {

    struct Data { mapping(uint=>bool) flags; }functioninsert(Data storage self, uint value)

    public

    returns (bool)

    {if(self.flags[value])return false; //already there

    self.flags[value] = true;return true;

    }functionremove(Data storage self, uint value)

    public

    returns (bool)

    {if (!self.flags[value])return false; //not there

    self.flags[value] = false;return true;

    }functioncontains(Data storage self, uint value)

    public

    view

    returns (bool)

    {returnself.flags[value];

    }

    }//然后合约中调用库中的函数

    contract C {

    using Setfor Set.Data; //this is the crucial change

    Set.Data knownValues;functionregister(uint value) public {//Here, all variables of type Set.Data have

    //corresponding member functions.

    //The following function call is identical to

    //`Set.insert(knownValues, value)`

    require(knownValues.insert(value));

    }

    }

    《2》 使用库来扩展数据类型

    pragma solidity ^0.4.20;

    library Search {functionindexOf(uint[] storage self, uint value)

    public

    view

    returns (uint)

    {for (uint i = 0; i < self.length; i++)if (self[i] == value) returni;return uint(-1);

    }

    }

    contract C {

    using Searchfor uint[];//使得类型为uint[]的数值可以有函数indexOf进行使用

    uint[] data;functionappend(uint value) public {

    data.push(value);

    }functionreplace(uint _old, uint _new) public {//This performs the library function call

    uint index =data.indexOf(_old);if (index == uint(-1))

    data.push(_new);elsedata[index]=_new;

    }

    }

    ()如果你想要把数据写到交易中:

    参考:https://blog.csdn.net/koastal/article/details/78794275

    let Web3 = require("web3");

    let fs= require("fs");

    let web3= newWeb3();

    web3.setProvider(new Web3.providers.HttpProvider("http://192.168.1.10:8545"));

    let log={

    time:(newDate).getTime(),

    type:"error",

    msg:"数据库连接失败"};

    let str=JSON.stringify(log);//将json转换为string

    let data= Buffer.from(str).toString('hex');

    data= '0x'+data;

    console.log(data);//将数据写入到交易中

    let coinbase = "0x8a1C505f1ff14045c03622E9ab82EB19c730cef3";

    let user1= "0xb4B6338977078f1c355ee394D67D6DFE5C24dad8";

    web3.personal.unlockAccount(coinbase,"coinbase");

    let address=web3.eth.sendTransaction({

    from:coinbase,

    to:user1,

    value:'0x00',

    data:data

    });//从交易地址获取数据

    let transaction =web3.eth.getTransaction(address);

    let inputData=transaction.input;

    let res_str= Buffer.from(inputData.replace('0x',''),'hex').toString();

    let res_json=JSON.parse(res_str);//将string转换为json

    console.log(transaction);

    console.log(res_json);

    web3.eth.sendTransaction(transactionObject [, callback])

    参数:

    transactionObject : Object - 要发送的交易对象。

    from: String - 指定的发送者的地址。如果不指定,使用web3.eth.defaultAccount。

    to: String - (可选)交易消息的目标地址,如果是合约创建,则不填.

    value: Number|String|BigNumber - (可选)交易携带的货币量,以wei为单位。如果合约创建交易,则为初始的基金。

    gas: Number|String|BigNumber - (可选)默认是自动,交易可使用的gas,未使用的gas会退回。

    gasPrice: Number|String|BigNumber - (可选)默认是自动确定,交易的gas价格,默认是网络gas价格的平均值 。

    data: String - (可选)或者包含相关数据的字节字符串,如果是合约创建,则是初始化要用到的代码。

    nonce: Number - (可选)整数,使用此值,可以允许你覆盖你自己的相同nonce的,正在pending中的交易11。

    Function - 回调函数,用于支持异步的方式执行7。

    返回值:

    String - 32字节的交易哈希串。用16进制表示。

    web3.eth.getTransaction(transactionHash [, callback])

    参数:

    transactionHash: String - 交易的哈希值。

    callback: Function - 回调函数,用于支持异步的方式执行7。

    返回值:

    Object - 一个交易对象

    hash: String - 32字节,交易的哈希值。

    nonce: Number - 交易的发起者在之前进行过的交易数量。

    blockHash: String - 32字节。交易所在区块的哈希值。当这个区块处于pending将会返回null。

    blockNumber: Number - 交易所在区块的块号。当这个区块处于pending将会返回null。

    transactionIndex: Number - 整数。交易在区块中的序号。当这个区块处于pending将会返回null。

    from: String - 20字节,交易发起者的地址。

    to: String - 20字节,交易接收者的地址。当这个区块处于pending将会返回null。

    value: BigNumber - 交易附带的货币量,单位为Wei。

    gasPrice: BigNumber - 交易发起者配置的gas价格,单位是wei。

    gas: Number - 交易发起者提供的gas。.

    input: String - 交易附带的数据。

    结果为:

    0x7b2274696d65223a313531333135363433363137392c2274797065223a226572726f72222c226d7367223a22e695b0e68daee5ba93e8bf9ee68ea5e5a4b1e8b4a5227d{ blockHash:'0x0000000000000000000000000000000000000000000000000000000000000000',

    blockNumber:null,

    from:'0x8a1c505f1ff14045c03622e9ab82eb19c730cef3',

    gas:90000,

    gasPrice: { [String:'18000000000'] s: 1, e: 10, c: [ 18000000000] },

    hash:'0x3149578fbb8cf75f264fc87426b7bfa2a89256763fe5194ccad8b724a0325470',

    input:'0x7b2274696d65223a313531333135363433363137392c2274797065223a226572726f72222c226d7367223a22e695b0e68daee5ba93e8bf9ee68ea5e5a4b1e8b4a5227d',

    nonce:57,

    to:'0xb4b6338977078f1c355ee394d67d6dfe5c24dad8',

    transactionIndex:0,

    value: { [String:'0'] s: 1, e: 0, c: [ 0] },

    v:'0x26',

    r:'0x742335f7b649c554a35baf624fe90c8e3fa3c9cfc116cfe858af420dc893359a',

    s:'0x20a63d760ab574736aaed4799f2a15bc727a988b4d41ee279e3b753b1c1f3913'}

    { time:1513156436179, type: 'error', msg: '数据库连接失败' }

    展开全文
  • pragma solidity ^0.4.0; contract stringtest1{ string testword='helloworld'; //68656c6c6f776f726c64为这串字符的16进制 function stringlength() public view returns (uint){ //r...
  • 1.solidity的四种可见度/访问权限 public:任何人都可以调用该函数,包括DApp的使用者。 private:只有合约本身可以调用该函数(在另一个函数中)。 internal:只有这份合同以及由此产生的所有合同才能称之为合同。...
  • Solidity学习笔记

    2021-05-24 15:21:43
    Solidity学习笔记 Solidity是为了编写智能合约而创建的编程语言,Solidity运行在以太坊虚拟机中(EVM)。 数据类型 值类型 值类型被用作函数参数或者用在赋值语句中时,总会进行值拷贝 bool 取值为字面常数值 true ...
  • solidity学习

    千次阅读 2021-12-11 20:34:15
    pragma solidity ^0.4.25; contract Asset { address public issuer; mapping (address => uint) public balances; event Sent(address from ,address to ,uint amount);//事件从一个地址发到另一个地址,发了...
  • solidity 合约文件结构 合约文件: 版本申明 合约主体: 状态变量 函数 结构类型 事件 函数修改器 代码注释 例子: pragma solidity ^0.4.0; import ""; contract Test { //状态变量 uint a; ...
  • solidity学习过程

    2021-01-15 11:36:21
    solidity学习过程+函数构造器constructor()+智能合约实例化
  • solidity 学习

    2018-04-16 20:09:13
    pragma solidity ^0.4.0; contract Person { //属性的默认权限是internal 方法的默认权限是public //只有public 权限的属性可以供外部访问。其他需要get set访问 //通过this(指针)只能访问public的方法。其他...
  • Solidity学习(3)

    千次阅读 2017-12-14 20:19:33
    1. Solidity源文件的布局源文件包括任意数量的合约定义、include指令和pragma指令。1.1 版本pragma源文件可以(应该)用一个所谓的版本注释来注释,以防止被编译以后可能会引入不兼容的编译器版本。 我们试图将这种...
  • Solidity学习(二)

    2021-01-09 21:00:42
    一、string如何存储中文字符 二、固长数组 三、动态数组 四、二维数组 五、动态二维数组 一、string如何存储中文字符 先看一段代码: pragma solidity >=0.4.22 ; contract Test { string name = "张三"; function ...
  • 学习其他语言时,感觉构造函数总是先执行,然后才能获取这个类。但是在智能合约中,先产生智能合约,然后在走构造函数。~~删除线格式~~ 涉及到this能否在构造函数中使用问题 因为智能合约的先产生,那我们可以在...
  • 在之后的内容中,我将分享我在solidity学习的过程中的每一步,比如遇到的难题,解决的思路等等。希望可以给和我一样刚刚上手的朋友一些帮助。也希望有朋友可以将我理解错误的地方指出。大家互相讨论,共同进步。 ...
  • pragma solidity ^0.4.0; ​ contract InfoFeed { ​ function info() payable returns (uint ret) { // 如果这里有payable,说明该函数外部调用的时候必须发送ether和gas return msg.value; } } ​ contract ...
  • 请结合网站 https://cryptozombies.io 一起学习,下面是自己的学习记录。 一、创建空合约 0.4.19(是基于这个版本编译的)   二、状态变量和整数 状态变量:状态变量是被永久地保存在合约中。也就是说它们...
  • pragma solidity ^0.4.16; /** * The SpecialVariables contract does this and that... 1.在属性定义的时候可以初始化,也可以调用全局变量,但是不能调用系统函数, 2.在构造函数里可以调用系统函数。 3.如果...
  • solidity学习笔记

    千次阅读 2021-07-20 19:30:38
    一些零散知识,记录下,学习时用的是remix在线编辑器 1.pure与view关键字 只要有了pure与view修饰符的函数,那么调用函数就不会消耗gas view: 可以自由调用,因为它只是“查看”区块链的状态而不改变它,函数输出...
  • 关于搭建的第一个solidity ----Hello World pragma solidity ^0.5.0; contract Hello { string public name; function sayhello() public returns (string memory){ name = 'hello world'; return ...
  • 使用remix开发/测试,通过对一个简版代币合约开发,学习代币逻辑与solidity语法 // 表明版本大于0.4.22 pragma solidity >0.4.22; // 一个合约 contract Coin { // 地址类型成员变量,访问权限public ...
  • solidity学习总结

    2019-02-27 20:42:23
    方法是让 Solidity 和以太坊变得如此酷的一部分 —— 它们是一种可以接收以太的特殊函数。 22.用 keccak256 来制造随机数 Solidity 中最好的随机数生成器是 keccak256 哈希函数,不安全,但一般时候够用,只要...
  • Solidity学习记录——第三章

    千次阅读 2021-09-26 13:41:41
    Solidity学习记录 第一章 创建生产僵尸的工厂 第二章 设置僵尸的攻击功能 第三章 编写DAPP所需的基础理论 文章目录Solidity学习记录前言一、本章主要目的二、学习过程1.本节课程知识点2.最终代码总结 前言 因为...
  • solidity学习记录 storage与memory // SPDX-License-Identifier: MIT pragma solidity ^0.7.0; contract structtest{ struct student{ uint age; string name; } student stu; //默认storage类型 // storage...
  • pragma solidity>=0.5.0; contract getter{ //public修饰符默认生成的get方法,供我们外部调用 uint public num = 100; //默认生成的get函数时external权限的,不能在合约的内部调用 function test() ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,548
精华内容 2,219
关键字:

solidity学习