Skip to content

Commit 588b67d

Browse files
feat: refactor method
1 parent 5de5dd7 commit 588b67d

File tree

13 files changed

+230
-109
lines changed

13 files changed

+230
-109
lines changed

scripts/prepublish-check.js

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,22 +66,22 @@ function checkFileSize(filePath, maxSizeKB, description) {
6666
function checkPackageJson() {
6767
return check('Package.json validation', () => {
6868
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
69-
69+
7070
const requiredFields = [
7171
'name', 'version', 'main', 'module', 'types', 'files',
7272
'author', 'license', 'description', 'repository'
7373
];
74-
74+
7575
for (const field of requiredFields) {
7676
if (!pkg[field]) {
7777
throw new Error(`Missing required field: ${field}`);
7878
}
7979
}
80-
80+
8181
if (!pkg.name.startsWith('@lazorkit/')) {
8282
throw new Error('Package name must start with @lazorkit/');
8383
}
84-
84+
8585
if (pkg.license !== 'MIT') {
8686
throw new Error('License must be MIT');
8787
}
@@ -103,20 +103,20 @@ function checkTypes() {
103103
function checkDistFiles() {
104104
const distFiles = [
105105
'dist/index.js',
106-
'dist/index.esm.js',
106+
'dist/index.esm.js',
107107
'dist/index.d.ts'
108108
];
109-
109+
110110
const results = [];
111111
for (const file of distFiles) {
112112
results.push(checkFileExists(file, `Dist file exists: ${file}`));
113113
}
114-
114+
115115
// Check file sizes
116116
results.push(checkFileSize('dist/index.js', 500, 'Main bundle size check'));
117117
results.push(checkFileSize('dist/index.esm.js', 500, 'ESM bundle size check'));
118118
results.push(checkFileSize('dist/index.d.ts', 200, 'Types file size check'));
119-
119+
120120
return results;
121121
}
122122

@@ -125,29 +125,29 @@ function checkDocumentation() {
125125
'README.md',
126126
'SECURITY.md'
127127
];
128-
128+
129129
const results = [];
130130
for (const doc of docs) {
131131
results.push(checkFileExists(doc, `Documentation exists: ${doc}`));
132132
}
133-
133+
134134
return results;
135135
}
136136

137137
function checkExports() {
138138
return check('Export verification', () => {
139139
const distIndex = fs.readFileSync('dist/index.d.ts', 'utf8');
140-
140+
141141
const requiredExports = [
142142
'LazorKitProvider',
143-
'useLazorWallet',
143+
'useWallet',
144144
'useWalletStore',
145145
'WalletInfo',
146146
'ConnectOptions',
147147
'SignOptions',
148148
'LazorKitError'
149149
];
150-
150+
151151
for (const exportName of requiredExports) {
152152
if (!distIndex.includes(exportName)) {
153153
throw new Error(`Missing export: ${exportName}`);
@@ -158,7 +158,7 @@ function checkExports() {
158158

159159
function main() {
160160
log('🚀 Starting pre-publish checks...', colors.bold + colors.blue);
161-
161+
162162
const checks = [
163163
checkPackageJson(),
164164
checkSecurity(),
@@ -168,9 +168,9 @@ function main() {
168168
...checkDocumentation(),
169169
checkExports()
170170
];
171-
171+
172172
const failed = checks.filter(check => !check.success);
173-
173+
174174
if (failed.length > 0) {
175175
log('\n❌ Pre-publish checks failed!', colors.bold + colors.red);
176176
log('Please fix the issues above before publishing.', colors.red);

src/actions.ts

Lines changed: 99 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
import { asCredentialHash, LazorkitClient, getBlockchainTimestamp, SmartWalletAction } from './contract';
2323
import { getFeePayer } from './core/paymaster';
2424
import { sha256 } from 'js-sha256';
25+
import { SignAndSendTransactionPayload } from './types';
2526

2627
/**
2728
* Connects to the wallet
@@ -46,7 +47,7 @@ export const connectAction = async (
4647

4748
try {
4849
const redirectUrl = options.redirectUrl;
49-
const connectUrl = `${config.ipfsUrl}/${API_ENDPOINTS.CONNECT
50+
const connectUrl = `${config.portalUrl}/${API_ENDPOINTS.CONNECT
5051
}&redirect_url=${encodeURIComponent(redirectUrl)}`;
5152

5253
const resultUrl = await openBrowser(connectUrl, redirectUrl);
@@ -105,7 +106,7 @@ export const disconnectAction = async (set: (state: Partial<WalletStateClient>)
105106
export const signAndExecuteTransaction = async (
106107
get: () => WalletStateClient,
107108
set: (state: Partial<WalletStateClient>) => void,
108-
instructions: anchor.web3.TransactionInstruction[],
109+
payload: SignAndSendTransactionPayload,
109110
options: SignOptions
110111
) => {
111112
const { isSigning, connection, wallet, config } = get();
@@ -134,20 +135,20 @@ export const signAndExecuteTransaction = async (
134135

135136
const feePayer = await getFeePayer(config.configPaymaster.paymasterUrl, config.configPaymaster.apiKey);
136137

137-
const timestamp = await getBlockchainTimestamp(connection);
138+
const timestamp = new anchor.BN(await getBlockchainTimestamp(connection));
138139

139140
const message = await lazorProgram.buildAuthorizationMessage({
140141
action: {
141142
type: SmartWalletAction.CreateChunk,
142143
args: {
143144
policyInstruction: null,
144-
cpiInstructions: instructions,
145+
cpiInstructions: payload.instructions,
145146
}
146147
},
147148
payer: feePayer,
148149
smartWallet: new anchor.web3.PublicKey(wallet.smartWallet),
149150
passkeyPublicKey: wallet.passkeyPubkey,
150-
timestamp: new anchor.BN(timestamp),
151+
timestamp,
151152
credentialHash: asCredentialHash(
152153
Array.from(
153154
new Uint8Array(
@@ -166,16 +167,20 @@ export const signAndExecuteTransaction = async (
166167
const messageV0 = new anchor.web3.TransactionMessage({
167168
payerKey: feePayer, // PublicKey
168169
recentBlockhash: latestBlockhash.blockhash,
169-
instructions,
170+
instructions: payload.instructions,
170171
}).compileToV0Message();
171172

172173
const versionedTx = new anchor.web3.VersionedTransaction(messageV0);
173174
const base64Tx = Buffer.from(versionedTx.serialize()).toString("base64");
174175
const redirectUrl = options.redirectUrl;
175-
const signUrl = `${config.ipfsUrl}/${API_ENDPOINTS.SIGN}&message=${encodeURIComponent(
176+
let signUrl = `${config.portalUrl}/${API_ENDPOINTS.SIGN}&message=${encodeURIComponent(
176177
encodedChallenge
177178
)}&credentialId=${encodeURIComponent(wallet.credentialId)}&transaction=${encodeURIComponent(base64Tx)}&redirect_url=${encodeURIComponent(redirectUrl)}`;
178179

180+
if (payload.transactionOptions?.clusterSimulation) {
181+
signUrl += `&clusterSimulation=${payload.transactionOptions.clusterSimulation}`;
182+
}
183+
179184
await openSignBrowser(
180185
signUrl,
181186
redirectUrl,
@@ -196,11 +201,12 @@ export const signAndExecuteTransaction = async (
196201
type: SmartWalletAction.CreateChunk,
197202
args: {
198203
policyInstruction: null,
199-
cpiInstructions: instructions,
204+
cpiInstructions: payload.instructions,
200205
}
201206
},
202207
browserResult,
203-
options
208+
options,
209+
payload.transactionOptions
204210
);
205211
options?.onSuccess?.(txnSignature);
206212
} catch (error) {
@@ -228,3 +234,87 @@ export const signAndExecuteTransaction = async (
228234
set({ isSigning: false });
229235
}
230236
};
237+
238+
/**
239+
* Sign a message (arbitrary string or bytes)
240+
*
241+
* @param get - Zustand state getter function
242+
* @param set - Zustand state setter function
243+
* @param message - Message to sign (string)
244+
* @param options - Signing options with callbacks
245+
*/
246+
export const signMessageAction = async (
247+
get: () => WalletStateClient,
248+
set: (state: Partial<WalletStateClient>) => void,
249+
message: string,
250+
options: SignOptions
251+
) => {
252+
const { isSigning, wallet, config } = get();
253+
if (isSigning) {
254+
return;
255+
}
256+
257+
if (!wallet) {
258+
const error = new SigningError('No wallet connected');
259+
logger.error('Sign message failed: No wallet connected');
260+
options?.onFail?.(error);
261+
return;
262+
}
263+
264+
set({ isSigning: true, error: null });
265+
266+
try {
267+
const redirectUrl = options.redirectUrl;
268+
// For signMessage, we pass the message directly.
269+
// The portal will treat 'transaction' param as message if it's not a valid transaction or if action is 'sign'
270+
// But better to use a specific param or just rely on 'transaction' param being the message container as per portal logic
271+
// PortalCommunicator: transaction: urlParams.get('transaction')
272+
// TransactionReview: portalParams.transaction || portalParams.message
273+
// Let's use 'message' param for clarity if portal supports it, checking portal-communicator.ts:
274+
// message: urlParams.get('message') || ''
275+
// So we should use 'message' param.
276+
277+
// If message is not base64, we might want to encode it?
278+
// User passes string. Let's pass it as is, or base64 encoded?
279+
// Portal expects 'message' to be displayed. If we want it to be readable, pass as string.
280+
// If it's bytes, pass base64?
281+
// The type signature says `message: string`. Let's assume readable string.
282+
283+
const signUrl = `${config.portalUrl}/${API_ENDPOINTS.SIGN}&message=${encodeURIComponent(
284+
message
285+
)}&credentialId=${encodeURIComponent(wallet.credentialId)}&redirect_url=${encodeURIComponent(redirectUrl)}`;
286+
287+
await openSignBrowser(
288+
signUrl,
289+
redirectUrl,
290+
async (urlResult) => {
291+
try {
292+
const authResult = handleBrowserResult(urlResult);
293+
const signature = authResult.signature;
294+
const signedPayload = authResult.message;
295+
options?.onSuccess?.({ signature, signedPayload });
296+
} catch (error) {
297+
const err = error instanceof Error ? error : new Error(String(error));
298+
logger.error('Sign message browser result processing failed:', err, { urlResult });
299+
set({ error: err });
300+
options?.onFail?.(err);
301+
}
302+
},
303+
(error) => {
304+
logger.error('Sign message browser failed:', error, { signUrl, redirectUrl });
305+
set({ error });
306+
options?.onFail?.(error);
307+
}
308+
);
309+
} catch (error: unknown) {
310+
logger.error('Sign message action failed:', error, {
311+
smartWallet: wallet?.smartWallet,
312+
redirectUrl: options.redirectUrl,
313+
});
314+
const err = error instanceof Error ? error : new SigningError('Unknown error');
315+
set({ error: err });
316+
options?.onFail?.(err);
317+
} finally {
318+
set({ isSigning: false });
319+
}
320+
};

src/config/defaults.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export const DEFAULTS = {
2-
IPFS_URL: 'https://portal.lazor.sh',
2+
PORTAL_URL: 'https://portal.lazor.sh',
33
PAYMASTER_URL: 'https://kora.devnet.lazorkit.com',
44
RPC_ENDPOINT: 'https://api.devnet.solana.com',
55
} as const;

src/contract/transaction.ts

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -24,34 +24,6 @@ export function prependComputeUnitLimit(
2424
return [createComputeUnitLimitInstruction(computeUnitLimit), ...instructions];
2525
}
2626

27-
/**
28-
* Builds a versioned transaction (v0) from instructions
29-
*/
30-
export async function buildVersionedTransaction(
31-
connection: anchor.web3.Connection,
32-
payer: anchor.web3.PublicKey,
33-
instructions: anchor.web3.TransactionInstruction[]
34-
): Promise<anchor.web3.VersionedTransaction> {
35-
const result = await buildTransaction(connection, payer, instructions, {
36-
useVersionedTransaction: true,
37-
});
38-
return result.transaction as anchor.web3.VersionedTransaction;
39-
}
40-
41-
/**
42-
* Builds a legacy transaction from instructions
43-
*/
44-
export async function buildLegacyTransaction(
45-
connection: anchor.web3.Connection,
46-
payer: anchor.web3.PublicKey,
47-
instructions: anchor.web3.TransactionInstruction[]
48-
): Promise<anchor.web3.Transaction> {
49-
const result = await buildTransaction(connection, payer, instructions, {
50-
useVersionedTransaction: false,
51-
});
52-
return result.transaction as anchor.web3.Transaction;
53-
}
54-
5527
/**
5628
* Combines authentication verification instruction with smart wallet instructions
5729
*/
@@ -94,8 +66,7 @@ export async function buildTransaction(
9466
options: TransactionBuilderOptions = {}
9567
): Promise<TransactionBuilderResult> {
9668
const {
97-
useVersionedTransaction,
98-
addressLookupTable,
69+
addressLookupTables,
9970
recentBlockhash: customBlockhash,
10071
computeUnitLimit,
10172
} = options;
@@ -105,18 +76,14 @@ export async function buildTransaction(
10576
instructions,
10677
computeUnitLimit
10778
);
108-
109-
// Auto-detect: if addressLookupTable is provided, use versioned transaction
110-
const shouldUseVersioned = useVersionedTransaction ?? !!addressLookupTable;
79+
const lookupTables = addressLookupTables ? addressLookupTables : [];
11180

11281
// Get recent blockhash
11382
const recentBlockhash =
11483
customBlockhash || (await connection.getLatestBlockhash()).blockhash;
11584

116-
if (shouldUseVersioned) {
85+
if (lookupTables.length > 0) {
11786
// Build versioned transaction
118-
const lookupTables = addressLookupTable ? [addressLookupTable] : [];
119-
12087
const message = new anchor.web3.TransactionMessage({
12188
payerKey: payer,
12289
recentBlockhash,

src/contract/types.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,8 @@ export interface PasskeySignature {
121121
}
122122

123123
export interface TransactionBuilderOptions {
124-
/** Use versioned transaction (v0) instead of legacy */
125-
readonly useVersionedTransaction?: boolean;
126-
/** Address lookup table for versioned transactions */
127-
readonly addressLookupTable?: anchor.web3.AddressLookupTableAccount;
124+
/** Address lookup tables for versioned transactions */
125+
readonly addressLookupTables?: anchor.web3.AddressLookupTableAccount[];
128126
/** Custom recent blockhash (if not provided, fetched from connection) */
129127
readonly recentBlockhash?: string;
130128
/** Compute unit limit for transaction */

src/core/browser/parseResult.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ export const handleBrowserResult = (url: string): BrowserResult => {
2222
const signature = parsed.searchParams.get('signature');
2323
const clientDataJsonBase64 = parsed.searchParams.get('clientDataJSONReturn');
2424
const authenticatorDataBase64 = parsed.searchParams.get('authenticatorDataReturn');
25-
26-
if (!signature || !clientDataJsonBase64 || !authenticatorDataBase64) {
25+
const message = parsed.searchParams.get('msg');
26+
if (!signature || !clientDataJsonBase64 || !authenticatorDataBase64 || !message) {
2727
logger.error('Browser result failed: missing signature data', {
2828
url,
2929
hasSignature: !!signature,
3030
hasClientData: !!clientDataJsonBase64,
3131
hasAuthData: !!authenticatorDataBase64,
32+
hasMessage: !!message,
3233
});
3334
throw new Error('Missing signature or message from redirect');
3435
}
@@ -37,6 +38,7 @@ export const handleBrowserResult = (url: string): BrowserResult => {
3738
signature,
3839
clientDataJsonBase64,
3940
authenticatorDataBase64,
41+
message,
4042
};
4143
} catch (error) {
4244
logger.error('Failed to handle browser result:', error, { url });

0 commit comments

Comments
 (0)