@solana/web3.js 1.56.2 → 1.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solana/web3.js",
3
- "version": "1.56.2",
3
+ "version": "1.59.0",
4
4
  "description": "Solana Javascript API",
5
5
  "keywords": [
6
6
  "api",
@@ -69,7 +69,6 @@
69
69
  "buffer": "6.0.1",
70
70
  "fast-stable-stringify": "^1.0.0",
71
71
  "jayson": "^3.4.4",
72
- "js-sha3": "^0.8.0",
73
72
  "node-fetch": "2",
74
73
  "rpc-websockets": "^7.5.0",
75
74
  "superstruct": "^0.14.2"
package/src/connection.ts CHANGED
@@ -36,6 +36,7 @@ import {
36
36
  Transaction,
37
37
  TransactionStatus,
38
38
  TransactionVersion,
39
+ VersionedTransaction,
39
40
  } from './transaction';
40
41
  import {Message, MessageHeader, MessageV0, VersionedMessage} from './message';
41
42
  import {AddressLookupTableAccount} from './programs/address-lookup-table/state';
@@ -801,6 +802,22 @@ export type TransactionReturnData = {
801
802
  data: [string, TransactionReturnDataEncoding];
802
803
  };
803
804
 
805
+ export type SimulateTransactionConfig = {
806
+ /** Optional parameter used to enable signature verification before simulation */
807
+ sigVerify?: boolean;
808
+ /** Optional parameter used to replace the simulated transaction's recent blockhash with the latest blockhash */
809
+ replaceRecentBlockhash?: boolean;
810
+ /** Optional parameter used to set the commitment level when selecting the latest block */
811
+ commitment?: Commitment;
812
+ /** Optional parameter used to specify a list of account addresses to return post simulation state for */
813
+ accounts?: {
814
+ encoding: 'base64';
815
+ addresses: string[];
816
+ };
817
+ /** Optional parameter used to specify the minimum block slot that can be used for simulation */
818
+ minContextSlot?: number;
819
+ };
820
+
804
821
  export type SimulatedTransactionResponse = {
805
822
  err: TransactionError | string | null;
806
823
  logs: Array<string> | null;
@@ -4625,12 +4642,58 @@ export class Connection {
4625
4642
 
4626
4643
  /**
4627
4644
  * Simulate a transaction
4645
+ *
4646
+ * @deprecated Instead, call {@link simulateTransaction} with {@link
4647
+ * VersionedTransaction} and {@link SimulateTransactionConfig} parameters
4628
4648
  */
4629
- async simulateTransaction(
4649
+ simulateTransaction(
4630
4650
  transactionOrMessage: Transaction | Message,
4631
4651
  signers?: Array<Signer>,
4632
4652
  includeAccounts?: boolean | Array<PublicKey>,
4653
+ ): Promise<RpcResponseAndContext<SimulatedTransactionResponse>>;
4654
+
4655
+ /**
4656
+ * Simulate a transaction
4657
+ */
4658
+ // eslint-disable-next-line no-dupe-class-members
4659
+ simulateTransaction(
4660
+ transaction: VersionedTransaction,
4661
+ config?: SimulateTransactionConfig,
4662
+ ): Promise<RpcResponseAndContext<SimulatedTransactionResponse>>;
4663
+
4664
+ /**
4665
+ * Simulate a transaction
4666
+ */
4667
+ // eslint-disable-next-line no-dupe-class-members
4668
+ async simulateTransaction(
4669
+ transactionOrMessage: VersionedTransaction | Transaction | Message,
4670
+ configOrSigners?: SimulateTransactionConfig | Array<Signer>,
4671
+ includeAccounts?: boolean | Array<PublicKey>,
4633
4672
  ): Promise<RpcResponseAndContext<SimulatedTransactionResponse>> {
4673
+ if ('message' in transactionOrMessage) {
4674
+ const versionedTx = transactionOrMessage;
4675
+ const wireTransaction = versionedTx.serialize();
4676
+ const encodedTransaction =
4677
+ Buffer.from(wireTransaction).toString('base64');
4678
+ if (Array.isArray(configOrSigners) || includeAccounts !== undefined) {
4679
+ throw new Error('Invalid arguments');
4680
+ }
4681
+
4682
+ const config: any = configOrSigners || {};
4683
+ config.encoding = 'base64';
4684
+ if (!('commitment' in config)) {
4685
+ config.commitment = this.commitment;
4686
+ }
4687
+
4688
+ const args = [encodedTransaction, config];
4689
+ const unsafeRes = await this._rpcRequest('simulateTransaction', args);
4690
+ const res = create(unsafeRes, SimulatedTransactionResponseStruct);
4691
+ if ('error' in res) {
4692
+ throw new Error('failed to simulate transaction: ' + res.error.message);
4693
+ }
4694
+ return res.result;
4695
+ }
4696
+
4634
4697
  let transaction;
4635
4698
  if (transactionOrMessage instanceof Transaction) {
4636
4699
  let originalTx: Transaction = transactionOrMessage;
@@ -4645,6 +4708,11 @@ export class Connection {
4645
4708
  transaction._message = transaction._json = undefined;
4646
4709
  }
4647
4710
 
4711
+ if (configOrSigners !== undefined && !Array.isArray(configOrSigners)) {
4712
+ throw new Error('Invalid arguments');
4713
+ }
4714
+
4715
+ const signers = configOrSigners;
4648
4716
  if (transaction.nonceInfo && signers) {
4649
4717
  transaction.sign(...signers);
4650
4718
  } else {
@@ -4731,12 +4799,48 @@ export class Connection {
4731
4799
 
4732
4800
  /**
4733
4801
  * Sign and send a transaction
4802
+ *
4803
+ * @deprecated Instead, call {@link sendTransaction} with a {@link
4804
+ * VersionedTransaction}
4734
4805
  */
4735
- async sendTransaction(
4806
+ sendTransaction(
4736
4807
  transaction: Transaction,
4737
4808
  signers: Array<Signer>,
4738
4809
  options?: SendOptions,
4810
+ ): Promise<TransactionSignature>;
4811
+
4812
+ /**
4813
+ * Send a signed transaction
4814
+ */
4815
+ // eslint-disable-next-line no-dupe-class-members
4816
+ sendTransaction(
4817
+ transaction: VersionedTransaction,
4818
+ options?: SendOptions,
4819
+ ): Promise<TransactionSignature>;
4820
+
4821
+ /**
4822
+ * Sign and send a transaction
4823
+ */
4824
+ // eslint-disable-next-line no-dupe-class-members
4825
+ async sendTransaction(
4826
+ transaction: VersionedTransaction | Transaction,
4827
+ signersOrOptions?: Array<Signer> | SendOptions,
4828
+ options?: SendOptions,
4739
4829
  ): Promise<TransactionSignature> {
4830
+ if ('message' in transaction) {
4831
+ if (signersOrOptions && Array.isArray(signersOrOptions)) {
4832
+ throw new Error('Invalid arguments');
4833
+ }
4834
+
4835
+ const wireTransaction = transaction.serialize();
4836
+ return await this.sendRawTransaction(wireTransaction, options);
4837
+ }
4838
+
4839
+ if (signersOrOptions === undefined || !Array.isArray(signersOrOptions)) {
4840
+ throw new Error('Invalid arguments');
4841
+ }
4842
+
4843
+ const signers = signersOrOptions;
4740
4844
  if (transaction.nonceInfo) {
4741
4845
  transaction.sign(...signers);
4742
4846
  } else {
package/src/layout.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import {Buffer} from 'buffer';
2
2
  import * as BufferLayout from '@solana/buffer-layout';
3
3
 
4
+ import {VoteAuthorizeWithSeedArgs} from './programs/vote';
5
+
4
6
  /**
5
7
  * Layout for a public key
6
8
  */
@@ -141,6 +143,23 @@ export const voteInit = (property: string = 'voteInit') => {
141
143
  );
142
144
  };
143
145
 
146
+ /**
147
+ * Layout for a VoteAuthorizeWithSeedArgs object
148
+ */
149
+ export const voteAuthorizeWithSeedArgs = (
150
+ property: string = 'voteAuthorizeWithSeedArgs',
151
+ ) => {
152
+ return BufferLayout.struct<VoteAuthorizeWithSeedArgs>(
153
+ [
154
+ BufferLayout.u32('voteAuthorizationType'),
155
+ publicKey('currentAuthorityDerivedKeyOwnerPubkey'),
156
+ rustString('currentAuthorityDerivedKeySeed'),
157
+ publicKey('newAuthorized'),
158
+ ],
159
+ property,
160
+ );
161
+ };
162
+
144
163
  export function getAlloc(type: any, fields: any): number {
145
164
  const getItemAlloc = (item: any): number => {
146
165
  if (item.span >= 0) {
@@ -152,6 +171,9 @@ export function getAlloc(type: any, fields: any): number {
152
171
  if (Array.isArray(field)) {
153
172
  return field.length * getItemAlloc(item.elementLayout);
154
173
  }
174
+ } else if ('fields' in item) {
175
+ // This is a `Structure` whose size needs to be recursively measured.
176
+ return getAlloc({layout: item}, fields[item.property]);
155
177
  }
156
178
  // Couldn't determine allocated size of layout
157
179
  return 0;
@@ -0,0 +1,79 @@
1
+ import {LoadedAddresses} from '../connection';
2
+ import {PublicKey} from '../publickey';
3
+ import {TransactionInstruction} from '../transaction';
4
+ import {MessageCompiledInstruction} from './index';
5
+
6
+ export type AccountKeysFromLookups = LoadedAddresses;
7
+
8
+ export class MessageAccountKeys {
9
+ staticAccountKeys: Array<PublicKey>;
10
+ accountKeysFromLookups?: AccountKeysFromLookups;
11
+
12
+ constructor(
13
+ staticAccountKeys: Array<PublicKey>,
14
+ accountKeysFromLookups?: AccountKeysFromLookups,
15
+ ) {
16
+ this.staticAccountKeys = staticAccountKeys;
17
+ this.accountKeysFromLookups = accountKeysFromLookups;
18
+ }
19
+
20
+ keySegments(): Array<Array<PublicKey>> {
21
+ const keySegments = [this.staticAccountKeys];
22
+ if (this.accountKeysFromLookups) {
23
+ keySegments.push(this.accountKeysFromLookups.writable);
24
+ keySegments.push(this.accountKeysFromLookups.readonly);
25
+ }
26
+ return keySegments;
27
+ }
28
+
29
+ get(index: number): PublicKey | undefined {
30
+ for (const keySegment of this.keySegments()) {
31
+ if (index < keySegment.length) {
32
+ return keySegment[index];
33
+ } else {
34
+ index -= keySegment.length;
35
+ }
36
+ }
37
+ return;
38
+ }
39
+
40
+ get length(): number {
41
+ return this.keySegments().flat().length;
42
+ }
43
+
44
+ compileInstructions(
45
+ instructions: Array<TransactionInstruction>,
46
+ ): Array<MessageCompiledInstruction> {
47
+ // Bail early if any account indexes would overflow a u8
48
+ const U8_MAX = 255;
49
+ if (this.length > U8_MAX + 1) {
50
+ throw new Error('Account index overflow encountered during compilation');
51
+ }
52
+
53
+ const keyIndexMap = new Map();
54
+ this.keySegments()
55
+ .flat()
56
+ .forEach((key, index) => {
57
+ keyIndexMap.set(key.toBase58(), index);
58
+ });
59
+
60
+ const findKeyIndex = (key: PublicKey) => {
61
+ const keyIndex = keyIndexMap.get(key.toBase58());
62
+ if (keyIndex === undefined)
63
+ throw new Error(
64
+ 'Encountered an unknown instruction account key during compilation',
65
+ );
66
+ return keyIndex;
67
+ };
68
+
69
+ return instructions.map((instruction): MessageCompiledInstruction => {
70
+ return {
71
+ programIdIndex: findKeyIndex(instruction.programId),
72
+ accountKeyIndexes: instruction.keys.map(meta =>
73
+ findKeyIndex(meta.pubkey),
74
+ ),
75
+ data: instruction.data,
76
+ };
77
+ });
78
+ }
79
+ }
@@ -0,0 +1,165 @@
1
+ import {MessageHeader, MessageAddressTableLookup} from './index';
2
+ import {AccountKeysFromLookups} from './account-keys';
3
+ import {AddressLookupTableAccount} from '../programs';
4
+ import {TransactionInstruction} from '../transaction';
5
+ import assert from '../utils/assert';
6
+ import {PublicKey} from '../publickey';
7
+
8
+ export type CompiledKeyMeta = {
9
+ isSigner: boolean;
10
+ isWritable: boolean;
11
+ isInvoked: boolean;
12
+ };
13
+
14
+ type KeyMetaMap = Map<string, CompiledKeyMeta>;
15
+
16
+ export class CompiledKeys {
17
+ payer: PublicKey;
18
+ keyMetaMap: KeyMetaMap;
19
+
20
+ constructor(payer: PublicKey, keyMetaMap: KeyMetaMap) {
21
+ this.payer = payer;
22
+ this.keyMetaMap = keyMetaMap;
23
+ }
24
+
25
+ static compile(
26
+ instructions: Array<TransactionInstruction>,
27
+ payer: PublicKey,
28
+ ): CompiledKeys {
29
+ const keyMetaMap: KeyMetaMap = new Map();
30
+ const getOrInsertDefault = (pubkey: PublicKey): CompiledKeyMeta => {
31
+ const address = pubkey.toBase58();
32
+ let keyMeta = keyMetaMap.get(address);
33
+ if (keyMeta === undefined) {
34
+ keyMeta = {
35
+ isSigner: false,
36
+ isWritable: false,
37
+ isInvoked: false,
38
+ };
39
+ keyMetaMap.set(address, keyMeta);
40
+ }
41
+ return keyMeta;
42
+ };
43
+
44
+ const payerKeyMeta = getOrInsertDefault(payer);
45
+ payerKeyMeta.isSigner = true;
46
+ payerKeyMeta.isWritable = true;
47
+
48
+ for (const ix of instructions) {
49
+ getOrInsertDefault(ix.programId).isInvoked = true;
50
+ for (const accountMeta of ix.keys) {
51
+ const keyMeta = getOrInsertDefault(accountMeta.pubkey);
52
+ keyMeta.isSigner ||= accountMeta.isSigner;
53
+ keyMeta.isWritable ||= accountMeta.isWritable;
54
+ }
55
+ }
56
+
57
+ return new CompiledKeys(payer, keyMetaMap);
58
+ }
59
+
60
+ getMessageComponents(): [MessageHeader, Array<PublicKey>] {
61
+ const mapEntries = [...this.keyMetaMap.entries()];
62
+ assert(mapEntries.length <= 256, 'Max static account keys length exceeded');
63
+
64
+ const writableSigners = mapEntries.filter(
65
+ ([, meta]) => meta.isSigner && meta.isWritable,
66
+ );
67
+ const readonlySigners = mapEntries.filter(
68
+ ([, meta]) => meta.isSigner && !meta.isWritable,
69
+ );
70
+ const writableNonSigners = mapEntries.filter(
71
+ ([, meta]) => !meta.isSigner && meta.isWritable,
72
+ );
73
+ const readonlyNonSigners = mapEntries.filter(
74
+ ([, meta]) => !meta.isSigner && !meta.isWritable,
75
+ );
76
+
77
+ const header: MessageHeader = {
78
+ numRequiredSignatures: writableSigners.length + readonlySigners.length,
79
+ numReadonlySignedAccounts: readonlySigners.length,
80
+ numReadonlyUnsignedAccounts: readonlyNonSigners.length,
81
+ };
82
+
83
+ // sanity checks
84
+ {
85
+ assert(
86
+ writableSigners.length > 0,
87
+ 'Expected at least one writable signer key',
88
+ );
89
+ const [payerAddress] = writableSigners[0];
90
+ assert(
91
+ payerAddress === this.payer.toBase58(),
92
+ 'Expected first writable signer key to be the fee payer',
93
+ );
94
+ }
95
+
96
+ const staticAccountKeys = [
97
+ ...writableSigners.map(([address]) => new PublicKey(address)),
98
+ ...readonlySigners.map(([address]) => new PublicKey(address)),
99
+ ...writableNonSigners.map(([address]) => new PublicKey(address)),
100
+ ...readonlyNonSigners.map(([address]) => new PublicKey(address)),
101
+ ];
102
+
103
+ return [header, staticAccountKeys];
104
+ }
105
+
106
+ extractTableLookup(
107
+ lookupTable: AddressLookupTableAccount,
108
+ ): [MessageAddressTableLookup, AccountKeysFromLookups] | undefined {
109
+ const [writableIndexes, drainedWritableKeys] =
110
+ this.drainKeysFoundInLookupTable(
111
+ lookupTable.state.addresses,
112
+ keyMeta =>
113
+ !keyMeta.isSigner && !keyMeta.isInvoked && keyMeta.isWritable,
114
+ );
115
+ const [readonlyIndexes, drainedReadonlyKeys] =
116
+ this.drainKeysFoundInLookupTable(
117
+ lookupTable.state.addresses,
118
+ keyMeta =>
119
+ !keyMeta.isSigner && !keyMeta.isInvoked && !keyMeta.isWritable,
120
+ );
121
+
122
+ // Don't extract lookup if no keys were found
123
+ if (writableIndexes.length === 0 && readonlyIndexes.length === 0) {
124
+ return;
125
+ }
126
+
127
+ return [
128
+ {
129
+ accountKey: lookupTable.key,
130
+ writableIndexes,
131
+ readonlyIndexes,
132
+ },
133
+ {
134
+ writable: drainedWritableKeys,
135
+ readonly: drainedReadonlyKeys,
136
+ },
137
+ ];
138
+ }
139
+
140
+ /** @internal */
141
+ private drainKeysFoundInLookupTable(
142
+ lookupTableEntries: Array<PublicKey>,
143
+ keyMetaFilter: (keyMeta: CompiledKeyMeta) => boolean,
144
+ ): [Array<number>, Array<PublicKey>] {
145
+ const lookupTableIndexes = new Array();
146
+ const drainedKeys = new Array();
147
+
148
+ for (const [address, keyMeta] of this.keyMetaMap.entries()) {
149
+ if (keyMetaFilter(keyMeta)) {
150
+ const key = new PublicKey(address);
151
+ const lookupTableIndex = lookupTableEntries.findIndex(entry =>
152
+ entry.equals(key),
153
+ );
154
+ if (lookupTableIndex >= 0) {
155
+ assert(lookupTableIndex < 256, 'Max lookup table index exceeded');
156
+ lookupTableIndexes.push(lookupTableIndex);
157
+ drainedKeys.push(key);
158
+ this.keyMetaMap.delete(address);
159
+ }
160
+ }
161
+ }
162
+
163
+ return [lookupTableIndexes, drainedKeys];
164
+ }
165
+ }
@@ -1,5 +1,7 @@
1
1
  import {PublicKey} from '../publickey';
2
2
 
3
+ export * from './account-keys';
4
+ // note: compiled-keys is internal and doesn't need to be exported
3
5
  export * from './legacy';
4
6
  export * from './versioned';
5
7
  export * from './v0';
@@ -13,6 +13,9 @@ import {
13
13
  MessageAddressTableLookup,
14
14
  MessageCompiledInstruction,
15
15
  } from './index';
16
+ import {TransactionInstruction} from '../transaction';
17
+ import {CompiledKeys} from './compiled-keys';
18
+ import {MessageAccountKeys} from './account-keys';
16
19
 
17
20
  /**
18
21
  * An instruction to execute by a program
@@ -37,13 +40,19 @@ export type MessageArgs = {
37
40
  /** The message header, identifying signed and read-only `accountKeys` */
38
41
  header: MessageHeader;
39
42
  /** All the account keys used by this transaction */
40
- accountKeys: string[];
43
+ accountKeys: string[] | PublicKey[];
41
44
  /** The hash of a recent ledger block */
42
45
  recentBlockhash: Blockhash;
43
46
  /** Instructions that will be executed in sequence and committed in one atomic transaction if all succeed. */
44
47
  instructions: CompiledInstruction[];
45
48
  };
46
49
 
50
+ export type CompileLegacyArgs = {
51
+ payerKey: PublicKey;
52
+ instructions: Array<TransactionInstruction>;
53
+ recentBlockhash: Blockhash;
54
+ };
55
+
47
56
  /**
48
57
  * List of instructions to be processed atomically
49
58
  */
@@ -93,6 +102,29 @@ export class Message {
93
102
  return [];
94
103
  }
95
104
 
105
+ getAccountKeys(): MessageAccountKeys {
106
+ return new MessageAccountKeys(this.staticAccountKeys);
107
+ }
108
+
109
+ static compile(args: CompileLegacyArgs): Message {
110
+ const compiledKeys = CompiledKeys.compile(args.instructions, args.payerKey);
111
+ const [header, staticAccountKeys] = compiledKeys.getMessageComponents();
112
+ const accountKeys = new MessageAccountKeys(staticAccountKeys);
113
+ const instructions = accountKeys.compileInstructions(args.instructions).map(
114
+ (ix: MessageCompiledInstruction): CompiledInstruction => ({
115
+ programIdIndex: ix.programIdIndex,
116
+ accounts: ix.accountKeyIndexes,
117
+ data: bs58.encode(ix.data),
118
+ }),
119
+ );
120
+ return new Message({
121
+ header,
122
+ accountKeys: staticAccountKeys,
123
+ recentBlockhash: args.recentBlockhash,
124
+ instructions,
125
+ });
126
+ }
127
+
96
128
  isAccountSigner(index: number): boolean {
97
129
  return index < this.header.numRequiredSignatures;
98
130
  }
@@ -250,7 +282,7 @@ export class Message {
250
282
  for (let i = 0; i < accountCount; i++) {
251
283
  const account = byteArray.slice(0, PUBLIC_KEY_LENGTH);
252
284
  byteArray = byteArray.slice(PUBLIC_KEY_LENGTH);
253
- accountKeys.push(bs58.encode(Buffer.from(account)));
285
+ accountKeys.push(new PublicKey(Buffer.from(account)));
254
286
  }
255
287
 
256
288
  const recentBlockhash = byteArray.slice(0, PUBLIC_KEY_LENGTH);
package/src/message/v0.ts CHANGED
@@ -12,6 +12,10 @@ import {PublicKey, PUBLIC_KEY_LENGTH} from '../publickey';
12
12
  import * as shortvec from '../utils/shortvec-encoding';
13
13
  import assert from '../utils/assert';
14
14
  import {PACKET_DATA_SIZE, VERSION_PREFIX_MASK} from '../transaction/constants';
15
+ import {TransactionInstruction} from '../transaction';
16
+ import {AddressLookupTableAccount} from '../programs';
17
+ import {CompiledKeys} from './compiled-keys';
18
+ import {AccountKeysFromLookups, MessageAccountKeys} from './account-keys';
15
19
 
16
20
  /**
17
21
  * Message constructor arguments
@@ -29,6 +33,21 @@ export type MessageV0Args = {
29
33
  addressTableLookups: MessageAddressTableLookup[];
30
34
  };
31
35
 
36
+ export type CompileV0Args = {
37
+ payerKey: PublicKey;
38
+ instructions: Array<TransactionInstruction>;
39
+ recentBlockhash: Blockhash;
40
+ addressLookupTableAccounts?: Array<AddressLookupTableAccount>;
41
+ };
42
+
43
+ export type GetAccountKeysArgs =
44
+ | {
45
+ accountKeysFromLookups: AccountKeysFromLookups;
46
+ }
47
+ | {
48
+ addressLookupTableAccounts: AddressLookupTableAccount[];
49
+ };
50
+
32
51
  export class MessageV0 {
33
52
  header: MessageHeader;
34
53
  staticAccountKeys: Array<PublicKey>;
@@ -48,6 +67,124 @@ export class MessageV0 {
48
67
  return 0;
49
68
  }
50
69
 
70
+ get numAccountKeysFromLookups(): number {
71
+ let count = 0;
72
+ for (const lookup of this.addressTableLookups) {
73
+ count += lookup.readonlyIndexes.length + lookup.writableIndexes.length;
74
+ }
75
+ return count;
76
+ }
77
+
78
+ getAccountKeys(args?: GetAccountKeysArgs): MessageAccountKeys {
79
+ let accountKeysFromLookups: AccountKeysFromLookups | undefined;
80
+ if (args && 'accountKeysFromLookups' in args) {
81
+ if (
82
+ this.numAccountKeysFromLookups !=
83
+ args.accountKeysFromLookups.writable.length +
84
+ args.accountKeysFromLookups.readonly.length
85
+ ) {
86
+ throw new Error(
87
+ 'Failed to get account keys because of a mismatch in the number of account keys from lookups',
88
+ );
89
+ }
90
+ accountKeysFromLookups = args.accountKeysFromLookups;
91
+ } else if (args && 'addressLookupTableAccounts' in args) {
92
+ accountKeysFromLookups = this.resolveAddressTableLookups(
93
+ args.addressLookupTableAccounts,
94
+ );
95
+ } else if (this.addressTableLookups.length > 0) {
96
+ throw new Error(
97
+ 'Failed to get account keys because address table lookups were not resolved',
98
+ );
99
+ }
100
+ return new MessageAccountKeys(
101
+ this.staticAccountKeys,
102
+ accountKeysFromLookups,
103
+ );
104
+ }
105
+
106
+ resolveAddressTableLookups(
107
+ addressLookupTableAccounts: AddressLookupTableAccount[],
108
+ ): AccountKeysFromLookups {
109
+ const accountKeysFromLookups: AccountKeysFromLookups = {
110
+ writable: [],
111
+ readonly: [],
112
+ };
113
+
114
+ for (const tableLookup of this.addressTableLookups) {
115
+ const tableAccount = addressLookupTableAccounts.find(account =>
116
+ account.key.equals(tableLookup.accountKey),
117
+ );
118
+ if (!tableAccount) {
119
+ throw new Error(
120
+ `Failed to find address lookup table account for table key ${tableLookup.accountKey.toBase58()}`,
121
+ );
122
+ }
123
+
124
+ for (const index of tableLookup.writableIndexes) {
125
+ if (index < tableAccount.state.addresses.length) {
126
+ accountKeysFromLookups.writable.push(
127
+ tableAccount.state.addresses[index],
128
+ );
129
+ } else {
130
+ throw new Error(
131
+ `Failed to find address for index ${index} in address lookup table ${tableLookup.accountKey.toBase58()}`,
132
+ );
133
+ }
134
+ }
135
+
136
+ for (const index of tableLookup.readonlyIndexes) {
137
+ if (index < tableAccount.state.addresses.length) {
138
+ accountKeysFromLookups.readonly.push(
139
+ tableAccount.state.addresses[index],
140
+ );
141
+ } else {
142
+ throw new Error(
143
+ `Failed to find address for index ${index} in address lookup table ${tableLookup.accountKey.toBase58()}`,
144
+ );
145
+ }
146
+ }
147
+ }
148
+
149
+ return accountKeysFromLookups;
150
+ }
151
+
152
+ static compile(args: CompileV0Args): MessageV0 {
153
+ const compiledKeys = CompiledKeys.compile(args.instructions, args.payerKey);
154
+
155
+ const addressTableLookups = new Array<MessageAddressTableLookup>();
156
+ const accountKeysFromLookups: AccountKeysFromLookups = {
157
+ writable: new Array(),
158
+ readonly: new Array(),
159
+ };
160
+ const lookupTableAccounts = args.addressLookupTableAccounts || [];
161
+ for (const lookupTable of lookupTableAccounts) {
162
+ const extractResult = compiledKeys.extractTableLookup(lookupTable);
163
+ if (extractResult !== undefined) {
164
+ const [addressTableLookup, {writable, readonly}] = extractResult;
165
+ addressTableLookups.push(addressTableLookup);
166
+ accountKeysFromLookups.writable.push(...writable);
167
+ accountKeysFromLookups.readonly.push(...readonly);
168
+ }
169
+ }
170
+
171
+ const [header, staticAccountKeys] = compiledKeys.getMessageComponents();
172
+ const accountKeys = new MessageAccountKeys(
173
+ staticAccountKeys,
174
+ accountKeysFromLookups,
175
+ );
176
+ const compiledInstructions = accountKeys.compileInstructions(
177
+ args.instructions,
178
+ );
179
+ return new MessageV0({
180
+ header,
181
+ staticAccountKeys,
182
+ recentBlockhash: args.recentBlockhash,
183
+ compiledInstructions,
184
+ addressTableLookups,
185
+ });
186
+ }
187
+
51
188
  serialize(): Uint8Array {
52
189
  const encodedStaticAccountKeysLength = Array<number>();
53
190
  shortvec.encodeLength(