debridge
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesedeBridge Solana SDK Development Guide
deBridge Solana SDK 开发指南
A comprehensive guide for building Solana programs with the deBridge Solana SDK - enabling decentralized cross-chain transfers of arbitrary messages and value between blockchains.
为使用deBridge Solana SDK构建Solana程序提供的全面指南——支持区块链之间任意消息和价值的去中心化跨链传输。
Overview
概述
deBridge is a cross-chain infrastructure protocol enabling:
- Cross-Chain Transfers: Bridge assets between Solana and 20+ EVM chains
- Message Passing: Send arbitrary messages across blockchains
- External Calls: Execute smart contract calls on destination chains
- Sub-Second Settlement: ~2 second median settlement time
- Capital Efficiency: Intent-based architecture with 4bps lowest spreads
deBridge是一款跨链基础设施协议,支持:
- 跨链转账:在Solana与20多条EVM链之间桥接资产
- 消息传递:在区块链之间发送任意消息
- 外部调用:在目标链上执行智能合约调用
- 亚秒级结算:中位数结算时间约2秒
- 资本效率:基于意图的架构,点差低至4个基点
Key Features
核心特性
- 26+ security audits (Halborn, Zokyo, Ackee Blockchain)
- $200K bug bounty on Immunefi
- 100% uptime since launch
- Zero security incidents
- 26+次安全审计(由Halborn、Zokyo、Ackee Blockchain完成)
- 在Immunefi上设有20万美元的漏洞赏金
- 上线以来100%正常运行时间
- 无安全事故
Quick Start
快速开始
Installation
安装
Add the SDK to your Anchor/Solana program:
bash
cargo add --git ssh://git@github.com/debridge-finance/debridge-solana-sdk.git debridge-solana-sdkOr add to :
Cargo.tomltoml
[dependencies]
debridge-solana-sdk = { git = "ssh://git@github.com/debridge-finance/debridge-solana-sdk.git" }将SDK添加到你的Anchor/Solana程序中:
bash
cargo add --git ssh://git@github.com/debridge-finance/debridge-solana-sdk.git debridge-solana-sdk或者添加到:
Cargo.tomltoml
[dependencies]
debridge-solana-sdk = { git = "ssh://git@github.com/debridge-finance/debridge-solana-sdk.git" }Basic Setup (Anchor)
基础设置(Anchor)
rust
use anchor_lang::prelude::*;
use debridge_solana_sdk::prelude::*;
declare_id!("YourProgramId11111111111111111111111111111");
#[program]
pub mod my_bridge_program {
use super::*;
pub fn send_cross_chain(
ctx: Context<SendCrossChain>,
target_chain_id: [u8; 32],
receiver: Vec<u8>,
amount: u64,
) -> Result<()> {
// Invoke deBridge send
debridge_sending::invoke_debridge_send(
debridge_sending::SendIx {
target_chain_id,
receiver,
is_use_asset_fee: false, // Use native SOL for fees
amount,
submission_params: None,
referral_code: None,
},
ctx.remaining_accounts,
)?;
Ok(())
}
}
#[derive(Accounts)]
pub struct SendCrossChain<'info> {
#[account(mut)]
pub sender: Signer<'info>,
// Additional accounts passed via remaining_accounts
}rust
use anchor_lang::prelude::*;
use debridge_solana_sdk::prelude::*;
declare_id!("YourProgramId11111111111111111111111111111");
#[program]
pub mod my_bridge_program {
use super::*;
pub fn send_cross_chain(
ctx: Context<SendCrossChain>,
target_chain_id: [u8; 32],
receiver: Vec<u8>,
amount: u64,
) -> Result<()> {
// Invoke deBridge send
debridge_sending::invoke_debridge_send(
debridge_sending::SendIx {
target_chain_id,
receiver,
is_use_asset_fee: false, // Use native SOL for fees
amount,
submission_params: None,
referral_code: None,
},
ctx.remaining_accounts,
)?;
Ok(())
}
}
#[derive(Accounts)]
pub struct SendCrossChain<'info> {
#[account(mut)]
pub sender: Signer<'info>,
// Additional accounts passed via remaining_accounts
}Core Concepts
核心概念
1. Chain IDs
1. 链ID
deBridge uses 32-byte chain identifiers for all supported networks:
rust
use debridge_solana_sdk::chain_ids::*;
// Solana
let solana = SOLANA_CHAIN_ID; // Solana mainnet
// EVM Chains
let ethereum = ETHEREUM_CHAIN_ID; // Chain ID: 1
let polygon = POLYGON_CHAIN_ID; // Chain ID: 137
let bnb = BNB_CHAIN_CHAIN_ID; // Chain ID: 56
let arbitrum = ARBITRUM_CHAIN_ID; // Chain ID: 42161
let avalanche = AVALANCHE_CHAIN_ID; // Chain ID: 43114
let fantom = FANTOM_CHAIN_ID; // Chain ID: 250
let heco = HECO_CHAIN_ID; // Chain ID: 128deBridge对所有支持的网络使用32字节的链标识符:
rust
use debridge_solana_sdk::chain_ids::*;
// Solana
let solana = SOLANA_CHAIN_ID; // Solana主网
// EVM Chains
let ethereum = ETHEREUM_CHAIN_ID; // Chain ID: 1
let polygon = POLYGON_CHAIN_ID; // Chain ID: 137
let bnb = BNB_CHAIN_CHAIN_ID; // Chain ID: 56
let arbitrum = ARBITRUM_CHAIN_ID; // Chain ID: 42161
let avalanche = AVALANCHE_CHAIN_ID; // Chain ID: 43114
let fantom = FANTOM_CHAIN_ID; // Chain ID: 250
let heco = HECO_CHAIN_ID; // Chain ID: 1282. Program IDs
2. 程序ID
rust
use debridge_solana_sdk::{DEBRIDGE_ID, SETTINGS_ID};
// Main deBridge program for sending/claiming
let debridge_program = DEBRIDGE_ID;
// Settings and confirmation storage program
let settings_program = SETTINGS_ID;rust
use debridge_solana_sdk::{DEBRIDGE_ID, SETTINGS_ID};
// 用于发送/领取的主deBridge程序
let debridge_program = DEBRIDGE_ID;
// 设置和确认存储程序
let settings_program = SETTINGS_ID;3. Fee Structure
3. 费用结构
deBridge supports multiple fee payment methods:
rust
// Native Fee (SOL)
is_use_asset_fee: false // Pay fees in SOL
// Asset Fee
is_use_asset_fee: true // Pay fees in the bridged token
// Fee Constants
const BPS_DENOMINATOR: u64 = 10000; // Basis points divisordeBridge支持多种费用支付方式:
rust
// 原生费用(SOL)
is_use_asset_fee: false // 使用原生SOL支付费用
// 资产费用
is_use_asset_fee: true // 使用桥接代币支付费用
// 费用常量
const BPS_DENOMINATOR: u64 = 10000; // 基点除数4. Flags
4. 标志位
Control transfer behavior with flags:
rust
use debridge_solana_sdk::flags::*;
// Available flags (bit positions)
const UNWRAP_ETH: u8 = 0; // Unwrap to native ETH on destination
const REVERT_IF_EXTERNAL_FAIL: u8 = 1; // Revert if external call fails
const PROXY_WITH_SENDER: u8 = 2; // Include sender in proxy call
const SEND_HASHED_DATA: u8 = 3; // Send data as hash
const DIRECT_WALLET_FLOW: u8 = 31; // Use direct wallet flow
// Setting flags on submission params
let mut flags = [0u8; 32];
flags.set_reserved_flag(UNWRAP_ETH);
flags.set_reserved_flag(REVERT_IF_EXTERNAL_FAIL);通过标志位控制转账行为:
rust
use debridge_solana_sdk::flags::*;
// 可用标志位(比特位位置)
const UNWRAP_ETH: u8 = 0; // 在目标链上解包为原生ETH
const REVERT_IF_EXTERNAL_FAIL: u8 = 1; // 外部调用失败时回滚
const PROXY_WITH_SENDER: u8 = 2; // 在代理调用中包含发送方信息
const SEND_HASHED_DATA: u8 = 3; // 以哈希形式发送数据
const DIRECT_WALLET_FLOW: u8 = 31; // 使用直接钱包流程
// 在提交参数中设置标志位
let mut flags = [0u8; 32];
flags.set_reserved_flag(UNWRAP_ETH);
flags.set_reserved_flag(REVERT_IF_EXTERNAL_FAIL);Sending Cross-Chain Transfers
发送跨链转账
Basic Token Transfer
基础代币转账
rust
use debridge_solana_sdk::prelude::*;
pub fn send_tokens(
ctx: Context<SendTokens>,
amount: u64,
) -> Result<()> {
debridge_sending::invoke_debridge_send(
debridge_sending::SendIx {
target_chain_id: chain_ids::ETHEREUM_CHAIN_ID,
receiver: recipient_eth_address.to_vec(),
is_use_asset_fee: false,
amount,
submission_params: None,
referral_code: Some(12345), // Optional referral
},
ctx.remaining_accounts,
)?;
Ok(())
}rust
use debridge_solana_sdk::prelude::*;
pub fn send_tokens(
ctx: Context<SendTokens>,
amount: u64,
) -> Result<()> {
debridge_sending::invoke_debridge_send(
debridge_sending::SendIx {
target_chain_id: chain_ids::ETHEREUM_CHAIN_ID,
receiver: recipient_eth_address.to_vec(),
is_use_asset_fee: false,
amount,
submission_params: None,
referral_code: Some(12345), // 可选推荐码
},
ctx.remaining_accounts,
)?;
Ok(())
}Transfer with Fixed Native Fee
固定原生费用转账
rust
pub fn send_with_native_fee(
ctx: Context<Send>,
target_chain_id: [u8; 32],
receiver: Vec<u8>,
amount: u64,
) -> Result<()> {
// Get the fixed fee for the target chain
let fee = debridge_sending::get_chain_native_fix_fee(
&target_chain_id,
ctx.remaining_accounts,
)?;
debridge_sending::invoke_debridge_send(
debridge_sending::SendIx {
target_chain_id,
receiver,
is_use_asset_fee: false,
amount,
submission_params: None,
referral_code: None,
},
ctx.remaining_accounts,
)?;
Ok(())
}rust
pub fn send_with_native_fee(
ctx: Context<Send>,
target_chain_id: [u8; 32],
receiver: Vec<u8>,
amount: u64,
) -> Result<()> {
// 获取目标链的固定费用
let fee = debridge_sending::get_chain_native_fix_fee(
&target_chain_id,
ctx.remaining_accounts,
)?;
debridge_sending::invoke_debridge_send(
debridge_sending::SendIx {
target_chain_id,
receiver,
is_use_asset_fee: false,
amount,
submission_params: None,
referral_code: None,
},
ctx.remaining_accounts,
)?;
Ok(())
}Transfer with Asset Fee
资产费用转账
rust
pub fn send_with_asset_fee(
ctx: Context<Send>,
target_chain_id: [u8; 32],
receiver: Vec<u8>,
amount: u64,
) -> Result<()> {
// Check if asset fee is available for this chain
let is_available = debridge_sending::is_asset_fee_available(
&target_chain_id,
ctx.remaining_accounts,
)?;
if !is_available {
return Err(error!(ErrorCode::AssetFeeNotAvailable));
}
debridge_sending::invoke_debridge_send(
debridge_sending::SendIx {
target_chain_id,
receiver,
is_use_asset_fee: true, // Use asset for fees
amount,
submission_params: None,
referral_code: None,
},
ctx.remaining_accounts,
)?;
Ok(())
}rust
pub fn send_with_asset_fee(
ctx: Context<Send>,
target_chain_id: [u8; 32],
receiver: Vec<u8>,
amount: u64,
) -> Result<()> {
// 检查该链是否支持资产费用
let is_available = debridge_sending::is_asset_fee_available(
&target_chain_id,
ctx.remaining_accounts,
)?;
if !is_available {
return Err(error!(ErrorCode::AssetFeeNotAvailable));
}
debridge_sending::invoke_debridge_send(
debridge_sending::SendIx {
target_chain_id,
receiver,
is_use_asset_fee: true, // 使用资产支付费用
amount,
submission_params: None,
referral_code: None,
},
ctx.remaining_accounts,
)?;
Ok(())
}Transfer with Exact Amount
精确到账金额转账
rust
pub fn send_exact_amount(
ctx: Context<Send>,
target_chain_id: [u8; 32],
receiver: Vec<u8>,
exact_receive_amount: u64,
) -> Result<()> {
// Calculate total amount including fees
let total_with_fees = debridge_sending::add_all_fees(
exact_receive_amount,
&target_chain_id,
ctx.remaining_accounts,
)?;
debridge_sending::invoke_debridge_send(
debridge_sending::SendIx {
target_chain_id,
receiver,
is_use_asset_fee: true,
amount: total_with_fees,
submission_params: None,
referral_code: None,
},
ctx.remaining_accounts,
)?;
Ok(())
}rust
pub fn send_exact_amount(
ctx: Context<Send>,
target_chain_id: [u8; 32],
receiver: Vec<u8>,
exact_receive_amount: u64,
) -> Result<()> {
// 计算包含费用的总金额
let total_with_fees = debridge_sending::add_all_fees(
exact_receive_amount,
&target_chain_id,
ctx.remaining_accounts,
)?;
debridge_sending::invoke_debridge_send(
debridge_sending::SendIx {
target_chain_id,
receiver,
is_use_asset_fee: true,
amount: total_with_fees,
submission_params: None,
referral_code: None,
},
ctx.remaining_accounts,
)?;
Ok(())
}Transfer from PDA (Signed)
从PDA发送(签名版)
rust
pub fn send_from_pda(
ctx: Context<SendFromPda>,
target_chain_id: [u8; 32],
receiver: Vec<u8>,
amount: u64,
pda_seeds: Vec<Vec<u8>>,
) -> Result<()> {
// Use signed variant for PDA-owned tokens
debridge_sending::invoke_debridge_send_signed(
debridge_sending::SendIx {
target_chain_id,
receiver,
is_use_asset_fee: false,
amount,
submission_params: None,
referral_code: None,
},
ctx.remaining_accounts,
&pda_seeds,
)?;
Ok(())
}rust
pub fn send_from_pda(
ctx: Context<SendFromPda>,
target_chain_id: [u8; 32],
receiver: Vec<u8>,
amount: u64,
pda_seeds: Vec<Vec<u8>>,
) -> Result<()> {
// 针对PDA持有的代币使用签名版方法
debridge_sending::invoke_debridge_send_signed(
debridge_sending::SendIx {
target_chain_id,
receiver,
is_use_asset_fee: false,
amount,
submission_params: None,
referral_code: None,
},
ctx.remaining_accounts,
&pda_seeds,
)?;
Ok(())
}Message Passing
消息传递
Send messages without token transfers:
rust
use debridge_solana_sdk::prelude::*;
pub fn send_message(
ctx: Context<SendMessage>,
target_chain_id: [u8; 32],
receiver: Vec<u8>,
message_data: Vec<u8>,
) -> Result<()> {
// Create submission params with message
let submission_params = debridge_sending::SendSubmissionParamsInput {
execution_fee: 0,
flags: [0u8; 32],
fallback_address: receiver.clone(),
external_call_shortcut: compute_keccak256(&message_data),
};
// Send message (zero amount)
debridge_sending::invoke_send_message(
debridge_sending::SendIx {
target_chain_id,
receiver,
is_use_asset_fee: false,
amount: 0, // No token transfer
submission_params: Some(submission_params),
referral_code: None,
},
ctx.remaining_accounts,
)?;
Ok(())
}无需代币转账的纯消息发送:
rust
use debridge_solana_sdk::prelude::*;
pub fn send_message(
ctx: Context<SendMessage>,
target_chain_id: [u8; 32],
receiver: Vec<u8>,
message_data: Vec<u8>,
) -> Result<()> {
// 创建包含消息的提交参数
let submission_params = debridge_sending::SendSubmissionParamsInput {
execution_fee: 0,
flags: [0u8; 32],
fallback_address: receiver.clone(),
external_call_shortcut: compute_keccak256(&message_data),
};
// 发送消息(金额设为0)
debridge_sending::invoke_send_message(
debridge_sending::SendIx {
target_chain_id,
receiver,
is_use_asset_fee: false,
amount: 0, // 无代币转账
submission_params: Some(submission_params),
referral_code: None,
},
ctx.remaining_accounts,
)?;
Ok(())
}External Calls
外部调用
Execute smart contract calls on destination chains:
在目标链上执行智能合约调用:
Initialize External Call Buffer
初始化外部调用缓冲区
rust
pub fn init_external_call(
ctx: Context<InitExternalCall>,
target_chain_id: [u8; 32],
external_call_data: Vec<u8>,
) -> Result<()> {
let shortcut = compute_keccak256(&external_call_data);
debridge_sending::invoke_init_external_call(
debridge_sending::InitExternalCallIx {
external_call_len: external_call_data.len() as u32,
chain_id: target_chain_id,
external_call_shortcut: shortcut,
external_call: external_call_data,
},
ctx.remaining_accounts,
)?;
Ok(())
}rust
pub fn init_external_call(
ctx: Context<InitExternalCall>,
target_chain_id: [u8; 32],
external_call_data: Vec<u8>,
) -> Result<()> {
let shortcut = compute_keccak256(&external_call_data);
debridge_sending::invoke_init_external_call(
debridge_sending::InitExternalCallIx {
external_call_len: external_call_data.len() as u32,
chain_id: target_chain_id,
external_call_shortcut: shortcut,
external_call: external_call_data,
},
ctx.remaining_accounts,
)?;
Ok(())
}Send with External Call
带外部调用的转账
rust
pub fn send_with_external_call(
ctx: Context<SendWithExternalCall>,
target_chain_id: [u8; 32],
receiver: Vec<u8>, // Target contract address
amount: u64,
external_call_data: Vec<u8>,
execution_fee: u64, // Fee for executor on destination
) -> Result<()> {
let shortcut = compute_keccak256(&external_call_data);
// Set flags for external call behavior
let mut flags = [0u8; 32];
flags.set_reserved_flag(flags::REVERT_IF_EXTERNAL_FAIL);
let submission_params = debridge_sending::SendSubmissionParamsInput {
execution_fee,
flags,
fallback_address: ctx.accounts.fallback.key().to_bytes().to_vec(),
external_call_shortcut: shortcut,
};
debridge_sending::invoke_debridge_send(
debridge_sending::SendIx {
target_chain_id,
receiver,
is_use_asset_fee: false,
amount,
submission_params: Some(submission_params),
referral_code: None,
},
ctx.remaining_accounts,
)?;
Ok(())
}rust
pub fn send_with_external_call(
ctx: Context<SendWithExternalCall>,
target_chain_id: [u8; 32],
receiver: Vec<u8>, // 目标合约地址
amount: u64,
external_call_data: Vec<u8>,
execution_fee: u64, // 目标链执行方的费用
) -> Result<()> {
let shortcut = compute_keccak256(&external_call_data);
// 设置外部调用行为的标志位
let mut flags = [0u8; 32];
flags.set_reserved_flag(flags::REVERT_IF_EXTERNAL_FAIL);
let submission_params = debridge_sending::SendSubmissionParamsInput {
execution_fee,
flags,
fallback_address: ctx.accounts.fallback.key().to_bytes().to_vec(),
external_call_shortcut: shortcut,
};
debridge_sending::invoke_debridge_send(
debridge_sending::SendIx {
target_chain_id,
receiver,
is_use_asset_fee: false,
amount,
submission_params: Some(submission_params),
referral_code: None,
},
ctx.remaining_accounts,
)?;
Ok(())
}Claim Verification
领取验证
Verify claims on the receiving side:
在接收方验证领取请求:
Validate Incoming Claims
验证 incoming 领取请求
rust
use debridge_solana_sdk::check_claiming::*;
pub fn receive_tokens(ctx: Context<ReceiveTokens>) -> Result<()> {
// Get and validate the parent claim instruction
let claim_ix = ValidatedExecuteExtCallIx::try_from_current_ix()?;
// Validate submission details
let validation = SubmissionAccountValidation {
receiver_validation: Some(ctx.accounts.receiver.key()),
token_mint_validation: Some(ctx.accounts.token_mint.key()),
source_chain_id_validation: Some(chain_ids::ETHEREUM_CHAIN_ID),
..Default::default()
};
claim_ix.validate_submission_account(
&ctx.accounts.submission_account,
&validation,
)?;
// Proceed with claim logic
Ok(())
}rust
use debridge_solana_sdk::check_claiming::*;
pub fn receive_tokens(ctx: Context<ReceiveTokens>) -> Result<()> {
// 获取并验证父级领取指令
let claim_ix = ValidatedExecuteExtCallIx::try_from_current_ix()?;
// 验证提交详情
let validation = SubmissionAccountValidation {
receiver_validation: Some(ctx.accounts.receiver.key()),
token_mint_validation: Some(ctx.accounts.token_mint.key()),
source_chain_id_validation: Some(chain_ids::ETHEREUM_CHAIN_ID),
..Default::default()
};
claim_ix.validate_submission_account(
&ctx.accounts.submission_account,
&validation,
)?;
// 继续执行领取逻辑
Ok(())
}Get Submission Key
获取提交密钥
rust
pub fn get_claim_info(ctx: Context<ClaimInfo>) -> Result<Pubkey> {
let claim_ix = ValidatedExecuteExtCallIx::try_from_current_ix()?;
let submission_key = claim_ix.get_submission_key()?;
Ok(submission_key)
}rust
pub fn get_claim_info(ctx: Context<ClaimInfo>) -> Result<Pubkey> {
let claim_ix = ValidatedExecuteExtCallIx::try_from_current_ix()?;
let submission_key = claim_ix.get_submission_key()?;
Ok(submission_key)
}Fee Queries
费用查询
Get Transfer Fees
获取转账费用
rust
// Get base transfer fee (in BPS)
let transfer_fee = debridge_sending::get_transfer_fee(
ctx.remaining_accounts,
)?;
// Get transfer fee for specific chain
let chain_fee = debridge_sending::get_transfer_fee_for_chain(
&target_chain_id,
ctx.remaining_accounts,
)?;
// Get default native fix fee
let default_fee = debridge_sending::get_default_native_fix_fee(
ctx.remaining_accounts,
)?;
// Get chain-specific native fix fee
let native_fee = debridge_sending::get_chain_native_fix_fee(
&target_chain_id,
ctx.remaining_accounts,
)?;
// Get asset fix fee for chain
let asset_fee = debridge_sending::try_get_chain_asset_fix_fee(
&target_chain_id,
ctx.remaining_accounts,
)?;rust
// 获取基础转账费用(基点)
let transfer_fee = debridge_sending::get_transfer_fee(
ctx.remaining_accounts,
)?;
// 获取特定链的转账费用
let chain_fee = debridge_sending::get_transfer_fee_for_chain(
&target_chain_id,
ctx.remaining_accounts,
)?;
// 获取默认原生固定费用
let default_fee = debridge_sending::get_default_native_fix_fee(
ctx.remaining_accounts,
)?;
// 获取链专属原生固定费用
let native_fee = debridge_sending::get_chain_native_fix_fee(
&target_chain_id,
ctx.remaining_accounts,
)?;
// 获取链的资产固定费用
let asset_fee = debridge_sending::try_get_chain_asset_fix_fee(
&target_chain_id,
ctx.remaining_accounts,
)?;Calculate Total Amount with Fees
计算包含费用的总金额
rust
// Add transfer fee to amount
let with_transfer_fee = debridge_sending::add_transfer_fee(
amount,
ctx.remaining_accounts,
)?;
// Add all fees (transfer + execution + asset fees)
let total_amount = debridge_sending::add_all_fees(
amount,
&target_chain_id,
ctx.remaining_accounts,
)?;rust
// 为金额添加转账费用
let with_transfer_fee = debridge_sending::add_transfer_fee(
amount,
ctx.remaining_accounts,
)?;
// 添加所有费用(转账费+执行费+资产费)
let total_amount = debridge_sending::add_all_fees(
amount,
&target_chain_id,
ctx.remaining_accounts,
)?;Chain Support Queries
链支持查询
rust
// Check if chain is supported
let is_supported = debridge_sending::is_chain_supported(
&target_chain_id,
ctx.remaining_accounts,
)?;
// Get chain support info
let chain_info = debridge_sending::get_chain_support_info(
&target_chain_id,
ctx.remaining_accounts,
)?;
// Check if asset fee is available
let asset_fee_available = debridge_sending::is_asset_fee_available(
&target_chain_id,
ctx.remaining_accounts,
)?;rust
// 检查链是否受支持
let is_supported = debridge_sending::is_chain_supported(
&target_chain_id,
ctx.remaining_accounts,
)?;
// 获取链支持信息
let chain_info = debridge_sending::get_chain_support_info(
&target_chain_id,
ctx.remaining_accounts,
)?;
// 检查是否支持资产费用
let asset_fee_available = debridge_sending::is_asset_fee_available(
&target_chain_id,
ctx.remaining_accounts,
)?;PDA Derivation
PDA推导
Bridge Account
桥接账户
rust
use debridge_solana_sdk::keys::*;
// Find bridge PDA for a token mint
let (bridge_address, bump) = BridgePubkey::find_bridge_address(&token_mint);
// Create with known bump
let bridge_address = BridgePubkey::create_bridge_address(&token_mint, bump)?;rust
use debridge_solana_sdk::keys::*;
// 为代币铸币地址推导桥接PDA
let (bridge_address, bump) = BridgePubkey::find_bridge_address(&token_mint);
// 使用已知bump创建桥接地址
let bridge_address = BridgePubkey::create_bridge_address(&token_mint, bump)?;Chain Support Info
链支持信息
rust
// Find chain support info PDA
let (chain_support_info, bump) = ChainSupportInfoPubkey::find_chain_support_info_address(
&target_chain_id,
);rust
// 推导链支持信息PDA
let (chain_support_info, bump) = ChainSupportInfoPubkey::find_chain_support_info_address(
&target_chain_id,
);Asset Fee Info
资产费用信息
rust
// Find asset fee info PDA
let (asset_fee_info, bump) = AssetFeeInfoPubkey::find_asset_fee_info_address(
&bridge_pubkey,
&target_chain_id,
);
// Get default bridge fee address
let default_fee = AssetFeeInfoPubkey::default_bridge_fee_address();rust
// 推导资产费用信息PDA
let (asset_fee_info, bump) = AssetFeeInfoPubkey::find_asset_fee_info_address(
&bridge_pubkey,
&target_chain_id,
);
// 获取默认桥接费用地址
let default_fee = AssetFeeInfoPubkey::default_bridge_fee_address();External Call Storage
外部调用存储
rust
// Find external call storage PDA
let (storage, bump) = ExternalCallStoragePubkey::find_external_call_storage_address(
&shortcut,
&owner,
);
// Find external call meta PDA
let (meta, bump) = ExternalCallMetaPubkey::find_external_call_meta_address(
&storage_account,
);rust
// 推导外部调用存储PDA
let (storage, bump) = ExternalCallStoragePubkey::find_external_call_storage_address(
&shortcut,
&owner,
);
// 推导外部调用元数据PDA
let (meta, bump) = ExternalCallMetaPubkey::find_external_call_meta_address(
&storage_account,
);Required Accounts
所需账户
The SDK requires specific accounts passed via . The account order is important:
remaining_accounts| Index | Account | Signer | Writable | Description |
|---|---|---|---|---|
| 0 | Bridge | No | Yes | Bridge account for token |
| 1 | Token Mint | No | No | SPL Token mint |
| 2 | Staking Wallet | No | Yes | Staking rewards wallet |
| 3 | Mint Authority | No | No | Token mint authority |
| 4 | Chain Support Info | No | No | Target chain config |
| 5 | Settings Program | No | No | deBridge settings |
| 6 | SPL Token Program | No | No | Token program |
| 7 | State | No | No | Protocol state |
| 8 | deBridge Program | No | No | Main deBridge program |
| ... | Additional accounts | - | - | Varies by operation |
SDK要求通过传入特定账户,账户顺序非常重要:
remaining_accounts| 索引 | 账户 | 签名方 | 可写 | 描述 |
|---|---|---|---|---|
| 0 | Bridge | 否 | 是 | 代币对应的桥接账户 |
| 1 | Token Mint | 否 | 否 | SPL代币铸币地址 |
| 2 | Staking Wallet | 否 | 是 | 质押奖励钱包 |
| 3 | Mint Authority | 否 | 否 | 代币铸币权限 |
| 4 | Chain Support Info | 否 | 否 | 目标链配置 |
| 5 | Settings Program | 否 | 否 | deBridge设置程序 |
| 6 | SPL Token Program | 否 | 否 | 代币程序 |
| 7 | State | 否 | 否 | 协议状态 |
| 8 | deBridge Program | 否 | 否 | 主deBridge程序 |
| ... | 其他账户 | - | - | 随操作类型变化 |
TypeScript Client Integration
TypeScript客户端集成
Setup
设置
typescript
import { Connection, Keypair, PublicKey, Transaction } from '@solana/web3.js';
import { Program, AnchorProvider, Wallet } from '@coral-xyz/anchor';
const connection = new Connection('https://api.mainnet-beta.solana.com');
const wallet = new Wallet(keypair);
const provider = new AnchorProvider(connection, wallet, {});
// deBridge Program IDs
const DEBRIDGE_PROGRAM_ID = new PublicKey('DEbrdGj3HsRsAzx6uH4MKyREKxVAfBydijLUF3ygsFfh');
const SETTINGS_PROGRAM_ID = new PublicKey('DeSetTwWhjZq6Pz9Kfdo1KoS5NqtsM6G8ERbX4SSCSft');typescript
import { Connection, Keypair, PublicKey, Transaction } from '@solana/web3.js';
import { Program, AnchorProvider, Wallet } from '@coral-xyz/anchor';
const connection = new Connection('https://api.mainnet-beta.solana.com');
const wallet = new Wallet(keypair);
const provider = new AnchorProvider(connection, wallet, {});
// deBridge Program IDs
const DEBRIDGE_PROGRAM_ID = new PublicKey('DEbrdGj3HsRsAzx6uH4MKyREKxVAfBydijLUF3ygsFfh');
const SETTINGS_PROGRAM_ID = new PublicKey('DeSetTwWhjZq6Pz9Kfdo1KoS5NqtsM6G8ERbX4SSCSft');Build Send Transaction
构建发送交易
typescript
import {
TOKEN_PROGRAM_ID,
getAssociatedTokenAddress
} from '@solana/spl-token';
async function buildSendTransaction(
tokenMint: PublicKey,
amount: bigint,
targetChainId: Uint8Array,
receiver: Uint8Array,
): Promise<Transaction> {
// Derive required PDAs
const [bridge] = PublicKey.findProgramAddressSync(
[Buffer.from('BRIDGE'), tokenMint.toBuffer()],
DEBRIDGE_PROGRAM_ID
);
const [chainSupportInfo] = PublicKey.findProgramAddressSync(
[Buffer.from('CHAIN_SUPPORT_INFO'), targetChainId],
SETTINGS_PROGRAM_ID
);
const [state] = PublicKey.findProgramAddressSync(
[Buffer.from('STATE')],
DEBRIDGE_PROGRAM_ID
);
// Build instruction with remaining accounts
const instruction = await program.methods
.sendViaDebridge(
Array.from(targetChainId),
Array.from(receiver),
new BN(amount.toString()),
)
.remainingAccounts([
{ pubkey: bridge, isSigner: false, isWritable: true },
{ pubkey: tokenMint, isSigner: false, isWritable: false },
// ... additional required accounts
])
.instruction();
return new Transaction().add(instruction);
}typescript
import {
TOKEN_PROGRAM_ID,
getAssociatedTokenAddress
} from '@solana/spl-token';
async function buildSendTransaction(
tokenMint: PublicKey,
amount: bigint,
targetChainId: Uint8Array,
receiver: Uint8Array,
): Promise<Transaction> {
// 推导所需PDA
const [bridge] = PublicKey.findProgramAddressSync(
[Buffer.from('BRIDGE'), tokenMint.toBuffer()],
DEBRIDGE_PROGRAM_ID
);
const [chainSupportInfo] = PublicKey.findProgramAddressSync(
[Buffer.from('CHAIN_SUPPORT_INFO'), targetChainId],
SETTINGS_PROGRAM_ID
);
const [state] = PublicKey.findProgramAddressSync(
[Buffer.from('STATE')],
DEBRIDGE_PROGRAM_ID
);
// 构建包含剩余账户的指令
const instruction = await program.methods
.sendViaDebridge(
Array.from(targetChainId),
Array.from(receiver),
new BN(amount.toString()),
)
.remainingAccounts([
{ pubkey: bridge, isSigner: false, isWritable: true },
{ pubkey: tokenMint, isSigner: false, isWritable: false },
// ... 其他所需账户
])
.instruction();
return new Transaction().add(instruction);
}Build External Call Data
构建外部调用数据
typescript
import { ethers } from 'ethers';
import { keccak256 } from '@ethersproject/keccak256';
function buildExternalCallData(
targetContract: string,
functionSig: string,
params: any[]
): { data: Uint8Array; shortcut: Uint8Array } {
const iface = new ethers.Interface([functionSig]);
const calldata = iface.encodeFunctionData(
functionSig.split('(')[0].replace('function ', ''),
params
);
const data = ethers.getBytes(calldata);
const shortcut = ethers.getBytes(keccak256(data));
return { data, shortcut };
}
// Example: ERC20 approve call
const { data, shortcut } = buildExternalCallData(
'0xTargetContract...',
'function approve(address spender, uint256 amount)',
['0xSpenderAddress...', ethers.parseEther('1000')]
);typescript
import { ethers } from 'ethers';
import { keccak256 } from '@ethersproject/keccak256';
function buildExternalCallData(
targetContract: string,
functionSig: string,
params: any[]
): { data: Uint8Array; shortcut: Uint8Array } {
const iface = new ethers.Interface([functionSig]);
const calldata = iface.encodeFunctionData(
functionSig.split('(')[0].replace('function ', ''),
params
);
const data = ethers.getBytes(calldata);
const shortcut = ethers.getBytes(keccak256(data));
return { data, shortcut };
}
// 示例:ERC20授权调用
const { data, shortcut } = buildExternalCallData(
'0xTargetContract...',
'function approve(address spender, uint256 amount)',
['0xSpenderAddress...', ethers.parseEther('1000')]
);Testing
测试
Anchor Test Setup
Anchor测试设置
toml
undefinedtoml
undefinedAnchor.toml
Anchor.toml
[provider]
cluster = "mainnet" # Use mainnet for testing with real deBridge
[programs.mainnet]
my_program = "YourProgramId..."
undefined[provider]
cluster = "mainnet" # 使用主网测试真实deBridge
[programs.mainnet]
my_program = "YourProgramId..."
undefinedRun Tests
运行测试
bash
undefinedbash
undefinedFull build and test
完整构建并测试
cd example_program && anchor build && anchor test
cd example_program && anchor build && anchor test
Test only (skip rebuild)
仅测试(跳过重新构建和部署)
anchor test --skip-build --skip-deploy
undefinedanchor test --skip-build --skip-deploy
undefinedLocal Testing Tips
本地测试技巧
- Use Mainnet Fork: deBridge infrastructure is on mainnet
- Mock Remaining Accounts: Create mock accounts for unit tests
- Test Fee Calculations: Verify fee amounts before sending
- 使用主网分叉:deBridge基础设施部署在主网
- 模拟剩余账户:为单元测试创建模拟账户
- 测试费用计算:发送前验证费用金额
Build Features
构建特性
The SDK supports different environments via Cargo features:
toml
undefinedSDK通过Cargo特性支持不同环境:
toml
undefinedProduction (default) - uses hardcoded program IDs
生产环境(默认)- 使用硬编码程序ID
debridge-solana-sdk = { git = "..." }
debridge-solana-sdk = { git = "..." }
Custom environment - uses env vars
自定义环境 - 使用环境变量
debridge-solana-sdk = { git = "...", features = ["env"] }
Environment variables for custom networks:
- `DEBRIDGE_PROGRAM_PUBKEY`: Custom deBridge program ID
- `DEBRIDGE_SETTINGS_PROGRAM_PUBKEY`: Custom settings program IDdebridge-solana-sdk = { git = "...", features = ["env"] }
自定义网络的环境变量:
- `DEBRIDGE_PROGRAM_PUBKEY`: 自定义deBridge程序ID
- `DEBRIDGE_SETTINGS_PROGRAM_PUBKEY`: 自定义设置程序IDResources
资源
Skill Structure
Skill结构
debridge/
├── SKILL.md # This file
├── resources/
│ ├── sdk-api-reference.md # Complete SDK API reference
│ ├── chain-ids.md # Supported chain identifiers
│ ├── program-ids.md # Program IDs and PDAs
│ └── error-codes.md # Error types and handling
├── examples/
│ ├── basic-transfer/ # Simple cross-chain transfer
│ ├── external-calls/ # External call execution
│ ├── message-passing/ # Message-only transfers
│ └── fee-configurations/ # Fee payment options
└── docs/
└── troubleshooting.md # Common issues and solutionsdebridge/
├── SKILL.md # 本文档
├── resources/
│ ├── sdk-api-reference.md # 完整SDK API参考
│ ├── chain-ids.md # 支持的链标识符
│ ├── program-ids.md # 程序ID和PDA
│ └── error-codes.md # 错误类型与处理
├── examples/
│ ├── basic-transfer/ # 简单跨链转账
│ ├── external-calls/ # 外部调用执行
│ ├── message-passing/ # 纯消息转账
│ └── fee-configurations/ # 费用支付选项
└── docs/
└── troubleshooting.md # 常见问题与解决方案