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