Here are scenarios how the product works on the UI and contract side for private supply, borrow, repay, and withdraw.
- Debt is tracked at the vault level by Aave, not synthetic per-position accounting in adapter storage.
- Authorization for sensitive actions is per-position secret hash:
- secret must match current stored hash
- successful borrow/repay rotates the hash.
- Borrow/repay fee reserve checks are enforced in private USDC by WebCLI before submission.
- WebCLI currently prints
WithdrawAuth backupin terminal after secret-changing actions; if leaked, the latest secret can be used to control that position.
Use this command sequence for a quick end-to-end product review.
- Optional setup in
.env: setVITE_LOGIN_TEST_MNEMONIC="word1 word2 ..." - Run
login-testto load the test private account - Run
private-balance usdcto check available private collateral - Run
private-supply <amount>to supply private collateral to Aave - Run
supply-positionsto get the currentpositionId - Run
private-borrow <positionId> <amount>to borrow private WETH - Run
private-repay <positionId> <amount>to repay private WETH debt
On frontend:
- User runs
private-supply <amount>in WebCLI - WebCLI builds a Hinkal private action with 2 Emporium ops:
ERC20.transfer(adapter, amount)PrivateSupplyAdapter.onPrivateDeposit(token, amount, data)data includeszkOwnerHash+withdrawAuthHash
In contracts:
PrivateSupplyAdaptervalidates caller (privacyExecutor) and token config- Adapter asks
VaultFactoryfor vault byzkOwnerHash - Since it is first supply,
VaultFactorydeploys a newUserVaultand stores mappingzkOwnerHash -> vault - Adapter routes funds into that vault; vault supplies to Aave pool
- Adapter stores/updates position state (positionId, owner hash, token, amount, withdrawAuthHash)
On frontend:
- User runs
private-withdraw <positionId> <amount|max> - WebCLI loads local
withdrawAuthSecretfor this position and createsnextWithdrawAuthHash(for rotation) - WebCLI builds Hinkal private action calling:
PrivateSupplyAdapter.withdrawToRecipient(positionId, amount, withdrawAuthSecret, nextWithdrawAuthHash, emporium)
In contracts:
- Adapter validates caller (
privacyExecutor) - Verifies provided secret matches stored
withdrawAuthHash - Calls vault/Aave withdraw path to pull funds out
- Sends withdrawn funds to recipient (Emporium for private-credit flow)
- Updates position amount
- If partial withdraw: stores new
withdrawAuthHash(rotated) - If full withdraw: position amount becomes zero (and hash cleared/closed state)
- If partial withdraw: stores new
On frontend:
- User runs
private-borrow <positionId> <amount> - WebCLI loads local auth secret and creates
nextAuthHash - WebCLI builds Hinkal private action calling:
PrivateSupplyAdapter.borrowToRecipient(positionId, debtToken, amount, authSecret, nextAuthHash, emporium)
In contracts:
- Adapter validates caller (
privacyExecutor) - Verifies position exists and auth secret hash matches
- Validates borrow token is allowlisted
- Calls vault/Aave borrow path with variable rate mode
- Routes borrowed WETH to Emporium (private-credit flow)
- Rotates stored auth hash
On frontend:
- User runs
private-repay <positionId> <amount> - WebCLI checks private WETH balance and private USDC fee reserve
- WebCLI builds Hinkal private action with 2 ops:
ERC20.transfer(adapter, amount)for WETHPrivateSupplyAdapter.repayFromPrivate(positionId, debtToken, amount, authSecret, nextAuthHash)
In contracts:
- Adapter validates caller (
privacyExecutor) - Verifies auth secret and borrow token allowlist
- Transfers WETH from adapter to vault
- Vault calls Aave repay on behalf of the vault
- Adapter rotates stored auth hash