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
approveMore
to 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:
erc20SafetyCheck
blocks dangerous ERC20 methods in metadata selectors (approve, mint, transferFrom, etc.).Parse
externalActionMetadata
and computeethAmount
to 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
feeToken
if configured.Snapshot balances/allowances: after execution
UTXO formation and allowance reconciliation:
formUtxosAndCheckSpendings
compares 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,
approveMore
restores the difference, ensuring aggregate == sum(individual).
Emits
NewApprovedUtxo
reflecting 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
externalAddress
pre-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 withcalcTokenChangesForProoflessDeposit
and accumulates amounts (rejects duplicate NFTs).Pulls funds from
msg.sender
viahandleTransfersFromProoflessDeposit
and assertsbalanceAfter - balanceBefore == amount
per token.Forms UTXOs one-to-one with user inputs using
handleUtxoCreationEach
, settingtimeStamp
toblock.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._inHinkalTransact
delegates toinHinkalTransact
Hinkal._internalRunExternalAction
delegates tohandleRunExternalAction
Hinkal._handleApprovalUtxos
delegates tohandleApprovalUtxos
Hinkal.prooflessDeposit
delegates tohandleProoflessDeposit
This separation keeps Hinkal
orchestration minimal while letting HinkalInLogic
focus on evolving execution semantics.
Last updated