-
Notifications
You must be signed in to change notification settings - Fork 526
feat: base sql api query action provider #843
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
🟡 Heimdall Review Status
|
Hi @gtspencer, thanks a lot for getting started with this! Was also on my TODO list and could be a very useful feature to add
|
I think it would be better to rename this from It can then be moved in the cdp folder together with CdpApiActionProvider etc |
const cdpApiKey = process.env.CDP_API_CLIENT_KEY; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a config to set the api key and thow error when not set (see for example ZerionActionProvider)
const cdpApiKey = process.env.CDP_API_CLIENT_KEY; | |
const cdpApiClientKey = config.cdpApiClientKey || process.env.CDP_API_CLIENT_KEY; | |
if (!cdpApiClientKey) { | |
throw new Error("CDP_API_CLIENT_KEY is not configured."); | |
} | |
this.cdpApiClientKey = cdpApiClientKey; |
if (!response.ok) { | ||
throw new Error(`HTTP error! status: ${response.status}`); | ||
} | ||
|
||
const result = await response.text(); | ||
|
||
return `Query executed with result: ${result}.`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should return more detailed error info
if (!response.ok) { | |
throw new Error(`HTTP error! status: ${response.status}`); | |
} | |
const result = await response.text(); | |
return `Query executed with result: ${result}.`; | |
if (!response.ok) { | |
const errorData = await response.json(); | |
return `Error ${response.status} executing Base SQL query: ${errorData.errorMessage || response.statusText}`; | |
} | |
const data = await response.json(); | |
return JSON.stringify(data.result); |
], | ||
}, | ||
{ | ||
tableName: "base.transfers", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it seems base.transfers table is not live yet, will check with the team. Comment out for now
sqlQuery: z.string().describe("The sql query to execute, using the defined tables and fields"), | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add query limitations
sqlQuery: z.string().describe("The sql query to execute, using the defined tables and fields"), | |
}); | |
sqlQuery: z | |
.string() | |
.min(1, "SQL query cannot be empty") | |
.max(10000, "Query exceeds maximum length of 10,000 characters") | |
.describe( | |
`The SQL query to execute using ClickHouse syntax. Must be a read-only SELECT statement. ` + | |
`Limitations: max 10,000 characters, max 5 JOINs, no cartesian products, 30s timeout. API supports a max of 10,000 result rows but limit it to 10 unless otherwise specified.` | |
),}); |
This action can call Coinbase's Base SQL API to retrieve onchain data on Base. | ||
Call this action if the user requests historical data on Base. | ||
The SQL API schema is a set of opinionated tables and columns used to organize onchain data for efficient retrieval. | ||
The supported table names, and fields in each table, for SQL queries are defined in the json string: | ||
${schemaJson} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest to add query requirements and use cases.
I think it would help to include the examples from the sql playground as one-shot examples to better guide the LLM.
This action can call Coinbase's Base SQL API to retrieve onchain data on Base. | |
Call this action if the user requests historical data on Base. | |
The SQL API schema is a set of opinionated tables and columns used to organize onchain data for efficient retrieval. | |
The supported table names, and fields in each table, for SQL queries are defined in the json string: | |
${schemaJson} | |
This action executes read-only SQL queries against indexed blockchain data using the CDP SQL API. | |
**Use Cases:** | |
- Query transaction history and patterns | |
- Analyze event logs and smart contract interactions | |
- Retrieve block information and metadata | |
- Examine token transfers and DeFi activity | |
**IMPORTANT Query Requirements:** | |
- Must be SELECT statements only (ClickHouse SQL dialect) | |
- Casts use the ::<Type> syntax (not CAST(... AS ...)) | |
- Maximum query length: 10,000 characters | |
- Maximum result rows: 10,000 | |
- Query timeout: 30 seconds | |
- Maximum JOINs: 5 | |
- No cartesian products allowed | |
- No DDL/DML operations (INSERT, UPDATE, DELETE, etc.) | |
- Keep it simple and break down the task into several queries if appropriate. | |
**Available Tables:** | |
- base.events: Decoded event logs with parameters and signatures | |
- base.transactions: Complete transaction data including gas and signatures | |
- base.blocks: Block information and metadata | |
- base.encoded_logs: Raw log data that couldn't be decoded | |
**Table Schema Details:** | |
${schemaJson} | |
**Example Queries:** | |
1. Get ERC-20 token transfers for USDC: | |
SELECT | |
parameters['from']::String AS sender, | |
parameters['to']::String AS to, | |
parameters['value']::UInt256 AS amount, | |
address AS token_address | |
FROM base.events | |
WHERE | |
event_signature = 'Transfer(address,address,uint256)' | |
AND address = '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913' | |
LIMIT 10; | |
2. Get swap events from Uniswap v2-style DEXes: | |
SELECT parameters ['to']::String AS to, | |
parameters ['amount0In']::UInt256 AS amount0In, | |
parameters ['amount0Out']::UInt256 AS amount0Out, | |
parameters ['amount1In']::UInt256 AS amount1In, | |
parameters ['amount1Out']::UInt256 AS amount1Out, | |
parameters ['sender']::String AS sender | |
FROM base.events | |
WHERE event_signature = 'Swap(address,uint256,uint256,uint256,uint256,address)' | |
LIMIT 10; | |
3. Show me 10 rows from the events table: | |
SELECT * FROM base.events LIMIT 10; | |
4. Aggregate ZORA content rewards by coin and currency for payout recipient 0x0bC5f409e4d9298B93E98920276128b89280d832: | |
SELECT | |
parameters ['coin']::String as coin, | |
parameters ['currency']::String as currency, | |
sum( | |
( | |
replaceAll( | |
splitByChar(' ', parameters ['marketRewards']::String) [1], | |
'{', | |
'' | |
) | |
)::UInt64 | |
) as market_rewards | |
FROM base.events | |
WHERE | |
event_signature = 'CoinMarketRewardsV4(address,address,address,address,address,address,address,(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256))' | |
AND parameters ['payoutRecipient']::String = lower('0x0bC5f409e4d9298B93E98920276128b89280d832') | |
GROUP BY coin, currency; |
Hi @gtspencer, the very simple queries from your examples work fine but anything a little bit more complex (eg "find largets usdc (0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) transfers in last block") just throws errors. Please have a look at my suggested improvements for the description and schema to better guide the LLM. gpt-4o-mini still failed most of my queries, gpt-5-mini was decent. One issue I see is that the description is very long with all the excessive schema details which could overwhelm LLMs.
But let's first get in the other changes suggested above and then go from there. |
Hey @phdargen good changes, just updated! And completely agree, the entire schema in 1 file is somewhat unwieldy. I briefly played around with the result of your requested changes, and it feels better, but I'll test it out tomorrow with it broken into multiple actions, 1 for the main query, and 1 for the schema lookup and see how they compare to each other. |
- No DDL/DML operations (INSERT, UPDATE, DELETE, etc.) | ||
- Keep it simple and break down the task into several queries if appropriate. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add this line:
- No DDL/DML operations (INSERT, UPDATE, DELETE, etc.) | |
- Keep it simple and break down the task into several queries if appropriate. | |
- No DDL/DML operations (INSERT, UPDATE, DELETE, etc.) | |
- Always normalize addresses with lower('<addr>') | |
- Keep it simple and break down the task into several queries if appropriate. |
├── cdpSmartWalletActionProvider.test.ts # Tests for CDP Smart Wallet provider | ||
├── cdpSqlApiActionProvider.ts # Main provider implementation | ||
├── cdpSqlApiActionProvider.test.ts # Provider test suite | ||
├── baseSqlApiDescription.ts # Variables describing the action and valid SQL Schemas |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
baseSqlApiDescription -> cdpSqlApiDescription
Hi @gtspencer, changes look good so far! Here some updates:
|
FROM base.events | ||
WHERE | ||
event_signature = 'Transfer(address,address,uint256)' | ||
AND address = '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AND address = '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913' | |
AND address = lower('0x833589fcd6edb6e08f4c7c32d4f71b54bda02913') |
Description
Added support for the recently released (Coinbase SQL API)[https://docs.cdp.coinbase.com/data/sql-api/welcome]
Tests
Checklist
A couple of things to include in your PR for completeness:
Notes (and questions)