@bounded-sh/server 0.0.67 → 0.0.68
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/dist/index.js +43 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +43 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -53,6 +53,20 @@ async function getIdToken() {
|
|
|
53
53
|
throw new Error('getIdToken is not available as a tarobase-server top-level auth operation. Use createWalletClient({ keypair }) and call the returned client methods.');
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* DS3-0414: does the transaction already carry a REAL signer signature? A Solana
|
|
58
|
+
* signature is computed over the compiled message, which INCLUDES recentBlockhash, so
|
|
59
|
+
* the blockhash must not be rewritten once any signer (sponsor / multisig / co-signer)
|
|
60
|
+
* has signed - a rewrite silently invalidates their signature. Legacy signatures are
|
|
61
|
+
* `{ signature: Buffer | null }` entries; versioned signatures are 64-byte arrays that
|
|
62
|
+
* stay all-zero placeholders until a signer fills them. Mirrors turnkey-signer-bridge.ts.
|
|
63
|
+
*/
|
|
64
|
+
function transactionHasSignature(transaction) {
|
|
65
|
+
if ('serializeMessage' in transaction) {
|
|
66
|
+
return transaction.signatures.some((s) => s.signature != null);
|
|
67
|
+
}
|
|
68
|
+
return transaction.signatures.some((sig) => sig.some((b) => b !== 0));
|
|
69
|
+
}
|
|
56
70
|
class SolanaKeypairProvider {
|
|
57
71
|
constructor(rpcUrl, serverKeypair) {
|
|
58
72
|
this.rpcUrl = rpcUrl;
|
|
@@ -109,25 +123,34 @@ class SolanaKeypairProvider {
|
|
|
109
123
|
// 1. Get RPC URL and create connection
|
|
110
124
|
const rpcUrl = this.getRpcUrl();
|
|
111
125
|
const connection = new web3_js.Connection(rpcUrl, 'confirmed');
|
|
112
|
-
// 2. Get fresh blockhash and set it on the transaction before signing
|
|
113
|
-
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
126
|
+
// 2. Get fresh blockhash and set it on the transaction before signing.
|
|
127
|
+
// DS3-0414: a Solana signature covers the compiled message (recentBlockhash included),
|
|
128
|
+
// so refreshing the blockhash after a sponsor / co-signer has already signed silently
|
|
129
|
+
// invalidates their signature - and this is the sponsored/fee-payer submit path, where a
|
|
130
|
+
// co-signed transaction is exactly what arrives. signTransaction only ADDS this keypair's
|
|
131
|
+
// signature (partialSign), it never re-signs the others. So only refresh the blockhash
|
|
132
|
+
// while the transaction is still unsigned; once a real signature is present, sign and
|
|
133
|
+
// submit the caller's message verbatim.
|
|
134
|
+
if (!transactionHasSignature(transaction)) {
|
|
135
|
+
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
|
|
136
|
+
// Use duck typing instead of instanceof to handle multiple @solana/web3.js versions
|
|
137
|
+
const isLegacyTransaction = 'recentBlockhash' in transaction && !('message' in transaction && 'staticAccountKeys' in transaction.message);
|
|
138
|
+
if (isLegacyTransaction) {
|
|
139
|
+
const legacyTx = transaction;
|
|
140
|
+
legacyTx.recentBlockhash = blockhash;
|
|
141
|
+
legacyTx.lastValidBlockHeight = lastValidBlockHeight;
|
|
142
|
+
// Set feePayer if not already set
|
|
143
|
+
if (!legacyTx.feePayer) {
|
|
144
|
+
legacyTx.feePayer = feePayer !== null && feePayer !== void 0 ? feePayer : this.keypair.publicKey;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
// VersionedTransaction
|
|
149
|
+
const versionedTx = transaction;
|
|
150
|
+
versionedTx.message.recentBlockhash = blockhash;
|
|
151
|
+
// Note: VersionedTransaction feePayer is set in the message at creation time
|
|
152
|
+
// and cannot be modified after creation
|
|
123
153
|
}
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
// VersionedTransaction
|
|
127
|
-
const versionedTx = transaction;
|
|
128
|
-
versionedTx.message.recentBlockhash = blockhash;
|
|
129
|
-
// Note: VersionedTransaction feePayer is set in the message at creation time
|
|
130
|
-
// and cannot be modified after creation
|
|
131
154
|
}
|
|
132
155
|
// 3. Sign the transaction
|
|
133
156
|
const signedTx = await this.signTransaction(transaction);
|
|
@@ -333,6 +356,8 @@ class EvmKeypairProvider {
|
|
|
333
356
|
this.defaultChainId = defaultChainId;
|
|
334
357
|
}
|
|
335
358
|
user() {
|
|
359
|
+
// @user.address (the Solana wallet) is null for an EVM keypair login: this
|
|
360
|
+
// principal has only an EVM address. User.address is typed `string | null`.
|
|
336
361
|
return { id: this.address, address: null, evmAddress: this.address, provider: this };
|
|
337
362
|
}
|
|
338
363
|
async createBoundedSession(sessionConfig) {
|