Skip to content

Commit

Permalink
feat: Router의 PoC 추가 (#9)
Browse files Browse the repository at this point in the history
* feat: Router의 PoC 추가

* Apply suggestions from code review

Co-authored-by: Lee ByeongJun <[email protected]>

* review 지적사항 수정

* MyRouter.Swap() error message 수정

---------

Co-authored-by: tolelom <[email protected]>
Co-authored-by: Lee ByeongJun <[email protected]>
  • Loading branch information
3 people authored Aug 30, 2024
1 parent 7ba8127 commit dceaa95
Show file tree
Hide file tree
Showing 12 changed files with 251 additions and 81 deletions.
95 changes: 45 additions & 50 deletions core/alpha_router.go
Original file line number Diff line number Diff line change
@@ -1,55 +1,50 @@
package core

//import (
// "router/core/coins/fractions"
// "router/core/currency"
//)
//
//type AlphaRouter struct {
// portionProvider IPortionProvider
//}
//
//func NewAlphaRouter(params AlphaRouterParams) *AlphaRouter {
// return &AlphaRouter{}
//}
//
//// TODO: 원본 코드에서는 async 함수
//// 라우트 한 결과는 SwapRoute
//func (a AlphaRouter) route(
// amount fractions.CurrencyAmount,
// quoteCurrency currency.Currency,
// tradeType TradeType,
// swapConfig SwapOptions,
//) SwapRoute {
// //originalAmount := amount
// //
// //currencyIn, currencyOut := a.determineCurrencyInOutFromTradeType(tradeType, amount, quoteCurrency)
// //
// //// currencyIn, currencyOut은 Currency 타입이고
// //// Currency 타입은 NativeCurrency(GNOT)이거나 Token 타입이다.
// //// 아래에서 Token 타입이길 원하는 듯하다.
// //tokenIn := currencyIn.Wrapped()
// //tokenOut := currencyOut.Wrapped()
// //
// //// core 패키지를 TradeType 패키지로 변경하면 가독성이 더 좋아질 듯 하다.
// //if tradeType == EXACT_OUTPUT {
// // // TODO: GetPortionAmount에서 반환 값인 CurrencyAmount을 반환하지 못할 경우가 있을 수도 있다.(높은 확률로)
// // portionAmount := a.portionProvider.GetPortionAmount(
// // amount,
// // tradeType,
// // swapConfig,
// // )
// //
// // //result := portionAmount.GreaterThan(0)
// // //if result {
// // // amount = amount.add(portionAmount)
// // //}
// //}
// //
// //swapRoute := SwapRoute{}
// //return swapRoute
// return SwapRoute{}
//}
import "router/core/currency"

type AlphaRouter struct {
//chainId ChainId
//portionProvider IPortionProvider
}

func NewAlphaRouter(params AlphaRouterParams) *AlphaRouter {
return &AlphaRouter{}
}

func (a AlphaRouter) route(
baseCurrency currency.Currency, // currencyIn
quoteCurrency currency.Currency, // currencyOut으로 바꿔도 될 것 같다.
amount float64,
// amount fractions.CurrencyAmount,
// tradeType TradeType,
// swapConfig SwapOptions,
) SwapRoute {
//originalAmount := amount

// currencyIn, currencyOut은 Currency 타입이고
// Currency 타입은 NativeCurrency(GNOT)이거나 Token 타입이다.
// 아래에서 Token 타입이길 원하는 듯하다.
//tokenIn := currencyIn.Wrapped()
//tokenOut := currencyOut.Wrapped()

//core 패키지를 TradeType 패키지로 변경하면 가독성이 더 좋아질 듯 하다.
//if tradeType == EXACT_OUTPUT {
// // TODO: GetPortionAmount에서 반환 값인 CurrencyAmount을 반환하지 못할 경우가 있을 수도 있다.(높은 확률로)
// portionAmount := a.portionProvider.GetPortionAmount(
// amount,
// tradeType,
// swapConfig,
// )
//
//result := portionAmount.GreaterThan(0)
//if result {
// amount = amount.add(portionAmount)
//}
//}

return SwapRoute{}
}

//
//func (a AlphaRouter) determineCurrencyInOutFromTradeType(
// tradeType TradeType,
Expand Down
10 changes: 6 additions & 4 deletions core/currency/base_currency.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ package currency

type BaseCurrency struct {
// The chain ID on which this currency resides
chainId int
ChainId int64
// The decimals used in representing currency amounts
decimals int
decimals int64

// 이 아래 필드는 옵션

// The symbol of the currency, i.e. a short textual non-unique identifier
symbol string
// The name of the currency, i.e. a descriptive textual non-unique identifier
name string
address string
}

func NewBaseCurrency(chainId int, decimals int, symbol string, name string) *BaseCurrency {
// 이게 필요할까??
func NewBaseCurrency(chainId int64, decimals int64, symbol string, name string) *BaseCurrency {
// 아래 코드는 원문
//invariant(Number.isSafeInteger(chainId), 'CHAIN_ID');
//invariant(
Expand All @@ -28,7 +30,7 @@ func NewBaseCurrency(chainId int, decimals int, symbol string, name string) *Bas
}

return &BaseCurrency{
chainId: chainId,
ChainId: chainId,
decimals: decimals,
symbol: symbol,
name: name,
Expand Down
1 change: 0 additions & 1 deletion core/currency/currency.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@ package currency

// Currency는 Token | NativeCurrency
type Currency interface {
Wrapped() Token
}
1 change: 1 addition & 0 deletions core/currency/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ package currency

type Token struct {
BaseCurrency
address string
}
33 changes: 7 additions & 26 deletions core/math/fraction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ func TestNewFraction(t *testing.T) {
tests := []struct {
numerator, denominator int64
expectedNumerator, expectedDenominator int64
expectedError bool
expectedPanic bool
}{
{1, 1, 1, 1, false},
{2, 2, 1, 1, false},
Expand All @@ -25,7 +25,7 @@ func TestNewFraction(t *testing.T) {
t.Run("", func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
if !test.expectedError {
if !test.expectedPanic {
t.Fatalf("NewFraction: unexpected panic: %v", r)
}
}
Expand Down Expand Up @@ -109,7 +109,7 @@ func TestFraction_Mul(t *testing.T) {
numerator1, denominator1 int64
numerator2, denominator2 int64
expectedNumerator, expectedDenominator int64
shouldPanic bool
expectedPanic bool
}{
{1, 2, 1, 3, 1, 6, false},
{-100, 10, 256, -10, 256, 1, false},
Expand All @@ -128,32 +128,15 @@ func TestFraction_Mul(t *testing.T) {
t.Run("", func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
if tt.shouldPanic {
if tt.expectedPanic {
return
}
t.Fatalf("Mul: unexpected panic: %v", r)
}
}()
fraction1 := NewFraction(tt.numerator1, tt.denominator1)

defer func() {
if r := recover(); r != nil {
if tt.shouldPanic {
return
}
t.Fatalf("Mul: unexpected panic: %v", r)
}
}()
fraction2 := NewFraction(tt.numerator2, tt.denominator2)

defer func() {
if r := recover(); r != nil {
if tt.shouldPanic {
return
}
t.Fatalf("Mul: unexpected panic: %v", r)
}
}()
result := fraction1.Mul(fraction2)
expected := NewFraction(tt.expectedNumerator, tt.expectedDenominator)

Expand All @@ -170,7 +153,7 @@ func TestFraction_Div(t *testing.T) {
numerator1, denominator1 int64
numerator2, denominator2 int64
expectedNumerator, expectedDenominator int64
expectedError bool
expectedPanic bool
}{
{1, 2, 1, 3, 3, 2, false},
{-100, 10, 256, -10, 100, 256, false},
Expand All @@ -186,10 +169,8 @@ func TestFraction_Div(t *testing.T) {
t.Run("", func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
if tt.denominator2 == 0 {
if !tt.expectedError {
t.Fatalf("Div: unexpected panic: %v", r)
}
if !tt.expectedPanic {
t.Fatalf("Div: unexpected panic: %v", r)
}
}
}()
Expand Down
17 changes: 17 additions & 0 deletions core/tokens/gnot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package tokens

import "router/core/currency"

type Gnot struct {
currency.NativeCurrency
}

func NewGnot(chainId1 int64) *Gnot {
return &Gnot{
NativeCurrency: currency.NativeCurrency{
BaseCurrency: currency.BaseCurrency{
ChainId: chainId1,
},
},
}
}
83 changes: 83 additions & 0 deletions poc/my_router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package poc

import (
"fmt"
"math"
)

type MyRouter struct {
network map[string]*Pool
}

func NewMyRouter(edges []*Pool) *MyRouter {
router := &MyRouter{
network: make(map[string]*Pool),
}

for _, edge := range edges {
router.network[edge.Address] = edge
}

return router
}

func (m *MyRouter) Swap(request SwapRequest) (SwapResult, error) {
// poolName은 from:to가 아니라 to:from일 수 있다.
poolName := request.FromToken + ":" + request.ToToken

if pool, ok := m.network[poolName]; ok {
fmt.Printf("pool found: %v\n", pool)

reserveFromToken, reserveToToken := m.getReserveOfTokenFromPool(request.FromToken, request.ToToken, *pool)
exchangedAmount := m.calculateAmountOfToToken(reserveFromToken, reserveToToken, request.AmountIn, *pool)

//saveSwap()
// TODO: 지금은 간이로 코드 작성하고 나중에 함수로 빼든 리팩토링 할 것
if pool.TokenA.Symbol == request.FromToken {
pool.ReserveA += request.AmountIn
pool.ReserveB += exchangedAmount
} else {
pool.ReserveA += exchangedAmount
pool.ReserveB += request.AmountIn
}

return SwapResult{
AmountIn: request.AmountIn,
AmountOut: math.Abs(exchangedAmount),
}, nil
}

return SwapResult{}, fmt.Errorf("pool %s not found", poolName)
}

func (m *MyRouter) getReserveOfTokenFromPool(fromTokenName string, toTokenName string, pool Pool) (float64, float64) {
if fromTokenName == pool.TokenA.Symbol {
return pool.ReserveA, pool.ReserveB
}
return pool.ReserveB, pool.ReserveA
}

func (m *MyRouter) calculateAmountOfToToken(reserveFromToken, reserveToToken, amountIn float64, pool Pool) float64 {
X := reserveFromToken
Y := reserveToToken
dX := amountIn

K := X * Y
L := math.Sqrt(K)
P := X / Y

X_ := X + dX
P_ := (X_ / L) * (X_ / L)

dY := L * (1/math.Sqrt(P_) - 1/math.Sqrt(P))

// X 코인이 dX개 만큼 증가했을 때
// Y 코인은 dY개 만큼 감소해야 한다.
// X -> X + dX, Y -> Y + dY

return dY
}

func (m *MyRouter) dijskrtra() {

}
54 changes: 54 additions & 0 deletions poc/my_router_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package poc

import (
"fmt"
"math"
"testing"
)

func TestMyRouter(t *testing.T) {
tokens := map[string]Token{
"a": Token{Symbol: "a"},
"b": Token{Symbol: "b"},
}

tests := []struct {
edges []*Pool
requests []SwapRequest
results []SwapResult
}{
{
[]*Pool{
{"a:b", tokens["a"], tokens["b"], 4000, 1000}},
[]SwapRequest{
{"a", "b", 2000}},
[]SwapResult{
{2000.0, 2000.0 / 6.0},
},
},
}

for _, test := range tests {
t.Run("", func(t *testing.T) {
router := NewMyRouter(test.edges)

for i, request := range test.requests {
result, err := router.Swap(request)
if err != nil {
t.Fatalf("Swap Error: can't find pool: %v:%v", request.FromToken, request.ToToken)
}

diff := math.Abs(result.AmountOut - test.results[i].AmountOut)
tolerance := 0.00000001
if diff > tolerance {
t.Fatalf("Swap: Unexpected Token output number, expected: %v, got %v", test.results[i].AmountOut, result.AmountOut)
}
fmt.Println(result)

for _, pool := range router.network {
fmt.Println(pool)
}
}
})
}
}
Loading

0 comments on commit dceaa95

Please sign in to comment.