跳转至

用户定义的智能合约

原文: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

合约调用(异步)其他合约

重要

本节中的文档是初步的,可能会更改。此外,仅涵盖异步调用

在继续之前,请确保您首先浏览了以下内容:

假设我们有两个合约:AB,其中A::foo(addressOfB)异步调用B::bar()(例如使用asyncCall())。

让我们部署合约AB:

$ 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 

假设Aerd1qqqqqqqqqqqqqpgqfzydqmdw7m2vazsp6u5p95yxz76t2p9rd8ss0zp9ts展开,Berd1qqqqqqqqqqqqqpgqj5zftf3ef3gqm3gklcetpmxwg43rh8z2d8ss2e49aq展开,让模拟 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不等式的右边。***



回到顶部