@solana/web3.js 1.56.1 → 1.58.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/lib/index.browser.cjs.js +498 -13
- package/lib/index.browser.cjs.js.map +1 -1
- package/lib/index.browser.esm.js +497 -14
- package/lib/index.browser.esm.js.map +1 -1
- package/lib/index.cjs.js +498 -13
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +3060 -2948
- package/lib/index.esm.js +497 -14
- package/lib/index.esm.js.map +1 -1
- package/lib/index.iife.js +498 -13
- package/lib/index.iife.js.map +1 -1
- package/lib/index.iife.min.js +3 -3
- package/lib/index.iife.min.js.map +1 -1
- package/lib/index.native.js +498 -13
- package/lib/index.native.js.map +1 -1
- package/package.json +1 -1
- package/src/connection.ts +112 -2
- package/src/message/account-keys.ts +79 -0
- package/src/message/compiled-keys.ts +165 -0
- package/src/message/index.ts +2 -0
- package/src/message/legacy.ts +34 -2
- package/src/message/v0.ts +137 -0
- package/src/publickey.ts +12 -0
- package/src/transaction/index.ts +1 -0
- package/src/transaction/message.ts +147 -0
package/package.json
CHANGED
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;
|
|
@@ -888,6 +905,8 @@ export type ParsedTransactionMeta = {
|
|
|
888
905
|
err: TransactionError | null;
|
|
889
906
|
/** The collection of addresses loaded using address lookup tables */
|
|
890
907
|
loadedAddresses?: LoadedAddresses;
|
|
908
|
+
/** The compute units consumed after processing the transaction */
|
|
909
|
+
computeUnitsConsumed?: number;
|
|
891
910
|
};
|
|
892
911
|
|
|
893
912
|
export type CompiledInnerInstruction = {
|
|
@@ -917,6 +936,8 @@ export type ConfirmedTransactionMeta = {
|
|
|
917
936
|
err: TransactionError | null;
|
|
918
937
|
/** The collection of addresses loaded using address lookup tables */
|
|
919
938
|
loadedAddresses?: LoadedAddresses;
|
|
939
|
+
/** The compute units consumed after processing the transaction */
|
|
940
|
+
computeUnitsConsumed?: number;
|
|
920
941
|
};
|
|
921
942
|
|
|
922
943
|
/**
|
|
@@ -1993,6 +2014,7 @@ const ConfirmedTransactionMetaResult = pick({
|
|
|
1993
2014
|
preTokenBalances: optional(nullable(array(TokenBalanceResult))),
|
|
1994
2015
|
postTokenBalances: optional(nullable(array(TokenBalanceResult))),
|
|
1995
2016
|
loadedAddresses: optional(LoadedAddressesResult),
|
|
2017
|
+
computeUnitsConsumed: optional(number()),
|
|
1996
2018
|
});
|
|
1997
2019
|
|
|
1998
2020
|
/**
|
|
@@ -2017,6 +2039,7 @@ const ParsedConfirmedTransactionMetaResult = pick({
|
|
|
2017
2039
|
preTokenBalances: optional(nullable(array(TokenBalanceResult))),
|
|
2018
2040
|
postTokenBalances: optional(nullable(array(TokenBalanceResult))),
|
|
2019
2041
|
loadedAddresses: optional(LoadedAddressesResult),
|
|
2042
|
+
computeUnitsConsumed: optional(number()),
|
|
2020
2043
|
});
|
|
2021
2044
|
|
|
2022
2045
|
const TransactionVersionStruct = union([literal(0), literal('legacy')]);
|
|
@@ -4619,12 +4642,58 @@ export class Connection {
|
|
|
4619
4642
|
|
|
4620
4643
|
/**
|
|
4621
4644
|
* Simulate a transaction
|
|
4645
|
+
*
|
|
4646
|
+
* @deprecated Instead, call {@link simulateTransaction} with {@link
|
|
4647
|
+
* VersionedTransaction} and {@link SimulateTransactionConfig} parameters
|
|
4622
4648
|
*/
|
|
4623
|
-
|
|
4649
|
+
simulateTransaction(
|
|
4624
4650
|
transactionOrMessage: Transaction | Message,
|
|
4625
4651
|
signers?: Array<Signer>,
|
|
4626
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>,
|
|
4627
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
|
+
|
|
4628
4697
|
let transaction;
|
|
4629
4698
|
if (transactionOrMessage instanceof Transaction) {
|
|
4630
4699
|
let originalTx: Transaction = transactionOrMessage;
|
|
@@ -4639,6 +4708,11 @@ export class Connection {
|
|
|
4639
4708
|
transaction._message = transaction._json = undefined;
|
|
4640
4709
|
}
|
|
4641
4710
|
|
|
4711
|
+
if (configOrSigners !== undefined && !Array.isArray(configOrSigners)) {
|
|
4712
|
+
throw new Error('Invalid arguments');
|
|
4713
|
+
}
|
|
4714
|
+
|
|
4715
|
+
const signers = configOrSigners;
|
|
4642
4716
|
if (transaction.nonceInfo && signers) {
|
|
4643
4717
|
transaction.sign(...signers);
|
|
4644
4718
|
} else {
|
|
@@ -4725,12 +4799,48 @@ export class Connection {
|
|
|
4725
4799
|
|
|
4726
4800
|
/**
|
|
4727
4801
|
* Sign and send a transaction
|
|
4802
|
+
*
|
|
4803
|
+
* @deprecated Instead, call {@link sendTransaction} with a {@link
|
|
4804
|
+
* VersionedTransaction}
|
|
4728
4805
|
*/
|
|
4729
|
-
|
|
4806
|
+
sendTransaction(
|
|
4730
4807
|
transaction: Transaction,
|
|
4731
4808
|
signers: Array<Signer>,
|
|
4732
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,
|
|
4733
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;
|
|
4734
4844
|
if (transaction.nonceInfo) {
|
|
4735
4845
|
transaction.sign(...signers);
|
|
4736
4846
|
} else {
|
|
@@ -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
|
+
}
|
package/src/message/index.ts
CHANGED
package/src/message/legacy.ts
CHANGED
|
@@ -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(
|
|
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(
|
package/src/publickey.ts
CHANGED
|
@@ -40,6 +40,9 @@ function isPublicKeyData(value: PublicKeyInitData): value is PublicKeyData {
|
|
|
40
40
|
return (value as PublicKeyData)._bn !== undefined;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
// local counter used by PublicKey.unique()
|
|
44
|
+
let uniquePublicKeyCounter = 1;
|
|
45
|
+
|
|
43
46
|
/**
|
|
44
47
|
* A public key
|
|
45
48
|
*/
|
|
@@ -73,6 +76,15 @@ export class PublicKey extends Struct {
|
|
|
73
76
|
}
|
|
74
77
|
}
|
|
75
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Returns a unique PublicKey for tests and benchmarks using acounter
|
|
81
|
+
*/
|
|
82
|
+
static unique(): PublicKey {
|
|
83
|
+
const key = new PublicKey(uniquePublicKeyCounter);
|
|
84
|
+
uniquePublicKeyCounter += 1;
|
|
85
|
+
return new PublicKey(key.toBuffer());
|
|
86
|
+
}
|
|
87
|
+
|
|
76
88
|
/**
|
|
77
89
|
* Default public key value. (All zeros)
|
|
78
90
|
*/
|