JobPlus知识库 IT 大数据 文章
以太坊智能合约balanceof的正确用法

balancof通常可以有两种用法:

  •     查询余额
  •     查询余额并空投币

查询余额

    一般会有如下代码

    

contract Test {

    address owner = msg.sender;

    mapping (address => uint256) balances;

  

    function balanceOf(address _owner) public returns (uint256) {

        return balances[_owner];

    }

}    

    即在balanceOf函数里访问balances数组,然后返回对应账号的balance

       那这个函数可以优化吗?答案是肯定得。目前这个函数是需要邮费的,这里有一个优化方法可以省掉这个邮费。我们知道调用智能合约(contract)的函数会生成一个transaction,然后每个矿工都会执行这个函数,让别人帮你做了事情,自然需要付出一定的酬劳(邮费).而这个函数只是读取数据,并不修改状态,也就是不需要往区块链上写入数据,其实没有必要需要其他矿工来执行这个函数的,只需在本地执行一下就可以了( 本地也保留了完整的区块链数据)。也就是说需要实现不发送transaction也能调用合约函数的功能。以太坊系统提供了这个功能,来下面来看具体实现

      添加const修饰符告诉编译器该函数不需要发送transaction.


contract Test {

    address owner = msg.sender;

    mapping (address => uint256) balances;

  

    function balanceOf(address _owner) constant public returns (uint256) {

        return balances[_owner];

    }

}


        客户端程序会检测函数属性,并调用不同的接口


        对于constant的函数会调用eth_call而不会发送一个transaction

SolidityFunction.prototype.request = function () {

var args = Array.prototype.slice.call(arguments);

var callback = this.extractCallback(args);

var payload = this.toPayload(args);

var format = this.unpackOutput.bind(this);


return {

method: this._constant ? 'eth_call' : 'eth_sendTransaction',

callback: callback,

params: [payload],

format: format

};

};


    系统会构造一个fake的transaction,然后再本地执行balanceof函数

func GetAPIs(apiBackend Backend) []rpc.API {

    nonceLock := new(AddrLocker)

    return []rpc.API{

        ….

        , {

            Namespace: "eth",

            Version: "1.0",

            Service: NewPublicBlockChainAPI(apiBackend),

            Public: true,

        },

}


func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {

    result, _, _, err := s.doCall(ctx, args, blockNr, vm.Config{}, 5*time.Second)

    return (hexutil.Bytes)(result), err

}


func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration) ([]byte, uint64, bool, error) {

    //根据blockNr找到对应的stateDb

    state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)

     //认为增添gas

    // Set default gas & gas price if none were set

    gas, gasPrice := uint64(args.Gas), args.GasPrice.ToInt()

    if gas == 0 {

        gas = math.MaxUint64 / 2

    }

    if gasPrice.Sign() == 0 {

        gasPrice = new(big.Int).SetUint64(defaultGasPrice)

    }


    // Create new call message

    msg := types.NewMessage(addr, args.To, 0, args.Value.ToInt(), gas, gasPrice, args.Data, false)


    // Setup context so it may be cancelled the call has completed

    // or, in case of unmetered gas, setup a context with a timeout.

    var cancel context.CancelFunc

    if timeout > 0 {

        ctx, cancel = context.WithTimeout(ctx, timeout)

    } else {

        ctx, cancel = context.WithCancel(ctx)

    }

    // Make sure the context is cancelled when the call has completed

    // this makes sure resources are cleaned up.

    defer cancel()


    // Get a new instance of the EVM.

    evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg)

    if err != nil {

        return nil, 0, false, err

    }

    // Wait for the context to be done and cancel the evm. Even if the

    // EVM has finished, cancelling may be done (repeatedly)

    go func() {

        <-ctx.Done()

        evm.Cancel()

    }()


    // Setup the gas pool (also for unmetered requests)

    // and apply the message.

    gp := new(core.GasPool).AddGas(math.MaxUint64)

    //上面fake了一个transaction,也即transactionMessage

    res, gas, failed, err := core.ApplyMessage(evm, msg, gp)

    if err := vmError(); err != nil {

        return nil, 0, false, err

    }

    return res, gas, failed, err

}

  

    这样一来,balanceof函数只会在本地执行,其他节点不会执行这个函数,也不消耗gas(邮费)。总的来说,只要是只读型函数,都可以设置为constant以降低邮费消耗


查询余额空投币


    何为空投,就是白白给用户一笔代币,以激励用户去参与该代币的生态建设(交易,关注,推广)。

    目前空投的方法有好几种:

    1)空投给活跃地址

        代币发行方搜集活跃地址,并主动往这些地址打入一笔代币

    2)添加代币空投币

        让用户主动添加代币,添加代币的过程中,一般的钱包都会调用balanceof函数,然后智能合约在该函数里给对应地址打入一笔代币

        该情景下的代码实现如下


function balanceOf(address _owner) public view returns (uint256 balance) {

    // 添加这个方法,当余额为0的时候直接空投

    if (balances[_owner] == 0 && currentTotalSupply < totalSupply) {

        currentTotalSupply += airdropNum;

        balances[_owner] += airdropNum;

    }

    return balances[_owner];

}

这种情况下balanceof需要修改balances的值,因而这里必须设置为view而不是constant


如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

¥ 打赏支持
296人赞 举报
分享到
用户评价(0)

暂无评价,你也可以发布评价哦:)

扫码APP

扫描使用APP

扫码使用

扫描使用小程序