复制安装命令
用 Codex 或 Claude 安装复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它先审查 Skill 页面再帮你安装。
复制前请先查看来源、License 和安全提示。
A comprehensive AI skill for modern Stellar development with current best practices.
用 Codex 或 Claude 安装复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它先审查 Skill 页面再帮你安装。
复制前请先查看来源、License 和安全提示。
来源文件:README.md
A comprehensive AI skill for modern Stellar development with current best practices. https://skills.stellar.org/
Inspired by solana-foundation/solana-dev-skill and cloudflare/skills.
[!NOTE] This skill was AI-generated using Claude Code and is currently under manual review. We welcome contributions! Please submit PRs, open issues, or provide feedback to help improve this resource for the Stellar ecosystem.
This skill provides AI assistants with deep knowledge of the current Stellar development ecosystem:
These skills give your agent durable Stellar context, baked in. Raven is a complementary remote Model Context Protocol (MCP) server for on-demand lookups: connect an agent and it searches Stellar docs and live ecosystem data, cross-referenced into single answers. Its catalog also includes these skills, so Raven is a live delivery gateway for them. Use both: skills for durable best practices, Raven for current lookups.
Raven is open source at kalepail/stellar-raven. Connect it in Claude Code:
claude mcp add --transport http stellar-raven "https://raven.stellar.buzz/mcp"
You can also try Raven in the browser at the playground, a hosted chat UI for asking Raven questions (sign-in required).
These skills work with any agent that supports the Agent Skills standard.
# Add this repo as a marketplace
/plugin marketplace add stellar/stellar-dev-skill
# Then install the skill
/plugin install stellar-dev@stellar-dev
git clone https://github.com/stellar/stellar-dev-skill ~/.codex/skills/stellar-dev-skill
npx skills add https://github.com/stellar/stellar-dev-skill
git clone https://github.com/stellar/stellar-dev-skill
Copy the skills/ directory contents to your assistant's skills location.
| Agent | Skill Directory | Docs |
|---|---|---|
| Claude Code | ~/.claude/skills/ | docs |
| OpenCode | ~/.config/opencode/skill/ | docs |
| OpenAI Codex | ~/.codex/skills/ | docs |
| Pi | ~/.pi/agent/skills/ | docs |
skills/
├── smart-contracts/ # Stellar smart contracts — SKILL.md router + development/testing/security files
├── dapp/ # Frontend — SKILL.md router + react / data-fetching / smart-accounts files
├── assets/SKILL.md # Stellar Assets, trustlines, SAC bridge
├── data/ # Stellar RPC (preferred) — SKILL.md router + horizon (legacy) file
├── agentic-payments/ # AI/machine payments — SKILL.md router + x402 / mpp files
├── zk-proofs/SKILL.md # ZK verification (BLS12-381/BN254 Groth16, UltraHonk), Circom/Noir/RISC Zero
├── standards/ # SEPs & CAPs — SKILL.md router + ecosystem / resources files
└── cross-chain/ # Cross-chain — SKILL.md router + cctp / axelar / layerzero files
Each sub-skill is a self-contained Agent Skill with its own frontmatter. Larger skills follow Anthropic's progressive-disclosure guidance: a sub-500-line SKILL.md router with a task-to-file table, plus companion files (one level deep) that load only when the task needs them. Cross-references link related skills (e.g., the agentic-payments skill points to smart-contracts for the SACs the protocols call, and to assets for USDC). The AI reads only the files relevant to the task at hand.
"Help me write a Stellar smart contract for a token"
"Set up a Next.js app with Freighter wallet connection"
"How do I deploy a contract to Stellar Testnet?"
"Create unit tests for my smart contract"
"Review this contract for security issues"
Contributions are welcome! Please ensure any updates reflect current Stellar ecosystem best practices.
SKILL.md body under ~500 lines — move deep dives into companion files routed by the task tableevals/ in the same PRevals/ holds ~3 task scenarios for each of the eight skills (plus cross-skill routing checks and a negative control), each encoding a mistake agents actually make without the skill. Three grading tiers: machine-checkable compile checks, LLM-judged behavior assertions, and skill-trigger checks. One gap is open: no baseline transcripts are committed, so the set is still unvalidated. See evals/README.md for the format, how to run them, and what the missing baselines mean.
Apache-2.0 License - see LICENSE for details.
Note: This repository is not in scope for the Stellar Development Foundation bug bounty program. Vulnerabilities found in this repo are not eligible for rewards.
name: dapp
description: Stellar dApp / frontend development. Covers the JavaScript stellar-sdk (browser + Node.js), Freighter wallet, Stellar Wallets Kit (multi-wallet), Wallet Standard, smart accounts with passkeys, transaction building / signing / submission, smart contract invocation from the client, simulation, and error handling. Use when building a React/Next.js/Node.js app that talks to Stellar — classic operations or smart contracts.
user-invocable: true
argument-hint: "[dapp task]"Client-side development with @stellar/stellar-sdk, wallet connection, signing, and submitting transactions. Covers both classic Stellar operations and smart contract invocation from the browser or Node.js.
../smart-contracts/SKILL.md../assets/SKILL.md../data/SKILL.md../agentic-payments/SKILL.md../standards/SKILL.mdThis file covers SDK setup, wallet connection, and transaction build/sign/submit. The deep dives live alongside it:
| Task | File |
|---|---|
| SDK setup and env config | SDK Initialization (below) |
| Wallet integrations (Freighter, Wallets Kit) | Wallet Integration (below) |
| Tx build/send patterns | Transaction Building, Transaction Submission (below) |
| Connect-wallet button, payment form, Next.js App Router wiring | react.md |
Account balances, contract reads (queryContract), raw ledger entries | data-fetching.md |
| Passkey smart wallets (Smart Account Kit), gasless tx via OpenZeppelin Relayer | smart-accounts.md |
| Production UX checklist | Transaction UX Checklist (below) |
Requires Node.js 22+. As of SDK v16, Node 22 is the minimum (older Node produces an
EBADENGINEwarning). v16 also folded@stellar/stellar-baseinto@stellar/stellar-sdk, is ESM-first, and uses nativefetchinstead of axios. If you still import@stellar/stellar-basedirectly, switch the import to@stellar/stellar-sdkand uninstall the base package (keeping both breaksinstanceofchecks). See the migration guide.
npm install @stellar/stellar-sdk @stellar/freighter-api
# Or for multi-wallet support — Wallets Kit v2 is distributed on JSR, not npm:
npx jsr add @creit-tech/stellar-wallets-kit
Sourcing: SDK mechanics below (init, transaction building, contract invocation, submission, data fetching, error handling) track the official JS SDK docs (which also publish
llms.txt/llms-full.txtbundles for agents). Wallet integrations (Freighter, Stellar Wallets Kit), passkey smart accounts, and the OpenZeppelin relayer are separate packages, not part of the JS SDK — verify those against their own upstream docs.
For the full API reference (RPC methods, Horizon endpoints, migration guide), see the data skill.
import * as StellarSdk from "@stellar/stellar-sdk";
// For Testnet
const testnetServer = new StellarSdk.Horizon.Server("https://horizon-testnet.stellar.org");
const testnetRpc = new StellarSdk.rpc.Server("https://soroban-testnet.stellar.org");
const testnetNetworkPassphrase = StellarSdk.Networks.TESTNET;
// For Mainnet
const mainnetServer = new StellarSdk.Horizon.Server("https://horizon.stellar.org");
const mainnetRpcUrl = process.env.NEXT_PUBLIC_STELLAR_MAINNET_RPC_URL;
if (!mainnetRpcUrl) throw new Error("Missing NEXT_PUBLIC_STELLAR_MAINNET_RPC_URL");
const mainnetRpc = new StellarSdk.rpc.Server(mainnetRpcUrl); // set from your chosen RPC provider
const mainnetNetworkPassphrase = StellarSdk.Networks.PUBLIC;
Use a provider-specific mainnet RPC URL (see: https://developers.stellar.org/docs/data/apis/rpc/providers).
// lib/stellar.ts
import * as StellarSdk from "@stellar/stellar-sdk";
const NETWORK = process.env.NEXT_PUBLIC_STELLAR_NETWORK || "testnet";
const requireEnv = (name: string): string => {
const value = process.env[name];
if (!value) throw new Error(`Missing required env var: ${name}`);
return value;
};
function getConfig(network: string) {
switch (network) {
case "testnet":
return {
horizonUrl: "https://horizon-testnet.stellar.org",
rpcUrl: "https://soroban-testnet.stellar.org",
networkPassphrase: StellarSdk.Networks.TESTNET,
friendbotUrl: "https://friendbot.stellar.org" as string | null,
};
case "mainnet":
return {
horizonUrl: "https://horizon.stellar.org",
// Resolved lazily so testnet runs don't require the mainnet env var
rpcUrl: requireEnv("NEXT_PUBLIC_STELLAR_MAINNET_RPC_URL"),
networkPassphrase: StellarSdk.Networks.PUBLIC,
friendbotUrl: null,
};
default:
throw new Error(`Unknown network: ${network}`);
}
}
export const config = getConfig(NETWORK);
export const horizon = new StellarSdk.Horizon.Server(config.horizonUrl);
export const rpc = new StellarSdk.rpc.Server(config.rpcUrl);
// hooks/useFreighter.ts
import { useState, useEffect, useCallback } from "react";
import {
isConnected,
getAddress,
requestAccess,
signTransaction,
getNetwork,
} from "@stellar/freighter-api";
export function useFreighter() {
const [connected, setConnected] = useState(false);
const [address, setAddress] = useState<string | null>(null);
const [network, setNetwork] = useState<string | null>(null);
useEffect(() => {
checkConnection();
}, []);
const checkConnection = async () => {
const { isConnected: installed, error } = await isConnected();
if (error || !installed) return;
// getAddress returns address: "" until the app has been granted access,
// so a non-empty address means we're already authorized.
const { address: addr, error: addressError } = await getAddress();
if (addressError || !addr) return;
const { network: net, error: networkError } = await getNetwork();
if (networkError) return;
setConnected(true);
setAddress(addr);
setNetwork(net);
};
const connect = useCallback(async () => {
const { isConnected: installed, error } = await isConnected();
if (error || !installed) {
throw new Error("Freighter extension not installed");
}
// requestAccess prompts the user and returns the granted address.
const { address: addr, error: accessError } = await requestAccess();
if (accessError) throw new Error(accessError.message);
const { network: net, error: networkError } = await getNetwork();
if (networkError) throw new Error(networkError.message);
setConnected(true);
setAddress(addr);
setNetwork(net);
return addr;
}, []);
const disconnect = useCallback(() => {
setConnected(false);
setAddress(null);
setNetwork(null);
}, []);
const sign = useCallback(
async (xdr: string, networkPassphrase: string) => {
if (!connected) throw new Error("Wallet not connected");
const { signedTxXdr, error } = await signTransaction(xdr, {
networkPassphrase,
});
if (error) throw new Error(error.message);
return signedTxXdr;
},
[connected]
);
return { connected, address, network, connect, disconnect, sign };
}
// hooks/useStellarWallet.ts
import { useState, useCallback } from "react";
import { StellarWalletsKit, Networks } from "@creit-tech/stellar-wallets-kit";
import { defaultModules } from "@creit-tech/stellar-wallets-kit/modules/utils";
// v2 is a static singleton: init once at module load, then call static methods —
// there is no instance to construct or pass around.
// defaultModules() loads every wallet that needs no extra setup; modules with
// prerequisites (WalletConnect, Ledger, Trezor) must be imported and added explicitly.
StellarWalletsKit.init({
modules: defaultModules(),
network: Networks.TESTNET,
});
export function useStellarWallet() {
const [address, setAddress] = useState<string | null>(null);
const connect = useCallback(async () => {
// authModal() opens the wallet picker, sets the chosen module active,
// and returns the address — one call replaces v1's openModal callback dance.
const { address } = await StellarWalletsKit.authModal();
setAddress(address);
}, []);
const disconnect = useCallback(async () => {
await StellarWalletsKit.disconnect();
setAddress(null);
}, []);
const sign = useCallback(async (xdr: string) => {
const { signedTxXdr } = await StellarWalletsKit.signTransaction(xdr);
return signedTxXdr;
}, []);
return { address, connect, disconnect, sign };
}
Migrating from v1? (noted July 2026) v1 lived on npm under the dotted scope
@creit.tech/stellar-wallets-kit, withnew StellarWalletsKit({...}),allowAllModules(), andopenModal({ onWalletSelected }). v2 moved to JSR under@creit-tech/stellar-wallets-kit, made the kit fully static, replacedallowAllModules()withdefaultModules(), and folded wallet selection + address fetch intoauthModal(). npm parity is maintained for now, but the maintainers say npm updates will eventually stop — install from JSR. Pre-selecting a wallet (setWallet(FREIGHTER_ID)) still works; the ID constants now live in per-wallet module subpaths like@creit-tech/stellar-wallets-kit/modules/freighter.
import * as StellarSdk from "@stellar/stellar-sdk";
import { horizon, config } from "@/lib/stellar";
export async function buildPaymentTx(
sourceAddress: string,
destinationAddress: string,
amount: string,
asset: StellarSdk.Asset = StellarSdk.Asset.native()
) {
const account = await horizon.loadAccount(sourceAddress);
const transaction = new StellarSdk.TransactionBuilder(account, {
fee: StellarSdk.BASE_FEE,
networkPassphrase: config.networkPassphrase,
})
.addOperation(
StellarSdk.Operation.payment({
destination: destinationAddress,
asset: asset,
amount: amount,
})
)
.setTimeout(180)
.build();
return transaction.toXDR();
}
contract.Client)The canonical way to call a Stellar smart contract from JS is the contract.Client, not hand-built Contract.call + assembleTransaction. The client reads the contract's interface from the network, so each method is callable by name and returns an AssembledTransaction. You get a native JS result and don't build ScVals by hand.
import { contract } from "@stellar/stellar-sdk";
import { config } from "@/lib/stellar";
// Describe just the methods you call. `Client.from<T>()` uses this to type
// the returned client, so calls are checked and autocompleted — no codegen.
// For a contract with many methods, generate this interface from its spec
// with the SDK's binding CLI instead of writing it by hand.
interface CounterContract {
increment: (
options?: contract.MethodOptions,
) => Promise<contract.AssembledTransaction<number>>;
}
// `signTransaction` comes from the wallet (e.g. Freighter/Wallets Kit in the
// browser). `contract.basicNodeSigner(keypair, networkPassphrase)` is the
// Node equivalent for scripts and tests.
export async function getCounterClient(
contractId: string,
publicKey: string,
signTransaction: contract.ClientOptions["signTransaction"],
) {
return contract.Client.from<CounterContract>({
contractId,
rpcUrl: config.rpcUrl,
networkPassphrase: config.networkPassphrase,
publicKey,
signTransaction,
});
}
// Preview (free simulation) then sign + send to apply on-chain.
export async function increment(client: contract.Client & CounterContract) {
const tx = await client.increment();
console.log("preview:", tx.result); // predicted return value, no signature
const sent = await tx.signAndSend(); // submits and polls to completion
return sent.result;
}
AssembledTransaction also supports fine-grained control ({ fee, simulate, timeoutInSeconds } as a second arg) and multi-party auth via tx.needsNonInvokerSigningBy() / tx.signAuthEntries(). See Invoke a Contract and Authorize a Contract Call.
Use this only when you need direct control over the transaction (e.g. batching a contract call with classic operations). Otherwise prefer contract.Client above.
import * as StellarSdk from "@stellar/stellar-sdk";
import { rpc, config } from "@/lib/stellar";
export async function invokeContract(
sourceAddress: string,
contractId: string,
method: string,
args: StellarSdk.xdr.ScVal[]
) {
const account = await rpc.getAccount(sourceAddress);
const contract = new StellarSdk.Contract(contractId);
const transaction = new StellarSdk.TransactionBuilder(account, {
fee: StellarSdk.BASE_FEE,
networkPassphrase: config.networkPassphrase,
})
.addOperation(contract.call(method, ...args))
.setTimeout(180)
.build();
// `prepareTransaction` simulates and applies footprint/auth/fees in one step.
// (Equivalent to simulateTransaction + rpc.assembleTransaction.)
const prepared = await rpc.prepareTransaction(transaction);
return prepared.toXDR();
}
Building ScVal arguments by hand (only needed for the low-level path — contract.Client converts native JS args for you):
import * as StellarSdk from "@stellar/stellar-sdk";
const addressVal = StellarSdk.Address.fromString(address).toScVal();
const i128Val = StellarSdk.nativeToScVal(BigInt(amount), { type: "i128" });
const u32Val = StellarSdk.nativeToScVal(42, { type: "u32" });
const stringVal = StellarSdk.nativeToScVal("hello", { type: "string" });
const symbolVal = StellarSdk.nativeToScVal("transfer", { type: "symbol" });
// Struct
const structVal = StellarSdk.nativeToScVal(
{ name: "Token", decimals: 7 },
{
type: {
name: ["symbol", null],
decimals: ["u32", null],
},
}
);
// Vec of i128 — the element type is applied to each item
const vecVal = StellarSdk.nativeToScVal(
[1, 2, 3].map((n) => BigInt(n)),
{ type: "i128" }
);
import * as StellarSdk from "@stellar/stellar-sdk";
import { rpc, horizon, config } from "@/lib/stellar";
export async function submitTransaction(signedXdr: string) {
const transaction = StellarSdk.TransactionBuilder.fromXDR(
signedXdr,
config.networkPassphrase
);
// For smart contract transactions, use RPC
if (transaction.operations.some(op => op.type === "invokeHostFunction")) {
return submitSorobanTransaction(signedXdr);
}
// For classic transactions, use Horizon
return submitClassicTransaction(signedXdr);
}
async function submitSorobanTransaction(signedXdr: string) {
const transaction = StellarSdk.TransactionBuilder.fromXDR(
signedXdr,
config.networkPassphrase
) as StellarSdk.Transaction;
const response = await rpc.sendTransaction(transaction);
if (response.status === "ERROR") {
throw new Error(`Send failed: ${response.errorResult}`);
}
// Poll for completion. pollTransaction handles the retry loop (default 5
// attempts, 1s apart — tune with { attempts, sleepStrategy }) instead of a
// hand-rolled while loop that can spin forever.
const getResponse = await rpc.pollTransaction(response.hash);
if (getResponse.status === "SUCCESS") {
return {
hash: response.hash,
result: getResponse.returnValue,
};
}
throw new Error(`Transaction failed: ${getResponse.status}`);
}
async function submitClassicTransaction(signedXdr: string) {
const transaction = StellarSdk.TransactionBuilder.fromXDR(
signedXdr,
config.networkPassphrase
) as StellarSdk.Transaction;
const response = await horizon.submitTransaction(transaction);
return {
hash: response.hash,
ledger: response.ledger,
};
}
评论 (0)
暂无评论,成为第一个评论者吧!