以太坊智能合约balanceof的正确用法
Category : 区块链CAT2——以太坊编程
balancof通常可以有两种用法:
- 查询余额
- 查询余额并空投币
查询余额
一般会有如下代码
[codesyntax lang=”java”]
contract Test {
address owner = msg.sender;
mapping (address => uint256) balances;
function balanceOf(address _owner) public returns (uint256) {
return balances[_owner];
}
}
[/codesyntax]
即在balanceOf函数里访问balances数组,然后返回对应账号的balance
那这个函数可以优化吗?答案是肯定得。目前这个函数是需要邮费的,这里有一个优化方法可以省掉这个邮费。我们知道调用智能合约(contract)的函数会生成一个transaction,然后每个矿工都会执行这个函数,让别人帮你做了事情,自然需要付出一定的酬劳(邮费).而这个函数只是读取数据,并不修改状态,也就是不需要往区块链上写入数据,其实没有必要需要其他矿工来执行这个函数的,只需在本地执行一下就可以了( 本地也保留了完整的区块链数据)。也就是说需要实现不发送transaction也能调用合约函数的功能。以太坊系统提供了这个功能,来下面来看具体实现
添加const修饰符告诉编译器该函数不需要发送transaction.
[codesyntax lang=”java”]
contract Test {
address owner = msg.sender;
mapping (address => uint256) balances;
function balanceOf(address _owner) constant public returns (uint256) {
return balances[_owner];
}
}
[/codesyntax]
客户端程序会检测函数属性,并调用不同的接口
对于constant的函数会调用eth_call而不会发送一个transaction
[codesyntax lang=”java”]
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
};
};
[/codesyntax]
系统会构造一个fake的transaction,然后再本地执行balanceof函数
[codesyntax lang=”java”]
[/codesyntax]
这样一来,balanceof函数只会在本地执行,其他节点不会执行这个函数,也不消耗gas(邮费)。总的来说,只要是只读型函数,都可以设置为constant以降低邮费消耗
查询余额空投币
何为空投,就是白白给用户一笔代币,以激励用户去参与该代币的生态建设(交易,关注,推广)。
目前空投的方法有好几种:
1)空投给活跃地址
代币发行方搜集活跃地址,并主动往这些地址打入一笔代币
2)添加代币空投币
让用户主动添加代币,添加代币的过程中,一般的钱包都会调用balanceof函数,然后智能合约在该函数里给对应地址打入一笔代币
该情景下的代码实现如下
[codesyntax lang=”java”]
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
}
function balanceOf(address _owner) public view returns (uint256 balance) {
// 添加这个方法,当余额为0的时候直接空投
if (balances[_owner] == 0 && currentTotalSupply < totalSupply) {
currentTotalSupply += airdropNum;
balances[_owner] += airdropNum;
}
return balances[_owner];
}
[/codesyntax]
这种情况下balanceof需要修改balances的值,因而这里必须设置为view而不是constant
/********************************
******************************************/