# HFA Skill

Use this skill when a human wants an AI agent to route Superfluid Wallet (Turnkey) transactions through HFA for human approval.

HFA is a human-first approval layer. The agent prepares a concrete EVM transaction, signs a Turnkey `SIGN_TRANSACTION_V2` activity with its local P-256 API key, and asks HFA to notify the paired human device. The transaction executes only after the human approves and the HFA provider co-signs the same Turnkey activity.

Do not assume why a transaction is being requested. The trigger may be a human asking in chat, an agent-owned workflow, a scheduled job, monitoring logic, or another process.

## Mental Model

There are three separate flows:

1. **Setup flow:** once per agent P-256 key and Superfluid Wallet.
2. **Notification pairing:** once per wallet address (push device registration). After wallet setup, the human completes this via the wallet success screen — do not send a separate pairing URL unless they skipped that step.
3. **Request flow:** repeated whenever the agent needs human approval for a transaction.

Current public agent path:

- Turnkey delegated-access execution only (Superfluid Wallet embedded wallet).
- One concrete EVM action per request.
- Agent holds a local P-256 Turnkey API key; never send the private key to HFA or the wallet.
- Agent signs Turnkey `CONSENSUS_NEEDED` activities; HFA verifies and notifies the human.
- Human approval triggers HFA provider co-sign, Turnkey completion, and HFA broadcast.

## Discovery

Canonical app:

```text
https://hfa.s.superfluid.dev
```

Canonical API base URL:

```text
https://hfa.s.superfluid.dev/api
```

Canonical skill URLs:

```text
https://hfa.s.superfluid.dev/skill.md
https://hfa.s.superfluid.dev/skill/manifest.json
https://hfa.s.superfluid.dev/skill/openapi.json
https://hfa.s.superfluid.dev/.well-known/llms.txt
```

If this skill is installed as a bundle, prefer local bundled files over fetching remote copies during every task.

## Dependencies

Bundled helpers require:

- Python 3
- `curl`
- `cryptography` for Turnkey P-256 API stamping (`apt install python3-cryptography` on Ubuntu, or `pip install cryptography`)

HFA HTTP calls use Python stdlib `urllib`. Turnkey signing uses `curl` with an `X-Stamp` header produced by `scripts/turnkey_stamp.py`.

## Agent Credentials

After setup, the agent keeps a local env file (default `.env.hfa-agent.local`) with:

```text
TURNKEY_AGENT_API_PUBLIC_KEY=
TURNKEY_AGENT_API_PRIVATE_KEY=
TURNKEY_SUB_ORGANIZATION_ID=
TURNKEY_WALLET_ADDRESS=
```

Optional metadata:

```text
HFA_AGENT_LABEL=
HFA_TURNKEY_SETUP_SESSION_ID=
TURNKEY_HFA_AGENT_USER_ID=
TURNKEY_HFA_PROVIDER_USER_ID=
TURNKEY_HFA_POLICY_IDS=
```

Never expose `TURNKEY_AGENT_API_PRIVATE_KEY` to HFA, Superfluid Wallet, or chat logs.

## Missing Information

Do not invent missing chain IDs, token addresses, recipients, amounts, or wallet addresses.

During setup, ask the human to open the setup URL in Superfluid Wallet and complete **Enable HFA**.

During transaction submission, follow the calling task's policy for missing inputs: ask if interactive, otherwise fail with a clear error.

For a transaction request, ask for:

- target chain
- target contract or known protocol action
- recipient or counterparty, if relevant
- token address and amount, if relevant
- any timing, slippage, or risk limits that matter for the requested action

## Setup Flow

Prerequisites:

- HFA provider is running and reachable
- Superfluid Wallet is available to the human

Recommended: use `scripts/hfa-setup.py`.

```bash
scripts/hfa-setup.py \
  --provider-url https://hfa.s.superfluid.dev \
  --agent-label "My Agent" \
  --env-file .env.hfa-agent.local
```

The helper:

1. Generates a local P-256 agent API keypair if missing.
2. Calls `POST /api/turnkey/hfa/setup-sessions`.
3. Prints the Superfluid Wallet setup URL.
4. Writes the local agent keypair and `HFA_TURNKEY_SETUP_SESSION_ID` to `.env.hfa-agent.local`.
5. Exits so the agent can show the setup URL to the human.

Tell the human to open the setup URL, sign in with email OTP if needed, and click **Enable HFA**.

