Skip to content

Add Bitcoin support to Relay swapper#998

Open
0xh3rman wants to merge 3 commits intomainfrom
relay-bitcoin-swap-limit
Open

Add Bitcoin support to Relay swapper#998
0xh3rman wants to merge 3 commits intomainfrom
relay-bitcoin-swap-limit

Conversation

@0xh3rman
Copy link
Collaborator

  • Add Bitcoin chain support to Relay swapper with PSBT-based swap data
  • Add BitcoinStepData variant to StepData enum for PSBT transactions
  • Map Relay gas fees as Bitcoin transaction fee (PR Use swap contract gas_limit for bitcoin transaction fee #979)
  • Rename SwapQuoteData.gas_limit to limit with serde alias for backward-compatible deserialization from gasLimit JSON field
  • Fix near intents test

- Add Bitcoin chain support to Relay swapper with PSBT-based swap data
- Add BitcoinStepData variant to StepData enum for PSBT transactions
- Map Relay gas fees as Bitcoin transaction fee (PR #979)
- Rename SwapQuoteData.gas_limit to limit with serde alias for
  backward-compatible deserialization from gasLimit JSON field
Extract swap_provider_fee helper and simplify Bitcoin preload fee logic; parse relay swap fee limits as TransactionFee. Update relay asset mapping to use RelayChain (and remove is_evm), handle EVM zero address and token checksum more robustly, and adjust asset_to_currency accordingly. Rename gas_limit→fee_limit in mapper and stop converting RelayChain to Chain when resolving metadata. Also update near_intents test: increase test value, broaden error handling to include InputAmountError, and assert presence of a memo.
@semanticdiff-com
Copy link

semanticdiff-com bot commented Mar 10, 2026

@0xh3rman 0xh3rman requested a review from gemcoder21 March 10, 2026 07:55
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly expands the capabilities of the Relay swapper by integrating full Bitcoin chain support. It introduces the necessary data structures and logic to handle Bitcoin transactions, including PSBT-based swap data and proper mapping of Relay's gas fees to Bitcoin transaction fees. Additionally, a key refactoring was performed by renaming a core field in SwapQuoteData to improve clarity while ensuring compatibility with existing systems. These changes collectively enable seamless Bitcoin swaps through the Relay provider and enhance the overall robustness of the swapper module.

Highlights

  • Bitcoin Support for Relay Swapper: Added comprehensive support for Bitcoin transactions within the Relay swapper, utilizing PSBT (Partially Signed Bitcoin Transaction) for swap data.
  • Fee Handling for Bitcoin Swaps: Implemented logic to correctly map Relay gas fees to Bitcoin transaction fees, ensuring accurate cost calculation for Bitcoin-based swaps.
  • Refactoring SwapQuoteData Field: Renamed the gas_limit field in SwapQuoteData to limit across the codebase, with a serde alias (gasLimit) to maintain backward compatibility during deserialization.
  • Enhanced StepData Enum: Introduced a new BitcoinStepData variant to the StepData enum, specifically designed to encapsulate PSBT transaction details for Bitcoin swaps.
  • Near Intents Test Fix: Resolved an issue in the Near intents test by adjusting the input value and refining the assertion for deposit memos, improving test reliability.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • crates/gem_aptos/src/signer/chain_signer.rs
    • Updated resolve_max_gas_amount to use the renamed limit field instead of gas_limit.
  • crates/gem_bitcoin/src/provider/preload.rs
    • Added imports for SwapProvider, TransactionFee, and SwapQuoteDataType.
    • Modified get_transaction_load to apply swap provider fees for Relay Bitcoin transactions.
    • Introduced swap_provider_fee function to extract and parse Relay swap fees for Bitcoin.
  • crates/gem_evm/src/provider/preload_mapper.rs
    • Updated get_extra_fee_gas_limit to reference the limit field instead of gas_limit.
  • crates/gem_solana/src/provider/preload_mapper.rs
    • Updated get_gas_limit to use the limit field.
    • Modified mock_swap_data_with_gas_limit to set the limit field.
  • crates/gem_solana/src/signer/chain_signer.rs
    • Updated gas_limit_as_u32 to limit_as_u32 for consistency.
  • crates/primitives/src/swap/approval.rs
    • Renamed gas_limit field to limit and added a serde(alias = "gasLimit") attribute for backward compatibility.
    • Updated gas_limit_as_u32 function to limit_as_u32 and adjusted its implementation to use the limit field.
    • Modified new_contract constructor to accept limit instead of gas_limit.
    • Updated new_stellar_deposit to initialize the limit field as None.
  • crates/primitives/src/testkit/swap_mock.rs
    • Updated SwapQuoteData::mock to initialize limit instead of gas_limit.
    • Renamed mock_with_gas_limit to mock_with_limit and adjusted its usage.
  • crates/swapper/src/near_intents/provider.rs
    • Adjusted the value in test_near_intents_stellar_swap to 12000000.
    • Expanded error handling in test_near_intents_stellar_swap to include SwapperError::InputAmountError.
    • Changed assertion in test_near_intents_stellar_swap to check quote_data.memo.is_some() instead of !quote_data.data.is_empty().
  • crates/swapper/src/proxy/provider.rs
    • Updated check_approval_and_limit to use the limit field instead of gas_limit.
    • Modified test functions (test_solana_preserves_provider_gas_limit, test_evm_native_ignores_provider_gas_limit) to use mock_with_limit.
  • crates/swapper/src/relay/asset.rs
    • Removed Solana-specific imports and is_native_currency function.
    • Updated map_currency_to_asset_id to handle RelayChain variants (Bitcoin, Evm).
    • Added Bitcoin to SUPPORTED_CHAINS.
    • Modified asset_to_currency to accept RelayChain and handle Bitcoin and Evm chains.
    • Updated test cases for asset_to_currency to reflect RelayChain usage and added Bitcoin asset test.
  • crates/swapper/src/relay/chain.rs
    • Defined BITCOIN_CHAIN_ID and BITCOIN_CURRENCY constants.
    • Added Bitcoin variant to the RelayChain enum.
    • Implemented chain_id, from_chain, to_chain, and from_chain_id methods for RelayChain to support Bitcoin.
    • Updated test cases for from_chain and added test_from_chain_id to cover Bitcoin.
  • crates/swapper/src/relay/mapper.rs
    • Imported gas_fee_amount from model.
    • Modified map_quote_data to accept from_value and handle BitcoinStepData by extracting PSBT and fee limit.
    • Adjusted map_swap_result to use RelayChain directly for from_chain and to_chain.
    • Added new test cases (test_map_bitcoin_quote_data, test_map_bitcoin_quote_data_with_gas_fee, test_map_swap_result_evm_to_btc) for Bitcoin functionality.
    • Updated existing test calls to map_quote_data to pass from_value.
  • crates/swapper/src/relay/model.rs
    • Updated Step::to_address to return Option<String>.
    • Added Bitcoin variant to StepData enum.
    • Modified StepData::to_address to return Option<String> and handle BitcoinStepData by returning None.
    • Introduced BitcoinStepData struct with a psbt field.
    • Added gas_fee_amount helper function to extract gas fee from RelayFees.
  • crates/swapper/src/relay/provider.rs
    • Updated calls to asset_to_currency to pass the RelayChain.
    • Modified get_quote_data to pass quote.from_value to mapper::map_quote_data.
    • Added test_relay_btc_to_eth integration test for Bitcoin to Ethereum swaps.
  • crates/swapper/src/relay/testkit.rs
    • Imported BitcoinStepData.
    • Added mock_bitcoin function to Step for creating Bitcoin step data.
  • crates/swapper/src/swapper.rs
    • Updated get_quote_data to use the limit field instead of gas_limit.
  • crates/swapper/src/thorchain/quote_data_mapper.rs
    • Updated test assertions to check for the limit field instead of gas_limit.
  • gemstone/src/models/swap.rs
    • Renamed gas_limit field to limit in GemSwapQuoteData.
Activity
  • The pull request author, 0xh3rman, initiated this change to introduce Bitcoin support to the Relay swapper.
  • The changes involve significant refactoring of asset and chain handling within the swapper module to accommodate Bitcoin's unique transaction model (PSBT).
  • Updates were made across multiple crates (gem_aptos, gem_bitcoin, gem_evm, gem_solana, primitives, swapper, gemstone) to ensure consistent usage of the renamed limit field and to integrate Bitcoin-specific logic.
  • New test cases were added, particularly for Bitcoin-related functionalities and the Near intents fix, to validate the new features and ensure stability.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Introduce a BitcoinChainSigner implementation that signs PSBTs (supports Relay PSBT flow; Thorchain/Chainflip not implemented). Wire the signer into gem_bitcoin (new chain_signer.rs), export it from the signer module, and register it in gemstone's GemChainSigner constructor. Add bitcoin as an optional workspace dependency and enable necessary features in the top-level Cargo.toml. Also derive PartialEq for SwapQuoteDataType to allow comparisons.
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces Bitcoin support for the Relay swapper, a significant feature addition. The changes span multiple crates, including adding a BitcoinStepData variant for PSBT-based swaps and mapping Relay gas fees to Bitcoin transaction fees. A notable part of this PR is the refactoring of gas_limit to limit across the codebase for consistency, with backward compatibility handled via serde aliases. The implementation is solid, but I have identified a couple of areas for improvement related to code consistency and idiomatic Rust usage, for which I've provided suggestions.

Comment on lines +70 to +81
fn swap_provider_fee(input: &TransactionLoadInput) -> Option<Result<TransactionFee, &'static str>> {
let swap_data = input.input_type.get_swap_data().ok()?;
if swap_data.data.data_type != SwapQuoteDataType::Contract {
return None;
}
let provider = swap_data.quote.provider_data.provider;
if !matches!(provider, SwapProvider::Relay) {
return None;
}
let limit = swap_data.data.limit.as_deref()?;
Some(limit.parse::<BigInt>().map(TransactionFee::new_from_fee).map_err(|_| "invalid swap fee"))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The function swap_provider_fee can be refactored for better readability and to be more idiomatic. Using if let to destructure input.input_type directly avoids the get_swap_data() call and contains the logic for Relay swaps within a single block, improving maintainability.

fn swap_provider_fee(input: &TransactionLoadInput) -> Option<Result<TransactionFee, &'static str>> {
    if let TransactionInputType::Swap(_, _, swap_data) = &input.input_type {
        if swap_data.data.data_type == SwapQuoteDataType::Contract
            && matches!(swap_data.quote.provider_data.provider, SwapProvider::Relay)
        {
            return swap_data.data.limit.as_deref().map(|limit| {
                limit
                    .parse::<BigInt>()
                    .map(TransactionFee::new_from_fee)
                    .map_err(|_| "invalid swap fee")
            });
        }
    }
    None
}

Comment on lines 123 to 127
fn mock_swap_data_with_gas_limit(provider: SwapProvider, gas_limit: Option<&str>) -> SwapData {
let mut data = SwapData::mock_with_provider(provider);
data.data.gas_limit = gas_limit.map(|s| s.to_string());
data.data.limit = gas_limit.map(|s| s.to_string());
data
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with the global rename of gas_limit to limit, this test helper function and its parameter should also be renamed. mock_swap_data_with_gas_limit should be mock_swap_data_with_limit, and the parameter gas_limit should be limit.

Suggested change
fn mock_swap_data_with_gas_limit(provider: SwapProvider, gas_limit: Option<&str>) -> SwapData {
let mut data = SwapData::mock_with_provider(provider);
data.data.gas_limit = gas_limit.map(|s| s.to_string());
data.data.limit = gas_limit.map(|s| s.to_string());
data
}
fn mock_swap_data_with_limit(provider: SwapProvider, limit: Option<&str>) -> SwapData {
let mut data = SwapData::mock_with_provider(provider);
data.data.limit = limit.map(|s| s.to_string());
data
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant