Kit Plugin

@solana/surfpool/kit εκτελεί ένα Surfnet — ένα τοπικό, συμβατό με Solana δίκτυο — μέσα στη διαδικασία των tests σας και επιστρέφει έναν Solana Kit client ήδη στραμμένο σε αυτό. Ένα .use(surfpool()) αντικαθιστά το RPC plugin που θα χρησιμοποιούσατε κανονικά (solanaLocalRpc(), litesvm()) και προσθέτει έναν προχρηματοδοτημένο payer μαζί με τα cheatcodes του Surfpool:

import { createClient } from "@solana/kit";
import { surfpool } from "@solana/surfpool/kit";
const client = await createClient().use(surfpool());
const slot = await client.rpc.getSlot().send();
await client.cheatcodes.timeTravel({ absoluteSlot: 1_000_000n }).send();

Χωρίς port να επιλέξετε, χωρίς payer να δημιουργήσετε και να χρηματοδοτήσετε, και χωρίς ξεχωριστή διαδικασία surfpool start να διαχειριστείτε. Είστε νέοι στο SDK; Ξεκινήστε με την Επισκόπηση.

Ποιο Entry Point Θέλετε

Entry pointΧρησιμοποιήστε το όταν
surfpool()Προεπιλογή για tests. Ένα απομονωμένο Surfnet ανά αρχείο test, με έναν Kit client ήδη συνδεδεμένο.
surfpool({ rpcUrl })Μια μακρόβια παρουσία surfpool start κοινοποιείται μεταξύ διεργασιών, ή η πλατφόρμα σας δεν διαθέτει εγγενές δυαδικό αρχείο.
surfnetCheatcodes()Έχετε ήδη έναν client και θέλετε μόνο cheatcodes σε αυτόν.
Surfnet από το @solana/surfpoolΔεν χρησιμοποιείτε Kit — δείτε την αναφορά JS.

Προαπαιτούμενα

  • Node.js 20.18+, το ελάχιστο που δηλώνει το @solana/kit v7. Το @solana/surfpool ίδιο τρέχει σε 18+, αλλά τα πακέτα Kit όχι. Ορισμένα program plugins απαιτούν περισσότερα — το @solana-program/token δηλώνει 24+.
  • Μια υποστηριζόμενη πλατφόρμα (macOS, Linux x86-64) για ενσωματωμένη λειτουργία, η οποία φορτώνει ένα εγγενές δυαδικό αρχείο. Αλλού, χρησιμοποιήστε τη λειτουργία σύνδεσης.
  • Εξοικείωση με τη σύνθεση plugin του Kit — οι clients κατασκευάζονται αλυσιδωτά με κλήσεις .use(), και κάθε plugin προσθέτει ιδιότητες στον client.

Εγκατάσταση

npm install --save-dev @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-signer @solana/surfpool
# or
pnpm add -D @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-signer @solana/surfpool

Αυτά δηλώνονται ως προαιρετικές peer dependencies του @solana/surfpool: παραλείψτε τα αν χρησιμοποιείτε μόνο την κλάση Surfnet απευθείας, αλλά η εισαγωγή του @solana/surfpool/kit απαιτεί @solana/kit και @solana/kit-plugin-rpc. Δείτε Εγκατάσταση για τον πίνακα υποστήριξης πλατφορμών και την αντιμετώπιση προβλημάτων.

Ενσωματωμένη Λειτουργία

Η κλήση του surfpool() χωρίς rpcUrl εκκινεί ένα Surfnet εντός της διαδικασίας σε δυναμικά ports και κατευθύνει ολόκληρο τον Kit client σε αυτό. Το plugin είναι async, οπότε χρησιμοποιήστε await στην αλυσίδα .use():

import { createClient } from "@solana/kit";
import { surfpool } from "@solana/surfpool/kit";
const client = await createClient().use(surfpool());

Παράλληλα αρχεία test

Κάθε κλήση surfpool() δεσμεύει τα δικά της δυναμικά ports, οπότε κάθε αρχείο test μπορεί να εκκινεί το δικό του απομονωμένο Surfnet και η σουίτα εξακολουθεί να εκτελείται παράλληλα.

Ένα Πλήρες Test

Εκκινήστε ένα Surfnet, στείλτε μια μεταφορά που πληρώνεται από τον προχρηματοδοτημένο payer, και ελέγξτε το αποτέλεσμα. Τα παραδείγματα εδώ χρησιμοποιούν node:test· το Vitest και το Jest λειτουργούν με τον ίδιο τρόπο με τα δικά τους hooks after / afterAll.

