Skip to content

Commit 9c460c3

Browse files
author
zlace0x
committed
feat: added compute swap logic
1 parent 5df0686 commit 9c460c3

File tree

4 files changed

+12197
-0
lines changed

4 files changed

+12197
-0
lines changed

src/lib/market.ts

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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+
}

src/react-app-env.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="react-scripts" />

tsconfig.json

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es5",
4+
"lib": [
5+
"dom",
6+
"dom.iterable",
7+
"esnext"
8+
],
9+
"allowJs": true,
10+
"skipLibCheck": true,
11+
"esModuleInterop": true,
12+
"allowSyntheticDefaultImports": true,
13+
"strict": true,
14+
"forceConsistentCasingInFileNames": true,
15+
"noFallthroughCasesInSwitch": true,
16+
"module": "esnext",
17+
"moduleResolution": "node",
18+
"resolveJsonModule": true,
19+
"isolatedModules": true,
20+
"noEmit": true,
21+
"jsx": "react-jsx"
22+
},
23+
"include": [
24+
"src"
25+
]
26+
}

0 commit comments

Comments
 (0)