1
+ import { Coin , Coins , Dec } from "@terra-money/terra.js"
2
+
3
+ export function computeSwap ( offerCoin : Coin , askDenom : string , rates : Coins , basePool : Dec , minStabilitySpread : Dec , delta : Dec ) {
4
+
5
+ // get offerCoin in sdr base denom
6
+ const baseOfferCoin = _computeInternalSwap ( offerCoin , 'usdr' , rates ) ;
7
+ if ( ! baseOfferCoin ) {
8
+ throw new Error ( `Invalid base rate (sdr) for ${ offerCoin . denom } ` ) ;
9
+ }
10
+
11
+ // get ask return amount from baseOfferCoin
12
+ const retCoin = _computeInternalSwap ( baseOfferCoin , askDenom , rates ) ;
13
+
14
+ // Skips tobin tax logic for terra <> terra swaps
15
+
16
+ // constant-product aka k in k=xy
17
+ const cp = new Dec ( basePool . pow ( 2 ) ) ;
18
+
19
+ const terraPool = basePool . add ( delta ) ;
20
+ const lunaPool = cp . div ( terraPool ) ;
21
+
22
+ let offerPool : Dec , askPool : Dec ;
23
+
24
+ if ( offerCoin . denom === 'uluna' ) {
25
+ // Luna -> Terra swap
26
+ offerPool = lunaPool
27
+ askPool = terraPool
28
+ } else {
29
+ // Terra -> Luna swap
30
+ offerPool = terraPool
31
+ askPool = lunaPool
32
+ }
33
+
34
+ // Get cp(constant-product) based swap amount
35
+ // askBaseAmount = askPool - cp / (offerPool + offerBaseAmount)
36
+ // askBaseAmount is base denom(usdr) unit
37
+ const askBaseAmount = askPool . sub ( cp . div ( offerPool . plus ( baseOfferCoin . amount ) ) )
38
+
39
+ // Both baseOffer and baseAsk are usdr units, so spread can be calculated by
40
+ // spread = (baseOfferAmt - baseAskAmt) / baseOfferAmt
41
+ const baseOfferAmount = baseOfferCoin . amount ;
42
+ let spread = baseOfferAmount . sub ( askBaseAmount ) . div ( baseOfferAmount )
43
+
44
+ if ( spread . lessThan ( minStabilitySpread ) ) {
45
+ spread = minStabilitySpread ;
46
+ }
47
+
48
+ return [ retCoin , spread ] ;
49
+ }
50
+
51
+ export function coinAfterSpread ( coin : Coin , spread : Dec ) {
52
+ return new Coin ( coin . denom , coin . amount . minus ( coin . amount . times ( spread ) ) ) ;
53
+ }
54
+
55
+ function _computeInternalSwap ( offerCoin : Coin , askDenom : string , rates : Coins ) {
56
+ if ( offerCoin . denom == askDenom ) {
57
+ return offerCoin ;
58
+ }
59
+
60
+ // get rates of offer & ask denoms in uluna.
61
+ const offerRate = rates . get ( offerCoin . denom ) ?. amount ;
62
+ if ( ! offerRate ) {
63
+ throw new Error ( "Invalid offer rate (uluna) for " + offerCoin . denom ) ;
64
+ }
65
+ const askRate = askDenom == "uluna" ? new Dec ( 1 ) : rates . get ( askDenom ) ?. amount ;
66
+ if ( ! askRate ) {
67
+ throw new Error ( "Invalid ask rate (uluna) for " + askDenom ) ;
68
+ }
69
+
70
+ const retAmount = offerCoin . amount . times ( askRate ) . dividedBy ( offerRate ) ?? new Dec ( 0 )
71
+
72
+ if ( retAmount . lessThanOrEqualTo ( 0 ) ) {
73
+ throw new Error ( "Invalid return amount" ) ;
74
+ }
75
+
76
+ return new Coin ( askDenom , retAmount ) ;
77
+ }
0 commit comments