HinkalHelper
Overview
HinkalHelper
acts as the policy and preflight engine for Hinkal transactions. It centralizes:
Relay whitelisting and fee policy (via
RelayStore
)Token registry and per-token limits (via
ERC20TokenRegistry
)Circuit public input construction (via
CircomDataBuilder
)EIP-712 validation for in-logic external call metadata
Access token (allowlist) verification and minting
The helper is upgradeable separately from core state, allowing governance to evolve policy without changing state layout.
Inheritance
RelayStore
— whitelisted relays and fee calculationsERC20TokenRegistry
— gas token list and per-token limitsCircomDataBuilder
— forms public inputs for circuitsHinkalInLogicMetadataParser
— decodes signed metadataEIP712
— domain separator and typed-data hashing
Key Storage
address public hinkalAddress
— only this address may call certain functionsIAccessToken public accessToken
— access token registryaddress public inLogicSigner
— signer authorized for EXECUTE metadata
Admin Functions
setAccessToken(address)
— update access token contractsetHinkalAddress(address)
— set the canonical Hinkal callersetInLogicSigner(address)
— set expected signer for EXECUTE metadataupdateStakeCircuits(uint256 id, bool value)
— toggle stake-circuit IDs inCircomDataBuilder
Plus the registry/relay admin functions inherited from parents.
Relay Policy
function relayerIsValid(address relay) internal view
If relay is non-zero: requires
tx.origin == relay
andisRelayInList(relay)
. Rationale: ensures the relay who submits is the one being paid and is whitelisted.
function checkRelayFeeParameters(CircomData circomData) internal view
For non-wallet actions or Emporium action,
feeToken
must be a registered gas token.For any non-zero logic action,
feeToken
must be present in the tx token list.
Token Registry Policy
function checkTokenRegistry(address[] erc20TokenAddresses, int256[] amountChanges)
public view returns (bool limitHit)
If registry disabled, returns false.
Otherwise, checks each token’s absolute
amountChanges[i]
against configuredtokenLimit
.Returns true if any limit is exceeded — used to decide if access token checks/minting are required.
Dimension and Shape Validation
function dimensionsCheck(CircomData circomData, Dimensions dimensions) internal pure
Enforces equality of array lengths with
dimensions
for tokens, nullifiers per input, and outputs.Requires non-zero
extraRandomization
instealthAddressStructure
.Asserts
onChainCreation
,slippageValues
, anduseApprovalUtxoData
lengths equal token count.If
hinkalLogicAction != 0
, requiresexternalActionId == HINKAL_WALLET_ACTION_ID
(wallet flow).If action is not RELEASE_BUFFER/EXECUTE, then
executeApprovalChanges
must be all zeros.
In-Logic Metadata Validation (EXECUTE)
function checkInLogicMetadata(CircomData circomData) internal view
Only for action EXECUTE (3):
Parses metadata, checks EIP-1271/EOA signature with
inLogicSigner
over a typed hash of(deadline, tokens[], externalAddress, externalCallData)
.Requires
deadline >= block.timestamp
.Ensures every signed token is present in
circomData.erc20TokenAddresses
.
function calculateInLogicHash(CircomData circomData, ParsedInLogicMetadata parsed)
internal view returns (bytes32)
EIP-712 domain:
HinkalHelper
, version1.0.0
.Encoding binds call intent to the current
externalAddress
and token set.
performHinkalChecks — The Preflight Gate
function performHinkalChecks(CircomData circomData, Dimensions dimensions, address sender)
external view returns (uint256[] inputForCircom)
Sequence:
Timestamp freshness check (commented for dev/forking convenience).
Sender/relay consistency:
Either relayed (nonzero relay,
originalSender == 0
) or direct (relay zero,originalSender == sender
).
Calldata integrity:
CircomDataBuilder.getHashedCalldata(circomData) == circomData.calldataHash
.Relay policy:
relayerIsValid(circomData.relay)
andcheckRelayFeeParameters
.Dimensions and shape:
dimensionsCheck
.Token registry and access token requirement:
If any token exceeds limit and no signature provided (
v == 0
): requireaccessToken.checkForRootHash(rootHashAccessToken, tx.origin)
.If signature provided: require
rootHashAccessToken == signatureData.accessKey
.
EXECUTE metadata validation:
checkInLogicMetadata
(signature + deadline + token set).Return
formInputForCircom(circomData)
for proof verification.
Effect: This function is the canonical place where user intent, policy, and circuit shape are bound and validated before any value moves.
performSideEffects — Access Token Minting
function performSideEffects(CircomData circomData) external onlyHinkal
Calls
mintAccessTokenIfNecessary
which mints a token viaaccessToken.addToken(signatureData)
when a signature is provided.
Integration Points
Called by
Hinkal.transact
prior to proof verification.Provides
calculateRelayFee
and relay list fromRelayStore
for use in core flows.Supplies
isGasToken
and token limits viaERC20TokenRegistry
.Builds the Groth16 public input vector consistent with the circuit.
Reverts and Errors
"Unauthorized relay" / "Relay is not whitelisted"
"invalid value for originalSender"
"Calldata Hash Integrity Check Failed"
"FeeToken should be gas token"
"fee token should be included in erc20Token array"
Dimension mismatches (token/nullifier/commitment counts)
"Extra-randomization cannot be zero"
"spend approvals should be zero" (when action not allowed to spend)
"Wallet External Id should be choosen"
"invalid inLogic Signature" / "deadline passed" / "missing token from parsedMetadata"
Access token checks: "Access Token Root Hash is Incorrect" / "accessKey must be used"
Security Notes
EIP-712 binding of EXECUTE intent prevents replay across addresses or altered call data.
Relay control plus registry policy ensure gas economics are enforced out-of-circuit.
All checks are read-only and must pass before proof verification and state mutations in
Hinkal.sol
.
Last updated