Skip to content

Commit

Permalink
Default assets (#932)
Browse files Browse the repository at this point in the history
* lint

* clean code

* refactor get all assets

* fixup

* fixup

* format

* add default assets

* fix dep cycle with test utils package

* prevent removing default assets

* remove unused import

* test: improve initialize logger
  • Loading branch information
Thykof authored May 14, 2024
1 parent 25789d3 commit ad229ae
Show file tree
Hide file tree
Showing 13 changed files with 279 additions and 60 deletions.
11 changes: 11 additions & 0 deletions api/server/models/asset_info_with_balance.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 2 additions & 7 deletions api/server/restapi/configure_massa_wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,8 @@ func webAppMiddleware(handler http.Handler) http.Handler {
Resource: strings.TrimPrefix(r.URL.Path, prefix),
}
responder := html.HandleWebApp(params)
if responder != nil {
// Handle the successful response
responder.WriteResponse(w, runtime.JSONProducer())
return
}
// Handle nil response
http.Error(w, "No response from handler", http.StatusInternalServerError)
// Handle the successful response
responder.WriteResponse(w, runtime.JSONProducer())
return
}
handler.ServeHTTP(w, r)
Expand Down
6 changes: 6 additions & 0 deletions api/server/restapi/embedded_spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 9 additions & 7 deletions api/walletApi-V0.yml
Original file line number Diff line number Diff line change
Expand Up @@ -283,13 +283,13 @@ paths:
operationId: SignMessage
description: Sign a message using the account associated with the provided nickname in the path.
parameters:
- $ref: "#/parameters/nickname"
- in: body
name: body
required: true
x-nullable: false
schema:
$ref: "#/definitions/SignMessageRequest"
- $ref: "#/parameters/nickname"
- in: body
name: body
required: true
x-nullable: false
schema:
$ref: "#/definitions/SignMessageRequest"
produces:
- application/json
responses:
Expand Down Expand Up @@ -753,3 +753,5 @@ definitions:
properties:
balance:
type: string
isDefault:
type: boolean
136 changes: 102 additions & 34 deletions internal/handler/wallet/get_all_assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,65 +37,133 @@ func (g *getAllAssets) Handle(params operations.GetAllAssetsParams) middleware.R
}

// Create a slice to store the assets with their balances
AssetsWithBalance := make([]*models.AssetInfoWithBalance, 0)
assetsWithBalance := make([]*models.AssetInfoWithBalance, 0)

massaAsset, resp := g.getMASAsset(acc)
if resp != nil {
return resp
}

assetsWithBalance = append(assetsWithBalance, massaAsset)

userAssetData, resp := g.getAssetsData(acc)
if resp != nil {
return resp
}

assetsWithBalance = append(assetsWithBalance, userAssetData...)

// sort AssetsWithBalance by name
sort.Slice(assetsWithBalance, func(i, j int) bool {
return assetsWithBalance[i].Name < assetsWithBalance[j].Name
})

// Return the list of assets with balance
return operations.NewGetAllAssetsOK().WithPayload(assetsWithBalance)
}