transfer.test.ts
import { after, test } from "node:test";
import assert from "node:assert/strict";
import { getTransferSolInstruction } from "@solana-program/system";
import { createClient, generateKeyPairSigner, lamports } from "@solana/kit";
import { surfpool } from "@solana/surfpool/kit";
const client = await createClient().use(surfpool());
after(() => {
client.surfnet.stop();
});
test("transfers SOL on an embedded Surfnet", async () => {
const recipient = await generateKeyPairSigner();
const amount = lamports(5_000_000n);
await client.sendTransaction(
getTransferSolInstruction({
amount,
destination: recipient.address,
source: client.payer
})
);
const { value: balance } = await client.rpc
.getBalance(recipient.address)
.send();
assert.equal(balance, amount);
});

Κύκλος Ζωής

Καλέστε το client.surfnet.stop() κατά την αποδόμηση, όπως παραπάνω, ώστε τα ports και οι διακομιστές του Surfnet να αποδεσμευτούν. Το stop() είναι idempotent και σύγχρονο — επιστρέφει μόλις το runtime έχει πράγματι κλείσει. Η διακοπή είναι οριστική· η δημιουργία ενός νέου client εκκινεί μια νέα παρουσία.

Η αποδόμηση δεν είναι αυτόματη

Ένας client που διατηρείται σε εύρος module — το συνηθισμένο μοτίβο για ένα αρχείο test — δεν απορρίπτεται ποτέ, οπότε τίποτα δεν σταματά το Surfnet για εσάς. Χωρίς hook αποδόμησης η διαδικασία μπορεί να κολλήσει ή να καταγράψει προειδοποιήσεις connection reset καθώς το ΛΣ κλείνει τα sockets κατά την έξοδο.

Τι Εγκαθιστά το Plugin

Στον clientΠροέρχεται απόΤι είναι
client.payer@solana/kit-plugin-signerΈνα KeyPairSigner για τον προχρηματοδοτημένο λογαριασμό payer του Surfnet
client.rpc / client.rpcSubscriptions@solana/kit-plugin-rpcΟι τυπικοί clients RPC και συνδρομών Solana, κατευθυνόμενοι στο Surfnet
client.airdrop@solana/kit-plugin-rpcrequestAirdrop έναντι του Surfnet
client.getMinimumBalance@solana/kit-plugin-rpcΑναζητήσεις απαλλαγής από ενοίκιο
client.transactionPlanner / ...PlanExecutor@solana/kit-plugin-rpcΣχεδιασμός και εκτέλεση συναλλαγών
client.sendTransaction / client.sendTransactions@solana/kit-plugin-rpc (μέσω kit-plugin-instruction-plan)Σχεδιασμός και αποστολή οδηγιών σε μία κλήση
client.rpcUrl / client.wsUrl@solana/surfpool/kitΤα URLs HTTP και WebSocket του Surfnet
client.surfnet@solana/surfpool/kitΤο εγγενές handle Surfnet (fundSol, deploy, drainEvents, …)
client.cheatcodes@solana/surfpool/kitΈνα typed RPC που καλύπτει κάθε cheatcode surfnet_*

Το plugin δεν εγκαθιστά identity. Προσθέστε ένα με .use(identity(...)) αν το test σας χρειάζεται μια αρχή ξεχωριστή από το client.payer.

Cheatcodes

Τα Cheatcodes είναι μεταλλάξεις κατάστασης που παρακάμπτουν την κανονική ροή συναλλαγών — εκτελούνται αμέσως, χωρίς να καταναλώνουν blockhash ή να πληρώνουν fees, κάτι που είναι ακριβώς αυτό που θέλετε για την προετοιμασία tests. Το client.cheatcodes τα εκθέτει όλα ως typed RPC.

Τα ονόματα μεθόδων αποκόπτουν το πρόθεμα surfnet_, οπότε το surfnet_pauseClock είναι client.cheatcodes.pauseClock(), και οι αποκρίσεις φτάνουν ήδη αποσυσκευασμένες από τον φάκελο { context, value } τους.

import { address, generateKeyPairSigner } from "@solana/kit";
// Deterministic clock.
const paused = await client.cheatcodes.pauseClock().send();
await client.cheatcodes
.timeTravel({ absoluteSlot: paused.absoluteSlot + 1_000n })
.send();
await client.cheatcodes.resumeClock().send();
// Arbitrary account state. `data` is hex-encoded.
const account = (await generateKeyPairSigner()).address;
const owner = (await generateKeyPairSigner()).address;
await client.cheatcodes
.setAccount(account, { data: "aabbcc", lamports: 777_777, owner })
.send();
// Token balances, without minting through the token program. The mint must
// already exist — create it, or clone it from mainnet with cloneProgramAccount.
const mint = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
await client.cheatcodes
.setTokenAccount(owner, mint, { amount: 1_000_000n })
.send();

