Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new QuoteOrder RPC #252

Merged
merged 4 commits into from
May 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion auctioneerrpc/auctioneer.proto
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,7 @@ message ServerBid {
a node this tier or higher will be eligible for matching with this bid.
*/
NodeTier min_node_tier = 5;

/*
Give the incoming channel that results from this bid being matched an
initial outbound balance by adding additional funds from the taker's account
Expand Down
50 changes: 24 additions & 26 deletions cmd/pool/order.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/lightninglabs/pool/order"
"github.com/lightninglabs/pool/poolrpc"
"github.com/lightninglabs/pool/sidecar"
"github.com/lightninglabs/pool/terms"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/urfave/cli"
)
Expand Down Expand Up @@ -279,7 +278,7 @@ func ordersSubmitAsk(ctx *cli.Context) error { // nolint: dupl
if !ctx.Bool("force") {
if err := printOrderDetails(
client, btcutil.Amount(ask.Details.Amt),
order.SupplyUnit(ask.Details.MinUnitsMatch).ToSatoshis(),
order.SupplyUnit(ask.Details.MinUnitsMatch),
0, order.FixedRatePremium(ask.Details.RateFixed),
ask.LeaseDurationBlocks,
chainfee.SatPerKWeight(
Expand Down Expand Up @@ -311,47 +310,46 @@ func ordersSubmitAsk(ctx *cli.Context) error { // nolint: dupl
return nil
}

func printOrderDetails(client poolrpc.TraderClient, amt,
minChanAmt, selfChanBalance btcutil.Amount, rate order.FixedRatePremium,
leaseDuration uint32, maxBatchFeeRate chainfee.SatPerKWeight,
isAsk bool, sidecarTicket *sidecar.Ticket) error {

auctionFee, err := client.AuctionFee(
context.Background(), &poolrpc.AuctionFeeRequest{},
func printOrderDetails(client poolrpc.TraderClient, amt btcutil.Amount,
minUnitsMatch order.SupplyUnit, selfChanBalance btcutil.Amount,
rate order.FixedRatePremium, leaseDuration uint32,
maxBatchFeeRate chainfee.SatPerKWeight, isAsk bool,
sidecarTicket *sidecar.Ticket) error {

quote, err := client.QuoteOrder(
context.Background(), &poolrpc.QuoteOrderRequest{
Amt: uint64(amt),
RateFixed: uint32(rate),
LeaseDurationBlocks: leaseDuration,
MaxBatchFeeRateSatPerKw: uint64(maxBatchFeeRate),
MinUnitsMatch: uint32(minUnitsMatch),
},
)
if err != nil {
return err
}

feeSchedule := terms.NewLinearFeeSchedule(
btcutil.Amount(auctionFee.ExecutionFee.BaseFee),
btcutil.Amount(auctionFee.ExecutionFee.FeeRate),
)
exeFee := feeSchedule.BaseFee() + feeSchedule.ExecutionFee(amt)

orderType := "Bid"
premiumDescription := "paid to maker"
if isAsk {
orderType = "Ask"
premiumDescription = "yield from taker"
}
ratePerMil := float64(rate) / order.FeeRateTotalParts

premium := rate.LumpSumPremium(amt, leaseDuration)

maxNumMatches := amt / minChanAmt
chainFee := maxNumMatches * order.EstimateTraderFee(1, maxBatchFeeRate)

fmt.Println("-- Order Details --")
fmt.Printf("%v Amount: %v\n", orderType, amt)
fmt.Printf("%v Duration: %v\n", orderType, leaseDuration)
fmt.Printf("Total Premium (%v): %v \n", premiumDescription, premium)
fmt.Printf("Total Premium (%v): %v \n", premiumDescription,
btcutil.Amount(quote.TotalPremiumSat))
fmt.Printf("Rate Fixed: %v\n", rate)
fmt.Printf("Rate Per Block: %.9f (%.7f%%)\n", ratePerMil, ratePerMil*100)
fmt.Println("Execution Fee: ", exeFee)
fmt.Printf("Rate Per Block: %.9f (%.7f%%)\n", quote.RatePerBlock,
quote.RatePercent)
fmt.Println("Execution Fee: ",
btcutil.Amount(quote.TotalExecutionFeeSat))
fmt.Printf("Max batch fee rate: %d sat/vByte\n",
maxBatchFeeRate.FeePerKVByte()/1000)
fmt.Println("Max chain fee:", chainFee)
fmt.Println("Max chain fee:",
btcutil.Amount(quote.WorstCaseChainFeeSat))

if selfChanBalance > 0 {
fmt.Printf("Self channel balance: %v\n", selfChanBalance)
Expand Down Expand Up @@ -551,7 +549,7 @@ func ordersSubmitBid(ctx *cli.Context) error { // nolint: dupl
if !ctx.Bool("force") {
if err := printOrderDetails(
client, btcutil.Amount(bid.Details.Amt),
order.SupplyUnit(bid.Details.MinUnitsMatch).ToSatoshis(),
order.SupplyUnit(bid.Details.MinUnitsMatch),
btcutil.Amount(bid.SelfChanBalance),
order.FixedRatePremium(bid.Details.RateFixed),
bid.LeaseDurationBlocks,
Expand Down
4 changes: 4 additions & 0 deletions macaroons.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ var (
Entity: "order",
Action: "write",
}},
"/poolrpc.Trader/QuoteOrder": {{
Entity: "order",
Action: "read",
}},
"/poolrpc.Trader/AuctionFee": {{
Entity: "auction",
Action: "read",
Expand Down
39 changes: 39 additions & 0 deletions order/tradingfees.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,45 @@ func EstimateTraderFee(numTraderChans uint32,
return feeRate.FeeForWeight(weightEstimate)
}

// Quote is a struct holding the result of an order quote calculation.
type Quote struct {
// TotalPremium is the total order premium in satoshis for filling the
// entire order.
TotalPremium btcutil.Amount

// RatePerBlock is the fixed order rate expressed as a fraction instead
// of parts per billion.
RatePerBlock float64

// TotalExecutionFee is the total execution fee in satoshis that needs
// to be paid to the auctioneer for executing the entire order.
TotalExecutionFee btcutil.Amount

// WorstCaseChainFee is the chain fees that have to be paid in the worst
// case scenario where fees spike up to the given max batch fee rate and
// the order is executed in the maximum parts possible (amount divided
// by minimum channel amount).
WorstCaseChainFee btcutil.Amount
}

// NewQuote returns a new quote for an order with the given parameters.
func NewQuote(amt, minChanAmt btcutil.Amount, rate FixedRatePremium,
leaseDuration uint32, maxBatchFeeRate chainfee.SatPerKWeight,
schedule terms.FeeSchedule) *Quote {

exeFee := schedule.BaseFee() + schedule.ExecutionFee(amt)

maxNumMatches := amt / minChanAmt
chainFee := maxNumMatches * EstimateTraderFee(1, maxBatchFeeRate)

return &Quote{
TotalPremium: rate.LumpSumPremium(amt, leaseDuration),
RatePerBlock: float64(rate) / FeeRateTotalParts,
TotalExecutionFee: exeFee,
WorstCaseChainFee: chainFee,
}
}

// AccountTally keeps track of an account's balance and fees for all orders in
// a batch that spend from/use that account.
type AccountTally struct {
Expand Down
85 changes: 85 additions & 0 deletions order/tradingfees_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package order

import (
"testing"

"github.com/btcsuite/btcutil"
"github.com/lightninglabs/pool/terms"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/stretchr/testify/require"
)

// TestNewQuote makes sure the order quote calculations are correct.
func TestNewQuote(t *testing.T) {
testCases := []struct {
amt btcutil.Amount
minChanAmt btcutil.Amount
rate FixedRatePremium
leaseDuration uint32
maxBatchFeeRate chainfee.SatPerKWeight
schedule terms.FeeSchedule
result *Quote
}{{
amt: 1_000_000,
minChanAmt: 100_000,
rate: 1,
leaseDuration: 1,
maxBatchFeeRate: chainfee.FeePerKwFloor,
schedule: terms.NewLinearFeeSchedule(1, 1),
result: &Quote{
TotalPremium: 0,
RatePerBlock: 0.000000001,
TotalExecutionFee: 2,
WorstCaseChainFee: 1650,
},
}, {
amt: 5_000_000,
minChanAmt: 200_000,
rate: 14880,
leaseDuration: 2016,
maxBatchFeeRate: chainfee.SatPerKVByte(100_000).FeePerKWeight(),
schedule: terms.NewLinearFeeSchedule(1, 1000),
result: &Quote{
TotalPremium: 149990,
RatePerBlock: 0.00001488,
TotalExecutionFee: 5001,
WorstCaseChainFee: 408125,
},
}, {
amt: 10_000_000,
minChanAmt: 10_000_000,
rate: 12444,
leaseDuration: 8036,
maxBatchFeeRate: chainfee.FeePerKwFloor,
schedule: terms.NewLinearFeeSchedule(1, 1000),
result: &Quote{
TotalPremium: 999999,
RatePerBlock: 0.000012444,
TotalExecutionFee: 10001,
WorstCaseChainFee: 165,
},
}, {
amt: 10_000_000,
minChanAmt: 1_000_000,
rate: 49603,
leaseDuration: 2016,
maxBatchFeeRate: chainfee.SatPerKVByte(200_000).FeePerKWeight(),
schedule: terms.NewLinearFeeSchedule(1, 1000),
result: &Quote{
TotalPremium: 999996,
RatePerBlock: 0.000049603,
TotalExecutionFee: 10001,
WorstCaseChainFee: 326500,
},
}}

for _, tc := range testCases {
tc := tc

q := NewQuote(
tc.amt, tc.minChanAmt, tc.rate, tc.leaseDuration,
tc.maxBatchFeeRate, tc.schedule,
)
require.Equal(t, tc.result, q)
}
}
3 changes: 3 additions & 0 deletions poolrpc/rest-annotations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ http:
get: "/v1/pool/orders"
- selector: poolrpc.Trader.CancelOrder
delete: "/v1/pool/orders/{order_nonce}"
- selector: poolrpc.Trader.QuoteOrder
post: "/v1/pool/orders/quote"
body: "*"
- selector: poolrpc.Trader.AuctionFee
get: "/v1/pool/fee"
- selector: poolrpc.Trader.LeaseDurations
Expand Down
Loading