@solana/transactions 2.0.0-20241006045741
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/LICENSE +20 -0
- package/README.md +94 -0
- package/dist/index.browser.cjs +202 -0
- package/dist/index.browser.cjs.map +1 -0
- package/dist/index.browser.mjs +192 -0
- package/dist/index.browser.mjs.map +1 -0
- package/dist/index.native.mjs +192 -0
- package/dist/index.native.mjs.map +1 -0
- package/dist/index.node.cjs +202 -0
- package/dist/index.node.cjs.map +1 -0
- package/dist/index.node.mjs +192 -0
- package/dist/index.node.mjs.map +1 -0
- package/dist/types/codecs/index.d.ts +2 -0
- package/dist/types/codecs/index.d.ts.map +1 -0
- package/dist/types/codecs/signatures-encoder.d.ts +4 -0
- package/dist/types/codecs/signatures-encoder.d.ts.map +1 -0
- package/dist/types/codecs/transaction-codec.d.ts +6 -0
- package/dist/types/codecs/transaction-codec.d.ts.map +1 -0
- package/dist/types/compile-transaction.d.ts +7 -0
- package/dist/types/compile-transaction.d.ts.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/lifetime.d.ts +21 -0
- package/dist/types/lifetime.d.ts.map +1 -0
- package/dist/types/signatures.d.ts +10 -0
- package/dist/types/signatures.d.ts.map +1 -0
- package/dist/types/transaction.d.ts +17 -0
- package/dist/types/transaction.d.ts.map +1 -0
- package/dist/types/wire-transaction.d.ts +6 -0
- package/dist/types/wire-transaction.d.ts.map +1 -0
- package/package.json +96 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2023 Solana Labs, Inc
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
[![npm][npm-image]][npm-url]
|
|
2
|
+
[![npm-downloads][npm-downloads-image]][npm-url]
|
|
3
|
+
[![semantic-release][semantic-release-image]][semantic-release-url]
|
|
4
|
+
<br />
|
|
5
|
+
[![code-style-prettier][code-style-prettier-image]][code-style-prettier-url]
|
|
6
|
+
|
|
7
|
+
[code-style-prettier-image]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square
|
|
8
|
+
[code-style-prettier-url]: https://github.com/prettier/prettier
|
|
9
|
+
[npm-downloads-image]: https://img.shields.io/npm/dm/@solana/transactions/rc.svg?style=flat
|
|
10
|
+
[npm-image]: https://img.shields.io/npm/v/@solana/transactions/rc.svg?style=flat
|
|
11
|
+
[npm-url]: https://www.npmjs.com/package/@solana/transactions/v/rc
|
|
12
|
+
[semantic-release-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
|
|
13
|
+
[semantic-release-url]: https://github.com/semantic-release/semantic-release
|
|
14
|
+
|
|
15
|
+
# @solana/transactions
|
|
16
|
+
|
|
17
|
+
This package contains types and functions for compiling, signing and sending transactions. It can be used standalone, but it is also exported as part of the Solana JavaScript SDK [`@solana/web3.js@rc`](https://github.com/solana-labs/solana-web3.js/tree/master/packages/library).
|
|
18
|
+
|
|
19
|
+
Transactions are created by compiling a transaction message. They must then be signed before being submitted to the network.
|
|
20
|
+
|
|
21
|
+
## Compiling a transaction
|
|
22
|
+
|
|
23
|
+
### Functions
|
|
24
|
+
|
|
25
|
+
#### `compileTransaction()`
|
|
26
|
+
|
|
27
|
+
Given a `TransactionMessage`, this function returns a `Transaction` object. This includes the compiled bytes of the transaction message, and a map of signatures. This map will have a key for each address that is required to sign the transaction. The transaction will not yet have signatures for any of these addresses.
|
|
28
|
+
|
|
29
|
+
Whether a transaction message is ready to be compiled or not is enforced for you at the type level. In order to be signable, a transaction message must:
|
|
30
|
+
|
|
31
|
+
- have a version and a list of zero or more instructions (ie. conform to `BaseTransactionMessage`)
|
|
32
|
+
- have a fee payer set (ie. conform to `ITransactionMessageWithFeePayer`)
|
|
33
|
+
- have a lifetime specified (ie. conform to `TransactionMessageWithBlockhashLifetime | TransactionMessageWithDurableNonceLifetime`)
|
|
34
|
+
|
|
35
|
+
## Signing transactions
|
|
36
|
+
|
|
37
|
+
In order to be landed on the network, a transaction must be signed by all of the private keys belonging to accounts that are required signers of the transaction.
|
|
38
|
+
|
|
39
|
+
### Types
|
|
40
|
+
|
|
41
|
+
#### `FullySignedTransaction`
|
|
42
|
+
|
|
43
|
+
This type represents a transaction that is signed by all of its required signers. Being fully signed is a prerequisite of functions designed to land transactions on the network.
|
|
44
|
+
|
|
45
|
+
### Functions
|
|
46
|
+
|
|
47
|
+
#### `getSignatureFromTransaction()`
|
|
48
|
+
|
|
49
|
+
Given a transaction signed by its fee payer, this method will return the `Signature` that uniquely identifies it. This string can be used to look up transactions at a later date, for example on a Solana block explorer.
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { getSignatureFromTransaction } from '@solana/transactions';
|
|
53
|
+
|
|
54
|
+
const signature = getSignatureFromTransaction(tx);
|
|
55
|
+
console.debug(`Inspect this transaction at https://explorer.solana.com/tx/${signature}`);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### `signTransaction()`
|
|
59
|
+
|
|
60
|
+
Given an array of `CryptoKey` objects which are private keys pertaining to addresses that are required to sign a transaction, this method will return a new signed transaction of type `FullySignedTransaction`. The transaction must have a signature for all required signers after being signed by the input `CryptoKey` objects.
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { generateKeyPair } from '@solana/keys';
|
|
64
|
+
import { signTransaction } from '@solana/transactions';
|
|
65
|
+
|
|
66
|
+
const signedTransaction = await signTransaction([myPrivateKey], tx);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### `partiallySignTransaction()`
|
|
70
|
+
|
|
71
|
+
This function is the same as `signTransaction()` but does not require the transaction to be signed by all signers. A partially signed transaction cannot be landed on the network, but can be serialized and deserialized.
|
|
72
|
+
|
|
73
|
+
## Serializing transactions
|
|
74
|
+
|
|
75
|
+
Before sending a transaction to be landed on the network, you must serialize it in a particular way. You can use these types and functions to serialize a signed transaction into a binary format suitable for transit over the wire.
|
|
76
|
+
|
|
77
|
+
### Types
|
|
78
|
+
|
|
79
|
+
#### `Base64EncodedWireTransaction`
|
|
80
|
+
|
|
81
|
+
This type represents the wire format of a transaction as a base64-encoded string.
|
|
82
|
+
|
|
83
|
+
### Functions
|
|
84
|
+
|
|
85
|
+
#### `getBase64EncodedWireTransaction()`
|
|
86
|
+
|
|
87
|
+
Given a signed transaction, this method returns the transaction as a string that conforms to the `Base64EncodedWireTransaction` type.
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { getBase64EncodedWireTransaction, signTransaction } from '@solana/transactions';
|
|
91
|
+
|
|
92
|
+
const serializedTransaction = getBase64EncodedWireTransaction(signedTransaction);
|
|
93
|
+
const signature = await rpc.sendTransaction(serializedTransaction, { encoding: 'base64' }).send();
|
|
94
|
+
```
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var addresses = require('@solana/addresses');
|
|
4
|
+
var codecsCore = require('@solana/codecs-core');
|
|
5
|
+
var codecsDataStructures = require('@solana/codecs-data-structures');
|
|
6
|
+
var codecsNumbers = require('@solana/codecs-numbers');
|
|
7
|
+
var errors = require('@solana/errors');
|
|
8
|
+
var transactionMessages = require('@solana/transaction-messages');
|
|
9
|
+
var codecsStrings = require('@solana/codecs-strings');
|
|
10
|
+
var keys = require('@solana/keys');
|
|
11
|
+
|
|
12
|
+
// src/codecs/transaction-codec.ts
|
|
13
|
+
function getSignaturesToEncode(signaturesMap) {
|
|
14
|
+
const signatures = Object.values(signaturesMap);
|
|
15
|
+
if (signatures.length === 0) {
|
|
16
|
+
throw new errors.SolanaError(errors.SOLANA_ERROR__TRANSACTION__CANNOT_ENCODE_WITH_EMPTY_SIGNATURES);
|
|
17
|
+
}
|
|
18
|
+
return signatures.map((signature) => {
|
|
19
|
+
if (!signature) {
|
|
20
|
+
return new Uint8Array(64).fill(0);
|
|
21
|
+
}
|
|
22
|
+
return signature;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function getSignaturesEncoder() {
|
|
26
|
+
return codecsCore.transformEncoder(
|
|
27
|
+
codecsDataStructures.getArrayEncoder(codecsCore.fixEncoderSize(codecsDataStructures.getBytesEncoder(), 64), { size: codecsNumbers.getShortU16Encoder() }),
|
|
28
|
+
getSignaturesToEncode
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/codecs/transaction-codec.ts
|
|
33
|
+
function getTransactionEncoder() {
|
|
34
|
+
return codecsDataStructures.getStructEncoder([
|
|
35
|
+
["signatures", getSignaturesEncoder()],
|
|
36
|
+
["messageBytes", codecsDataStructures.getBytesEncoder()]
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
function getTransactionDecoder() {
|
|
40
|
+
return codecsCore.transformDecoder(
|
|
41
|
+
codecsDataStructures.getStructDecoder([
|
|
42
|
+
["signatures", codecsDataStructures.getArrayDecoder(codecsCore.fixDecoderSize(codecsDataStructures.getBytesDecoder(), 64), { size: codecsNumbers.getShortU16Decoder() })],
|
|
43
|
+
["messageBytes", codecsDataStructures.getBytesDecoder()]
|
|
44
|
+
]),
|
|
45
|
+
decodePartiallyDecodedTransaction
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
function getTransactionCodec() {
|
|
49
|
+
return codecsCore.combineCodec(getTransactionEncoder(), getTransactionDecoder());
|
|
50
|
+
}
|
|
51
|
+
function decodePartiallyDecodedTransaction(transaction) {
|
|
52
|
+
const { messageBytes, signatures } = transaction;
|
|
53
|
+
const signerAddressesDecoder = codecsDataStructures.getTupleDecoder([
|
|
54
|
+
// read transaction version
|
|
55
|
+
transactionMessages.getTransactionVersionDecoder(),
|
|
56
|
+
// read first byte of header, `numSignerAccounts`
|
|
57
|
+
// padRight to skip the next 2 bytes, `numReadOnlySignedAccounts` and `numReadOnlyUnsignedAccounts` which we don't need
|
|
58
|
+
codecsCore.padRightDecoder(codecsNumbers.getU8Decoder(), 2),
|
|
59
|
+
// read static addresses
|
|
60
|
+
codecsDataStructures.getArrayDecoder(addresses.getAddressDecoder(), { size: codecsNumbers.getShortU16Decoder() })
|
|
61
|
+
]);
|
|
62
|
+
const [_txVersion, numRequiredSignatures, staticAddresses] = signerAddressesDecoder.decode(messageBytes);
|
|
63
|
+
const signerAddresses = staticAddresses.slice(0, numRequiredSignatures);
|
|
64
|
+
if (signerAddresses.length !== signatures.length) {
|
|
65
|
+
throw new errors.SolanaError(errors.SOLANA_ERROR__TRANSACTION__MESSAGE_SIGNATURES_MISMATCH, {
|
|
66
|
+
numRequiredSignatures,
|
|
67
|
+
signaturesLength: signatures.length,
|
|
68
|
+
signerAddresses
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const signaturesMap = {};
|
|
72
|
+
signerAddresses.forEach((address, index) => {
|
|
73
|
+
const signatureForAddress = signatures[index];
|
|
74
|
+
if (signatureForAddress.every((b) => b === 0)) {
|
|
75
|
+
signaturesMap[address] = null;
|
|
76
|
+
} else {
|
|
77
|
+
signaturesMap[address] = signatureForAddress;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
return {
|
|
81
|
+
messageBytes,
|
|
82
|
+
signatures: Object.freeze(signaturesMap)
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function compileTransaction(transactionMessage) {
|
|
86
|
+
const compiledMessage = transactionMessages.compileTransactionMessage(transactionMessage);
|
|
87
|
+
const messageBytes = transactionMessages.getCompiledTransactionMessageEncoder().encode(
|
|
88
|
+
compiledMessage
|
|
89
|
+
);
|
|
90
|
+
const transactionSigners = compiledMessage.staticAccounts.slice(0, compiledMessage.header.numSignerAccounts);
|
|
91
|
+
const signatures = {};
|
|
92
|
+
for (const signerAddress of transactionSigners) {
|
|
93
|
+
signatures[signerAddress] = null;
|
|
94
|
+
}
|
|
95
|
+
let lifetimeConstraint;
|
|
96
|
+
if (transactionMessages.isTransactionMessageWithBlockhashLifetime(transactionMessage)) {
|
|
97
|
+
lifetimeConstraint = {
|
|
98
|
+
blockhash: transactionMessage.lifetimeConstraint.blockhash,
|
|
99
|
+
lastValidBlockHeight: transactionMessage.lifetimeConstraint.lastValidBlockHeight
|
|
100
|
+
};
|
|
101
|
+
} else {
|
|
102
|
+
lifetimeConstraint = {
|
|
103
|
+
nonce: transactionMessage.lifetimeConstraint.nonce,
|
|
104
|
+
nonceAccountAddress: transactionMessage.instructions[0].accounts[0].address
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const transaction = {
|
|
108
|
+
lifetimeConstraint,
|
|
109
|
+
messageBytes,
|
|
110
|
+
signatures: Object.freeze(signatures)
|
|
111
|
+
};
|
|
112
|
+
return Object.freeze(transaction);
|
|
113
|
+
}
|
|
114
|
+
var base58Decoder;
|
|
115
|
+
function getSignatureFromTransaction(transaction) {
|
|
116
|
+
if (!base58Decoder) base58Decoder = codecsStrings.getBase58Decoder();
|
|
117
|
+
const signatureBytes = Object.values(transaction.signatures)[0];
|
|
118
|
+
if (!signatureBytes) {
|
|
119
|
+
throw new errors.SolanaError(errors.SOLANA_ERROR__TRANSACTION__FEE_PAYER_SIGNATURE_MISSING);
|
|
120
|
+
}
|
|
121
|
+
const transactionSignature = base58Decoder.decode(signatureBytes);
|
|
122
|
+
return transactionSignature;
|
|
123
|
+
}
|
|
124
|
+
function uint8ArraysEqual(arr1, arr2) {
|
|
125
|
+
return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]);
|
|
126
|
+
}
|
|
127
|
+
async function partiallySignTransaction(keyPairs, transaction) {
|
|
128
|
+
let newSignatures;
|
|
129
|
+
let unexpectedSigners;
|
|
130
|
+
await Promise.all(
|
|
131
|
+
keyPairs.map(async (keyPair) => {
|
|
132
|
+
const address = await addresses.getAddressFromPublicKey(keyPair.publicKey);
|
|
133
|
+
const existingSignature = transaction.signatures[address];
|
|
134
|
+
if (existingSignature === void 0) {
|
|
135
|
+
unexpectedSigners ||= /* @__PURE__ */ new Set();
|
|
136
|
+
unexpectedSigners.add(address);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (unexpectedSigners) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const newSignature = await keys.signBytes(keyPair.privateKey, transaction.messageBytes);
|
|
143
|
+
if (existingSignature !== null && uint8ArraysEqual(newSignature, existingSignature)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
newSignatures ||= {};
|
|
147
|
+
newSignatures[address] = newSignature;
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
if (unexpectedSigners && unexpectedSigners.size > 0) {
|
|
151
|
+
const expectedSigners = Object.keys(transaction.signatures);
|
|
152
|
+
throw new errors.SolanaError(errors.SOLANA_ERROR__TRANSACTION__ADDRESSES_CANNOT_SIGN_TRANSACTION, {
|
|
153
|
+
expectedAddresses: expectedSigners,
|
|
154
|
+
unexpectedAddresses: [...unexpectedSigners]
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
if (!newSignatures) {
|
|
158
|
+
return transaction;
|
|
159
|
+
}
|
|
160
|
+
return Object.freeze({
|
|
161
|
+
...transaction,
|
|
162
|
+
signatures: Object.freeze({
|
|
163
|
+
...transaction.signatures,
|
|
164
|
+
...newSignatures
|
|
165
|
+
})
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
async function signTransaction(keyPairs, transaction) {
|
|
169
|
+
const out = await partiallySignTransaction(keyPairs, transaction);
|
|
170
|
+
assertTransactionIsFullySigned(out);
|
|
171
|
+
Object.freeze(out);
|
|
172
|
+
return out;
|
|
173
|
+
}
|
|
174
|
+
function assertTransactionIsFullySigned(transaction) {
|
|
175
|
+
const missingSigs = [];
|
|
176
|
+
Object.entries(transaction.signatures).forEach(([address, signatureBytes]) => {
|
|
177
|
+
if (!signatureBytes) {
|
|
178
|
+
missingSigs.push(address);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
if (missingSigs.length > 0) {
|
|
182
|
+
throw new errors.SolanaError(errors.SOLANA_ERROR__TRANSACTION__SIGNATURES_MISSING, {
|
|
183
|
+
addresses: missingSigs
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function getBase64EncodedWireTransaction(transaction) {
|
|
188
|
+
const wireTransactionBytes = getTransactionEncoder().encode(transaction);
|
|
189
|
+
return codecsStrings.getBase64Decoder().decode(wireTransactionBytes);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
exports.assertTransactionIsFullySigned = assertTransactionIsFullySigned;
|
|
193
|
+
exports.compileTransaction = compileTransaction;
|
|
194
|
+
exports.getBase64EncodedWireTransaction = getBase64EncodedWireTransaction;
|
|
195
|
+
exports.getSignatureFromTransaction = getSignatureFromTransaction;
|
|
196
|
+
exports.getTransactionCodec = getTransactionCodec;
|
|
197
|
+
exports.getTransactionDecoder = getTransactionDecoder;
|
|
198
|
+
exports.getTransactionEncoder = getTransactionEncoder;
|
|
199
|
+
exports.partiallySignTransaction = partiallySignTransaction;
|
|
200
|
+
exports.signTransaction = signTransaction;
|
|
201
|
+
//# sourceMappingURL=index.browser.cjs.map
|
|
202
|
+
//# sourceMappingURL=index.browser.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/codecs/signatures-encoder.ts","../src/codecs/transaction-codec.ts","../src/compile-transaction.ts","../src/signatures.ts","../src/wire-transaction.ts"],"names":["SolanaError","SOLANA_ERROR__TRANSACTION__CANNOT_ENCODE_WITH_EMPTY_SIGNATURES","transformEncoder","getArrayEncoder","fixEncoderSize","getBytesEncoder","getShortU16Encoder","getStructEncoder","transformDecoder","getStructDecoder","getArrayDecoder","fixDecoderSize","getBytesDecoder","getShortU16Decoder","combineCodec","getTupleDecoder","getTransactionVersionDecoder","padRightDecoder","getU8Decoder","getAddressDecoder","SOLANA_ERROR__TRANSACTION__MESSAGE_SIGNATURES_MISMATCH","compileTransactionMessage","getCompiledTransactionMessageEncoder","isTransactionMessageWithBlockhashLifetime","getBase58Decoder","SOLANA_ERROR__TRANSACTION__FEE_PAYER_SIGNATURE_MISSING","getAddressFromPublicKey","signBytes","SOLANA_ERROR__TRANSACTION__ADDRESSES_CANNOT_SIGN_TRANSACTION","SOLANA_ERROR__TRANSACTION__SIGNATURES_MISSING","getBase64Decoder"],"mappings":"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","file":"index.browser.cjs","sourcesContent":["import { fixEncoderSize, transformEncoder, VariableSizeEncoder } from '@solana/codecs-core';\nimport { getArrayEncoder, getBytesEncoder } from '@solana/codecs-data-structures';\nimport { getShortU16Encoder } from '@solana/codecs-numbers';\nimport { SOLANA_ERROR__TRANSACTION__CANNOT_ENCODE_WITH_EMPTY_SIGNATURES, SolanaError } from '@solana/errors';\nimport { SignatureBytes } from '@solana/keys';\n\nimport { SignaturesMap } from '../transaction';\n\nfunction getSignaturesToEncode(signaturesMap: SignaturesMap): SignatureBytes[] {\n const signatures = Object.values(signaturesMap);\n if (signatures.length === 0) {\n throw new SolanaError(SOLANA_ERROR__TRANSACTION__CANNOT_ENCODE_WITH_EMPTY_SIGNATURES);\n }\n\n return signatures.map(signature => {\n if (!signature) {\n return new Uint8Array(64).fill(0) as SignatureBytes;\n }\n return signature;\n });\n}\n\nexport function getSignaturesEncoder(): VariableSizeEncoder<SignaturesMap> {\n return transformEncoder(\n getArrayEncoder(fixEncoderSize(getBytesEncoder(), 64), { size: getShortU16Encoder() }),\n getSignaturesToEncode,\n );\n}\n","import { getAddressDecoder } from '@solana/addresses';\nimport {\n combineCodec,\n fixDecoderSize,\n padRightDecoder,\n ReadonlyUint8Array,\n transformDecoder,\n VariableSizeCodec,\n VariableSizeDecoder,\n VariableSizeEncoder,\n} from '@solana/codecs-core';\nimport {\n getArrayDecoder,\n getBytesDecoder,\n getBytesEncoder,\n getStructDecoder,\n getStructEncoder,\n getTupleDecoder,\n} from '@solana/codecs-data-structures';\nimport { getShortU16Decoder, getU8Decoder } from '@solana/codecs-numbers';\nimport { SOLANA_ERROR__TRANSACTION__MESSAGE_SIGNATURES_MISMATCH, SolanaError } from '@solana/errors';\nimport { SignatureBytes } from '@solana/keys';\nimport { getTransactionVersionDecoder } from '@solana/transaction-messages';\n\nimport { SignaturesMap, Transaction, TransactionMessageBytes } from '../transaction';\nimport { getSignaturesEncoder } from './signatures-encoder';\n\nexport function getTransactionEncoder(): VariableSizeEncoder<Transaction> {\n return getStructEncoder([\n ['signatures', getSignaturesEncoder()],\n ['messageBytes', getBytesEncoder()],\n ]);\n}\n\nexport function getTransactionDecoder(): VariableSizeDecoder<Transaction> {\n return transformDecoder(\n getStructDecoder([\n ['signatures', getArrayDecoder(fixDecoderSize(getBytesDecoder(), 64), { size: getShortU16Decoder() })],\n ['messageBytes', getBytesDecoder()],\n ]),\n decodePartiallyDecodedTransaction,\n );\n}\n\nexport function getTransactionCodec(): VariableSizeCodec<Transaction> {\n return combineCodec(getTransactionEncoder(), getTransactionDecoder());\n}\n\ntype PartiallyDecodedTransaction = {\n messageBytes: ReadonlyUint8Array;\n signatures: ReadonlyUint8Array[];\n};\n\nfunction decodePartiallyDecodedTransaction(transaction: PartiallyDecodedTransaction): Transaction {\n const { messageBytes, signatures } = transaction;\n\n /*\n Relevant message structure is at the start:\n - transaction version (0 bytes for legacy transactions, 1 byte for versioned transactions)\n - `numRequiredSignatures` (1 byte, we verify this matches the length of signatures)\n - `numReadOnlySignedAccounts` (1 byte, not used here)\n - `numReadOnlyUnsignedAccounts` (1 byte, not used here)\n - static addresses, with signers first. This is an array of addresses, prefixed with a short-u16 length\n */\n\n const signerAddressesDecoder = getTupleDecoder([\n // read transaction version\n getTransactionVersionDecoder(),\n // read first byte of header, `numSignerAccounts`\n // padRight to skip the next 2 bytes, `numReadOnlySignedAccounts` and `numReadOnlyUnsignedAccounts` which we don't need\n padRightDecoder(getU8Decoder(), 2),\n // read static addresses\n getArrayDecoder(getAddressDecoder(), { size: getShortU16Decoder() }),\n ]);\n const [_txVersion, numRequiredSignatures, staticAddresses] = signerAddressesDecoder.decode(messageBytes);\n\n const signerAddresses = staticAddresses.slice(0, numRequiredSignatures);\n\n // signer addresses and signatures must be the same length\n // we encode an all-zero signature when the signature is missing\n if (signerAddresses.length !== signatures.length) {\n throw new SolanaError(SOLANA_ERROR__TRANSACTION__MESSAGE_SIGNATURES_MISMATCH, {\n numRequiredSignatures,\n signaturesLength: signatures.length,\n signerAddresses,\n });\n }\n\n // combine the signer addresses + signatures into the signatures map\n const signaturesMap: SignaturesMap = {};\n signerAddresses.forEach((address, index) => {\n const signatureForAddress = signatures[index];\n if (signatureForAddress.every(b => b === 0)) {\n signaturesMap[address] = null;\n } else {\n signaturesMap[address] = signatureForAddress as SignatureBytes;\n }\n });\n\n return {\n messageBytes: messageBytes as TransactionMessageBytes,\n signatures: Object.freeze(signaturesMap),\n };\n}\n","import { ReadonlyUint8Array } from '@solana/codecs-core';\nimport {\n CompilableTransactionMessage,\n compileTransactionMessage,\n getCompiledTransactionMessageEncoder,\n isTransactionMessageWithBlockhashLifetime,\n TransactionMessageWithBlockhashLifetime,\n TransactionMessageWithDurableNonceLifetime,\n} from '@solana/transaction-messages';\n\nimport {\n TransactionWithBlockhashLifetime,\n TransactionWithDurableNonceLifetime,\n TransactionWithLifetime,\n} from './lifetime';\nimport { SignaturesMap, Transaction, TransactionMessageBytes } from './transaction';\n\nexport function compileTransaction(\n transactionMessage: CompilableTransactionMessage & TransactionMessageWithBlockhashLifetime,\n): Readonly<Transaction & TransactionWithBlockhashLifetime>;\n\nexport function compileTransaction(\n transactionMessage: CompilableTransactionMessage & TransactionMessageWithDurableNonceLifetime,\n): Readonly<Transaction & TransactionWithDurableNonceLifetime>;\n\nexport function compileTransaction(\n transactionMessage: CompilableTransactionMessage,\n): Readonly<Transaction & TransactionWithLifetime>;\n\nexport function compileTransaction(\n transactionMessage: CompilableTransactionMessage,\n): Readonly<Transaction & TransactionWithLifetime> {\n const compiledMessage = compileTransactionMessage(transactionMessage);\n const messageBytes = getCompiledTransactionMessageEncoder().encode(\n compiledMessage,\n ) as ReadonlyUint8Array as TransactionMessageBytes;\n\n const transactionSigners = compiledMessage.staticAccounts.slice(0, compiledMessage.header.numSignerAccounts);\n const signatures: SignaturesMap = {};\n for (const signerAddress of transactionSigners) {\n signatures[signerAddress] = null;\n }\n\n let lifetimeConstraint: TransactionWithLifetime['lifetimeConstraint'];\n if (isTransactionMessageWithBlockhashLifetime(transactionMessage)) {\n lifetimeConstraint = {\n blockhash: transactionMessage.lifetimeConstraint.blockhash,\n lastValidBlockHeight: transactionMessage.lifetimeConstraint.lastValidBlockHeight,\n };\n } else {\n lifetimeConstraint = {\n nonce: transactionMessage.lifetimeConstraint.nonce,\n nonceAccountAddress: transactionMessage.instructions[0].accounts[0].address,\n };\n }\n\n const transaction: Transaction & TransactionWithLifetime = {\n lifetimeConstraint,\n messageBytes: messageBytes as TransactionMessageBytes,\n signatures: Object.freeze(signatures),\n };\n\n return Object.freeze(transaction);\n}\n","import { Address, getAddressFromPublicKey } from '@solana/addresses';\nimport { Decoder } from '@solana/codecs-core';\nimport { getBase58Decoder } from '@solana/codecs-strings';\nimport {\n SOLANA_ERROR__TRANSACTION__ADDRESSES_CANNOT_SIGN_TRANSACTION,\n SOLANA_ERROR__TRANSACTION__FEE_PAYER_SIGNATURE_MISSING,\n SOLANA_ERROR__TRANSACTION__SIGNATURES_MISSING,\n SolanaError,\n} from '@solana/errors';\nimport { Signature, SignatureBytes, signBytes } from '@solana/keys';\n\nimport { Transaction } from './transaction';\n\nexport interface FullySignedTransaction extends Transaction {\n readonly __brand: unique symbol;\n}\n\nlet base58Decoder: Decoder<string> | undefined;\n\nexport function getSignatureFromTransaction(transaction: Transaction): Signature {\n if (!base58Decoder) base58Decoder = getBase58Decoder();\n\n // We have ordered signatures from the compiled message accounts\n // first signature is the fee payer\n const signatureBytes = Object.values(transaction.signatures)[0];\n if (!signatureBytes) {\n throw new SolanaError(SOLANA_ERROR__TRANSACTION__FEE_PAYER_SIGNATURE_MISSING);\n }\n const transactionSignature = base58Decoder.decode(signatureBytes);\n return transactionSignature as Signature;\n}\n\nfunction uint8ArraysEqual(arr1: Uint8Array, arr2: Uint8Array) {\n return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]);\n}\n\nexport async function partiallySignTransaction<T extends Transaction>(\n keyPairs: CryptoKeyPair[],\n transaction: T,\n): Promise<T> {\n let newSignatures: Record<Address, SignatureBytes> | undefined;\n let unexpectedSigners: Set<Address> | undefined;\n\n await Promise.all(\n keyPairs.map(async keyPair => {\n const address = await getAddressFromPublicKey(keyPair.publicKey);\n const existingSignature = transaction.signatures[address];\n\n // Check if the address is expected to sign the transaction\n if (existingSignature === undefined) {\n // address is not an expected signer for this transaction\n unexpectedSigners ||= new Set();\n unexpectedSigners.add(address);\n return;\n }\n\n // Return if there are any unexpected signers already since we won't be using signatures\n if (unexpectedSigners) {\n return;\n }\n\n const newSignature = await signBytes(keyPair.privateKey, transaction.messageBytes);\n\n if (existingSignature !== null && uint8ArraysEqual(newSignature, existingSignature)) {\n // already have the same signature set\n return;\n }\n\n newSignatures ||= {};\n newSignatures[address] = newSignature;\n }),\n );\n\n if (unexpectedSigners && unexpectedSigners.size > 0) {\n const expectedSigners = Object.keys(transaction.signatures);\n throw new SolanaError(SOLANA_ERROR__TRANSACTION__ADDRESSES_CANNOT_SIGN_TRANSACTION, {\n expectedAddresses: expectedSigners,\n unexpectedAddresses: [...unexpectedSigners],\n });\n }\n\n if (!newSignatures) {\n return transaction;\n }\n\n return Object.freeze({\n ...transaction,\n signatures: Object.freeze({\n ...transaction.signatures,\n ...newSignatures,\n }),\n });\n}\n\nexport async function signTransaction<T extends Transaction>(\n keyPairs: CryptoKeyPair[],\n transaction: T,\n): Promise<FullySignedTransaction & T> {\n const out = await partiallySignTransaction(keyPairs, transaction);\n assertTransactionIsFullySigned(out);\n Object.freeze(out);\n return out;\n}\n\nexport function assertTransactionIsFullySigned(\n transaction: Transaction,\n): asserts transaction is FullySignedTransaction {\n const missingSigs: Address[] = [];\n Object.entries(transaction.signatures).forEach(([address, signatureBytes]) => {\n if (!signatureBytes) {\n missingSigs.push(address as Address);\n }\n });\n\n if (missingSigs.length > 0) {\n throw new SolanaError(SOLANA_ERROR__TRANSACTION__SIGNATURES_MISSING, {\n addresses: missingSigs,\n });\n }\n}\n","import { getBase64Decoder } from '@solana/codecs-strings';\n\nimport { getTransactionEncoder } from './codecs';\nimport { Transaction } from './transaction';\n\nexport type Base64EncodedWireTransaction = string & {\n readonly __brand: unique symbol;\n};\n\nexport function getBase64EncodedWireTransaction(transaction: Transaction): Base64EncodedWireTransaction {\n const wireTransactionBytes = getTransactionEncoder().encode(transaction);\n return getBase64Decoder().decode(wireTransactionBytes) as Base64EncodedWireTransaction;\n}\n"]}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { getAddressDecoder, getAddressFromPublicKey } from '@solana/addresses';
|
|
2
|
+
import { transformDecoder, fixDecoderSize, combineCodec, padRightDecoder, transformEncoder, fixEncoderSize } from '@solana/codecs-core';
|
|
3
|
+
import { getStructEncoder, getBytesEncoder, getStructDecoder, getArrayDecoder, getBytesDecoder, getTupleDecoder, getArrayEncoder } from '@solana/codecs-data-structures';
|
|
4
|
+
import { getShortU16Decoder, getU8Decoder, getShortU16Encoder } from '@solana/codecs-numbers';
|
|
5
|
+
import { SolanaError, SOLANA_ERROR__TRANSACTION__MESSAGE_SIGNATURES_MISMATCH, SOLANA_ERROR__TRANSACTION__FEE_PAYER_SIGNATURE_MISSING, SOLANA_ERROR__TRANSACTION__ADDRESSES_CANNOT_SIGN_TRANSACTION, SOLANA_ERROR__TRANSACTION__SIGNATURES_MISSING, SOLANA_ERROR__TRANSACTION__CANNOT_ENCODE_WITH_EMPTY_SIGNATURES } from '@solana/errors';
|
|
6
|
+
import { getTransactionVersionDecoder, compileTransactionMessage, getCompiledTransactionMessageEncoder, isTransactionMessageWithBlockhashLifetime } from '@solana/transaction-messages';
|
|
7
|
+
import { getBase58Decoder, getBase64Decoder } from '@solana/codecs-strings';
|
|
8
|
+
import { signBytes } from '@solana/keys';
|
|
9
|
+
|
|
10
|
+
// src/codecs/transaction-codec.ts
|
|
11
|
+
function getSignaturesToEncode(signaturesMap) {
|
|
12
|
+
const signatures = Object.values(signaturesMap);
|
|
13
|
+
if (signatures.length === 0) {
|
|
14
|
+
throw new SolanaError(SOLANA_ERROR__TRANSACTION__CANNOT_ENCODE_WITH_EMPTY_SIGNATURES);
|
|
15
|
+
}
|
|
16
|
+
return signatures.map((signature) => {
|
|
17
|
+
if (!signature) {
|
|
18
|
+
return new Uint8Array(64).fill(0);
|
|
19
|
+
}
|
|
20
|
+
return signature;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function getSignaturesEncoder() {
|
|
24
|
+
return transformEncoder(
|
|
25
|
+
getArrayEncoder(fixEncoderSize(getBytesEncoder(), 64), { size: getShortU16Encoder() }),
|
|
26
|
+
getSignaturesToEncode
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/codecs/transaction-codec.ts
|
|
31
|
+
function getTransactionEncoder() {
|
|
32
|
+
return getStructEncoder([
|
|
33
|
+
["signatures", getSignaturesEncoder()],
|
|
34
|
+
["messageBytes", getBytesEncoder()]
|
|
35
|
+
]);
|
|
36
|
+
}
|
|
37
|
+
function getTransactionDecoder() {
|
|
38
|
+
return transformDecoder(
|
|
39
|
+
getStructDecoder([
|
|
40
|
+
["signatures", getArrayDecoder(fixDecoderSize(getBytesDecoder(), 64), { size: getShortU16Decoder() })],
|
|
41
|
+
["messageBytes", getBytesDecoder()]
|
|
42
|
+
]),
|
|
43
|
+
decodePartiallyDecodedTransaction
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
function getTransactionCodec() {
|
|
47
|
+
return combineCodec(getTransactionEncoder(), getTransactionDecoder());
|
|
48
|
+
}
|
|
49
|
+
function decodePartiallyDecodedTransaction(transaction) {
|
|
50
|
+
const { messageBytes, signatures } = transaction;
|
|
51
|
+
const signerAddressesDecoder = getTupleDecoder([
|
|
52
|
+
// read transaction version
|
|
53
|
+
getTransactionVersionDecoder(),
|
|
54
|
+
// read first byte of header, `numSignerAccounts`
|
|
55
|
+
// padRight to skip the next 2 bytes, `numReadOnlySignedAccounts` and `numReadOnlyUnsignedAccounts` which we don't need
|
|
56
|
+
padRightDecoder(getU8Decoder(), 2),
|
|
57
|
+
// read static addresses
|
|
58
|
+
getArrayDecoder(getAddressDecoder(), { size: getShortU16Decoder() })
|
|
59
|
+
]);
|
|
60
|
+
const [_txVersion, numRequiredSignatures, staticAddresses] = signerAddressesDecoder.decode(messageBytes);
|
|
61
|
+
const signerAddresses = staticAddresses.slice(0, numRequiredSignatures);
|
|
62
|
+
if (signerAddresses.length !== signatures.length) {
|
|
63
|
+
throw new SolanaError(SOLANA_ERROR__TRANSACTION__MESSAGE_SIGNATURES_MISMATCH, {
|
|
64
|
+
numRequiredSignatures,
|
|
65
|
+
signaturesLength: signatures.length,
|
|
66
|
+
signerAddresses
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const signaturesMap = {};
|
|
70
|
+
signerAddresses.forEach((address, index) => {
|
|
71
|
+
const signatureForAddress = signatures[index];
|
|
72
|
+
if (signatureForAddress.every((b) => b === 0)) {
|
|
73
|
+
signaturesMap[address] = null;
|
|
74
|
+
} else {
|
|
75
|
+
signaturesMap[address] = signatureForAddress;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
messageBytes,
|
|
80
|
+
signatures: Object.freeze(signaturesMap)
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function compileTransaction(transactionMessage) {
|
|
84
|
+
const compiledMessage = compileTransactionMessage(transactionMessage);
|
|
85
|
+
const messageBytes = getCompiledTransactionMessageEncoder().encode(
|
|
86
|
+
compiledMessage
|
|
87
|
+
);
|
|
88
|
+
const transactionSigners = compiledMessage.staticAccounts.slice(0, compiledMessage.header.numSignerAccounts);
|
|
89
|
+
const signatures = {};
|
|
90
|
+
for (const signerAddress of transactionSigners) {
|
|
91
|
+
signatures[signerAddress] = null;
|
|
92
|
+
}
|
|
93
|
+
let lifetimeConstraint;
|
|
94
|
+
if (isTransactionMessageWithBlockhashLifetime(transactionMessage)) {
|
|
95
|
+
lifetimeConstraint = {
|
|
96
|
+
blockhash: transactionMessage.lifetimeConstraint.blockhash,
|
|
97
|
+
lastValidBlockHeight: transactionMessage.lifetimeConstraint.lastValidBlockHeight
|
|
98
|
+
};
|
|
99
|
+
} else {
|
|
100
|
+
lifetimeConstraint = {
|
|
101
|
+
nonce: transactionMessage.lifetimeConstraint.nonce,
|
|
102
|
+
nonceAccountAddress: transactionMessage.instructions[0].accounts[0].address
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const transaction = {
|
|
106
|
+
lifetimeConstraint,
|
|
107
|
+
messageBytes,
|
|
108
|
+
signatures: Object.freeze(signatures)
|
|
109
|
+
};
|
|
110
|
+
return Object.freeze(transaction);
|
|
111
|
+
}
|
|
112
|
+
var base58Decoder;
|
|
113
|
+
function getSignatureFromTransaction(transaction) {
|
|
114
|
+
if (!base58Decoder) base58Decoder = getBase58Decoder();
|
|
115
|
+
const signatureBytes = Object.values(transaction.signatures)[0];
|
|
116
|
+
if (!signatureBytes) {
|
|
117
|
+
throw new SolanaError(SOLANA_ERROR__TRANSACTION__FEE_PAYER_SIGNATURE_MISSING);
|
|
118
|
+
}
|
|
119
|
+
const transactionSignature = base58Decoder.decode(signatureBytes);
|
|
120
|
+
return transactionSignature;
|
|
121
|
+
}
|
|
122
|
+
function uint8ArraysEqual(arr1, arr2) {
|
|
123
|
+
return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]);
|
|
124
|
+
}
|
|
125
|
+
async function partiallySignTransaction(keyPairs, transaction) {
|
|
126
|
+
let newSignatures;
|
|
127
|
+
let unexpectedSigners;
|
|
128
|
+
await Promise.all(
|
|
129
|
+
keyPairs.map(async (keyPair) => {
|
|
130
|
+
const address = await getAddressFromPublicKey(keyPair.publicKey);
|
|
131
|
+
const existingSignature = transaction.signatures[address];
|
|
132
|
+
if (existingSignature === void 0) {
|
|
133
|
+
unexpectedSigners ||= /* @__PURE__ */ new Set();
|
|
134
|
+
unexpectedSigners.add(address);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (unexpectedSigners) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const newSignature = await signBytes(keyPair.privateKey, transaction.messageBytes);
|
|
141
|
+
if (existingSignature !== null && uint8ArraysEqual(newSignature, existingSignature)) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
newSignatures ||= {};
|
|
145
|
+
newSignatures[address] = newSignature;
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
if (unexpectedSigners && unexpectedSigners.size > 0) {
|
|
149
|
+
const expectedSigners = Object.keys(transaction.signatures);
|
|
150
|
+
throw new SolanaError(SOLANA_ERROR__TRANSACTION__ADDRESSES_CANNOT_SIGN_TRANSACTION, {
|
|
151
|
+
expectedAddresses: expectedSigners,
|
|
152
|
+
unexpectedAddresses: [...unexpectedSigners]
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
if (!newSignatures) {
|
|
156
|
+
return transaction;
|
|
157
|
+
}
|
|
158
|
+
return Object.freeze({
|
|
159
|
+
...transaction,
|
|
160
|
+
signatures: Object.freeze({
|
|
161
|
+
...transaction.signatures,
|
|
162
|
+
...newSignatures
|
|
163
|
+
})
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
async function signTransaction(keyPairs, transaction) {
|
|
167
|
+
const out = await partiallySignTransaction(keyPairs, transaction);
|
|
168
|
+
assertTransactionIsFullySigned(out);
|
|
169
|
+
Object.freeze(out);
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
function assertTransactionIsFullySigned(transaction) {
|
|
173
|
+
const missingSigs = [];
|
|
174
|
+
Object.entries(transaction.signatures).forEach(([address, signatureBytes]) => {
|
|
175
|
+
if (!signatureBytes) {
|
|
176
|
+
missingSigs.push(address);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
if (missingSigs.length > 0) {
|
|
180
|
+
throw new SolanaError(SOLANA_ERROR__TRANSACTION__SIGNATURES_MISSING, {
|
|
181
|
+
addresses: missingSigs
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function getBase64EncodedWireTransaction(transaction) {
|
|
186
|
+
const wireTransactionBytes = getTransactionEncoder().encode(transaction);
|
|
187
|
+
return getBase64Decoder().decode(wireTransactionBytes);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export { assertTransactionIsFullySigned, compileTransaction, getBase64EncodedWireTransaction, getSignatureFromTransaction, getTransactionCodec, getTransactionDecoder, getTransactionEncoder, partiallySignTransaction, signTransaction };
|
|
191
|
+
//# sourceMappingURL=index.browser.mjs.map
|
|
192
|
+
//# sourceMappingURL=index.browser.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/codecs/signatures-encoder.ts","../src/codecs/transaction-codec.ts","../src/compile-transaction.ts","../src/signatures.ts","../src/wire-transaction.ts"],"names":["getBytesEncoder","SolanaError"],"mappings":"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","file":"index.browser.mjs","sourcesContent":["import { fixEncoderSize, transformEncoder, VariableSizeEncoder } from '@solana/codecs-core';\nimport { getArrayEncoder, getBytesEncoder } from '@solana/codecs-data-structures';\nimport { getShortU16Encoder } from '@solana/codecs-numbers';\nimport { SOLANA_ERROR__TRANSACTION__CANNOT_ENCODE_WITH_EMPTY_SIGNATURES, SolanaError } from '@solana/errors';\nimport { SignatureBytes } from '@solana/keys';\n\nimport { SignaturesMap } from '../transaction';\n\nfunction getSignaturesToEncode(signaturesMap: SignaturesMap): SignatureBytes[] {\n const signatures = Object.values(signaturesMap);\n if (signatures.length === 0) {\n throw new SolanaError(SOLANA_ERROR__TRANSACTION__CANNOT_ENCODE_WITH_EMPTY_SIGNATURES);\n }\n\n return signatures.map(signature => {\n if (!signature) {\n return new Uint8Array(64).fill(0) as SignatureBytes;\n }\n return signature;\n });\n}\n\nexport function getSignaturesEncoder(): VariableSizeEncoder<SignaturesMap> {\n return transformEncoder(\n getArrayEncoder(fixEncoderSize(getBytesEncoder(), 64), { size: getShortU16Encoder() }),\n getSignaturesToEncode,\n );\n}\n","import { getAddressDecoder } from '@solana/addresses';\nimport {\n combineCodec,\n fixDecoderSize,\n padRightDecoder,\n ReadonlyUint8Array,\n transformDecoder,\n VariableSizeCodec,\n VariableSizeDecoder,\n VariableSizeEncoder,\n} from '@solana/codecs-core';\nimport {\n getArrayDecoder,\n getBytesDecoder,\n getBytesEncoder,\n getStructDecoder,\n getStructEncoder,\n getTupleDecoder,\n} from '@solana/codecs-data-structures';\nimport { getShortU16Decoder, getU8Decoder } from '@solana/codecs-numbers';\nimport { SOLANA_ERROR__TRANSACTION__MESSAGE_SIGNATURES_MISMATCH, SolanaError } from '@solana/errors';\nimport { SignatureBytes } from '@solana/keys';\nimport { getTransactionVersionDecoder } from '@solana/transaction-messages';\n\nimport { SignaturesMap, Transaction, TransactionMessageBytes } from '../transaction';\nimport { getSignaturesEncoder } from './signatures-encoder';\n\nexport function getTransactionEncoder(): VariableSizeEncoder<Transaction> {\n return getStructEncoder([\n ['signatures', getSignaturesEncoder()],\n ['messageBytes', getBytesEncoder()],\n ]);\n}\n\nexport function getTransactionDecoder(): VariableSizeDecoder<Transaction> {\n return transformDecoder(\n getStructDecoder([\n ['signatures', getArrayDecoder(fixDecoderSize(getBytesDecoder(), 64), { size: getShortU16Decoder() })],\n ['messageBytes', getBytesDecoder()],\n ]),\n decodePartiallyDecodedTransaction,\n );\n}\n\nexport function getTransactionCodec(): VariableSizeCodec<Transaction> {\n return combineCodec(getTransactionEncoder(), getTransactionDecoder());\n}\n\ntype PartiallyDecodedTransaction = {\n messageBytes: ReadonlyUint8Array;\n signatures: ReadonlyUint8Array[];\n};\n\nfunction decodePartiallyDecodedTransaction(transaction: PartiallyDecodedTransaction): Transaction {\n const { messageBytes, signatures } = transaction;\n\n /*\n Relevant message structure is at the start:\n - transaction version (0 bytes for legacy transactions, 1 byte for versioned transactions)\n - `numRequiredSignatures` (1 byte, we verify this matches the length of signatures)\n - `numReadOnlySignedAccounts` (1 byte, not used here)\n - `numReadOnlyUnsignedAccounts` (1 byte, not used here)\n - static addresses, with signers first. This is an array of addresses, prefixed with a short-u16 length\n */\n\n const signerAddressesDecoder = getTupleDecoder([\n // read transaction version\n getTransactionVersionDecoder(),\n // read first byte of header, `numSignerAccounts`\n // padRight to skip the next 2 bytes, `numReadOnlySignedAccounts` and `numReadOnlyUnsignedAccounts` which we don't need\n padRightDecoder(getU8Decoder(), 2),\n // read static addresses\n getArrayDecoder(getAddressDecoder(), { size: getShortU16Decoder() }),\n ]);\n const [_txVersion, numRequiredSignatures, staticAddresses] = signerAddressesDecoder.decode(messageBytes);\n\n const signerAddresses = staticAddresses.slice(0, numRequiredSignatures);\n\n // signer addresses and signatures must be the same length\n // we encode an all-zero signature when the signature is missing\n if (signerAddresses.length !== signatures.length) {\n throw new SolanaError(SOLANA_ERROR__TRANSACTION__MESSAGE_SIGNATURES_MISMATCH, {\n numRequiredSignatures,\n signaturesLength: signatures.length,\n signerAddresses,\n });\n }\n\n // combine the signer addresses + signatures into the signatures map\n const signaturesMap: SignaturesMap = {};\n signerAddresses.forEach((address, index) => {\n const signatureForAddress = signatures[index];\n if (signatureForAddress.every(b => b === 0)) {\n signaturesMap[address] = null;\n } else {\n signaturesMap[address] = signatureForAddress as SignatureBytes;\n }\n });\n\n return {\n messageBytes: messageBytes as TransactionMessageBytes,\n signatures: Object.freeze(signaturesMap),\n };\n}\n","import { ReadonlyUint8Array } from '@solana/codecs-core';\nimport {\n CompilableTransactionMessage,\n compileTransactionMessage,\n getCompiledTransactionMessageEncoder,\n isTransactionMessageWithBlockhashLifetime,\n TransactionMessageWithBlockhashLifetime,\n TransactionMessageWithDurableNonceLifetime,\n} from '@solana/transaction-messages';\n\nimport {\n TransactionWithBlockhashLifetime,\n TransactionWithDurableNonceLifetime,\n TransactionWithLifetime,\n} from './lifetime';\nimport { SignaturesMap, Transaction, TransactionMessageBytes } from './transaction';\n\nexport function compileTransaction(\n transactionMessage: CompilableTransactionMessage & TransactionMessageWithBlockhashLifetime,\n): Readonly<Transaction & TransactionWithBlockhashLifetime>;\n\nexport function compileTransaction(\n transactionMessage: CompilableTransactionMessage & TransactionMessageWithDurableNonceLifetime,\n): Readonly<Transaction & TransactionWithDurableNonceLifetime>;\n\nexport function compileTransaction(\n transactionMessage: CompilableTransactionMessage,\n): Readonly<Transaction & TransactionWithLifetime>;\n\nexport function compileTransaction(\n transactionMessage: CompilableTransactionMessage,\n): Readonly<Transaction & TransactionWithLifetime> {\n const compiledMessage = compileTransactionMessage(transactionMessage);\n const messageBytes = getCompiledTransactionMessageEncoder().encode(\n compiledMessage,\n ) as ReadonlyUint8Array as TransactionMessageBytes;\n\n const transactionSigners = compiledMessage.staticAccounts.slice(0, compiledMessage.header.numSignerAccounts);\n const signatures: SignaturesMap = {};\n for (const signerAddress of transactionSigners) {\n signatures[signerAddress] = null;\n }\n\n let lifetimeConstraint: TransactionWithLifetime['lifetimeConstraint'];\n if (isTransactionMessageWithBlockhashLifetime(transactionMessage)) {\n lifetimeConstraint = {\n blockhash: transactionMessage.lifetimeConstraint.blockhash,\n lastValidBlockHeight: transactionMessage.lifetimeConstraint.lastValidBlockHeight,\n };\n } else {\n lifetimeConstraint = {\n nonce: transactionMessage.lifetimeConstraint.nonce,\n nonceAccountAddress: transactionMessage.instructions[0].accounts[0].address,\n };\n }\n\n const transaction: Transaction & TransactionWithLifetime = {\n lifetimeConstraint,\n messageBytes: messageBytes as TransactionMessageBytes,\n signatures: Object.freeze(signatures),\n };\n\n return Object.freeze(transaction);\n}\n","import { Address, getAddressFromPublicKey } from '@solana/addresses';\nimport { Decoder } from '@solana/codecs-core';\nimport { getBase58Decoder } from '@solana/codecs-strings';\nimport {\n SOLANA_ERROR__TRANSACTION__ADDRESSES_CANNOT_SIGN_TRANSACTION,\n SOLANA_ERROR__TRANSACTION__FEE_PAYER_SIGNATURE_MISSING,\n SOLANA_ERROR__TRANSACTION__SIGNATURES_MISSING,\n SolanaError,\n} from '@solana/errors';\nimport { Signature, SignatureBytes, signBytes } from '@solana/keys';\n\nimport { Transaction } from './transaction';\n\nexport interface FullySignedTransaction extends Transaction {\n readonly __brand: unique symbol;\n}\n\nlet base58Decoder: Decoder<string> | undefined;\n\nexport function getSignatureFromTransaction(transaction: Transaction): Signature {\n if (!base58Decoder) base58Decoder = getBase58Decoder();\n\n // We have ordered signatures from the compiled message accounts\n // first signature is the fee payer\n const signatureBytes = Object.values(transaction.signatures)[0];\n if (!signatureBytes) {\n throw new SolanaError(SOLANA_ERROR__TRANSACTION__FEE_PAYER_SIGNATURE_MISSING);\n }\n const transactionSignature = base58Decoder.decode(signatureBytes);\n return transactionSignature as Signature;\n}\n\nfunction uint8ArraysEqual(arr1: Uint8Array, arr2: Uint8Array) {\n return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]);\n}\n\nexport async function partiallySignTransaction<T extends Transaction>(\n keyPairs: CryptoKeyPair[],\n transaction: T,\n): Promise<T> {\n let newSignatures: Record<Address, SignatureBytes> | undefined;\n let unexpectedSigners: Set<Address> | undefined;\n\n await Promise.all(\n keyPairs.map(async keyPair => {\n const address = await getAddressFromPublicKey(keyPair.publicKey);\n const existingSignature = transaction.signatures[address];\n\n // Check if the address is expected to sign the transaction\n if (existingSignature === undefined) {\n // address is not an expected signer for this transaction\n unexpectedSigners ||= new Set();\n unexpectedSigners.add(address);\n return;\n }\n\n // Return if there are any unexpected signers already since we won't be using signatures\n if (unexpectedSigners) {\n return;\n }\n\n const newSignature = await signBytes(keyPair.privateKey, transaction.messageBytes);\n\n if (existingSignature !== null && uint8ArraysEqual(newSignature, existingSignature)) {\n // already have the same signature set\n return;\n }\n\n newSignatures ||= {};\n newSignatures[address] = newSignature;\n }),\n );\n\n if (unexpectedSigners && unexpectedSigners.size > 0) {\n const expectedSigners = Object.keys(transaction.signatures);\n throw new SolanaError(SOLANA_ERROR__TRANSACTION__ADDRESSES_CANNOT_SIGN_TRANSACTION, {\n expectedAddresses: expectedSigners,\n unexpectedAddresses: [...unexpectedSigners],\n });\n }\n\n if (!newSignatures) {\n return transaction;\n }\n\n return Object.freeze({\n ...transaction,\n signatures: Object.freeze({\n ...transaction.signatures,\n ...newSignatures,\n }),\n });\n}\n\nexport async function signTransaction<T extends Transaction>(\n keyPairs: CryptoKeyPair[],\n transaction: T,\n): Promise<FullySignedTransaction & T> {\n const out = await partiallySignTransaction(keyPairs, transaction);\n assertTransactionIsFullySigned(out);\n Object.freeze(out);\n return out;\n}\n\nexport function assertTransactionIsFullySigned(\n transaction: Transaction,\n): asserts transaction is FullySignedTransaction {\n const missingSigs: Address[] = [];\n Object.entries(transaction.signatures).forEach(([address, signatureBytes]) => {\n if (!signatureBytes) {\n missingSigs.push(address as Address);\n }\n });\n\n if (missingSigs.length > 0) {\n throw new SolanaError(SOLANA_ERROR__TRANSACTION__SIGNATURES_MISSING, {\n addresses: missingSigs,\n });\n }\n}\n","import { getBase64Decoder } from '@solana/codecs-strings';\n\nimport { getTransactionEncoder } from './codecs';\nimport { Transaction } from './transaction';\n\nexport type Base64EncodedWireTransaction = string & {\n readonly __brand: unique symbol;\n};\n\nexport function getBase64EncodedWireTransaction(transaction: Transaction): Base64EncodedWireTransaction {\n const wireTransactionBytes = getTransactionEncoder().encode(transaction);\n return getBase64Decoder().decode(wireTransactionBytes) as Base64EncodedWireTransaction;\n}\n"]}
|