@@ -53,6 +53,159 @@ var getPairsCommand = &cli.Command{
5353 },
5454}
5555
56+ var getSwapQuoteCommand = & cli.Command {
57+ Name : "quote" ,
58+ Category : "Infos" ,
59+ Usage : "Get a fee quote for a swap" ,
60+ ArgsUsage : "<type>" ,
61+ Description : `Gets a detailed quote for a swap including fee breakdown.
62+ Type can be: submarine, reverse, or chain
63+
64+ Currency defaults by swap type:
65+ - submarine: --to is always BTC (lightning), --from defaults to BTC
66+ - reverse: --from is always BTC (lightning), --to defaults to LBTC
67+ - chain: both --from and --to must be specified
68+
69+ Examples:
70+ Get quote for a submarine swap receiving 100000 sats on lightning:
71+ > boltzcli quote submarine --receive 100000
72+
73+ Get quote for a reverse swap sending 100000 sats from lightning to L-BTC:
74+ > boltzcli quote reverse --send 100000
75+
76+ Get quote for a chain swap from BTC to L-BTC:
77+ > boltzcli quote chain --send 100000 --from BTC --to LBTC` ,
78+ Action : getSwapQuote ,
79+ Flags : []cli.Flag {
80+ jsonFlag ,
81+ & cli.Uint64Flag {
82+ Name : "send" ,
83+ Usage : "Amount to send (in satoshis)" ,
84+ },
85+ & cli.Uint64Flag {
86+ Name : "receive" ,
87+ Usage : "Amount to receive (in satoshis)" ,
88+ },
89+ & cli.StringFlag {
90+ Name : "from" ,
91+ Usage : "Currency to swap from (BTC or LBTC). For reverse swaps, always BTC." ,
92+ },
93+ & cli.StringFlag {
94+ Name : "to" ,
95+ Usage : "Currency to swap to (BTC or LBTC). For submarine swaps, always BTC." ,
96+ },
97+ },
98+ }
99+
100+ func getSwapQuote (ctx * cli.Context ) error {
101+ if ctx .NArg () < 1 {
102+ return cli .ShowSubcommandHelp (ctx )
103+ }
104+
105+ client := getClient (ctx )
106+
107+ typeStr := strings .ToLower (ctx .Args ().First ())
108+ var swapType boltzrpc.SwapType
109+ switch typeStr {
110+ case "submarine" , "sub" :
111+ swapType = boltzrpc .SwapType_SUBMARINE
112+ case "reverse" , "rev" :
113+ swapType = boltzrpc .SwapType_REVERSE
114+ case "chain" :
115+ swapType = boltzrpc .SwapType_CHAIN
116+ default :
117+ return fmt .Errorf ("invalid swap type: %s (use submarine, reverse, or chain)" , typeStr )
118+ }
119+
120+ var from , to boltzrpc.Currency
121+ var err error
122+
123+ // Apply currency defaults/constraints based on swap type
124+ switch swapType {
125+ case boltzrpc .SwapType_SUBMARINE :
126+ // Submarine: on-chain → Lightning. To is always BTC (Lightning)
127+ to = boltzrpc .Currency_BTC
128+ fromStr := ctx .String ("from" )
129+ if fromStr != "" {
130+ from , err = parseCurrency (fromStr )
131+ if err != nil {
132+ return err
133+ }
134+ }
135+ case boltzrpc .SwapType_REVERSE :
136+ // Reverse: Lightning → on-chain. From is always BTC (Lightning)
137+ from = boltzrpc .Currency_BTC
138+ toStr := ctx .String ("to" )
139+ if toStr != "" {
140+ to , err = parseCurrency (toStr )
141+ if err != nil {
142+ return err
143+ }
144+ }
145+ case boltzrpc .SwapType_CHAIN :
146+ // Chain: both must be specified explicitly
147+ fromStr := ctx .String ("from" )
148+ toStr := ctx .String ("to" )
149+ if fromStr == "" || toStr == "" {
150+ return fmt .Errorf ("chain swaps require both --from and --to to be specified" )
151+ }
152+ from , err = parseCurrency (fromStr )
153+ if err != nil {
154+ return err
155+ }
156+ to , err = parseCurrency (toStr )
157+ if err != nil {
158+ return err
159+ }
160+ }
161+
162+ request := & boltzrpc.GetSwapQuoteRequest {
163+ Type : swapType ,
164+ Pair : & boltzrpc.Pair {From : from , To : to },
165+ }
166+
167+ sendAmount := ctx .Uint64 ("send" )
168+ receiveAmount := ctx .Uint64 ("receive" )
169+
170+ if sendAmount > 0 && receiveAmount > 0 {
171+ return fmt .Errorf ("specify either --send or --receive, not both" )
172+ }
173+ if sendAmount == 0 && receiveAmount == 0 {
174+ return fmt .Errorf ("specify either --send or --receive" )
175+ }
176+
177+ if sendAmount > 0 {
178+ request .Amount = & boltzrpc.GetSwapQuoteRequest_SendAmount {SendAmount : sendAmount }
179+ } else {
180+ request .Amount = & boltzrpc.GetSwapQuoteRequest_ReceiveAmount {ReceiveAmount : receiveAmount }
181+ }
182+
183+ quote , err := client .GetSwapQuote (request )
184+ if err != nil {
185+ return err
186+ }
187+
188+ if ctx .Bool ("json" ) {
189+ printJson (quote )
190+ return nil
191+ }
192+
193+ fmt .Printf ("Swap Quote (%s)\n " , swapType )
194+ fmt .Printf (" %s -> %s\n " , from , to )
195+ fmt .Println ()
196+ fmt .Printf (" Send Amount: %s\n " , utils .Satoshis (quote .SendAmount ))
197+ fmt .Printf (" Receive Amount: %s\n " , utils .Satoshis (quote .ReceiveAmount ))
198+ fmt .Println ()
199+ fmt .Println ("Fee Breakdown:" )
200+ fmt .Printf (" Boltz Fee: %s (%.2f%%)\n " , utils .Satoshis (quote .BoltzFee ), quote .PairInfo .Fees .Percentage )
201+ fmt .Printf (" Network Fee: %s\n " , utils .Satoshis (quote .NetworkFee ))
202+ fmt .Printf (" Total Fee: %s\n " , utils .Satoshis (quote .BoltzFee + quote .NetworkFee ))
203+ fmt .Println ()
204+ fmt .Printf ("Limits: %s - %s\n " , utils .Satoshis (quote .PairInfo .Limits .Minimal ), utils .Satoshis (quote .PairInfo .Limits .Maximal ))
205+
206+ return nil
207+ }
208+
56209func getPairs (ctx * cli.Context ) error {
57210 client := getClient (ctx )
58211 pairs , err := client .GetPairs ()
0 commit comments