Skip to content
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
33 changes: 32 additions & 1 deletion pkg/defi/asset_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ type jettonAssetDumpItem struct {
Provider string `json:"provider"`
}

const stonfiProviderID = "stonfi"
const (
stonfiProviderID = "stonfi"
dedustProviderID = "dedust"
)

type JettonMasterSource interface {
GetJettonMastersByAddresses(ctx context.Context, addresses []ton.AccountID) ([]core.JettonMaster, error)
Expand Down Expand Up @@ -95,6 +98,15 @@ var stonfiPoolCodeHashes = map[string]bool{
mustBase64Hash("cf5d0b99fa704e7cf2c9d50a8ff8b8bc7ce0b8a74e414b9c279ac544e7aade05"): true,
}

var dedustPoolCodeHashes = map[string]bool{
// DeDust pool, e.g. https://dedust.io/pools/0:00576440c4a6f443af2fcef7b27a2277eb0c552e8b898190cce9c3393d17c6e5
mustBase64Hash("778f0d3fe6482c50888970df5e787f40f3a4ab282170c035a5920877058c99d3"): true,
// DeDust pool, e.g. https://dedust.io/pools/0:011e4c677529eaac180c20347bdcb0741410c33340bf529dfd10bd519f76a8ff
mustBase64Hash("1275095b6da3911292406f4f4386f9e780099b854c6dee9ee2895ddce70927c1"): true,
// DeDust pool, e.g. https://dedust.io/pools/0:0706d6f329feb86a9876c7d1dcbb189fb99482f5794f0c746c75d204b15fccac
mustBase64Hash("c0f9d14fbc8e14f0d72cba2214165eee35836ab174130912baf9dbfa43ead562"): true,
}

func parseJettonAssetDump(data []byte, providers map[string]Provider) map[tongo.AccountID]AssetInfo {
raw := g.MustParseJson[map[tongo.AccountID]jettonAssetDumpItem](data)
return g.MapMapValues(raw, func(item jettonAssetDumpItem) AssetInfo {
Expand Down Expand Up @@ -141,6 +153,20 @@ func stonfiPoolAssetInfo(admin *tongo.AccountID, codeHash string) (AssetInfo, bo
}, true
}

func dedustPoolAssetInfo(codeHash string) (AssetInfo, bool) {
if !dedustPoolCodeHashes[codeHash] {
return AssetInfo{}, false
}
provider, ok := jettonDefiProviders[dedustProviderID]
if !ok {
return AssetInfo{}, false
}
return AssetInfo{
TokenType: TokenTypeLiquidPool,
DefiProvider: provider,
}, true
}

func mustBase64Hash(hexHash string) string {
b, err := hex.DecodeString(hexHash)
if err != nil {
Expand Down Expand Up @@ -178,6 +204,11 @@ func AssetInfos(ctx context.Context, source JettonMasterSource, logger *zap.Logg
stonfiInfo, stonfiOk := stonfiPoolAssetInfo(master.Admin, master.CodeHash)
if stonfiOk {
result[master.Address] = stonfiInfo
continue
}
dedustInfo, dedustOk := dedustPoolAssetInfo(master.CodeHash)
if dedustOk {
result[master.Address] = dedustInfo
}
}
return result
Expand Down
163 changes: 35 additions & 128 deletions pkg/defi/asset_info_test.go
Original file line number Diff line number Diff line change
@@ -1,79 +1,20 @@
package defi

import (
"path"
"strings"
"context"
"testing"

"github.com/tonkeeper/opentonapi/internal/g"
"github.com/tonkeeper/opentonapi/pkg/core"
"github.com/tonkeeper/tongo"
"github.com/tonkeeper/tongo/ton"
"go.uber.org/zap"
)

func TestJettonAssetDumpProvidersExist(t *testing.T) {
raw := g.MustParseJson[map[tongo.AccountID]jettonAssetDumpItem](jettonAssetDump)
for master, item := range raw {
if _, ok := jettonDefiProviders[item.Provider]; !ok {
t.Fatalf("master %v references unknown provider %q", master, item.Provider)
}
}
}

func TestJettonAssetDumpHasNoEmptyProviders(t *testing.T) {
raw := g.MustParseJson[map[tongo.AccountID]jettonAssetDumpItem](jettonAssetDump)
for master, item := range raw {
if item.Provider == "" {
t.Fatalf("master %v has empty provider", master)
}
}
}

func TestJettonAssetDumpHasNoSTONFiLiquidPools(t *testing.T) {
raw := g.MustParseJson[map[tongo.AccountID]jettonAssetDumpItem](jettonAssetDump)
for master, item := range raw {
if item.Provider == stonfiProviderID && item.TokenType == TokenTypeLiquidPool {
t.Fatalf("master %v should be covered by STON.fi pool rules, not dump", master)
}
}
}

func TestJettonDefiProvidersDump(t *testing.T) {
for _, provider := range []string{"affluent", "ethena", "stonfi", "tonstakers"} {
if _, ok := jettonDefiProviders[provider]; !ok {
t.Fatalf("expected provider %q in provider dump", provider)
}
}
}

func TestJettonDefiProviderAssetsAreEmbedded(t *testing.T) {
for providerID, provider := range jettonDefiProviders {
for _, assetURL := range []string{provider.Card, provider.Full, provider.Icon} {
if assetURL == "" {
continue
}
if !strings.HasPrefix(assetURL, "/v2/assets/defi/") {
t.Fatalf("provider %q asset URL is not local: %v", providerID, assetURL)
}
assetName := strings.TrimPrefix(assetURL, "/v2/assets/defi/")
asset, err := defiAssetsFS.Open(path.Join("assets", assetName))
if err != nil {
t.Fatalf("provider %q asset %q is not embedded in package assets: %v", providerID, assetName, err)
}
_ = asset.Close()
}
}
}

func TestJettonAssetInfoSTONFiLiquidPoolMaster(t *testing.T) {
master := core.JettonMaster{
Address: tongo.MustParseAddress("EQCGScrZe1xbyWqWDvdI6mzP-GAcAWFv6ZXuaJOuSqemxku4").ID,
Admin: accountIDPtr(ton.MustParseAccountID("0:92e1411ae546892f33b2c8a89ea90390d8ff4cfbb917a643b91e73f706fdb9d1")),
CodeHash: "7GFOpKrqP3doYG8cFjKzN0094Jah58S6Q8gAnEh/7p0=",
}

if master.CodeHash != mustBase64Hash("ec614ea4aaea3f7768606f1c1632b3374d3de096a1e7c4ba43c8009c487fee9d") {
t.Fatalf("unexpected code hash fixture for master %v", master.Address.ToRaw())
CodeHash: mustBase64Hash("ec614ea4aaea3f7768606f1c1632b3374d3de096a1e7c4ba43c8009c487fee9d"),
}

if _, ok := jettonAssetDumpInfos[master.Address]; ok {
Expand All @@ -87,77 +28,22 @@ func TestJettonAssetInfoSTONFiLiquidPoolMaster(t *testing.T) {
assertSTONFiLiquidPoolAssetInfo(t, stonfiInfo)
}

func TestSTONFiLiquidPoolUsesWhitelistedVaults(t *testing.T) {
whitelistedAdmin := accountIDPtr(ton.MustParseAccountID("0:779dcc815138d9500e449c5291e7f12738c23d575b5310000f6a253bd607384e"))
for _, codeHash := range []string{
"82566ad72b6568fe7276437d3b0c911aab65ed701c13601941b2917305e81c11",
"ec614ea4aaea3f7768606f1c1632b3374d3de096a1e7c4ba43c8009c487fee9d",
"f04a14c3231221056c3499965e4604417e324f8e9121d840120d803288715594",
"fbc7e8fcca72c2b9c078b359ffa936f46384491b895b6577b0a6cb3f569040bc",
"dac47636ae899081ebd4f47dc90ef9de98456b1000591069773f683c6d601fa9",
"cf5d0b99fa704e7cf2c9d50a8ff8b8bc7ce0b8a74e414b9c279ac544e7aade05",
} {
stonfiInfo, ok := stonfiPoolAssetInfo(whitelistedAdmin, mustBase64Hash(codeHash))
if !ok {
t.Fatalf("expected STON.fi logic asset info for whitelisted vault and code hash %v", codeHash)
}
assertSTONFiLiquidPoolAssetInfo(t, stonfiInfo)
}

notWhitelistedAdmin := accountIDPtr(ton.MustParseAccountID("0:9ecf00744c80dffb8fff2e8e0e4930844403962a747d13921403ebe34bb51b56"))
if _, ok := stonfiPoolAssetInfo(notWhitelistedAdmin, mustBase64Hash("82566ad72b6568fe7276437d3b0c911aab65ed701c13601941b2917305e81c11")); ok {
t.Fatalf("did not expect STON.fi logic asset info for non-whitelisted vault")
func TestJettonAssetInfoDeDustLiquidPoolMaster(t *testing.T) {
master := core.JettonMaster{
Address: tongo.MustParseAddress("0:00576440c4a6f443af2fcef7b27a2277eb0c552e8b898190cce9c3393d17c6e5").ID,
CodeHash: mustBase64Hash("778f0d3fe6482c50888970df5e787f40f3a4ab282170c035a5920877058c99d3"),
}
}

func TestJettonAssetInfoAddressMatches(t *testing.T) {
tests := []struct {
name string
master string
tokenType TokenType
tag string
provider string
}{
{
name: "affluent yield token",
master: "0:edc50aa4808450411e615a5ad0c6224cb4bc23477ac460d5016c472f3c3c02b3",
tokenType: TokenTypeYieldToken,
tag: "factorial",
provider: "Affluent",
},
{
name: "ethena liquid staking",
master: "0:d0e545323c7acb7102653c073377f7e3c67f122eb94d430a250739f109d4a57d",
tokenType: TokenTypeLiquidStaking,
tag: "ethena",
provider: "Ethena",
},
{
name: "tonstakers liquid staking",
master: "0:bdf3fa8098d129b54b4f73b5bac5d1e1fd91eb054169c3916dfc8ccd536d1000",
tokenType: TokenTypeLiquidStaking,
tag: "tonstakers",
provider: "Tonstakers",
},
if _, ok := jettonAssetDumpInfos[master.Address]; ok {
t.Fatalf("did not expect DeDust liquid pool in dump for master %v", master.Address.ToRaw())
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
info, ok := jettonAssetAddressInfos[tongo.MustParseAccountID(tt.master)]
if !ok {
t.Fatalf("expected address match for master %v", tt.master)
}
if info.TokenType != tt.tokenType {
t.Fatalf("unexpected token type: got %q want %q", info.TokenType, tt.tokenType)
}
if info.DefiProvider.Tag != tt.tag {
t.Fatalf("unexpected provider tag: got %q want %q", info.DefiProvider.Tag, tt.tag)
}
if info.DefiProvider.Name != tt.provider {
t.Fatalf("unexpected provider name: got %q want %q", info.DefiProvider.Name, tt.provider)
}
})
infos := AssetInfos(context.Background(), staticJettonMasterSource{masters: []core.JettonMaster{master}}, zap.NewNop(), []tongo.AccountID{master.Address})
dedustInfo, ok := infos[master.Address]
if !ok {
t.Fatalf("expected DeDust logic asset info for master %v", master.Address.ToRaw())
}
assertDeDustLiquidPoolAssetInfo(t, dedustInfo)
}

func assertSTONFiLiquidPoolAssetInfo(t *testing.T, info AssetInfo) {
Expand All @@ -173,6 +59,27 @@ func assertSTONFiLiquidPoolAssetInfo(t *testing.T, info AssetInfo) {
}
}

func assertDeDustLiquidPoolAssetInfo(t *testing.T, info AssetInfo) {
t.Helper()
if info.TokenType != TokenTypeLiquidPool {
t.Fatalf("unexpected token type: got %q", info.TokenType)
}
if info.DefiProvider.Tag != dedustProviderID {
t.Fatalf("unexpected provider tag: got %q", info.DefiProvider.Tag)
}
if info.DefiProvider.Name != "DeDust.io" {
t.Fatalf("unexpected provider name: got %q", info.DefiProvider.Name)
}
}

func accountIDPtr(accountID tongo.AccountID) *tongo.AccountID {
return &accountID
}

type staticJettonMasterSource struct {
masters []core.JettonMaster
}

func (s staticJettonMasterSource) GetJettonMastersByAddresses(_ context.Context, _ []ton.AccountID) ([]core.JettonMaster, error) {
return s.masters, nil
}
Binary file added pkg/defi/assets/bemo_card.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/bemo_full.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/bemo_icon.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/bidask_card.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/bidask_full.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/bidask_icon.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/coffin_card.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/coffin_full.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/coffin_icon.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed pkg/defi/assets/ethena_card.png
Binary file not shown.
Binary file added pkg/defi/assets/ethena_card.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed pkg/defi/assets/ethena_full.png
Binary file not shown.
Binary file added pkg/defi/assets/ethena_full.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed pkg/defi/assets/ethena_icon.png
Binary file not shown.
Binary file added pkg/defi/assets/ethena_icon.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/evaa_card.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/evaa_full.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/evaa_icon.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/getgems_card.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/getgems_full.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/getgems_icon.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/hipo_card.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/hipo_full.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/hipo_icon.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/jvault_card.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/defi/assets/jvault_full.webp
Binary file added pkg/defi/assets/jvault_icon.webp
Binary file added pkg/defi/assets/kton_card.webp
Binary file added pkg/defi/assets/kton_full.webp
Binary file added pkg/defi/assets/kton_icon.webp
File renamed without changes
File renamed without changes
File renamed without changes
Binary file added pkg/defi/assets/stakee_card.webp
Binary file added pkg/defi/assets/stakee_full.webp
Binary file added pkg/defi/assets/stakee_icon.webp
Binary file added pkg/defi/assets/swapcoffee_card.webp
Binary file added pkg/defi/assets/swapcoffee_full.webp
Binary file added pkg/defi/assets/swapcoffee_icon.webp
Binary file added pkg/defi/assets/thusd_card.webp
Binary file added pkg/defi/assets/thusd_full.webp
Binary file added pkg/defi/assets/thusd_icon.webp
Binary file added pkg/defi/assets/ton_card.webp
Binary file added pkg/defi/assets/ton_full.webp
Binary file added pkg/defi/assets/ton_icon.webp
Binary file added pkg/defi/assets/tonco_card.webp
Binary file added pkg/defi/assets/tonco_full.webp
Binary file added pkg/defi/assets/tonco_icon.webp
Binary file added pkg/defi/assets/tonraffles_card.webp
Binary file added pkg/defi/assets/tonraffles_full.webp
Binary file added pkg/defi/assets/tonraffles_icon.webp
Binary file added pkg/defi/assets/tonstakers_card.webp
Binary file added pkg/defi/assets/tonstakers_full.webp
Binary file added pkg/defi/assets/tonstakers_icon.webp
Binary file added pkg/defi/assets/utonics_card.webp
Binary file added pkg/defi/assets/utonics_full.webp
Binary file added pkg/defi/assets/utonics_icon.webp
Binary file added pkg/defi/assets/whales_card.webp
Binary file added pkg/defi/assets/whales_full.webp
Binary file added pkg/defi/assets/whales_icon.webp
Binary file added pkg/defi/assets/x1000_card.webp
Binary file added pkg/defi/assets/x1000_full.webp
Binary file added pkg/defi/assets/x1000_icon.webp
Loading
Loading