HinkalInLogic
Overview
HinkalInLogic and HinkalInLogicBase implement the execution-layer logic that Hinkal.sol delegates to. They manage:
Approval UTXOs lifecycle (creation, modification, spending)
External protocol execution with precise allowance/balance accounting
Proofless deposit flow (token/NFT collection and UTXO formation)
Files
contracts/HinkalInLogicBase.sol— core logic and helperscontracts/HinkalInLogic.sol— concrete implementation (external actions, proofless deposit)
Storage
mapping(address => ApprovedUtxo[]) approvedUtxos— per interaction/externalAddress approval notes
Approval UTXOs represent token-specific allowances scoped by an inHinkalAddress (a stealth/view context). They enable privacy-preserving approvals that can later be spent during operations.
Logic Actions (driven by CircomData.hinkalLogicArgs.hinkalLogicAction)
ONLY_APPROVAL (1)
Sends flat fee to relay and returns immediately. Used to publish approvals without execution.
EXECUTE (2)
Full external call flow: spend approvals, perform external call atomically, pay relay, reconcile balances/allowances, mint UTXOs.
Entry point:
function inHinkalTransact(CircomData circomData, int256[] approvalChangesPerToken)
external payable returns (UTXO[] utxoSet)Routes to one of: ONLY_APPROVAL, or EXECUTE via inHinkalExecutionFlow (reverts on unknown action).
Approvals: Create/Modify/Spend
handleApprovalUtxos
function handleApprovalUtxos(CircomData circomData) public returns (int256[] result)Iterates
UseApprovalUTXOData[]per token index to apply per-externalAddress approval deltas.For each tuple
(externalAddress, token, inHinkalAddress, approvalChange):If an approval note exists, update or remove when new amount hits zero.
If not found, append a new approval UTXO (only when
approvalChange >= 0).Always adjust aggregate ERC20 allowance via
approveMoreto reflect the net change.
Emits
NewApprovedUtxo(externalAddress, tokenAddress, approvalChange, inHinkalAddress)per token.
EXECUTE Path: inHinkalExecutionFlow
function inHinkalExecutionFlow(CircomData circomData) internal returns (UTXO[] utxoSet)Spend approvals:
spendApprovedUtxos(circomData)Validates the user owns matching approval UTXOs for tokens being spent.
Decrements or removes approval notes according to
executeApprovalChanges.Tracks any tokens that must remain balance-stable (unspent set).
Snapshot balances/allowances: before execution
Safety checks:
erc20SafetyCheckblocks dangerous ERC20 methods in metadata selectors (approve, mint, transferFrom, etc.).Parse
externalActionMetadataand computeethAmountto attach when the proof indicates ETH spend.Enforce signature/deadline via helper (see
HinkalHelper.checkInLogicMetadata).
External call:
(success, returnData) = externalAddress.call{value: ethAmount}(callData)Bubble up revert reasons if present; else use a generic error.
Relay payment: send flat fee in
feeTokenif configured.Snapshot balances/allowances: after execution
UTXO formation and allowance reconciliation:
formUtxosAndCheckSpendingscompares pre/post balances and allowances:Positive balance deltas mint UTXOs for that token.
Negative balance deltas must match allowance spend (plus relay fee when feeToken matches), else revert.
If aggregate allowance spend exceeds intended individual spend,
approveMorerestores the difference, ensuring aggregate == sum(individual).
Emits
NewApprovedUtxoreflecting the final per-token execution change.
Unspent-invariant: tokens identified as unspent must have identical balances before and after.
Outcome: returns UTXO[] to be turned into commitments by Hinkal.sol.
External Actions (HinkalInLogic.sol)
function handleRunExternalAction(CircomData circomData, int256[] approvalChangesPerToken)
external returns (UTXO[])Computes
deltaAmountChanges[i] = calculateDeltaAmount(...)per token.For negative deltas, transfers tokens/NFTs to
externalAddresspre-call.Invokes
IExternalActionV2(externalAddress).runAction(circomData, deltaAmountChanges)which returnsUTXO[]created during the action.
This path is used by Hinkal._internalRunExternalAction to integrate with DeFi targets in a privacy-preserving, atomic way.
Proofless Deposit (HinkalInLogic.sol)
function handleProoflessDeposit(address[] erc20Addresses, uint256[] amounts, uint256[] tokenIds, StealthAddressStructure[] stealthAddressStructures)
public payable returns (UTXO[] utxoArray)Groups identical
(token, tokenId)pairs withcalcTokenChangesForProoflessDepositand accumulates amounts (rejects duplicate NFTs).Pulls funds from
msg.senderviahandleTransfersFromProoflessDepositand assertsbalanceAfter - balanceBefore == amountper token.Forms UTXOs one-to-one with user inputs using
handleUtxoCreationEach, settingtimeStamptoblock.timestamp.
Used by Hinkal.prooflessDeposit to create on-chain commitments without a ZK proof.
Safety and Helpers
erc20SafetyCheck: blacklist of ERC20 methods (approve/mint/transferFrom, etc.) disallowed in external calls via metadata selector checks.isERC20: coarse detection to apply safety checks only when necessary.calculateBalances/calculateAllowances: snapshot helpers.approveMore: adjusts aggregate ERC20 allowances to match individual approval changes exactly.
Events
NewApprovedUtxo(address approveTo, address tokenAddress, int256 amount, uint256 inHinkalAddress)
These events allow indexers and frontends to reconstruct approval states independently of private commitments.
Reverts and Invariants
inHinkalTransact: "msg.value non allowed for inHinkalTransactions"
spendApprovedUtxos: ownership and exact-spend assertions; "you cannot spend something you do not own"
inHinkalExecutionFlow:
External call bubble-up or "Hinkal: External call failed"
Unspent equality: "array's must be equal"
Allowance/balance match: "balance and allowance Dif mismatch"
Approvals: guard against negative creation or out-of-range indices
Proofless deposit: length checks, duplicate NFT protection, and exact transfer assertions
Integration Surface with Hinkal.sol
Hinkal._inHinkalTransactdelegates toinHinkalTransactHinkal._internalRunExternalActiondelegates tohandleRunExternalActionHinkal._handleApprovalUtxosdelegates tohandleApprovalUtxosHinkal.prooflessDepositdelegates tohandleProoflessDeposit
This separation keeps Hinkal orchestration minimal while letting HinkalInLogic focus on evolving execution semantics.
Last updated