如何在Substrate Ink中获取类似Solidity tx.origin的对应值?
问题
我正在开发一套跨合约调用的合约体系:当用户Alice通过合约A调用合约B时,希望在合约B中识别出发起这次调用的原始用户是Alice。但目前在合约B中使用let caller = self.env().caller();获取到的是合约A的地址,想知道在ink!合约中有没有类似Solidity里tx.origin的对应方法可以获取原始交易发起者?
回答
- ink! 原生不提供与Solidity
tx.origin完全等价的API。这是因为Substrate生态的合约设计更注重调用链的安全性,tx.origin这类直接暴露原始发起者的机制存在被钓鱼攻击利用的风险,因此默认不支持这类原生获取方式。 - 有两种可行的替代方案:
- 主动传递原始调用者地址
这是最安全可控的方式:在合约A调用合约B时,先获取自己的直接调用者(也就是Alice),再将该地址作为参数传递给合约B的调用方法。
合约A中的调用代码示例:
合约B中接收并使用的代码示例:#[ink(message)] pub fn call_contract_b(&mut self) { let original_caller = self.env().caller(); // 获取Alice的地址 // 调用合约B的目标方法并传入原始调用者地址 contract_b::contract::Call::target_function(original_caller) .call() .expect("调用合约B失败"); }#[ink(message)] pub fn target_function(&mut self, original_caller: AccountId) { // 这里的original_caller就是发起交易的Alice // 可根据需求进行验证或业务逻辑处理 } - 通过调用栈获取原始发起者
ink!的Env提供了call_stack()方法,该方法会返回整个调用链的账户序列,从最顶层的交易发起者开始,依次到直接调用当前合约的账户。对于Alice→合约A→合约B的调用链,在合约B中调用call_stack()会得到包含[Alice, 合约A地址]的列表,取第一个元素即可得到原始发起者。
代码示例:
注意:这种方式依赖调用链的完整性,且同样存在类似#[ink(message)] pub fn get_original_caller(&self) -> AccountId { let call_stack = self.env().call_stack(); // 正常交易场景下调用栈至少包含一个元素,即原始发起者 call_stack.first().cloned().unwrap_or_default() }tx.origin的安全风险,除非必要,优先推荐第一种方案。
- 主动传递原始调用者地址
内容的提问来源于stack exchange,提问作者s.Takahashi
相关产品推荐
相关产品推荐

