I'm developing a Solana token creation platform with Phantom Wallet integration for wallet management. I'm using the solana-kite framework by helius-labs to handle token creation and minting, but I'm hitting a roadblock with an error in the browser console when attempting to create a token:
Console errors 🐛🐛🐛
token-manager.js:44 Creating token with data: {data: {…}}
token-manager.js:65 Error creating token: RangeError: The number NaN cannot be converted to a BigInt because it is not an integer
at BigInt (<anonymous>)
at sendTransactionFromInstructions (transactions.ts:58:24)
at async Object.createTokenMint (tokens.ts:274:5)
at async Object.createAndMintToken (token-manager.js:47:33)
The error suggests an issue with a NaN value being passed where a BigInt is expected, likely in the token creation parameters. How can I properly set up token creation and minting with Solana Kite, ensuring the input data is correctly formatted to avoid this error? Any guidance on debugging or structuring the integration would be appreciated.
--- vite.config.js
{
"private": true,
"type": "module",
"scripts": {
"build": "vite build",
"dev": "vite"
},
"devDependencies": {
"@tailwindcss/vite": "^4.0.0",
"axios": "^1.8.2",
"concurrently": "^9.0.1",
"laravel-vite-plugin": "^1.2.0",
"tailwindcss": "^4.0.0",
"vite": "^6.2.4"
},
"dependencies": {
"@solana/webcrypto-ed25519-polyfill": "^2.1.0",
"solana-kite": "^1.1.1"
}
}
--- resources\js\app.js
import "./bootstrap";
import "./wallet-manager.js";
import "./token-manager.js";
--- resources\js\wallet-manager.js
/**
* Manages Phantom Wallet interactions and dispatches Livewire events.
* Acts as a signer with address, keypair, and signing methods.
*/
const walletManager = {
/**
* Getter for the Phantom wallet instance if available.
*
* @returns {Object|null} Phantom wallet instance or null if unavailable.
*/
get walletManager() {
return window.phantom?.solana?.isPhantom ? window.phantom.solana : null;
},
/**
* Getter to check if the wallet is connected.
*
* @returns {boolean} True if the wallet is connected.
*/
get isConnected() {
return this.walletManager?.isConnected ?? false;
},
/**
* Getter for the connected wallet's public key as a string.
*
* @returns {string|null} The public key string or null if unavailable.
*/
get publicKey() {
return this.walletManager?.publicKey?.toString() ?? null;
},
/**
* Getter for the wallet's public key address.
*
* @returns {string|null} The public key string or null if unavailable.
*/
get address() {
return this.publicKey;
},
/**
* Getter for the keypair. Returns null since Phantom Wallet does not expose the private key.
*
* @returns {null} Always null, as the keypair is managed internally by the wallet.
*/
get keypair() {
return null; // Phantom Wallet does not provide access to the private key
},
/**
* Signs an array of messages using the connected wallet.
*
* @param {Uint8Array[]} messages - The messages to sign.
* @returns {Promise<Uint8Array[]>} An array of signatures.
* @throws {Error} If the wallet is not connected or signing fails.
*/
async signMessages(messages) {
if (!this.isConnected) {
throw new Error("Wallet not connected");
}
const signatureObjects = await Promise.all(
messages.map((message) => this.walletManager.signMessage(message))
);
return signatureObjects.map((obj) => obj.signature);
},
/**
* Signs an array of transactions using the connected wallet.
*
* @param {Transaction[]} transactions - The transactions to sign.
* @returns {Promise<Transaction[]>} An array of signed transactions.
* @throws {Error} If the wallet is not connected or signing fails.
*/
async signTransactions(transactions) {
// Console log the transactions for debugging purposes
console.log("Signing transactions:", transactions);
// Check if the wallet is connected
if (!this.isConnected) {
throw new Error("Wallet not connected");
}
// Return the signed transactions using the wallet manager's signAllTransactions method
return await this.walletManager.signAllTransactions(transactions);
},
/**
* Dispatches a Livewire event indicating a successful wallet connection.
*/
dispatchConnected() {
Livewire.dispatch("wallet-connected", {
data: { publicKey: this.publicKey },
});
},
/**
* Dispatches a Livewire event when an error occurs.
*
* @param {Error} error - The error object to dispatch.
*/
dispatchError(error) {
Livewire.dispatch("wallet-error", {
error: { message: error.message || "Unknown wallet error" },
});
},
/**
* Dispatches a Livewire event indicating that a Phantom wallet has been detected.
*/
dispatchWalletDetected() {
Livewire.dispatch("wallet-detected");
},
/**
* Detects the presence of the Phantom wallet and dispatches an event if found.
*/
detectWallet() {
if (this.walletManager) {
this.dispatchWalletDetected();
}
},
/**
* Initiates a connection to the Phantom wallet.
*/
async connect() {
try {
await this.walletManager.connect();
localStorage.setItem("walletManager", "Phantom");
this.dispatchConnected();
} catch (error) {
this.dispatchError(error);
}
},
/**
* Disconnects from the Phantom wallet and dispatches appropriate events.
*/
async disconnect() {
try {
await this.walletManager.disconnect();
localStorage.removeItem("walletManager");
Livewire.dispatch("wallet-disconnected");
} catch (error) {
this.dispatchError(error);
}
},
/**
* Automatically attempts to connect the wallet if previously connected.
*/
async autoConnect() {
if (this.walletManager && localStorage.walletManager === "Phantom") {
await this.connect();
}
},
};
// Event listeners
Livewire.on("detect-wallet", () => walletManager.detectWallet());
Livewire.on("connect-wallet", () => walletManager.connect());
Livewire.on("disconnect-wallet", () => walletManager.disconnect());
document.addEventListener("livewire:init", () => walletManager.autoConnect());
// Expose for testing
window.walletManager = walletManager;
export default walletManager;
--- resources\js\token-manager.js
import { install } from "@solana/webcrypto-ed25519-polyfill";
import walletManager from "./wallet-manager.js";
import { connect } from "solana-kite";
// Install the WebCrypto polyfill for Ed25519.
install();
// Establish a connection to the Solana network.
const connection = connect("devnet");
// Define tokenManager as an object to encapsulate token-related operations.
const tokenManager = {
/**
* Fetches the SOL balance and updates the UI.
* @returns {Promise<number|null>} Balance in SOL, or console error.
*/
async getBalance() {
try {
// Get the lamport balance of the connected wallet.
const lamports = await connection.getLamportBalance(
walletManager.publicKey
);
// Dispatch a Livewire event with the balance data.
Livewire.dispatch("wallet-balance", {
data: {
balance: (Number(lamports) / 1e9).toFixed(2),
},
});
} catch (error) {
// Handle errors while fetching balance
console.error("Error fetching balance:", error.message);
}
},
/**
* Creates and mints a new token using the provided token data.
* @param {Object} tokenData - The data for the new token.
* @returns {Promise<void>} Resolves when the token is created and minted.
*/
async createAndMintToken(tokenData) {
try {
// Console log the token data for debugging purposes.
console.log("Creating token with data:", tokenData);
// Create a new token mint using the connection and wallet manager.
const mintAddress = await connection.createTokenMint({
mintAuthority: walletManager,
decimals: tokenData.data.tokenDecimals,
name: tokenData.data.tokenName,
symbol: tokenData.data.tokenSymbol,
uri: tokenData.data.metadataUri,
additionalMetadata: {
description: tokenData.data.tokenDescription,
website: tokenData.data.tokenWebsite || null,
},
});
// Log the mint address for debugging purposes.
console.log("Mint address:", mintAddress);
// Dispatch a Livewire event indicating the token has been created.
Livewire.dispatch("token-created", { data: tokenData });
} catch (error) {
console.error("Error creating token:", error);
}
},
};
// Listen for the Livewire 'wallet-connected' event to fetch the balance.
Livewire.on("wallet-connected", () => tokenManager.getBalance());
// Listen for the Livewire 'create-token' event to create and mint a new token.
Livewire.on("create-token", (tokenData) =>
tokenManager.createAndMintToken(tokenData)
);
// Export tokenManager as the default export for use in other modules.
export default tokenManager;