Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,117 @@ For scenarios that need to run against real, live testnet (account funding, paym

## Examples

### Minimal working examples

The two tasks new users reach for first — sending a payment and calling a Soroban contract — shown end-to-end with the full error-check idiom.

#### Send a payment

```ts
import { createSorokitClient, FreighterAdapter } from "sorokit-core";
// swkInstance is your initialised @creit.tech/stellar-wallets-kit instance

// 1. Create client
const clientResult = createSorokitClient({ network: "testnet" });
if (clientResult.status === "error") throw new Error(clientResult.error.message);
const client = clientResult.data;

// 2. Connect wallet and get the public key
const adapter = new FreighterAdapter(swkInstance);
const conn = await client.wallet.connect(adapter);
if (conn.status === "error") throw new Error(conn.error.message);
const { publicKey } = conn.data;

// 3. Build the payment transaction (returns a signed-envelope XDR stub)
const tx = await client.transaction.buildPayment(publicKey, {
destination: "GDEST...WXYZ",
amount: "10",
// asset defaults to native XLM; pass { code: "USDC", issuer: "GA5Z..." } for custom assets
});
if (tx.status === "error") throw new Error(tx.error.message);

// 4. Sign with the connected wallet
const signed = await client.wallet.signTransaction(adapter, {
transactionXdr: tx.data,
networkPassphrase: client.networkConfig.networkPassphrase,
});
if (signed.status === "error") throw new Error(signed.error.message);

// 5. Submit to Horizon
const result = await client.transaction.submit(signed.data);
if (result.status === "ok") {
console.log("Confirmed in ledger:", result.data.ledger);
} else {
console.error("Submit failed:", result.error.code, result.error.message);
}
```

#### Call a Soroban smart contract

```ts
import { createSorokitClient, FreighterAdapter } from "sorokit-core";
import { nativeToScVal, Address } from "@stellar/stellar-sdk";

// 1. Create a client with an RPC endpoint (required for Soroban)
const clientResult = createSorokitClient({
network: "testnet",
rpcUrl: "https://soroban-testnet.stellar.org",
});
if (clientResult.status === "error") throw new Error(clientResult.error.message);
const client = clientResult.data;

// 2. Connect wallet
const adapter = new FreighterAdapter(swkInstance);
const conn = await client.wallet.connect(adapter);
if (conn.status === "error") throw new Error(conn.error.message);
const { publicKey } = conn.data;

// 3. Use the all-in-one invoke helper: prepare → sign → execute
const result = await client.soroban.invoke(
{
contractId: "CCONTRACT...ID",
method: "transfer",
args: [
new Address(publicKey).toScVal(), // from
new Address("GDEST...WXYZ").toScVal(), // to
nativeToScVal(1_000_000, { type: "i128" }), // amount (in contract units)
],
publicKey,
},
// signing callback — receives the assembled XDR, returns the signed XDR
async (xdr) => {
const sig = await client.wallet.signTransaction(adapter, {
transactionXdr: xdr,
networkPassphrase: client.networkConfig.networkPassphrase,
});
if (sig.status === "error") throw new Error(sig.error.message);
return sig.data;
},
);

if (result.status === "ok") {
console.log("Transaction hash:", result.data);
} else {
console.error("Contract call failed:", result.error.code, result.error.message);
}
```

For read-only contract calls (no signing required):

```ts
const balance = await client.soroban.read({
contractId: "CCONTRACT...ID",
method: "balance",
args: [new Address(publicKey).toScVal()],
publicKey,
});
if (balance.status === "ok") console.log("Balance:", balance.data.result);
```

---

### Example apps

| Example | Shows |
| --- | --- |
| [`examples/router-swap`](examples/router-swap) | DEX swap: path discovery, quote, sign, submit, track |
Expand Down
15 changes: 15 additions & 0 deletions src/account/streamAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,21 @@ export async function* streamAccount(
});

errorResult = result;

// Account no longer exists on the network (deleted / merged away).
// Continuing to poll would create a busy-loop of repeated 404s, so
// yield the error once and stop the stream.
if (result.error.code === SorokitErrorCode.ACCOUNT_NOT_FOUND) {
yield result;
logger?.debug("account.stream", {
operation: "account.stream",
status: "stopped",
reason: "account_not_found",
publicKey,
polls,
});
return;
}
}

if (result.status === "ok") {
Expand Down
32 changes: 27 additions & 5 deletions src/transaction/transactionQueue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,11 +470,33 @@ export class TransactionQueue {
tx.processingStartedAt = Date.now();
}

submissionFn(batch).catch((error) => {
for (const tx of batch) {
this.markFailed(tx.id, error.message);
}
});
submissionFn(batch)
.then((batchResult) => {
// Correlate results back to queued transactions by ID, not by
// array index. Results may arrive out of order when the
// submissionFn executes operations concurrently, so we always
// look up each transaction by its stable queue ID rather than
// relying on positional correspondence.
for (const tx of batch) {
const result = batchResult.results.get(tx.id);
if (result === undefined) {
// submissionFn did not report a result for this ID — treat
// as a failure so the transaction can be retried.
this.markFailed(tx.id, "No result returned for transaction");
} else if (result.success) {
this.markCompleted(tx.id, result.hash);
} else {
this.markFailed(tx.id, result.error ?? "Submission failed");
}
}
})
.catch((error: unknown) => {
const message =
error instanceof Error ? error.message : String(error);
for (const tx of batch) {
this.markFailed(tx.id, message);
}
});
}
}, this.config.schedulingIntervalMs);
}
Expand Down
Loading