返回博客

Dune 学习笔记:一次完整的 Meteora DBC 链上查询实战推演

·0xPM
Web3数据分析Dune

1.明确需求,搞清楚业务逻辑

开发 Launchpad 需要调用 Meteora DBC 的发射功能,而 config 创建是其中的核心一步——它决定了代币的初始价格、迁移阈值、供应量等关键参数。为了有参考依据,我希望调研同样基于 Meteora DBC 构建的平台,看看它们是如何配置的。

调研对象首先确定为:Believe、Bags、Moonshot、Jupiter Studio。

问题是这些平台的链上地址并不能直接获取,需要从它们发射过的代币地址反向推导。这个"反推"的过程正好也属于下一步"搜索数据表"的一部分——业务逻辑与数据结构往往相互印证,理解其中一方有助于看懂另一方。

2.搜索相关的表

用 Dune 内置的搜索 在 Dune 编辑器里,按 Ctrl+Space 触发自动补全,可以浏览表名和字段

本次需要的三张表

event & function

这里出现一个疑问,表格似乎有两种类型,分别代表什么呢?

1. event(事件)
  • 含义:智能合约中记录的重要操作日志,链上触发后会被索引并存储。
  • 数据内容:包含交易哈希、区块时间、操作参数等。
2. function(函数)
  • 含义:智能合约中可调用的方法,用户或合约通过交易调用它们。
  • 数据内容:包含调用者、输入参数、输出结果等。

事件(event)和函数调用(function call)在 Dune 等区块链数据平台中并不是重复的数据,而是从不同角度记录同一操作的两种数据结构,它们互为补充。❗️详情见附录

回到本次需求:如果想查各发射平台的迁移阈值、初始价格等预设参数,应该查 call_createConfig 或 evt_CreateConfigV2 这类记录创建配置的表——这是"意图"侧的数据。如果想知道实际迁移时的价格,则需要结合 evt_Swap2 事件表,从触发迁移的那笔交易里找——这是"结果"侧的数据。两者不重复,但往往需要联表才能还原全貌。

3.查看表字段,了解含义

EvtCreateConfig

平台创建 Bonding Curve 配置时触发的事件。这是最核心的表,记录了每个发射平台设置的所有参数:初始价格、迁移阈值、代币供应量、手续费等。每个 config 地址对应一个平台的一套发行规则。❗️详情见附录

EvtInitializePool

每个代币创建时初始化虚拟池触发的事件。记录"哪个代币使用了哪套平台配置",是连接 config(平台配置)和具体代币池的桥梁表。

EvtCurveComplete

Bonding Curve 达到毕业条件(募集额达到 migration_quote_threshold)时触发的事件,记录"毕业"时刻的池子状态。可通过此表统计各平台的毕业数量和毕业时的 base/quote 储备。

4. 开始写查询

从代币地址倒推平台owner

SELECT ip.base_mint, ip.pool, ip.config, cc.owner FROM meteora_solana.dynamic_bonding_curve_evt_evtinitializepool ip LEFT JOIN meteora_solana.dynamic_bonding_curve_evt_evtcreateconfig cc ON ip.config = cc.config WHERE ip.base_mint = 'BFgdzMkTPdKKJeTipv2njtDEwhKxkgFueJQfJGt1jups';

查询owner创建的config及每个config发射代币情况

查看Dune查询