If the HFA server returns a localhost setup link (common when server env vars are unset), `hfa-setup.py` rewrites it to the bundled public wallet and provider URLs before printing it.

After the human confirms setup is complete, rerun the same command with the same `--env-file`. The helper resumes `HFA_TURNKEY_SETUP_SESSION_ID`, fetches `GET /api/turnkey/hfa/setup-sessions/:id`, and writes `TURNKEY_SUB_ORGANIZATION_ID`, `TURNKEY_WALLET_ADDRESS`, and delegated-access metadata to `.env.hfa-agent.local`.

Only pass `--wait` when a human explicitly wants the terminal command to block while polling. Agents should not use `--wait` by default because it hides the setup URL inside a long-running command.

Manual setup API (fallback):

```http
POST https://hfa.s.superfluid.dev/api/turnkey/hfa/setup-sessions
Content-Type: application/json

{
  "agentPublicKey": "<compressed P-256 public key hex, 66 chars, prefix 02 or 03>",
  "agentLabel": "Optional human-readable label"
}
```

Poll:

```text
GET https://hfa.s.superfluid.dev/api/turnkey/hfa/setup-sessions/<SETUP_SESSION_ID>
```

When complete, store `subOrganizationId`, `walletAddress`, and the agent keypair locally.

## Notification Pairing

After wallet setup completes, the Superfluid Wallet success screen sends the human to HFA to register push notifications. The wallet opens:

```text
https://hfa.s.superfluid.dev/?agent=<TURNKEY_WALLET_ADDRESS>&from=hfa-setup&mode=relay
```

The human taps **Enable approval notifications** and allows browser push permission. Use the checksummed wallet address from setup (`TURNKEY_WALLET_ADDRESS`).

Do **not** ask the human to open a separate pairing URL after setup unless they skipped the wallet handoff step.

If no device is paired, `POST /api/turnkey/requests` may succeed but the human will not receive a prompt.

## Request Flow

Use this flow whenever a transaction needs human approval.

High-level sequence:

1. Build one concrete EVM action.
2. Prefer `scripts/hfa-turnkey-request.py` to create, verify, sign via Turnkey (curl), and submit to HFA.
3. Optionally watch `GET /api/turnkey/requests/<REQUEST_ID>` with `scripts/hfa-watch.py`.

### 1. Build Actions

The current Turnkey HFA API accepts exactly **one** contract-call action per request.

Action shape:

```json
{
  "chainId": 84532,
  "to": "0xContractAddress",
  "data": "0xCalldata",
  "value": "0"
}
```

Rules:

- `chainId` is an integer EVM chain ID.
- `to` is the target contract address.
- `data` is ABI-encoded calldata.
- `value` is wei as a decimal string.

Use standard ABI tooling such as `viem`, `ethers`, or `cast` to build calldata. HFA resolves ERC-7730 clear-signing text for the human approval surface.

HFA does not choose token, protocol, recipient, or chain addresses for you. If the human gives a token symbol without an address, verify the contract address from a trusted source or ask the human to confirm it.

### 2. Recommended: Use The Turnkey Request Helper

```bash
scripts/hfa-turnkey-request.py \
  --provider-url https://hfa.s.superfluid.dev \
  --env-file .env.hfa-agent.local \
  --intent-file intent.json
```

`intent.json`:

```json
{
  "actions": [
    { "chainId": 84532, "to": "0x...", "data": "0x...", "value": "0" }
  ]
}
```

The helper:

1. `POST /api/turnkey/drafts` with `agentPublicKey` and intent.
2. Verifies the returned draft intent matches the submitted intent.
3. Builds Turnkey `sign_transaction` JSON and stamps it (P-256).
4. Calls Turnkey via **curl** with `X-Stamp`.
5. Expects `ACTIVITY_STATUS_CONSENSUS_NEEDED` with `activityId` and `fingerprint`.
6. `POST /api/turnkey/requests` with `draftId`, `activityId`, and `fingerprint`.

Debug without calling Turnkey:

```bash
scripts/hfa-turnkey-request.py --intent-file intent.json --dry-run --print-curl
```

If the helper succeeds, HFA has notified the paired human device. Do not repeat the manual submit path.

### 3. Manual Fallback: Create Draft

```http
POST https://hfa.s.superfluid.dev/api/turnkey/drafts
Content-Type: application/json
```

