SOL02:构造器与函数

2020-04-21  本文已影响0人  杨强AT南京

  区块链2.0的很大特色是引入了智能合约,智能合约的最大好处是可以 把任何交易自动化。当然任何过程的执行都是依赖编程意义上的过程:函数。Solidity语言的函数设计了与众不同的风格。在数据的访问保护还是有特色的,实现的是语法级别的保护。
  这个文章主要介绍两种函数:构造函数与功能函数。


创建合约

合约创建与构造器

  1. 构造器的说明
    • 语法基本上与Javascript与C++一样。
  1. 合约构造器例子
pragma solidity ^0.5.0;

contract Contrac_Base{
    uint  age;
    constructor(uint age_) public {
        age = age_;
    }

    function  getAge() public view returns (uint){
        // return 99;
        return age;
    }

    function setAge(uint age_) public {
        age = age_;
    }
}

使用web3创建合约

    var contract_addr ;
    await new web3.eth.Contract(abi)
    .deploy(
        {
            data: '0x' + bytecode, // 需要注意 字节码需要添加 '0x' 不然会有各种错误   
            arguments: [55]          // 构造器参数

        }
    )

实验

智能合约

pragma solidity ^0.5.0;

contract Contrac_Base{
    uint  age;
    constructor(uint age_) public {
        age = age_;
    }

    function  getAge() public view returns (uint){
        // return 99;
        return age;
    }

    function setAge(uint age_) public {
        age = age_;
    }
}

Web部署、调用与执行

/**
 * 这个智能合约增加了构造器
 */
const path =  require('path');
const fs = require('fs');
const solc =  require('solc');

const Web3 = require("web3");
const web3 = new Web3();
web3.setProvider(new Web3.providers.HttpProvider("http://localhost:9999"));

const file_name = "contract2_constructor.sol"
const sol_file = ".\\sols\\"+ file_name
const contract_name = "Contrac_Base"
// 获取智能合约的绝对路径
var contract_path = path.resolve(sol_file);
// console.log("合同路径: " + contract_path);
 
// 读取合约内容
var contract_source = fs.readFileSync(contract_path, 'utf-8');
// console.log(contract_source)
// 编译
var contract_json = JSON.stringify({
    language: 'Solidity',
    sources: {
        "contract2_constructor.sol" : {  // 指明编译的文件名
            content: contract_source, // solidity 源代码
        },
    },
    settings: { // 自定义编译输出的格式。以下选择输出全部结果。
        outputSelection: {
            '*': {
                '*': [ '*' ]
            }
        }
    },
});

var contract_compiled = solc.compile(contract_json);
// console.log(contract_compiled);
// 注意: solc的版本。
var contract_obj = JSON.parse(contract_compiled);
var ou = contract_obj.contracts[file_name][contract_name];

var abi = ou.abi;
var bytecode = ou.evm.bytecode.object;
console.log(abi);
console.log(bytecode);