WITH -- 平台配置参数 platform_config AS ( SELECT evt_block_time, owner, fee_claimer, evt_tx_signer, config, CASE quote_mint WHEN 'So11111111111111111111111111111111111111112' THEN 'SOL' WHEN 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' THEN 'USDC' WHEN 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB' THEN 'USDT' ELSE quote_mint -- 遇到其他代币直接显示 mint 地址 END AS quote_symbol, -- ROUND(POWER(CAST(sqrt_start_price AS DOUBLE) / POWER(2, 64), 2) / POWER(10, 9 - token_decimal), 18) AS initial_price, ROUND(POWER(CAST(sqrt_start_price AS DOUBLE) / POWER(2, 64), 2) , 18) AS initial_price, CAST(migration_quote_threshold AS DOUBLE) / CASE quote_mint WHEN 'So11111111111111111111111111111111111111112' THEN 1e9 -- Wrapped SOL WHEN 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' THEN 1e6 -- USDC WHEN 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB' THEN 1e6 -- USDT ELSE 1e9 -- 默认按 SOL 精度 END AS migration_threshold_adj, token_decimal, fixed_token_supply_flag, migration_option, CAST(migration_base_amount AS DOUBLE) / POWER(10, token_decimal) AS migration_base_amount_adj, CAST(pre_migration_token_supply AS DOUBLE) / POWER(10, token_decimal) AS pre_migration_token_supply_adj, CAST(migration_base_amount AS DOUBLE) / POWER(10, token_decimal) / NULLIF(CAST(pre_migration_token_supply AS DOUBLE) / POWER(10, token_decimal), 0) AS migration_pct FROM meteora_solana.dynamic_bonding_curve_evt_evtcreateconfig WHERE owner = 'CWcERiVd7xkUrcJK5QBdcKC5GG8JMATMLNHtCEUguwPz' ), -- 每个config创建池子数 pool_info AS ( SELECT config, COUNT(pool) AS total_pools FROM meteora_solana.dynamic_bonding_curve_evt_evtinitializepool GROUP BY config ), -- 每个config毕业池子数 grad_info AS ( SELECT config, COUNT(pool) AS completed_pools FROM meteora_solana.dynamic_bonding_curve_evt_evtcurvecomplete GROUP BY config ) -- 最终主查询 SELECT pc.evt_block_time, pc.owner, pc.fee_claimer, pc.evt_tx_signer, pc.config, pc.quote_symbol, pc.initial_price, pc.migration_threshold_adj, pc.token_decimal, pc.fixed_token_supply_flag, pc.migration_option, pc.migration_base_amount_adj, pc.pre_migration_token_supply_adj, pc.migration_pct, COALESCE(pi.total_pools, 0) AS total_pools, -- 该 config 下的总池子数 COALESCE(gi.completed_pools, 0) AS completed_pools, -- 该 config 下已毕业的池子数 ROUND(completed_pools * 1.0 / NULLIF(total_pools, 0), 4) AS graduation_rate FROM platform_config pc LEFT JOIN pool_info pi ON pc.config = pi.config LEFT JOIN grad_info gi ON pc.config = gi.config ORDER BY pi.total_pools DESC;

5. 获取并整理结果

在solscan查询owner地址,对比确认平台

Owner地址平台标记
5wkyL2FLEcyUUgc3UeGntHTAfWfzDrVuxMnaMm7792GkMoonshot.money Fees Vaultsolscan标记确认
BAGSB9TpGrZxQbEsrEznv5jXXdwyP6AXerN8aVRiAmcvBags: Token Authoritysolscan标记确认
9supzj2Nbnjut3r9UFfWE2RiuAenQMoYaNtZXmmtm5oCBelieve未标记
CWcERiVd7xkUrcJK5QBdcKC5GG8JMATMLNHtCEUguwPzJupiter Studio未标记

6. 总结与反思:从链上数据看产品设计

本次针对 Meteora DBC 的链上查询,完成了从业务需求到数据验证的闭环推演。核心沉淀为以下三点:

  • 业务是数据的指南针:链上分析不能盲目摸黑。厘清“代币地址 > 平台 Owner > 全局配置”的漏斗路径,才能在成百上千张表中找准关联键(JOIN Keys)。
  • “意图”与“结果”互补:function call 记录了平台创建配置的“最初意图”(初始价格、毕业阈值),而 event 记录了市场博弈后的“最终结果”(实际毕业率、储备量)。两者结合,才是完整的全景数据。
  • 避开 Solana 的精度陷阱:Solana 链上数据有其特殊性。处理价格时必须通过公式将 Q64.64 定点数还原,并根据 quote_mint 的不同(SOL 为 9 位精度,USDC 为 6 位精度)动态对齐精度,否则数据会失真。

价值落地:通过对 Moonshot、Bags 等头部平台配置参数与真实毕业率(graduation_rate)的解构,我们拿到了公开的行业标杆。这些真实的链上指标,将为我们自研 Launchpad 的代币经济学和价格曲线设计提供最坚实的数据支撑。

附录

event和function核心区别

维度事件(event)函数调用(function call)
存储表名通常以 evt_ 开头,如 evt_Swap通常以 call_ 开头,如 call_swap
记录时机执行后:由智能合约主动触发,记录在交易收据中执行前:作为交易输入数据的一部分,记录调用者意图
内容包含执行结果(如兑换后的新价格、实际成交数量)包含调用参数(如输入金额、滑点、接收方)
是否必须不是所有操作都必须触发事件(但规范项目一般会)每个操作都必须有函数调用才能执行
索引性事件字段通常被解析成独立的列,便于筛选分析函数参数可能存储在结构化字段中,也可能在 data 字段里
用途追踪状态变化、监控合约活动分析用户行为、还原原始输入
以 swap 操作为例

