以太坊智能合约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
/********************************
* 本文来自CSDN博主"爱踢门"
* 转载请标明出处 : http://blog.csdn.net/itleaks
******************************************/


猜你喜欢

转载自blog.csdn.net/itleaks/article/details/80333866