Η πλήρης λίστα μεθόδων — συμπεριλαμβανομένων των streamAccount, cloneProgramAccount, profileTransaction, registerIdl και resetNetwork — τεκμηριώνεται στα Cheatcodes και την αναφορά RPC.

Εγγραφή Δομημένων Λογαριασμών Με Codec

Το setAccount δέχεται ακατέργαστα bytes ως hex, κάτι που ταιριάζει καλά με τους encoders λογαριασμών που αποστέλλουν τα program clients του Kit. Αντί να στέλνετε συναλλαγές για τη δημιουργία κατάστασης, κωδικοποιήστε τον λογαριασμό που θέλετε και γράψτε τον απευθείας — εδώ, ένα πλήρως αρχικοποιημένο SPL mint με προμήθεια ήδη σε αυτό:

import {
fetchMint,
getMintEncoder,
TOKEN_PROGRAM_ADDRESS
} from "@solana-program/token";
import {
generateKeyPairSigner,
getBase16Decoder,
none,
some
} from "@solana/kit";
const mint = (await generateKeyPairSigner()).address;
const data = getMintEncoder().encode({
decimals: 6,
freezeAuthority: none(),
isInitialized: true,
mintAuthority: some(client.payer.address),
supply: 1_000_000_000n
});
await client.cheatcodes
.setAccount(mint, {
// getBase16Decoder() turns the encoded bytes into the hex `data` expects.
data: getBase16Decoder().decode(data),
lamports: 1_461_600, // rent-exempt minimum for an 82-byte mint
owner: TOKEN_PROGRAM_ADDRESS
})
.send();
// Reads back as a normal mint through the program client.
const account = await fetchMint(client.rpc, mint);
account.data.decimals; // 6
account.data.supply; // 1_000_000_000n

Το ίδιο μοτίβο λειτουργεί για οποιοδήποτε client δημιουργήθηκε από Codama: κωδικοποιήστε με τον encoder του λογαριασμού, μετατρέψτε σε hex και δώστε το στο setAccount. Συνδυάστε το με το setTokenAccount παραπάνω για να στήσετε ένα mint και χρηματοδοτημένους κατόχους χωρίς μία συναλλαγή.

Οι αποκρίσεις cheatcode χρησιμοποιούν bigint

Η μεταφορά cheatcodes αναλύει κάθε ακέραιο JSON ως bigint, οπότε τιμές u64 όπως το rentEpoch επιβιώνουν πέρα από το 2^53. Τα payloads αιτημάτων δέχονται number | bigint.

Cheatcodes Χωρίς το Plugin

Δύο μικρότερα entry points καλύπτουν περιπτώσεις όπου δεν θέλετε το πλήρες plugin. Και τα δύο είναι σύγχρονα — επισυνάπτουν μόνο ένα transport, οπότε κανένα δεν χρειάζεται await.

import {
createSurfnetCheatcodesRpc,
surfnetCheatcodes
} from "@solana/surfpool/kit";
// Standalone RPC, no Kit client involved.
const cheatcodes = createSurfnetCheatcodesRpc("http://127.0.0.1:8899");
await cheatcodes.pauseClock().send();
// Add `client.cheatcodes` to a client you already composed.
const client = createClient().use(surfnetCheatcodes());