假设用户调用 swap 函数进行代币兑换:

  • 函数调用表(call_swap)会记录:
    • 调用者地址
    • 输入的代币数量(amount_in)
    • 最小输出代币数量(min_amount_out)
    • 接收方地址等原始参数。
  • 事件表(evt_Swap)会记录:
    • 实际兑换后的输出数量(amount_out)
    • 交易完成后的池子状态(如新的储备量、新的价格)
    • 可能包含交易手续费等执行细节。

可以看到,事件包含了函数调用中不存在的信息(如实际成交结果),而函数调用包含了事件中不存在的信息(如调用者的滑点设置)。两者结合才能完整还原一笔交易的全貌。

表格字段详情

EvtCreateConfig

平台创建 Bonding Curve 配置时触发的事件。这是最核心的表,记录了每个发射平台设置的所有参数:初始价格、迁移阈值、代币供应量、手续费等。每个 config 地址对应一个平台的一套发行规则。

字段名类型说明
🕐 区块元数据
evt_block_slotbigint该事件所在的 Solana 区块 slot 编号,可用于排序和时间对齐
evt_block_datedate区块日期(YYYY-MM-DD),方便按天聚合统计
evt_block_timetimestamp区块精确时间戳(UTC),用于精确时间筛选
evt_block_hashvarchar区块哈希,唯一标识该区块
📍 指令位置
evt_tx_indexinteger该交易在区块内的顺序编号(第几笔交易)
evt_outer_instruction_indexinteger外层指令的索引(顶层 instruction 编号)
evt_inner_instruction_indexinteger内层指令的索引(CPI 调用时的子指令编号,非内层时为 null)
evt_is_innerboolean是否为内层指令(CPI)。true = 由其他合约间接调用
🔗 执行账户
evt_executing_accountvarchar实际执行该指令的程序账户地址(通常即 DBC 程序地址)
evt_outer_executing_accountvarchar外层(顶层)执行账户,若为 CPI 则为发起调用的程序地址
evt_inner_executing_accountvarchar内层执行账户(仅 CPI 场景有值)
📄 交易信息
evt_tx_idvarchar交易签名(Transaction Hash),可在 Solscan 查询原始交易
evt_tx_signervarchar交易签名者(通常为平台的操作钱包地址,可用于识别不同平台)
⭐ 核心业务字段
configvarchar配置账户地址,唯一标识这套发行规则。JOIN 其他表的关键字段
quote_mintvarchar计价代币 Mint 地址(如 SOL 的 Wrapped SOL 地址或 USDC 地址),即用什么代币来买
fee_claimervarchar手续费接收者钱包地址(平台方收费地址)
ownervarchar该配置的所有者(创建此 config 的平台钱包),可用于区分不同发射平台
💰 价格与供应量(最重要)
sqrt_start_priceuint256初始价格的平方根(Q64.64 定点数格式)。初始价格 = (sqrt_start_price / 2^64)²,单位为 quote/base
migration_quote_thresholduint256迁移阈值:Bonding Curve 池子需要募集到多少 quote 代币才触发迁移(即"毕业线")
migration_base_amountuint256迁移时注入 AMM 的 base 代币数量,即迁移到 Meteora DAMM 时带过去多少项目代币
swap_base_amountuint256在 Bonding Curve 阶段可供交易的 base 代币总量(即 bonding curve 上可买到的代币总量)
pre_migration_token_supplyuint256迁移前的代币总供应量
post_migration_token_supplyuint256迁移后的代币总供应量(可能含增发或销毁,迁移完成后的最终总量)
🔧 配置选项
pool_feesvarchar池子手续费配置(JSON 格式字符串),包含 base fee、dynamic fee 等参数
collect_fee_modeinteger手续费收取模式。0=仅收 quote 代币手续费,1=收 base+quote 双向手续费
migration_optioninteger迁移目标选项。0=迁移到 Meteora DAMM v1,1=迁移到 DAMM v2
activation_typeinteger代币激活方式。0=按 slot 激活,1=按时间戳激活
token_decimalintegerbase 代币小数位数(通常为 6 或 9)
token_typeinteger代币标准类型。0=SPL Token,1=Token2022
fixed_token_supply_flaginteger是否固定代币总量。1=固定供应,0=非固定(影响迁移后是否有剩余代币)
migration_fee_optioninteger迁移手续费方案选项,对应不同的迁移费率档位
curvearray(varchar)Bonding Curve 的分段参数数组,定义价格随购买量变化的曲线形状(各段斜率等)
locked_vestingvarchar锁仓/归属计划配置(JSON 格式),描述 LP 代币的锁定释放规则
📊 LP 分配比例
partner_locked_lp_percentageinteger平台方获得的锁仓 LP 百分比
partner_lp_percentageinteger平台方获得的可流通 LP 百分比
creator_locked_lp_percentageinteger代币创建者获得的锁仓 LP 百分比
creator_lp_percentageinteger代币创建者获得的可流通 LP 百分比
partner_permanent_locked_liquidity_percentageinteger平台方的永久锁仓流动性百分比(永不解锁)
partner_liquidity_percentageinteger平台方的普通流动性百分比
creator_permanent_locked_liquidity_percentageinteger代币创建者的永久锁仓流动性百分比(永不解锁)
creator_liquidity_percentageinteger代币创建者的普通流动性百分比

