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
SolverNetExecutorcontract is called on the destination chain. - The
SolverNetExecutorexecutes the targetcallspecified (e.g.,Vault.deposit()which credits vault shares tomsg.sender). - After the original
callcompletes, theSolverNetExecutor(which was themsg.senderand received the assets) checks its own balance of a specifiedtoken. - It transfers the entire balance of that
tokenit 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
tokenin thetransferconfiguration ofwithExecAndTransferis 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 finaltoaddress. UsezeroAddressif thecallresults 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, thetokenyou expect the executor to receive, and the final recipient (to). - Use the wrapped call: Pass the result of
withExecAndTransfer(which is aCallobject itself) into thecallsarray 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],
});