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
共同学习,写下你的评论
暂无评论
作者其他优质文章