```json
{
  "agentPublicKey": "<TURNKEY_AGENT_API_PUBLIC_KEY>",
  "agent": "<HFA_AGENT_LABEL>",
  "intent": {
    "actions": [
      {
        "chainId": 84532,
        "to": "<TARGET>",
        "data": "<CALLDATA>",
        "value": "0"
      }
    ]
  }
}
```

Response includes:

- `draftId`
- `clearSigning` — human-readable preview when available
- `turnkeyTransaction.signWith` — wallet address (case-sensitive for Turnkey)
- `turnkeyTransaction.unsignedTransaction` — raw unsigned tx hex
- `expiresAt`

### 4. Manual Fallback: Sign With Turnkey (curl)

Build the Turnkey body:

```json
{
  "type": "ACTIVITY_TYPE_SIGN_TRANSACTION_V2",
  "timestampMs": "<milliseconds>",
  "organizationId": "<TURNKEY_SUB_ORGANIZATION_ID>",
  "parameters": {
    "signWith": "<draft.turnkeyTransaction.signWith verbatim>",
    "unsignedTransaction": "<draft.turnkeyTransaction.unsignedTransaction>",
    "type": "TRANSACTION_TYPE_ETHEREUM"
  }
}
```

Stamp the exact JSON string with the agent P-256 private key (`scripts/turnkey_stamp.py` implements this).

Submit:

```bash
curl -sS -X POST \
  -H 'Content-Type: application/json' \
  -H 'X-Stamp: <base64url stamp>' \
  -d '<exact JSON body>' \
  https://api.turnkey.com/public/v1/submit/sign_transaction
```

Expect `ACTIVITY_STATUS_CONSENSUS_NEEDED` with `id` and `fingerprint`.

### 5. Manual Fallback: Submit To HFA

```http
POST https://hfa.s.superfluid.dev/api/turnkey/requests
Content-Type: application/json
```

```json
{
  "draftId": "<DRAFT_ID>",
  "activityId": "<TURNKEY_ACTIVITY_ID>",
  "fingerprint": "<TURNKEY_FINGERPRINT>"
}
```

Successful response:

```json
{
  "requestId": "...",
  "status": "pending_human",
  "description": "Transfer 0.01 USDC to 0x30B1...24eE",
  "createdAt": 1700000000000
}
```

At this point HFA has notified the paired human device. The agent should not assume approval.

### 6. Optional: Watch Request Status

```bash
scripts/hfa-watch.py <REQUEST_ID>
```

Optional environment:

```text
PROVIDER_URL=https://hfa.s.superfluid.dev
WALLET_URL=https://wallet.superfluid.org
HFA_POLL_INTERVAL_SECONDS=2
HFA_POLL_TIMEOUT_SECONDS=300
```

Manual status check:

```text
GET https://hfa.s.superfluid.dev/api/turnkey/requests/<REQUEST_ID>
```

Terminal statuses:

- `rejected_by_human`
- `approval_failed`
- `signing_failed`
- `broadcast_failed`
- `executed`

Non-terminal statuses:

- `pending_human`
- `approving`

Report terminal status and any `txHash` or `error` back to the human or calling process when the task requires it.

## Operational Safety Rules

- Never submit if the draft intent differs from the intended transaction.
- Prefer `scripts/hfa-turnkey-request.py`; it verifies draft intent before Turnkey signing.
- Use `turnkeyTransaction.signWith` verbatim when calling Turnkey (addresses are case-sensitive).
- Never assume a missing token address, recipient, chain, amount, or wallet address.
- Treat `clearSigning.aggregatedDescription` as the human-facing approval text, not as proof of correctness.
- Do not expose agent private keys to HFA or the wallet.
- If a draft expires, create a fresh draft before signing again.

## Common Failure Handling

- `404 No HFA pairing found for agentPublicKey`: run setup again with `scripts/hfa-setup.py`.
- Turnkey `Could not find any resource to sign with. Addresses are case sensitive.`: use the draft's `signWith` value verbatim, not a lowercased address.
- `400 Invalid Turnkey activity`: activity does not match the draft; do not retry with different IDs.
- `410 Turnkey draft expired`: create a new draft.
- `409 Turnkey draft already consumed`: create a new draft.
- `503 Push notifications not configured`: provider instance is not ready for approval requests.
- No notification received: ensure the human completed push registration via the wallet success screen (or manually at `/?agent=<WALLET_ADDRESS>&from=hfa-setup&mode=relay`).
