withExecAndTransfer
Use the withExecAndTransfer
utility for interacting with destination contracts that accept token deposits, and return dynamic tokenized positions.
For example:
- Alice deposits 1 ETH into a contract
- Alice receives some dynamic amount of vTokens (value tokens) in return
How it Works
This utility wraps your intended destination contract call (call
) within another call targeting the SolverNetExecutor
contract. When the solver fulfills the intent:
- The
SolverNetExecutor
contract is called on the destination chain. - The
SolverNetExecutor
executes the targetcall
specified (e.g.,Vault.deposit()
which credits vault shares tomsg.sender
). - After the original
call
completes, theSolverNetExecutor
(which was themsg.sender
and received the assets) checks its own balance of a specifiedtoken
. - It transfers the entire balance of that
token
it holds to the specified final recipient address (to
).
Here's the relevant part of the SolverNetExecutor
contract:
contract SolverNetExecutor {
// ...
/**
* @notice Execute a call and transfer any received ERC20 tokens back to the recipient
* @dev Intended to be used when interacting with contracts that don't allow us to specify a recipient
* This should be triggered by `execute` by executing an external call against this Executor contract
* @param token Token to transfer
* @param to Recipient address
* @param target Call target address
* @param data Calldata for the call
*/
function executeAndTransfer(address token, address to, address target, bytes calldata data)
external
payable
onlySelf
{
(bool success,) = target.call{ value: msg.value }(data);
if (!success) revert CallFailed();
if (token == address(0)) SafeTransferLib.safeTransferAllETH(to);
else token.safeTransferAll(to);
}
// ...
}
- The
token
in thetransfer
configuration ofwithExecAndTransfer
is the address of the asset you expect the executor to receive as a result of executing yourcall
. This is the asset the executor will forward to the finalto
address. UsezeroAddress
if thecall
results in native ETH being sent to the executor.
Usage
- Define your target call: This is the call to the target contract function (e.g.,
vault.deposit{ value: amount }()
) that sends assets tomsg.sender
. - Wrap it: Use
withExecAndTransfer
, providing your original call, thetoken
you expect the executor to receive, and the final recipient (to
). - Use the wrapped call: Pass the result of
withExecAndTransfer
(which is aCall
object itself) into thecalls
array foruseOrder
.
import { withExecAndTransfer } from '@omni-network/core';
// ABI for Vault.deposit{ value: amount }()
const vaultABI = [
{
inputs: [],
name: 'deposit',
outputs: [],
stateMutability: 'payable',
type: 'function',
},
] as const;
const vaultAddress = "0x..." as const; // Your tokenized vault address
const vaultTokenAddress = "0x..." as const; // The ERC20 token minted by the vault to msg.sender
const vaultDepositAmt = parseEther('1'); // Amount to deposit into the vault
const userAddress = "0x..." as const; // The final recipient of the vault tokens
// 1. Define the target call (e.g., vault.deposit{ value: amount }())
const targetCall = {
target: vaultAddress,
abi: vaultABI,
functionName: 'deposit',
value: vaultDepositAmt,
};
// 2. Wrap the call
const wrappedCall = withExecAndTransfer({
call: targetCall,
transfer: {
token: vaultTokenAddress, // Token the executor should forward
to: userAddress, // Final recipient
}
});
// 3. Use the wrapped call in useOrder's calls array
const order = useOrder({
// ...
calls: [wrappedCall],
});