Skip to content

Commit 7258548

Browse files
authored
Task/sean/str 204 (#237)
* Added "transfer()" commands to token cost analysis * stash * Add new coins to asset table on Quote * fix unit21 tests
1 parent 536bcde commit 7258548

File tree

7 files changed

+80
-18
lines changed

7 files changed

+80
-18
lines changed

api/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func NewServices(config APIConfig, repos repository.Repositories) service.Servic
4343
device := service.NewDevice(deviceRepos, fingerprint)
4444

4545
auth := service.NewAuth(repos, verification, device)
46-
cost := service.NewCost(config.Redis)
46+
cost := service.NewCost(config.Redis, repos)
4747
executor := service.NewExecutor()
4848
geofencing := service.NewGeofencing(config.Redis)
4949

pkg/internal/unit21/transaction_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,12 @@ func mockTransactionRows(mock sqlmock.Sqlmock, transaction model.Transaction, us
168168
AddRow(transaction.DestinationTxLegId, time.Now(), "1", transaction.TransactionAmount, assetId2, userId, instrumentId2)
169169
mock.ExpectQuery("SELECT * FROM tx_leg WHERE id = $1 AND deleted_at IS NULL").WithArgs(transaction.DestinationTxLegId).WillReturnRows(mockedTxLegRow2)
170170

171-
mockedAssetRow1 := sqlmock.NewRows([]string{"id", "name", "description", "decimals", "is_crypto", "value_oracle"}).
172-
AddRow(assetId1, "USD", "fiat USD", 6, false, "self")
171+
mockedAssetRow1 := sqlmock.NewRows([]string{"id", "name", "description", "decimals", "is_crypto", "network_id", "value_oracle"}).
172+
AddRow(assetId1, "USD", "fiat USD", 6, false, transaction.NetworkId, "self")
173173
mock.ExpectQuery("SELECT * FROM asset WHERE id = $1 AND deleted_at IS NULL").WithArgs(assetId1).WillReturnRows(mockedAssetRow1)
174174

175-
mockedAssetRow2 := sqlmock.NewRows([]string{"id", "name", "description", "decimals", "is_crypto", "value_oracle"}).
176-
AddRow(assetId2, "Noose The Goose", "Noose the Goose NFT", 0, true, "joepegs.com")
175+
mockedAssetRow2 := sqlmock.NewRows([]string{"id", "name", "description", "decimals", "is_crypto", "network_id", "value_oracle"}).
176+
AddRow(assetId2, "Noose The Goose", "Noose the Goose NFT", 0, true, transaction.NetworkId, "joepegs.com")
177177
mock.ExpectQuery("SELECT * FROM asset WHERE id = $1 AND deleted_at IS NULL").WithArgs(assetId2).WillReturnRows(mockedAssetRow2)
178178

179179
mockedDeviceRow := sqlmock.NewRows([]string{"id", "type", "description", "fingerprint", "ip_addresses", "user_id"}).

pkg/model/entity.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@ type Asset struct {
6565
Description string `json:"description" db:"description"`
6666
Decimals uint64 `json:"decimals" db:"decimals"`
6767
IsCrypto bool `json:"isCrypto" db:"is_crypto"`
68+
NetworkId string `json:"networkId" db:"network_id"`
6869
ValueOracle sql.NullString `json:"valueOracle" db:"value_oracle"`
6970
ValueOracle2 sql.NullString `json:"valueOracle2" db:"value_oracle_2"`
71+
Address sql.NullString `json:"address" db:"address"`
7072
}
7173

7274
type UserToPlatform struct {

pkg/repository/asset.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type Asset interface {
1717
Create(ctx context.Context, m model.Asset) (model.Asset, error)
1818
GetById(ctx context.Context, id string) (model.Asset, error)
1919
GetByName(ctx context.Context, name string) (model.Asset, error)
20+
GetByKey(ctx context.Context, networkId string, address string) (model.Asset, error)
2021
Update(ctx context.Context, Id string, updates any) error
2122
}
2223

@@ -32,16 +33,8 @@ func (a asset[T]) Create(ctx context.Context, insert model.Asset) (model.Asset,
3233
m := model.Asset{}
3334

3435
query, args, err := a.Named(`
35-
WITH insert_asset AS (
36-
INSERT INTO asset (name, description, decimals, is_crypto, value_oracle, value_oracle_2)
37-
VALUES(:name, :description, :decimals, :is_crypto, :value_oracle, :value_oracle_2)
38-
ON CONFLICT (name) DO NOTHING
39-
RETURNING *
40-
)
41-
INSERT INTO asset_to_network (asset_id, network_id)
42-
VALUES(insert_asset.id, :network_id)
43-
ON CONFLICT (asset_id, network_id) DO NOTHING
44-
`, insert)
36+
INSERT INTO asset (name, description, decimals, is_crypto, network_id, value_oracle, value_oracle_2, address)
37+
VALUES(:name, :description, :decimals, :is_crypto, :network_id, :value_oracle, :value_oracle_2, :address) RETURNING *`, insert)
4538

4639
if err != nil {
4740
return m, libcommon.StringError(err)
@@ -64,3 +57,12 @@ func (a asset[T]) GetByName(ctx context.Context, name string) (model.Asset, erro
6457
}
6558
return m, nil
6659
}
60+
61+
func (a asset[T]) GetByKey(ctx context.Context, networkId string, address string) (model.Asset, error) {
62+
m := model.Asset{}
63+
err := a.Store.GetContext(ctx, &m, fmt.Sprintf("SELECT * FROM %s WHERE network_id = $1 AND address = $2", a.Table), networkId, address)
64+
if err != nil && err == sql.ErrNoRows {
65+
return m, serror.NOT_FOUND
66+
}
67+
return m, nil
68+
}

pkg/service/cost.go

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package service
22

33
import (
4+
"context"
5+
"database/sql"
46
"fmt"
57
"math"
68
"math/big"
@@ -13,6 +15,7 @@ import (
1315
"github.com/String-xyz/string-api/config"
1416
"github.com/String-xyz/string-api/pkg/internal/common"
1517
"github.com/String-xyz/string-api/pkg/model"
18+
"github.com/String-xyz/string-api/pkg/repository"
1619
"github.com/String-xyz/string-api/pkg/store"
1720
"github.com/pkg/errors"
1821
)
@@ -77,14 +80,16 @@ type CostCache struct {
7780
type Cost interface {
7881
EstimateTransaction(p EstimationParams, chain Chain) (estimate model.Estimate[float64], err error)
7982
LookupUSD(quantity float64, coins ...string) (float64, error)
83+
AddCoinToAssetTable(id string, networkId string, address string) error
8084
}
8185

8286
type cost struct {
8387
redis database.RedisStore // cached token and gas costs
88+
repos repository.Repositories
8489
subnetTokenProxies map[CoinKey]CoinKey
8590
}
8691

87-
func NewCost(redis database.RedisStore) Cost {
92+
func NewCost(redis database.RedisStore, repos repository.Repositories) Cost {
8893
// Temporarily hard-coding this to reduce future cost-of-change with database
8994
subnetTokenProxies := map[CoinKey]CoinKey{
9095
// USDc DFK Subnet -> USDc Avalanche:
@@ -94,6 +99,7 @@ func NewCost(redis database.RedisStore) Cost {
9499
}
95100
return &cost{
96101
redis: redis,
102+
repos: repos,
97103
subnetTokenProxies: subnetTokenProxies,
98104
}
99105
}
@@ -118,6 +124,44 @@ func GetCoingeckoPlatformMapping() (map[uint64]string, map[string]uint64, error)
118124
return idToPlatform, platformToId, nil
119125
}
120126

127+
func GetCoingeckoCoinData(id string) (CoingeckoCoin, error) {
128+
var coin CoingeckoCoin
129+
err := common.GetJsonGeneric("https://api.coingecko.com/api/v3/coins/"+id+"?localization=false&tickers=false&market_data=false&community_data=false&developer_data=false&sparkline=false", &coin)
130+
if err != nil {
131+
return coin, libcommon.StringError(err)
132+
}
133+
return coin, nil
134+
}
135+
136+
func (c cost) AddCoinToAssetTable(id string, networkId string, address string) error {
137+
coinData, err := GetCoingeckoCoinData(id)
138+
if err != nil {
139+
return libcommon.StringError(err)
140+
}
141+
_, err = c.repos.Asset.GetByKey(context.Background(), networkId, address)
142+
if err != nil && err == serror.NOT_FOUND {
143+
// add it to the asset table
144+
_, err := c.repos.Asset.Create(context.Background(), model.Asset{
145+
Name: coinData.Symbol, // Name in our database is the Symbol
146+
Description: coinData.Name, // Description in our database is the Name, note: coingecko provides an actual description
147+
Decimals: 18, // TODO: Get this from coinData - it's listed per network. Decimals only affects display.
148+
IsCrypto: true,
149+
ValueOracle: sql.NullString{String: id, Valid: true},
150+
NetworkId: networkId,
151+
Address: sql.NullString{String: address, Valid: true},
152+
// TODO: Get second oracle data using data from first oracle
153+
})
154+
if err != nil {
155+
return libcommon.StringError(err)
156+
}
157+
} else if err != nil {
158+
return libcommon.StringError(err)
159+
}
160+
// Check if we are on a new chain
161+
162+
return nil
163+
}
164+
121165
func GetCoingeckoCoinMapping() (map[string]string, error) {
122166
_, platformToId, err := GetCoingeckoPlatformMapping()
123167
if err != nil {
@@ -243,6 +287,13 @@ func (c cost) EstimateTransaction(p EstimationParams, chain Chain) (estimate mod
243287
if !ok {
244288
return estimate, errors.New("CoinGecko does not list token " + p.TokenAddrs[i])
245289
}
290+
291+
// Check if the token is in our database and add it if it's not in there
292+
err = c.AddCoinToAssetTable(tokenName, chain.UUID, p.TokenAddrs[i])
293+
if err != nil {
294+
return estimate, libcommon.StringError(err)
295+
}
296+
246297
tokenCost, err := c.LookupUSD(costTokenEth, tokenName)
247298
if err != nil {
248299
return estimate, libcommon.StringError(err)

pkg/service/cost_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package service
22

33
import (
4+
"fmt"
45
"testing"
56

67
"github.com/stretchr/testify/assert"
@@ -13,3 +14,9 @@ func TestGetTokenPrices(t *testing.T) {
1314
assert.True(t, ok)
1415
assert.Equal(t, "usd-coin", name)
1516
}
17+
18+
func TestGetTokenData(t *testing.T) {
19+
coin, err := GetCoingeckoCoinData("defi-kingdoms")
20+
assert.NoError(t, err)
21+
fmt.Printf("%+v", coin)
22+
}

pkg/service/transaction.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ func (t transaction) testTransaction(executor Executor, request model.Transactio
655655
if err != nil {
656656
return res, eth, CallEstimate{}, libcommon.StringError(err)
657657
}
658-
cost := NewCost(t.redis)
658+
cost := NewCost(t.redis, t.repos)
659659
estimationParams := EstimationParams{
660660
ChainId: chainId,
661661
CostETH: estimateEVM.Value,
@@ -860,7 +860,7 @@ func (t transaction) tenderTransaction(ctx context.Context, p transactionProcess
860860
_, finish := Span(ctx, "service.transaction.tenderTransaction", SpanTag{"platformId": p.platformId})
861861
defer finish()
862862

863-
cost := NewCost(t.redis)
863+
cost := NewCost(t.redis, t.repos)
864864
trueWei := big.NewInt(0).Add(p.cumulativeValue, big.NewInt(int64(p.trueGas)))
865865
trueEth := common.WeiToEther(trueWei)
866866
trueUSD, err := cost.LookupUSD(trueEth, p.chain.CoingeckoName, p.chain.CoincapName)

0 commit comments

Comments
 (0)