Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 3 additions & 4 deletions internal/data/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"database/sql"
"errors"
"fmt"
"slices"
"strings"
"time"

Expand Down Expand Up @@ -44,23 +43,23 @@ func AssetColumnNames(tableReference, resultAlias string, includeDates bool) str
// IsNative returns true if the asset is the native asset (XLM).
func (a Asset) IsNative() bool {
return strings.TrimSpace(a.Issuer) == "" &&
slices.Contains([]string{"XLM", "NATIVE"}, strings.ToUpper(a.Code))
(a.Code == "XLM" || a.Code == "NATIVE")
}

// Equals returns true if the asset is the same as the other asset. Case-insensitive.
func (a Asset) Equals(other Asset) bool {
if a.IsNative() && other.IsNative() {
return true
}
return strings.EqualFold(a.Code, other.Code) && strings.EqualFold(a.Issuer, other.Issuer)
return a.Code == other.Code && strings.EqualFold(a.Issuer, other.Issuer)
}

func (a Asset) EqualsHorizonAsset(horizonAsset base.Asset) bool {
if a.IsNative() && horizonAsset.Type == "native" {
return true
}

return strings.EqualFold(a.Code, horizonAsset.Code) && strings.EqualFold(a.Issuer, horizonAsset.Issuer)
return a.Code == horizonAsset.Code && strings.EqualFold(a.Issuer, horizonAsset.Issuer)
}