Το surfnetCheatcodes() επιλύει το endpoint του από το url αν δοθεί, έπειτα από ένα υπάρχον client.rpcUrl (οπότε συνθέτεται με οποιοδήποτε client που το φέρει), και τέλος από το DEFAULT_SURFNET_ENDPOINT (http://127.0.0.1:8899). Και τα δύο δέχονται επιλογή headers για πιστοποίηση έναντι απομακρυσμένου Surfpool.

Διαμόρφωση

Οι επιλογές εκκίνησης Surfnet τοποθετούνται κάτω από το κλειδί surfnet και προωθούνται στο Surfnet.startWithConfig(). Όλα τα υπόλοιπα προωθούνται στο τοπικό plugin Solana RPC:

const client = await createClient().use(
surfpool({
surfnet: { offline: true }, // Surfnet startup config
skipPreflight: true // forwarded to solanaLocalRpc()
})
);

Παραλείψτε εντελώς το surfnet και το plugin καλεί το Surfnet.start() με τις προεπιλογές του. Δείτε Διαμόρφωση για το πλήρες σύνολο επιλογών εκκίνησης — απομακρυσμένη εναλλακτική RPC, λειτουργία παραγωγής block, χρονισμός slot, πύλες χαρακτηριστικών και προσαρμοσμένοι payers.

Σύνθεση Με Program Plugins

Επειδή το surfpool() ικανοποιεί τις ίδιες συμβάσεις με το solanaLocalRpc(), τα program plugins του Kit τοποθετούνται πάνω του και οι οδηγίες τους εκτελούνται έναντι του ενσωματωμένου Surfnet. Μόνο το τελικό αποτέλεσμα χρειάζεται await — το use() σε έναν async client επιστρέφει έναν άλλο async client, οπότε τα σύγχρονα και ασύγχρονα plugins αλυσιδώνουν ελεύθερα.

import { createClient, generateKeyPairSigner } from "@solana/kit";
import { tokenProgram } from "@solana-program/token";
import { surfpool } from "@solana/surfpool/kit";
const client = await createClient().use(surfpool()).use(tokenProgram());
const newMint = await generateKeyPairSigner();
await client.token.instructions
.createMint({ decimals: 6, mintAuthority: client.payer.address, newMint })
.sendTransaction();
await client.token.instructions
.mintToATA({
amount: 1_000_000n,
decimals: 6,
mint: newMint.address,
mintAuthority: client.payer,
owner: client.payer.address
})
.sendTransaction();

Λειτουργία Σύνδεσης

Η μεταβίβαση του rpcUrl μεταβάλλει το plugin σε λειτουργία σύνδεσης: συνδέεται σε ένα ήδη εκτελούμενο Surfpool — ένα που εκκινήθηκε με surfpool start — αντί να εκκινεί ένα. Δεν φορτώνεται εγγενές module, οπότε αυτή η λειτουργία λειτουργεί σε πλατφόρμες χωρίς προκατασκευασμένο δυαδικό αρχείο. Είναι επίσης σύγχρονη, οπότε τίποτα δεν χρειάζεται await:

import { createKeyPairSignerFromBytes, createClient } from "@solana/kit";
import { payer } from "@solana/kit-plugin-signer";
import { surfpool } from "@solana/surfpool/kit";
import { readFile } from "node:fs/promises";
// Any funded signer works; this loads the local CLI keypair.
const keypairPath = `${process.env.HOME}/.config/solana/id.json`;
const myPayer = await createKeyPairSignerFromBytes(
new Uint8Array(JSON.parse(await readFile(keypairPath, "utf8")))
);
const client = createClient()
.use(payer(myPayer))
.use(surfpool({ rpcUrl: "http://127.0.0.1:8899" }));

Τρεις διαφορές από την ενσωματωμένη λειτουργία:

  • Ο client πρέπει να έχει ήδη έναν payer. Η λειτουργία σύνδεσης δεν έχει πρόσβαση στο μυστικό κλειδί payer της εκτελούμενης παρουσίας, οπότε δεν εγκαθιστά κανένα. Χρηματοδοτήστε οποιοδήποτε signer παρέχετε με client.cheatcodes.setAccount(...) ή το δικό του faucet της εκτελούμενης παρουσίας.
  • Δεν υπάρχει handle client.surfnet. Τα βοηθητικά εντός διαδικασίας δεν είναι διαθέσιμα· χρησιμοποιήστε client.cheatcodes για χειρισμό κατάστασης.
  • Η διαμόρφωση εκκίνησης surfnet απορρίπτεται. Η παρουσία εκτελείται ήδη, οπότε το rpcUrl και το surfnet είναι αμοιβαία αποκλειόμενα στους τύπους.

Port WebSocket

Το Surfpool εξυπηρετεί συνδρομές στο δικό του port (προεπιλογή 8900, --ws-port), ανεξάρτητα από το HTTP port. Όταν το rpcUrl έχει ρητό port, το plugin εξάγει το URL συνδρομών ως port 8900 στον ίδιο host. Όταν δεν έχει port — πίσω από proxy, ας πούμε — ανταλλάσσεται μόνο το πρωτόκολλο με ws/wss. Ορίστε το rpcSubscriptionsUrl μόνοι σας όταν καμία κανόνας δεν εφαρμόζεται.

Επόμενα Βήματα

  • Programs — αναπτύξτε το πρόγραμμά σας στο Surfnet πριν από ένα test
  • Cheatcodes — η πλήρης επιφάνεια μετάλλαξης κατάστασης
  • Διαμόρφωση — forking mainnet, παραγωγή block, πύλες χαρακτηριστικών
  • Εγκατάσταση — υποστήριξη πλατφορμών και αντιμετώπιση προβλημάτων
  • Αναφορά JS — η κλάση Surfnet πίσω από το client.surfnet

Is this page helpful?

© 2026 Ίδρυμα Solana. Με επιφύλαξη παντός δικαιώματος.