@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/kitv7. Το@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# orpnpm 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.
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-rpc | requestAirdrop έναντι του 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 mintowner: TOKEN_PROGRAM_ADDRESS}).send();// Reads back as a normal mint through the program client.const account = await fetchMint(client.rpc, mint);account.data.decimals; // 6account.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 configskipPreflight: 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?