@solana/web3.js 0.0.0-development → 0.0.0-pr-29130
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/README.md +18 -16
- package/lib/index.browser.cjs.js +1955 -1660
- package/lib/index.browser.cjs.js.map +1 -1
- package/lib/index.browser.esm.js +2058 -1770
- package/lib/index.browser.esm.js.map +1 -1
- package/lib/index.cjs.js +4733 -1870
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +3451 -2821
- package/lib/index.esm.js +4761 -1909
- package/lib/index.esm.js.map +1 -1
- package/lib/index.iife.js +5212 -5553
- package/lib/index.iife.js.map +1 -1
- package/lib/index.iife.min.js +7 -14
- package/lib/index.iife.min.js.map +1 -1
- package/lib/index.native.js +1955 -1660
- package/lib/index.native.js.map +1 -1
- package/package.json +18 -21
- package/src/account.ts +19 -10
- package/src/bpf-loader.ts +2 -2
- package/src/connection.ts +1377 -185
- package/src/epoch-schedule.ts +1 -1
- package/src/keypair.ts +20 -25
- package/src/layout.ts +29 -0
- package/src/message/account-keys.ts +79 -0
- package/src/message/compiled-keys.ts +165 -0
- package/src/message/index.ts +21 -6
- package/src/message/legacy.ts +103 -16
- package/src/message/v0.ts +496 -0
- package/src/message/versioned.ts +36 -0
- package/src/nonce-account.ts +7 -3
- package/src/programs/address-lookup-table/state.ts +1 -1
- package/src/programs/compute-budget.ts +3 -0
- package/src/programs/ed25519.ts +2 -2
- package/src/programs/secp256k1.ts +5 -7
- package/src/programs/vote.ts +109 -2
- package/src/publickey.ts +25 -73
- package/src/transaction/constants.ts +2 -0
- package/src/transaction/expiry-custom-errors.ts +13 -0
- package/src/transaction/index.ts +2 -0
- package/src/transaction/legacy.ts +46 -9
- package/src/transaction/message.ts +140 -0
- package/src/transaction/versioned.ts +126 -0
- package/src/utils/ed25519.ts +46 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/send-and-confirm-raw-transaction.ts +13 -0
- package/src/utils/send-and-confirm-transaction.ts +53 -19
- package/src/agent-manager.ts +0 -44
package/src/epoch-schedule.ts
CHANGED
|
@@ -26,7 +26,7 @@ function nextPowerOfTwo(n: number) {
|
|
|
26
26
|
/**
|
|
27
27
|
* Epoch schedule
|
|
28
28
|
* (see https://docs.solana.com/terminology#epoch)
|
|
29
|
-
* Can be retrieved with the {@link
|
|
29
|
+
* Can be retrieved with the {@link Connection.getEpochSchedule} method
|
|
30
30
|
*/
|
|
31
31
|
export class EpochSchedule {
|
|
32
32
|
/** The maximum number of slots in each epoch */
|
package/src/keypair.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import {generateKeypair, getPublicKey, Ed25519Keypair} from './utils/ed25519';
|
|
3
2
|
import {PublicKey} from './publickey';
|
|
4
3
|
|
|
5
4
|
/**
|
|
@@ -10,14 +9,6 @@ export interface Signer {
|
|
|
10
9
|
secretKey: Uint8Array;
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
/**
|
|
14
|
-
* Ed25519 Keypair
|
|
15
|
-
*/
|
|
16
|
-
export interface Ed25519Keypair {
|
|
17
|
-
publicKey: Uint8Array;
|
|
18
|
-
secretKey: Uint8Array;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
12
|
/**
|
|
22
13
|
* An account keypair used for signing transactions.
|
|
23
14
|
*/
|
|
@@ -31,18 +22,14 @@ export class Keypair {
|
|
|
31
22
|
* @param keypair ed25519 keypair
|
|
32
23
|
*/
|
|
33
24
|
constructor(keypair?: Ed25519Keypair) {
|
|
34
|
-
|
|
35
|
-
this._keypair = keypair;
|
|
36
|
-
} else {
|
|
37
|
-
this._keypair = nacl.sign.keyPair();
|
|
38
|
-
}
|
|
25
|
+
this._keypair = keypair ?? generateKeypair();
|
|
39
26
|
}
|
|
40
27
|
|
|
41
28
|
/**
|
|
42
29
|
* Generate a new random keypair
|
|
43
30
|
*/
|
|
44
31
|
static generate(): Keypair {
|
|
45
|
-
return new Keypair(
|
|
32
|
+
return new Keypair(generateKeypair());
|
|
46
33
|
}
|
|
47
34
|
|
|
48
35
|
/**
|
|
@@ -61,16 +48,20 @@ export class Keypair {
|
|
|
61
48
|
secretKey: Uint8Array,
|
|
62
49
|
options?: {skipValidation?: boolean},
|
|
63
50
|
): Keypair {
|
|
64
|
-
|
|
51
|
+
if (secretKey.byteLength !== 64) {
|
|
52
|
+
throw new Error('bad secret key size');
|
|
53
|
+
}
|
|
54
|
+
const publicKey = secretKey.slice(32, 64);
|
|
65
55
|
if (!options || !options.skipValidation) {
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
56
|
+
const privateScalar = secretKey.slice(0, 32);
|
|
57
|
+
const computedPublicKey = getPublicKey(privateScalar);
|
|
58
|
+
for (let ii = 0; ii < 32; ii++) {
|
|
59
|
+
if (publicKey[ii] !== computedPublicKey[ii]) {
|
|
60
|
+
throw new Error('provided secretKey is invalid');
|
|
61
|
+
}
|
|
71
62
|
}
|
|
72
63
|
}
|
|
73
|
-
return new Keypair(
|
|
64
|
+
return new Keypair({publicKey, secretKey});
|
|
74
65
|
}
|
|
75
66
|
|
|
76
67
|
/**
|
|
@@ -79,7 +70,11 @@ export class Keypair {
|
|
|
79
70
|
* @param seed seed byte array
|
|
80
71
|
*/
|
|
81
72
|
static fromSeed(seed: Uint8Array): Keypair {
|
|
82
|
-
|
|
73
|
+
const publicKey = getPublicKey(seed);
|
|
74
|
+
const secretKey = new Uint8Array(64);
|
|
75
|
+
secretKey.set(seed);
|
|
76
|
+
secretKey.set(publicKey, 32);
|
|
77
|
+
return new Keypair({publicKey, secretKey});
|
|
83
78
|
}
|
|
84
79
|
|
|
85
80
|
/**
|
|
@@ -93,6 +88,6 @@ export class Keypair {
|
|
|
93
88
|
* The raw secret key for this keypair
|
|
94
89
|
*/
|
|
95
90
|
get secretKey(): Uint8Array {
|
|
96
|
-
return this._keypair.secretKey;
|
|
91
|
+
return new Uint8Array(this._keypair.secretKey);
|
|
97
92
|
}
|
|
98
93
|
}
|
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
|
*/
|
|
@@ -8,6 +10,13 @@ export const publicKey = (property: string = 'publicKey') => {
|
|
|
8
10
|
return BufferLayout.blob(32, property);
|
|
9
11
|
};
|
|
10
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Layout for a signature
|
|
15
|
+
*/
|
|
16
|
+
export const signature = (property: string = 'signature') => {
|
|
17
|
+
return BufferLayout.blob(64, property);
|
|
18
|
+
};
|
|
19
|
+
|
|
11
20
|
/**
|
|
12
21
|
* Layout for a 64bit unsigned value
|
|
13
22
|
*/
|
|
@@ -134,6 +143,23 @@ export const voteInit = (property: string = 'voteInit') => {
|
|
|
134
143
|
);
|
|
135
144
|
};
|
|
136
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
|
+
|
|
137
163
|
export function getAlloc(type: any, fields: any): number {
|
|
138
164
|
const getItemAlloc = (item: any): number => {
|
|
139
165
|
if (item.span >= 0) {
|
|
@@ -145,6 +171,9 @@ export function getAlloc(type: any, fields: any): number {
|
|
|
145
171
|
if (Array.isArray(field)) {
|
|
146
172
|
return field.length * getItemAlloc(item.elementLayout);
|
|
147
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]);
|
|
148
177
|
}
|
|
149
178
|
// Couldn't determine allocated size of layout
|
|
150
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
|
+
}
|
package/src/message/index.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
import {PublicKey} from '../publickey';
|
|
2
|
+
|
|
3
|
+
export * from './account-keys';
|
|
4
|
+
// note: compiled-keys is internal and doesn't need to be exported
|
|
1
5
|
export * from './legacy';
|
|
6
|
+
export * from './versioned';
|
|
7
|
+
export * from './v0';
|
|
2
8
|
|
|
3
9
|
/**
|
|
4
10
|
* The message header, identifying signed and read-only account
|
|
@@ -15,18 +21,27 @@ export type MessageHeader = {
|
|
|
15
21
|
numReadonlyUnsignedAccounts: number;
|
|
16
22
|
};
|
|
17
23
|
|
|
24
|
+
/**
|
|
25
|
+
* An address table lookup used to load additional accounts
|
|
26
|
+
*/
|
|
27
|
+
export type MessageAddressTableLookup = {
|
|
28
|
+
accountKey: PublicKey;
|
|
29
|
+
writableIndexes: Array<number>;
|
|
30
|
+
readonlyIndexes: Array<number>;
|
|
31
|
+
};
|
|
32
|
+
|
|
18
33
|
/**
|
|
19
34
|
* An instruction to execute by a program
|
|
20
35
|
*
|
|
21
36
|
* @property {number} programIdIndex
|
|
22
|
-
* @property {number[]}
|
|
23
|
-
* @property {
|
|
37
|
+
* @property {number[]} accountKeyIndexes
|
|
38
|
+
* @property {Uint8Array} data
|
|
24
39
|
*/
|
|
25
|
-
export type
|
|
40
|
+
export type MessageCompiledInstruction = {
|
|
26
41
|
/** Index into the transaction keys array indicating the program account that executes this instruction */
|
|
27
42
|
programIdIndex: number;
|
|
28
43
|
/** Ordered indices into the transaction keys array indicating which accounts to pass to the program */
|
|
29
|
-
|
|
30
|
-
/** The program input data
|
|
31
|
-
data:
|
|
44
|
+
accountKeyIndexes: number[];
|
|
45
|
+
/** The program input data */
|
|
46
|
+
data: Uint8Array;
|
|
32
47
|
};
|
package/src/message/legacy.ts
CHANGED
|
@@ -5,10 +5,33 @@ import * as BufferLayout from '@solana/buffer-layout';
|
|
|
5
5
|
import {PublicKey, PUBLIC_KEY_LENGTH} from '../publickey';
|
|
6
6
|
import type {Blockhash} from '../blockhash';
|
|
7
7
|
import * as Layout from '../layout';
|
|
8
|
-
import {PACKET_DATA_SIZE} from '../transaction/constants';
|
|
8
|
+
import {PACKET_DATA_SIZE, VERSION_PREFIX_MASK} from '../transaction/constants';
|
|
9
9
|
import * as shortvec from '../utils/shortvec-encoding';
|
|
10
10
|
import {toBuffer} from '../utils/to-buffer';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
MessageHeader,
|
|
13
|
+
MessageAddressTableLookup,
|
|
14
|
+
MessageCompiledInstruction,
|
|
15
|
+
} from './index';
|
|
16
|
+
import {TransactionInstruction} from '../transaction';
|
|
17
|
+
import {CompiledKeys} from './compiled-keys';
|
|
18
|
+
import {MessageAccountKeys} from './account-keys';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* An instruction to execute by a program
|
|
22
|
+
*
|
|
23
|
+
* @property {number} programIdIndex
|
|
24
|
+
* @property {number[]} accounts
|
|
25
|
+
* @property {string} data
|
|
26
|
+
*/
|
|
27
|
+
export type CompiledInstruction = {
|
|
28
|
+
/** Index into the transaction keys array indicating the program account that executes this instruction */
|
|
29
|
+
programIdIndex: number;
|
|
30
|
+
/** Ordered indices into the transaction keys array indicating which accounts to pass to the program */
|
|
31
|
+
accounts: number[];
|
|
32
|
+
/** The program input data encoded as base 58 */
|
|
33
|
+
data: string;
|
|
34
|
+
};
|
|
12
35
|
|
|
13
36
|
/**
|
|
14
37
|
* Message constructor arguments
|
|
@@ -17,13 +40,19 @@ export type MessageArgs = {
|
|
|
17
40
|
/** The message header, identifying signed and read-only `accountKeys` */
|
|
18
41
|
header: MessageHeader;
|
|
19
42
|
/** All the account keys used by this transaction */
|
|
20
|
-
accountKeys: string[];
|
|
43
|
+
accountKeys: string[] | PublicKey[];
|
|
21
44
|
/** The hash of a recent ledger block */
|
|
22
45
|
recentBlockhash: Blockhash;
|
|
23
46
|
/** Instructions that will be executed in sequence and committed in one atomic transaction if all succeed. */
|
|
24
47
|
instructions: CompiledInstruction[];
|
|
25
48
|
};
|
|
26
49
|
|
|
50
|
+
export type CompileLegacyArgs = {
|
|
51
|
+
payerKey: PublicKey;
|
|
52
|
+
instructions: Array<TransactionInstruction>;
|
|
53
|
+
recentBlockhash: Blockhash;
|
|
54
|
+
};
|
|
55
|
+
|
|
27
56
|
/**
|
|
28
57
|
* List of instructions to be processed atomically
|
|
29
58
|
*/
|
|
@@ -51,19 +80,68 @@ export class Message {
|
|
|
51
80
|
);
|
|
52
81
|
}
|
|
53
82
|
|
|
83
|
+
get version(): 'legacy' {
|
|
84
|
+
return 'legacy';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
get staticAccountKeys(): Array<PublicKey> {
|
|
88
|
+
return this.accountKeys;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
get compiledInstructions(): Array<MessageCompiledInstruction> {
|
|
92
|
+
return this.instructions.map(
|
|
93
|
+
(ix): MessageCompiledInstruction => ({
|
|
94
|
+
programIdIndex: ix.programIdIndex,
|
|
95
|
+
accountKeyIndexes: ix.accounts,
|
|
96
|
+
data: bs58.decode(ix.data),
|
|
97
|
+
}),
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
get addressTableLookups(): Array<MessageAddressTableLookup> {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
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
|
+
|
|
54
128
|
isAccountSigner(index: number): boolean {
|
|
55
129
|
return index < this.header.numRequiredSignatures;
|
|
56
130
|
}
|
|
57
131
|
|
|
58
132
|
isAccountWritable(index: number): boolean {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
133
|
+
const numSignedAccounts = this.header.numRequiredSignatures;
|
|
134
|
+
if (index >= this.header.numRequiredSignatures) {
|
|
135
|
+
const unsignedAccountIndex = index - numSignedAccounts;
|
|
136
|
+
const numUnsignedAccounts = this.accountKeys.length - numSignedAccounts;
|
|
137
|
+
const numWritableUnsignedAccounts =
|
|
138
|
+
numUnsignedAccounts - this.header.numReadonlyUnsignedAccounts;
|
|
139
|
+
return unsignedAccountIndex < numWritableUnsignedAccounts;
|
|
140
|
+
} else {
|
|
141
|
+
const numWritableSignedAccounts =
|
|
142
|
+
numSignedAccounts - this.header.numReadonlySignedAccounts;
|
|
143
|
+
return index < numWritableSignedAccounts;
|
|
144
|
+
}
|
|
67
145
|
}
|
|
68
146
|
|
|
69
147
|
isProgramId(index: number): boolean {
|
|
@@ -190,16 +268,25 @@ export class Message {
|
|
|
190
268
|
// Slice up wire data
|
|
191
269
|
let byteArray = [...buffer];
|
|
192
270
|
|
|
193
|
-
const numRequiredSignatures = byteArray.shift()
|
|
194
|
-
|
|
195
|
-
|
|
271
|
+
const numRequiredSignatures = byteArray.shift()!;
|
|
272
|
+
if (
|
|
273
|
+
numRequiredSignatures !==
|
|
274
|
+
(numRequiredSignatures & VERSION_PREFIX_MASK)
|
|
275
|
+
) {
|
|
276
|
+
throw new Error(
|
|
277
|
+
'Versioned messages must be deserialized with VersionedMessage.deserialize()',
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const numReadonlySignedAccounts = byteArray.shift()!;
|
|
282
|
+
const numReadonlyUnsignedAccounts = byteArray.shift()!;
|
|
196
283
|
|
|
197
284
|
const accountCount = shortvec.decodeLength(byteArray);
|
|
198
285
|
let accountKeys = [];
|
|
199
286
|
for (let i = 0; i < accountCount; i++) {
|
|
200
287
|
const account = byteArray.slice(0, PUBLIC_KEY_LENGTH);
|
|
201
288
|
byteArray = byteArray.slice(PUBLIC_KEY_LENGTH);
|
|
202
|
-
accountKeys.push(
|
|
289
|
+
accountKeys.push(new PublicKey(Buffer.from(account)));
|
|
203
290
|
}
|
|
204
291
|
|
|
205
292
|
const recentBlockhash = byteArray.slice(0, PUBLIC_KEY_LENGTH);
|
|
@@ -208,7 +295,7 @@ export class Message {
|
|
|
208
295
|
const instructionCount = shortvec.decodeLength(byteArray);
|
|
209
296
|
let instructions: CompiledInstruction[] = [];
|
|
210
297
|
for (let i = 0; i < instructionCount; i++) {
|
|
211
|
-
const programIdIndex = byteArray.shift()
|
|
298
|
+
const programIdIndex = byteArray.shift()!;
|
|
212
299
|
const accountCount = shortvec.decodeLength(byteArray);
|
|
213
300
|
const accounts = byteArray.slice(0, accountCount);
|
|
214
301
|
byteArray = byteArray.slice(accountCount);
|