token program: Fix token account parsing

This commit is contained in:
gagliardetto 2022-02-15 14:01:16 +01:00
parent ca5e469dba
commit ffe95d7199
6 changed files with 311 additions and 28 deletions

View File

@ -2266,7 +2266,9 @@ import (
"context"
"github.com/davecgh/go-spew/spew"
bin "github.com/gagliardetto/binary"
"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/programs/token"
"github.com/gagliardetto/solana-go/rpc"
)
@ -2279,15 +2281,34 @@ func main() {
context.TODO(),
pubKey,
&rpc.GetTokenAccountsConfig{
Mint: solana.MustPublicKeyFromBase58("So11111111111111111111111111111111111111112"),
Mint: solana.WrappedSol.ToPointer(),
},
&rpc.GetTokenAccountsOpts{
Encoding: solana.EncodingBase64Zstd,
},
nil,
)
if err != nil {
panic(err)
}
spew.Dump(out)
{
tokenAccounts := make([]token.Account, 0)
for _, rawAccount := range out.Value {
var tokAcc token.Account
data := rawAccount.Account.Data.GetBinary()
dec := bin.NewBinDecoder(data)
err := dec.Decode(&tokAcc)
if err != nil {
panic(err)
}
tokenAccounts = append(tokenAccounts, tokAcc)
}
spew.Dump(tokenAccounts)
}
}
```
#### [index](#contents) > [RPC](#rpc-methods) > GetTokenLargestAccounts

View File

@ -19,14 +19,13 @@ import (
bin "github.com/gagliardetto/binary"
"github.com/gagliardetto/solana-go"
ag_solanago "github.com/gagliardetto/solana-go"
)
type Mint struct {
// Optional authority used to mint new tokens. The mint authority may only be provided during
// mint creation. If no mint authority is present then the mint has a fixed supply and no
// further tokens may be minted.
MintAuthority *ag_solanago.PublicKey `bin:"optional"`
MintAuthority *solana.PublicKey `bin:"optional"`
// Total supply of tokens.
Supply uint64
@ -38,7 +37,7 @@ type Mint struct {
IsInitialized bool
// Optional authority to freeze token accounts.
FreezeAuthority *ag_solanago.PublicKey `bin:"optional"`
FreezeAuthority *solana.PublicKey `bin:"optional"`
}
func (mint *Mint) UnmarshalWithDecoder(dec *bin.Decoder) (err error) {
@ -53,6 +52,9 @@ func (mint *Mint) UnmarshalWithDecoder(dec *bin.Decoder) (err error) {
return err
}
mint.MintAuthority = solana.PublicKeyFromBytes(v).ToPointer()
} else {
// discard:
dec.ReadNBytes(32)
}
}
{
@ -87,6 +89,9 @@ func (mint *Mint) UnmarshalWithDecoder(dec *bin.Decoder) (err error) {
return err
}
mint.FreezeAuthority = solana.PublicKeyFromBytes(v).ToPointer()
} else {
// discard:
dec.ReadNBytes(32)
}
}
return nil
@ -99,6 +104,11 @@ func (mint Mint) MarshalWithEncoder(encoder *bin.Encoder) (err error) {
if err != nil {
return err
}
empty := solana.PublicKey{}
err = encoder.WriteBytes(empty[:], false)
if err != nil {
return err
}
} else {
err = encoder.WriteUint32(1, binary.LittleEndian)
if err != nil {
@ -128,6 +138,11 @@ func (mint Mint) MarshalWithEncoder(encoder *bin.Encoder) (err error) {
if err != nil {
return err
}
empty := solana.PublicKey{}
err = encoder.WriteBytes(empty[:], false)
if err != nil {
return err
}
} else {
err = encoder.WriteUint32(1, binary.LittleEndian)
if err != nil {
@ -144,17 +159,17 @@ func (mint Mint) MarshalWithEncoder(encoder *bin.Encoder) (err error) {
type Account struct {
// The mint associated with this account
Mint ag_solanago.PublicKey
Mint solana.PublicKey
// The owner of this account.
Owner ag_solanago.PublicKey
Owner solana.PublicKey
// The amount of tokens this account holds.
Amount uint64
// If `delegate` is `Some` then `delegated_amount` represents
// the amount authorized by the delegate
Delegate *ag_solanago.PublicKey `bin:"optional"`
Delegate *solana.PublicKey `bin:"optional"`
// The account's state
State AccountState
@ -168,5 +183,200 @@ type Account struct {
DelegatedAmount uint64
// Optional authority to close the account.
CloseAuthority *ag_solanago.PublicKey `bin:"optional"`
CloseAuthority *solana.PublicKey `bin:"optional"`
}
func (mint *Account) UnmarshalWithDecoder(dec *bin.Decoder) (err error) {
{
v, err := dec.ReadNBytes(32)
if err != nil {
return err
}
mint.Mint = solana.PublicKeyFromBytes(v)
}
{
v, err := dec.ReadNBytes(32)
if err != nil {
return err
}
mint.Owner = solana.PublicKeyFromBytes(v)
}
{
v, err := dec.ReadUint64(binary.LittleEndian)
if err != nil {
return err
}
mint.Amount = v
}
{
v, err := dec.ReadUint32(binary.LittleEndian)
if err != nil {
return err
}
if v == 1 {
v, err := dec.ReadNBytes(32)
if err != nil {
return err
}
mint.Delegate = solana.PublicKeyFromBytes(v).ToPointer()
} else {
// discard:
dec.ReadNBytes(32)
}
}
{
v, err := dec.ReadUint8()
if err != nil {
return err
}
mint.State = AccountState(v)
}
{
v, err := dec.ReadUint32(binary.LittleEndian)
if err != nil {
return err
}
if v == 1 {
v, err := dec.ReadUint64(bin.LE)
if err != nil {
return err
}
mint.IsNative = &v
} else {
// discard:
dec.ReadUint64(bin.LE)
}
}
{
v, err := dec.ReadUint64(binary.LittleEndian)
if err != nil {
return err
}
mint.DelegatedAmount = v
}
{
v, err := dec.ReadUint32(binary.LittleEndian)
if err != nil {
return err
}
if v == 1 {
v, err := dec.ReadNBytes(32)
if err != nil {
return err
}
mint.CloseAuthority = solana.PublicKeyFromBytes(v).ToPointer()
} else {
// discard:
dec.ReadNBytes(32)
}
}
return nil
}
func (mint Account) MarshalWithEncoder(encoder *bin.Encoder) (err error) {
{
err = encoder.WriteBytes(mint.Mint[:], false)
if err != nil {
return err
}
}
{
err = encoder.WriteBytes(mint.Owner[:], false)
if err != nil {
return err
}
}
{
err = encoder.WriteUint64(mint.Amount, bin.LE)
if err != nil {
return err
}
}
{
if mint.Delegate == nil {
err = encoder.WriteUint32(0, binary.LittleEndian)
if err != nil {
return err
}
empty := solana.PublicKey{}
err = encoder.WriteBytes(empty[:], false)
if err != nil {
return err
}
} else {
err = encoder.WriteUint32(1, binary.LittleEndian)
if err != nil {
return err
}
err = encoder.WriteBytes(mint.Delegate[:], false)
if err != nil {
return err
}
}
}
err = encoder.WriteUint8(uint8(mint.State))
if err != nil {
return err
}
{
if mint.IsNative == nil {
err = encoder.WriteUint32(0, binary.LittleEndian)
if err != nil {
return err
}
err = encoder.WriteUint64(0, bin.LE)
if err != nil {
return err
}
} else {
err = encoder.WriteUint32(1, binary.LittleEndian)
if err != nil {
return err
}
err = encoder.WriteUint64(*mint.IsNative, bin.LE)
if err != nil {
return err
}
}
}
{
err = encoder.WriteUint64(mint.DelegatedAmount, bin.LE)
if err != nil {
return err
}
}
{
if mint.CloseAuthority == nil {
err = encoder.WriteUint32(0, binary.LittleEndian)
if err != nil {
return err
}
empty := solana.PublicKey{}
err = encoder.WriteBytes(empty[:], false)
if err != nil {
return err
}
} else {
err = encoder.WriteUint32(1, binary.LittleEndian)
if err != nil {
return err
}
err = encoder.WriteBytes(mint.CloseAuthority[:], false)
if err != nil {
return err
}
}
}
return nil
}
type Multisig struct {
// Number of signers required
M uint8
// Number of valid signers
N uint8
// Is `true` if this structure has been initialized
IsInitialized bool
// Signer public keys
Signers [MAX_SIGNERS]solana.PublicKey
}

View File

@ -11,7 +11,7 @@ import (
)
func TestMint(t *testing.T) {
account := []byte{
accountBytes := []byte{
1, 0, 0, 0,
5, 234, 156, 241, 108, 228, 17, 152, 241, 164, 153, 55, 200, 140, 55, 10, 148, 212, 175, 255, 137, 181, 186, 203, 142, 244, 94, 99, 36, 187, 120, 247,
9, 169, 49, 235, 241, 182, 6, 0,
@ -21,7 +21,7 @@ func TestMint(t *testing.T) {
5, 234, 156, 241, 108, 228, 17, 152, 241, 164, 153, 55, 200, 140, 55, 10, 148, 212, 175, 255, 137, 181, 186, 203, 142, 244, 94, 99, 36, 187, 120, 247,
}
{
dec := bin.NewBinDecoder(account)
dec := bin.NewBinDecoder(accountBytes)
mint := Mint{}
err := dec.Decode(&mint)
@ -42,7 +42,55 @@ func TestMint(t *testing.T) {
buf := new(bytes.Buffer)
err := bin.NewBinEncoder(buf).Encode(mint)
require.NoError(t, err)
require.Equal(t, account, buf.Bytes(), bin.FormatByteSlice(buf.Bytes()))
require.Equal(t, accountBytes, buf.Bytes(), bin.FormatByteSlice(buf.Bytes()))
}
}
}
func TestAccount(t *testing.T) {
accountBytes := []byte{
6, 155, 136, 87, 254, 171, 129, 132, 251, 104, 127, 99, 70, 24, 192, 53, 218, 196, 57, 220, 26, 235, 59, 85, 152, 160, 240, 0, 0, 0, 0, 1,
93, 100, 62, 133, 31, 102, 235, 161, 170, 152, 161, 7, 39, 223, 9, 180, 1, 224, 134, 204, 54, 241, 9, 195, 240, 147, 219, 146, 35, 92, 26, 224,
42, 34, 176, 1, 0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1,
1, 0, 0, 0,
240, 29, 31, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
}
{
dec := bin.NewBinDecoder(accountBytes)
account := Account{}
err := dec.Decode(&account)
require.NoError(t, err, spew.Sdump(account))
balance := uint64(2039280)
require.Equal(t,
&Account{
Mint: solana.MustPublicKeyFromBase58("So11111111111111111111111111111111111111112"),
Owner: solana.MustPublicKeyFromBase58("7HZaCWazgTuuFuajxaaxGYbGnyVKwxvsJKue1W4Nvyro"),
Amount: (uint64)(28320298),
Delegate: (*solana.PublicKey)(nil),
State: (AccountState)(1),
IsNative: (*uint64)(&balance),
DelegatedAmount: (uint64)(0),
CloseAuthority: (*solana.PublicKey)(nil),
},
&account,
)
{
buf := bin.NewWriteByWrite("")
err := bin.NewBinEncoder(buf).Encode(account)
require.NoError(t, err)
require.Equal(t, accountBytes, buf.Bytes(), bin.FormatByteSlice(buf.Bytes()))
}
}
}

View File

@ -28,6 +28,7 @@ import (
ag_treeout "github.com/gagliardetto/treeout"
)
// Maximum number of multisignature signers (max N)
const MAX_SIGNERS = 11
var ProgramID ag_solanago.PublicKey = ag_solanago.TokenProgramID

View File

@ -16,7 +16,6 @@ package token
import (
ag_binary "github.com/gagliardetto/binary"
ag_solanago "github.com/gagliardetto/solana-go"
)
type AuthorityType ag_binary.BorshEnum
@ -49,17 +48,3 @@ const (
// the delegate are able to perform operations on this account.
Frozen
)
type Multisig struct {
// Number of signers required
M uint8
// Number of valid signers
N uint8
// Is `true` if this structure has been initialized
IsInitialized bool
// Signer public keys
Signers [11]ag_solanago.PublicKey
}

View File

@ -18,7 +18,9 @@ import (
"context"
"github.com/davecgh/go-spew/spew"
bin "github.com/gagliardetto/binary"
"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/programs/token"
"github.com/gagliardetto/solana-go/rpc"
)
@ -31,7 +33,7 @@ func main() {
context.TODO(),
pubKey,
&rpc.GetTokenAccountsConfig{
Mint: solana.MustPublicKeyFromBase58("So11111111111111111111111111111111111111112").ToPointer(),
Mint: solana.WrappedSol.ToPointer(),
},
&rpc.GetTokenAccountsOpts{
Encoding: solana.EncodingBase64Zstd,
@ -41,4 +43,20 @@ func main() {
panic(err)
}
spew.Dump(out)
{
tokenAccounts := make([]token.Account, 0)
for _, rawAccount := range out.Value {
var tokAcc token.Account
data := rawAccount.Account.Data.GetBinary()
dec := bin.NewBinDecoder(data)
err := dec.Decode(&tokAcc)
if err != nil {
panic(err)
}
tokenAccounts = append(tokenAccounts, tokAcc)
}
spew.Dump(tokenAccounts)
}
}