HookCall
The request a Solana app sends. Small enough to audit, explicit enough to enforce.
Schema
type HookCall = {
version: number; // schema version, currently 1
sourceChain: string; // "solana"
sourceApp: string; // registered app's program ID (base58)
hookId: string; // "imd.research.v1"
nonce: string; // per-app, strictly increasing (u64)
action: string; // "START_RESEARCH"
payloadHash: string; // H(canonical payload)
maxSpend?: string; // decimal string in the hook's spend currency
expiry: number; // unix seconds; gateway refuses after this
};The payload itself travels alongside the call. The hash commits to it, so a payload altered in transit fails verification.
Fields
| Field | Why it exists |
|---|---|
version | Lets the gateway reject encodings it doesn't understand instead of misreading them. |
sourceChain, sourceApp | Identify the caller. The gateway also checks the Wormhole emitter matches the registered router. |
hookId, action | What to run. Unknown hook → WH-303; paused → WH-304; not allowed → WH-305. |
nonce | Makes every call unique. Together with the source app and payload hash it derives the call ID. |
payloadHash | Commits to the payload so it can't be swapped. |
maxSpend | Upper bound on value the hook may consume. Checked against the hook cap and app cap (WH-306). |
expiry | A call delivered late is refused, never executed late (WH-201). |
Call ID
callId = H(sourceChain ‖ sourceApp ‖ nonce ‖ payloadHash)The ID is deterministic, so retries and redeliveries of the same call share it. The gateway records consumed IDs, which is why a duplicate delivery is rejected (WH-302) rather than executed twice.
Building one
You don't assemble HookCalls by hand. The SDK does it:
const call = await client.prepareCall({
hookId: "imd.research.v1",
action: "START_RESEARCH",
payload: { query: "…", depth: "standard", budget: "40" },
maxSpend: "40",
ttl: 900, // expiry = now + 15 minutes
});
call.callId; // 0x…
call.call.payloadHash; // 0x…
call.encoded; // mock encoding (skeleton)Choosing a TTL
Expiry must cover source finality (~13s on Solana), executor pickup, and destination inclusion. On Ethereum, a TTL under a few minutes leaves no slack for a slow executor. The SDK defaults to 900 seconds. Shorter TTLs are useful for time-sensitive actions, and you should expect WH-201 when delivery is slow.