Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions clients/cli/src/agent/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,20 +275,6 @@ export class AgentClient {
}
}

// ============================================================================
// Calldata
// ============================================================================

async getCalldata(id: string): Promise<{ data: string; to?: string; chain?: string }> {
const res = await fetch(`${this.baseUrl}/agent/calldata/${id}`, {
headers: this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {},
})
if (!res.ok) {
throw new Error(`Failed to resolve calldata_id ${id}: ${res.status} ${res.statusText}`)
}
return res.json() as Promise<{ data: string; to?: string; chain?: string }>
}

// ============================================================================
// Private helpers
// ============================================================================
Expand Down
61 changes: 8 additions & 53 deletions clients/cli/src/agent/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@ export class AgentExecutor {
private stateStore: VaultStateStore | null = null
/** Held chain lock release functions, keyed by chain name */
private chainLockReleases = new Map<string, () => Promise<void>>()
/** Backend client for resolving calldata_id references. */
private backendClient: { getCalldata(id: string): Promise<{ data: string; to?: string; chain?: string }> } | null =
null

constructor(vault: VaultBase, verbose = false, vaultId?: string) {
this.vault = vault
Expand All @@ -85,10 +82,6 @@ export class AgentExecutor {
this.password = password
}

setBackendClient(client: { getCalldata(id: string): Promise<{ data: string; to?: string; chain?: string }> }): void {
this.backendClient = client
}

/**
* Store a server-built transaction (from tx_ready SSE event).
* This allows sign_tx to find and sign it when the backend requests signing.
Expand Down Expand Up @@ -483,54 +476,12 @@ export class AgentExecutor {
}

private async buildTx(params: Record<string, unknown>): Promise<Record<string, unknown>> {
// Resolve calldata_id → actual data before any other checks
if (params.calldata_id && !params.data && this.backendClient) {
const id = params.calldata_id as string
if (this.verbose) process.stderr.write(`[executor] resolving calldata_id ${id}\n`)
const entry = await this.backendClient.getCalldata(id)
params = { ...params, data: entry.data }
if (!params.to && entry.to) params = { ...params, to: entry.to }
delete (params as Record<string, unknown>).calldata_id
if (this.verbose) process.stderr.write(`[executor] calldata_id resolved, data len=${entry.data.length}\n`)
}

// EVM contract call with function_name + typed params (e.g. from build_custom_tx)
// EVM contract call with function_name + typed params
if (params.function_name && params.contract_address) {
return this.buildContractCallTx(params)
}

// If this has raw contract call data (hex payload from MCP), treat it as a server-built tx
if (params.data || params.calldata || params.hex_payload) {
const txData = {
to: params.to || params.address || params.contract,
value: params.value || '0',
data: params.data || params.calldata || params.hex_payload,
chain: params.chain,
chain_id: params.chain_id,
}

// Store as a server-style tx for sign_tx to pick up
this.storeServerTransaction({
tx: txData,
chain: params.chain,
from_chain: params.chain,
})

const chain = resolveChain(params.chain as string) || Chain.Ethereum
const address = await this.vault.address(chain)

return {
status: 'ready',
chain: chain.toString(),
from: address,
to: txData.to,
value: txData.value,
has_calldata: true,
message: 'Transaction built. Ready to sign.',
}
}

// If we got here with contract_address but no function_name or data,
// If we got here with contract_address but no function_name,
// the params are incomplete for a contract call.
if (params.contract_address && !params.function_name) {
const provided = Object.keys(params).join(', ')
Expand All @@ -540,8 +491,12 @@ export class AgentExecutor {
)
}

// Fallback to simple send for native transfers
return this.buildSendTx(params)
throw new Error(
`build_custom_tx: unrecognized params shape. ` +
`Expected function_name + contract_address for ABI-encoding. ` +
`Server-built calldata should arrive via tx_ready, not via action params. ` +
`Got keys: ${Object.keys(params).join(', ')}`
)
}

/**
Expand Down
3 changes: 0 additions & 3 deletions clients/cli/src/agent/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,6 @@ export class AgentSession {
this.client.setAuthToken(auth.token)
saveCachedToken(this.publicKey, auth.token, auth.expiresAt)
}

// Give the executor access to the authenticated client for calldata_id resolution
this.executor.setBackendClient(this.client)
} catch (err: any) {
throw new Error(`Authentication failed: ${err.message}`)
}
Expand Down
Loading