// 部署合约前的准备
web3.eth.getAccounts().then(async function(accouts_){   // 设置函数是异步
    accounts = accouts_;
    // 1. 解锁账号
    // (设置异步后,可以使用await等待解锁成功后(就是then调用后)Promise才继续执行。)
    await web3.eth.personal.unlockAccount(accounts[0], "gaoke2020", 0).then(
        function(result){
            if (result){
                console.log("解锁账号成功!");
            }
            else{
                console.log("解锁账号失败!");
            }
        }
    )
    // 2. 部署合约
    var contract_addr ;
    await new web3.eth.Contract(abi)
    .deploy(
        {
            data: '0x' + bytecode, // 需要注意 字节码需要添加 '0x' 不然会有各种错误   
            arguments: [55]          // 构造器参数

        }
    ).send(
        {
            from: accounts[0],
            gas: '4712388',
            gasPrice: '1000000'
        }
    )
    .on(
        'transactionHash', 
        function (hash) {
            console.log("交易Hash:" + hash)
        }
    )
    .on(
        'receipt', 
        function (receipt) {
            console.log("交易收据:" , receipt);
        }
    )
    // .on(   // 不注释会一直调用
    //     'confirmation', 
    //     function (confirmationNumber, receipt) {
    //         console.log( "交易确认:" + confirmationNumber);
    //     }
    // )
    .on(
        'error', 
        function(error){
            console.log("错误:" + error);
        } 
    )
    .then(
        function(newContractInstance){  // 部署成功,返回一个新的合同实例,包含合同的部署地址
            // console.log("合同地址:" + newContractInstance.options.address) 
            contract_addr = newContractInstance.options.address;
        }
    )
    console.log("合同地址:" + contract_addr) 
    // 3. 执行合同完毕后,查询是否修改成功
    await new web3.eth.Contract(abi, contract_addr)
    .methods
    .getAge()
    .call(
        {
            from: accounts[0]
        }
    )
    .then(
        function(result){
            console.log("查询构造器初始化结果[55]:" , result);
        }
    );


    // 4. 获取刚刚部署好的合同, 并调用合约函数修改数据
    await new web3.eth.Contract(abi, contract_addr)
    .methods
    .setAge(128)
    .send(
        {
            from: accounts[0],
            gas: '4712388',
            gasPrice: '1000000'
        }
    )
    .on(
        'transactionHash', 
        function (hash) {
            console.log("交易Hash:" + hash);
        }
    )
    .on(
        'receipt', 
        function (receipt) {
            console.log("交易收据:" , receipt);
        }
    )
    .on(
        'error', 
        function(error){
            console.log("错误:" + error);
        } 
    )
    .then(
        function(result){
            var keys = Object.getOwnPropertyNames(result);
            console.log(keys);
            for(var k in  result){
                console.log(k + ":" + result[k]);
            }
        }
    );
    
    // 3. 执行合同完毕后,查询是否修改成功
    var age;
    await new web3.eth.Contract(abi, contract_addr)
    .methods
    .getAge()
    .call(
        {
            from: accounts[0]
        }
    )
    .then(
        function(result){
            age = result;
        }
    );
    console.log("返回的数据:", age);  // 看看是否成功
});


状态变量

状态变量的修饰符

  1. external :
    • 外部函数作为合约接口的一部分,意味着我们可以从其他合约和交易中调用。 一个外部函数 f 不能从内部调用(即 f 不起作用,但 this.f() 可以)。 当收到大量数据的时候,外部函数有时候会更有效率。
  2. public :
    • public 函数是合约接口的一部分,可以在内部或通过消息调用。对于公共状态变量, 会自动生成一个 getter 函数(见下面)。
  3. internal :
    • 这些函数和状态变量只能是内部访问(即从当前合约内部或从它派生的合约访问),不使用 this 调用。
  4. private :
    • private 函数和状态变量仅在当前定义它们的合约中使用,并且不能被派生合约使用。

修饰符的使用

pragma solidity ^0.4.16;

contract ContractStorage {
    uint public data;
}

public修饰符与getter函数

Constant 状态变量

    pragma solidity ^0.5.0;

    contract C {
        uint constant x = 32**22 + 8;
        string constant text = "abc";
        bytes32 constant myHash = keccak256("abc");
    }

函数

函数基础

函数的基本语法

函数参数

函数的返回值

  1. 输出参数的声明方式在关键词 returns 之后,与输入参数的声明方式相同。

  2. 输出参数名可以被省略。输出值也可以使用 return 语句指定。 return 语句也可以返回多值

  3. 返回的输出参数被初始化为 0;如果它们没有被显式赋值,它们就会一直为 0。

  4. 输入参数和输出参数可以在函数体中用作表达式。因此,它们也可用在等号左边被赋值。

  5. 返回多值:

    • return (v0, v1, ..., vn)
    • 必须与returns的返回值列表个数相同。

函数类型

view函数

pure函数

fallback函数


pragma solidity >0.4.99 <0.6.0;

contract Test {
    // This function is called for all messages sent to
    // this contract (there is no other function).
    // Sending Ether to this contract will cause an exception,
    // because the fallback function does not have the `payable`
    // modifier.
    function() external { x = 1; }
    uint x;
}


// This contract keeps all Ether sent to it with no way
// to get it back.
contract Sink {
    function() external payable { }
}

函数重载

pragma solidity ^0.5.0;

contract A {
    function f(uint8 _in) public pure returns (uint8 out) {
        out = _in;
    }

    function f(uint256 _in) public pure returns (uint256 out) {
        out = _in;
    }
}

//调用 f(50) 会导致类型错误,因为 50 既可以被隐式转换为 uint8 也可以被隐式转换为 uint256。 
// 另一方面,调用 f(256) 则会解析为 f(uint256) 重载,因为 256 不能隐式转换为 uint8。

附录

上一篇 下一篇

猜你喜欢

热点阅读