Solana Rust如何获取当前CPI函数调用返回值 移植Solidity的方案是否正确
你提到的拆分两次交易、靠全局账户存储返回值的方案是不正确的,存在竞态风险,且完全没有必要,目前有更安全高效的实现方式:
方案1:传入调用方可控的临时账户接收返回值(链上逻辑需要使用返回值时首选)
Solana的CPI调用支持传入自定义账户,你可以在调用deposit/withdraw方法时,额外传入一个由你当前程序控制、专门用于接收本次调用返回值的临时账户(可以是一次性的PDA,也可以是调用时创建的临时账户)。被调用的Strategy程序执行完核心逻辑后,直接把sharesAdded/sharesRemoved写入这个临时账户即可。
整个读写过程都在同一个原子交易中完成,不会出现并发修改的问题,你在当前交易的后续逻辑中可以直接读取这个账户里的返回值,不需要拆分两次交易。
对应Anchor实现的示例:// 被调用的Strategy程序的接口定义 pub fn deposit(ctx: Context<Deposit>, sender: Pubkey, want_amt: u64) -> Result<()> { // 核心存款逻辑计算份额 let shares_added = xxxx; // 写入返回值到调用方传入的临时账户 ctx.accounts.return_data_account.shares = shares_added; Ok(()) } #[derive(Accounts)] pub struct Deposit<'info> { // 其他原有业务账户... pub return_data_account: Account<'info, ReturnData>, }你作为调用方在发起CPI时传入该账户即可,执行完CPI直接读取这个账户的shares字段就能拿到返回值。
方案2:使用链上事件(仅链下客户端需要获取返回值时使用)
如果你不需要在链上逻辑中使用该返回值,仅需要链下客户端拿到结果,只需要让被调用的Strategy程序在执行完deposit/withdraw后emit对应的事件即可,客户端在交易确认后解析交易日志里的事件内容就能拿到sharesAdded/sharesRemoved,不需要额外的调用。
你之前设想的方案之所以不推荐,是因为如果把返回值存在被调用程序的公共存储账户中,多个用户同时调用时返回值会被互相覆盖,且两次交易之间的间隔无法保证数据一致性,完全没有必要采用。
内容的提问来源于stack exchange,提问作者Russo

