
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@flipflop-sdk/node
Advanced tools
A comprehensive Node.js SDK for FlipFlop token operations on Solana. This library provides programmatic access to token launches, Universal Referral Code (URC) management, batch minting operations, and metadata management.
npm install @flipflop-sdk/node
# or
yarn add @flipflop-sdk/node
const {
launchToken,
mintToken,
setUrc,
getMintData,
getUrcData,
getSystemConfig,
generateMetadataUri,
validateImageFile,
loadKeypairFromBase58
} = require('@flipflop-sdk/node');
async function example() {
// Generate metadata URI first
const metadataResult = await generateMetadataUri({
rpc: 'https://api.devnet.solana.com',
name: 'My Token',
symbol: 'MTK',
description: 'A sample token for demonstration',
imagePath: './path/to/token-logo.png'
});
if (metadataResult.success) {
console.log('Metadata URI:', metadataResult.metadataUrl);
console.log('Image URI:', metadataResult.imageUrl);
}
// Launch a new token with metadata
const launchResult = await launchToken({
rpc: 'https://api.devnet.solana.com',
name: 'My Token',
symbol: 'MTK',
tokenType: 'meme', // or 'standard'
uri: metadataResult.metadataUrl, // Use generated metadata URI
keypairBs58: 'your-base58-private-key'
});
console.log('Token launched:', launchResult.mintAddress.toString());
console.log('Transaction:', launchResult.transactionHash);
// Set URC code
const urcResult = await setUrc({
rpc: 'https://api.devnet.solana.com',
mint: launchResult.mintAddress.toString(),
urc: 'MYCODE2024',
keypairBs58: 'your-base58-private-key'
});
console.log('URC set:', urcResult.urc);
console.log('Usage count:', urcResult.usageCount);
// Mint tokens (pass a Keypair as `minter`)
const minter = loadKeypairFromBase58('minter-base58-private-key');
const mintResult = await mintToken({
rpc: 'https://api.devnet.solana.com',
mint: launchResult.mintAddress.toString(),
urc: 'MYCODE2024',
minter
});
if (mintResult.success) {
console.log('Mint successful:', mintResult.data?.tx);
console.log('Token account:', mintResult.data?.tokenAccount.toString());
}
// Get token information
const tokenInfo = await getMintData({
rpc: 'https://api.devnet.solana.com',
mint: launchResult.mintAddress.toString()
});
console.log('Token name:', tokenInfo.name);
console.log('Token symbol:', tokenInfo.symbol);
console.log('Current supply:', tokenInfo.currentSupply);
console.log('Max supply:', tokenInfo.maxSupply);
// Get URC information
const urcInfo = await getUrcData({
rpc: 'https://api.devnet.solana.com',
urc: 'MYCODE2024'
});
console.log('URC valid:', urcInfo.isValid);
console.log('Referrer:', urcInfo.referrerMain.toString());
console.log('Usage count:', urcInfo.usageCount);
// Get system configuration
const systemConfig = await getSystemConfig({
rpc: 'https://api.devnet.solana.com'
});
console.log('System manager:', systemConfig.systemManagerAccount.toString());
console.log('Protocol fee rate:', systemConfig.graduateFeeRate);
}
### TypeScript Usage
```typescript
import {
launchToken,
mintToken,
setUrc,
getMintData,
getUrcData,
getSystemConfig,
generateMetadataUri,
validateImageFile,
LaunchTokenOptions,
LaunchTokenResponse,
MintTokenOptions,
MintTokenResponse,
SetUrcOptions,
SetUrcResponse,
GetMintDataOptions,
GetMintDataResponse,
GetUrcDataOptions,
GetUrcDataResponse,
SystemConfigAccountOptions,
SystemConfigAccountData,
GenerateMetadataUriOptions,
MetadataUploadResponse,
loadKeypairFromBase58
} from '@flipflop-sdk/node';
async function example() {
// Generate metadata with type safety
const metadataOptions: GenerateMetadataUriOptions = {
rpc: 'https://api.devnet.solana.com',
name: 'TypeScript Token',
symbol: 'TST',
description: 'A TypeScript token example',
imagePath: './assets/logo.png'
};
const metadataResult: MetadataUploadResponse = await generateMetadataUri(metadataOptions);
// Launch token with type safety
const launchOptions: LaunchTokenOptions = {
rpc: 'https://api.devnet.solana.com',
name: 'TypeScript Token',
symbol: 'TST',
tokenType: 'standard',
uri: metadataResult.metadataUrl,
keypairBs58: 'your-base58-private-key'
};
const launchResult: LaunchTokenResponse = await launchToken(launchOptions);
// Set URC with type safety
const setUrcOptions: SetUrcOptions = {
rpc: 'https://api.devnet.solana.com',
mint: launchResult.mintAddress.toString(),
urc: 'TST_CODE',
keypairBs58: 'your-base58-private-key'
};
const urcResult: SetUrcResponse = await setUrc(setUrcOptions);
// Mint tokens with type safety (note: `minter` is a Keypair)
const mintOptions: MintTokenOptions = {
rpc: 'https://api.devnet.solana.com',
mint: launchResult.mintAddress, // PublicKey
urc: 'TST_CODE',
minter: loadKeypairFromBase58('minter-base58-private-key')
};
const mintResult = await mintToken(mintOptions);
}
generateMetadataUri(options: GenerateMetadataUriOptions): Promise<MetadataUploadResponse>NEW - Generate metadata URI by uploading image and metadata to Irys network.
Parameters:
interface GenerateMetadataUriOptions {
rpc: string; // RPC endpoint URL
name: string; // Token name
symbol: string; // Token symbol
description?: string; // Token description (optional)
imagePath: string; // Path to image file
}
Returns:
interface MetadataUploadResponse {
success: boolean;
metadataUrl?: string; // Irys gateway URL for metadata JSON
imageUrl?: string; // Irys gateway URL for uploaded image
error?: string; // Error message if upload failed
}
Image Requirements:
Example:
const result = await generateMetadataUri({
rpc: 'https://api.devnet.solana.com',
name: 'My Awesome Token',
symbol: 'MAT',
description: 'This is an awesome token for the community',
imagePath: './assets/token-logo.png'
});
if (result.success) {
console.log('Metadata URL:', result.metadataUrl);
console.log('Image URL:', result.imageUrl);
} else {
console.error('Upload failed:', result.error);
}
validateImageFile(imagePath: string): { valid: boolean; error?: string }NEW - Validate image file before upload.
Parameters:
imagePath: Path to the image fileReturns:
valid: Boolean indicating if file is validerror: Error message if validation failsExample:
const validation = validateImageFile('./logo.png');
if (!validation.valid) {
console.error('Image validation failed:', validation.error);
}
launchToken(options: LaunchTokenOptions): Promise<LaunchTokenResponse>Launch a new token with specified parameters.
Parameters:
interface LaunchTokenOptions {
rpc: string; // RPC endpoint URL
name: string; // Token name
symbol: string; // Token symbol
tokenType: string; // 'meme' or 'standard'
uri?: string; // Metadata URI (optional, use generateMetadataUri)
keypairBs58?: string; // Base58 encoded private key
keypairFile?: string; // Path to keypair file
}
Returns:
interface LaunchTokenResponse {
success: boolean;
transactionHash: string;
mintAddress: PublicKey;
configAddress: PublicKey;
metadata: TokenMetadata;
configuration: ConfigAccountData;
}
Example:
const result = await launchToken({
rpc: 'https://api.devnet.solana.com',
name: 'My Token',
symbol: 'MTK',
tokenType: 'meme',
uri: 'https://gateway.irys.xyz/your-metadata-id', // From generateMetadataUri
keypairBs58: 'your-base58-private-key'
});
setUrc(options: SetUrcOptions): Promise<SetUrcResponse>Set Universal Referral Code for a token.
Parameters:
interface SetUrcOptions {
rpc: string; // RPC endpoint URL
urc: string; // Universal Referral Code
mint: string; // Token mint address
keypairBs58?: string; // Base58 encoded private key
keypairFile?: string; // Path to keypair file
}
Returns:
interface SetUrcResponse {
transactionHash: string;
urc: string;
mint: PublicKey;
referrer: PublicKey;
referrerTokenAccount: PublicKey;
codeHash: PublicKey;
usageCount: number;
activatedAt: number;
}
Example:
const result = await setUrc({
rpc: 'https://api.devnet.solana.com',
mint: 'TokenMintAddress',
urc: 'UNIQUECODE',
keypairBs58: 'your-base58-private-key'
});
mintToken(options: MintTokenOptions): Promise<ApiResponse<MintTokenResponse>>Mint tokens using a URC code.
Parameters:
interface MintTokenOptions {
rpc: string; // RPC endpoint URL
minter: Keypair; // The minter's Keypair (wallet that pays & receives)
mint: PublicKey; // Token mint address (a string is also accepted; it will be converted)
urc: string; // Universal Referral Code
lookupTableAccount?: PublicKey; // Optional; defaults to network LUT from CONFIGS
skipPreflight?: boolean; // Optional; skip transaction preflight checks (default: false)
}
Returns:
// Generic API wrapper used across the SDK
interface ApiResponse<T> {
success: boolean;
data?: T;
message?: string;
}
// Mint call payload on success
interface MintTokenResponse {
tx: string; // Transaction signature
owner: PublicKey; // Minter's public key
tokenAccount: PublicKey; // Associated token account that received the mint
}
Promise<ApiResponse<MintTokenResponse>>.message is "Mint succeeded" and data contains tx, owner, and tokenAccount.Example:
const { loadKeypairFromBase58 } = require('@flipflop-sdk/node');
const minter = loadKeypairFromBase58('minter-base58-private-key');
const result = await mintToken({
rpc: 'https://api.devnet.solana.com',
mint: 'TokenMintAddress', // string is accepted; SDK will convert internally
urc: 'UNIQUECODE',
minter,
skipPreflight: true // Optional: skip preflight checks for faster execution
});
if (result.success) {
console.log('tx:', result.data.tx);
console.log('owner:', result.data.owner.toBase58());
console.log('tokenAccount:', result.data.tokenAccount.toBase58());
} else {
console.error('Mint failed:', result.message);
}
Notes:
urc must be a valid and active referral code on-chain; the SDK derives and verifies the referral PDA internally.lookupTableAccount is not provided, the SDK uses the preconfigured LUT for the detected network (via CONFIGS).{ success: false, message: 'Mint operation failed: ...' }.refundToken(options: RefundTokenOptions): Promise<ApiResponse<RefundTokenResponse>>Request a refund for the caller's tokens according to protocol rules.
Parameters:
interface RefundTokenOptions {
rpc: string; // RPC endpoint URL
mint: PublicKey; // Token mint address (a string is also accepted; it will be converted)
owner: Keypair; // The owner's Keypair (the account that holds tokens to refund)
}
Returns:
interface ApiResponse<T> {
success: boolean;
data?: T;
message?: string;
}
interface RefundTokenResponse {
tx: string; // Transaction signature
}
Example (JavaScript):
const { refundToken, loadKeypairFromBase58 } = require('@flipflop-sdk/node');
const owner = loadKeypairFromBase58('owner-base58-private-key');
const result = await refundToken({
rpc: 'https://api.devnet.solana.com',
mint: 'TokenMintAddress', // string is accepted; SDK will convert internally
owner
});
if (result.success) {
console.log('Refund tx:', result.data.tx);
} else {
console.error('Refund failed:', result.message);
}
Example (TypeScript):
import { refundToken, RefundTokenOptions } from '@flipflop-sdk/node';
import { PublicKey } from '@solana/web3.js';
const options: RefundTokenOptions = {
rpc: 'https://api.devnet.solana.com',
mint: new PublicKey('TokenMintAddress'),
owner: loadKeypairFromBase58('owner-base58-private-key'),
};
const res = await refundToken(options);
Notes:
{ success: true, data: { tx } }.message may contain strings like "Error refunding..." or "Something went wrong but you have refund successfully". In rare cases you may see "Mint operation failed: ..." from a shared error handler.getMintData(options: GetMintDataOptions): Promise<GetMintDataResponse>Get detailed information about a token.
Parameters:
interface GetMintDataOptions {
rpc: string; // RPC endpoint URL
mint: string; // Token mint address
}
Returns:
interface GetMintDataResponse {
mint: PublicKey;
name: string;
symbol: string;
uri: string;
isMutable: boolean;
configAccount: PublicKey;
admin: PublicKey;
tokenVault: PublicKey;
feeRate: number;
targetEras: number;
initialMintSize: number;
epochesPerEra: number;
targetSecondsPerEpoch: number;
reduceRatio: number;
maxSupply: number;
liquidityTokensRatio: number;
currentSupply: number;
liquidityTokensSupply: number;
minterTokensSupply: number;
}
Example:
const info = await getMintData({
rpc: 'https://api.devnet.solana.com',
mint: 'TokenMintAddress'
});
getUrcData(options: GetUrcDataOptions): Promise<GetUrcDataResponse>Get information about a URC code.
Parameters:
interface GetUrcDataOptions {
rpc: string; // RPC endpoint URL
urc: string; // Universal Referral Code
}
Returns:
interface GetUrcDataResponse {
urc: string;
codeHash: PublicKey;
mint: PublicKey;
referrerMain: PublicKey;
referrerAta: PublicKey;
usageCount: number;
activeTimestamp: number;
isValid: boolean;
}
Example:
const info = await getUrcData({
rpc: 'https://api.devnet.solana.com',
urc: 'UNIQUECODE'
});
getSystemConfig(options: SystemConfigAccountOptions): Promise<SystemConfigAccountData>Get system configuration information.
Parameters:
interface SystemConfigAccountOptions {
rpc: string; // RPC endpoint URL
}
Returns:
interface SystemConfigAccountData {
systemConfigAccount: PublicKey;
systemManagerAccount: PublicKey;
admin: PublicKey;
count: number;
referralUsageMaxCount: number;
protocolFeeAccount: PublicKey;
refundFeeRate: number;
referrerResetIntervalSeconds: number;
updateMetadataFee: number;
customizedDeployFee: number;
initPoolWsolAmount: number;
graduateFeeRate: number;
minGraduateFee: number;
raydiumCpmmCreateFee: number;
}
Example:
const config = await getSystemConfig({
rpc: 'https://api.devnet.solana.com'
});
All utility functions and constants can also be imported from the library:
import {
initProvider,
initProviderNoSigner,
loadKeypairFromBase58,
loadKeypairFromFile,
validateImageFile,
CONFIGS,
NetworkType
} from '@flipflop-sdk/node';
mainnet: Production networkdevnet: Development networklocal: Local validatormeme: Aggressive parameters for community tokensstandard: Conservative parameters for utility tokenshttps://api.mainnet-beta.solana.comhttps://api.devnet.solana.comhttp://127.0.0.1:8899https://api-dev.flipflop.plus/api/irys/uploadhttps://api.flipflop.plus/api/irys/uploadhttps://gateway.irys.xyz/The SDK supports two methods for providing keypairs:
const result = await launchToken({
// ... other options
keypairBs58: 'your-base58-encoded-private-key'
});
const result = await launchToken({
// ... other options
keypairFile: './path/to/keypair.json'
});
All functions throw descriptive errors for validation and runtime issues:
try {
// Validate image first
const validation = validateImageFile('./logo.png');
if (!validation.valid) {
throw new Error(`Image validation failed: ${validation.error}`);
}
// Generate metadata
const metadataResult = await generateMetadataUri({
rpc: 'https://api.devnet.solana.com',
name: 'My Token',
symbol: 'MTK',
description: 'My awesome token',
imagePath: './logo.png'
});
if (!metadataResult.success) {
throw new Error(`Metadata upload failed: ${metadataResult.error}`);
}
// Launch token
const result = await launchToken({
rpc: 'https://api.devnet.solana.com',
name: 'My Token',
symbol: 'MTK',
tokenType: 'meme',
uri: metadataResult.metadataUrl,
keypairBs58: 'invalid-key'
});
} catch (error) {
console.error('Operation failed:', error.message);
}
The SDK uses the following key dependencies:
Core Solana Libraries:
@coral-xyz/anchor (0.31.1) - Anchor framework for Solana@solana/web3.js (^1.98.0) - Solana Web3 JavaScript API@solana/spl-token (^0.4.9) - SPL Token library@solana/spl-token-metadata (^0.1.6) - Token metadata standardUtility Libraries:
axios (^1.11.0) - HTTP client for API requestsform-data (^4.0.4) - Multipart form data for file uploadsbn.js (5.2.1) - Big number arithmeticbs58 (^6.0.0) - Base58 encoding/decodingdecimal.js (^10.4.3) - Decimal arithmeticsleep-promise (^9.1.0) - Promise-based sleep utility# Install dependencies
npm install
# Build the library
npm run build
# Run tests
npm test
# Watch mode for development
npm run dev
# Run all tests
npm test
# Run specific test file
npm test launch.test.ts
# Run tests with coverage
npm run test:coverage
# Run metadata tests (requires network connection)
npm test metadata.test.ts
Note: Metadata tests make real API calls to devnet and require internet connection.
const {
generateMetadataUri,
launchToken,
setUrc,
mintToken
} = require('@flipflop-sdk/node');
async function completeFlowWithMetadata() {
const rpc = 'https://api.devnet.solana.com';
const creatorKey = 'creator-base58-private-key';
const minterKey = 'minter-base58-private-key';
// 1. Generate metadata URI
console.log('Step 1: Generating metadata...');
const metadata = await generateMetadataUri({
rpc,
name: 'Demo Token',
symbol: 'DEMO',
description: 'A demonstration token with custom metadata',
imagePath: './assets/demo-logo.png'
});
if (!metadata.success) {
throw new Error(`Metadata generation failed: ${metadata.error}`);
}
console.log('Metadata URL:', metadata.metadataUrl);
console.log('Image URL:', metadata.imageUrl);
// 2. Launch token with metadata
console.log('Step 2: Launching token...');
const launch = await launchToken({
rpc,
name: 'Demo Token',
symbol: 'DEMO',
tokenType: 'standard',
uri: metadata.metadataUrl,
keypairBs58: creatorKey
});
console.log('Token launched:', launch.mintAddress.toString());
// 3. Set URC
console.log('Step 3: Setting URC...');
const urc = await setUrc({
rpc,
mint: launch.mintAddress.toString(),
urc: 'DEMO_CODE',
keypairBs58: creatorKey
});
console.log('URC set:', urc.urc);
// 4. Mint tokens
console.log('Step 4: Minting tokens...');
const mint = await mintToken({
rpc,
mint: launch.mintAddress.toString(),
urc: 'DEMO_CODE',
keypairBs58: minterKey
});
console.log('Mint successful:', mint.success);
return {
metadata: metadata.metadataUrl,
mint: launch.mintAddress.toString(),
urc: urc.urc,
transaction: mint.data?.tx
};
}
async function batchMintWithValidation() {
const imagePaths = [
'./assets/logo1.png',
'./assets/logo2.jpg',
'./assets/logo3.gif'
];
// Validate all images first
const validations = imagePaths.map(path => ({
path,
validation: validateImageFile(path)
}));
const validImages = validations.filter(v => v.validation.valid);
const invalidImages = validations.filter(v => !v.validation.valid);
if (invalidImages.length > 0) {
console.warn('Invalid images found:');
invalidImages.forEach(img => {
console.warn(`- ${img.path}: ${img.validation.error}`);
});
}
// Process valid images
const metadataPromises = validImages.map((img, index) =>
generateMetadataUri({
rpc: 'https://api.devnet.solana.com',
name: `Batch Token ${index + 1}`,
symbol: `BT${index + 1}`,
description: `Batch generated token ${index + 1}`,
imagePath: img.path
})
);
const metadataResults = await Promise.all(metadataPromises);
console.log('Batch metadata generation results:', metadataResults);
return metadataResults;
}
| CLI Command | SDK Function |
|---|---|
flipflop launch | launchToken() |
flipflop set-urc | setUrc() |
flipflop mint | mintToken() |
flipflop display-mint | getMintData() |
flipflop display-urc | getUrcData() |
| NEW | generateMetadataUri() |
| NEW | validateImageFile() |
CLI usage:
flipflop launch --name "MyToken" --symbol "MTK" --keypair-file ./keypair.json --rpc https://api.devnet.solana.com
SDK usage:
const { generateMetadataUri, launchToken } = require('@flipflop-sdk/node');
// Generate metadata first (new capability)
const metadata = await generateMetadataUri({
rpc: 'https://api.devnet.solana.com',
name: 'MyToken',
symbol: 'MTK',
description: 'My awesome token',
imagePath: './logo.png'
});
// Launch with metadata
const result = await launchToken({
name: 'MyToken',
symbol: 'MTK',
tokenType: 'standard',
uri: metadata.metadataUrl,
rpc: 'https://api.devnet.solana.com',
keypairFile: './keypair.json'
});
Keypair.fromSeed() for deterministic key generationFull TypeScript support with comprehensive type definitions:
import {
LaunchTokenOptions,
LaunchTokenResponse,
MintTokenOptions,
MintTokenResponse,
SetUrcOptions,
SetUrcResponse,
GetMintDataOptions,
GetMintDataResponse,
GetUrcDataOptions,
GetUrcDataResponse,
SystemConfigAccountOptions,
SystemConfigAccountData,
GenerateMetadataUriOptions,
MetadataUploadResponse,
MetadataParams,
TokenMetadata,
ConfigAccountData,
NetworkType
} from '@flipflop-sdk/node';
MIT License - see LICENSE file for details.
FAQs
FlipFlop Node.js SDK for programmatic token operations
We found that @flipflop-sdk/node demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.