EvtInitializePool

每个代币创建时初始化虚拟池触发的事件。记录"哪个代币使用了哪套平台配置",是连接 config(平台配置)和具体代币池的桥梁表。

字段名类型说明
🕐 区块元数据
evt_block_slotbigint区块 slot 编号
evt_block_datedate区块日期
evt_block_timetimestamp区块时间戳(代币创建时间)
evt_block_hashvarchar区块哈希
evt_tx_indexinteger交易顺序
evt_outer_instruction_indexinteger外层指令索引
evt_inner_instruction_indexinteger内层指令索引
evt_inner_executing_accountvarchar内层执行账户
evt_outer_executing_accountvarchar外层执行账户
evt_executing_accountvarchar执行程序账户
evt_is_innerboolean是否为 CPI 内层调用
evt_tx_idvarchar交易签名
evt_tx_signervarchar交易签名者(代币创建者钱包)
⭐ 核心业务字段
poolvarchar虚拟池账户地址,代表该代币的 Bonding Curve 池,是池子的唯一标识
configvarchar使用的配置账户地址,JOIN EvtCreateConfig 的关键字段,通过此字段知道该代币用了哪个平台的规则
creatorvarchar代币创建者钱包地址
base_mintvarchar代币 Mint 地址(即项目代币的合约地址),可在 Solscan 查该代币信息
pool_typeinteger池子类型。0=普通虚拟池,1=SPL Token 池,2=Token2022 池(对应不同创建函数)
activation_pointuint256代币激活点(根据 activation_type 不同,为 slot 编号或 Unix 时间戳),在此之后代币才可交易

EvtCurveComplete

Bonding Curve 达到毕业条件(募集额达到 migration_quote_threshold)时触发的事件,记录"毕业"时刻的池子状态。可通过此表统计各平台的毕业数量和毕业时的 base/quote 储备。

字段名类型说明
🕐 区块元数据
evt_block_slotbigint毕业发生时的区块 slot
evt_block_datedate毕业日期
evt_block_timetimestamp毕业时间戳
evt_block_hashvarchar区块哈希
evt_tx_indexinteger交易顺序
evt_outer_instruction_indexinteger外层指令索引
evt_inner_instruction_indexinteger内层指令索引
evt_inner_executing_accountvarchar内层执行账户
evt_outer_executing_accountvarchar外层执行账户
evt_executing_accountvarchar执行程序账户
evt_is_innerboolean是否为 CPI 调用
evt_tx_idvarchar交易签名
evt_tx_signervarchar触发最后一笔交易(使曲线完成)的签名者
⭐ 核心业务字段
poolvarchar虚拟池地址,JOIN EvtInitializePool 可得到代币 Mint 和 config
configvarchar配置账户地址,JOIN EvtCreateConfig 可知是哪个平台的规则
base_reserveuint256曲线完成时池中剩余的 base(项目)代币数量,即将迁移到 AMM 的代币量(扣除精度后的实际数量)
quote_reserveuint256曲线完成时池中募集的 quote 代币总量(应约等于 migration_quote_threshold),即迁移到 AMM 的 SOL/USDC 数量

评论

加载中…