func (g *getAllAssets) getMASAsset(acc *account.Account) (*models.AssetInfoWithBalance, middleware.Responder) {
// Fetch the account information for the wallet using the massaClient
infos, err := g.massaClient.GetAccountsInfos([]*account.Account{acc})
if err != nil {
// Handle the error and return an internal server error response
errorMsg := fmt.Sprintf("Failed to fetch balance for asset %s: %s", "MASSA", err.Error())

return operations.NewGetAllAssetsInternalServerError().WithPayload(&models.Error{
return nil, operations.NewGetAllAssetsInternalServerError().WithPayload(&models.Error{
Code: errorFetchAssetBalance,
Message: errorMsg,
})
}

// Create the asset info for the Massa token and append it to the result slice
MassaAsset := &models.AssetInfoWithBalance{
massaAsset := &models.AssetInfoWithBalance{
AssetInfo: assets.MASInfo(),
Balance: fmt.Sprint(infos[0].CandidateBalance),
IsDefault: true,
}
AssetsWithBalance = append(AssetsWithBalance, MassaAsset)

// Retrieve all assets from the selected nickname
for assetAddress, assetInfo := range g.AssetsStore.Assets[params.Nickname].ContractAssets {
// First, check if the asset exists in the network
if !g.massaClient.AssetExistInNetwork(assetAddress) {
// If the asset does not exist in the network, skip it and go to the next one
logger.Infof("Asset %s does not exist in the network", assetAddress)
continue
return massaAsset, nil
}

func (g *getAllAssets) getAssetsData(acc *account.Account) ([]*models.AssetInfoWithBalance, middleware.Responder) {
defaultAssets, err := assets.GetDefaultAssets()
if err != nil {
logger.Errorf("Failed to get default assets: %s", err.Error())
}

assetsInfo := make([]*models.AssetInfoWithBalance, 0)

// Initialize map to track addressed already added
includedAddresses := map[string]bool{}

for _, asset := range defaultAssets {
completeAsset := &models.AssetInfoWithBalance{
AssetInfo: asset,
Balance: "",
IsDefault: true,
}
// Fetch the balance for the current asset
address, err := acc.Address.MarshalText()
if err != nil {
return newErrorResponse(err.Error(), errorGetAccount, http.StatusInternalServerError)
assetsInfo = append(assetsInfo, completeAsset)
includedAddresses[asset.Address] = true
}

// Append default assets ensuring no duplication
for _, asset := range g.AssetsStore.Assets[acc.Nickname].ContractAssets {
// Append the asset info to the result slice if it is not already in the list
if _, exists := includedAddresses[asset.Address]; !exists {
completeAsset := &models.AssetInfoWithBalance{
AssetInfo: asset,
Balance: "",
IsDefault: false,
}
assetsInfo = append(assetsInfo, completeAsset)
includedAddresses[asset.Address] = true
}
}

balance, err := g.massaClient.DatastoreAssetBalance(assetAddress, string(address))
if err != nil {
// Handle the error and return an internal server error response
errorMsg := fmt.Sprintf("Failed to fetch balance for asset %s: %s", assetAddress, err.Error())
assetsWithBalance := make([]*models.AssetInfoWithBalance, 0)

return operations.NewGetAllAssetsInternalServerError().WithPayload(&models.Error{
Code: errorFetchAssetBalance,
Message: errorMsg,
})
// Retrieve all assets from the selected nickname
for _, asset := range assetsInfo {
// Fetch the balance for the current asset
balance, resp := g.fetchAssetData(asset.Address, acc)
if resp != nil {
return nil, resp
}

// Create the asset info with balance and append it to the result slice
assetWithBalance := &models.AssetInfoWithBalance{
AssetInfo: assetInfo,
Balance: balance,
// If the asset does not exist in the network, skip it and go to the next one
if balance == nil {
continue
}
AssetsWithBalance = append(AssetsWithBalance, assetWithBalance)

asset.Balance = *balance
assetsWithBalance = append(assetsWithBalance, asset)
}

// sort AssetsWithBalance by name
sort.Slice(AssetsWithBalance, func(i, j int) bool {
return AssetsWithBalance[i].Name < AssetsWithBalance[j].Name
})
return assetsWithBalance, nil
}

// Return the list of assets with balance
return operations.NewGetAllAssetsOK().WithPayload(AssetsWithBalance)
func (g *getAllAssets) fetchAssetData(assetAddress string, acc *account.Account) (*string, middleware.Responder) {
// First, check if the asset exists in the network
if !g.massaClient.AssetExistInNetwork(assetAddress) {
logger.Infof("Asset %s does not exist in the network", assetAddress)
return nil, nil
}

address, err := acc.Address.MarshalText()
if err != nil {
return nil, newErrorResponse(err.Error(), errorGetAccount, http.StatusInternalServerError)
}

balance, err := g.massaClient.DatastoreAssetBalance(assetAddress, string(address))
if err != nil {
errorMsg := fmt.Sprintf("Failed to fetch balance for asset %s: %s", assetAddress, err.Error())

// Handle the error and return an internal server error response
return nil, operations.NewGetAllAssetsInternalServerError().WithPayload(&models.Error{
Code: errorFetchAssetBalance,
Message: errorMsg,
})
}

return &balance, nil
}
12 changes: 12 additions & 0 deletions internal/initialize/asset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package initialize

import "github.com/massalabs/station-massa-wallet/pkg/assets"

func Asset() error {
err := assets.InitDefaultAsset()
if err != nil {
return err
}

return nil
}
5 changes: 5 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ func main() {
logger.Fatalf("Failed to initialize wallet: %v", err)
}

err = initialize.Asset()
if err != nil {
logger.Errorf("Failed to initialize asset: %v", err)
}

app := walletApp.NewWalletApp(wallet)

if walletApp.IsTestMode() {
Expand Down
8 changes: 6 additions & 2 deletions pkg/assets/asset.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import (
"github.com/pkg/errors"
)

const (
permissionUrwGrOr = 0o644
)

// AssetsStore encapsulates all the nicknames with their related contract assets.
type AssetsStore struct {
Assets map[string]Assets
Expand Down Expand Up @@ -164,7 +168,7 @@ func (s *AssetsStore) save() error {
return errors.Wrap(err, "error getting assets JSON file")
}

if err := os.WriteFile(assetsJSONPath, data, 0o644); err != nil {
if err := os.WriteFile(assetsJSONPath, data, permissionUrwGrOr); err != nil {
return errors.Wrap(err, "failed to write JSON data to file")
}

Expand Down Expand Up @@ -254,7 +258,7 @@ func (s *AssetsStore) AllAssets(nickname string) []models.AssetInfo {

// createJSONFile creates an empty JSON file at the specified path.
func createJSONFile(path string) error {
if err := os.WriteFile(path, []byte("{}"), 0o644); err != nil {
if err := os.WriteFile(path, []byte("{}"), permissionUrwGrOr); err != nil {
return err
}

Expand Down
Loading

0 comments on commit ad229ae

Please sign in to comment.