Skip to content
Merged
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
82 changes: 82 additions & 0 deletions .claude/commands/bridge-out.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
Initiate Bridge Out via the `bridge-out` binary (payInvoice quote -> swap initialize -> init-tag).

## Instructions

1. Ask the user for the following parameters (skip any already provided as arguments: $ARGUMENTS):
- **rpc_url**: Node API base URL. Default: `http://localhost:8080`
- **from_addr**: Source GOAT address (used by `init-tag`)
- **to_addr**: Destination BTC address (used by `init-tag`)
- **pay_invoice_url**: Quote endpoint. Default: `https://152-32-185-32.nodes.atomiq.exchange:8443/tobtc/payInvoice?chain=GOAT`
- **amount**: Amount in pegBTC (human-readable). Example: `0.0015`
- **exact_in**: Whether quote is exact-in (`true`/`false`). Default: `true`
- **confirmation_target**: Bitcoin confirmation target. Default: `3`
- **confirmations**: Required confirmations. Default: `2`
- **token**: Token address
- **offerer**: Offerer GOAT address
- **additional_params_json**: Optional JSON object merged into payInvoice body
- **contract_address**: Optional (fallback to `GOAT_SWAP_CONTRACT_ADDRESS`)
- **max_wait_secs**: Max wait for tx receipt. Default: `60`

2. Ensure required environment and runtime prerequisites are ready:
- `GOAT_PRIVATE_KEY` must be set (unless user passes `--goat-private-key`)
- `GOAT_CHAIN_URL` and chain config must be valid for on-chain calls
- `GOAT_SWAP_CONTRACT_ADDRESS` should be set if `--contract-address` is omitted
- Optional but recommended: use `node/.env` to manage the above variables consistently

3. Check if the `bridge-out` binary exists at `./bin/bridge-out`. If not, run the install script to download it:
```bash
.claude/commands/install-bitvm2.sh install
```
To upgrade to the latest version:
```bash
.claude/commands/install-bitvm2.sh upgrade
```
The script auto-detects the platform (x86_64-linux / aarch64-macos), downloads from GitHub Releases,
verifies the sha256 checksum, and installs all binaries to `./bin/`.

4. Run swap initialize (this step calls payInvoice first, then sends token approve if needed, then swap `initialize` tx):
```bash
./bin/bridge-out --rpc-url <rpc_url> swap-initialize \
--pay-invoice-url <pay_invoice_url> \
--btc-address <to_addr> \
--amount <amount> \
--exact-in <exact_in> \
--confirmation-target <confirmation_target> \
--confirmations <confirmations> \
--token <token> \
--offerer <offerer> \
--max-wait-secs <max_wait_secs>
```
- If needed, append:
```bash
--additional-params-json '<json_object>'
--contract-address <contract_address>
--goat-private-key <goat_private_key>
```

5. Parse output from step 4:
- `swap initialize submitted: <tx_hash>`
- `escrow_hash (from Initialize log): <escrow_hash>`
Save `<escrow_hash>` for the next step.

6. Submit bridge-out init-tag to node API:
```bash
./bin/bridge-out --rpc-url <rpc_url> init-tag \
--from-addr <from_addr> \
--to-addr <to_addr> \
--escrow-hash <escrow_hash> \
--contract-address <contract_address>
```
- Alternative: derive escrow hash from tx logs directly:
```bash
./bin/bridge-out --rpc-url <rpc_url> init-tag \
--from-addr <from_addr> \
--to-addr <to_addr> \
--swap-init-tx-hash <tx_hash> \
--contract-address <contract_address>
```

