forked from miraclesu/uniswap-sdk-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
trade.go
193 lines (173 loc) · 5.54 KB
/
trade.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package entities
import (
"fmt"
"github.com/ZoneCapital/uniswap-sdk-go/constants"
)
var (
ErrInvalidSlippageTolerance = fmt.Errorf("invalid slippage tolerance")
)
// Trade Represents a trade executed against a list of pairs.
// Does not account for slippage, i.e. trades that front run this trade and move the price.
type Trade struct {
/**
* The route of the trade, i.e. which pairs the trade goes through.
*/
Route *Route
/**
* The type of the trade, either exact in or exact out.
*/
TradeType constants.TradeType
/**
* The input amount for the trade assuming no slippage.
*/
inputAmount *TokenAmount
/**
* The output amount for the trade assuming no slippage.
*/
outputAmount *TokenAmount
/**
* The price expressed in terms of output amount/input amount.
*/
ExecutionPrice *Price
/**
* The mid price after the trade executes assuming no slippage.
*/
NextMidPrice *Price
/**
* The percent difference between the mid price before the trade and the trade execution price.
*/
PriceImpact *Percent
}
func (t *Trade) InputAmount() *TokenAmount {
return t.inputAmount
}
func (t *Trade) OutputAmount() *TokenAmount {
return t.outputAmount
}
/**
* Constructs an exact in trade with the given amount in and route
* @param route route of the exact in trade
* @param amountIn the amount being passed in
*/
func ExactIn(route *Route, amountIn *TokenAmount) (*Trade, error) {
return NewTrade(route, amountIn, constants.ExactInput)
}
/**
* Constructs an exact out trade with the given amount out and route
* @param route route of the exact out trade
* @param amountOut the amount returned by the trade
*/
func ExactOut(route *Route, amountOut *TokenAmount) (*Trade, error) {
return NewTrade(route, amountOut, constants.ExactOutput)
}
// NewTrade creates a new trade
// nolint gocyclo
func NewTrade(route *Route, amount *TokenAmount, tradeType constants.TradeType) (*Trade, error) {
amounts := make([]*TokenAmount, len(route.Path))
nextPairs := make([]*Pair, len(route.Pairs))
if tradeType == constants.ExactInput {
if !route.Input.Currency.Equals(amount.Token.Currency) {
return nil, ErrInvalidCurrency
}
if !route.Input.Equals(amount.Token) {
return nil, ErrDiffToken
}
amounts[0] = amount
for i := 0; i < len(route.Path)-1; i++ {
outputAmount, nextPair, err := route.Pairs[i].GetOutputAmount(amounts[i])
if err != nil {
return nil, err
}
amounts[i+1] = outputAmount
nextPairs[i] = nextPair
}
} else {
if !route.Output.Currency.Equals(amount.Token.Currency) {
return nil, ErrInvalidCurrency
}
if !route.Output.Equals(amount.Token) {
return nil, ErrDiffToken
}
amounts[len(amounts)-1] = amount
for i := len(route.Path) - 1; i > 0; i-- {
inputAmount, nextPair, err := route.Pairs[i-1].GetInputAmount(amounts[i])
if err != nil {
return nil, err
}
amounts[i-1] = inputAmount
nextPairs[i-1] = nextPair
}
}
nextRoute, err := NewRoute(nextPairs, route.Input, nil)
if err != nil {
return nil, err
}
nextMidPrice, err := NewPriceFromRoute(nextRoute)
if err != nil {
return nil, err
}
inputAmount := amount
if tradeType == constants.ExactOutput {
inputAmount = amounts[0]
}
outputAmount := amount
if tradeType == constants.ExactInput {
outputAmount = amounts[len(amounts)-1]
}
price := NewPrice(inputAmount.Currency, outputAmount.Currency, inputAmount.Raw(), outputAmount.Raw())
return &Trade{
Route: route,
TradeType: tradeType,
inputAmount: inputAmount,
outputAmount: outputAmount,
ExecutionPrice: price,
NextMidPrice: nextMidPrice,
PriceImpact: computePriceImpact(route.MidPrice, inputAmount, outputAmount),
}, nil
}
/**
* Returns the percent difference between the mid price and the execution price, i.e. price impact.
* @param midPrice mid price before the trade
* @param inputAmount the input amount of the trade
* @param outputAmount the output amount of the trade
*/
func computePriceImpact(midPrice *Price, inputAmount, outputAmount *TokenAmount) *Percent {
exactQuote := midPrice.Raw().Multiply(NewFraction(inputAmount.Raw(), nil))
slippage := exactQuote.Subtract(NewFraction(outputAmount.Raw(), nil)).Divide(exactQuote)
return &Percent{
Fraction: slippage,
}
}
/**
* Get the minimum amount that must be received from this trade for the given slippage tolerance
* @param slippageTolerance tolerance of unfavorable slippage from the execution price of this trade
*/
func (t *Trade) MinimumAmountOut(slippageTolerance *Percent) (*TokenAmount, error) {
if slippageTolerance.LessThan(ZeroFraction) {
return nil, ErrInvalidSlippageTolerance
}
if t.TradeType == constants.ExactOutput {
return t.outputAmount, nil
}
slippageAdjustedAmountOut := NewFraction(constants.One, nil).
Add(slippageTolerance.Fraction).
Invert().
Multiply(NewFraction(t.outputAmount.Raw(), nil)).Quotient()
return NewTokenAmount(t.outputAmount.Token, slippageAdjustedAmountOut)
}
/**
* Get the maximum amount in that can be spent via this trade for the given slippage tolerance
* @param slippageTolerance tolerance of unfavorable slippage from the execution price of this trade
*/
func (t *Trade) MaximumAmountIn(slippageTolerance *Percent) (*TokenAmount, error) {
if slippageTolerance.LessThan(ZeroFraction) {
return nil, ErrInvalidSlippageTolerance
}
if t.TradeType == constants.ExactInput {
return t.inputAmount, nil
}
slippageAdjustedAmountIn := NewFraction(constants.One, nil).
Add(slippageTolerance.Fraction).
Multiply(NewFraction(t.inputAmount.Raw(), nil)).Quotient()
return NewTokenAmount(t.inputAmount.Token, slippageAdjustedAmountIn)
}