# Emporium

**Overview**

`EmporiumUpgradeable` is an upgradeable external action that executes a stack of arbitrary calls (stateless or via a stateful Hinkal Wallet) and returns newly formed UTXOs back to Hinkal. It supports:

* Multi-operation batches with per-op value and calldata
* Optional stateful execution through a user’s Hinkal Wallet contract
* EIP-712 signed authorization for stateful sequences (anti-replay via message nonce)
* Accurate token/NFT accounting, fee payment to relay, and UTXO formation

**Inheritance**

* `Initializable`, `EIP712Upgradeable`
* `ExternalActionBaseUpgradeable` — recipient allowlist + ownership
* `Transferer` — token and ETH helpers
* `EmporiumStorage` — upgrader-safe storage layout

**Storage**

* `IHinkalHelper _hinkalHelper` — for relay fee policy and helper access
* `mapping(uint256 => bool) usedMessages` — anti-replay for signed stacks

**Initialization**

```solidity
function initialize(IHinkalHelper _hinkalHelper, address[] _allowedRecipients, address _owner) public initializer
```

* Sets EIP712 domain: name `Emporium`, version `1.0.0`.
* Configures allowed recipients and owner, stores `_hinkalHelper`.

***

#### Metadata Format — EmporiumStack

```solidity
struct EmporiumStack {
    uint8 v; bytes32 r; bytes32 s;    // signature over message
    uint256 message;                  // signed nonce/message
    address signerAddress;            // wallet address (optional)
    bytes[] ops;                      // array of operations
}
```

Each op packs:

* `data[0:20]`: endpoint address
* `data[20]`: invokeWallet flag (nonzero → use IHinkalWallet)
* `data[21:37]`: value (uint128)
* `data[37:]`: callData for endpoint

Constants:

* `MIN_DATA_LENGTH = 37`
* `INVOKE_WALLET_FLAG = 20`

***

#### Entry Point

```solidity
function runAction(CircomData circomData, int256[] deltaAmountChanges)
    external onlyAllowedRecipient returns (UTXO[] utxoSet)
```

High-level flow:

1. Decode `EmporiumStack` from `circomData.externalActionMetadata`.
2. Snapshot `balancesBefore` for all `(erc20TokenAddresses, tokenIds)`.
3. `verifyWallet(stack)` — if any op is stateful, verify EIP-712 signature and anti-replay.
4. Execute ops in order:
   * If `invokeWallet` and `signerAddress != 0`: call `IHinkalWallet.callHinkalWallet(endpoint, callData, value)`.
   * Else: direct `endpoint.call{value: value}(callData)` with guard to block wallet-only methods.
   * Revert on failure bubbling `err` as `CallFailed(err)`.
5. Snapshot `balancesAfter`.
6. Per-token accounting and UTXO formation:
   * Start with `balanceChange = balancesAfter[i] - balancesBefore[i]`.
   * If `deltaAmountChanges[i] < 0`, add `-deltaAmountChanges[i]` to account for pre-move into Emporium.
   * Revert if `balanceChange < 0` (unless pre-balance existed, which is prohibited here).
   * If positive, `transferToken(token, msg.sender, balanceChange, tokenId)` and create matching `UTXO` with `stealthAddressStructure` and `timeStamp`.
7. Pay relay fee:
   * If `signerAddress != 0`: `IHinkalWallet.doSendToRelay(relay, flatFee, feeToken)`.
   * Else: `sendToRelay(relay, flatFee, feeToken)`.
8. Return compacted `utxoSet`.

Reverts:

* `ShortOperation()` — op shorter than 37 bytes
* `UnauthorizedWalletCall()` — attempt to call wallet-only methods directly
* `CallFailed(bytes err)` — bubbled revert data
* `BalanceChangeShouldBePositive()` — net outflow detected
* `InvalidSignature()` / `UsedMessage()` — EIP-712 checks failed

***

#### Wallet Verification

```solidity
function verifyWallet(EmporiumStack stack) internal
```

* Detects if any op requests `invokeWallet` with nonzero `signerAddress`.
* If none, return early (stateless batch).
* For stateful:
  * Enforce anti-replay: `usedMessages[stack.message]` must be false; mark true.
  * EIP-712 hash over `EmporiumSignature(uint256 message)`.
  * Recover signer and ensure it matches `stack.signerAddress`.

***

#### Integration with Hinkal

* Hinkal pre-moves tokens based on `deltaAmountChanges` and delegates here.
* Emporium executes ops, returns UTXOs representing net positive balances per token.
* Hinkal applies slippage/balance invariants and inserts commitments accordingly.

***

#### Security Notes

* Recipient gating: only Hinkal may invoke `runAction`.
* Strong EIP-712 binding for stateful sequences; replay-protection via `usedMessages`.
* Prohibits direct invocation of wallet-reserved selectors.
* Careful ETH handling via `value (uint128)` per op and `Transferer` helpers.
