github.com/gagliardetto/solana-go@v1.11.0/programs/address-lookup-table/address-lookup.go (about) 1 package addresslookuptable 2 3 import ( 4 "context" 5 "fmt" 6 "math" 7 8 bin "github.com/gagliardetto/binary" 9 "github.com/gagliardetto/solana-go" 10 "github.com/gagliardetto/solana-go/rpc" 11 ) 12 13 // The serialized size of lookup table metadata. 14 const ( 15 LOOKUP_TABLE_META_SIZE = 56 16 LOOKUP_TABLE_MAX_ADDRESSES = 256 17 ) 18 19 // DecodeAddressLookupTableState decodes the given account bytes into a AddressLookupTableState. 20 func DecodeAddressLookupTableState(data []byte) (*AddressLookupTableState, error) { 21 decoder := bin.NewBinDecoder(data) 22 var state AddressLookupTableState 23 if err := state.UnmarshalWithDecoder(decoder); err != nil { 24 return nil, err 25 } 26 return &state, nil 27 } 28 29 func GetAddressLookupTable( 30 ctx context.Context, 31 rpcClient *rpc.Client, 32 address solana.PublicKey, 33 ) (*AddressLookupTableState, error) { 34 account, err := rpcClient.GetAccountInfo(ctx, address) 35 if err != nil { 36 return nil, err 37 } 38 if account == nil { 39 return nil, fmt.Errorf("account not found") 40 } 41 return DecodeAddressLookupTableState(account.GetBinary()) 42 } 43 44 func GetAddressLookupTableStateWithOpts( 45 ctx context.Context, 46 rpcClient *rpc.Client, 47 address solana.PublicKey, 48 opts *rpc.GetAccountInfoOpts, 49 ) (*AddressLookupTableState, error) { 50 account, err := rpcClient.GetAccountInfoWithOpts(ctx, address, opts) 51 if err != nil { 52 return nil, err 53 } 54 if account == nil { 55 return nil, fmt.Errorf("account not found") 56 } 57 return DecodeAddressLookupTableState(account.GetBinary()) 58 } 59 60 type AddressLookupTableState struct { 61 TypeIndex uint32 62 DeactivationSlot uint64 63 LastExtendedSlot uint64 64 LastExtendedSlotStartIndex uint8 65 Authority *solana.PublicKey 66 Addresses solana.PublicKeySlice 67 } 68 69 func (a *AddressLookupTableState) UnmarshalWithDecoder(decoder *bin.Decoder) (err error) { 70 if a.TypeIndex, err = decoder.ReadUint32(bin.LE); err != nil { 71 return fmt.Errorf("failed to decode TypeIndex: %w", err) 72 } 73 if a.DeactivationSlot, err = decoder.ReadUint64(bin.LE); err != nil { 74 return fmt.Errorf("failed to decode DeactivationSlot: %w", err) 75 } 76 if a.LastExtendedSlot, err = decoder.ReadUint64(bin.LE); err != nil { 77 return fmt.Errorf("failed to decode LastExtendedSlot: %w", err) 78 } 79 if a.LastExtendedSlotStartIndex, err = decoder.ReadUint8(); err != nil { 80 return fmt.Errorf("failed to decode LastExtendedSlotStartIndex: %w", err) 81 } 82 has, err := decoder.ReadOption() 83 if err != nil { 84 return fmt.Errorf("failed to decode Authority option: %w", err) 85 } 86 if has { 87 var auth solana.PublicKey 88 if _, err := decoder.Read(auth[:]); err != nil { 89 return fmt.Errorf("failed to decode Authority: %w", err) 90 } 91 a.Authority = &auth 92 } else { 93 err = decoder.Discard(32) 94 if err != nil { 95 return fmt.Errorf("failed to decode Authority: %w", err) 96 } 97 } 98 serializedAddressesNumBytes := decoder.Len() - LOOKUP_TABLE_META_SIZE 99 if serializedAddressesNumBytes == 0 { 100 // No addresses (the lookup table is empty). 101 a.Addresses = make(solana.PublicKeySlice, 0) 102 return nil 103 } 104 105 numSerializedAddresses := serializedAddressesNumBytes / 32 106 if serializedAddressesNumBytes%32 != 0 { 107 // cut off the remaining bytes 108 // skip the difference 109 if err := decoder.Discard(serializedAddressesNumBytes % 32); err != nil { 110 return fmt.Errorf("failed to discard remaining bytes: %w", err) 111 } 112 // return fmt.Errorf("lookup table is invalid; serialized addresses are not a multiple of 32 bytes, with %d bytes remaining", serializedAddressesNumBytes%32) 113 } 114 if numSerializedAddresses > LOOKUP_TABLE_MAX_ADDRESSES { 115 return fmt.Errorf("lookup table is invalid: max addresses exceeded (%d > %d)", numSerializedAddresses, LOOKUP_TABLE_MAX_ADDRESSES) 116 } 117 118 // Set the position to the start of the serialized addresses. 119 if err := decoder.SetPosition(LOOKUP_TABLE_META_SIZE); err != nil { 120 return fmt.Errorf("failed to set position: %w", err) 121 } 122 a.Addresses = make(solana.PublicKeySlice, numSerializedAddresses) 123 124 for i := 0; i < numSerializedAddresses; i++ { 125 var address solana.PublicKey 126 numRead, err := decoder.Read(address[:]) 127 if err != nil { 128 return fmt.Errorf("failed to read addresses[%d]: %w", i, err) 129 } 130 if numRead != 32 { 131 return fmt.Errorf("failed to read addresses[%d]: expected to read 32, but read %d", i, numRead) 132 } 133 a.Addresses[i] = address 134 } 135 if decoder.Remaining() != 0 { 136 // return fmt.Errorf("failed to read all addresses: remaining %d bytes", decoder.Remaining()) 137 } 138 return nil 139 } 140 141 func (a AddressLookupTableState) MarshalWithEncoder(encoder *bin.Encoder) error { 142 if err := encoder.WriteUint32(a.TypeIndex, bin.LE); err != nil { 143 return err 144 } 145 if err := encoder.WriteUint64(a.DeactivationSlot, bin.LE); err != nil { 146 return err 147 } 148 if err := encoder.WriteUint64(a.LastExtendedSlot, bin.LE); err != nil { 149 return err 150 } 151 if err := encoder.WriteUint8(a.LastExtendedSlotStartIndex); err != nil { 152 return err 153 } 154 if a.Authority != nil { 155 if err := encoder.WriteOption(true); err != nil { 156 return err 157 } 158 if _, err := encoder.Write(a.Authority[:]); err != nil { 159 return err 160 } 161 } else { 162 if err := encoder.WriteOption(false); err != nil { 163 return err 164 } 165 if _, err := encoder.Write(make([]byte, 32)); err != nil { 166 return err 167 } 168 } 169 if _, err := encoder.Write(make([]byte, 2)); err != nil { 170 return err 171 } 172 for _, address := range a.Addresses { 173 if _, err := encoder.Write(address[:]); err != nil { 174 return err 175 } 176 } 177 return nil 178 } 179 180 func (a AddressLookupTableState) IsActive() bool { 181 return a.DeactivationSlot == math.MaxUint64 182 } 183 184 type KeyedAddressLookupTable struct { 185 Key solana.PublicKey 186 State AddressLookupTableState 187 } 188 189 func NewKeyedAddressLookupTable(key solana.PublicKey) *KeyedAddressLookupTable { 190 return &KeyedAddressLookupTable{ 191 Key: key, 192 } 193 } 194 195 func (a *KeyedAddressLookupTable) UnmarshalWithDecoder(decoder *bin.Decoder) error { 196 if err := a.State.UnmarshalWithDecoder(decoder); err != nil { 197 return err 198 } 199 return nil 200 } 201 202 func (a KeyedAddressLookupTable) MarshalWithEncoder(encoder *bin.Encoder) error { 203 if err := a.State.MarshalWithEncoder(encoder); err != nil { 204 return err 205 } 206 return nil 207 }