7. Optional verification (if instance id is known):
```bash
./bin/bridge-out --rpc-url <rpc_url> escrow-data --instance-id <instance_id>
```
41 changes: 40 additions & 1 deletion crates/client/src/goat_chain/chain_adaptor.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::btc_chain::MerkleProofExtend;
use crate::goat_chain::goat_adaptor::{GoatAdaptor, GoatInitConfig};
use crate::goat_chain::mock_goat_adaptor::MockAdaptor;
use alloy::primitives::{Address, U256};
use alloy::primitives::{Address, Bytes, U256};
use alloy::rpc::types::{
TransactionReceipt,
trace::geth::{GethDebugTracingOptions, GethTrace},
Expand All @@ -22,6 +22,22 @@ pub trait ChainAdaptor: Send + Sync {
tx_hash: &str,
trace_options: Option<GethDebugTracingOptions>,
) -> anyhow::Result<GethTrace>;
#[allow(clippy::too_many_arguments)]
async fn swap_initialize(
&self,
contract_address: Address,
escrow: SwapEscrowData,
signature: Bytes,
timeout: U256,
extra_data: Bytes,
value_wei: U256,
max_wait_secs: u64,
) -> anyhow::Result<SwapInitializeResult>;
async fn extract_initialize_escrow_hash_from_tx(
&self,
tx_hash: &str,
contract_address: Address,
) -> anyhow::Result<Option<String>>;

async fn gateway_get_min_challenge_amount_sats(&self) -> anyhow::Result<u64>;
async fn gateway_get_min_pegin_fee_sats(&self) -> anyhow::Result<u64>;
Expand Down Expand Up @@ -218,6 +234,29 @@ pub trait ChainAdaptor: Send + Sync {

async fn peg_btc_balance(&self, address: &[u8; 20]) -> anyhow::Result<U256>;
}

#[derive(Clone, Debug)]
pub struct SwapEscrowData {
pub offerer: Address,
pub claimer: Address,
pub amount: U256,
pub token: Address,
pub flags: U256,
pub claim_handler: Address,
pub claim_data: [u8; 32],
pub refund_handler: Address,
pub refund_data: [u8; 32],
pub security_deposit: U256,
pub claimer_bounty: U256,
pub deposit_token: Address,
pub success_action_commitment: [u8; 32],
}

#[derive(Clone, Debug)]
pub struct SwapInitializeResult {
pub tx_hash: String,
pub escrow_hash: String,
}
#[derive(Eq, PartialEq, Clone, Copy)]
pub enum GoatNetwork {
Main,
Expand Down
36 changes: 34 additions & 2 deletions crates/client/src/goat_chain/evmchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use crate::Utxo;
use crate::goat_chain::DisproveTxType;
use crate::goat_chain::chain_adaptor::{
BitcoinTx, BitcoinTxProof, ChainAdaptor, GraphData, PeginData, SequencerSetUpdateWitness,
WithdrawData,
SwapEscrowData, SwapInitializeResult, WithdrawData,
};
use crate::goat_chain::mock_goat_adaptor::MockAdaptor;
use alloy::primitives::{Address, U256};
use alloy::primitives::{Address, Bytes, U256};
use alloy::rpc::types::{
TransactionReceipt,
trace::geth::{GethDebugTracingOptions, GethTrace},
Expand Down Expand Up @@ -262,6 +262,38 @@ impl EvmChain {
self.adaptor.debug_trace_tx(tx_hash, trace_options).await
}

#[allow(clippy::too_many_arguments)]
pub async fn swap_initialize(
&self,
contract_address: Address,
escrow: SwapEscrowData,
signature: Bytes,
timeout: U256,
extra_data: Bytes,
value_wei: U256,
max_wait_secs: u64,
) -> anyhow::Result<SwapInitializeResult> {
self.adaptor
.swap_initialize(
contract_address,
escrow,
signature,
timeout,
extra_data,
value_wei,
max_wait_secs,
)
.await
}

pub async fn extract_initialize_escrow_hash_from_tx(
&self,
tx_hash: &str,
contract_address: Address,
) -> anyhow::Result<Option<String>> {
self.adaptor.extract_initialize_escrow_hash_from_tx(tx_hash, contract_address).await
}

pub async fn gateway_get_committee_pubkeys(
&self,
instance_id: &Uuid,
Expand Down
Loading
Loading