# Earnings Source: https://docs.garden.finance/api-reference/endpoint/affiliate/get-earnings GET /apps/earnings Get affiliate earnings and claimable amounts for different assets. # Supported Assets Source: https://docs.garden.finance/api-reference/endpoint/blockchain/get-assets GET /assets Retrieve a list of all assets supported across different blockchain networks, including symbols, addresses, and decimals. # Supported Chains Source: https://docs.garden.finance/api-reference/endpoint/blockchain/get-chains GET /chains Retrieve a list of all supported blockchain networks. # Available Liquidity Source: https://docs.garden.finance/api-reference/endpoint/blockchain/get-liquidity GET /liquidity Retrieve total available liquidity in the system. # Route Policy Source: https://docs.garden.finance/api-reference/endpoint/blockchain/get-policy GET /policy Retrieve route policies to calculate [supported routes](/developers/supported-routes) locally. # Schema Source: https://docs.garden.finance/api-reference/endpoint/blockchain/get-schema GET /schemas/{name} Retrieve the schema with the given name. # Supported Schemas Source: https://docs.garden.finance/api-reference/endpoint/blockchain/get-schemas GET /schemas Retrieve a list of all schemas across supported networks: ABI for EVM and Starknet chains, IDL for Solana-based chains. # Total Fees Source: https://docs.garden.finance/api-reference/endpoint/get-fees GET /fees Retrieve total fees processed by Garden in USD, or affiliate fees if a `garden-app-id` header is provided. # Server Health Source: https://docs.garden.finance/api-reference/endpoint/get-health GET /health Retrieve the health status of the API server. # Swap Quote Source: https://docs.garden.finance/api-reference/endpoint/get-quote GET /quote Get quote for the given asset pair. Get a quote for swapping assets across different blockchain networks. # Total Volume Source: https://docs.garden.finance/api-reference/endpoint/get-volume GET /volume Retrieve total volume processed by Garden in USD. # Create Order Source: https://docs.garden.finance/api-reference/endpoint/orders/create-order POST /orders Create a new swap order to exchange assets across blockchain networks. This starts the atomic swap process and returns a pre-built transaction to initiate a swap on-chain. # Instant Refund Hash Source: https://docs.garden.finance/api-reference/endpoint/orders/get-instant-refund-hash GET /orders/{order}/instant-refund-hash Retrieve the instant refund hash for a specific swap order. This hash can be used to immediately refund an order before the atomic swap's timelock expires. # Order Source: https://docs.garden.finance/api-reference/endpoint/orders/get-order-by-id GET /orders/{order} Retrieve detailed information about a specific order by its ID. # Orders Source: https://docs.garden.finance/api-reference/endpoint/orders/get-orders GET /orders Retrieve a list of orders with optional filtering and pagination. # Gasless Source: https://docs.garden.finance/api-reference/endpoint/orders/patch PATCH /orders/{order} Execute gasless Hashed Time Lock Contract (HTLC) actions on an existing swap order, such as initiate, redeem, refund, and instant refund. ## Initiate Use `action=initiate` to initiate the swap on the source chain. The [create order endpoint](./create-order) returns chain-specific signing data — sign it and submit here. The relayer broadcasts the transaction on the user's behalf, making the process gasless. The [create order](./create-order) response includes `typed_data` with EIP-712 domain and message fields. Sign this using `eth_signTypedData_v4`. Supported on all EVM chains (Ethereum, Arbitrum, Base, etc.) and Tron. An ECDSA signature generated by the initiator over an EIP-712 typed data message containing the redeemer address, timelock, amount, and secret hash from the [create order endpoint](./create-order). ```json Example theme={null} { "signature": "0xEIP712SignatureHex..." } ``` The [create order](./create-order) response includes `versioned_tx_gasless` — a base64-encoded versioned transaction. Deserialize it, sign it with the user's wallet, then serialize the signed transaction back to base64. The unique identifier for the order. Base64-encoded signed Solana versioned transaction. ```json Example theme={null} { "order_id": "", "serialized_tx": "base64EncodedSignedTransaction..." } ``` The [create order](./create-order) response includes `typed_data` with Starknet domain and message fields. Sign this typed data and submit the signature felt values as a comma-separated string. A Starknet signature over the typed data message containing the HTLC initiation parameters. Felt values should be comma-separated. ```json Example theme={null} { "signature": "0xfelt1,0xfelt2" } ``` Want to enable gasless initiations for your users? Reach out to us on [Townhall](https://discord.gg/B7RczEFuJ5) and we'll help you get set up. # Quickstart Source: https://docs.garden.finance/api-reference/quickstart Use Garden's API to get quotes, submit orders, and track their status. This guide provides a quick overview of the core API endpoints. This guide uses a testing app ID. Create your own API key at [portal.garden.finance](https://portal.garden.finance). Let's get a quote to trade 0.0005 BTC on **Bitcoin Testnet4** to WBTC on **Base Sepolia**. ```bash wrap theme={null} curl -X 'GET' 'https://testnet.api.garden.finance/v2/quote?from=bitcoin_testnet:btc&to=base_sepolia:wbtc&from_amount=50000' \ -H 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ -H 'accept: application/json' ``` ```json Response expandable theme={null} { "status": "Ok", "result": [ { "source": { "asset": "bitcoin_testnet:btc", "amount": "50000", "display": "0.00050000", "value": "58.8200" }, "destination": { "asset": "base_sepolia:wbtc", "amount": "49850", "display": "0.00049850", "value": "58.6435" }, "solver_id": "0x9dd9c2d208b07bf9a4ef9ca311f36d7185749635", "estimated_time": 600, "slippage": 50, "fee": 30, "fixed_fee": "0.0" } ] } ``` Once a quote is received, we can submit the order: Use `amount` and `asset` from the quote response. Use your wallet addresses for `owner`. ```bash theme={null} curl --location 'https://testnet.api.garden.finance/v2/orders' \ --header 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ --header 'Content-Type: application/json' \ --data '{ "source": { "asset": "bitcoin_testnet:btc", "owner": "tb1p4pr78swsn60y4ushe05v28mqpqppxxkfkxu2wun5jw6duc8unj3sjrh4gd", "amount": "50000" }, "destination": { "asset": "base_sepolia:wbtc", "owner": "0x004Cc75ACF4132Fc08cB6a252E767804F303F729", "amount": "49850" } }' ``` ```json Response expandable wrap theme={null} { "status": "Ok", "result": { "order_id": "f8a12d1320fce93c5888b6014abeb5f5de85ecc8c0eef8133f3da03822592121", "to": "tb1ptt49v22dcst7mquwfsmcu2t56xjg07whtcgufvhjuj5zu89y6q0qn8fvfp", "amount": "50000" } } ``` Send 0.0005 BTC to the `to` address in the response. You may use our [faucet](https://testnetbtc.com) on testnet. Check the order status using the `order_id` from the response. ```bash wrap theme={null} curl -X 'GET' \ 'https://testnet.api.garden.finance/v2/orders/f8a12d1320fce93c5888b6014abeb5f5de85ecc8c0eef8133f3da03822592121' \ -H 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ -H 'accept: application/json' ``` ```json Response expandable focus={46} theme={null} { "status": "Ok", "result": { "created_at": "2025-07-09T04:38:18.632122Z", "source_swap": { "created_at": "2025-07-09T04:38:18.632122Z", "swap_id": "tb1ptt49v22dcst7mquwfsmcu2t56xjg07whtcgufvhjuj5zu89y6q0qn8fvfp", "chain": "bitcoin_testnet", "asset": "bitcoin_testnet:btc", "initiator": "tb1p4pr78swsn60y4ushe05v28mqpqppxxkfkxu2wun5jw6duc8unj3sjrh4gd", "redeemer": "460f2e8ff81fc4e0a8e6ce7796704e3829e3e3eedb8db9390bdc51f4f04cf0a6", "delegate": "8f716d079bd4a6cb5c47a24cd22c352ffb50089e4461043cfe3b1f0f9082eec8", "timelock": 144, "filled_amount": "500000", "asset_price": 108468.0, "amount": "500000", "secret_hash": "3c4522983261f81e2e679346bbda5dddd8b4ea0367dd4073f73f13320d9dee62", "secret": "8cb8a02d5592b0b1556978e1d778b7eff2c23519b8aa65970fa6ac3daa33fb40", "instant_refund_tx": "020000000001013a18563d08a2b7d596488a96065466f52a82c221b981492b1f35b999401de1660000000000ffffffff0120a1070000000000225120a847e3c1d09e9e4af217cbe8c51f600802131ac9b1b8a7727493b4de60fc9ca3044107583b76a92f651f9bb3a34189cd1b81b7f98f99d1ebc71ba313e72c441a96270707d0fda5cacb0b1de2daacae318fa8c44b35881dc0ffd92764c8e1ce0a7bef834107583b76a92f651f9bb3a34189cd1b81b7f98f99d1ebc71ba313e72c441a96270707d0fda5cacb0b1de2daacae318fa8c44b35881dc0ffd92764c8e1ce0a7bef8346208f716d079bd4a6cb5c47a24cd22c352ffb50089e4461043cfe3b1f0f9082eec8ac20460f2e8ff81fc4e0a8e6ce7796704e3829e3e3eedb8db9390bdc51f4f04cf0a6ba529c61c02160e11a135f94e536a5b222e5d09fd9db1be5f5f5e753920290c0410cf388f09023174326647e3e2f5e7b7023a678341cd85556bfe7f539cdd369fd2ab1729114a7bc658045926f1b2c0e0e70292c66bda90ddbbee9e2ba771a4e0a57054bbc00000000", "initiate_tx_hash": "66e11d4099b9351f2b4981b921c2822af5665406968a4896d5b7a2083d56183a:90249", "redeem_tx_hash": "7820a77ecd05f4182a0a5595e8364e221bdb1a440507d53bf20c5f658ae3cd67", "refund_tx_hash": "", "initiate_block_number": "90249", "redeem_block_number": "90259", "refund_block_number": "0", "required_confirmations": 1, "current_confirmations": 1, "initiate_timestamp": null, "redeem_timestamp": null, "refund_timestamp": null }, "destination_swap": { "created_at": "2025-07-09T04:38:18.632122Z", "swap_id": "3ab702b4db8f9b54f56d4ceeab7811ee02447785605a2c5b8df7dcc071efed91", "chain": "base_sepolia", "asset": "base_sepolia:wbtc", "initiator": "0x661bA32eb5f86CaB358DDbB7F264b10c5825e2dd", "redeemer": "0x004Cc75ACF4132Fc08cB6a252E767804F303F729", "timelock": 3600, "filled_amount": "498500", "asset_price": 108468.0, "amount": "498500", "secret_hash": "3c4522983261f81e2e679346bbda5dddd8b4ea0367dd4073f73f13320d9dee62", "secret": "8cb8a02d5592b0b1556978e1d778b7eff2c23519b8aa65970fa6ac3daa33fb40", "initiate_tx_hash": "0xfb354436bbd8bd57760d82bfb441442e796e60aa7f8cd9e0978aa94a73eab2b8", "redeem_tx_hash": "0x7b3d81b0caddadbb35a9cf717b5f23b2ceddfa7a1a5251b2dbee723247cffdd6", "refund_tx_hash": "", "initiate_block_number": "28133930", "redeem_block_number": "28133933", "refund_block_number": "0", "required_confirmations": 0, "current_confirmations": 0, "initiate_timestamp": "2025-07-09T04:42:28Z", "redeem_timestamp": "2025-07-09T04:42:34Z", "refund_timestamp": null }, "nonce": "1751978328975", "order_id": "f8a12d1320fce93c5888b6014abeb5f5de85ecc8c0eef8133f3da03822592121", "affiliate_fees": [], "version": "v2", "solver_id": "0x90eb912279ee8a3c56f784a44dedffae4487524d", "integrator": "DocsTesting" } } ``` The swap is complete once the `order.destination_swap.redeem_tx_hash` field is populated. Let's get a quote to trade 0.01 LTC on **Litecoin Testnet** to WBTC on **Base Sepolia**. ```bash wrap theme={null} curl -X 'GET' 'https://testnet.api.garden.finance/v2/quote?from=litecoin_testnet:ltc&to=base_sepolia:wbtc&from_amount=1000000' \ -H 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ -H 'accept: application/json' ``` ```json Response expandable theme={null} { "status": "Ok", "result": [ { "source": { "asset": "litecoin_testnet:ltc", "amount": "1000000", "display": "0.01000000", "value": "0.7239" }, "destination": { "asset": "base_sepolia:wbtc", "amount": "755", "display": "0.00000755", "value": "0.7218" }, "solver_id": "0x90eb912279ee8a3c56f784a44dedffae4487524d", "estimated_time": 20, "slippage": 0, "fee": 30, "fixed_fee": "0" } ] } ``` Once a quote is received, we can submit the order: Use `amount` and `asset` from the quote response. Use your wallet addresses for `owner`. ```bash theme={null} curl --location 'https://testnet.api.garden.finance/v2/orders' \ --header 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ --header 'Content-Type: application/json' \ --data '{ "source": { "asset": "litecoin_testnet:ltc", "owner": "tltc1qycexnc7fjqh2x4dnaht6gumcjxdzkdpjnlxe4s", "amount": "1000000" }, "destination": { "asset": "base_sepolia:wbtc", "owner": "0x004Cc75ACF4132Fc08cB6a252E767804F303F729", "amount": "755" } }' ``` ```json Response expandable wrap theme={null} { "status": "Ok", "result": { "order_id": "353e76a02a46c493a600f0a35e7e873dab370633f16f1d27af2570fc12ada3e8", "to": "tltc1p9am469q2d6mfpkgrlgpd4c7tzv2lv5rug2yn9vz6vx0myj9jr5kqrc76rt", "amount": "1000000" } } ``` Send 0.01 LTC to the `to` address in the response. You may use this [faucet](https://cypherfaucet.com/ltc-testnet) on testnet. Check the order status using the `order_id` from the response. ```bash wrap theme={null} curl -X 'GET' \ 'https://testnet.api.garden.finance/v2/orders/353e76a02a46c493a600f0a35e7e873dab370633f16f1d27af2570fc12ada3e8' \ -H 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ -H 'accept: application/json' ``` ```json Response expandable focus={44} theme={null} { "status": "Ok", "result": { "created_at": "2026-01-08T06:03:46.125199Z", "source_swap": { "created_at": "2026-01-08T06:03:46.125199Z", "swap_id": "tltc1pqujpwdy2syf0agyyeg8xn0nwhy8qjxe9sqchmv563q7up9cgzc5q5cdl6s", "chain": "litecoin_testnet", "asset": "litecoin_testnet:ltc", "initiator": "c3989e1d1359c83db04785c14ee84f05210acc3bb4d270619559632869c15c3b", "redeemer": "2623c14333640ab262b4dda40382e64da8c20e78e98d597e218568fb5fe3e283", "timelock": 144, "filled_amount": "1000000", "asset_price": 81.64041962329875, "amount": "1000000", "secret_hash": "81e8dd7fd72d336f2ac3fbdece4d046054d0d114c511bf831cf4df61097c9aa5", "secret": "197b2e1e1105c9bec013e9a081dd13378855bd7a3b65b3fcd396a0d965bc506f", "initiate_tx_hash": "6dc881199ce0fac5287cf55761dceec7347bbb328fe6fc7f13207afe06f5f3f6:4512211", "redeem_tx_hash": "c6d176a2be224686e43cc837bd70f05b64711b314aa91015c42b8a31a7f35ce1", "refund_tx_hash": "", "initiate_block_number": "4512211", "redeem_block_number": "4512212", "refund_block_number": "0", "required_confirmations": 1, "current_confirmations": 1, "initiate_timestamp": "2026-01-08T06:08:10Z", "redeem_timestamp": "2026-01-08T06:12:39Z", "refund_timestamp": null }, "destination_swap": { "created_at": "2026-01-08T06:03:46.125199Z", "swap_id": "8334288fa1924abe3c89bbc709e95f8caae605534a6cd268315f943d6bd72c19", "chain": "base_sepolia", "asset": "base_sepolia:wbtc", "initiator": "0x133C4FCaBf1e79AE04100da0cDEC281a82ca226C", "redeemer": "0xf519fa84c50E3459a570a1a514d2eaA960AFf934", "timelock": 3600, "filled_amount": "896", "asset_price": 90802.0, "amount": "896", "secret_hash": "81e8dd7fd72d336f2ac3fbdece4d046054d0d114c511bf831cf4df61097c9aa5", "secret": "197b2e1e1105c9bec013e9a081dd13378855bd7a3b65b3fcd396a0d965bc506f", "initiate_tx_hash": "0xa28991ceecde416fd5acec5863ce0688d6e2500af2f94156ed4baf22a3b04f36", "redeem_tx_hash": "0x2c4c740be46277f4ac9b12b438f9278fd49ca403c742a0860ad0cd10ab04d381", "refund_tx_hash": "", "initiate_block_number": "36042112", "redeem_block_number": "36042116", "refund_block_number": "0", "required_confirmations": 0, "current_confirmations": 0, "initiate_timestamp": "2026-01-08T06:08:32Z", "redeem_timestamp": "2026-01-08T06:08:40Z", "refund_timestamp": null }, "nonce": "1767852225934", "order_id": "353e76a02a46c493a600f0a35e7e873dab370633f16f1d27af2570fc12ada3e8", "affiliate_fees": [], "integrator": "DocsTesting", "version": "v3", "solver_id": "0x90eb912279ee8a3c56f784a44dedffae4487524d" } } ``` The swap is complete once the `order.destination_swap.redeem_tx_hash` field is populated. Let's get a quote to trade 0.0005 WBTC on **Ethereum Sepolia** to BTC on **Bitcoin Testnet4**. ```bash wrap theme={null} curl -X 'GET' 'https://testnet.api.garden.finance/v2/quote?from=ethereum_sepolia:wbtc&to=bitcoin_testnet:btc&from_amount=50000' \ -H 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ -H 'accept: application/json' ``` ```json Response expandable theme={null} { "status": "Ok", "result": [ { "source": { "asset": "ethereum_sepolia:wbtc", "amount": "50000", "display": "0.00050000", "value": "58.8700" }, "destination": { "asset": "bitcoin_testnet:btc", "amount": "49850", "display": "0.00049850", "value": "58.6933" }, "solver_id": "0x9dd9c2d208b07bf9a4ef9ca311f36d7185749635", "estimated_time": 20, "slippage": 50, "fee": 30, "fixed_fee": "0.0" } ] } ``` Once a quote is received, we can submit the order: Use `amount` and `asset` from the quote response. Use your wallet addresses for `owner`. ```bash theme={null} curl --location 'https://testnet.api.garden.finance/v2/orders' \ --header 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ --header 'Content-Type: application/json' \ --data '{ "source": { "asset": "ethereum_sepolia:wbtc", "owner": "0x4cD3FB4a504cc978f316a81Dc5165D2C5b30592d", "amount": "50000" }, "destination": { "asset": "bitcoin_testnet:btc", "owner": "tb1p4pr78swsn60y4ushe05v28mqpqppxxkfkxu2wun5jw6duc8unj3sjrh4gd", "amount": "49850" } }' ``` ```json Response expandable wrap theme={null} { "status": "Ok", "result": { "approval_transaction": { "chain_id": 11155111, "data": "0x095ea7b3000000000000000000000000d1e0ba2b165726b3a6051b765d4564d030fdcf50ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "gas_limit": "0xea60", "to": "0xe918a5a47b8e0afac2382bc5d1e981613e63fb07", "value": "0x0" }, "initiate_transaction": { "chain_id": 11155111, "data": "0x97ffc7ae000000000000000000000000661ba32eb5f86cab358ddbb7f264b10c5825e2dd0000000000000000000000000000000000000000000000000000000000001c20000000000000000000000000000000000000000000000000000000000000c3507b32dd44045d8d6fc477615d0d72f42fc7696078e44724fe64c85b563fc8f306", "gas_limit": "0x493e0", "to": "0xd1e0ba2b165726b3a6051b765d4564d030fdcf50", "value": "0x0" }, "order_id": "7e69c6514fa6c7a46b88e235e484d9958029cbe748e125add69b1ffcf7265198", "typed_data": { "domain": { "chainId": "0xaa36a7", "name": "HTLC", "verifyingContract": "0xd1e0ba2b165726b3a6051b765d4564d030fdcf50", "version": "3" }, "message": { "amount": "0xc350", "redeemer": "0x661ba32eb5f86cab358ddbb7f264b10c5825e2dd", "secretHash": "0x7b32dd44045d8d6fc477615d0d72f42fc7696078e44724fe64c85b563fc8f306", "timelock": "0x1c20" }, "primaryType": "Initiate", "types": { "EIP712Domain": [ { "name": "name", "type": "string" }, { "name": "version", "type": "string" }, { "name": "chainId", "type": "uint256" }, { "name": "verifyingContract", "type": "address" } ], "Initiate": [ { "name": "redeemer", "type": "address" }, { "name": "timelock", "type": "uint256" }, { "name": "amount", "type": "uint256" }, { "name": "secretHash", "type": "bytes32" } ] } } } } ``` Then deposit the funds: ```typescript theme={null} await window.ethereum.request({ method: 'eth_sendTransaction', params: [response.result.transaction] }); ``` ```typescript theme={null} // Sign typed data. const signature = await window.ethereum.request({ method: 'eth_signTypedData_v4', params: [userAddress, JSON.stringify(response.result.typed_data)] }); // Initiate the swap with the signature. await fetch(`https://testnet.api.garden.finance/v2/orders/${response.result.order_id}?action=initiate`, { method: 'PATCH', headers: { 'garden-app-id': 'f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796', 'Content-Type': 'application/json' }, body: JSON.stringify({ signature }) }); ``` Check the order status using the `order_id` from the response. ```bash wrap theme={null} curl -X 'GET' \ 'https://testnet.api.garden.finance/v2/orders/7e69c6514fa6c7a46b88e235e484d9958029cbe748e125add69b1ffcf7265198' \ -H 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ -H 'accept: application/json' ``` ```json Response expandable focus={45} theme={null} { "status": "Ok", "result": { "created_at": "2025-09-01T06:48:02.138492Z", "source_swap": { "created_at": "2025-09-01T06:48:02.138492Z", "swap_id": "f511550e9629124790646668b8cdc2a42bbdc9b9333857c4fdd3f1bf514b6575", "chain": "ethereum_sepolia", "asset": "ethereum_sepolia:wbtc", "initiator": "0x4cD3FB4a504cc978f316a81Dc5165D2C5b30592d", "redeemer": "0x661bA32eb5f86CaB358DDbB7F264b10c5825e2dd", "timelock": 7200, "filled_amount": "0", "asset_price": 108107, "amount": "50000", "secret_hash": "7b32dd44045d8d6fc477615d0d72f42fc7696078e44724fe64c85b563fc8f306", "secret": "", "initiate_tx_hash": "", "redeem_tx_hash": "", "refund_tx_hash": "", "initiate_block_number": "0", "redeem_block_number": "0", "refund_block_number": "0", "required_confirmations": 1, "current_confirmations": 0, "initiate_timestamp": null, "redeem_timestamp": null, "refund_timestamp": null }, "destination_swap": { "created_at": "2025-09-01T06:48:02.138492Z", "swap_id": "tb1peepw3gum369qfu7zgnd6jq5sk77q5wyuzmznlpavzmeq8x72kcyqr66rxg", "chain": "bitcoin_testnet", "asset": "bitcoin_testnet:btc", "initiator": "460f2e8ff81fc4e0a8e6ce7796704e3829e3e3eedb8db9390bdc51f4f04cf0a6", "redeemer": "tb1p4pr78swsn60y4ushe05v28mqpqppxxkfkxu2wun5jw6duc8unj3sjrh4gd", "delegate": "03f4776668ef95e35ff2d51d8c59eb7a461bb2354b540553d8455dbec91087c1", "timelock": 12, "filled_amount": "0", "asset_price": 108107, "amount": "49850", "secret_hash": "7b32dd44045d8d6fc477615d0d72f42fc7696078e44724fe64c85b563fc8f306", "secret": "", "initiate_tx_hash": "", "redeem_tx_hash": "", "refund_tx_hash": "", "initiate_block_number": "0", "redeem_block_number": "0", "refund_block_number": "0", "required_confirmations": 0, "current_confirmations": 0, "initiate_timestamp": null, "redeem_timestamp": null, "refund_timestamp": null }, "nonce": "4326423548464636342", "order_id": "7e69c6514fa6c7a46b88e235e484d9958029cbe748e125add69b1ffcf7265198", "affiliate_fees": [], "integrator": "DocsTesting", "version": "v3", "solver_id": "0x90eb912279ee8a3c56f784a44dedffae4487524d" } } ``` The swap is complete once the `order.destination_swap.redeem_tx_hash` field is populated. Let's get a quote to trade 0.0005 WBTC on **Tron Shasta** to WBTC on **Arbitrum Sepolia**. ```bash wrap theme={null} curl -X 'GET' 'https://testnet.api.garden.finance/v2/quote?from=tron_shasta:wbtc&to=arbitrum_sepolia:wbtc&from_amount=50000' \ -H 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ -H 'accept: application/json' ``` ```json Response expandable theme={null} { "status": "Ok", "result": [ { "source": { "asset": "tron_shasta:wbtc", "amount": "50000", "display": "0.00050000", "value": "50.00" }, "destination": { "asset": "arbitrum_sepolia:wbtc", "amount": "49850", "display": "0.00049850", "value": "49.85" }, "solver_id": "0x90eb912279ee8a3c56f784a44dedffae4487524d", "estimated_time": 20, "slippage": 0, "fee": 30, "fixed_fee": "0" } ] } ``` Once a quote is received, we can submit the order: Use `amount` and `asset` from the quote response. Use your wallet addresses for `owner`. ```bash theme={null} curl --location 'https://testnet.api.garden.finance/v2/orders' \ --header 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ --header 'Content-Type: application/json' \ --data '{ "source": { "asset": "tron_shasta:wbtc", "owner": "TWbEz5ibiL6dreiLJ5oBF5CwDkw6Xfe6KX", "amount": "50000" }, "destination": { "asset": "arbitrum_sepolia:wbtc", "owner": "0xA39ABb978cfd2ba459163ad1EaB6E8940Fbf4359", "amount": "49850" } }' ``` ```json Response expandable wrap theme={null} { "status": "Ok", "result": { "approval_transaction": { "chain_id": 2494104990, "data": "0x095ea7b30000000000000000000000001ce431493d15db597bbc1c74ae09407a76f19458ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "gas_limit": "500000000", "to": "TCbyHXPZ2o3qPfQUSrqPoS3NkwEJ94P3AS", "value": "0" }, "initiate_transaction": { "chain_id": 2494104990, "data": "0x4ede0ab70000000000000000000000006b887db9741368e35c2e986a2bdae326632d06f80000000000000000000000000000000000000000000000000000000000069780000000000000000000000000000000000000000000000000000000012a05f200df8f1793684d3bbbd41aa803c8180807977e2aa33b67f63a7addbcf16831e15800000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000", "gas_limit": "500000000", "to": "TCbyHXPZ2o3qPfQUSrqPoS3NkwEJ94P3AS", "value": "0" }, "order_id": "23a75a6030f2cfe9381ccb78788513a0b82b1a2bb912145ca89af8c123cf3e2d", "typed_data": { "domain": { "chainId": "2494104990", "name": "HTLC", "verifyingContract": "TCbyHXPZ2o3qPfQUSrqPoS3NkwEJ94P3AS", "version": "3" }, "message": { "amount": "5000000000", "destinationData": "0x", "redeemer": "TKmnp9reskx58cYACSzvbXDBr7hKFh7XHS", "secretHash": "9bf64d0cd7a8f0d254dcc48724e4d2969fdf8dfc25619bc622a3bcecb33e29c8", "timelock": 432000 }, "primaryType": "Initiate", "types": { "EIP712Domain": [ { "name": "name", "type": "string" }, { "name": "version", "type": "string" }, { "name": "chainId", "type": "uint256" }, { "name": "verifyingContract", "type": "address" } ], "Initiate": [ { "name": "redeemer", "type": "address" }, { "name": "timelock", "type": "uint256" }, { "name": "amount", "type": "uint256" }, { "name": "secretHash", "type": "bytes32" }, { "name": "destinationData", "type": "bytes" } ] } } } } ``` Then deposit the funds: ```typescript theme={null} // Dependencies: "tronweb": "6.0.4" // Approval transaction const dataWithoutSelector = '0x' + response.result.approval_transaction.data.slice(10); const params = window.tronWeb.utils.abi.decodeParams( ['spender', 'amount'], ['address', 'uint256'], dataWithoutSelector, false, ); const approval_transaction = await window.tronWeb.transactionBuilder.triggerSmartContract( response.result.approval_transaction.to, 'approve(address,uint256)', { feeLimit: parseInt(response.result.approval_transaction.gas_limit), callValue: parseInt(response.result.approval_transaction.value) }, [ { type: 'address', value: params.spender }, { type: 'uint256', value: params.amount.toString() }, ], window.tronWeb.defaultAddress.base58 ); const approval_signedTx = await window.tronWeb.trx.sign(approval_transaction.transaction); const approval_broadcastResult = await window.tronWeb.trx.sendRawTransaction(approval_signedTx); // Initiate transaction const initiate_transaction = await window.tronWeb.transactionBuilder.triggerSmartContract( response.result.initiate_transaction.to, 'initiate(address,uint256,uint256,bytes32,bytes)', { feeLimit: parseInt(response.result.initiate_transaction.gas_limit), callValue: parseInt(response.result.initiate_transaction.value) }, [ { type: 'address', value: response.result.typed_data.message.redeemer }, { type: 'uint256', value: response.result.typed_data.message.timelock }, { type: 'uint256', value: response.result.typed_data.message.amount }, { type: 'bytes32', value: toBytes32Hex(String(response.result.typed_data.message.secretHash)) }, { type: 'bytes', value: response.result.typed_data.message.destinationData }, ], window.tronWeb.defaultAddress.base58 ); const initiate_signedTx = await window.tronWeb.trx.sign(initiate_transaction.transaction); const initiate_broadcastResult = await window.tronWeb.trx.sendRawTransaction(initiate_signedTx); ``` ```typescript theme={null} // Dependencies: "tronweb": "6.0.4" const typedData = response.result.typed_data; const types = { ...typedData.types }; delete types.EIP712Domain; types.Initiate = types.Initiate.filter( (field) => field.name !== 'destinationData' ); const message = { ...typedData.message }; delete message.destinationData; // Sign typed data using TronWeb const signature = await window.tronWeb.trx._signTypedData( typedData.domain, types, message ); // Initiate the swap with the signature. await fetch(`https://testnet.api.garden.finance/v2/orders/${response.result.order_id}?action=initiate`, { method: 'PATCH', headers: { 'garden-app-id': 'f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796', 'Content-Type': 'application/json' }, body: JSON.stringify({ signature }) }); ``` Check the order status using the `order_id` from the response. ```bash wrap theme={null} curl -X 'GET' \ 'https://testnet.api.garden.finance/v2/orders/b0d73ffcc814f4b6476be779d588985aeb178ba6eeee27a5cf4d9d33461b4005' \ -H 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ -H 'accept: application/json' ``` ```json Response expandable focus={44} theme={null} { "status": "Ok", "result": { "created_at": "2026-01-16T11:16:46.050428Z", "source_swap": { "created_at": "2026-01-16T11:16:46.050428Z", "swap_id": "2db870aaaf5a2b65d2362da52ef564f42d88542f38e55dacdcb802d5eabef44b", "chain": "tron_shasta", "asset": "tron_shasta:wbtc", "initiator": "TWbEz5ibiL6dreiLJ5oBF5CwDkw6Xfe6KX", "redeemer": "TKmnp9reskx58cYACSzvbXDBr7hKFh7XHS", "timelock": 432000, "filled_amount": "50000", "asset_price": 95291.0, "amount": "50000", "secret_hash": "9bf64d0cd7a8f0d254dcc48724e4d2969fdf8dfc25619bc622a3bcecb33e29c8", "secret": "f1a5f527cdffa795cd571b833e726a67df4ecfa77f5b01cbecd6c35c1e74eca4", "initiate_tx_hash": "0xc85acba4e03fb55807049ee048b96081b5686a6ceee4ea382d3cfa8f36d1a4c5", "redeem_tx_hash": "0xc66c1bfb1b6b276556a5a034f67b0525bedc44184827b68dd9147fbdf666c434", "refund_tx_hash": "", "initiate_block_number": "61503886", "redeem_block_number": "61503898", "refund_block_number": "0", "required_confirmations": 1, "current_confirmations": 1, "initiate_timestamp": "2026-01-16T11:17:36Z", "redeem_timestamp": "2026-01-16T11:18:18Z", "refund_timestamp": null }, "destination_swap": { "created_at": "2026-01-16T11:16:46.050428Z", "swap_id": "ce80898324c992c2112d8caafe8d2cc95c1c167ef1934bc6eed610c4b235b923", "chain": "arbitrum_sepolia", "asset": "arbitrum_sepolia:wbtc", "initiator": "0x35ac29159116f687fa7dec74c3331a2d5bb1e89e", "redeemer": "0xA39ABb978cfd2ba459163ad1EaB6E8940Fbf4359", "timelock": 36000, "filled_amount": "49850", "asset_price": 95291.0, "amount": "49850", "secret_hash": "9bf64d0cd7a8f0d254dcc48724e4d2969fdf8dfc25619bc622a3bcecb33e29c8", "secret": "f1a5f527cdffa795cd571b833e726a67df4ecfa77f5b01cbecd6c35c1e74eca4", "initiate_tx_hash": "0xc520b684eb9376b83622fb12871a8a96b5cdf4be68914abd426d24850ccf310f", "redeem_tx_hash": "0x1abd4e96badd88915547c0c67bea14de545fc78dc4e034aa37dc3ee94243bbf5", "refund_tx_hash": "", "initiate_block_number": "233982423", "redeem_block_number": "233982458", "refund_block_number": "0", "required_confirmations": 0, "current_confirmations": 0, "initiate_timestamp": "2026-01-16T11:17:59Z", "redeem_timestamp": "2026-01-16T11:18:07Z", "refund_timestamp": null }, "nonce": "1768562205873", "order_id": "b0d73ffcc814f4b6476be779d588985aeb178ba6eeee27a5cf4d9d33461b4005", "affiliate_fees": [], "integrator": "DocsTesting", "version": "v3", "solver_id": "0x90eb912279ee8a3c56f784a44dedffae4487524d" } } ``` The swap is complete once the `order.destination_swap.redeem_tx_hash` field is populated. Let's get a quote to trade 0.354608265 SOL on **Solana Testnet** to BTC on **Bitcoin Testnet4**. ```bash wrap theme={null} curl -X 'GET' 'https://testnet.api.garden.finance/v2/quote?from=solana_testnet:sol&to=bitcoin_testnet:btc&from_amount=354608265' \ -H 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ -H 'accept: application/json' ``` ```json Response expandable theme={null} { "status": "Ok", "result": [ { "source": { "asset": "solana_testnet:sol", "amount": "354608265", "display": "0.35460826", "value": "57.1238" }, "destination": { "asset": "bitcoin_testnet:btc", "amount": "48515", "display": "0.00048515", "value": "56.9524" }, "solver_id": "0x9dd9c2d208b07bf9a4ef9ca311f36d7185749635", "estimated_time": 20, "slippage": 50, "fee": 30, "fixed_fee": "0.0" } ] } ``` Once a quote is received, we can submit the order: Use `amount` and `asset` from the quote response. Use your wallet addresses for `owner`. ```bash theme={null} curl --location 'https://testnet.api.garden.finance/v2/orders' \ --header 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ --header 'Content-Type: application/json' \ --data '{ "source": { "asset": "solana_testnet:sol", "owner": "YH4btvqb4JBWSEJh22MuA231ekpJ5JqbBXQY1apJtKH", "amount": "354608265" }, "destination": { "asset": "bitcoin_testnet:btc", "owner": "tb1p4pr78swsn60y4ushe05v28mqpqppxxkfkxu2wun5jw6duc8unj3sjrh4gd", "amount": "48515" } }' ``` ```json Response wrap theme={null} { "status": "Ok", "result": { "order_id": "ee54521431fd3b7c01f46f2aaec086362d8cf2d6a7d3b31233c66c31bed9e4eb", "versioned_tx": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100020408032aa331a727d64421581d1ec2ea7cdde7ac7bb04a7ab7bb5e7f4ea5992f08b30c777cae9e6cbeb9830b8b0cd529b6e1f3667ae0c1c21071cb64bba2c7763e000000000000000000000000000000000000000000000000000000000000000017b7828ca6c4f06b51139a60254f81fe1249c8e2b122febf32ca81322b9f1e8ed3c02412b5458cdb8b14d42d4a61166f85f0f4e4d536c8a9f111a4de7e86b77801030301000258053f7b71994b940e89e4221500000000c04b03000000000044070018cffb963a80cb5bf8974e0d64934c614d26340d947ddf3b762a014cacb8841dc97c588ab9e1b94f62627b97d54f8c1048b28c26749d413032e9d66c4e", "versioned_tx_gasless": null } } ``` Then deposit the funds: ```typescript theme={null} // Decode and deserialize the `versioned_tx` field const buffer = Buffer.from(response.result.versioned_tx, 'base64'); const transaction = VersionedTransaction.deserialize(buffer); // Sign and send using your wallet. const signature = await wallet.signAndSendTransaction(transaction); ``` Currently, this is only supported for Solana SPL orders. As such, `versioned_tx_gasless` will be `null` for Solana orders with native SOL. ```typescript theme={null} // Note the use of `versioned_tx_gasless` const buffer = Buffer.from(response.result.versioned_tx_gasless, 'base64'); const transaction = VersionedTransaction.deserialize(buffer); // Sign using your wallet const signedTransaction = await wallet.signTransaction(transaction); // Serialize and base64 encode the signed transaction const encodedTx = Buffer.from(signedTransaction.serialize()).toString('base64'); // Pass this encoded transaction to the garden API await fetch(`https://testnet.api.garden.finance/v2/orders/${response.result.order_id}?action=initiate`, { method: 'PATCH', headers: { 'garden-app-id': 'f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796', 'Content-Type': 'application/json' }, body: JSON.stringify({ signature: encodedTx }) }); ``` Check the order status using the `order_id` from the response. ```bash wrap theme={null} curl -X 'GET' \ 'https://testnet.api.garden.finance/v2/orders/ee54521431fd3b7c01f46f2aaec086362d8cf2d6a7d3b31233c66c31bed9e4eb' \ -H 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ -H 'accept: application/json' ``` ```json Response expandable focus={45} theme={null} { "status": "Ok", "result": { "created_at": "2025-07-13T01:35:18.538262Z", "source_swap": { "created_at": "2025-07-13T01:35:18.538262Z", "swap_id": "4629969e622608f634a4be4116f80be482ef987bccf30ef9900eb3e0f242eb77", "chain": "solana_testnet", "asset": "solana_testnet:sol", "initiator": "YH4btvqb4JBWSEJh22MuA231ekpJ5JqbBXQY1apJtKH", "redeemer": "5aYv3mxzXupjUa6Yyi23Yn3n1kbgGEDfrJ6uBDq6VwQo", "timelock": 216000, "filled_amount": "0", "asset_price": 161.11, "amount": "354608265", "secret_hash": "b8841dc97c588ab9e1b94f62627b97d54f8c1048b28c26749d413032e9d66c4e", "secret": "", "initiate_tx_hash": "", "redeem_tx_hash": "", "refund_tx_hash": "", "initiate_block_number": "0", "redeem_block_number": "0", "refund_block_number": "0", "required_confirmations": 1, "current_confirmations": 0, "initiate_timestamp": null, "redeem_timestamp": null, "refund_timestamp": null }, "destination_swap": { "created_at": "2025-07-13T01:35:18.538262Z", "swap_id": "tb1pydwnxvdwfhs674py5z7wfst9nvm2t92s9lpfs6l7v4e0tsfxx64shepc0x", "chain": "bitcoin_testnet", "asset": "bitcoin_testnet:btc", "initiator": "460f2e8ff81fc4e0a8e6ce7796704e3829e3e3eedb8db9390bdc51f4f04cf0a6", "redeemer": "tb1p4pr78swsn60y4ushe05v28mqpqppxxkfkxu2wun5jw6duc8unj3sjrh4gd", "delegate": "be7d111dca1cc2b967d1d1a20bf05c2cd5050307b072dd8bfccdf8cd52fddae7", "timelock": 12, "filled_amount": "0", "asset_price": 161.11, "amount": "48515", "secret_hash": "b8841dc97c588ab9e1b94f62627b97d54f8c1048b28c26749d413032e9d66c4e", "secret": "", "initiate_tx_hash": "", "redeem_tx_hash": "", "refund_tx_hash": "", "initiate_block_number": "0", "redeem_block_number": "0", "refund_block_number": "0", "required_confirmations": 0, "current_confirmations": 0, "initiate_timestamp": null, "redeem_timestamp": null, "refund_timestamp": null }, "nonce": "18331192702570975643", "order_id": "ee54521431fd3b7c01f46f2aaec086362d8cf2d6a7d3b31233c66c31bed9e4eb", "affiliate_fees": [], "integrator": "DocsTesting", "version": "v1", "solver_id": "0x90eb912279ee8a3c56f784a44dedffae4487524d" } } ``` The swap is complete once the `order.destination_swap.redeem_tx_hash` field is populated. Let's get a quote to trade 0.0005 WBTC on **Starknet Sepolia** to BTC on **Bitcoin Testnet4**. ```bash wrap theme={null} curl -X 'GET' 'https://testnet.api.garden.finance/v2/quote?from=starknet_sepolia:wbtc&to=bitcoin_testnet:btc&from_amount=50000' \ -H 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ -H 'accept: application/json' ``` ```json Response expandable theme={null} { "status": "Ok", "result": [ { "source": { "asset": "starknet_sepolia:wbtc", "amount": "50000", "display": "0.00050000", "value": "58.8800" }, "destination": { "asset": "bitcoin_testnet:btc", "amount": "49850", "display": "0.00049850", "value": "58.7033" }, "solver_id": "0x9dd9c2d208b07bf9a4ef9ca311f36d7185749635", "estimated_time": 20, "slippage": 50, "fee": 30, "fixed_fee": "0.0" } ] } ``` Once a quote is received, we can submit the order: Use `amount` and `asset` from the quote response. Use your wallet addresses for `owner`. ```bash theme={null} curl --location 'https://testnet.api.garden.finance/v2/orders' \ --header 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ --header 'Content-Type: application/json' \ --data '{ "source": { "asset": "starknet_sepolia:wbtc", "owner": "0x00609190b1348bcc06da44d58c79709495c11a5a6f0b9e154e1209f2a17dd933", "amount": "50000" }, "destination": { "asset": "bitcoin_testnet:btc", "owner": "tb1p4pr78swsn60y4ushe05v28mqpqppxxkfkxu2wun5jw6duc8unj3sjrh4gd", "amount": "49850" } }' ``` ```json Response expandable wrap theme={null} { "status": "Ok", "result": { "approval_transaction": { "calldata": [ "0x6579d255314109429a4477d89629bc2b94f529ae01979c2f8014f9246482603", "0xffffffffffffffffffffffffffffffff", "0xffffffffffffffffffffffffffffffff" ], "selector": "0x219209e083275171774dab1df80982e9df2096516f06319c5c6d71ae0a8480c", "to": "0x496bef3ed20371382fbe0ca6a5a64252c5c848f9f1f0cccf8110fc4def912d5" }, "initiate_transaction": { "calldata": [ "0x78f8ae98fe78b5df9d3755b5910864c028d5249b4af685eb45687b1b76a4205", "0xb40", "0xc350", "0x0", "0x5e0dfe4c", "0x3c6495a", "0x5f81f922", "0x204d8c16", "0x964f6ca5", "0xcc766f3a", "0x6a4cc6cd", "0x2117fdb4" ], "selector": "0x2aed25fcd0101fcece997d93f9d0643dfa3fbd4118cae16bf7d6cd533577c28", "to": "0x6579d255314109429a4477d89629bc2b94f529ae01979c2f8014f9246482603" }, "order_id": "f64db51ba3894ba3720925aaa816d4eb538f623802686595bf8ac50ecbc68bad", "typed_data": { "domain": { "chainId": "0x534e5f5345504f4c4941", "name": "HTLC", "revision": 1, "version": "0x31" }, "message": { "amount": { "high": "0x0", "low": "0xc350" }, "redeemer": "3419807808164513772718725483164424774502669183354591566218616648407888577029", "secretHash": [ 1577975372, 63326554, 1602353442, 541953046, 2521787557, 3430313786, 1783416525, 555220404 ], "timelock": "2880" }, "primaryType": "Initiate", "types": { "Initiate": [ { "name": "redeemer", "type": "ContractAddress" }, { "name": "amount", "type": "u256" }, { "name": "timelock", "type": "u128" }, { "name": "secretHash", "type": "u128*" } ], "StarknetDomain": [ { "name": "name", "type": "shortstring" }, { "name": "version", "type": "shortstring" }, { "name": "chainId", "type": "shortstring" }, { "name": "revision", "type": "shortstring" } ] } } } } ``` Then deposit the funds: ```typescript theme={null} await account.execute([{ contractAddress: response.result.transaction.to, entrypoint: response.result.transaction.selector, calldata: response.result.transaction.calldata }]); ``` ```typescript theme={null} // Sign typed data. const signature = await account.signMessage(response.result.typed_data); // Initiate the swap with the signature. await fetch(`https://testnet.api.garden.finance/v2/orders/${response.result.order_id}?action=initiate`, { method: 'PATCH', headers: { 'garden-app-id': 'f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796', 'Content-Type': 'application/json' }, body: JSON.stringify({ signature }) }); ``` Check the order status using the `order_id` from the response. ```bash wrap theme={null} curl -X 'GET' \ 'https://testnet.api.garden.finance/v2/orders/f64db51ba3894ba3720925aaa816d4eb538f623802686595bf8ac50ecbc68bad' \ -H 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ -H 'accept: application/json' ``` ```json Response expandable focus={45} theme={null} { "status": "Ok", "result": { "created_at": "2025-09-01T07:02:07.851646Z", "source_swap": { "created_at": "2025-09-01T07:02:07.851646Z", "swap_id": "3ef65007bb6cabe9749309ecfb4e343bc31c3f1e66865dd49623c557c275dcb", "chain": "starknet_sepolia", "asset": "starknet_sepolia:wbtc", "initiator": "0x00609190b1348bcc06da44d58c79709495c11a5a6f0b9e154e1209f2a17dd933", "redeemer": "0x078f8ae98fe78b5df9d3755b5910864c028d5249b4af685eb45687b1b76a4205", "timelock": 2880, "filled_amount": "0", "asset_price": 108201, "amount": "50000", "secret_hash": "5e0dfe4c03c6495a5f81f922204d8c16964f6ca5cc766f3a6a4cc6cd2117fdb4", "secret": "", "initiate_tx_hash": "", "redeem_tx_hash": "", "refund_tx_hash": "", "initiate_block_number": "0", "redeem_block_number": "0", "refund_block_number": "0", "required_confirmations": 1, "current_confirmations": 0, "initiate_timestamp": null, "redeem_timestamp": null, "refund_timestamp": null }, "destination_swap": { "created_at": "2025-09-01T07:02:07.851646Z", "swap_id": "tb1p3nug6w3cjtnvjuu43aytqtnx5420q6nmntzsrdkngk4na7ny4x7qfayy5n", "chain": "bitcoin_testnet", "asset": "bitcoin_testnet:btc", "initiator": "460f2e8ff81fc4e0a8e6ce7796704e3829e3e3eedb8db9390bdc51f4f04cf0a6", "redeemer": "tb1p4pr78swsn60y4ushe05v28mqpqppxxkfkxu2wun5jw6duc8unj3sjrh4gd", "delegate": "e6ca4c227c7e048c5d605bfe478e79ee313de490cdfbb11160c2cc1221dbdc71", "timelock": 12, "filled_amount": "0", "asset_price": 108201, "amount": "49850", "secret_hash": "5e0dfe4c03c6495a5f81f922204d8c16964f6ca5cc766f3a6a4cc6cd2117fdb4", "secret": "", "initiate_tx_hash": "", "redeem_tx_hash": "", "refund_tx_hash": "", "initiate_block_number": "0", "redeem_block_number": "0", "refund_block_number": "0", "required_confirmations": 0, "current_confirmations": 0, "initiate_timestamp": null, "redeem_timestamp": null, "refund_timestamp": null }, "nonce": "5463708687999347822", "order_id": "f64db51ba3894ba3720925aaa816d4eb538f623802686595bf8ac50ecbc68bad", "affiliate_fees": [], "integrator": "DocsTesting", "version": "v2", "solver_id": "0x90eb912279ee8a3c56f784a44dedffae4487524d" } } ``` The swap is complete once the `order.destination_swap.redeem_tx_hash` field is populated. Let's get a quote to trade 10 PURR on **HyperCore (Testnet)** to SOL on **Solana Testnet**. ```bash wrap theme={null} curl -X 'GET' 'https://testnet.api.garden.finance/v2/quote?from=hypercore_testnet:purr&to=solana_testnet:sol&from_amount=10000000000000000000&indicative=false' \ -H 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ -H 'accept: application/json' ``` ```json Response expandable theme={null} { "status": "Ok", "result": [ { "source": { "asset": "hypercore_testnet:purr", "amount": "10000000000000000000", "display": "10.000000000000000000", "value": "10.00" }, "destination": { "asset": "solana_testnet:sol", "amount": "1500000000", "display": "1.500000000", "value": "9.97" }, "solver_id": "0x9dd9c2d208b07bf9a4ef9ca311f36d7185749635", "estimated_time": 60, "slippage": 50, "fee": 30, "fixed_fee": "0.0" } ] } ``` Once a quote is received, submit the order: Use `amount` and `asset` from the quote response. Use your wallet addresses for `owner`. The `owner` for the source must be your HyperEVM address. ```bash theme={null} curl --location 'https://testnet.api.garden.finance/v2/orders' \ --header 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ --header 'Content-Type: application/json' \ --data '{ "source": { "asset": "hypercore_testnet:purr", "owner": "0xYourHyperEVMAddress", "amount": "10000000000000000000" }, "destination": { "asset": "solana_testnet:sol", "owner": "YourSolanaAddress", "amount": "1500000000" } }' ``` ```json Response expandable focus={29:35} theme={null} { "status": "Ok", "result": { "order_id": "c4d3ee14603bb46eef84062697cbf9512a3b4c5d", "approval_transaction": null, "initiate_transaction": { "to": "0x...", "value": "0x0", "data": "0x97ffc7ae...", "gas_limit": "0x493e0", "chain_id": 998 }, "typed_data": { "domain": { "name": "HTLC", "version": "3", "chainId": "0x3e6", "verifyingContract": "0x..." }, "primaryType": "Initiate", "types": { "..." }, "message": { "redeemer": "0x5f3a295d23bff9de6e09fe0a88f4f08e15f4c59c", "timelock": "0x15180", "amount": "0x989680", "secretHash": "0xe63a6d36..." } }, "core_to_evm_transfer": { "to": "0x3333333333333333333333333333333333333333", "value": "0x0", "data": "0x17938e13...", "gas_limit": "0x186a0", "chain_id": 998 } } } ``` For HyperCore → X swaps, the response includes a `core_to_evm_transfer` field. This is a pre-built transaction you must submit on HyperEVM **before** initiating the HTLC — it calls the CoreWriter precompile to move your PURR from your HyperCore spot account to HyperEVM. Funds become available in the next block. #### 1. Bridge to HyperEVM Submit this on HyperEVM (chain ID `998` on testnet) to move your PURR from HyperCore spot to HyperEVM: ```typescript theme={null} await window.ethereum.request({ method: 'eth_sendTransaction', params: [response.result.core_to_evm_transfer] }); ``` Funds are available on HyperEVM in the next block. #### 2. Initiate the swap Lock the funds in the swap contract: ```typescript theme={null} await window.ethereum.request({ method: 'eth_sendTransaction', params: [response.result.initiate_transaction] }); ``` ```typescript theme={null} // Sign the EIP-712 typed data off-chain const signature = await window.ethereum.request({ method: 'eth_signTypedData_v4', params: [userAddress, JSON.stringify(response.result.typed_data)] }); // Submit the signature — the solver initiates on your behalf await fetch(`https://testnet.api.garden.finance/v2/orders/${response.result.order_id}?action=initiate`, { method: 'PATCH', headers: { 'garden-app-id': 'f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796', 'Content-Type': 'application/json' }, body: JSON.stringify({ signature }) }); ``` Check the order status using the `order_id` from the response. ```bash wrap theme={null} curl -X 'GET' \ 'https://testnet.api.garden.finance/v2/orders/c4d3ee14603bb46eef84062697cbf9512a3b4c5d' \ -H 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ -H 'accept: application/json' ``` The swap is complete once the `order.destination_swap.redeem_tx_hash` field is populated. **Refund path:** If the swap expires before the solver fulfills it, calling `refund()` on the HyperEVM HTLC will route your PURR back directly to your HyperCore spot account via the CoreWriter precompile — the same mechanism as settlement. No manual bridging back to HyperCore is required. Let's get a quote to trade 3 Sui on **Sui Testnet** to BTC on **Bitcoin Testnet4**. ```bash wrap theme={null} curl -X 'GET' 'https://testnet.api.garden.finance/v2/quote?from=sui_testnet:sui&to=bitcoin_testnet:btc&from_amount=3000000000' \ -H 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ -H 'accept: application/json' ``` ```json Response expandable theme={null} { "status": "Ok", "result": [ { "source": { "asset": "sui_testnet:sui", "amount": "3000000000", "display": "3.00000000", "value": "9.9997" }, "destination": { "asset": "bitcoin_testnet:btc", "amount": "9079", "display": "0.00009079", "value": "9.9697" }, "solver_id": "0x9dd9c2d208b07bf9a4ef9ca311f36d7185749635", "estimated_time": 20, "slippage": 50, "fee": 30, "fixed_fee": "0.0" } ] } ``` Once a quote is received, we can submit the order: Use `amount` and `asset` from the quote response. Use your wallet addresses for `owner`. ```bash theme={null} curl --location 'https://testnet.api.garden.finance/v2/orders' \ --header 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ --header 'Content-Type: application/json' \ --data '{ "source": { "asset": "sui_testnet:sui", "owner": "0x79a1582388c16d0ab85904f320eb0527481391a9b9ab4b2ab46adc4c2564f9d0", "amount": "3000000000" }, "destination": { "asset": "bitcoin_testnet:btc", "owner": "tb1p4pr78swsn60y4ushe05v28mqpqppxxkfkxu2wun5jw6duc8unj3sjrh4gd", "amount": "9079" } }' ``` ```json Response expandable wrap theme={null} { "status": "Ok", "result": { "order_id": "e7a2d8b8dbf8d1e315c17e40cd9287410ea9449258f806a1833b7f9ad68527f5", "ptb_bytes": [0,8,0,8,0,94,208,178,0,0,0,0,1,1,92,67,135,21,183,220,192,45,18,171,146,68,145,83,161,229,173,226,48,22,32,213,191,96,170,116,143,0,103,38,211,105,76,48,204,31,0,0,0,0,1,0,32,121,161,88,35,136,193,109,10,184,89,4,243,32,235,5,39,72,19,145,169,185,171,75,42,180,106,220,76,37,100,249,208,0,32,63,109,158,7,253,76,218,187,170,112,151,193,158,207,101,88,9,200,147,169,207,227,244,24,195,255,188,4,37,13,59,76,0,33,32,57,81,42,177,166,145,123,61,75,78,219,100,208,167,124,32,76,159,215,52,146,144,167,63,206,85,74,183,15,149,211,219,0,32,0,92,38,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,1,0,0,0,0,0,0,0,0,2,2,0,1,1,0,0,0,153,134,91,3,221,27,192,61,10,106,128,92,69,78,162,87,196,100,247,171,204,202,233,205,75,98,27,145,231,202,4,222,10,65,116,111,109,105,99,83,119,97,112,8,105,110,105,116,105,97,116,101,1,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,115,117,105,3,83,85,73,0,9,1,1,0,1,2,0,1,3,0,1,4,0,1,0,0,1,5,0,1,6,0,2,0,0,1,7,0] } } ``` Then deposit the funds: ```typescript theme={null} import { SuiClient, getFullnodeUrl } from "@mysten/sui/client"; import { Transaction } from "@mysten/sui/transactions"; import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519"; const client = new SuiClient({ url: getFullnodeUrl('testnet') }); const gasPrice = await client.getReferenceGasPrice(); const estimatedGasBudget = 10000000; // 10M gas units as a conservative estimate let transaction = Transaction.fromKind(new Uint8Array(ptb_bytes)) // ptb_bytes are the Programmable Transaction Bytes returned from the create order response transaction.setSender(sender); transaction.setGasPrice(gasPrice); transaction.setGasBudget(estimatedGasBudget); const suiWallet = Ed25519Keypair.fromSecretKey("[private-key]"); try { const result = await client.signAndExecuteTransaction({ transaction: transaction, signer: suiWallet, options: { showEffects: true, }, }); } catch (error) { console.error('Transaction execution failed:', error); } ``` Check the order status using the `order_id` from the response. ```bash wrap theme={null} curl -X 'GET' \ 'https://testnet.api.garden.finance/v2/orders/e7a2d8b8dbf8d1e315c17e40cd9287410ea9449258f806a1833b7f9ad68527f5' \ -H 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ -H 'accept: application/json' ``` ```json Response expandable focus={45} theme={null} { "status": "Ok", "result": { "created_at": "2025-09-01T09:33:10.050305Z", "source_swap": { "created_at": "2025-09-01T09:33:10.050305Z", "swap_id": "1220ade677e925f325fbcef8ded85efd4d416852015678634dd471bfd77e8f2f", "chain": "sui_testnet", "asset": "sui_testnet:sui", "initiator": "0x79a1582388c16d0ab85904f320eb0527481391a9b9ab4b2ab46adc4c2564f9d0", "redeemer": "0x3f6d9e07fd4cdabbaa7097c19ecf655809c893a9cfe3f418c3ffbc04250d3b4c", "timelock": 86400000, "filled_amount": "0", "asset_price": 3.3332479255988647, "amount": "3000000000", "secret_hash": "39512ab1a6917b3d4b4edb64d0a77c204c9fd7349290a73fce554ab70f95d3db", "secret": "", "initiate_tx_hash": "", "redeem_tx_hash": "", "refund_tx_hash": "", "initiate_block_number": "0", "redeem_block_number": "0", "refund_block_number": "0", "required_confirmations": 0, "current_confirmations": 0, "initiate_timestamp": null, "redeem_timestamp": null, "refund_timestamp": null }, "destination_swap": { "created_at": "2025-09-01T09:33:10.050305Z", "swap_id": "tb1p77xn22avz3qyu9tkm00q4rfuyz5c2960y33vde7z58xwh0zymv3sk879l4", "chain": "bitcoin_testnet", "asset": "bitcoin_testnet:btc", "initiator": "460f2e8ff81fc4e0a8e6ce7796704e3829e3e3eedb8db9390bdc51f4f04cf0a6", "redeemer": "tb1p4pr78swsn60y4ushe05v28mqpqppxxkfkxu2wun5jw6duc8unj3sjrh4gd", "delegate": "f5a577508b4b55e65d693f7c772eab767b2e22626e8a76b654b6142abc3ffd5e", "timelock": 12, "filled_amount": "0", "asset_price": 109799.0308368164, "amount": "9079", "secret_hash": "39512ab1a6917b3d4b4edb64d0a77c204c9fd7349290a73fce554ab70f95d3db", "secret": "", "initiate_tx_hash": "", "redeem_tx_hash": "", "refund_tx_hash": "", "initiate_block_number": "0", "redeem_block_number": "0", "refund_block_number": "0", "required_confirmations": 0, "current_confirmations": 0, "initiate_timestamp": null, "redeem_timestamp": null, "refund_timestamp": null }, "nonce": "9973478672661146798", "order_id": "e7a2d8b8dbf8d1e315c17e40cd9287410ea9449258f806a1833b7f9ad68527f5", "affiliate_fees": [], "integrator": "DocsTesting", "version": "v2", "solver_id": "0x90eb912279ee8a3c56f784a44dedffae4487524d" } } ``` The swap is complete once the `order.destination_swap.redeem_tx_hash` field is populated. Congratulations, you have successfully completed a cross-chain swap! # Changelog Source: https://docs.garden.finance/changelog Product updates and announcements ### Endpoint updates * Updated GET `/orders/{order}` to include `deadline` field in the order details. ### New chains * Added [Litecoin](/developers/supported-chains) support. ### Endpoint updates * Updated GET `/orders` `status` parameter to include `display` as a filter value. ### New chains * Added support for [BTC.b](/developers/supported-chains) on megaETH. * Added testnet support for [USDC](/developers/supported-chains) on HyperEVM. ### Endpoint updates * Update GET `/quote` to accept `indicative` query parameter ### Endpoint updates * Update GET `/chains` to include the `display` name and `native_asset_id` * Update GET `/liquidity` to include the solver's `address` ### Endpoint updates * Update GET `/orders` to accept `integrator` query parameter ### Endpoint updates * Added Tron and Litecoin support. * Update GET `/fee`, `/volume`, `/chains` and `/assets` endpoints to accept new query parameters * Update PATCH `/orders/:id` endpoint requires `signatures` array instead of `signature` string for `action=instant-refund` ### Endpoint updates * Update GET `/orders` to accept `solver_id` query parameter ### Endpoint updates * Update GET `/chains` to include the `id` and the `explorer_url` fields * Update GET `/assets` to include ticker symbols in the `name` field and chain identifiers in the `chain` field * Update GET `/liquidity` to include corresponding `solver_id` field for each liquidity entry * Update GET `/schemas` to return an array of schemas ### Endpoint update * Update GET `/quote` endpoint to include `fee` and `fixed_fee` * Update GET `/assets` endpoint to include `name` * Update GET `/chains` endpoint to include `name` ### New endpoint * Add `/apps/earnings` endpoint for fetching affiliate earnings ### Endpoint updates * Update POST `/orders` to use `initiate_on_behalf` calldata when the source delegate is passed ### Endpoint updates * Update GET `/orders` to include the following filters: `from_owner`, `to_owner` * Update GET `/quote` endpoint to accept `slippage` parameter ### Endpoint update * Update GET `/quote` endpoint to include `estimated_time` ### New chains * Add Core support ### New assets * Add SPL tokens support for Solana ### Approval flow improvements * Add check after approval to verify the latest allowance amount. ### Watcher improvements * Update event watch deadline for Bitcoin swaps from 1 hour to 6 hours ### New chains * Add Sui support ### API call improvements * Extend `getOrders` function with new filters (e.g. status , transaction hash) for more precise order queries. * Accept `AbortController` as a parameter in get methods of `@gardenfi/orderbook` to allow cancellation of ongoing orderbook API calls. ### Network switching improvements * Fix error handling in `switchOrAddNetwork` utility for case when network is not found. ### Local testing * Add `yarn link` support allowing developers to locally test the changes done in SDK within a working project directory. ### Affiliate fees endpoint * Add GET `/apps/earnings` to track an integrator's affiliate fees ### New chains * Add Sui support ### Order status fixes * Fix order status parsing to ensure swap initiation and initiation detection are confirmed before the attested deadline, preventing orders from being accepted after expiry. ### New chains * Add SPL tokens support for Solana ### Endpoint updates * Update GET `/orders` to include the following filters: `address`, `tx_hash`, `from_chain`, `to_chain`, `page`, `per_page`, `status` * Update GET `/chains` to include the default explorer URL ### New chains * Add BNB Chain support ### One-click swaps * Add `handleSecretManagement` flag for optional secret management. * When set to `false`, we use [one-click swaps](/developers/api/1click) * When set to `true`, we manage secrets within the SDK, and the user has to keep his session active ### New endpoint * Add PATCH `/orders/:id?action=refund` endpoint to process Bitcoin refunds ### Endpoint updates * Update GET `/assets` endpoint to return `chain_id` and fiat value * Update POST `/orders` endpoint to make Bitcoin address optional, to allow refund address updates after expiry ### Phantom wallet fixes * Finalize all inputs of a Partially Signed Bitcoin Transaction in `PhantomProvider`, fixing issues with transaction broadcasting ### Dependency update * Internalised `@catalogfi/wallets` dependency to Garden SDK ### New endpoints * Add `/schemas/{name}` endpoint for fetching specific contract schemas * Add `/schemas` endpoint for browsing available contract interfaces ### Enhanced schema types * Add support for different HTLC contract types across chains: * `evm:htlc`, `evm:htlc_erc20` * `solana:htlc`, `solana:htlc_spltoken` * `starknet:htlc`, `starknet:htlc_erc20` * Add support for blockchain-specific token standards: * `evm:erc20`, `solana:spltoken`, `starknet:erc20` * Add `supported_htlc_schemas` and `supported_token_schemas` fields to chain information * Update `htlc` and `token` fields to be objects that include schema names ### API architecture overhaul * Migrate from microservice-based endpoints to unified API at https\://\{environment}.garden.finance/v2 * Replace Bearer JWT authentication with `garden-app-id` header authentication ### New endpoints * Add `/policy` endpoint for client-side route validation and reduced API dependencies * Add `/liquidity` endpoint for real-time liquidity information ### Consolidated API structure * Unify quote functionality under `/quote` endpoint * Streamline order operations with `/orders` and `/orders/{order}` endpoints * Simplify chain and asset queries with `/chains` and `/assets` endpoints * Add `/health` endpoint for service monitoring * Integrate `/fees` and `/volume` endpoints for swap metrics ### Developer experience improvements * Enhance OpenAPI specification with detailed descriptions and examples * Improve schema definitions for better SDK generation * Standardize error response formats across all endpoints * Add Solana support * Add retry logic for transactions dropped in mempool * Fix `getBalance` utility in Phantom provider * Upgrade Starknet dependencies * Update Arbitrum Sepolia address * Add Botanix support * Add Corn support * Initialise quote using API * Automatically handle network switching in wallets * Add support for Phantom, Xverse, and Keplr wallets * Fix build issues * Exit script execution if build fails * Update script file * Add Unichain support * Remove unnecessary API calls in `react-hooks` * Support for affiliate fees * Add helper function `getQuoteFromAssets` * Add Starknet support * Add support for network switching in Node environment (previously supported in browser) # Bitcoin Source: https://docs.garden.finance/contracts/bitcoin Implement atomic swaps on Bitcoin using scripts for initiate, redeem, refund, and instant refund operations Learn about [P2TR (Pay To Taproot)](https://learnmeabitcoin.com/technical/script/p2tr/) to understand how to spend from a HTLC script. ## Script architecture Instead of contract functions, Bitcoin implements atomic swap logic through scripts that are executed when UTXOs are spent. Let's explore the four core operations: We will use the following struct for passing swap info: ```go theme={null} type HTLC struct { // X-only pubkey of the initiator InitiatorPubkey []byte // X-only pubkey of the redeemer RedeemerPubkey []byte // sha256(preimage) SecretHash []byte // expiry in blocks Timelock uint32 } ``` ## Core operations ### Initiate Initiating an atomic swap on Bitcoin involves creating a transaction that locks funds in an HTLC script. Here's what happens during initiation: The initiator creates a Bitcoin transaction that: * Spends from their wallet (input). * Creates an output that pays to the HTLC script address (derived from the HTLC parameters — pubkeys, secret hash, timelock, and destination data) The HTLC script ensures that the locked funds can only be spent if either: * The redeemer provides the correct secret and their signature (redeem path) * The initiator claims back after timelock expiry (refund path) * Both parties agree to cancel (instant refund path) The amount locked in the script is used as the swap amount. ### Redeem Redeeming an atomic swap means claiming the locked funds by providing the secret that matches the hash used during initiation. The redeem process involves: 1. Creating a transaction that spends from the HTLC script address. 2. Providing the secret that matches the hash. 3. Signing the transaction with the redeemer's private key. Let's understand this through a high-level function similar to an EVM contract: ```go theme={null} func Redeem(htlc *HTLC, secret []byte, signature []byte) { if sha256(secret) != htlc.SecretHash || recover(sig) != htlc.RedeemerPubkey { panic("script verification failed") } withdraw() } ``` This function performs two critical checks: * Secret validation: Ensures the provided secret hashes to the expected value. * Signature verification: Confirms the redeemer is authorized to claim. When translated to Bitcoin Script, we need to consider its stack-based nature. Here's how the script works: ```javascript theme={null} OP_SHA256 OP_DATA_32 {secretHash} OP_EQUALVERIFY OP_DATA_32 {redeemerPubkey} OP_CHECKSIG ``` Stack-based execution provides deterministic behavior and prevents many classes of smart contract vulnerabilities. #### Stack visualization ``` Initial State: After OP_SHA256: After Push secretHash: +----------------+ +----------------+ +----------------+ | secret | | hashed_secret | | secretHash | ← Known hash from HTLC +----------------+ +----------------+ +----------------+ | signature | | signature | | hashed_secret | ← Computed hash from provided secret +----------------+ +----------------+ +----------------+ | signature | +----------------+ After EQUALVERIFY: After Push pubkey: After CHECKSIG: +----------------+ +----------------+ +----------------+ | signature | | redeemerPubkey | | true | ← Script succeeds only if true +----------------+ +----------------+ +----------------+ | signature | +----------------+ ``` Notes: * The stack grows and shrinks from the top (top element is consumed first) * `OP_SHA256` consumes the secret and produces its hash (proves knowledge of preimage) * `OP_EQUALVERIFY` is critical — it fails immediately if the hashes don't match * Signature verification happens only if the secret is valid If all conditions are met, the script returns true and the funds can be sent to the redeemer's address. ### Refund The refund mechanism is a critical safety feature in atomic swaps that prevents funds from being locked forever if the swap fails to complete. Key concepts in refunding: * Timelock: A waiting period measured in blocks. * Relative time: Measured from when the swap was initiated. * Initiator authentication: Only the initiator can refund. The refund process involves: * Waiting for the timelock to expire * Creating a transaction that spends from the HTLC script * Providing proof of being the initiator (signature) Let's look at the high-level function: ```go theme={null} func Refund(htlc *HTLC, currentBlockHeight uint32, initiatedBlockHeight uint32, signature []byte) { if currentBlockHeight - initiateBlockHeight > htlc.Timelock { panic("not expired") } if recover(signature) != htlc.InitiatorPubkey { panic("invalid signature") } withdraw() } ``` This translates to the following Bitcoin Script: ```javascript theme={null} OP_10 OP_CHECKSEQUENCEVERIFY OP_DROP OP_DATA_32 {initiatorPubkey} OP_CHECKSIG ``` #### Stack visualization ``` Initial State: After OP_10: After CSV: +----------------+ +----------------+ +----------------+ | signature | | 10 | | 10 | +----------------+ +----------------+ +----------------+ | signature | | signature | +----------------+ +----------------+ After OP_DROP: After Push pubkey: After CHECKSIG: +----------------+ +----------------+ +----------------+ | signature | | initiatorPubkey| | true | +----------------+ +----------------+ +----------------+ | signature | +----------------+ ``` Notes: * `OP_CHECKSEQUENCEVERIFY` verifies that enough time has passed (relative to when UTXO was created). * CSV fails and prevents spending if the timelock hasn't expired. * CSV doesn't consume the timelock value, so `OP_DROP` is needed to remove it. * The final signature check ensures only the original initiator can refund. Relative timelock with CSV provides more flexibility than absolute block numbers, as it's measured from when the UTXO was created rather than a specific block height. This provides a secure way to recover funds if: * The counterparty never participates. * Network issues prevent completion. * The counterparty abandons the swap. ### Instant refund Instant refund is a cooperative cancellation mechanism that allows both parties to mutually agree to terminate the swap early, without waiting for the timelock to expire. Key concepts in instant refund: * Multi-signature: Requires both parties to agree. * No timelock: Can be executed at any time. * Mutual authentication: Both parties must provide valid signatures. The instant refund process involves creating a transaction that spends from the HTLC script address, having both parties sign the transaction, and providing both signatures in the correct order. Let's understand this through a high-level function similar to an EVM contract: ```go theme={null} func InstantRefund(htlc *HTLC, initiatorSignature []byte, redeemerSignature []byte) { if recover(initiatorSignature) != htlc.InitiatorPubkey { panic("invalid initiator signature") } if recover(redeemerSignature) != htlc.RedeemerPubkey { panic("invalid redeemer signature") } withdraw() } ``` This function performs two critical checks: * Initiator signature validation: Ensures the initiator has agreed to cancel * Redeemer signature validation: Ensures the redeemer has agreed to cancel When translated to Bitcoin Script, we need to consider its stack-based nature. Here's how the script works: ```javascript theme={null} OP_DATA_32 {initiatorPubkey} OP_CHECKSIG OP_DATA_32 {redeemerPubkey} OP_CHECKSIGADD OP_2 OP_NUMEQUAL ``` #### Stack visualization ``` Initial State: After Push initiatorPubkey: After First CHECKSIG: +----------------------+ +----------------------+ +----------------------+ | initiatorSignature | | initiatorPubkey | | 1 or 0 | +----------------------+ +----------------------+ +----------------------+ | redeemerSignature | | initiatorSignature | | redeemerSignature | +----------------------+ +----------------------+ +----------------------+ | redeemerSignature | +----------------------+ After Push redeemerPubkey: After CHECKSIGADD: After Push 2: +----------------------+ +----------------------+ +----------------------+ | redeemerPubkey | | 1+1 or 1+0 or 0+0 | | 2 | +----------------------+ +----------------------+ +----------------------+ | 1 or 0 | | 1+1 or 1+0 or 0+0 | +----------------------+ +----------------------+ | redeemerSignature | +----------------------+ After NUMEQUAL: +----------------------+ | true/false | +----------------------+ ``` Notes about `OP_CHECKSIGADD`: It consumes three stack elements: * A public key (redeemerPubkey) * A numeric value to add to (result of first CHECKSIG) * A signature to verify (redeemerSignature) It performs these operations: * Verifies if the signature is valid for the pubkey * Adds 1 (if valid) or 0 (if invalid) to the numeric value * Pushes the sum back onto the stack The final state: * Sum = 2: Both signatures valid (1+1) * Sum = 1: Only one signature valid (1+0 or 0+1) * Sum = 0: Neither signature valid (0+0) `OP_NUMEQUAL` with 2 ensures both signatures must be valid The `OP_CHECKSIGADD` operation provides efficient multi-signature verification using Tapscript's enhanced capabilities. This approach efficiently implements a 2-of-2 multisignature requirement using Tapscript's enhanced capabilities. The script ensures that both signatures are valid and match their respective public keys, confirming that both parties have mutually agreed to cancel the swap. ## Bitcoin-specific considerations ### Transaction construction * Input selection: Choose appropriate UTXOs to fund the HTLC script, considering transaction fees and optimal UTXO management. * Script address generation: Generate the script address using Taproot's address format for better privacy, efficiency and cost optimization. * Fee calculation: Calculate appropriate fees based on current network conditions and transaction size. # EVM Source: https://docs.garden.finance/contracts/evm Implement atomic swaps on EVM-compatible blockchains using smart contracts ## Contract architecture Garden uses Hashed Time Lock Contracts (HTLCs) to implement atomic swap functionality on EVM chains. The contract manages the lifecycle of a swap through four main operations with enhanced signature support and flexible initiation methods: ## Core functions ### Initiate The initiate function creates a new HTLC by locking tokens in the contract. EVM provides different implementations for ERC20 tokens vs. native ETH: #### Basic initiation ```solidity ERC20 theme={null} function initiate( address redeemer, uint256 timelock, uint256 amount, bytes32 secretHash ) external ``` ```solidity Native ETH theme={null} function initiate( address payable redeemer, uint256 timelock, uint256 amount, bytes32 secretHash ) external payable ``` #### Initiation on behalf ```solidity ERC20 theme={null} function initiateOnBehalf( address initiator, address redeemer, uint256 timelock, uint256 amount, bytes32 secretHash ) external ``` ```solidity Native ETH theme={null} function initiateOnBehalf( address payable initiator, address payable redeemer, uint256 timelock, uint256 amount, bytes32 secretHash ) external payable ``` #### Signature-based initiation (ERC20 only) ```solidity theme={null} function initiateWithSignature( address initiator, address redeemer, uint256 timelock, uint256 amount, bytes32 secretHash, bytes calldata signature ) external ``` Uses EIP712 signatures for off-chain authorization, enabling gasless transactions where authorized third parties can initiate swaps on behalf of users. ### Redeem The redeem function allows the redeemer to claim the locked tokens by providing the secret that hashes to the stored secret hash. ```solidity ERC20 theme={null} function redeem( bytes32 orderID, bytes calldata secret ) external ``` ```solidity Native ETH theme={null} function redeem( bytes32 orderID, bytes calldata secret ) external ``` The secret must hash to the exact value stored during initiation using SHA256. Once revealed, this secret enables the counterparty to claim funds on the other chain. No signature required - anyone can execute if they know the secret. ### Refund The refund function allows the initiator to reclaim their tokens after the timelock has expired and the redeemer has not claimed the funds. ```solidity ERC20 theme={null} function refund(bytes32 orderID) external ``` ```solidity Native ETH theme={null} function refund(bytes32 orderID) external ``` Uses absolute block numbers for timelock, which provides predictable settlement windows based on consistent block production times. ### Instant refund The instant refund function provides a way for the redeemer to consent to canceling the swap before the timelock expires using EIP712 signatures. ```solidity ERC20 theme={null} function instantRefund( bytes32 orderID, bytes calldata signature ) external ``` ```solidity Native ETH theme={null} function instantRefund( bytes32 orderID, bytes calldata signature ) external ``` This requires the redeemer's EIP712 signature to prevent unauthorized instant refunds. This ensures mutual consent before the settlement window expires. ## EVM-specific features ### Order state management Both contracts use a struct to store swap state with different address types for native vs. ERC20 handling: ```solidity ERC20 theme={null} struct Order { address initiator; address redeemer; uint256 initiatedAt; uint256 timelock; uint256 amount; uint256 fulfilledAt; } ``` ```solidity Native ETH theme={null} struct Order { address payable initiator; address payable redeemer; uint256 initiatedAt; uint256 timelock; uint256 amount; uint256 fulfilledAt; } ``` ### Token handling differences **ERC20 implementation:** * Uses SafeERC20 for secure token transfers. * Requires token approval before initiation. * Transfers tokens via `transferFrom` and `transfer`. **Native ETH implementation:** * Uses `payable` functions and `msg.value`. * Direct ETH transfers via `.transfer()`. * No approval required. The native ETH version simplifies the user experience by eliminating the need for token approvals, while the ERC20 version provides compatibility with all standard tokens. ### EIP712 signature support Both contracts implement EIP712 for secure off-chain message signing: ```solidity theme={null} bytes32 private constant _REFUND_TYPEHASH = keccak256("Refund(bytes32 orderId)"); function instantRefundDigest(bytes32 orderID) public view returns (bytes32) { return _hashTypedDataV4(keccak256(abi.encode(_REFUND_TYPEHASH, orderID))); } ``` ### Event logging Both contracts emit identical events for each state transition to enable efficient off-chain monitoring: ```solidity theme={null} event Initiated(bytes32 indexed orderID); event Redeemed(bytes32 indexed orderID, bytes secret); event Refunded(bytes32 indexed orderID); ``` ### Order ID generation Unique order identifiers are generated using SHA256 hashing with chain-specific parameters: ```solidity theme={null} bytes32 orderID = sha256(abi.encode( block.chainid, secretHash, initiator, redeemer, timelock, amount )); ``` Chain ID inclusion prevents cross-chain replay attacks, while the parameter combination ensures each order is uniquely identifiable across the network. # HyperCore Source: https://docs.garden.finance/contracts/hypercore Atomic swap support for HyperCore users via a modified HTLC on HyperEVM **The HTLC contract used for HyperCore swaps lives entirely on HyperEVM — not on HyperCore.** HyperCore is supported as a chain through Garden's integration with HyperEVM. No separate contract is deployed on HyperCore itself; what makes this integration unique is the modified `redeem` function that automatically settles funds into a user's HyperCore spot account. ## Overview HyperCore users can send and receive assets via atomic swaps through a modified HTLC deployed on HyperEVM. The key enhancement is HyperCore-native settlement on redemption: when `redeem()` is called, the contract routes funds directly to the redeemer's HyperCore spot account via the CoreWriter precompile — no separate bridge or withdrawal step is needed. ## Network details | Network | Chain ID | | ---------------- | -------- | | HyperEVM Mainnet | `999` | | HyperEVM Testnet | `998` | All contract interactions and EIP-712 signing use the HyperEVM chain ID. There is no separate HyperCore chain ID from the integration side. ## CoreWriter precompile The CoreWriter precompile at `0x3333333333333333333333333333333333333333` enables the HTLC to trigger HyperCore L1 actions directly from HyperEVM. It is used for two purposes: * **Inbound (X → HyperCore):** After `redeem()` is called, the contract calls `spotSend` via CoreWriter to credit the asset directly to the redeemer's HyperCore spot balance. * **Outbound (HyperCore → X):** Before initiating the HTLC, the user calls `sendRawAction` via CoreWriter to move assets from their HyperCore spot account to HyperEVM. For a deeper look at how HyperEVM interacts with HyperCore — including available precompile actions, L1 state reads, and block timing — see the [official Hyperliquid docs on interacting with HyperCore](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/hyperevm/interacting-with-hypercore). ## Contract architecture Garden uses Hashed Time Lock Contracts (HTLCs) for atomic swaps. The HyperCore HTLC shares the same interface as the standard EVM HTLC, with the `redeem` function modified to auto-settle into HyperCore spot. ## Core functions ### Initiate Locks ERC20 tokens (e.g. USDC) in the contract on HyperEVM. Two modes are supported: #### Direct initiation ```solidity theme={null} function initiate( address redeemer, uint256 timelock, uint256 amount, bytes32 secretHash ) external ``` #### Signature-based initiation (gasless) ```solidity theme={null} function initiateWithSignature( address initiator, address redeemer, uint256 timelock, uint256 amount, bytes32 secretHash, bytes calldata signature ) external ``` With gasless initiation, the user signs an EIP-712 `Initiate` message off-chain and hands it to the solver. The solver calls `initiateWithSignature` on the user's behalf — the user pays no gas for HTLC initiation. ### Redeem The redeem function is modified from the standard EVM HTLC to settle funds directly into the redeemer's **HyperCore spot account**. ```solidity theme={null} function redeem( bytes32 orderID, bytes calldata secret ) external ``` When called, the contract: 1. Verifies the secret against the stored SHA-256 hash. 2. Transfers the token to its system address (`0x2000...0000`), triggering a HyperCore deposit. 3. Calls `spotSend(redeemer, tokenIndex, amount)` via the CoreWriter precompile — the asset lands in the redeemer's HyperCore spot balance. The HyperCore `spotSend` transaction hash is treated as the final settlement proof. No additional signing or bridging is required after redemption. ### Refund Allows the initiator to reclaim locked tokens after the timelock has expired. For HyperCore swaps, the modified `refund()` routes funds back to the initiator's **HyperCore spot account** — no manual bridging back is needed. ```solidity theme={null} function refund(bytes32 orderID) external ``` When called on a HyperCore order, the contract: 1. Verifies the timelock has expired. 2. Transfers the token to its system address (`0x2000...0000`), triggering a HyperCore deposit. 3. Calls `spotSend(initiator, tokenIndex, amount)` via the CoreWriter precompile — the asset is returned directly to the initiator's HyperCore spot balance. Uses absolute block numbers for the timelock, consistent with the standard EVM HTLC. ### Instant refund Allows the swap to be cancelled before the timelock expires, provided the redeemer consents via an EIP-712 signature. ```solidity theme={null} function instantRefund( bytes32 orderID, bytes calldata signature ) external ``` Requires the redeemer's signature to prevent unauthorized cancellations. This ensures both parties consent before the settlement window expires. ## EIP-712 signing ### Initiation domain For gasless initiation, the user signs an EIP-712 `Initiate` message: ```json theme={null} Domain: { name: "HTLC", version: "3", chainId: 999 } Type: Initiate(address redeemer, uint256 timelock, uint256 amount, bytes32 secretHash) ``` ### Instant refund digest ```solidity theme={null} bytes32 private constant _REFUND_TYPEHASH = keccak256("Refund(bytes32 orderId)"); function instantRefundDigest(bytes32 orderID) public view returns (bytes32) { return _hashTypedDataV4(keccak256(abi.encode(_REFUND_TYPEHASH, orderID))); } ``` ## Event logging Both state transitions and the final HyperCore settlement emit standard events for off-chain monitoring: ```solidity theme={null} event Initiated(bytes32 indexed orderID); event Redeemed(bytes32 indexed orderID, bytes secret); event Refunded(bytes32 indexed orderID); ``` ## Order ID generation ```solidity theme={null} bytes32 orderID = sha256(abi.encode( block.chainid, secretHash, initiator, redeemer, timelock, amount )); ``` Chain ID inclusion prevents cross-chain replay attacks, while the parameter combination ensures each order is uniquely identifiable. # Litecoin Source: https://docs.garden.finance/contracts/litecoin Implement atomic swaps on Litecoin using scripts for initiate, redeem, refund, and instant refund operations Litecoin uses the same script architecture as Bitcoin. The implementation details in this guide are identical to the [Bitcoin guide](/contracts/bitcoin), as Litecoin maintains full compatibility with Bitcoin's scripting system. Learn about [P2TR (Pay To Taproot)](https://learnmeabitcoin.com/technical/script/p2tr/) to understand how to spend from a HTLC script. ## Script architecture Instead of contract functions, Litecoin implements atomic swap logic through scripts that are executed when UTXOs are spent. Let's explore the four core operations: We will use the following struct for passing swap info: ```go theme={null} type HTLC struct { // X-only pubkey of the initiator InitiatorPubkey []byte // X-only pubkey of the redeemer RedeemerPubkey []byte // sha256(preimage) SecretHash []byte // expiry in blocks Timelock uint32 } ``` ## Core operations ### Initiate Initiating an atomic swap on Litecoin involves creating a transaction that locks funds in an HTLC script. Here's what happens during initiation: The initiator creates a Litecoin transaction that: * Spends from their wallet (input). * Creates an output that pays to the HTLC script address (derived from the HTLC parameters — pubkeys, secret hash, timelock, and destination data) The HTLC script ensures that the locked funds can only be spent if either: * The redeemer provides the correct secret and their signature (redeem path) * The initiator claims back after timelock expiry (refund path) * Both parties agree to cancel (instant refund path) The amount locked in the script is used as the swap amount. ### Redeem Redeeming an atomic swap means claiming the locked funds by providing the secret that matches the hash used during initiation. The redeem process involves: 1. Creating a transaction that spends from the HTLC script address. 2. Providing the secret that matches the hash. 3. Signing the transaction with the redeemer's private key. Let's understand this through a high-level function similar to an EVM contract: ```go theme={null} func Redeem(htlc *HTLC, secret []byte, signature []byte) { if sha256(secret) != htlc.SecretHash || recover(sig) != htlc.RedeemerPubkey { panic("script verification failed") } withdraw() } ``` This function performs two critical checks: * Secret validation: Ensures the provided secret hashes to the expected value. * Signature verification: Confirms the redeemer is authorized to claim. When translated to Litecoin Script, we need to consider its stack-based nature. Here's how the script works: ```javascript theme={null} OP_SHA256 OP_DATA_32 {secretHash} OP_EQUALVERIFY OP_DATA_32 {redeemerPubkey} OP_CHECKSIG ``` Stack-based execution provides deterministic behavior and prevents many classes of smart contract vulnerabilities. #### Stack visualization ``` Initial State: After OP_SHA256: After Push secretHash: +----------------+ +----------------+ +----------------+ | secret | | hashed_secret | | secretHash | ← Known hash from HTLC +----------------+ +----------------+ +----------------+ | signature | | signature | | hashed_secret | ← Computed hash from provided secret +----------------+ +----------------+ +----------------+ | signature | +----------------+ After EQUALVERIFY: After Push pubkey: After CHECKSIG: +----------------+ +----------------+ +----------------+ | signature | | redeemerPubkey | | true | ← Script succeeds only if true +----------------+ +----------------+ +----------------+ | signature | +----------------+ ``` Notes: * The stack grows and shrinks from the top (top element is consumed first) * `OP_SHA256` consumes the secret and produces its hash (proves knowledge of preimage) * `OP_EQUALVERIFY` is critical — it fails immediately if the hashes don't match * Signature verification happens only if the secret is valid If all conditions are met, the script returns true and the funds can be sent to the redeemer's address. ### Refund The refund mechanism is a critical safety feature in atomic swaps that prevents funds from being locked forever if the swap fails to complete. Key concepts in refunding: * Timelock: A waiting period measured in blocks. * Relative time: Measured from when the swap was initiated. * Initiator authentication: Only the initiator can refund. The refund process involves: * Waiting for the timelock to expire * Creating a transaction that spends from the HTLC script * Providing proof of being the initiator (signature) Let's look at the high-level function: ```go theme={null} func Refund(htlc *HTLC, currentBlockHeight uint32, initiatedBlockHeight uint32, signature []byte) { if currentBlockHeight - initiateBlockHeight > htlc.Timelock { panic("not expired") } if recover(signature) != htlc.InitiatorPubkey { panic("invalid signature") } withdraw() } ``` This translates to the following Litecoin Script: ```javascript theme={null} OP_10 OP_CHECKSEQUENCEVERIFY OP_DROP OP_DATA_32 {initiatorPubkey} OP_CHECKSIG ``` #### Stack visualization ``` Initial State: After OP_10: After CSV: +----------------+ +----------------+ +----------------+ | signature | | 10 | | 10 | +----------------+ +----------------+ +----------------+ | signature | | signature | +----------------+ +----------------+ After OP_DROP: After Push pubkey: After CHECKSIG: +----------------+ +----------------+ +----------------+ | signature | | initiatorPubkey| | true | +----------------+ +----------------+ +----------------+ | signature | +----------------+ ``` Notes: * `OP_CHECKSEQUENCEVERIFY` verifies that enough time has passed (relative to when UTXO was created). * CSV fails and prevents spending if the timelock hasn't expired. * CSV doesn't consume the timelock value, so `OP_DROP` is needed to remove it. * The final signature check ensures only the original initiator can refund. Relative timelock with CSV provides more flexibility than absolute block numbers, as it's measured from when the UTXO was created rather than a specific block height. This provides a secure way to recover funds if: * The counterparty never participates. * Network issues prevent completion. * The counterparty abandons the swap. ### Instant refund Instant refund is a cooperative cancellation mechanism that allows both parties to mutually agree to terminate the swap early, without waiting for the timelock to expire. Key concepts in instant refund: * Multi-signature: Requires both parties to agree. * No timelock: Can be executed at any time. * Mutual authentication: Both parties must provide valid signatures. The instant refund process involves creating a transaction that spends from the HTLC script address, having both parties sign the transaction, and providing both signatures in the correct order. Let's understand this through a high-level function similar to an EVM contract: ```go theme={null} func InstantRefund(htlc *HTLC, initiatorSignature []byte, redeemerSignature []byte) { if recover(initiatorSignature) != htlc.InitiatorPubkey { panic("invalid initiator signature") } if recover(redeemerSignature) != htlc.RedeemerPubkey { panic("invalid redeemer signature") } withdraw() } ``` This function performs two critical checks: * Initiator signature validation: Ensures the initiator has agreed to cancel * Redeemer signature validation: Ensures the redeemer has agreed to cancel When translated to Litecoin Script, we need to consider its stack-based nature. Here's how the script works: ```javascript theme={null} OP_DATA_32 {initiatorPubkey} OP_CHECKSIG OP_DATA_32 {redeemerPubkey} OP_CHECKSIGADD OP_2 OP_NUMEQUAL ``` #### Stack visualization ``` Initial State: After Push initiatorPubkey: After First CHECKSIG: +----------------------+ +----------------------+ +----------------------+ | initiatorSignature | | initiatorPubkey | | 1 or 0 | +----------------------+ +----------------------+ +----------------------+ | redeemerSignature | | initiatorSignature | | redeemerSignature | +----------------------+ +----------------------+ +----------------------+ | redeemerSignature | +----------------------+ After Push redeemerPubkey: After CHECKSIGADD: After Push 2: +----------------------+ +----------------------+ +----------------------+ | redeemerPubkey | | 1+1 or 1+0 or 0+0 | | 2 | +----------------------+ +----------------------+ +----------------------+ | 1 or 0 | | 1+1 or 1+0 or 0+0 | +----------------------+ +----------------------+ | redeemerSignature | +----------------------+ After NUMEQUAL: +----------------------+ | true/false | +----------------------+ ``` Notes about `OP_CHECKSIGADD`: It consumes three stack elements: * A public key (redeemerPubkey) * A numeric value to add to (result of first CHECKSIG) * A signature to verify (redeemerSignature) It performs these operations: * Verifies if the signature is valid for the pubkey * Adds 1 (if valid) or 0 (if invalid) to the numeric value * Pushes the sum back onto the stack The final state: * Sum = 2: Both signatures valid (1+1) * Sum = 1: Only one signature valid (1+0 or 0+1) * Sum = 0: Neither signature valid (0+0) `OP_NUMEQUAL` with 2 ensures both signatures must be valid The `OP_CHECKSIGADD` operation provides efficient multi-signature verification using Tapscript's enhanced capabilities. This approach efficiently implements a 2-of-2 multisignature requirement using Tapscript's enhanced capabilities. The script ensures that both signatures are valid and match their respective public keys, confirming that both parties have mutually agreed to cancel the swap. ## Litecoin-specific considerations ### Transaction construction * Input selection: Choose appropriate UTXOs to fund the HTLC script, considering transaction fees and optimal UTXO management. * Script address generation: Generate the script address using Taproot's address format for better privacy, efficiency and cost optimization. * Fee calculation: Calculate appropriate fees based on current network conditions and transaction size. # HTLCs Source: https://docs.garden.finance/contracts/overview Atomic swaps enable secure cross-chain swaps using Hashed Time Lock Contracts (HTLCs) HTLCs ensure that either both parties complete the swap or both get their funds back — no party can be cheated. ## Universal concepts Regardless of the blockchain, all atomic swap implementations share these core methods: Start the cross-chain swap by depositing funds into the HTLC on the source chain. Complete the cross-chain swap by redeeming funds from the HTLC on the destination chain. Get your funds refunded on the source chain, if swap does not happen within the settlement window. Cancel the swap and get an instant refund on the source chain during the settlement window. ## Security features Any chain that supports SHA256 hashing and Relative Timelocks can be added to Garden. Funds can always be recovered if the counterparty doesn't participate within the specified timeframe. Only designated parties can claim funds, preventing unauthorized access. The swap either completes fully on both chains or fails completely. ## Cross-chain coordination The preimage revealed when redeeming on one chain can be used to claim funds on the other chain. This creates the *atomic* property: 1. **Initiator** locks funds on Chain A with `sha256(preimage)` 2. **Redeemer** locks funds on Chain B with the same `sha256(preimage)` 3. **Initiator** redeems on Chain B by revealing the preimage 4. **Redeemer** uses the revealed preimage to claim funds on Chain A Both implementations use the same cryptographic primitives and security model, but differ in their execution environments and technical approaches. # Solana Source: https://docs.garden.finance/contracts/solana Implement atomic swaps on Solana using Anchor programs and PDAs ## Contract architecture Garden uses Hashed Time Lock Contracts (HTLCs) to implement atomic swap functionality on Solana. The program manages the lifecycle of a swap through four main operations using Program Derived Addresses (PDAs) for state management: ## Core functions ### Initiate The initiate function creates a new HTLC by transferring SOL to a PDA vault. It requires: * Secret hash: SHA256 hash of a preimage known only to the initiator. * Timelock: Number of slots after which refund is possible (1 slot ≈ 400ms). * Redeemer: Public key authorized to claim the funds. * Amount: Quantity of native SOL in lamports (1 SOL = 1,000,000,000 lamports). ```rust theme={null} pub fn initiate( ctx: Context, amount_lamports: u64, expires_in_slots: u64, redeemer: Pubkey, secret_hash: [u8; 32], ) -> Result<()> ``` Uses PDAs as deterministic vaults, ensuring each swap with the same initiator and secret hash is unique and prevents replay attacks. ### Redeem The redeem function allows the redeemer to claim the locked SOL by providing the secret that hashes to the stored secret hash. ```rust theme={null} pub fn redeem( ctx: Context, secret: [u8; 32] ) -> Result<()> ``` The secret must hash to the exact value stored during initiation. Once revealed, this secret enables the counterparty to claim funds on the other chain. No signature required — anyone can execute if they know the secret. ### Refund The refund function allows the initiator to reclaim their SOL after the timelock has expired and the redeemer has not claimed the funds. ```rust theme={null} pub fn refund(ctx: Context) -> Result<()> ``` Uses slot-based timing which provides more granular control than Bitcoin's block-based system. Each slot represents approximately 400ms. ### Instant refund The instant refund function provides a way for the redeemer to consent to canceling the swap before the timelock expires. ```rust theme={null} pub fn instant_refund(ctx: Context) -> Result<()> ``` Requires the redeemer's signature to prevent unauthorized instant refunds. This ensures mutual consent before the settlement window expires. ## Solana-specific features ### PDA state management The program uses a PDA (Program Derived Address) to store swap state, derived from deterministic seeds: ```rust theme={null} #[account] #[derive(InitSpace)] pub struct SwapAccount { amount_lamports: u64, // SOL amount in base units expiry_slot: u64, // Absolute slot for expiry initiator: Pubkey, // Initiator's public key redeemer: Pubkey, // Redeemer's public key secret_hash: [u8; 32], // SHA256 hash of secret } ``` ### Account constraints Anchor provides compile-time account validation and runtime constraints: ```rust theme={null} #[account( init, payer = initiator, seeds = [b"swap_account", initiator.key().as_ref(), &secret_hash], bump, space = ANCHOR_DISCRIMINATOR + SwapAccount::INIT_SPACE, )] pub swap_account: Account<'info, SwapAccount>, ``` The seed structure ensures that each swap is uniquely identified by the initiator and secret hash, preventing duplicate swaps until completion. ### Event logging The program emits events for each state transition to enable efficient off-chain monitoring: ```rust theme={null} #[event] pub struct Initiated { pub swap_amount: u64, pub expires_in_slots: u64, pub initiator: Pubkey, pub redeemer: Pubkey, pub secret_hash: [u8; 32], } #[event] pub struct Redeemed { pub initiator: Pubkey, pub secret: [u8; 32], } ``` ### Rent management Solana's rent system is automatically handled through Anchor's `close` attribute: ```rust theme={null} #[account(mut, close = initiator)] pub swap_account: Account<'info, SwapAccount>, ``` When a swap completes (redeem/refund), the PDA is closed and rent is automatically refunded to the initiator, ensuring no SOL is permanently locked. ### Error handling Custom error types provide clear feedback for failed operations: ```rust theme={null} #[error_code] pub enum SwapError { #[msg("The provided initiator is not the original initiator of this swap")] InvalidInitiator, #[msg("The provided redeemer is not the original redeemer of this swap")] InvalidRedeemer, #[msg("The provided secret does not correspond to the secret hash of this swap")] InvalidSecret, #[msg("Attempt to perform a refund before expiry time")] RefundBeforeExpiry, } ``` # Starknet Source: https://docs.garden.finance/contracts/starknet Implement atomic swaps on Starknet using Cairo contracts and SNIP-12 signatures ## Contract architecture Garden uses Hashed Time Lock Contracts (HTLCs) to implement atomic swap functionality on Starknet. The contract manages the lifecycle of a swap through four main operations with enhanced signature support and flexible initiation methods: ## Core functions ### Initiate The initiate function creates a new HTLC by locking ERC20 tokens in the contract. Starknet provides three flexible initiation methods: #### Basic initiation ```rust theme={null} fn initiate( ref self: ContractState, redeemer: ContractAddress, timelock: u128, amount: u256, secret_hash: [u32; 8], ) ``` #### Initiation on behalf ```rust theme={null} fn initiate_on_behalf( ref self: ContractState, initiator: ContractAddress, redeemer: ContractAddress, timelock: u128, amount: u256, secret_hash: [u32; 8], ) ``` #### Signature-based initiation ```rust theme={null} fn initiate_with_signature( ref self: ContractState, initiator: ContractAddress, redeemer: ContractAddress, timelock: u128, amount: u256, secret_hash: [u32; 8], signature: Array, ) ``` Uses SNIP-12 signatures for off-chain authorization, enabling gasless transactions where authorized third parties can initiate swaps on behalf of users. ### Redeem The redeem function allows the redeemer to claim the locked tokens by providing the secret that hashes to the stored secret hash. ```rust theme={null} fn redeem( ref self: ContractState, order_id: felt252, secret: Array ) -> Result<()> ``` The secret must hash to the exact value stored during initiation using SHA256. Once revealed, this secret enables the counterparty to claim funds on the other chain. No signature required — anyone can execute if they know the secret. ### Refund The refund function allows the initiator to reclaim their tokens after the timelock has expired and the redeemer has not claimed the funds. ```rust theme={null} fn refund( ref self: ContractState, order_id: felt252 ) ``` Starknet uses block number-based timing similar to Ethereum, providing predictable settlement windows based on Starknet's consistent block production. ### Instant refund The instant refund function provides a way for the redeemer to consent to canceling the swap before the timelock expires using SNIP-12 signatures. ```rust theme={null} fn instant_refund( ref self: ContractState, order_id: felt252, signature: Array ) ``` This requires the redeemer's SNIP-12 signature to prevent unauthorized instant refunds. This ensures mutual consent before the settlement window expires. ## Starknet-specific features ### Order state management The contract uses a Cairo struct to store swap state with efficient storage patterns: ```rust theme={null} #[derive(Drop, Serde, starknet::Store, Debug)] pub struct Order { is_fulfilled: bool, initiator: ContractAddress, redeemer: ContractAddress, initiated_at: u128, timelock: u128, amount: u256, } ``` ### Poseidon-based order IDs Starknet uses the Poseidon hash function for generating unique order identifiers: ```rust theme={null} fn generate_order_id( self: @ContractState, chain_id: felt252, secret_hash: [u32; 8], initiator_address: felt252, ) -> felt252 { let mut state = PoseidonTrait::new(); state = state.update(chain_id); state = state.update_with(secret_hash); state = state.update(initiator_address); state.finalize() } ``` Poseidon hashing is optimized for zero-knowledge proofs and provides efficient computation on Starknet while ensuring order uniqueness. ### SNIP-12 signature validation The contract implements SNIP-12 (Starknet Improvement Proposal 12) for secure off-chain message signing: ```rust theme={null} let is_valid = ISRC6Dispatcher { contract_address: initiator } .is_valid_signature(message_hash, signature); let is_valid_signature = is_valid == starknet::VALIDATED || is_valid == 1; assert!(is_valid_signature, "HTLC: invalid initiator signature"); ``` ### Event logging The contract emits events for each state transition to enable efficient off-chain monitoring: ```rust theme={null} #[event] #[derive(Drop, starknet::Event)] pub enum Event { Initiated: Initiated, Redeemed: Redeemed, Refunded: Refunded, } #[derive(Drop, starknet::Event)] pub struct Initiated { order_id: felt252, secret_hash: [u32; 8], amount: u256, } ``` ### ERC20 token integration Unlike native currency swaps, Starknet HTLC works with ERC20 tokens: ```rust theme={null} #[storage] struct Storage { pub token: IERC20Dispatcher, pub orders: Map::, } ``` The contract uses OpenZeppelin's ERC20 interface for secure token transfers with proper allowance and balance checks. ### Gas optimization Starknet's unique fee model enables several optimizations: ```rust theme={null} fn safe_params( self: @ContractState, redeemer: ContractAddress, timelock: u128, amount: u256, ) { assert!(redeemer.is_non_zero(), "HTLC: zero address redeemer"); assert!(timelock > 0, "HTLC: zero timelock"); assert!(amount > 0, "HTLC: zero amount"); } ``` Parameter validation is grouped into a single function to optimize for Starknet's execution model while maintaining security. # Sui Source: https://docs.garden.finance/contracts/sui Implement atomic swaps on Sui using Move contracts with object-based architecture and registry pattern ## Contract architecture Garden uses Hashed Time Lock Contracts (HTLCs) to implement atomic swap functionality on Sui. The contract leverages Sui's object model with a shared registry pattern for efficient order management: ## Core functions ### Initiate The initiate function creates a new HTLC by locking coins in a shared registry. Sui provides two initiation methods: #### Basic initiation ```move theme={null} public fun initiate( orders_reg: &mut OrdersRegistry, redeemer_pubk: vector, secret_hash: vector, amount: u64, timelock: u256, coins: Coin, clock: &Clock, ctx: &mut TxContext, ) ``` #### Initiation on behalf ```move theme={null} public fun initiate_on_behalf( orders_reg: &mut OrdersRegistry, initiator: address, redeemer_pubk: vector, secret_hash: vector, amount: u64, timelock: u256, coins: Coin, clock: &Clock, ctx: &mut TxContext, ) ``` Sui uses Ed25519 public keys for redeemers instead of addresses, providing more flexibility in key management and enabling advanced signature schemes. ### Redeem The redeem function allows the redeemer to claim the locked tokens by providing the secret that hashes to the stored secret hash. ```move theme={null} public fun redeem_swap( orders_reg: &mut OrdersRegistry, order_id: vector, secret: vector, ctx: &mut TxContext, ) ``` The secret must hash to the exact value stored during initiation using SHA256. Once revealed, this secret enables the counterparty to claim funds on the other chain. No signature required - anyone can execute if they know the secret. ### Refund The refund function allows the initiator to reclaim their tokens after the timelock has expired and the redeemer has not claimed the funds. ```move theme={null} public fun refund_swap( orders_reg: &mut OrdersRegistry, order_id: vector, clock: &Clock, ctx: &mut TxContext, ) ``` Uses absolute timestamps in milliseconds for timelock, providing more granular control compared to block-based systems. ### Instant refund The instant refund function provides a way for the redeemer to consent to canceling the swap before the timelock expires using Ed25519 signatures. ```move theme={null} public fun instant_refund( orders_reg: &mut OrdersRegistry, order_id: vector, signature: vector, ctx: &mut TxContext, ) ``` This requires the redeemer's Ed25519 signature to prevent unauthorized instant refunds. This ensures mutual consent before the settlement window expires. ## Sui-specific features ### Order state management The contract uses Move structs to store swap state with Sui's object model: ```move theme={null} public struct Order has key, store { id: UID, is_fulfilled: bool, initiator: address, redeemer_pubk: vector, amount: u64, initiated_at: u256, coins: Coin, timelock: u256, } ``` ### Registry pattern Sui's shared object model enables efficient order management through a registry: ```move theme={null} public struct OrdersRegistry has key, store { id: UID, } ``` The registry uses dynamic fields to store orders, allowing unlimited scalability while maintaining efficient access patterns. ### Token handling **Native Coin objects:** * Orders store coins directly as `Coin` objects. * Built-in safety guarantees prevent accidental coin loss. * Uses `transfer::public_transfer` for secure transfers. * No manual balance tracking required. Sui's Coin objects provide superior safety compared to manual balance management, as coins cannot be accidentally lost or duplicated due to Move's resource type system. ### Address generation Sui generates addresses from Ed25519 public keys using a specific scheme: ```move theme={null} fun gen_addr(pubk: vector): address { // 0x00 = ED25519, 0x01 = Secp256k1, 0x02 = Secp256r1, 0x03 = multiSig let flag: u8 = 0; let mut preimage = vector::empty(); vector::push_back(&mut preimage, flag); vector::append(&mut preimage, pubk); let addr = blake2b256(&preimage); address::from_bytes(addr) } ``` ### Event logging The contract emits events for each state transition to enable efficient off-chain monitoring: ```move theme={null} public struct Initiated has copy, drop { order_id: vector, secret_hash: vector, amount: u64, } public struct Redeemed has copy, drop { order_id: vector, secret_hash: vector, secret: vector, } public struct Refunded has copy, drop { order_id: vector, } ``` ### Order ID generation Unique order identifiers are generated using SHA256 hashing with chain-specific parameters: ```move theme={null} fun create_order_id( secret_hash: vector, initiator: address, redeemer: address, timelock: u256, amount: u64, ): vector ``` Chain ID inclusion prevents cross-chain replay attacks, while the parameter combination ensures each order is uniquely identifiable across the network. # Tron Source: https://docs.garden.finance/contracts/tron Implement atomic swaps on Tron using EVM-compatible smart contracts for TRC20 tokens ## Contract architecture Tron uses EVM-compatible smart contracts for atomic swaps. The implementation is identical to the [EVM guide](/contracts/evm), but currently **only supports TRC20 tokens** (native TRX is not supported yet). Garden uses Hashed Time Lock Contracts (HTLCs) to implement atomic swap functionality on Tron. The contract manages the lifecycle of a swap through four main operations with enhanced signature support and flexible initiation methods: ## Core functions ### Initiate The initiate function creates a new HTLC by locking TRC20 tokens in the contract: #### Basic initiation ```solidity TRC20 theme={null} function initiate( address redeemer, uint256 timelock, uint256 amount, bytes32 secretHash ) external ``` #### Initiation on behalf ```solidity TRC20 theme={null} function initiateOnBehalf( address initiator, address redeemer, uint256 timelock, uint256 amount, bytes32 secretHash ) external ``` #### Signature-based initiation ```solidity theme={null} function initiateWithSignature( address initiator, address redeemer, uint256 timelock, uint256 amount, bytes32 secretHash, bytes calldata signature ) external ``` Uses EIP712 signatures for off-chain authorization, enabling gasless transactions where authorized third parties can initiate swaps on behalf of users. ### Redeem The redeem function allows the redeemer to claim the locked TRC20 tokens by providing the secret that hashes to the stored secret hash. ```solidity TRC20 theme={null} function redeem( bytes32 orderID, bytes calldata secret ) external ``` The secret must hash to the exact value stored during initiation using SHA256. Once revealed, this secret enables the counterparty to claim funds on the other chain. No signature required - anyone can execute if they know the secret. ### Refund The refund function allows the initiator to reclaim their TRC20 tokens after the timelock has expired and the redeemer has not claimed the funds. ```solidity TRC20 theme={null} function refund(bytes32 orderID) external ``` Uses absolute block numbers for timelock, which provides predictable settlement windows based on consistent block production times. ### Instant refund The instant refund function provides a way for the redeemer to consent to canceling the swap before the timelock expires using EIP712 signatures. ```solidity TRC20 theme={null} function instantRefund( bytes32 orderID, bytes calldata signature ) external ``` This requires the redeemer's EIP712 signature to prevent unauthorized instant refunds. This ensures mutual consent before the settlement window expires. ## Tron-specific features ### Order state management The contract uses a struct to store swap state: ```solidity TRC20 theme={null} struct Order { address initiator; address redeemer; uint256 initiatedAt; uint256 timelock; uint256 amount; uint256 fulfilledAt; } ``` ### Token handling **TRC20 implementation:** * Uses SafeERC20 for secure token transfers. * Requires token approval before initiation. * Transfers tokens via `transferFrom` and `transfer`. Native TRX transfers are not currently supported. Only TRC20 tokens can be used for atomic swaps on Tron. ### EIP712 signature support The contract implements EIP712 for secure off-chain message signing: ```solidity theme={null} bytes32 private constant _REFUND_TYPEHASH = keccak256("Refund(bytes32 orderId)"); function instantRefundDigest(bytes32 orderID) public view returns (bytes32) { return _hashTypedDataV4(keccak256(abi.encode(_REFUND_TYPEHASH, orderID))); } ``` ### Event logging The contract emits events for each state transition to enable efficient off-chain monitoring: ```solidity theme={null} event Initiated(bytes32 indexed orderID); event Redeemed(bytes32 indexed orderID, bytes secret); event Refunded(bytes32 indexed orderID); ``` ### Order ID generation Unique order identifiers are generated using SHA256 hashing with chain-specific parameters: ```solidity theme={null} bytes32 orderID = sha256(abi.encode( block.chainid, secretHash, initiator, redeemer, timelock, amount )); ``` Chain ID inclusion prevents cross-chain replay attacks, while the parameter combination ensures each order is uniquely identifiable across the network. # Affiliate Fees Source: https://docs.garden.finance/developers/affiliate-fees Implement affiliate fees to earn revenue from Garden swaps through your integration Garden allows partners to charge an affiliate fee for each swap initiated through their SDK or API integration. This fee must be specified when requesting a quote and is charged in addition to protocol and solver fees. Fees are expressed in basis points (bps), where 1 bps = 0.01%. For example, a 30 bps fee equals 0.3% of the source asset value. Affiliates can earn rewards in USDC or cbBTC on [supported chains](/developers/supported-chains). Fees can be sent entirely to a single address in one asset, or split across multiple addresses and assets. For example, a 30 bps fee could be split by sending 10 bps in USDC to an Ethereum address, and 20 bps in cbBTC to a Base address. The amount of each asset the affiliate will receive is calculated based on prices at the time of the quote and is also stored in the order data. All affiliate fees collected during the week are distributed to the specified addresses at the end of the week. ## Implementation To apply an affiliate fee via API, include the `affiliate_fee` parameter when requesting a quote: ```shell theme={null} curl -X 'GET' \ 'https://testnet.api.garden.finance/v2/quote?from=bitcoin_testnet:btc&to=base_sepolia:wbtc&from_amount=100000&affiliate_fee=30' \ -H 'garden-app-id: YOUR_APP_ID' \ -H 'accept: application/json' ``` In this example, we've added a 30 bps affiliate fee. To include affiliate fees, add the `affiliate_fees` field when **creating an order**. Here's a sample create order request: ```shell highlight={15-19} theme={null} curl --location 'http://testnet.api.garden.finance/v2/orders' \ --header 'garden-app-id: f242ea49332293424c96c562a6ef575a819908c878134dcb4fce424dc84ec796' \ --header 'Content-Type: application/json' \ --data '{ "source": { "asset": "bitcoin_testnet:btc", "owner": "tb1p4pr78swsn60y4ushe05v28mqpqppxxkfkxu2wun5jw6duc8unj3sjrh4gd", "amount": "50000" }, "destination": { "asset": "base_sepolia:wbtc", "owner": "0x004Cc75ACF4132Fc08cB6a252E767804F303F729", "amount": "49850" }, "affiliate_fees": [{ "asset": "base_sepolia:wbtc", "address": "0x7A3d05c70581bD345fe117c06e45f9669205384f", "fee": 30 }] }' ``` This process involves two steps: Requesting a quote with the affiliate fee applied Submitting the order using the selected quote To request a quote with an affiliate fee, include the `affiliateFee` parameter in the `options` object. ```ts React theme={null} import { SupportedAssets } from "@gardenfi/orderbook"; import { useGarden } from "@gardenfi/react-hooks"; const { swapAndInitiate, getQuote } = useGarden(); const fromAsset = SupportedAssets.testnet.ethereum_sepolia_WBTC; const toAsset = SupportedAssets.testnet.arbitrum_sepolia_WBTC; const amount = 100000; const isExactOut = false; const quoteRes = await getQuote({ fromAsset, toAsset, amount, isExactOut, options: { affiliateFee: 30 // in bps }, }); ``` ```ts NodeJS theme={null} const orderpair = 'ethereum_sepolia:0x29C9C37D0Fec7E64AFab0f806c8049d9e2f9B0b6::arbitrum_sepolia:0x795Dcb58d1cd4789169D5F938Ea05E17ecEB68cA' const amount = 100000 const isExactOut = false const quoteRes = await garden.quote.getQuote( orderpair, amount, isExactOut, { affiliateFee: 30, // in bps }, ); ``` While creating the order using the `swap` function, you can include the `affiliateFee` property to specify the recipient addresses, the fee amounts (in bps), and optionally the assets and chains you want the payout to be in. Garden supports payout in USDC and cbBTC. ```ts React theme={null} const [_strategyId, quoteAmount] = Object.entries(quoteRes.val.quotes)[0]; const response = await swapAndInitiate({ fromAsset, toAsset, sendAmount: amount.toString(), receiveAmount: quoteAmount, additionalData: { strategyId: _strategyId, }, affiliateFee: [ { address: "", asset: "", fee: 30 }, // Add more splits as needed ] }); ``` ```ts Node.js theme={null} const [_strategyId, quoteAmount] = Object.entries(quoteRes.val.quotes)[0]; const swapParams: SwapParams = { fromAsset, toAsset, sendAmount, receiveAmount: quoteAmount, additionalData: { strategyId: _strategyId, }, affiliateFee:[ { address: "", chain: "", asset: "", fee: 30 }, // Add more splits as needed ] }; const swapResult = await garden.swap(swapParams); ``` ## How to claim The claiming process involves two steps: checking your available earnings through the API, then submitting an on-chain transaction to withdraw them. ### Check available earnings First, call the earnings endpoint to get your claimable amounts: ```bash cURL theme={null} curl -X 'GET' 'https://api.garden.finance/v2/apps/earnings' \ -H 'garden-app-id: YOUR_APP_ID' \ -H 'accept: application/json' ``` Example response: ```json Success theme={null} { "status": "Ok", "result": [ { "total_earnings": "15075000000", "total_earnings_usd": "15075", "affiliate": "0x004Cc75ACF4132Fc08cB6a252E767804F303F729", "asset": "ethereum:usdc", "token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "claim_amount": "5075000000", "claim_amount_usd": "5075", "claim_signature": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b", "claim_contract": "0x5EbEC4D8DA437b2BAD656D43d40fE412bA5D217a" } ] } ``` The `claim_amount`, `claim_amount_usd`, `claim_signature`, and `claim_contract` fields are only included when you have unclaimed earnings available. ### Claim your earnings To claim your earnings, you need to call the `claim` function on the distributor contract. Here's how to construct the transaction: Make sure to call the claim function on the same chain where you received the rewards. For example, if your earnings are in `ethereum:usdc`, you should use the Ethereum mainnet RPC URL and chain configuration. ```javascript JavaScript theme={null} import { createWalletClient, createPublicClient, http, parseAbi } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { mainnet } from 'viem/chains'; async function claimAffiliateEarnings(claimData) { const account = privateKeyToAccount(''); // Create wallet client for sending transactions const walletClient = createWalletClient({ account, chain: mainnet, transport: http('YOUR_RPC_URL'), }); // Create public client for reading transaction receipts const publicClient = createPublicClient({ chain: mainnet, transport: http('YOUR_RPC_URL'), }); // Parse the ABI for the claim function const abi = parseAbi([ 'function claim((address toAddress, address tokenAddress, uint256 totalRewards) _claim, bytes _signature)' ]); // Prepare claim parameters const claimParams = { toAddress: claimData.affiliate, tokenAddress: claimData.token_address, totalRewards: BigInt(claimData.total_earnings) }; // Execute the claim transaction const hash = await walletClient.writeContract({ address: claimData.claim_contract, abi, functionName: 'claim', args: [claimParams, claimData.claim_signature] }); // Wait for transaction receipt const receipt = await publicClient.waitForTransactionReceipt({ hash }); return receipt; } ``` # One-Click Swaps Source: https://docs.garden.finance/developers/api/1click Integrate one-click cross-chain swaps that handle the entire flow in the background ## Overview This framework eliminates the need for users to stay online until the end of the swap, removing the need for the user side application to manually redeem on the destination chain. We recommend using this system in stateless or unstable network environments, if you have access to persistent state and access to a background process consider managing the preimages on the user side. ## How It Works * This allows a user or an integrator to grant preimage manager access to generate the preimage, hold it until the solver initiate on the Destination chain. * Revealing the preimage claims funds to user's address, the preimage manager is not allowed to change the addresses of sender or recipient as they are pre set on-chain, and are immutable. * This is an **optional** convenience feature, allowing the user to interact with the swapping system, and not stay online until confirmation, this could add high UX value for swaps from slow chains like Bitcoin. ### Architecture Overview ```mermaid theme={null} sequenceDiagram participant U as User participant API as Garden API participant PM as Preimage Manager participant SC as Source Chain participant DC as Destination Chain participant S as Solver U->>API: Create an intent without the secret hash API->>PM: Request hashlock Note over PM: Generate preimage & calculate hashlock PM->>API: Return hashlock API->>U: Return initiate request prefilled with hashlock U->>SC: Initiate swap with hashlock Note over SC: Source initiate transaction mined S->>DC: Initiate swap with same hashlock Note over DC: Destination initiate transaction mined PM->>DC: Reveal preimage (claim funds to user) Note over DC: User receives funds on destination S->>SC: Use revealed preimage to claim funds Note over SC: Solver receives funds on source ``` The diagram shows how the preimage manager eliminates the need for users to stay online throughout the entire swap process. Once the user initiates the swap on the source chain, the preimage manager handles the final redemption automatically when the solver completes their part of the atomic swap. # Garden API Source: https://docs.garden.finance/developers/api/overview Integrate Bitcoin bridging into your wallet, app, or back-end service using our API endpoints The **Garden API** is highly flexible, making it perfect for advanced workflows. Begin by exploring the quickstart guide: Quickstart guide for getting started with the Garden API. Once you have an understanding of the basic endpoints, explore the following API tips & tricks to make your integration seamless: Follow our route policy system to validate supported asset trading pairs. Earn revenue from Garden swaps through your integration. # Order Lifecycle Source: https://docs.garden.finance/developers/core/order-lifecycle Understanding the various states an order can go through during the swap process This page outlines the various states an order can go through during the swap process, helping you manage integration logic effectively. ## Order states The tables below classify all the order statuses, providing descriptions and the corresponding actions. ### Order creation and matching | Status | Description | Action | | ------------- | ------------------------------------------------------- | --------------------------------------------------------------- | | **`Created`** | The order is created and waiting for a solver to match. | -- | | **`Matched`** | A solver has matched the order. | The user has to initiate the transaction on their source chain. | ### Settlement: User and solver execution | Status | Description | Action | | ---------------------------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------------------- | | **`InitiateDetected`** | The user's initiation transaction is detected on-chain but not confirmed yet. | -- | | **`Initiated`** | The user's initiation is confirmed. The solver must now initiate the transaction on the destination chain. | Wait for the solver to initiate. | | **`CounterPartyInitiateDetected`** | The solver's initiation transaction is detected on-chain but not confirmed. | -- | | **`CounterPartyInitiated`** | The solver's initiation is confirmed, and the user must redeem their funds on the destination chain. | The user has to redeem their funds. | ### Redemption and completion | Status | Description | Action | | -------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------ | | **`RedeemDetected`** | The user's redeem transaction is detected on-chain but not confirmed yet. | -- | | **`Redeemed`** | The user has redeemed their funds. The solver will now redeem their funds on the source chain. | -- | | **`CounterPartyRedeemDetected`** | The solver's redeem transaction is detected on-chain but not confirmed. | -- | | **`CounterPartyRedeemed`** / **`Completed`** | The solver has redeemed their funds, marking the entire order as completed. | -- | ### Exceptional and failure states | Status | Description | Action | | ----------------------------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | | **`CounterPartySwapExpired`** | The solver's HTLC has expired. | The user must wait for their HTLC to expire to claim a refund. | | **`Expired`** | The user's HTLC has expired. | The user has to refund their funds. | | **`RefundDetected`** | The user's refund transaction is detected on-chain but not confirmed. | -- | | **`Refunded`** | The user's refund is confirmed, and they have reclaimed their funds. | -- | | **`DeadLineExceeded`** | 1. Initiate transaction is not detected within 1 hour. 2. Initiate transaction is not confirmed within 12 hours. | -- | ## Swap states Each swap (user or solver side) transitions through the following states: | Status | Description | | ---------------------- | ----------------------------------------------------- | | **`Idle`** | The swap is not yet initiated. | | **`InitiateDetected`** | Initiation transaction is detected but not confirmed. | | **`Initiated`** | The initiation transaction is confirmed. | | **`RedeemDetected`** | Redemption transaction is detected but not confirmed. | | **`Redeemed`** | Redemption transaction is confirmed. | | **`RefundDetected`** | Refund transaction is detected but not confirmed. | | **`Refunded`** | Refund transaction is confirmed. | | **`Expired`** | The swap expired, and a refund is required. | The order progresses through these states sequentially, with some states allowing for parallel execution between user and solver actions. # Sessions Source: https://docs.garden.finance/developers/core/sessions Understanding how sessions enable secure, trustless atomic swaps while delivering seamless user experience **Sessions** play a vital role in enabling secure, trustless [atomic swaps](/home/fundamentals/core-concepts/atomic-swaps) while also delivering a seamless swapping experience. For integrators, sessions provide a unified framework to manage secrets, interact with Bitcoin's P2SH addresses, and ensure smooth interactions without compromising security. By abstracting complex processes like secret generation and Bitcoin wallet integration, sessions simplify the developer experience while maintaining robust protections for users. ## Why do we need sessions? In traditional apps, users authenticate once and perform multiple actions seamlessly. dApps, on the other hand, often require repeated prompts for signing and confirmations, which can create unnecessary friction. We developed an app session architecture to eliminate these inefficiencies while maintaining the decentralized and trustless nature of atomic swaps. ## How do sessions work? ### Secure key generation and storage Sessions begin with the creation of a **P-256 key**, derived securely from the user's EIP-712 signature. This key is unique to the user and serves as the foundation for managing secrets and interactions. It is securely stored in the browser's **IndexedDB**, ensuring persistence across sessions without exposing the key to tampering or misuse. The P-256 key is generated using cryptographically secure methods and is tied directly to the user's wallet signature, ensuring each session is unique and secure. ### Bitcoin wallet integration Atomic swaps on the Bitcoin network require funds to be locked and redeemed from **P2SH (Pay-to-Script-Hash)** addresses, which most Bitcoin wallets do not natively support. Garden's sessions leverage the P-256 key as a lightweight Bitcoin wallet. This enables users to unlock P2SH addresses, redeem funds securely, and direct them to their desired Bitcoin addresses without needing specialized wallet functionality. ### Secrets management The session securely generates and manages the cryptographic secrets required for atomic swaps. Using the P-256 key, Garden derives the secret and secret hash for each transaction: * The **secret** is the hash of the EIP-712 signature * The **secret hash** is the double hash of the signature This ensures the integrity of the swap process while maintaining trustlessness. ### Workflow Session components come together to power atomic swaps seamlessly: 1. **Session initialization**: The P-256 key acts as a secure anchor for all subsequent interactions, ensuring user-specific, cryptographically protected actions 2. **Quote and intent creation**: The session securely handles signing and broadcasting the user's intent to swap assets, ensuring [no custody risk](/home/fundamentals/benefits/no-custody-risk) and aligning with atomic swap requirements 3. **Script management**: The secrets and script hashes generated by the session enable conditional fund transfers using P2SH addresses, maintaining trustlessness in the swap process 4. **Redemption and settlement**: The session manages the interaction with the destination chain, unlocking funds securely and ensuring seamless coordination between the user and solver ## How sessions improve security Sessions ensure users retain full control of their assets throughout the swap process. The P-256 key is generated dynamically for each user and tied directly to their session, preventing unauthorized access. By storing the key in **IndexedDB**, it remains isolated within the browser's same-origin policy, making it inaccessible to external threats. Key security benefits include: * **User control**: Users retain full ownership of their assets throughout the entire swap process * **Unique secrets**: Secrets are generated per transaction, ensuring they are unique and valid only for the specific swap * **Browser isolation**: Keys are stored securely in IndexedDB with same-origin policy protection * **No custody risk**: Garden never holds user funds or private keys * **Cryptographic integrity**: All operations use proven cryptographic methods for maximum security ## Integration considerations When integrating sessions into your application: * Sessions handle complex Bitcoin operations automatically * No need to implement P2SH address management * Secrets are managed securely without developer intervention * Session persistence across browser refreshes * Automatic cleanup of expired sessions ## Next steps Understand how orders progress through different states Learn how to integrate Garden SDK with session support # Cookbook Source: https://docs.garden.finance/developers/guides/cookbook Step-by-step guides for building on top of Garden Each guide walks you through a different use case, helping you integrate efficiently. Build a Bitcoin bridge in Next.js using Garden SDK. Build a terminal UI app using Garden API (full cookbook guide coming soon). Check back regularly for new recipes! If you're looking for a specific recipe, drop by our [Townhall](https://discord.gg/B7RczEFuJ5) and let us know. # Bridge using Garden SDK Source: https://docs.garden.finance/developers/guides/sdk Step-by-step guide to integrating Garden SDK for fetching quotes, executing swaps, and tracking them If you are stuck at any part of the implementation, drop a message in our [Townhall](https://discord.gg/B7RczEFuJ5)—our dev team is ready to assist! This cookbook provides a step-by-step guide to integrating Garden SDK for fetching quotes, executing swaps, and tracking them. It walks through building a simple cross-chain bridge in a Next.js environment, enabling seamless swaps between BTC (Bitcoin Testnet4) and WBTC (Ethereum Sepolia). For a fully functional reference, check out the [Bridge](https://github.com/gardenfi/demo-app-nextjs), allowing you to see how these steps integrate into a working application. ## What you'll build start UI * **Cross-chain swaps**: Swap component for seamless cross-chain swaps using Garden SDK. * **Swap history**: History component for keeping users informed about the status of their swaps. ## Setting up the SDK The `GardenProvider` is the core of the SDK integration. It acts as a wrapper around your application, handling: * **Session management**: Maintains active user sessions and transaction state. * **Wallet connectivity**: Manages wallet connections, transaction signing, and approvals. * **Environment configuration**: Switches between testnet and mainnet as needed. Before interacting with the SDK, wrap your application with the `GardenProvider`. The provider requires `walletClient`, which is provided by `wagmi`. For this, you'll need to: 1. Get the `walletClient` using the `useWalletClient` hook. 2. Pass it to your `GardenProvider` configuration. Here's how you set it up: ```tsx GardenProvider.tsx theme={null} "use client"; import { GardenProvider } from "@gardenfi/react-hooks"; import { Environment } from "@gardenfi/utils"; import { useWalletClient } from "wagmi"; const getStorage = (): Storage => { if (typeof window !== "undefined") { return localStorage; } return { getItem: () => null, setItem: () => {}, removeItem: () => {}, clear: () => {}, length: 0, key: () => null, }; }; function GardenProviderWrapper({ children }: { children: React.ReactNode }) { const { data: walletClient } = useWalletClient(); return ( { children } ); } export default GardenProviderWrapper; ``` ## Fetching quotes Now that you have your `walletClient`, you can use it to initialize the `GardenProvider`. Before diving into swap, your app needs to fetch real-time quotes for their swap params `fromAsset`, `toAsset`, `amount`. The `getQuote` hook from Garden SDK provides real-time USD values and exchange rates for any two [supported assets](/developers/supported-chains). You'll need to provide: * `fromAsset`: The token you want to swap from. * `toAsset`: The token you want to receive. * `amount`: The amount you want to swap. * `isExactOut`: Whether you're specifying the input or output amount. Here's how you implement: ```tsx SwapComponent.tsx theme={null} import { useGarden } from "@gardenfi/react-hooks"; import BigNumber from "bignumber.js"; const SwapComponent = () => { const { getQuote } = useGarden(); const { swapParams } = swapStore(); const fetchQuote = async (amount: string) => { if (!getQuote) return; const amountInDecimals = new BigNumber(amount).multipliedBy( 10 ** swapParams.fromAsset.decimals ); const quote = await getQuote({ fromAsset: swapParams.fromAsset, toAsset: swapParams.toAsset, amount: amountInDecimals.toNumber(), isExactOut: false, }); }; }; ``` ```ts SwapStore.ts theme={null} import { SupportedAssets } from "@gardenfi/orderbook"; import { SwapParams } from "@gardenfi/core"; import { create } from "zustand"; interface SwapState { swapParams: SwapParams; setSwapParams: (params: Partial) => void; } export const swapStore = create((set) => ({ swapParams: { fromAsset: SupportedAssets.testnet.ethereum_sepolia_WBTC, toAsset: SupportedAssets.testnet.bitcoin_testnet_BTC, sendAmount: "0", receiveAmount: "0", additionalData: { strategyId: "" }, }, setSwapParams: (params) => set((state) => ({ swapParams: { ...state.swapParams, ...params }, })), })); ``` ## Executing swap Now that you have the quotes, it's time to execute the swap. Garden SDK provides the `swapAndInitiate` hook that handles the entire swap process for you. Here's what it does: 1. Creates your swap order. 2. Waits for it to be matched with the [solver](/home/fundamentals/core-concepts/solvers). 3. Automatically initiates the swap if you're on a smart contract chain. You'll need to provide the swap parameters (including the quote details you got earlier). The hook will return either a matched order or an error message if something goes wrong. Here's how you can implement this: ```tsx TokenSwap.tsx theme={null} import { useGarden } from "@gardenfi/react-hooks"; const TokenSwap = () => { const { swapAndInitiate } = useGarden(); // We get the `strategyId` and `receiveAmount` from the quote response. const performSwap = async (strategyId: string, receiveAmount: string) => { const response = await swapAndInitiate({ fromAsset: swapParams.fromAsset, toAsset: swapParams.toAsset, sendAmount, receiveAmount, additionalData: { btcAddress, strategyId, }, }); console.log(response); return response; }; }; ``` ## Fetching order status Your swap is now initiated, but what's happening with your order? You can keep your users informed by tracking the order status right in your app. The Garden SDK simplifies this with the `ParseOrderStatus` function, which determines the order's current state. By checking block numbers on both chains, it can identify if the order is: * `Expired` - The user's swap has expired, and they have to refund their funds. * `Initiated` - User initiated, waiting for counterparty to initiate. * `Redeemed` - User redeemed, counterparty has to redeem. * `Refunded` - User refunded. Here's how you can implement this status tracking: ```tsx OrderStatusParser.tsx theme={null} import { ParseOrderStatus } from "@gardenfi/core"; const OrderStatusParser = () => { const status = ParseOrderStatus( order.val, blockNumbers.val[order.val.source_swap.chain], blockNumbers.val[order.val.destination_swap.chain], ); console.log('Status:', status); }; ``` You have now everything needed to build a simple swap application using the Garden SDK! ## Next steps By following this cookbook, you've implemented the core functionalities of a cross-chain application using Garden SDK. If you are interested in building further, consider implementing: * **Robust error handling** to manage API failures and network disruptions gracefully. * **Notifications or status updates** to keep users informed on swap progress and completion. * **Expanded asset support** to extend swap functionality across more chains and tokens. * **UI/UX improvements** such as swap progress indicators. # Localnet Source: https://docs.garden.finance/developers/localnet Local testing environment for Garden SDK development Localnet testing is a crucial step in ensuring your Garden SDK integration works as intended before deploying it to a testnet or mainnet. To support your testing, we provide **Merry**, an in-house tool designed for comprehensive cross-chain testing in a local environment. Merry is a CLI tool that leverages Docker to set up a multi-chain testing environment with a single command. It includes: * **Bitcoin regtest node:** A local Bitcoin testnet environment. * **EVM localnet nodes:** Local Ethereum and Arbitrum test environments. Simply add the localnet details to your EVM wallet to detect and interact. * **Filler bot:** Simulates the behavior of a live [solver](/home/fundamentals/core-concepts/solvers) based on predefined strategies. * **Orderbook:** Local version of the [order book](/home/fundamentals/core-concepts/auctions) to test how intents are matched and fulfilled. * **Faucet:** Generate unlimited test funds for seamless testing. * **Electrum services:** Lightweight wallet support for interacting with Bitcoin network. Merry eliminates block mining delays, provides a complete environment for multi-chain workflows, and allows developers to test integrations independently of external services. It’s customizable, fast, and supports iterative testing with features like local service replacement. ## Installation **Prerequisites**: Ensure Docker is installed and running. Download Docker from [docker.com](https://www.docker.com). Merry supports arm64 and amd64 architectures. For Windows, use Windows Subsystem for Linux (WSL). Run these scripts based on your environment. ```bash Linux & MacOS theme={null} # Run the following command to install Merry: curl https://get.merry.dev | bash ``` ```bash Windows theme={null} # In a WSL terminal, run sudo dockerd and verify if the docker daemon is running, then: curl https://get.merry.dev | bash ``` Merry stores its configuration and other data in a `.merry` directory on your system. ## Commands Merry provides a variety of commands to manage your testing environment. ### Start Merry Start all services with: ```bash theme={null} merry go ``` * `--bare`: Starts multi-chain services only (Bitcoin and Ethereum nodes with explorers) without Garden services * `--headless`: Starts all services without frontend interfaces for server environments ### Stop Merry Stops all running services: ```bash theme={null} merry stop ``` Use `--delete` or `-d` to remove data: ```bash theme={null} merry stop -d ``` ### List all commands Display all available commands: ```bash theme={null} merry --help ``` ### Get logs Access logs for specific [services](#supported-services): ```bash theme={null} merry logs -s ``` Replace `` with the specific service (e.g., filler, orderbook) to view its logs. ```bash theme={null} # Example: Get EVM logs merry logs -s evm ``` ### Replace a service with local version Replace a service with your local development version: ```bash theme={null} merry replace ``` Make sure you're in the directory containing the local service's Dockerfile. You can only replace filler, orderbook, and EVM chain services. ### Interact with Bitcoin RPC Run Bitcoin RPC [commands](https://developer.bitcoin.org/reference/rpc/) directly: ```bash theme={null} merry rpc ``` Example - get blockchain info: ```bash theme={null} merry rpc getblockchaininfo ``` ### Fund accounts Use the faucet to fund Bitcoin or Ethereum addresses for testing: ```bash theme={null} merry faucet --to
``` ### Update Docker images Keep your environment up-to-date by pulling the latest Docker images: ```bash theme={null} merry update ``` ### Generate auto-completion scripts Generate scripts for your shell (bash, zsh, fish, powershell): ```bash theme={null} merry completion ``` ### Get version info Check the version of Merry installed: ```bash theme={null} merry version ``` ## Supported services The following services are available when running Merry. Use these ports to connect your applications and tools. | Service | Port | Description | | ----------------------------------- | --------------------------- | ------------------------------ | | **Bitcoin regtest node** | `localhost:18443` & `18444` | Local Bitcoin test network | | **Bitcoin esplora frontend** | `localhost:5050` | Bitcoin blockchain explorer UI | | **Bitcoin esplora electrs indexer** | `localhost:50000` & `30000` | Bitcoin transaction indexer | | **Ethereum localnode** | `localhost:8545` | Local Ethereum test network | | **Ethereum otterscan** | `localhost:5100` | Ethereum blockchain explorer | | **Arbitrum localnode** | `localhost:8546` | Local Arbitrum test network | | **Arbitrum otterscan** | `localhost:5101` | Arbitrum blockchain explorer | | **Postgres** | `localhost:5432` | Database for order storage | | **Redis** | `localhost:6379` | Cache and session storage | | **Orderbook** | `localhost:8080` | Garden orderbook API | | **Filler** | - | Background solver simulation | ## Next steps Start integrating Garden SDK with your local testing environment Explore the Garden API endpoints # Developer Overview Source: https://docs.garden.finance/developers/overview Add native Bitcoin swaps to your wallet, aggregator, or dApp Garden lets you add Bitcoin swaps to any product. Pick your integration path below and follow the quickstart — most integrators are up and running in under 30 minutes. *** ## Choose your integration path You control the full flow. Most of the integration is already handled for you. *** ## Before you start * **App ID** — sign up on the [Integrator Portal](https://portal.garden.finance) to get your own production keys. * **Testnet funds** — get testnet BTC from our [faucet](https://testnetbtc.com). *** ## Up and running in 30 minutes `GET /quote` returns a valid quote for your chosen route. `POST /orders` returns an order with an ID. Status is `Created`. Once matched, interact with the contract on the source chain to initiate the swap. Poll `GET /orders/{id}` until the order reaches `Completed` state. Follow the [API Quickstart](/api-reference/quickstart) for the full walkthrough. Call `getQuote()` and get back a valid quote with send amount, receive amount, and fee. Call `swap()` — the SDK handles order creation, fund submission, and settlement automatically. Order moves through its states and reaches `Completed`. Funds arrive on the destination chain. Follow the [React](/developers/sdk/react/quickstart) or [Node.js](/developers/sdk/nodejs/quickstart) quickstart for the full walkthrough. *** ## Next steps Every order status and what it means. Read this before building your UI states. HTLC contract details for each supported chain. All supported routes and chain identifiers. Earn revenue from swaps through your integration. # Quickstart Source: https://docs.garden.finance/developers/sdk/nodejs/quickstart This guide assumes that you have completed the [Setup](/developers/sdk/nodejs/setup) guide. Initialize wallets and providers only for the chains you need. ```ts theme={null} import { BitcoinProvider, BitcoinNetwork, BitcoinWallet, } from '@gardenfi/core'; import { with0x } from '@gardenfi/utils'; import { createWalletClient, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { arbitrumSepolia } from 'viem/chains'; import { RpcProvider, Account } from 'starknet'; import * as anchor from '@coral-xyz/anchor'; import { web3 } from '@coral-xyz/anchor'; // Ethereum wallet setup const ethereumAccount = privateKeyToAccount(with0x('')); const ethereumWalletClient = createWalletClient({ account: ethereumAccount, chain: arbitrumSepolia, transport: http(), }); // Starknet wallet setup const starknetProvider = new RpcProvider(); // Using default RPC URL const starknetWallet = new Account({ provider: starknetProvider, signer: , address: , }); // Solana wallet setup const solanaPrivKeyBytes = new Uint8Array(''); const solanaUser = web3.Keypair.fromSecretKey(solanaPrivKeyBytes); const solanaConnection = new web3.Connection(RPC_URL, { commitment: 'confirmed' }); const solanaWallet = new anchor.Wallet(solanaUser); const solanaProvider = new anchor.AnchorProvider(solanaConnection, solanaWallet); ``` For API key, reach out to us in the [Townhall](https://discord.gg/B7RczEFuJ5) or raise a support ticket. ```js theme={null} import { Garden } from '@gardenfi/core'; import { Network } from '@gardenfi/utils'; const garden = Garden.fromWallets({ environment: Network.TESTNET, apiKey: '', wallets: { evm: ethereumWalletClient, starknet: starknetWallet, solana: solanaProvider } }); ``` You can initialize the Garden instance with your own HTLC client implementations. Check out the [IEVMHTLC](https://github.com/gardenfi/garden.js/blob/main/packages/core/src/lib/evm/htlc.types.ts), [IStarknetHTLC](https://github.com/gardenfi/garden.js/blob/main/packages/core/src/lib/starknet/starknetHTLC.types.ts), and [ISolanaHTLC](https://github.com/gardenfi/garden.js/blob/main/packages/core/src/lib/solana/htlc/ISolanaHTLC.ts) interfaces for more details. ```js theme={null} import { Garden } from '@gardenfi/core'; import { Network } from '@gardenfi/utils'; const garden = new Garden({ environment: Network.TESTNET, apiKey: '', htlc: { evm: , starknet: , solana: }, }); ``` ```js theme={null} import { Quote, SwapParams } from '@gardenfi/core'; import { Assets, ChainAsset } from '@gardenfi/orderbook'; const fromAsset = ChainAsset.from(Assets.ethereum_sepolia.WBTC); const toAsset = ChainAsset.from(Assets.bitcoin_testnet.BTC); const sendAmount = '1000000'; // 0.01 WBTC // Get the quote for the send amount const quote = await garden.quote.getQuote( fromAsset, toAsset, sendAmount, false, {}, ); const receiveAmount = quote.val?.[0].destination.amount; const solverId = quote.val?.[0].solver_id; const sourceAddress = ethereumAccount.address; const destinationAddress = ' # Setup Source: https://docs.garden.finance/developers/sdk/nodejs/setup Install the following packages: ```bash npm theme={null} npm install @gardenfi/core @gardenfi/utils @gardenfi/orderbook viem @catalogfi/wallets ``` ```bash yarn theme={null} yarn add yarn add @gardenfi/core @gardenfi/utils @gardenfi/orderbook viem @catalogfi/wallets ``` # Garden SDK Source: https://docs.garden.finance/developers/sdk/overview Integrate Bitcoin bridging into your dApp with TypeScript packages for React and Node.js The **Garden SDK** is a set of Typescript packages that allow you to bridge Bitcoin to EVM or non-EVM chains and vice versa. It is an abstraction over the Garden APIs, allowing developers to integrate Garden components into their dApps easily. Simplify development with our React providers and hooks. Ideal for server-side and Node.js environments. # Quickstart Source: https://docs.garden.finance/developers/sdk/react/quickstart This guide assumes that you have completed the [Setup](/developers/sdk/react/setup) guide. For API key, reach out to us in the [Townhall](https://discord.gg/B7RczEFuJ5) or raise a support ticket. Integrate Garden into your React app by wrapping it with the **GardenProvider**. This enables interaction with the protocol and handles session management. The Starknet and Solana configurations are only necessary if you choose to support those chains in your app. ```tsx app.tsx theme={null} import { GardenProvider } from '@gardenfi/react-hooks'; import { Network } from '@gardenfi/utils'; import { useAccount } from 'starknet-react/core'; import { useWalletClient } from 'wagmi'; import { useAnchorWallet, useConnection } from "@solana/wallet-adapter-react"; import { AnchorProvider } from "@coral-xyz/anchor"; function App() { // EVM const { data: walletClient } = useWalletClient(); // Starknet const { account: starknetWallet } = useAccount(); // Solana const { connection } = useConnection(); const anchorWallet = useAnchorWallet(); const solanaAnchorProvider = new AnchorProvider(connection, anchorWallet, {}); return ( , wallets: { evm: walletClient, starknet: starknetWallet, solana: solanaAnchorProvider } }} store={localStorage} > {/* Your swap component */} ); } export default App; ``` ```tsx main.tsx theme={null} import React from 'react'; import ReactDOM from 'react-dom/client'; import { WagmiProvider } from 'wagmi'; import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; import { wagmiConfig } from 'wagmi.ts'; import { StarknetConfig } from '@starknet-react/core'; import { starknetChains, connectors as starknetConnectors, starknetProviders } from './starknetConfig.ts'; import { SolanaProvider } from "./solanaProvider.tsx"; ReactDOM.createRoot(document.getElementById('root')!).render( ); ``` ```tsx constants.ts theme={null} export const network = import.meta.env.NETWORK ``` ```tsx wagmi.ts theme={null} import { createConfig, http } from 'wagmi'; import { arbitrum, arbitrumSepolia, mainnet, sepolia } from 'wagmi/chains'; import { injected, metaMask, safe } from 'wagmi/connectors'; export const wagmiConfig = createConfig({ chains: [mainnet, arbitrum, sepolia, arbitrumSepolia], // All EVM chains you choose to support connectors: [injected(), metaMask(), safe()], transports: { [mainnet.id]: http(), [arbitrum.id]: http(), [sepolia.id]: http(), [arbitrumSepolia.id]: http(), }, }); ``` ```tsx starknetConfig.ts theme={null} import { publicProvider } from '@starknet-react/core'; import { sepolia, mainnet } from '@starknet-react/chains'; import { argent } from '@starknet-react/core'; import { braavos } from '@starknet-react/core'; import { InjectedConnector } from 'starknetkit/injected'; export const connectors = [ new InjectedConnector({ options: { id: 'argentX' } }), new InjectedConnector({ options: { id: 'braavos' } }), new InjectedConnector({ options: { id: 'keplr' } }), ]; export const starknetChains = [mainnet, sepolia]; export const starknetProviders = publicProvider(); ``` ```tsx solanaProvider.tsx theme={null} import { FC, ReactNode } from "react"; import { ConnectionProvider, WalletProvider } from "@solana/wallet-adapter-react"; import { network } from "./constants"; import { Network } from "@gardenfi/utils"; interface SolanaProviderProps { children: ReactNode; } export const SolanaProvider: FC = ({ children }) => { const rpcEndpoint = network === Network.MAINNET ? "https://solana-rpc.publicnode.com" : "https://api.devnet.solana.com"; return ( { children } ); }; ``` The lifecycle of a swap is as follows: 1. Get a quote 2. Pick the best quote 3. Initiate the transaction to complete the swap ```tsx swap.tsx theme={null} import { useState } from 'react'; import { useGarden } from '@gardenfi/react-hooks'; import { Assets } from '@gardenfi/orderbook'; import BigNumber from 'bignumber.js'; import { useAccount } from "wagmi"; export const Swap = () => { const [quoteAmount, setQuoteAmount] = useState(); const [solverId, setSolverId] = useState(); const { swap, getQuote } = useGarden(); const { address: evmAddress } = useAccount(); // Define the assets involved in the swap const inputAsset = Assets.arbitrum_sepolia.WBTC; const outputAsset = Assets.bitcoin_testnet.BTC; // Amount to be swapped, converted to the smallest unit of the input asset const amount = new BigNumber(0.01).multipliedBy(10 ** inputAsset.decimals); // User's Bitcoin address to receive funds const btcAddress = 'tb1q25q3632323232323232323232323232323232'; const handleGetQuote = async () => { if (!getQuote) return; // Fetch a quote for the swap const quote = await getQuote({ fromAsset: inputAsset, toAsset: outputAsset, amount: amount.toNumber(), isExactOut: false, // Set to `true` if you wish to specify the output (receive) amount }); if (!quote.ok) { return alert(quote.error); } // Select a quote and save it (the user will confirm this quote before the swap is executed) const quoteAmount = quote.val[0].destination.amount const solverId = quote.val[0].solver_id setQuoteAmount(quoteAmount); setSolverId(solverId) }; const handleSwap = async () => { if (!swap || !quote) return; // Initiate the swap with the selected quote and user's details const order = await swap({ fromAsset: inputAsset, toAsset: outputAsset, sendAmount: amount.toString(), receiveAmount: quoteAmount, solverId, sourceAddress: evmAddress, destinationAddress: btcAddress, }); if (!order.ok) { return alert(order.error); } console.log('✅ Order created:', order.val); }; return (
{/* Fetch swap quote */} {/* Initiate the swap */}
); } ```
To include affiliate fees in your swap flow, refer to the implementation [here](/developers/affiliate-fees#using-sdk). # Setup Source: https://docs.garden.finance/developers/sdk/react/setup Install the core Garden Protocol packages: ```bash npm theme={null} npm install @gardenfi/core @gardenfi/orderbook @gardenfi/react-hooks @tanstack/react-query wagmi ``` ```bash yarn theme={null} yarn add @gardenfi/core @gardenfi/orderbook @gardenfi/react-hooks @tanstack/react-query wagmi ``` Install additional packages based on the blockchains your app supports: ```bash npm theme={null} npm install @starknet-react/core starknet starknetkit ``` ```bash yarn theme={null} yarn add @starknet-react/core starknet starknetkit ``` ```bash npm theme={null} npm install @coral-xyz/anchor @solana/wallet-adapter-react ``` ```bash yarn theme={null} yarn add @coral-xyz/anchor @solana/wallet-adapter-react ``` Install the Vite plugins: ```bash npm theme={null} npm install --save-dev vite-plugin-wasm vite-plugin-top-level-await vite-plugin-node-polyfills ``` ```bash yarn theme={null} yarn add --dev vite-plugin-wasm vite-plugin-top-level-await vite-plugin-node-polyfills ``` Update your `vite.config.ts` as follows: ```typescript theme={null} import { defineConfig } from "vite"; import wasm from "vite-plugin-wasm"; import { nodePolyfills } from "vite-plugin-node-polyfills"; import topLevelAwait from "vite-plugin-top-level-await"; export default defineConfig({ plugins: [ nodePolyfills(), wasm(), topLevelAwait(), // Other plugins ], // Other settings }); ``` In your Webpack config add support for Wasm: ```typescript theme={null} /** @type {import('next').NextConfig} */ const nextConfig = { webpack: function (config, options) { // Other webpack config options config.experiments = { ...config.experiments, asyncWebAssembly: true, }; return config; }, // Other settings }; module.exports = nextConfig; ``` # Supported Chains and Assets Source: https://docs.garden.finance/developers/supported-chains Garden uses the following contracts to perform swaps on respective chains: If you would like to see a chain or asset that isn't listed here, reach out to us in the [Townhall](https://discord.gg/B7RczEFuJ5). # Supported Routes Source: https://docs.garden.finance/developers/supported-routes Follow our route policy system to validate supported asset trading pairs ## Overview The route policy system provides a lightweight way to determine which asset pairs can be traded without requiring individual API calls for each route validation. Instead of fetching all possible routes from the server, you can download a compact policy configuration and compute valid routes locally. This approach significantly reduces API calls and enables real-time route validation in your application. ## How it works Route policies use a priority-based validation system with four key components: 1. **Isolation Groups** (Highest Priority): Assets that can ONLY trade with each other. 2. **Whitelist Overrides**: Explicit exceptions that bypass other restrictions. 3. **Blacklist Pairs**: Forbidden trading pairs with wildcard support. 4. **Default Policy**: Fallback behavior for unconfigured routes. If either asset is in an isolation group, the route is only valid if both assets are in the same isolation group. ```typescript theme={null} // Example: SEED tokens can only trade with other SEED tokens "ethereum:SEED <-> arbitrum:SEED" ``` Explicit exceptions that allow routes regardless of other restrictions. ```typescript theme={null} // Example: Allow specific emergency routes "bitcoin:BTC -> ethereum:WBTC" ``` Forbidden routes that override the default policy. ```typescript theme={null} // Example: Prevent direct BTC to wrapped BTC trades "bitcoin:BTC -> *:WBTC" ``` Applied when no other rules match — either "open" (allow) or "closed" (deny). ## Local route matrix generation Here's a complete script to route matrix generation for local route validation: ```typescript TypeScript expandable theme={null} // Types for the policy configuration interface RoutePolicy { default: 'open' | 'closed'; isolation_groups: string[]; blacklist_pairs: string[]; whitelist_overrides: string[]; } interface PolicyResponse { status: 'Ok' | 'Error'; result: RoutePolicy; error?: string; } // Asset identifier type type AssetId = string; // e.g., "ethereum:SEED", "bitcoin:BTC" class RouteValidator { private policy: RoutePolicy | null = null; constructor(private apiBaseUrl: string, private apiKey: string) {} // Fetch policy from the API async loadPolicy(): Promise { try { const response = await fetch(`${this.apiBaseUrl}/policy`, { headers: { 'garden-app-id': this.apiKey, 'accept': 'application/json' } }); const data: PolicyResponse = await response.json(); if (data.status === 'Ok') { this.policy = data.result; } else { throw new Error(`API Error: ${data.error}`); } } catch (error) { throw new Error(`Failed to load policy: ${error}`); } } // Check if a route is valid based on the policy isValidRoute(fromAsset: AssetId, toAsset: AssetId): boolean { if (!this.policy) { throw new Error('Policy not loaded. Call loadPolicy() first.'); } // Can't swap to the same asset if (fromAsset === toAsset) { return false; } // Check isolation groups first (highest priority) if (this.isInIsolationGroup(fromAsset, toAsset)) { return this.isValidIsolationGroup(fromAsset, toAsset); } // Check whitelist overrides (bypass other restrictions) if (this.isWhitelistOverride(fromAsset, toAsset)) { return true; } // Check blacklist pairs if (this.isBlacklisted(fromAsset, toAsset)) { return false; } // Apply default policy return this.policy.default === 'open'; } // Get all valid destination assets for a given source asset getValidDestinations(fromAsset: AssetId, allAssets: AssetId[]): AssetId[] { return allAssets.filter(toAsset => this.isValidRoute(fromAsset, toAsset)); } // Get all possible routes from a list of assets getAllValidRoutes(assets: AssetId[]): Array<{ from: AssetId; to: AssetId }> { const routes: Array<{ from: AssetId; to: AssetId }> = []; for (const fromAsset of assets) { for (const toAsset of assets) { if (this.isValidRoute(fromAsset, toAsset)) { routes.push({ from: fromAsset, to: toAsset }); } } } return routes; } // Private helper methods private isInIsolationGroup(fromAsset: AssetId, toAsset: AssetId): boolean { return this.policy!.isolation_groups.some(group => { const assets = this.parseIsolationGroup(group); return assets.includes(fromAsset) || assets.includes(toAsset); }); } private isValidIsolationGroup(fromAsset: AssetId, toAsset: AssetId): boolean { return this.policy!.isolation_groups.some(group => { const assets = this.parseIsolationGroup(group); return assets.includes(fromAsset) && assets.includes(toAsset); }); } private isWhitelistOverride(fromAsset: AssetId, toAsset: AssetId): boolean { return this.policy!.whitelist_overrides.some(override => this.matchesPattern(fromAsset, toAsset, override) ); } private isBlacklisted(fromAsset: AssetId, toAsset: AssetId): boolean { return this.policy!.blacklist_pairs.some(blacklist => this.matchesPattern(fromAsset, toAsset, blacklist) ); } private parseIsolationGroup(group: string): AssetId[] { // Parse "ethereum:SEED <-> arbitrum:SEED" format const assets = group.split('<->').map(asset => asset.trim()); return assets; } private matchesPattern(fromAsset: AssetId, toAsset: AssetId, pattern: string): boolean { const [fromPattern, toPattern] = pattern.split('->').map(p => p.trim()); return this.matchesAssetPattern(fromAsset, fromPattern) && this.matchesAssetPattern(toAsset, toPattern); } private matchesAssetPattern(asset: AssetId, pattern: string): boolean { // Handle wildcard patterns if (pattern === '*') return true; if (pattern.includes('*')) { // Handle patterns like "starknet:*" or "*:USDC" if (pattern.endsWith(':*')) { const chainPattern = pattern.slice(0, -2); return asset.startsWith(chainPattern + ':'); } if (pattern.startsWith('*:')) { const symbolPattern = pattern.slice(2); return asset.endsWith(':' + symbolPattern); } } // Exact match return asset === pattern; } } // Helper function to build route matrix for UI function buildRouteMatrix(assets: AssetId[], validator: RouteValidator): Record { const matrix: Record = {}; for (const fromAsset of assets) { matrix[fromAsset] = validator.getValidDestinations(fromAsset, assets); } return matrix; } // Export for use in your application export { RouteValidator, buildRouteMatrix, type RoutePolicy, type AssetId }; ``` ```rust Rust expandable theme={null} use std::collections::HashMap; use serde::{Deserialize, Serialize}; use reqwest; use anyhow::{Result, Error}; // Types for the policy configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RoutePolicy { pub default: String, // "open" or "closed" pub isolation_groups: Vec, pub blacklist_pairs: Vec, pub whitelist_overrides: Vec, } #[derive(Debug, Serialize, Deserialize)] pub struct PolicyResponse { pub status: String, pub result: RoutePolicy, pub error: Option, } pub type AssetId = String; pub struct RouteValidator { api_base_url: String, api_key: String, policy: Option, } impl RouteValidator { pub fn new(api_base_url: String, api_key: String) -> Self { RouteValidator { api_base_url, api_key, policy: None, } } // Fetch policy from the API pub async fn load_policy(&mut self) -> Result<()> { let client = reqwest::Client::new(); let url = format!("{}/policy", self.api_base_url); let response = client .get(&url) .header("garden-app-id", &self.api_key) .header("accept", "application/json") .send() .await?; let data: PolicyResponse = response.json().await?; if data.status == "Ok" { self.policy = Some(data.result); Ok(()) } else { Err(Error::msg(format!("API Error: {:?}", data.error))) } } // Check if a route is valid based on the policy pub fn is_valid_route(&self, from_asset: &AssetId, to_asset: &AssetId) -> Result { let policy = self.policy.as_ref() .ok_or_else(|| Error::msg("Policy not loaded. Call load_policy() first."))?; // Can't swap to the same asset if from_asset == to_asset { return Ok(false); } // Check isolation groups first (highest priority) if self.is_in_isolation_group(from_asset, to_asset, policy) { return Ok(self.is_valid_isolation_group(from_asset, to_asset, policy)); } // Check whitelist overrides (bypass other restrictions) if self.is_whitelist_override(from_asset, to_asset, policy) { return Ok(true); } // Check blacklist pairs if self.is_blacklisted(from_asset, to_asset, policy) { return Ok(false); } // Apply default policy Ok(policy.default == "open") } // Get all valid destination assets for a given source asset pub fn get_valid_destinations(&self, from_asset: &AssetId, all_assets: &[AssetId]) -> Result> { let mut valid_destinations = Vec::new(); for to_asset in all_assets { if self.is_valid_route(from_asset, to_asset)? { valid_destinations.push(to_asset.clone()); } } Ok(valid_destinations) } // Get all possible routes from a list of assets pub fn get_all_valid_routes(&self, assets: &[AssetId]) -> Result> { let mut routes = Vec::new(); for from_asset in assets { for to_asset in assets { if self.is_valid_route(from_asset, to_asset)? { routes.push((from_asset.clone(), to_asset.clone())); } } } Ok(routes) } // Private helper methods fn is_in_isolation_group(&self, from_asset: &AssetId, to_asset: &AssetId, policy: &RoutePolicy) -> bool { policy.isolation_groups.iter().any(|group| { let assets = self.parse_isolation_group(group); assets.contains(from_asset) || assets.contains(to_asset) }) } fn is_valid_isolation_group(&self, from_asset: &AssetId, to_asset: &AssetId, policy: &RoutePolicy) -> bool { policy.isolation_groups.iter().any(|group| { let assets = self.parse_isolation_group(group); assets.contains(from_asset) && assets.contains(to_asset) }) } fn is_whitelist_override(&self, from_asset: &AssetId, to_asset: &AssetId, policy: &RoutePolicy) -> bool { policy.whitelist_overrides.iter().any(|override_pattern| { self.matches_pattern(from_asset, to_asset, override_pattern) }) } fn is_blacklisted(&self, from_asset: &AssetId, to_asset: &AssetId, policy: &RoutePolicy) -> bool { policy.blacklist_pairs.iter().any(|blacklist_pattern| { self.matches_pattern(from_asset, to_asset, blacklist_pattern) }) } fn parse_isolation_group(&self, group: &str) -> Vec { group.split("<->") .map(|asset| asset.trim().to_string()) .collect() } fn matches_pattern(&self, from_asset: &AssetId, to_asset: &AssetId, pattern: &str) -> bool { let parts: Vec<&str> = pattern.split("->").map(|p| p.trim()).collect(); if parts.len() != 2 { return false; } self.matches_asset_pattern(from_asset, parts[0]) && self.matches_asset_pattern(to_asset, parts[1]) } fn matches_asset_pattern(&self, asset: &AssetId, pattern: &str) -> bool { // Handle wildcard patterns if pattern == "*" { return true; } if pattern.contains('*') { // Handle patterns like "starknet:*" or "*:USDC" if pattern.ends_with(":*") { let chain_pattern = &pattern[..pattern.len() - 2]; return asset.starts_with(&format!("{}:", chain_pattern)); } if pattern.starts_with("*:") { let symbol_pattern = &pattern[2..]; return asset.ends_with(&format!(":{}", symbol_pattern)); } } // Exact match asset == pattern } } // Helper function to build route matrix for UI pub fn build_route_matrix(assets: &[AssetId], validator: &RouteValidator) -> Result>> { let mut matrix = HashMap::new(); for from_asset in assets { let destinations = validator.get_valid_destinations(from_asset, assets)?; matrix.insert(from_asset.clone(), destinations); } Ok(matrix) } ``` ```go Golang expandable theme={null} package main import ( "encoding/json" "fmt" "net/http" "strings" "errors" ) // Types for the policy configuration type RoutePolicy struct { Default string `json:"default"` IsolationGroups []string `json:"isolation_groups"` BlacklistPairs []string `json:"blacklist_pairs"` WhitelistOverrides []string `json:"whitelist_overrides"` } type PolicyResponse struct { Status string `json:"status"` Result RoutePolicy `json:"result"` Error *string `json:"error,omitempty"` } type AssetId string type RouteValidator struct { apiBaseURL string apiKey string policy *RoutePolicy } func NewRouteValidator(apiBaseURL, apiKey string) *RouteValidator { return &RouteValidator{ apiBaseURL: apiBaseURL, apiKey: apiKey, policy: nil, } } // Fetch policy from the API func (rv *RouteValidator) LoadPolicy() error { client := &http.Client{} url := fmt.Sprintf("%s/policy", rv.apiBaseURL) req, err := http.NewRequest("GET", url, nil) if err != nil { return err } req.Header.Set("garden-app-id", rv.apiKey) req.Header.Set("accept", "application/json") resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() var data PolicyResponse if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { return err } if data.Status == "Ok" { rv.policy = &data.Result return nil } errorMsg := "Unknown error" if data.Error != nil { errorMsg = *data.Error } return fmt.Errorf("API Error: %s", errorMsg) } // Check if a route is valid based on the policy func (rv *RouteValidator) IsValidRoute(fromAsset, toAsset AssetId) (bool, error) { if rv.policy == nil { return false, errors.New("policy not loaded. Call LoadPolicy() first") } // Can't swap to the same asset if fromAsset == toAsset { return false, nil } // Check isolation groups first (highest priority) if rv.isInIsolationGroup(fromAsset, toAsset) { return rv.isValidIsolationGroup(fromAsset, toAsset), nil } // Check whitelist overrides (bypass other restrictions) if rv.isWhitelistOverride(fromAsset, toAsset) { return true, nil } // Check blacklist pairs if rv.isBlacklisted(fromAsset, toAsset) { return false, nil } // Apply default policy return rv.policy.Default == "open", nil } // Get all valid destination assets for a given source asset func (rv *RouteValidator) GetValidDestinations(fromAsset AssetId, allAssets []AssetId) ([]AssetId, error) { var validDestinations []AssetId for _, toAsset := range allAssets { if valid, err := rv.IsValidRoute(fromAsset, toAsset); err != nil { return nil, err } else if valid { validDestinations = append(validDestinations, toAsset) } } return validDestinations, nil } // Get all possible routes from a list of assets func (rv *RouteValidator) GetAllValidRoutes(assets []AssetId) ([]struct{From, To AssetId}, error) { var routes []struct{From, To AssetId} for _, fromAsset := range assets { for _, toAsset := range assets { if valid, err := rv.IsValidRoute(fromAsset, toAsset); err != nil { return nil, err } else if valid { routes = append(routes, struct{From, To AssetId}{From: fromAsset, To: toAsset}) } } } return routes, nil } // Private helper methods func (rv *RouteValidator) isInIsolationGroup(fromAsset, toAsset AssetId) bool { for _, group := range rv.policy.IsolationGroups { assets := rv.parseIsolationGroup(group) if rv.contains(assets, fromAsset) || rv.contains(assets, toAsset) { return true } } return false } func (rv *RouteValidator) isValidIsolationGroup(fromAsset, toAsset AssetId) bool { for _, group := range rv.policy.IsolationGroups { assets := rv.parseIsolationGroup(group) if rv.contains(assets, fromAsset) && rv.contains(assets, toAsset) { return true } } return false } func (rv *RouteValidator) isWhitelistOverride(fromAsset, toAsset AssetId) bool { for _, override := range rv.policy.WhitelistOverrides { if rv.matchesPattern(fromAsset, toAsset, override) { return true } } return false } func (rv *RouteValidator) isBlacklisted(fromAsset, toAsset AssetId) bool { for _, blacklist := range rv.policy.BlacklistPairs { if rv.matchesPattern(fromAsset, toAsset, blacklist) { return true } } return false } func (rv *RouteValidator) parseIsolationGroup(group string) []AssetId { parts := strings.Split(group, "<->") var assets []AssetId for _, part := range parts { assets = append(assets, AssetId(strings.TrimSpace(part))) } return assets } func (rv *RouteValidator) matchesPattern(fromAsset, toAsset AssetId, pattern string) bool { parts := strings.Split(pattern, "->") if len(parts) != 2 { return false } fromPattern := strings.TrimSpace(parts[0]) toPattern := strings.TrimSpace(parts[1]) return rv.matchesAssetPattern(fromAsset, fromPattern) && rv.matchesAssetPattern(toAsset, toPattern) } func (rv *RouteValidator) matchesAssetPattern(asset AssetId, pattern string) bool { // Handle wildcard patterns if pattern == "*" { return true } if strings.Contains(pattern, "*") { // Handle patterns like "starknet:*" or "*:USDC" if strings.HasSuffix(pattern, ":*") { chainPattern := pattern[:len(pattern)-2] return strings.HasPrefix(string(asset), chainPattern+":") } if strings.HasPrefix(pattern, "*:") { symbolPattern := pattern[2:] return strings.HasSuffix(string(asset), ":"+symbolPattern) } } // Exact match return string(asset) == pattern } func (rv *RouteValidator) contains(slice []AssetId, item AssetId) bool { for _, s := range slice { if s == item { return true } } return false } // Helper function to build route matrix for UI func BuildRouteMatrix(assets []AssetId, validator *RouteValidator) (map[AssetId][]AssetId, error) { matrix := make(map[AssetId][]AssetId) for _, fromAsset := range assets { destinations, err := validator.GetValidDestinations(fromAsset, assets) if err != nil { return nil, err } matrix[fromAsset] = destinations } return matrix, nil } ``` ```python Python expandable theme={null} import requests from typing import List, Dict, Optional, Tuple from dataclasses import dataclass import json # Types for the policy configuration @dataclass class RoutePolicy: default: str # "open" or "closed" isolation_groups: List[str] blacklist_pairs: List[str] whitelist_overrides: List[str] @dataclass class PolicyResponse: status: str result: RoutePolicy error: Optional[str] = None AssetId = str class RouteValidator: def __init__(self, api_base_url: str, api_key: str): self.api_base_url = api_base_url self.api_key = api_key self.policy: Optional[RoutePolicy] = None async def load_policy(self) -> None: """Fetch policy from the API""" try: headers = { 'garden-app-id': self.api_key, 'accept': 'application/json' } response = requests.get(f"{self.api_base_url}/policy", headers=headers) response.raise_for_status() data = response.json() if data['status'] == 'Ok': result = data['result'] self.policy = RoutePolicy( default=result['default'], isolation_groups=result['isolation_groups'], blacklist_pairs=result['blacklist_pairs'], whitelist_overrides=result['whitelist_overrides'] ) else: raise Exception(f"API Error: {data.get('error', 'Unknown error')}") except Exception as e: raise Exception(f"Failed to load policy: {e}") def is_valid_route(self, from_asset: AssetId, to_asset: AssetId) -> bool: """Check if a route is valid based on the policy""" if self.policy is None: raise Exception("Policy not loaded. Call load_policy() first.") # Can't swap to the same asset if from_asset == to_asset: return False # Check isolation groups first (highest priority) if self._is_in_isolation_group(from_asset, to_asset): return self._is_valid_isolation_group(from_asset, to_asset) # Check whitelist overrides (bypass other restrictions) if self._is_whitelist_override(from_asset, to_asset): return True # Check blacklist pairs if self._is_blacklisted(from_asset, to_asset): return False # Apply default policy return self.policy.default == 'open' def get_valid_destinations(self, from_asset: AssetId, all_assets: List[AssetId]) -> List[AssetId]: """Get all valid destination assets for a given source asset""" return [to_asset for to_asset in all_assets if self.is_valid_route(from_asset, to_asset)] def get_all_valid_routes(self, assets: List[AssetId]) -> List[Tuple[AssetId, AssetId]]: """Get all possible routes from a list of assets""" routes = [] for from_asset in assets: for to_asset in assets: if self.is_valid_route(from_asset, to_asset): routes.append((from_asset, to_asset)) return routes # Private helper methods def _is_in_isolation_group(self, from_asset: AssetId, to_asset: AssetId) -> bool: for group in self.policy.isolation_groups: assets = self._parse_isolation_group(group) if from_asset in assets or to_asset in assets: return True return False def _is_valid_isolation_group(self, from_asset: AssetId, to_asset: AssetId) -> bool: for group in self.policy.isolation_groups: assets = self._parse_isolation_group(group) if from_asset in assets and to_asset in assets: return True return False def _is_whitelist_override(self, from_asset: AssetId, to_asset: AssetId) -> bool: return any(self._matches_pattern(from_asset, to_asset, override) for override in self.policy.whitelist_overrides) def _is_blacklisted(self, from_asset: AssetId, to_asset: AssetId) -> bool: return any(self._matches_pattern(from_asset, to_asset, blacklist) for blacklist in self.policy.blacklist_pairs) def _parse_isolation_group(self, group: str) -> List[AssetId]: """Parse 'ethereum:SEED <-> arbitrum:SEED' format""" return [asset.strip() for asset in group.split('<->')] def _matches_pattern(self, from_asset: AssetId, to_asset: AssetId, pattern: str) -> bool: parts = [p.strip() for p in pattern.split('->')] if len(parts) != 2: return False from_pattern, to_pattern = parts return (self._matches_asset_pattern(from_asset, from_pattern) and self._matches_asset_pattern(to_asset, to_pattern)) def _matches_asset_pattern(self, asset: AssetId, pattern: str) -> bool: """Handle wildcard patterns""" if pattern == '*': return True if '*' in pattern: # Handle patterns like "starknet:*" or "*:USDC" if pattern.endswith(':*'): chain_pattern = pattern[:-2] return asset.startswith(chain_pattern + ':') if pattern.startswith('*:'): symbol_pattern = pattern[2:] return asset.endswith(':' + symbol_pattern) # Exact match return asset == pattern # Helper function to build route matrix for UI def build_route_matrix(assets: List[AssetId], validator: RouteValidator) -> Dict[AssetId, List[AssetId]]: """Build route matrix for UI components""" matrix = {} for from_asset in assets: matrix[from_asset] = validator.get_valid_destinations(from_asset, assets) return matrix # Usage example async def example(): validator = RouteValidator('https://testnet.api.garden.finance/v2', 'your-api-key') try: # Load policy from API await validator.load_policy() # Example assets assets = [ 'ethereum:SEED', 'arbitrum:SEED', 'bitcoin:BTC', 'ethereum:WBTC', 'starknet:WBTC' ] # Check specific routes print('ethereum:SEED -> arbitrum:SEED:', validator.is_valid_route('ethereum:SEED', 'arbitrum:SEED')) print('ethereum:SEED -> bitcoin:BTC:', validator.is_valid_route('ethereum:SEED', 'bitcoin:BTC')) print('bitcoin:BTC -> starknet:WBTC:', validator.is_valid_route('bitcoin:BTC', 'starknet:WBTC')) # Get all valid destinations from ethereum:SEED valid_destinations = validator.get_valid_destinations('ethereum:SEED', assets) print('Valid destinations from ethereum:SEED:', valid_destinations) # Get all valid routes all_routes = validator.get_all_valid_routes(assets) print('All valid routes:', all_routes) except Exception as error: print('Error:', error) ``` ## Integration guide ```typescript theme={null} import { RouteValidator, buildRouteMatrix } from './RouteValidator'; // Initialize the RouteValidator with your API configuration. const validator = new RouteValidator( 'https://testnet.api.garden.finance/v2', 'your-api-key' ); //Fetch the policy configuration from the API. try { await validator.loadPolicy(); } catch (error) { throw new Error('Failed to load policy:', error); } ``` Use the validator to check if specific routes are allowed: ```typescript theme={null} // Check individual routes. const isValid = validator.isValidRoute('ethereum:SEED', 'arbitrum:SEED'); // Get all valid destinations for a source asset. const destinations = validator.getValidDestinations('bitcoin:BTC', allAssets); // Generate complete route matrix for UI. const routeMatrix = buildRouteMatrix(allAssets, validator); ``` ## Wildcard patterns The system supports wildcard patterns for flexible policy configuration: Match all assets on a specific chain: ```typescript theme={null} // Block all routes from Bitcoin to any Starknet asset. "bitcoin:BTC -> starknet:*" // Allow any Ethereum asset to trade with Bitcoin. "ethereum:* -> bitcoin:BTC" ``` Match specific assets across all chains: ```typescript theme={null} // Block all WBTC variants from trading with native BTC. "*:WBTC -> bitcoin:BTC" // Allow all USDC variants to trade with each other. "*:USDC -> *:USDC" ``` Match any asset (use with caution): ```typescript theme={null} // Allow any asset to trade with Ethereum WETH. "* -> ethereum:WETH" ``` ## Best practices 1. **Store the policy configuration locally** and refresh it periodically rather than fetching it on every route validation. 2. **Implement proper error handling** and refresh the policy when a get quote or create order fails due to an unsupported pair. # About Garden Source: https://docs.garden.finance/home/about Swap Bitcoin trustlessly anywhere in as little as 30 seconds. Garden is an intent-based Bitcoin rails protocol. You express what you want to swap, an open network of solvers competes to fill the order, and settlement happens on-chain via HTLC contracts. Both sides complete or neither does — your funds stay in your control, start to finish. Garden has processed billions of dollars in volume, powering Bitcoin swaps across apps like Phantom, MetaMask, Lightspark, LI.FI, and many more. ## Use cases Fast, self-custodial Bitcoin swaps across chains.\ [**Try Garden →**](https://app.garden.finance) Integrate native Bitcoin swaps into your product in 30 minutes using Garden's [**API**](/developers/api/overview) or [**SDK**](/developers/sdk/overview). Earn by solving on Garden's open execution network.\ [**Apply to become a solver →**](https://docs.google.com/forms/d/e/1FAIpQLSfZcxgttWipkOwygbkBrjU0H0nFD_EUNpH9eu0c-lKPEpbPHA/viewform?usp=publish-editor) [**Stake SEED**](https://app.garden.finance/stake) to back solvers, earn Bitcoin rewards, and participate in governance. # Address Screening Source: https://docs.garden.finance/home/compliance/address_screening Overview of Garden’s risk monitoring process Garden is a permissionless protocol for peer-to-peer swaps of Bitcoin and other assets through a network of solvers. The protocol is non-custodial and open for anyone to participate as a solver. Garden integrates **TRM Labs, ZeroShadow, Bybit** and **Binance** screening APIs to support responsible usage and prevent sanctioned entities from interacting with the protocol. * Wallet addresses connecting to the Garden interface are automatically screened through the integrated APIs. * If an address is identified as sanctioned, the swap functionality is disabled. * When a swap involves Bitcoin deposits, the depositing address is also screened. If flagged, solvers are signaled not to initiate the swap. And, the user can refund their Bitcoin once the swap expires. * Orders submitted via third-party integrations using Garden’s SDK or APIs follow the same screening process. # Better Prices Source: https://docs.garden.finance/home/fundamentals/benefits/better-prices Garden delivers unmatched rates by leveraging on-chain and off-chain liquidity Traditional bridges often rely on AMMs to facilitate swaps, exposing users to slippage based on the depth of the liquidity pools. This is especially inefficient for swaps between likewise assets, such as BTC and WBTC or cbBTC. Garden's intents-based architecture removes this inefficiency. [Solvers](/home/fundamentals/core-concepts/solvers) maintain flexibility in their quotes and compete to fulfill user intents using on-chain and off-chain liquidity. Users not only get a competitive price at the time of swap initiation but also have the potential to settle at better prices. By leveraging peer-to-peer matching, and advanced settlement mechanisms like [xCoW](/home/fundamentals/how-it-works/cross-chain-coincidence-of-wants-xcow), Garden dynamically optimizes swaps during execution. When users initiate a swap, they are shown a minimum guaranteed quote. During settlement, however, Garden's price improvement capabilities can unlock better pricing. ## How price improvement works Solvers can tap into private off-chain liquidity sources not available to public DEXs Direct matching between users with complementary intents eliminates intermediary fees Advanced coincidence of wants matching reduces slippage and improves execution Solvers compete to provide the best price through the auction mechanism # Broad Chain Coverage Source: https://docs.garden.finance/home/fundamentals/benefits/broad-chain-coverage Garden's simple design enables quick-and-easy support for new chains Supporting many chains typically requires maintaining liquidity pools on each chain. This ties up idle capital, limiting how many chains traditional bridges can practically support. Using an [intents](/home/fundamentals/core-concepts/intents)-based model removes the need for pre-deployed liquidity. Liquidity is provided on demand at the moment of execution. This allows support for a wide range of blockchains, without additional liquidity constraints. New chains can be supported without requiring liquidity pools, reducing the operational and capital overhead typically needed to expand coverage. Liquidity is provided on demand through intents, allowing assets to move seamlessly across chains while keeping the system scalable and efficient. # Free Option Protection Source: https://docs.garden.finance/home/fundamentals/benefits/free-option-protection Garden protects against the free option problems via its incentive structure The **free option problem** in [atomic swaps](/home/fundamentals/core-concepts/atomic-swaps) arises when one party exploits market conditions by deciding to complete or abort a swap based on price movements, leaving the counterparty at a disadvantage. These cases are covered in [scenarios and safeguards](/home/fundamentals/core-concepts/atomic-swaps#scenarios-and-safeguards). Garden addresses this challenge with built-in mechanisms that ensure fairness and accountability for the participants. ## Solver safeguards Solver and its respective stakers are slashed if the solver fails to initiate a deposit on their side after the user's deposit. The slashed amount is awarded to the user. This penalty directly discourages solver misuse and ensures stakers will keep solvers accountable as they can always switch their vote to another solver. Settlement speed is already factored into the solver score [formula](/home/fundamentals/core-concepts/auctions), reducing the ability of unreliable solvers to win future intents. ## User safeguards If a solver fails to initiate an asset after a being matched with the user, a portion of the stakers' and solvers' SEED stake (proportional to the trade size) is slashed and awarded to the user. Stakers earn yield from the protocol and govern its operations, making them responsible for ensuring users interact with the UI effectively. # No Custody Risk Source: https://docs.garden.finance/home/fundamentals/benefits/no-custody-risk Garden eliminates custody risks through peer-to-peer atomic swaps Many bridging solutions require you to hand over control of your assets to an intermediary. This can take the form of a multisig wallet, validator network, or a trusted intermediary. These risks are well-documented: Hacks like the \$600M Ronin Bridge exploit or exchange collapses (e.g., Mt. Gox, FTX) highlight the catastrophic risks of entrusting funds to third parties. Even decentralized solutions relying on Multi-Party Computation (MPC) or liquidity pools aren't immune, as seen with the Nomad Bridge (\$190M hack) or smart contract bugs in Wormhole (\$325M hack). These examples highlight how shared custody models, even those claiming "self-custody", can expose users to significant risks. ## How Garden eliminates custody risks Garden's approach eliminates this category of risk by ensuring users maintain complete control of their assets throughout the bridging process. At no point does Garden or any intermediary gain custody. Here's how user custody is maintained at every step of the Intent Flow: User intents are cryptographically signed and broadcast without transferring custody. No funds are required to leave the user's wallet at this stage. No custody risk arises here, as funds are not involved during the solver selection process. After a solver is selected, the user locks their funds in a Hashed Timelock Contract (HTLC) on the source chain. HTLCs enforce conditional asset transfers using cryptographic hashes and timelocks. This ensures: * Funds can only be claimed by the rightful owner when swap conditions are met * Funds are automatically refunded to the user if the timelock expires without settlement The user redeems their funds on the destination chain using a cryptographic secret. This guarantees: * The solver can only claim funds on the source chain after fulfilling their obligations on the destination chain * Simultaneous execution prevents custody transfer to intermediaries at any point For more details, see [atomic swaps](/home/fundamentals/core-concepts/atomic-swaps) and [intent flow](/home/fundamentals/how-it-works/intent-flow). # Overview Source: https://docs.garden.finance/home/fundamentals/benefits/overview Discover the unique advantages that Garden offers over traditional bridge solutions. Maintain complete control of your assets at every step Trade any asset across any chain with flexible intent expression Get competitive prices with potential for price improvement Built-in safeguards prevent exploitation by malicious actors # Atomic Swaps Source: https://docs.garden.finance/home/fundamentals/core-concepts/atomic-swaps Garden uses atomic swaps for trustless cross-chain settlements Atomic swaps are trustless peer-to-peer swaps that are used for settlement between user and the solver chosen to implement their intent. The atomic nature of these swaps comes from the fact that they are designed in a way to either complete the swap or not do it at all, there is no intermediate state. As a result, users and solvers don't lose control of their asset at any point of the swap process. Traditional bridges require you to give up control of your asset during the bridging process to the custodian (WBTC), intermediary network (Threshold, Thorchain), or a multisig (Avalanche bridge). Garden uses **Hash Time Locked Contracts** (HTLCs) to implement atomic swaps. As the name suggests, they are used to enforce time-bound conditions for transferring assets between two parties. They require the receiver of a transaction to acknowledge receiving the payment by generating a cryptographic proof within a certain timeframe. This proof is a response to a cryptographic challenge embedded in the contract, typically involving a hash function. If the receiver fails to provide the correct proof within the specified time, the transaction is automatically reversed, returning the assets to the sender. This is better explained with an example and flow chart. ## How do atomic swaps work? Let's take our case, user gives an intent to swap. The auction house selects a solver for this. After executing the intent, the solver and the user have to settle (swap) their assets. Atomic Swap Process 1. User deposits asset A into a transparent vault on chain A. 2. Solver checks the deposited amount and, if it matches the expected amount, deposits asset B into an identical transparent vault on chain B. 3. User (client UI) verifies the amount deposited by the solver and, if correct, opens the vault to collect asset B on chain B. 4. Solver can now collect asset A from the user's vault on chain A; the swap is complete. This is what we call a happy flow where everything runs as expected. In the next section, failure scenarios in this system and Garden's safeguards against them will be explained. ## Scenarios and safeguards ### 1. Lock Asset A and B happens, but claim asset B doesn't happen Failure Scenario 1 * **Penalty:** Stakers are slashed 1% of the intent value. * **Solver resolution:** Refund of Asset B after 24 hours, plus slashed SEED as compensation. * **User resolution:** Refund of Asset A after 48 hours. ### 2. Lock Asset B doesn't happen Failure Scenario 2 * **Penalty:** Solver and stakers are slashed 0.5% of the intent value. * **User resolution:** Refund of Asset A after 48 hours, plus slashed SEED as compensation. # Auctions Source: https://docs.garden.finance/home/fundamentals/core-concepts/auctions Garden's auction system selects solvers to execute user intents efficiently The auction process identifies a solver to execute user intents, such as performing a swap. Garden's auction system ensures intents are fulfilled efficiently and at competitive prices. Garden balances both competition and fairness by leveraging **solver scores** to create a system where solvers are incentivized to optimize their performance while maintaining accountability to their stakers. ## Solver score The solver score combines performance efficiency and vote power to determine a solver's priority in the auction process. It is calculated as: **Settlement score for a specific chain C:** $$ \text{Settlement Score}_{C} = \frac{\text{Average Settlement Time}_{C} - \text{Solver's Settlement Time}_{C}}{\text{Standard Deviation}_{C}} $$ **Weighted settlement score across all chains:** $$ \text{Weight}_{C} = \frac{\text{Solver Settlements}_{C}}{\text{Total Solver Settlements}} $$ $$ \text{Settlement Score} = e^{\left(\sum_{C} \left(\text{Weight}_{C} \times \text{Settlement Score}_{C}\right)\right)} $$ **Final solver score:** $$ \text{Solver Score} = \left(\frac{\text{Total Volume on Garden} \times \text{Stake Against Solver}}{\text{Volume Filled by Solver} \times \text{Total Staked Amount}}\right) \times \text{Settlement Score} $$ Solvers with a score greater than 1 get priority in the auction. Solver scores are calculated before every auction. ## How does a typical auction work? 1. All solvers submit their quotes for a given intent to the order book. Each quote specifies the solver's proposed execution price for fulfilling the intent. 2. The order book identifies the solver with the best price (i.e., the lowest quote). 3. Solvers with a **staker score** higher than the solver with the best quote have a 5-second window to accept the best quote. This mechanism ensures that solvers with higher backing from stakers have the opportunity to get first dibs on intents. 4. The outcome depends on the participation during the acceptance period: ### Auction outcomes The solver with the best quote wins the auction and fills the order. The solver who accepts the quote gets the order. Single Solver Selection Among the solvers who accept the best quote, the one with the highest **staker score** wins the auction and fills the order. Multiple Solver Selection # Intents Source: https://docs.garden.finance/home/fundamentals/core-concepts/intents Garden's intent-based approach pushes the boundaries of cross-chain interactions Intents offer a fundamentally user-centric approach to on-chain interactions. Instead of signing a raw transaction—a rigid sequence of machine-readable instructions dictating what actions to take (as seen on platforms like Uniswap or Aave)—users define an overarching objective, or "intent," and sign that instead. This intent encapsulates the desired outcome in a structured message. For Garden, this means specifying parameters such as assets, chains, and amounts for the swap. Intent vs Transaction Requirements By abstracting away the granular steps, intents align the interaction more closely with desired goals, making the process both intuitive and efficient. This approach is increasingly adopted as a standard in the industry, with Across using intents-based bridging and CoW Protocol applying intents for DEX trading. ## Why do we choose intents? Intents bring flexibility, security, and efficiency to on-chain interactions: Intents enable solvers to tap into any on-chain/off-chain liquidity, including private order flows, **boosting accessible liquidity** compared to conventional methods. Solvers can **match users directly** against each other, potentially offering better prices without relying solely on external liquidity pools. Intents **shield users from MEV** bots and public mempool vulnerabilities by operating in a safe, walled-garden environment. Users can execute orders across chains without needing to hold the native gas token like ETH, making cross-chain interactions **seamless and gasless**. Users only **pay for successful executions**, eliminating wasted fee costs. Intents enable solvers to batch multiple orders, **reducing execution costs** and passing savings to users through better quotes. They **remove the need for custodians** (centralized or decentralized), mitigating liquidity risks and enhancing trustlessness in the system. Multiple solvers **compete to fulfill your intent**, driving innovation in execution strategies and ensuring you get the **best possible price and speed** through market forces. # Solvers Source: https://docs.garden.finance/home/fundamentals/core-concepts/solvers Understand how solvers act as market makers in Garden protocol to execute user intents efficiently Solvers are the market makers of Garden protocol, responsible for ensuring user [intents](./intents) are executed efficiently and securely. By leveraging diverse liquidity sources—on-chain, off-chain, and private order flows—solvers are incentivized to optimize every transaction for users, delivering competitive quotes. Solvers address key challenges in decentralized systems. They enhance liquidity access by enabling the protocol to utilize existing DeFi liquidity rather than fragmenting it with native pools. For users, solvers offer [benefits](./intents#why-do-we-choose-intents%3F) such as better pricing, MEV resistance, and higher chances of transaction finality, making the system more efficient and user-friendly. ## How do solvers work? Solvers receive user intents via the [order book](./auctions), detailing the desired outcomes such as assets, chains, and amounts. Solvers evaluate available liquidity sources and identify the most efficient path to execute the intent. Each solver provides their best quote back to the order book, which selects the most competitive option for optimal user pricing. Once chosen, solvers fulfill the user's intent by leveraging their tools and liquidity. ## How to become a solver? To become a solver, here's what you need: Solvers must stake **210,000 SEED** as collateral. This stake aligns solvers with the network's goals and acts as a safeguard against dishonesty or inefficiency. Solvers should have the ability to run arbitrage bots, manage liquidity, and maintain 24/7 operational uptime to meet the demands of intent execution. Adequate liquidity and tools for rebalancing assets are essential to efficiently execute intents. Please fill out this [form](https://docs.google.com/forms/d/e/1FAIpQLSfZcxgttWipkOwygbkBrjU0H0nFD_EUNpH9eu0c-lKPEpbPHA/viewform) to onboard as a Solver, and our team will get in touch shortly. # Stakers Source: https://docs.garden.finance/home/fundamentals/core-concepts/stakers Stakers support Garden protocol through token staking and solver governance Staking serves as the gateway for users to engage with the Garden protocol, supporting its decentralization, efficiency, and accountability. By staking **SEED tokens**, stakers can vote for trusted [solvers](./solvers) to execute intents and earn rewards based on their performance. This system incentivizes long-term [commitment](https://dune.com/garden_finance/gardenfinance#stake-your-seed), balances solver selection, and helps maintain the protocol's fairness and reliability. ## How does it work? Anyone holding a minimum of **2,100 SEED** tokens is eligible to [stake](https://app.garden.finance/stake/). Staking has to be done in multiples of 2,100 SEED. Stakers deposit **SEED tokens** into the protocol, locking them for a chosen duration. Longer lock periods result in a higher staking multiplier, increasing both votes and potential rewards. Follow [this tutorial](/home/resources/guides/stake/stake-seed) for a step-by-step breakdown of staking. For those looking to maximize their staking benefits, staking 21,000 SEED permanently mints the [Gardener pass](/home/governance/gardener-pass), an exclusive NFT that unlocks enhanced rewards, governance power, and tradability. Staking Multiplier and Rewards System Stakers allocate their votes to specific solvers based on performance metrics and projected APY. A solver's ability to process intents is proportional to the votes they receive. Stakers earn a share of solver fees based on their voting weight. Staking rewards are distributed on a weekly basis in **Bitcoin**. The staking multiplier ensures that longer commitments lead to higher APY, encouraging stability in the protocol. If a solver fails or behaves maliciously, the stakers who voted them face a minor penalty, ensuring accountability and improving solver reliability over time. ## Why are stakers important? Stakers are not necessarily a passive actor in Garden protocol, they are incentivized to: Maintain accountability among solvers because stakers would only vote for solvers who settle all their assigned intents (complete rate) without downtime (speed). Remove solver monopoly as staker rewards are adjusted by the voting concentration of a particular solver. Stakers seeking higher returns are incentivized to distribute their votes across underrepresented solvers, ensuring a balanced and fair ecosystem. Phase out solvers who are old and not functional, as stakers weekly rewards are tied to the solver's volume. If the solver is not functioning, the stakes doesn't get any rewards and will change their vote to an active solver. # Cross-chain Coincidence of Wants (xCoW) Source: https://docs.garden.finance/home/fundamentals/how-it-works/cross-chain-coincidence-of-wants-xcow Garden matches complementary intents to optimize cross-chain trades and reduce reliance on external liquidity Garden's xCoW mechanism builds upon the concept of Coincidence of Wants (CoW), pioneered by CoW Swap for single-chain environments and extended into the cross-chain space, similar to Across Protocol. What sets Garden apart is its ability to integrate Proof-of-Work (PoW) chains like Bitcoin, overcoming the challenges of building trustless interoperability on these networks. By enabling seamless matching of complementary intents, Garden reduces reliance on external liquidity, optimizes pricing, and enhances transaction efficiency across multiple chains. ## Types of xCoW scenarios ### Simple user-to-user matching Simple xCoW User-to-User Matching xCoW matches users with complementary intents directly. For example, if one user wants to swap Bitcoin (BTC) for USDC and another wants to swap USDC for BTC, xCoW connects them peer-to-peer, eliminating intermediaries and improving pricing and execution speed. ### Trader rings (more than 2 participants) Multi-participant Trading Rings For intents involving three or more participants, xCoW creates trading rings. These interconnected settlements optimize liquidity usage and reduce costs, enabling trades that might otherwise rely on fragmented liquidity pools to be executed efficiently across multiple users and chains. ### Intermediary CoW events Intermediary CoW Events When trades require intermediate assets, xCoW matches overlapping intents to improve execution. For example: * A user swaps Bitcoin (BTC) for Shiba Inu (SHIB). * Another swaps Bonk (BONK) for Bitcoin (BTC). Both trades pass through USDC as an intermediary: * BTC → **USDC** → SHIB * BONK → **USDC** → BTC Here, xCoW matches the BTC ↔ USDC portion directly between users, minimizing slippage and improving pricing for both trades. ### Batching Transaction Batching xCoW supports batching in order to reduce gas costs. Multiple transactions are grouped and submitted on-chain as a single operation, lowering the gas cost per transaction. This mechanism ensures cost efficiency at scale, particularly for high-volume trade scenarios. # Intent Flow Source: https://docs.garden.finance/home/fundamentals/how-it-works/intent-flow Understand the complete technical flow of how user intents are processed in Garden protocol Garden Protocol Architecture Overview Now that we know all the actors and modules in the system, let's take a detailed technical look at how all of them come together. Here's a simplified representation of the process: User requests quote → Signs intent → Submits intent to order book Order book selects solver → Solver begins settlement User initiates transaction → Solver executes on destination chain User redeems funds → Solver completes settlement Here is the detailed look into each step of the process. ## Creating an intent The user starts the process by requesting a **quote** for a desired swap. The quote contains details like the input asset, output asset, chains involved, and the desired amounts. The user receives the quote and must **accept it** by creating a **signed intent message.** This signed intent represents the user's objective, including parameters like assets, chains, amounts, and expiration details. ## Selection of solver in the order book Once the user signs the intent, it is submitted to the **order book**, a decentralized system where solvers compete to fulfill the intent. ### Auction process: All solvers submit their quotes based on the intent's parameters. The **best quote** (lowest price or highest efficiency) is determined. Solvers with a **higher staker score** than the solver with the best quote have a 5-second window to match the quote. * **No acceptance:** The best-quote solver wins. * **One acceptance:** The accepting solver wins. * **Multiple acceptances:** The solver with the highest staker score wins. Once a solver is selected, they prepare to execute the swap. ## Settlement: User-side execution The settlement process begins once the selected solver is confirmed. The user must **initiate settlement** by creating a **signed transaction message** for the intent. The signed message is submitted to a **relayer**, which broadcasts it on the blockchain. The transaction is processed on-chain, and the user's funds are locked or transferred to the relevant contract or wallet. Once the transaction achieves the required number of blockchain confirmations, the settlement progresses to the solver's side. ## Settlement: Solver-side execution The solver, upon receiving confirmation of the user-side transaction, initiates the corresponding transaction on their end. This step involves the solver locking funds on the destination chain, earmarked for the user. The solver's transaction also undergoes a confirmation process on the destination chain to ensure security and reliability. ## Redemption After the solver's transaction achieves the required confirmations: * The user can redeem funds on destination revealing the swap prehash. Once the user reveals the prehash the solver is able to recieve funds on the source chain, completing the swap. # Governance Source: https://docs.garden.finance/home/governance Garden is governed entirely by Garden token holders. Explore the governance sections below to learn more about tokenomics, governance processes, and how to participate. Learn about SEED token distribution and economics Understand how decisions are made in Garden Learn about the Gardener Pass NFT program Participate in governance voting through Snapshot # Gardener Pass Source: https://docs.garden.finance/home/governance/gardener-pass Gardener Passes unlock the highest level of access and recognition in the Garden community Initially introduced during the [seasons](https://garden.finance/blog/seasons-intro) program, Gardener pass can now be minted by staking 21,000 SEED perpetually. Each pass offers holders exclusive access to a range of benefits and opportunities within the ecosystem: Pass holders gain a 7x multiplier on their staking rewards. Staking 21,000 SEED normally gives you 10 governance votes. However, Gardener Pass holders receive 70 votes because of the 7x multiplier, offering substantial influence in protocol governance and revenue sharing. As a fully tradable NFT, the pass can be sold or transferred on [marketplaces](https://opensea.io/collection/gardener-pass). Minting a pass requires permanently staking SEED, effectively burning it, which establishes its inherent value. You can mint the Gardener Pass by staking **21,000 SEED** indefinitely on the [staking page](https://app.garden.finance/stake). ## What's next? As we step into Act 2, the pass becomes more than just a feature—it's a way to truly be part of Garden's journey. With it, you'll get **early access to new features** and **opportunities to participate more deeply** in what we're building together. Staking SEED to get the pass isn't just about governance power or staking rewards. It's about aligning with Garden's growth and long-term vision. This ecosystem is owned by the community, and the pass is a **rite of passage** for anyone ready to step up and help steer its direction. By committing, you're not just investing—you're shaping the future alongside us. The value goes beyond rewards—it's about having a real say in where we go from here. # Governance Process Source: https://docs.garden.finance/home/governance/governance-process Garden's community governance process overview, from temp checks to implementation To participate in Garden's governance, one must be an active [staker](/home/fundamentals/core-concepts/stakers). The number of governance votes you own is dependent on the staking multiplier, similar to staking rewards. Voting Multiplier Chart For example, if you staked 21,000 SEED for 24 months, then your total voting power is 30 votes. ## Proposal process Before raising a proposal at Garden, it is essential to initiate a [Temp Check](https://gov.garden.finance/). This preliminary step allows you to gauge community interest and determine whether there is sufficient support to alter the status quo. After your proposal passes the temp check, you can create a formal proposal for community voting on [Snapshot](https://snapshot.box/#/s:gardenfinance.eth/). The voting phase lasts for 7 days where stakers cast their votes via Snapshot. Once a proposal achieves quorum, the Garden core team works with the proposer to ensure effective implementation. ### Temp check #### Engage with community You can share your idea with the community and gather feedback by raising a **temp check** on [https://gov.garden.finance/](https://gov.garden.finance/). This is the first step to understand perspectives and build alignment. A temp check is considered **passed** once it receives **10 upvotes**. Once that happens, the idea moves to the **Garden Improvement Proposal (GIP)** stage. At this stage, gardeners who have staked SEED will formally vote on the proposal via Snapshot. ### Raise a proposal After your proposal passes the temp check, you can start creating a proposal. A formal proposal marks the transition from idea to action. Proposals have to be submitted for community voting on [Snapshot](https://snapshot.org/#/gardenfinance.eth). In your proposal, clearly state the problem you aim to address and describe the proposed solution in detail. Outline actionable steps required to implement this solution, providing links to any relevant code repositories or resources. Additionally, discuss the expected outcomes, detailing both the potential benefits and any risks. Raising a proposal requires a minimum of **500 votes**. Proposers are responsible for finding a community backer with enough votes to sponsor their proposal. ### Community voting The voting phase lasts for **7 days**, where stakers cast their votes via snapshot. Proposers can use this time to actively engage with the community in the [Townhall](https://discord.gg/B7RczEFuJ5) to generate excitement and support for their proposal. Encourage discussions, answer questions, and highlight the benefits of the proposal to motivate community members to participate in the voting process. ### Implementation Once a proposal achieves a **quorum of at least 6,000 votes**, it is officially passed. The Garden core team will then work with the proposer to ensure effective implementation. This process ensures alignment with Garden's strategic goals and community interests. # Snapshot Source: https://docs.garden.finance/home/governance/snapshot Garden uses Snapshot to enable gasless governance voting Single-choice 6,000 votes 7 days gardenfinance.eth ## Roles on snapshot Garden's Snapshot system features two key roles: * Manages and oversees the Snapshot space. * Maintains the integrity of the governance process. * Can create and submit proposals for voting. * Any staker with more than 500 votes is eligible to become an author. ## Voting power Voting power in Garden is determined by staking [multipliers](/home/fundamentals/core-concepts/stakers) and ownership of the Gardener Pass NFT. Stakers gain increased voting power based on their staking multiplier, which rewards long-term commitment to the protocol. Additionally, Gardener Pass NFT holders receive a fixed voting power of 70 votes. # Tokenomics Source: https://docs.garden.finance/home/governance/tokenomics SEED is the utility token of Garden, designed to facilitate participation in the Garden protocol The SEED token launched on [January 18th, 2024](https://etherscan.io/tx/0x02446e6d65cef97f2a172382179c035bf5cd5738dfc1ba3c01c7f8a8439ec00d). It empowers holders to actively shape the Garden ecosystem: Stake SEED to participate in protocol governance and earn revenue. Staking 210,000 SEED tokens grants eligibility to become a solver. Participate in protocol decision-making, or have a say in selecting community incentive programs and treasury allocation. Burn 21,000 SEED to mint a Gardener Pass, unlocking early access to new features, exclusive contests, and other benefits. ## Contract addresses | Chain | Address | | -------- | ----------------------------------------------------------------------------------------------------------------------- | | Ethereum | [`0x5eed99d066a8CaF10f3E4327c1b3D8b673485eED`](https://etherscan.io/address/0x5eed99d066a8CaF10f3E4327c1b3D8b673485eED) | | Arbitrum | [`0x86f65121804D2Cdbef79F9f072D4e0c2eEbABC08`](https://arbiscan.io/address/0x86f65121804D2Cdbef79F9f072D4e0c2eEbABC08) | # How to swap BTC to any EVM asset Source: https://docs.garden.finance/home/resources/guides/swap/btc-wbtc Step-by-step guide to swapping Bitcoin for Wrapped Bitcoin (WBTC) on Ethereum / Arbitrum using Garden Convert your native Bitcoin to Wrapped Bitcoin (WBTC) on Ethereum or Arbitrum networks. WBTC maintains a 1:1 peg with Bitcoin and can be used in DeFi protocols. The flow is same for BTC to any EVM based asset. This guide covers swapping **Bitcoin (BTC)** to **Wrapped Bitcoin (WBTC)** on Ethereum or Arbitrum. The process takes approximately 30 seconds to complete. ## What You'll Need * **EVM Wallet**: MetaMask, Coinbase Wallet, or other EVM-compatible wallet * **Bitcoin Wallet**: Optional (you can swap without connecting) * Bitcoin (BTC) in your Bitcoin wallet or ready to send manually ## Method 1: With Bitcoin Wallet Connected (Recommended) This is the easiest method as Garden handles the Bitcoin transaction for you. 1. Visit [Garden Finance](https://app.garden.finance) 2. Click **Connect Wallet** and connect your **EVM wallet** 3. Click **Connect Bitcoin Wallet** and connect your Bitcoin wallet Connect wallets to Garden 1. In the **Send** section, select **Bitcoin (BTC)** 2. In the **Receive** section, select **WBTC** and choose your preferred network: * **Ethereum**: For use in Ethereum DeFi protocols * **Arbitrum**: For lower fees and faster transactions 3. Enter the amount of BTC you want to swap Configure BTC to WBTC swap 1. Review the swap details: * **Exchange rate**: Current BTC to WBTC rate * **Fees**: Network fees and Garden protocol fees * **Estimated completion time**: Usually under 30 seconds 2. Click **Swap** to proceed 1. **EVM Wallet**: First, approve the transaction in your EVM wallet 2. **Bitcoin Wallet**: Then approve and send the Bitcoin transaction Approve transactions Keep the browser tab open while the swap processes. You'll see real-time updates and receive a notification when complete. Swap in progress ## Method 2: Manual Bitcoin Transfer If you prefer not to connect your Bitcoin wallet, you can send Bitcoin manually. 1. Visit [Garden Finance](https://app.garden.finance) 2. Connect only your **EVM wallet** (skip Bitcoin wallet connection) 1. Enter your **Bitcoin refund address** manually 2. This is where your BTC will be returned if the swap fails 3. **Double-check this address** - incorrect addresses cannot be recovered Enter Bitcoin refund address 1. Configure your swap (BTC to WBTC) 2. Enter the amount and click **Swap** 3. Approve the transaction in your EVM wallet 1. Copy the **Bitcoin deposit address** shown on the swap page 2. Send the **exact BTC amount** from your Bitcoin wallet to this address 3. **Important**: Send the exact amount shown - sending more or less may cause issues Manual Bitcoin deposit Monitor the swap progress on the page. The swap will complete once the solver receives your Bitcoin. Successful swap ## Understanding WBTC **Wrapped Bitcoin** is an ERC-20 token backed 1:1 by Bitcoin, allowing BTC to be used in Ethereum DeFi protocols. **DeFi Integration**: Use WBTC in lending protocols, DEXs, yield farming, and other DeFi applications. ## Network Options ### Ethereum * **Pros**: Largest DeFi ecosystem, highest liquidity * **Cons**: Higher gas fees * **Best for**: Large swaps, maximum DeFi compatibility ### Arbitrum * **Pros**: Lower fees, faster transactions * **Cons**: Smaller ecosystem than Ethereum * **Best for**: Smaller swaps, cost-efficient transactions ## Important Notes **Exact Amounts**: When sending Bitcoin manually, always send the exact amount shown. Sending more or less than the specified amount can cause delays or require manual intervention. **Security**: Garden uses atomic swaps, meaning your Bitcoin is never at risk. If the swap fails, your BTC will be automatically refunded to your specified address. **First-Time Users**: The first transaction may take slightly longer due to blockchain confirmations. Subsequent swaps in the same session are faster. ## Next Steps After receiving your WBTC: * **Ethereum WBTC**: Can be used immediately in Ethereum DeFi protocols * **Arbitrum WBTC**: Can be used in Arbitrum DeFi protocols or bridged to other networks * **Track Your Assets**: WBTC will appear in your EVM wallet on the selected network ## Need Help? * **Questions**: Join our [Discord community](https://discord.gg/B7RczEFuJ5) * **Technical Issues**: Reach out for [support on Discord](https://discord.gg/B7RczEFuJ5) Want to swap back? Check out our guide on [how to swap WBTC to BTC](/home/resources/guides/swap/wbtc-btc). # How to swap any EVM asset to BTC Source: https://docs.garden.finance/home/resources/guides/swap/wbtc-btc Step-by-step guide to swapping Wrapped Bitcoin (WBTC) back to native Bitcoin using Garden Finance Convert your Wrapped Bitcoin (WBTC) back to native Bitcoin. This process "unwraps" your WBTC from Ethereum or Arbitrum networks and returns it as native Bitcoin. This guide covers swapping **Wrapped Bitcoin (WBTC)** to **Bitcoin (BTC)**. The process typically completes in under 30 seconds. ## What You'll Need * **EVM Wallet**: MetaMask, Coinbase Wallet, or other EVM-compatible wallet with WBTC * **Bitcoin Wallet**: Optional (you can provide a Bitcoin address manually) * WBTC tokens on Ethereum or Arbitrum * Small amount of ETH or ARB for gas fees to approve the transaction ## Method 1: With Bitcoin Wallet Connected (Recommended) This method automatically handles the Bitcoin address for receiving your unwrapped Bitcoin. 1. Visit [Garden Finance](https://app.garden.finance) 2. Click **Connect Wallet** and connect your **EVM wallet** containing WBTC 3. Click **Connect Bitcoin Wallet** and connect your Bitcoin wallet Connect wallets to Garden 1. In the **Send** section, select **WBTC** and choose the network where your WBTC is located: * **Ethereum**: If your WBTC is on Ethereum mainnet * **Arbitrum**: If your WBTC is on Arbitrum 2. In the **Receive** section, select **Bitcoin (BTC)** 3. Enter the amount of WBTC you want to convert to BTC Configure WBTC to BTC swap 1. Review the conversion details: * **Exchange rate**: Current WBTC to BTC rate (should be close to 1:1) * **Network fees**: Gas fees for the EVM transaction * **Garden fees**: Protocol fees for the swap * **Receiving address**: Your connected Bitcoin wallet address 2. Click **Swap** when ready to proceed 1. Your EVM wallet will prompt you to approve the WBTC transfer 2. Confirm the transaction and pay the gas fees 3. Wait for the transaction to be confirmed on the blockchain Approve WBTC transaction Monitor the swap progress. Once complete, you'll receive native Bitcoin in your connected Bitcoin wallet. Successful WBTC to BTC swap ## Method 2: Manual Bitcoin Address If you prefer not to connect your Bitcoin wallet, you can provide a Bitcoin address manually. 1. Visit [Garden Finance](https://app.garden.finance) 2. Connect your **EVM wallet** containing WBTC 3. Skip connecting a Bitcoin wallet 1. In the **Receive** section, select **Bitcoin (BTC)** 2. Manually enter your **Bitcoin receiving address** 3. **Double-check this address** - incorrect addresses cannot be recovered 4. Ensure the address is from a wallet you control 1. Configure the swap amount and review details 2. Click **Swap** and approve the WBTC transaction in your EVM wallet 3. Wait for the transaction confirmation and swap completion ## Security & Safety **Atomic Swaps**: Garden uses atomic swap technology, meaning the transaction either completes entirely or fails entirely. Your WBTC is never at risk of being lost without receiving Bitcoin. **Address Verification**: Always double-check your Bitcoin receiving address. Garden cannot recover funds sent to incorrect addresses. **Network Confirmation**: Bitcoin transactions require blockchain confirmations. While Garden's process is fast, final Bitcoin confirmation may take 10-60 minutes depending on network conditions. ## Fees and Costs | Fee Type | Description | Typical Amount | | ----------------------- | ------------------------------- | ---------------------------- | | **Gas Fees** | EVM network transaction costs | Varies by network congestion | | **Garden Protocol Fee** | Service fee for atomic swap | Small percentage | | **Network Spread** | Market-based pricing difference | Usually minimal | ## Troubleshooting ### Transaction Pending * **Check gas fees**: Ensure you've set adequate gas prices * **Network congestion**: Wait for network conditions to improve * **Wallet issues**: Try disconnecting and reconnecting wallets ### Incorrect Amount Received * **Slippage**: Market prices may change during transaction * **Fees**: Final amount reflects deducted fees * **Contact support**: Join Discord for assistance if amounts seem incorrect ## After Your Swap Once you receive your Bitcoin: * **Verify Receipt**: Check that Bitcoin arrived in your specified wallet * **Security**: Consider moving Bitcoin to cold storage if not actively trading ## Related Guides Want to wrap Bitcoin again? Learn how to convert BTC back to WBTC Move WBTC between different blockchain networks ## Need Help? * **Questions**: Join our [Discord community](https://discord.gg/B7RczEFuJ5) * **Technical Support**: Reach out for [support on Discord](https://discord.gg/B7RczEFuJ5) # How to swap EVM assets across chains Source: https://docs.garden.finance/home/resources/guides/swap/wbtc-wbtc Step-by-step guide to moving Wrapped Bitcoin (WBTC) between Ethereum, Arbitrum, and other supported networks using Garden Finance # How to Swap WBTC Cross-Chain Move your Wrapped Bitcoin (WBTC) between different blockchain networks. Garden Finance enables seamless cross-chain transfers without requiring you to unwrap to Bitcoin first. This guide covers transferring **WBTC between different networks** such as Ethereum to Arbitrum, Arbitrum to Ethereum, or to other supported chains. The process typically completes in under 30 seconds. ## Supported Networks Garden Finance supports WBTC on multiple networks: **Mainnet WBTC**\ Most established with highest liquidity\ Higher gas fees but maximum DeFi compatibility **Layer 2 WBTC**\ Lower fees and faster transactions\ Growing DeFi ecosystem ## What You'll Need * **EVM Wallet**: MetaMask, Coinbase Wallet, or other EVM-compatible wallet * **Source Network**: WBTC on the blockchain you're moving from * **Destination Network**: Gas tokens for the destination network (if needed) * WBTC tokens on the source network * Gas fees for transaction approval (ETH on Ethereum, ARB on Arbitrum) ## Common Cross-Chain Routes ### Ethereum to Arbitrum Moving WBTC from Ethereum to Arbitrum for lower transaction costs. 1. Visit [Garden Finance](https://app.garden.finance) 2. Connect your EVM wallet containing Ethereum WBTC 3. Ensure your wallet is connected to Ethereum network 1. **Send Section**: Select **WBTC on Ethereum** 2. **Receive Section**: Select **WBTC on Arbitrum** 3. Enter the amount you want to transfer 4. The interface will show you the receiving address (same as your sending address on different network) * **Source**: WBTC on Ethereum * **Destination**: WBTC on Arbitrum * **Rate**: Should be approximately 1:1 (minus fees) * **Gas Cost**: Ethereum gas fees for approval * **Completion Time**: Usually under 30 seconds 1. Click **Swap** to initiate the transfer 2. Approve the WBTC transaction in your wallet 3. Pay the Ethereum gas fees 4. Monitor progress on the Garden interface 1. Switch your wallet to Arbitrum network 2. Check that WBTC appears in your wallet on Arbitrum 3. You can now use WBTC in Arbitrum DeFi protocols ### Arbitrum to Ethereum Moving WBTC from Arbitrum back to Ethereum mainnet. 1. Connect your wallet to Garden Finance 2. Switch your wallet to Arbitrum network 3. Ensure you have WBTC on Arbitrum and enough ARB for gas 1. **Send**: WBTC on Arbitrum 2. **Receive**: WBTC on Ethereum 3. Enter transfer amount 1. Approve the transaction (low Arbitrum gas fees) 2. Wait for completion 3. Switch wallet to Ethereum to see received WBTC ## Why Move WBTC Cross-Chain? **Lower Fees**: Move to Arbitrum for cheaper DeFi transactions\ **Batch Operations**: Perform multiple operations on L2 then return to mainnet **Different DeFi Protocols**: Access unique opportunities on each network\ **Liquidity Arbitrage**: Take advantage of price differences between chains ## Network Comparison | Aspect | Ethereum | Arbitrum | | --------------------- | ---------------------- | -------------------------- | | **Gas Fees** | High during congestion | Very low | | **Transaction Speed** | 12-15 seconds | 1-2 seconds | | **DeFi Ecosystem** | Most mature | Growing rapidly | | **Security** | Highest (Layer 1) | Inherits Ethereum security | | **WBTC Liquidity** | Highest | Good and growing | ## Advanced Strategies ### DeFi Arbitrage 1. **Monitor Rates**: Watch for rate differences between networks 2. **Move Capital**: Transfer WBTC to the network with better opportunities 3. **Execute Strategy**: Participate in farming, lending, or trading 4. **Return Profits**: Move gains back to preferred network ### Cost Management 1. **Batch Transactions**: Accumulate transactions on Arbitrum 2. **Strategic Timing**: Move during low Ethereum gas periods 3. **Fee Planning**: Consider gas costs in your profit calculations ## Security Considerations **Same Address**: Your WBTC will appear at the same wallet address on the destination network. Garden's atomic swaps ensure safe cross-chain transfers. **Network Verification**: Always verify you're on the correct network before initiating transfers. Sending to wrong networks may result in permanent loss. **Gas Requirements**: Ensure you have gas tokens on both source and destination networks for optimal experience. ## Fees and Timing ### Fee Structure * **Source Network Gas**: Pay gas on the network you're sending from * **Garden Protocol Fee**: Small percentage for the cross-chain service * **No Double Bridge Fees**: Direct cross-chain transfer without multiple bridge hops ### Timing Expectations * **Approval**: Immediate wallet confirmation * **Processing**: 30 seconds typical completion time * **Finality**: Near-instant availability on destination network ## Troubleshooting Common Issues ### Transaction Stuck * **Check Network**: Ensure wallet is on correct source network * **Gas Price**: Increase gas price if transaction is pending * **Cancel & Retry**: Cancel stuck transaction and retry with higher gas ### Wrong Network * **Prevention**: Double-check network selection before confirming * **Recovery**: If sent to wrong network, contact Garden support immediately * **Verification**: Always verify destination network in Garden interface ### Insufficient Balance * **Gas Tokens**: Ensure you have ETH (Ethereum) or ARB (Arbitrum) for gas * **WBTC Balance**: Verify you have enough WBTC plus a small buffer for precision * **Fee Reserve**: Keep some gas tokens for future transactions ## After Your Cross-Chain Transfer Switch your wallet to the destination network and confirm WBTC arrival Your portfolio trackers should reflect the new network location Research DeFi protocols available on your new network ## Popular Use Cases ### Moving to Arbitrum * **DeFi Farming**: Lower fees for yield farming strategies * **Active Trading**: Cheaper transactions for frequent trading * **Experimentation**: Test strategies with lower gas costs ### Moving to Ethereum * **Blue-Chip DeFi**: Access to most established protocols * **Maximum Liquidity**: Largest liquidity pools and trading volume * **New Protocol Access**: Many protocols launch on Ethereum first ## Related Guides Wrap Bitcoin to get WBTC for cross-chain transfers Unwrap WBTC back to native Bitcoin ## Need Help? * **Discord Support**: Join our [community](https://discord.gg/B7RczEFuJ5) for assistance * **Technical Issues**: Reach out for [support on Discord](https://discord.gg/B7RczEFuJ5) * **Documentation**: Review our [developer docs](/developers/overview) for integration details Cross-chain WBTC transfers open up new DeFi opportunities across different networks. Always verify network compatibility before interacting with new protocols. # Source: https://docs.garden.finance/index
Swap Bitcoin cross-chain in minutes.
Ask me anything...
Get started →
Start building
Everything you need to integrate Garden into your application.
Bring Bitcoin to your app in 30 minutes. Dive into our API endpoints. Hands-on examples of Garden integration.
Read the contracts
Dive into the atomic swapping contracts powering Garden.