捕获异常的唯一方法是对生成异常的 Promise 进行回调。
在解释的场景中,不contractA.callback()
应该崩溃。您需要足够仔细地构建合约以避免回调失败。大多数情况下是可以做到的,因为您控制回调的输入和附加的气体量。如果回调失败,则类似于在异常处理代码中出现异常。
另请注意,您可以确保callback
正确安排contractA.run()
. 如果不是这种情况,例如您没有足够的 gas 附加到run
,回调和其他承诺的调度将失败,并且整个run
更改状态将回滚。但是一旦run
完成,状态变化就会run
被提交并且callback
必须仔细处理。
我们在lockup
合同中有几个地方允许回调失败:https ://github.com/near/core-contracts/blob/6fb13584d5c9eb1b372cfd80cd18f4a4ba8d15b6/lockup/src/owner_callbacks.rs#L7-L24
还有大多数回调不会失败的地方:https ://github.com/near/core-contracts/blob/6fb13584d5c9eb1b372cfd80cd18f4a4ba8d15b6/lockup/src/owner_callbacks.rs#L28-L61
指出在某些情况下合约不想依赖其他合约的稳定性,例如当流量为A --> B --> A --> B
. 在这种情况下B
,无法将回调附加到给定的资源A
。对于这些场景,我们正在讨论添加一个特定构造的可能性,该构造是一个原子并且一旦它被删除就具有解析回调。我们称之为Safe
:https ://github.com/nearprotocol/NEPs/pull/26
编辑
如果contractB.run
失败并且我想更新状态contractA
以回滚更改contractA.run
怎么办?
在这种情况下contractA.callback()
仍然被调用,但它具有PromiseResult::Failed
它的依赖关系contractB.run
。
因此callback()
可以通过修改状态contractA
来恢复更改。
例如,锁定合约实现的回调以处理从权益池合约中退出:https ://github.com/near/core-contracts/blob/6fb13584d5c9eb1b372cfd80cd18f4a4ba8d15b6/lockup/src/foundation_callbacks.rs#L143-L185
如果我们调整名称以匹配示例:
锁仓合约 ( contractA
) 尝试run()
从质押池 ( ) 中提取资金 ( contractB
),但由于最近取消质押,资金可能仍被锁定,因此提取失败 (contractB.run()
失败)。回调被称为 ( contractA.callback()
) 并检查承诺 (of contractB.run
) 的成功。由于提款失败,回调将状态恢复到原始状态(恢复状态)。
实际上,它稍微复杂一些,因为实际的序列是A.withdraw_all -> B.get_amount -> A.on_amount_for_withdraw -> B.withdraw(amount) -> A.on_withdraw