func (a Asset) ToBasicAsset() txnbuild.Asset {
Expand Down
41 changes: 27 additions & 14 deletions internal/data/assets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,11 @@ func Test_Asset_Equals(t *testing.T) {
}{
{Asset{Code: "XLM"}, Asset{Code: "XLM"}, true},
{Asset{Code: "NATIVE"}, Asset{Code: "XLM"}, true},
{Asset{Code: "XLM"}, Asset{Code: "xlm"}, true},
{Asset{Code: "NATIVE"}, Asset{Code: "native"}, false},
{Asset{Code: "XLM"}, Asset{Code: "xlm"}, false},
{Asset{Code: "XLM"}, Asset{Code: "ABC"}, false},
{Asset{Issuer: "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", Code: "USDC"}, Asset{Issuer: "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", Code: "usdc"}, true},
{Asset{Issuer: "gbbD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", Code: "USDC"}, Asset{Issuer: "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", Code: "usdc"}, true},
{Asset{Issuer: "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", Code: "USDC"}, Asset{Issuer: "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", Code: "usdc"}, false},
{Asset{Issuer: "gbbD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", Code: "USDC"}, Asset{Issuer: "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", Code: "USDC"}, true},
{Asset{Issuer: "Issuer1", Code: "ABC"}, Asset{Issuer: "Issuer2", Code: "ABC"}, false},
{Asset{Issuer: "Issuer1", Code: "ABC"}, Asset{Issuer: "Issuer1", Code: "XYZ"}, false},
}
Expand All @@ -138,16 +139,16 @@ func Test_Asset_EqualsHorizonAsset(t *testing.T) {
expectedResult bool
}{
{
name: "🟢 native assets",
name: "🟢 XLM alias is equal to native type",
localAsset: Asset{Code: "XLM"},
horizonAsset: base.Asset{Type: "native"},
expectedResult: true,
},
{
name: "🟢 native asset 2",
localAsset: Asset{Code: "NATIVE"},
name: "🔴 xlm alias is not equal to native type",
localAsset: Asset{Code: "xlm"},
horizonAsset: base.Asset{Type: "native"},
expectedResult: true,
expectedResult: false,
},
{
name: "🟢 issued assets are equal",
Expand All @@ -156,31 +157,43 @@ func Test_Asset_EqualsHorizonAsset(t *testing.T) {
expectedResult: true,
},
{
name: "🟢 issued assets are equal2",
name: "🟢 issued assets with different case in issuer are equal",
localAsset: Asset{Code: "USDC", Issuer: "gbbD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"},
horizonAsset: base.Asset{Type: "credit_alphanum4", Code: "USDC", Issuer: "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"},
expectedResult: true,
},
{
name: "🔴 issued assets with different case in code are not equal",
localAsset: Asset{Code: "usdc", Issuer: "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"},
horizonAsset: base.Asset{Type: "credit_alphanum4", Code: "USdc", Issuer: "gbbD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"},
expectedResult: false,
},
{
name: "🟢 NATIVE asset alias is equal to native type",
localAsset: Asset{Code: "NATIVE"},
horizonAsset: base.Asset{Type: "native"},
expectedResult: true,
},
{
name: "🔴 native asset != issued asset",
localAsset: Asset{Code: "XLM"},
horizonAsset: base.Asset{Type: "credit_alphanum4", Code: "NATIVE", Issuer: "issuer"},
name: "🔴 native asset alias is not equal to native type",
localAsset: Asset{Code: "native"},
horizonAsset: base.Asset{Type: "native"},
expectedResult: false,
},
{
name: "🔴 issued asset != native asset",
name: "🔴 issued asset is not equal to native asset",
localAsset: Asset{Code: "USDC", Issuer: "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"},
horizonAsset: base.Asset{Type: "native"},
expectedResult: false,
},
{
name: "🔴 issued asset != issued asset",
name: "🔴 issued asset is not equal to issued asset with different code",
localAsset: Asset{Code: "USDC", Issuer: "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"},
horizonAsset: base.Asset{Type: "credit_alphanum4", Code: "EUROC", Issuer: "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"},
expectedResult: false,
},
{
name: "🔴 issued asset != issued asset 2",
name: "🔴 issued asset is not equal to issued asset with different issuer",
localAsset: Asset{Code: "USDC", Issuer: "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"},
horizonAsset: base.Asset{Type: "credit_alphanum4", Code: "USDC", Issuer: "another-issuer"},
expectedResult: false,
Expand Down
3 changes: 1 addition & 2 deletions internal/data/fixtures.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"image"
"image/color"
"math/big"
"strings"
"testing"
"time"

Expand All @@ -28,7 +27,7 @@ const (
func CreateAssetFixture(t *testing.T, ctx context.Context, sqlExec db.SQLExecuter, code, issuer string) *Asset {
issuerAddress := issuer

if issuerAddress == "" && strings.ToUpper(code) != "XLM" {
if issuerAddress == "" && code != "XLM" && code != "NATIVE" {
issuer, err := utils.RandomString(56)
require.NoError(t, err)
issuerAddress = issuer
Expand Down
13 changes: 8 additions & 5 deletions internal/serve/httphandler/assets_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@ import (
"github.com/stellar/stellar-disbursement-platform-backend/internal/serve/httperror"
"github.com/stellar/stellar-disbursement-platform-backend/internal/serve/validators"
"github.com/stellar/stellar-disbursement-platform-backend/internal/services"
"github.com/stellar/stellar-disbursement-platform-backend/internal/services/assets"
"github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine"
tssUtils "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/utils"
"github.com/stellar/stellar-disbursement-platform-backend/internal/utils"
"github.com/stellar/stellar-disbursement-platform-backend/pkg/schema"
)

const stellarNativeAssetCode = "XLM"
func isNativeAssetCode(code string) bool {
return code == assets.XLMAssetCode || code == assets.XLMAssetCodeAlias
}

var errCouldNotRemoveTrustline = errors.New("could not remove trustline")

Expand Down Expand Up @@ -144,12 +147,12 @@ func (c AssetsHandler) CreateAsset(w http.ResponseWriter, r *http.Request) {
return
}

assetCode := strings.TrimSpace(strings.ToUpper(assetRequest.Code))
assetCode := strings.TrimSpace(assetRequest.Code)
assetIssuer := strings.TrimSpace(assetRequest.Issuer)

v := validators.NewValidator()
v.Check(assetCode != "", "code", "code is required")
if assetCode != stellarNativeAssetCode {
if !isNativeAssetCode(assetCode) {
v.Check(strkey.IsValidEd25519PublicKey(assetIssuer), "issuer", "issuer is invalid")
}

Expand Down Expand Up @@ -267,7 +270,7 @@ func (c AssetsHandler) handleUpdateAssetTrustlineForDistributionAccount(

changeTrustOperations := make([]*txnbuild.ChangeTrust, 0)
// remove asset
if assetToRemoveTrustline != nil && strings.ToUpper(assetToRemoveTrustline.Code) != stellarNativeAssetCode {
if assetToRemoveTrustline != nil && !isNativeAssetCode(assetToRemoveTrustline.Code) {
for _, balance := range acc.Balances {
if balance.Asset.Code == assetToRemoveTrustline.Code && balance.Asset.Issuer == assetToRemoveTrustline.Issuer {
assetToRemoveTrustlineBalance, parseBalErr := amount.ParseInt64(balance.Balance)
Expand Down Expand Up @@ -305,7 +308,7 @@ func (c AssetsHandler) handleUpdateAssetTrustlineForDistributionAccount(
}

// add asset
if assetToAddTrustline != nil && strings.ToUpper(assetToAddTrustline.Code) != stellarNativeAssetCode {
if assetToAddTrustline != nil && !isNativeAssetCode(assetToAddTrustline.Code) {
var assetToAddTrustlineFound bool
for _, balance := range acc.Balances {
if balance.Asset.Code == assetToAddTrustline.Code && balance.Asset.Issuer == assetToAddTrustline.Issuer {
Expand Down
2 changes: 1 addition & 1 deletion internal/serve/validators/wallet_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ func (wv *WalletValidator) inferAssetType(asset AssetReference) AssetReference {
// Inference logic for backward compatibility
result := asset

if strings.ToUpper(asset.Code) == assets.XLMAssetCode && asset.Issuer == "" {
if (asset.Code == assets.XLMAssetCode || asset.Code == "NATIVE") && asset.Issuer == "" {
result.Type = string(AssetReferenceTypeNative)
result.Code = ""
return result
Expand Down
6 changes: 2 additions & 4 deletions internal/serve/validators/wallet_validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -635,8 +635,7 @@ func TestWalletValidator_inferAssetType(t *testing.T) {
Issuer: "",
},
expected: AssetReference{
Type: "native",
Code: "",
Code: "xlm",
Issuer: "",
},
},
Expand Down Expand Up @@ -693,8 +692,7 @@ func TestWalletValidator_inferAssetType(t *testing.T) {
Code: "XLm",
},
expected: AssetReference{
Type: "native",
Code: "",
Code: "XLm",
},
},
}
Expand Down
2 changes: 2 additions & 0 deletions internal/services/assets/assets_pubnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ var EURCAssetPubnet = data.Asset{

const XLMAssetCode = "XLM"

const XLMAssetCodeAlias = "NATIVE"

var XLMAsset = data.Asset{
Code: XLMAssetCode,
Issuer: "",
Expand Down
6 changes: 5 additions & 1 deletion internal/services/payment_to_submitter_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/stellar/stellar-disbursement-platform-backend/internal/circle"
"github.com/stellar/stellar-disbursement-platform-backend/internal/data"
"github.com/stellar/stellar-disbursement-platform-backend/internal/sdpcontext"
"github.com/stellar/stellar-disbursement-platform-backend/internal/services/assets"
"github.com/stellar/stellar-disbursement-platform-backend/internal/services/paymentdispatchers"
"github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/signing"
txSubStore "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/store"
Expand Down Expand Up @@ -167,7 +168,10 @@ func validatePaymentReadyForSending(p *data.Payment) error {
return fmt.Errorf("payment asset code is empty for payment %s", p.ID)
}
// 3. payment.asset.Issuer is used as transaction.AssetIssuer
if strings.TrimSpace(p.Asset.Issuer) == "" && strings.TrimSpace(strings.ToUpper(p.Asset.Code)) != "XLM" {
codeTrimmed := strings.TrimSpace(p.Asset.Code)
if strings.TrimSpace(p.Asset.Issuer) == "" &&
codeTrimmed != assets.XLMAssetCode &&
codeTrimmed != assets.XLMAssetCodeAlias {
return fmt.Errorf("payment asset issuer is empty for payment %s", p.ID)
}
// 4. payment.Amount is used as transaction.Amount
Expand Down
4 changes: 2 additions & 2 deletions internal/services/send_receiver_wallets_invite_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"html/template"
"net/url"
"path"
"slices"
"strings"
"time"

Expand All @@ -18,6 +17,7 @@ import (
"github.com/stellar/stellar-disbursement-platform-backend/internal/data"
"github.com/stellar/stellar-disbursement-platform-backend/internal/message"
"github.com/stellar/stellar-disbursement-platform-backend/internal/sdpcontext"
"github.com/stellar/stellar-disbursement-platform-backend/internal/services/assets"
"github.com/stellar/stellar-disbursement-platform-backend/internal/utils"
)

Expand Down Expand Up @@ -329,7 +329,7 @@ type WalletDeepLink struct {

func (wdl WalletDeepLink) isNativeAsset() bool {
return wdl.AssetIssuer == "" &&
slices.Contains([]string{"XLM", "NATIVE"}, strings.ToUpper(wdl.AssetCode))
(wdl.AssetCode == assets.XLMAssetCode || wdl.AssetCode == assets.XLMAssetCodeAlias)
}

func (wdl WalletDeepLink) assetName() string {
Expand Down
16 changes: 8 additions & 8 deletions internal/services/send_receiver_wallets_invite_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1213,10 +1213,10 @@ func Test_WalletDeepLink_isNativeAsset(t *testing.T) {
wantResult: true,
},
{
name: "🟢 xLm without issuer should be native asset (case insensitive)",
assetCode: "XLM",
name: "🔴 xLm without issuer should NOT be native asset (case sensitive)",
assetCode: "xLm",
assetIssuer: "",
wantResult: true,
wantResult: false,
},
{
name: "🔴 XLM with issuer should NOT be native asset",
Expand All @@ -1225,16 +1225,16 @@ func Test_WalletDeepLink_isNativeAsset(t *testing.T) {
wantResult: false,
},
{
name: "🟢 native without issuer should be native asset",
assetCode: "native",
name: "🟢 NATIVE without issuer should be native asset",
assetCode: "NATIVE",
assetIssuer: "",
wantResult: true,
},
{
name: "🟢 NaTiVe without issuer should be native asset (case insensitive)",
name: "🔴 NaTiVe without issuer should NOT be native asset (case sensitive)",
assetCode: "NaTiVe",
assetIssuer: "",
wantResult: true,
wantResult: false,
},
{
name: "🔴 native with issuer should NOT be native asset",
Expand Down Expand Up @@ -1286,7 +1286,7 @@ func Test_WalletDeepLink_assetName(t *testing.T) {
name: "'native' native asset",
assetCode: "native",
assetIssuer: "",
wantResult: "native",
wantResult: "native-",
},
{
name: "'native' with an issuer",
Expand Down
5 changes: 2 additions & 3 deletions internal/transactionsubmission/services/horizon.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"slices"
"sort"
"strconv"
"strings"
"time"

"github.com/avast/retry-go/v4"
Expand Down Expand Up @@ -410,7 +409,7 @@ func getAccountDetails(client horizonclient.ClientInterface, accountID string) (
return &account, nil
}

// getAssetID returns asset identifier formatted as CODE:issuer.
// getAssetID returns asset identifier formatted as code:issuer.
func getAssetID(code, issuer string) string {
return fmt.Sprintf("%s:%s", strings.ToUpper(code), issuer)
return fmt.Sprintf("%s:%s", code, issuer)
}
4 changes: 2 additions & 2 deletions internal/transactionsubmission/transaction_worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"slices"
"strings"

"github.com/google/uuid"
"github.com/stellar/go-stellar-sdk/clients/horizonclient"
Expand All @@ -17,6 +16,7 @@ import (
"github.com/stellar/stellar-disbursement-platform-backend/db"
"github.com/stellar/stellar-disbursement-platform-backend/internal/crashtracker"
sdpMonitor "github.com/stellar/stellar-disbursement-platform-backend/internal/monitor"
"github.com/stellar/stellar-disbursement-platform-backend/internal/services/assets"
"github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine"
tssMonitor "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/monitor"
"github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/store"
Expand Down Expand Up @@ -435,7 +435,7 @@ func (tw *TransactionWorker) buildAndSignTransaction(ctx context.Context, txJob
return nil, fmt.Errorf("asset code cannot be empty")
}
var asset txnbuild.Asset = txnbuild.NativeAsset{}
if strings.ToUpper(txJob.Transaction.AssetCode) != "XLM" {
if txJob.Transaction.AssetCode != assets.XLMAssetCode && txJob.Transaction.AssetCode != assets.XLMAssetCodeAlias {
if !strkey.IsValidEd25519PublicKey(txJob.Transaction.AssetIssuer) {
return nil, fmt.Errorf("invalid asset issuer: %v", txJob.Transaction.AssetIssuer)
}
Expand Down
4 changes: 2 additions & 2 deletions internal/transactionsubmission/transaction_worker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"net/http"
"strings"
"testing"

"github.com/google/uuid"
Expand All @@ -29,6 +28,7 @@ import (
"github.com/stellar/stellar-disbursement-platform-backend/internal/crashtracker"
sdpMonitor "github.com/stellar/stellar-disbursement-platform-backend/internal/monitor"
"github.com/stellar/stellar-disbursement-platform-backend/internal/serve/httpclient"
"github.com/stellar/stellar-disbursement-platform-backend/internal/services/assets"
"github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine"
engineMocks "github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/mocks"
"github.com/stellar/stellar-disbursement-platform-backend/internal/transactionsubmission/engine/preconditions"
Expand Down Expand Up @@ -1575,7 +1575,7 @@ func Test_TransactionWorker_buildAndSignTransaction(t *testing.T) {

// Check that the transaction was built correctly:
var wantAsset txnbuild.Asset = txnbuild.NativeAsset{}
if strings.ToUpper(txJob.Transaction.AssetCode) != "XLM" {
if txJob.Transaction.AssetCode != assets.XLMAssetCode && txJob.Transaction.AssetCode != assets.XLMAssetCodeAlias {
wantAsset = txnbuild.CreditAsset{
Code: txJob.Transaction.AssetCode,
Issuer: txJob.Transaction.AssetIssuer,
Expand Down