ExternalActionSwap
Overview
ExternalActionSwap defines a common swap action interface used by Hinkal external actions. It receives user intent from HinkalInLogic.handleRunExternalAction, executes a swap on a target router (Uniswap, 1inch, Odos), pays the relay, returns tokens back to Hinkal, and reports resulting UTXOs.
Concrete adapters:
UniswapExternalAction— Uniswap V3 exactInputSingleOneInchExternalAction— 1inch Router arbitrary calldataOdosExternalAction— Odos Router encoded calldata
Inheritance
Transferer— ERC20/ETH helpers (approve, transfers, balances)ExternalActionBaseV2— recipient allowlist and ownership controls
Key Storage
IHinkalHelper public hinkalHelper— to calculate relay feeIWrapper internal immutable wrapper— WETH/Native wrapperaddress public immutable router— target DEX/aggregator router
Entry Point from Hinkal
function runAction(CircomData circomData, int256[] deltaAmounts)
external onlyAllowedRecipient returns (UTXO[] utxoSet)Delegates to
swap(circomData, deltaAmounts).Protected by
onlyAllowedRecipientso only the registered Hinkal contract can call.
Flow:
Read input token/amount from
circomDataanddeltaAmounts[0].If
feeToken == inputToken, subtract flat fee from the input amount.Execute DEX swap via
callRouter.Compute
relayFee = hinkalHelper.calculateRelayFee(0, flatFee, variableRate)and pay it infeeToken.If
feeToken == outputToken, subtractrelayFeefrom the received amount.Transfer output amount to
msg.sender(Hinkal), and create a singleUTXOwith output token, amount, and stealth info.
Note: ETH is supported via the wrapper (WETH) for routers that require ERC20s.
Adapters
UniswapExternalAction
externalActionMetadataencodesuint24 fee(pool fee tier).If
outputToken == address(0), swap towrapperand unwrap to ETH at the end.If
inputToken == address(0), deposit ETH towrapperfirst.Approves router unlimited allowance for
inputToken.Calls
ISwapRouter.exactInputSinglewithamountOutMinimum=0(slippage is enforced at Hinkal-level viaslippageValues).Returns
swappedAmountfrom router; unwraps to ETH if necessary.
Usage scenario (Uniswap V3):
Input: TOKEN A, Output: TOKEN B,
fee = 3000(0.3%).Metadata:
abi.encode(uint24(3000)).
OneInchExternalAction
Measures
outputTokenbalance before call.If input is ETH, forwards
inputAmountasmsg.valuetorouter.call(externalActionMetadata); else approves and calls without value.Requires success with distinct revert messages for native vs ERC20.
Computes
swappedAmountas the post-call balance delta foroutputToken.
Metadata: raw calldata produced off-chain by 1inch API for the specific chain and tokens.
OdosExternalAction
Wraps/unwraps ETH via
wrapperifoutputTokenorinputTokenis native.Approves router for
inputToken.Calls
router.call(externalActionMetadata); expects returned bytes encodinguint256 swappedAmount.Reverts on failure.
Metadata: raw calldata produced off-chain by Odos API; must return the output amount.
Fees and Accounting
Input-side: If the
feeTokenis the input token, the flat fee is removed from input before the swap.Output-side: Relay fee is computed and, if
feeToken == outputToken, deducted from the swap result before returning to Hinkal.Hinkal later enforces slippage and balance equations; this adapter focuses on execution and fee pass-through.
ETH Handling
Uses
wrapper.deposit{value: inputAmount}()when input is native.Uses
wrapper.withdraw(swappedAmount)when output is native.Transfers via
transferERC20TokenOrETHso ETH and ERC20 paths are unified.
Security Considerations
onlyAllowedRecipientprevents arbitrary callers from abusing approvals/funds.No internal slippage check: circuit/Hinkal-level
slippageValuesenforce minimum outputs.Unlimited approval is given per-call to routers; this contract is trust-minimized by recipient gating and Hinkal balance checks.
Explicit revert messages for external call failures improve debuggability.
Example Integrations
Uniswap V3 single-hop swap using 0.3% pool.
1inch multi-hop route with permit2 where metadata includes router calldata.
Odos split-route aggregation returning exact amount out.
In all cases, Hinkal’s HinkalInLogic.handleRunExternalAction handles pre-swap token movements and invokes runAction, then Hinkal inserts commitments from the returned UTXOs.
Last updated