diff --git a/pkg/wallet/token.go b/pkg/wallet/token.go new file mode 100644 index 000000000..2c68f467c --- /dev/null +++ b/pkg/wallet/token.go @@ -0,0 +1,60 @@ +package wallet + +import ( + "encoding/json" + + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/util" +) + +// Token represents imported token contract. +type Token struct { + Name string + Hash util.Uint160 + Decimals int64 + Symbol string + Address string +} + +type tokenAux struct { + Name string `json:"name"` + Hash util.Uint160 `json:"script_hash"` + Decimals int64 `json:"decimals"` + Symbol string `json:"symbol"` +} + +// NewToken returns new token contract info. +func NewToken(tokenHash util.Uint160, name, symbol string, decimals int64) *Token { + return &Token{ + Name: name, + Hash: tokenHash, + Decimals: decimals, + Symbol: symbol, + Address: address.Uint160ToString(tokenHash), + } +} + +// MarshalJSON implements json.Marshaler interface. +func (t *Token) MarshalJSON() ([]byte, error) { + m := &tokenAux{ + Name: t.Name, + Hash: t.Hash.Reverse(), // address should be marshaled in LE but default marshaler uses BE. + Decimals: t.Decimals, + Symbol: t.Symbol, + } + return json.Marshal(m) +} + +// UnmarshalJSON implements json.Unmarshaler interface. +func (t *Token) UnmarshalJSON(data []byte) error { + aux := new(tokenAux) + if err := json.Unmarshal(data, aux); err != nil { + return err + } + t.Name = aux.Name + t.Hash = aux.Hash.Reverse() + t.Decimals = aux.Decimals + t.Symbol = aux.Symbol + t.Address = address.Uint160ToString(t.Hash) + return nil +} diff --git a/pkg/wallet/token_test.go b/pkg/wallet/token_test.go new file mode 100644 index 000000000..a44d30d18 --- /dev/null +++ b/pkg/wallet/token_test.go @@ -0,0 +1,29 @@ +package wallet + +import ( + "encoding/json" + "testing" + + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/stretchr/testify/require" +) + +func TestToken_MarshalJSON(t *testing.T) { + // From the https://neo-python.readthedocs.io/en/latest/prompt.html#import-nep5-compliant-token + h, err := util.Uint160DecodeStringLE("f8d448b227991cf07cb96a6f9c0322437f1599b9") + require.NoError(t, err) + + tok := NewToken(h, "NEP5 Standard", "NEP5", 8) + require.Equal(t, "NEP5 Standard", tok.Name) + require.Equal(t, "NEP5", tok.Symbol) + require.EqualValues(t, 8, tok.Decimals) + require.Equal(t, h, tok.Hash) + require.Equal(t, "AYhE3Svuqdfh1RtzvE8hUhNR7HSpaSDFQg", tok.Address) + + data, err := json.Marshal(tok) + require.NoError(t, err) + + actual := new(Token) + require.NoError(t, json.Unmarshal(data, actual)) + require.Equal(t, tok, actual) +} diff --git a/pkg/wallet/wallet.go b/pkg/wallet/wallet.go index e01c22eba..447df7bff 100644 --- a/pkg/wallet/wallet.go +++ b/pkg/wallet/wallet.go @@ -28,7 +28,7 @@ type Wallet struct { // Extra metadata can be used for storing arbitrary data. // This field can be empty. - Extra interface{} `json:"extra"` + Extra Extra `json:"extra"` // Path where the wallet file is located.. path string @@ -37,6 +37,12 @@ type Wallet struct { rw io.ReadWriter } +// Extra stores imported token contracts. +type Extra struct { + // Tokens is a list of imported token contracts. + Tokens []*Token +} + // NewWallet creates a new NEO wallet at the given location. func NewWallet(location string) (*Wallet, error) { file, err := os.Create(location) @@ -96,6 +102,11 @@ func (w *Wallet) AddAccount(acc *Account) { w.Accounts = append(w.Accounts, acc) } +// AddToken adds new token to a wallet. +func (w *Wallet) AddToken(tok *Token) { + w.Extra.Tokens = append(w.Extra.Tokens, tok) +} + // Path returns the location of the wallet on the filesystem. func (w *Wallet) Path() string { return w.path