Skip to content

Commit

Permalink
client: api-types: Add exact output amount specifiers in external ord…
Browse files Browse the repository at this point in the history
…er (#36)
  • Loading branch information
joeykraut authored Feb 15, 2025
1 parent 6e69298 commit b658a53
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 8 deletions.
43 changes: 35 additions & 8 deletions client/api_types/api_external_match.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ type ApiExternalOrder struct { //nolint:revive
BaseAmount Amount `json:"base_amount"`
// The amount of the quote asset to buy/sell
QuoteAmount Amount `json:"quote_amount"`
// The exact output amount of the base token
// If specified, the relayer's matching engine will attempt to fill the order
// to result in exactly this base amount, net of fees
ExactBaseAmountOutput Amount `json:"exact_base_output"`
// The exact output amount of the quote token
// If specified, the relayer's matching engine will attempt to fill the order
// to result in exactly this quote amount, net of fees
ExactQuoteAmountOutput Amount `json:"exact_quote_output"`
// The side of the order
Side string `json:"side"`
// The minimum fill amount to cross the order at
Expand All @@ -35,12 +43,14 @@ type ApiExternalOrderBuilder struct { //nolint:revive
func NewExternalOrderBuilder() *ApiExternalOrderBuilder {
return &ApiExternalOrderBuilder{
order: ApiExternalOrder{
BaseMint: "",
QuoteMint: "",
BaseAmount: Amount(*big.NewInt(0)),
QuoteAmount: Amount(*big.NewInt(0)),
Side: "",
MinFillSize: Amount(*big.NewInt(0)),
BaseMint: "",
QuoteMint: "",
BaseAmount: Amount(*big.NewInt(0)),
QuoteAmount: Amount(*big.NewInt(0)),
ExactBaseAmountOutput: Amount(*big.NewInt(0)),
ExactQuoteAmountOutput: Amount(*big.NewInt(0)),
Side: "",
MinFillSize: Amount(*big.NewInt(0)),
},
}
}
Expand Down Expand Up @@ -69,6 +79,22 @@ func (b *ApiExternalOrderBuilder) WithQuoteAmount(amount Amount) *ApiExternalOrd
return b
}

// WithExactBaseAmountOutput sets the exact base output amount
// If specified, the relayer's matching engine will attempt to fill the order
// to result in exactly this base amount, net of fees
func (b *ApiExternalOrderBuilder) WithExactBaseAmountOutput(amount Amount) *ApiExternalOrderBuilder {
b.order.ExactBaseAmountOutput = amount
return b
}

// WithExactQuoteAmountOutput sets the exact quote output amount
// If specified, the relayer's matching engine will attempt to fill the order
// to result in exactly this quote amount, net of fees
func (b *ApiExternalOrderBuilder) WithExactQuoteAmountOutput(amount Amount) *ApiExternalOrderBuilder {
b.order.ExactQuoteAmountOutput = amount
return b
}

// WithSide sets the side
func (b *ApiExternalOrderBuilder) WithSide(side string) *ApiExternalOrderBuilder {
b.order.Side = side
Expand All @@ -92,8 +118,9 @@ func (b *ApiExternalOrderBuilder) Build() (*ApiExternalOrder, error) {
if b.order.Side == "" {
return nil, errors.New("side is required")
}
if b.order.BaseAmount.IsZero() && b.order.QuoteAmount.IsZero() {
return nil, errors.New("either base amount or quote amount must be set")
amountUnset := b.order.BaseAmount.IsZero() && b.order.QuoteAmount.IsZero() && b.order.ExactBaseAmountOutput.IsZero() && b.order.ExactQuoteAmountOutput.IsZero()
if amountUnset {
return nil, errors.New("one of `BaseAmount`, `QuoteAmount`, `ExactBaseAmountOutput`, or `ExactQuoteAmountOutput` must be set")
}
return &b.order, nil
}
Expand Down
98 changes: 98 additions & 0 deletions examples/06_exact_amount_out/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package main

import (
"fmt"
"math/big"
"os"

"github.com/renegade-fi/golang-sdk/client/api_types"
external_match_client "github.com/renegade-fi/golang-sdk/client/external_match_client"
"github.com/renegade-fi/golang-sdk/wallet"
)

func main() {
// Get API credentials from environment
apiKey := os.Getenv("EXTERNAL_MATCH_KEY")
apiSecret := os.Getenv("EXTERNAL_MATCH_SECRET")
if apiKey == "" || apiSecret == "" {
panic("EXTERNAL_MATCH_KEY and EXTERNAL_MATCH_SECRET must be set")
}

apiSecretKey, err := new(wallet.HmacKey).FromBase64String(apiSecret)
if err != nil {
panic(err)
}

externalMatchClient := external_match_client.NewTestnetExternalMatchClient(apiKey, &apiSecretKey)

// Fetch token mappings from the relayer
quoteMint, err := findTokenAddr("USDC", externalMatchClient)
if err != nil {
panic(err)
}
baseMint, err := findTokenAddr("WETH", externalMatchClient)
if err != nil {
panic(err)
}

// Create an order that specifies we want exactly 0.1 WETH out, net of fees
// We're willing to spend up to 200 USDC for this amount
quoteAmountOut := new(big.Int).SetUint64(20_000_000) // $200 USDC max spend

order, err := api_types.NewExternalOrderBuilder().
WithQuoteMint(quoteMint).
WithBaseMint(baseMint).
WithExactQuoteAmountOutput(api_types.Amount(*quoteAmountOut)).
WithSide("Sell").
Build()
if err != nil {
panic(err)
}

if err := getQuoteWithExactAmount(order, externalMatchClient); err != nil {
panic(err)
}
}

// getQuoteAndSubmit gets a quote, assembles it, then submits the bundle
func getQuoteWithExactAmount(order *api_types.ApiExternalOrder, client *external_match_client.ExternalMatchClient) error {
// 1. Get a quote from the relayer
fmt.Println("Getting quote...")
quote, err := client.GetExternalMatchQuote(order)
if err != nil {
return err
}

if quote == nil {
fmt.Println("No quote found")
return nil
}

// Print the quote details
fmt.Printf("Quote found!\n")
fmt.Printf("You will send: %v %s\n", quote.Quote.Send.Amount, quote.Quote.Send.Mint)
fmt.Printf("You will receive (net of fees): %v %s\n", quote.Quote.Receive.Amount, quote.Quote.Receive.Mint)
fmt.Printf("Total fees: %v\n", quote.Quote.Fees.Total())

// You can now assemble the quote and submit a bundle, see `01_external_match` for an example
return nil
}

// -----------
// | Helpers |
// -----------

func findTokenAddr(symbol string, client *external_match_client.ExternalMatchClient) (string, error) {
tokens, err := client.GetSupportedTokens()
if err != nil {
return "", err
}

for _, token := range tokens {
if token.Symbol == symbol {
return token.Address, nil
}
}

return "", fmt.Errorf("token not found")
}

0 comments on commit b658a53

Please sign in to comment.