用户定义的智能合约
原文:https://docs.elrond.com/developers/gas-and-fees/user-defined-smart-contracts
对于用户定义的智能合约部署和函数调用,处理的实际气体消耗包含前面提到的两个成本组成部分——尽管值移动和数据处理组成部分很容易计算(使用前面描述的公式),但是合约执行组成部分很难精确确定先验。因此,对于该组件,我们必须依靠模拟和估计。
对于模拟,我们将使用erdpy启动一个本地测试网(详细的设置说明可以在这里找到)。因此,在继续之前,确保您的本地 testnet 已经启动并运行。
合约部署
为了获得部署交易所需的gasLimit(实际天然气成本),应该使用众所周知的erdpy contract deploy命令,但是设置了--simulate标志。
首先,为gas-limit传递最大可能量(无猜测)。
$ erdpy --verbose contract deploy --bytecode=./counter.wasm \
--recall-nonce --gas-limit=600000000 \
--pem=~/elrondsdk/testwallets/latest/users/alice.pem \
--simulate
在输出中,寻找txGasUnits。例如:
"txSimulation": {
...
"cost": {
"txGasUnits": 1849711,
...
}
}
注
模拟成本txGasUnits包含成本的两个组成部分。
之后,通过再次运行模拟来检查成本模拟,但这次使用精确的gas-limit:
$ erdpy --verbose contract deploy --bytecode=./counter.wasm \
--recall-nonce --gas-limit=1849711 \
--pem=~/elrondsdk/testwallets/latest/users/alice.pem \
--simulate
在输出中,查找status——应该是success:
"txSimulation": {
"execution": {
"result": {
"status": "success",
...
},
...
}
最后,让我们实际部署合约:
$ erdpy --verbose contract deploy --bytecode=./counter.wasm \
--recall-nonce --gas-limit=1849711 \
--pem=~/elrondsdk/testwallets/latest/users/alice.pem \
--send --wait-result
重要
对于部署,成本的执行部分与实例化智能合约和调用其init()函数相关联。
如果init()的流程依赖于输入参数或者它引用了区块链数据,那么成本也会根据这些变量而变化。确保您模拟了足够的部署场景,并增加(减少)了gas-limit。
合约调用
为了获得合约调用所需的gasLimit(实际天然气成本)应该首先部署合约,然后使用erdpy contract call命令,并设置--simulate标志。
**##### 重要
如果该合约进一步调用其他合约,请阅读下一节。
假设我们已经部署了合约(见上文),让我们获得调用它的一个端点的成本:
$ erdpy --verbose contract call erd1qqqqqqqqqqqqqpgqygvvtlty3v7cad507v5z793duw9jjmlxd8sszs8a2y \
--pem=~/elrondsdk/testwallets/latest/users/alice.pem \
--function=increment\
--recall-nonce --gas-limit=600000000\
--simulate
在输出中,寻找txGasUnits。例如:
"txSimulation": {
...
"cost": {
"txGasUnits": 1225515,
...
}
}
最后,让我们称之为合约:
$ erdpy --verbose contract call erd1qqqqqqqqqqqqqpgqygvvtlty3v7cad507v5z793duw9jjmlxd8sszs8a2y \
--pem=~/elrondsdk/testwallets/latest/users/alice.pem \
--function=increment\
--recall-nonce --gas-limit=1225515\
--send --wait-result
重要
如果被调用函数的流程依赖于输入参数或者它引用区块链数据,那么成本也会根据这些变量而变化。确保您为合约呼叫模拟了足够多的场景,并增加(减少)了gas-limit。
合约调用(异步)其他合约
重要
本节中的文档是初步的,可能会更改。此外,仅涵盖异步调用。
在继续之前,请确保您首先浏览了以下内容:
假设我们有两个合约:A和B,其中A::foo(addressOfB)异步调用B::bar()(例如使用asyncCall())。
让我们部署合约A和B:
$ erdpy --verbose contract deploy --bytecode=./a.wasm \
--recall-nonce --gas-limit=5000000 \
--pem=~/elrondsdk/testwallets/latest/users/alice.pem \
--send --wait-result --outfile=a.json
$ erdpy --verbose contract deploy --bytecode=./b.wasm \
--recall-nonce --gas-limit=5000000 \
--pem=~/elrondsdk/testwallets/latest/users/alice.pem \
--send --wait-result --outfile=b.json
假设A在erd1qqqqqqqqqqqqqpgqfzydqmdw7m2vazsp6u5p95yxz76t2p9rd8ss0zp9ts展开,B在erd1qqqqqqqqqqqqqpgqj5zftf3ef3gqm3gklcetpmxwg43rh8z2d8ss2e49aq展开,让模拟 A::foo(addressOfB)(首先通过一个足够大的或最大的gas-limit):
$ export hexAddressOfB=0x$(erdpy wallet bech32 --decode erd1qqqqqqqqqqqqqpgqj5zftf3ef3gqm3gklcetpmxwg43rh8z2d8ss2e49aq)
$ erdpy --verbose contract call erd1qqqqqqqqqqqqqpgqfzydqmdw7m2vazsp6u5p95yxz76t2p9rd8ss0zp9ts \
--pem=~/elrondsdk/testwallets/latest/users/alice.pem \
--function=foo\
--recall-nonce --gas-limit=50000000\
--arguments ${hexAddressOfB}\
--simulate
在输出中,查找模拟成本(如上所示):
"txSimulation": {
...
"cost": {
"txGasUnits": 3473900,
...
}
}
模拟成本代表调用A::foo()、B::bar()和A::callBack()的实际天然气成本。
然而,上面的模拟成本并不是我们要用作gasLimit的值。如果我们这样做,就会出现错误not enough gas。
在到达对A::foo()内部B::bar()的调用时,Elrond VM 在运行时检查剩余气体和临时锁定(保留)其一部分,以允许一旦对B::bar()的调用返回就执行A::callBack()。
关于 VM 气体调度,前述运行时 的剩余气体必须满足以下条件,以使临时气锁保留成功:***
onTheSpotRemainingGas > gasToLockForCallback
gasToLockForCallback =
costOf(AsyncCallStep) +
costOf(AsyncCallbackGasLock) +
codeSizeOf(callingContract) * costOf(AoTPreparePerByte)
注
后续的异步调用(由异步调用合约执行的异步调用)也需要临时气锁。
在我们的例子中,A有 453 个字节,那么gasToLockForCallback就是(截至 2022 年 2 月):
gasToLockForCallback = 100000 + 4000000 + 100 * 453 = 4145300
因此,gasLimit的值应该是:
simulatedCost < gasLimit < simulatedCost + gasToLockForCallback
对于我们的示例,这将是:
3473900 < gasLimit < 7619200
重要
截至 2022 年 2 月,对于调用其他合约的合约,使用 erdpy 不容易确定成功执行所需的最低gasLimit。虽然这个值可以通过仔细检查本地 testnet 日志来确定,但目前推荐的方法是从上面不等式的右边(simulatedCost + gasToLockForCallback)开始,并在模拟调用和寻找成功输出的同时逐渐减小值。
对于我们的示例,让我们使用以下gasLimit : 7619200, 7000000, 6000000值进行模拟:
$ erdpy --verbose contract call erd1qqqqqqqqqqqqqpgqfzydqmdw7m2vazsp6u5p95yxz76t2p9rd8ss0zp9ts \
--pem=~/elrondsdk/testwallets/latest/users/alice.pem \
--function=foo\
--recall-nonce --gas-limit=7619200\
--arguments ${hexAddressOfB}\
--simulate
... inspect output (possibly testnet logs); execution is successful
erdpy --verbose contract call erd1qqqqqqqqqqqqqpgqfzydqmdw7m2vazsp6u5p95yxz76t2p9rd8ss0zp9ts \
--pem=~/elrondsdk/testwallets/latest/users/alice.pem \
--function=foo\
--recall-nonce --gas-limit=7000000\
--arguments ${hexAddressOfB}\
--simulate
... inspect output (possibly testnet logs); execution is successful
erdpy --verbose contract call erd1qqqqqqqqqqqqqpgqfzydqmdw7m2vazsp6u5p95yxz76t2p9rd8ss0zp9ts \
--pem=~/elrondsdk/testwallets/latest/users/alice.pem \
--function=foo\
--recall-nonce --gas-limit=6000000\
--arguments ${hexAddressOfB}\
--simulate
... inspect output (possibly testnet logs); ERROR: out of gas when executing B::bar()
因此,在我们的例子中,gasLimit的合理值应该是 7000000。
重要
如果需要消耗超过gasToLockForCallback的高油耗回叫(不推荐),应通过检查合约调用输出和测试网络日志,适当增加上述gasLimit不等式的右边。***
