withExecAndTransfer
Use the withExecAndTransfer
utility for interacting with destination contracts that accept native 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 SolverNetMiddleman
contract. When the solver fulfills the intent:
- The
SolverNetMiddleman
contract is called on the destination chain. - The
SolverNetMiddleman
executes the targetcall
specified (e.g.,Vault.deposit()
which credits vault shares tomsg.sender
). - After the original
call
completes, theSolverNetMiddleman
(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 SolverNetMiddleman
contract:
contract SolverNetMiddleman {
// ...
/**
* @notice Execute a call and transfer any received tokens back to the recipient
* @dev Intended to be used when interacting with contracts that don't allow us to specify a recipient
* @param token Token to transfer (address(0) for ETH)
* @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 nonReentrant {
(bool success, ) = target.call{value: msg.value}(data); // Executes the original call
if (!success) revert CallFailed();
// Transfer received assets to the intended recipient
if (token == address(0)) {
SafeTransferLib.safeTransferAllETH(to);
} else {
IERC20(token).safeTransferAll(to); // safeTransferAll checks balance and transfers if > 0
}
}
// ...
}
- The
token
in thetransfer
configuration ofwithExecAndTransfer
is the address of the asset you expect the middleman to receive as a result of executing yourcall
. This is the asset the middleman will forward to the finalto
address. UsezeroAddress
if thecall
results in native ETH being sent to the middleman.
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 the OmnimiddlemanAddress
, your original call, thetoken
you expect the middleman 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 middlemanAddress = "0x..." as const; // Omni contract address
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({
middlemanAddress: middlemanAddress,
call: targetCall,
transfer: {
token: vaultTokenAddress, // Token the middleman should forward
to: userAddress, // Final recipient
}
});
// 3. Use the wrapped call in useOrder's calls array
const order = useOrder({
// ...
calls: [wrappedCall],
});