-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge PR #3747: Implement initial simple denom convert utils
- Loading branch information
1 parent
5115dd4
commit 25408e7
Showing
4 changed files
with
149 additions
and
2 deletions.
There are no files selected for viewing
1 change: 1 addition & 0 deletions
1
.pending/features/sdk/Implement-coin-conversion-and-denomination-registration-utilities
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Implement coin conversion and denomination registration utilities |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package types | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
// denomUnits contains a mapping of denomination mapped to their respective unit | ||
// multipliers (e.g. 1atom = 10^-6uatom). | ||
var denomUnits = map[string]Dec{} | ||
|
||
// RegisterDenom registers a denomination with a corresponding unit. If the | ||
// denomination is already registered, an error will be returned. | ||
func RegisterDenom(denom string, unit Dec) error { | ||
if err := validateDenom(denom); err != nil { | ||
return err | ||
} | ||
|
||
if _, ok := denomUnits[denom]; ok { | ||
return fmt.Errorf("denom %s already registered", denom) | ||
} | ||
|
||
denomUnits[denom] = unit | ||
return nil | ||
} | ||
|
||
// GetDenomUnit returns a unit for a given denomination if it exists. A boolean | ||
// is returned if the denomination is registered. | ||
func GetDenomUnit(denom string) (Dec, bool) { | ||
if err := validateDenom(denom); err != nil { | ||
return ZeroDec(), false | ||
} | ||
|
||
unit, ok := denomUnits[denom] | ||
if !ok { | ||
return ZeroDec(), false | ||
} | ||
|
||
return unit, true | ||
} | ||
|
||
// ConvertCoin attempts to convert a coin to a given denomination. If the given | ||
// denomination is invalid or if neither denomination is registered, an error | ||
// is returned. | ||
func ConvertCoin(coin Coin, denom string) (Coin, error) { | ||
if err := validateDenom(denom); err != nil { | ||
return Coin{}, err | ||
} | ||
|
||
srcUnit, ok := GetDenomUnit(coin.Denom) | ||
if !ok { | ||
return Coin{}, fmt.Errorf("source denom not registered: %s", coin.Denom) | ||
} | ||
|
||
dstUnit, ok := GetDenomUnit(denom) | ||
if !ok { | ||
return Coin{}, fmt.Errorf("destination denom not registered: %s", denom) | ||
} | ||
|
||
if srcUnit.Equal(dstUnit) { | ||
return NewCoin(denom, coin.Amount), nil | ||
} | ||
|
||
return NewCoin(denom, coin.Amount.ToDec().Mul(srcUnit.Quo(dstUnit)).TruncateInt()), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package types | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
var ( | ||
atom = "atom" // 1 (base denom unit) | ||
matom = "matom" // 10^-3 (milli) | ||
uatom = "uatom" // 10^-6 (micro) | ||
natom = "natom" // 10^-9 (nano) | ||
) | ||
|
||
func TestRegisterDenom(t *testing.T) { | ||
atomUnit := OneDec() // 1 (base denom unit) | ||
|
||
require.NoError(t, RegisterDenom(atom, atomUnit)) | ||
require.Error(t, RegisterDenom(atom, atomUnit)) | ||
|
||
res, ok := GetDenomUnit(atom) | ||
require.True(t, ok) | ||
require.Equal(t, atomUnit, res) | ||
|
||
res, ok = GetDenomUnit(matom) | ||
require.False(t, ok) | ||
require.Equal(t, ZeroDec(), res) | ||
|
||
// reset registration | ||
denomUnits = map[string]Dec{} | ||
} | ||
|
||
func TestConvertCoins(t *testing.T) { | ||
atomUnit := OneDec() // 1 (base denom unit) | ||
require.NoError(t, RegisterDenom(atom, atomUnit)) | ||
|
||
matomUnit := NewDecWithPrec(1, 3) // 10^-3 (milli) | ||
require.NoError(t, RegisterDenom(matom, matomUnit)) | ||
|
||
uatomUnit := NewDecWithPrec(1, 6) // 10^-6 (micro) | ||
require.NoError(t, RegisterDenom(uatom, uatomUnit)) | ||
|
||
natomUnit := NewDecWithPrec(1, 9) // 10^-9 (nano) | ||
require.NoError(t, RegisterDenom(natom, natomUnit)) | ||
|
||
testCases := []struct { | ||
input Coin | ||
denom string | ||
result Coin | ||
expErr bool | ||
}{ | ||
{NewCoin("foo", ZeroInt()), atom, Coin{}, true}, | ||
{NewCoin(atom, ZeroInt()), "foo", Coin{}, true}, | ||
{NewCoin(atom, ZeroInt()), "FOO", Coin{}, true}, | ||
|
||
{NewCoin(atom, NewInt(5)), matom, NewCoin(matom, NewInt(5000)), false}, // atom => matom | ||
{NewCoin(atom, NewInt(5)), uatom, NewCoin(uatom, NewInt(5000000)), false}, // atom => uatom | ||
{NewCoin(atom, NewInt(5)), natom, NewCoin(natom, NewInt(5000000000)), false}, // atom => natom | ||
|
||
{NewCoin(uatom, NewInt(5000000)), matom, NewCoin(matom, NewInt(5000)), false}, // uatom => matom | ||
{NewCoin(uatom, NewInt(5000000)), natom, NewCoin(natom, NewInt(5000000000)), false}, // uatom => natom | ||
{NewCoin(uatom, NewInt(5000000)), atom, NewCoin(atom, NewInt(5)), false}, // uatom => atom | ||
|
||
{NewCoin(matom, NewInt(5000)), natom, NewCoin(natom, NewInt(5000000000)), false}, // matom => natom | ||
{NewCoin(matom, NewInt(5000)), uatom, NewCoin(uatom, NewInt(5000000)), false}, // matom => uatom | ||
} | ||
|
||
for i, tc := range testCases { | ||
res, err := ConvertCoin(tc.input, tc.denom) | ||
require.Equal( | ||
t, tc.expErr, err != nil, | ||
"unexpected error; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom, | ||
) | ||
require.Equal( | ||
t, tc.result, res, | ||
"invalid result; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom, | ||
) | ||
} | ||
|
||
// reset registration | ||
denomUnits = map[string]Dec{} | ||
} |