diff --git a/internal/funds/scaling.go b/internal/funds/scaling.go index f8213880..4afc1bbf 100644 --- a/internal/funds/scaling.go +++ b/internal/funds/scaling.go @@ -52,6 +52,11 @@ func BuildScaledAsset(baseAsset string, scale int64) string { // GetAssets collects, per scale, the (uncolored) amount an account holds of // baseAsset. Scaling converts only uncolored balances and emits uncolored // postings, so colored balances are excluded. +// +// Postings name the asset with BuildScaledAsset, so a balance whose asset is not +// in that form (e.g. "EUR/0", "EUR/02") is excluded too: it can't be withdrawn +// under the name the conversion would post, and it would collide with the +// canonical asset of the same scale. func GetAssets(accountBalances []AccountBalance, baseAsset string) map[int64]*big.Int { result := make(map[int64]*big.Int) for _, accBalance := range accountBalances { @@ -59,7 +64,7 @@ func GetAssets(accountBalances []AccountBalance, baseAsset string) map[int64]*bi continue } accBalanceAsset, scale := GetBaseAndScale(accBalance.Asset) - if accBalanceAsset == baseAsset { + if accBalanceAsset == baseAsset && BuildScaledAsset(baseAsset, scale) == accBalance.Asset { result[scale] = new(big.Int).Set(accBalance.Amount) } } diff --git a/internal/funds/scaling_test.go b/internal/funds/scaling_test.go index 35b6d30f..c619939e 100644 --- a/internal/funds/scaling_test.go +++ b/internal/funds/scaling_test.go @@ -24,6 +24,21 @@ func TestGetAssetsExcludesColoredBalances(t *testing.T) { }, assets) } +func TestGetAssetsExcludesNonCanonicalAssets(t *testing.T) { + assets := funds.GetAssets([]funds.AccountBalance{ + {Asset: "EUR", Amount: big.NewInt(5)}, + {Asset: "EUR/0", Amount: big.NewInt(0)}, + {Asset: "EUR/02", Amount: big.NewInt(3)}, + {Asset: "EUR/x", Amount: big.NewInt(4)}, + {Asset: "EUR/2", Amount: big.NewInt(100)}, + }, "EUR") + + require.Equal(t, map[int64]*big.Int{ + 0: big.NewInt(5), + 2: big.NewInt(100), + }, assets) +} + func TestScalingAvoidSwappingAlreadyHaveAsset(t *testing.T) { // Need [USD/2 200] // Got: {USD/2 100, USD 2} diff --git a/internal/interpreter/testdata/script-tests/experimental/asset-scaling/scaling.num.specs.json b/internal/interpreter/testdata/script-tests/experimental/asset-scaling/scaling.num.specs.json index 7dc6102a..18d5102e 100644 --- a/internal/interpreter/testdata/script-tests/experimental/asset-scaling/scaling.num.specs.json +++ b/internal/interpreter/testdata/script-tests/experimental/asset-scaling/scaling.num.specs.json @@ -104,6 +104,41 @@ } ] }, + { + "it": "a non-canonical asset of the same scale does not mask the canonical one", + "balances": [ + { + "account": "src", + "asset": "EUR", + "amount": 4 + }, + { + "account": "src", + "asset": "EUR/0", + "amount": 0 + } + ], + "expect.postings": [ + { + "source": "src", + "destination": "swap", + "asset": "EUR", + "amount": 4 + }, + { + "source": "swap", + "destination": "src", + "asset": "EUR/2", + "amount": 400 + }, + { + "source": "src", + "destination": "dest", + "asset": "EUR/2", + "amount": 400 + } + ] + }, { "it": "fails when there aren't enough funds", "balances": [