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
76 changes: 67 additions & 9 deletions docs/guides/06-invoke-a-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ free and safe to repeat.
[Deploy the Increment Contract](https://developers.stellar.org/docs/build/smart-contracts/getting-started/deploy-increment-contract)
tutorial once (about 20 to 30 minutes), then paste the contract ID it prints
into `contractId` below. You will not touch the CLI again in this guide.
Generating a typed client from a contract is covered later in the series.
This guide types the client with a small hand-written interface; generating one
from a contract's spec is covered later in the series.
- The examples use testnet RPC at `https://soroban-testnet.stellar.org`.

## Connect and load the contract
Expand All @@ -41,9 +42,18 @@ import { contract, Keypair, Networks } from "@stellar/stellar-sdk";
const rpcUrl = "https://soroban-testnet.stellar.org";
const networkPassphrase = Networks.TESTNET;

// Describe just the methods you call. `Client.from<T>()` uses this to type the
// returned client, so the calls below are checked and autocompleted — no code
// generation needed.
interface IncrementContract {
increment: (
options?: contract.MethodOptions,
) => Promise<contract.AssembledTransaction<number>>;
}

const { signTransaction } = contract.basicNodeSigner(keypair, networkPassphrase);

const client = await contract.Client.from({
const client = await contract.Client.from<IncrementContract>({
contractId,
rpcUrl,
networkPassphrase,
Expand All @@ -54,10 +64,52 @@ const client = await contract.Client.from({

Here `keypair` is your funded account from
[Connect and Fund an Account](/guides/01-connect-and-fund/) and `contractId` is
your deployed contract's `C...` ID. Because the client is built from the live
contract at runtime, its methods are **not typed**: TypeScript does not know
`client.increment` exists, so calls below use `(client as any)`. A fully typed
client comes from generating bindings, covered later in the series.
your deployed contract's `C...` ID. The client is built from the live contract at
runtime, so TypeScript cannot infer its methods on its own. Passing an interface
to [`Client.from<T>()`](/reference/contracts-client/#contractclient) types them:
`client.increment()` below is fully typed and autocompleted, with no code
generation. For a contract with many methods, generate that interface from its
spec (covered later in the series) rather than writing it by hand.

## Query contract state

Sometimes you only want to **inspect** a contract or **read** a value from it, not
change anything. For that, `rpc.Server` has two one-line shortcuts that build the
contract's interface for you — including the built-in spec for Stellar Asset
Contracts (SACs) — so they work from just a contract ID, with no client setup.

[`getContractMethods`](/reference/network-rpc/#servergetcontractmethodscontractid-networkpassphrase)
lists a contract's callable methods and their signatures, which is handy when you
are inspecting a contract you did not write.
[`queryContract`](/reference/network-rpc/#serverquerycontractcontractid-method-args-networkpassphrase)
runs a **read-only**
call and returns the decoded result. It simulates the call the same way the preview
below does, so it needs no signing or fee, but it hands you the value directly. Here
both run against a token contract — discover its methods, then read one:

```ts
import { rpc } from "@stellar/stellar-sdk";

const server = new rpc.Server(rpcUrl);

// Discover what the contract exposes, from just its ID.
const methods = await server.getContractMethods(tokenId);
// [
// { name: "decimals", inputs: [], outputs: ["U32"] },
// { name: "balance", inputs: [{ name: "id", type: "Address" }], outputs: ["I128"] },
// { name: "transfer", inputs: [...], outputs: [] },
// ]

// Read one of its read-only methods in a single line.
const decimals = await server.queryContract<number>(tokenId, "decimals");

const balance = await server.queryContract<bigint>(tokenId, "balance", {
id: "G...", // named arguments, keyed by the method's parameter names
});
```

`queryContract` is for reads only. To **change** state you build a client and sign a
transaction, as shown next.

## Preview a call with simulation

Expand All @@ -68,7 +120,7 @@ no signature. Read the predicted return value from
[`tx.result`](/reference/contracts-client/#contractassembledtransaction):

```ts
const tx = await (client as any).increment();
const tx = await client.increment();

tx.result; // the value the call would return; nothing has been sent
```
Expand Down Expand Up @@ -113,6 +165,12 @@ const rpcUrl = "https://soroban-testnet.stellar.org";
const networkPassphrase = Networks.TESTNET;
const contractId = "C..."; // your deployed increment contract (see Prerequisites)

interface IncrementContract {
increment: (
options?: contract.MethodOptions,
) => Promise<contract.AssembledTransaction<number>>;
}

async function main() {
const server = new rpc.Server(rpcUrl);
const keypair = Keypair.random();
Expand All @@ -125,7 +183,7 @@ async function main() {
// Fund a throwaway account to invoke from (the RPC-side friendbot).
await server.fundAddress(keypair.publicKey());

const client = await contract.Client.from({
const client = await contract.Client.from<IncrementContract>({
contractId,
rpcUrl,
networkPassphrase,
Expand All @@ -134,7 +192,7 @@ async function main() {
});

// Preview the call for free with simulation.
const tx = await (client as any).increment();
const tx = await client.increment();
console.log("preview:", tx.result);

// Sign and send to apply it on-chain.
Expand Down
13 changes: 10 additions & 3 deletions docs/guides/07-contract-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ authorization entry that account must sign. Build the transaction as in
need to sign:

```ts
const tx = await (client as any).increment({ user: signer.publicKey(), value: 1 });
const tx = await client.increment({ user: signer.publicKey(), value: 1 });

tx.needsNonInvokerSigningBy(); // [signer.publicKey()]
```
Expand Down Expand Up @@ -218,6 +218,13 @@ const rpcUrl = "https://soroban-testnet.stellar.org";
const networkPassphrase = Networks.TESTNET;
const contractId = "C..."; // your deployed Auth contract (see Prerequisites)

interface AuthContract {
increment: (
args: { user: string; value: number },
options?: contract.MethodOptions,
) => Promise<contract.AssembledTransaction<number>>;
}

async function main() {
const server = new rpc.Server(rpcUrl);

Expand All @@ -234,7 +241,7 @@ async function main() {
source,
networkPassphrase,
);
const client = await contract.Client.from({
const client = await contract.Client.from<AuthContract>({
contractId,
rpcUrl,
networkPassphrase,
Expand All @@ -243,7 +250,7 @@ async function main() {
});

// A call that requires `signer` (not the source) to authorize it.
const tx = await (client as any).increment({
const tx = await client.increment({
user: signer.publicKey(),
value: 1,
});
Expand Down
52 changes: 41 additions & 11 deletions docs/reference/contracts-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -632,9 +632,9 @@ transaction.
class Client {
constructor(spec: Spec, options: ClientOptions);
static deploy<T = Client>(args: Record<string, any> | null, options: MethodOptions & Omit<ClientOptions, "contractId"> & { address?: string; format?: "base64" | "hex"; salt?: Uint8Array<ArrayBufferLike> | Buffer<ArrayBufferLike>; wasmHash: string | Buffer<ArrayBufferLike> }): Promise<AssembledTransaction<T>>;
static from(options: ClientOptions): Promise<Client>;
static fromWasm(wasm: Buffer, options: ClientOptions): Promise<Client>;
static fromWasmHash(wasmHash: string | Buffer<ArrayBufferLike>, options: ClientOptions, format: "base64" | "hex" = "hex"): Promise<Client>;
static from<T = unknown>(options: ClientOptions): Promise<Client & T>;
static fromWasm<T = unknown>(wasm: Buffer, options: ClientOptions): Promise<Client & T>;
static fromWasmHash<T = unknown>(wasmHash: string | Buffer<ArrayBufferLike>, options: ClientOptions, format: "base64" | "hex" = "hex"): Promise<Client & T>;
readonly options: ClientOptions;
readonly spec: Spec;
txFromJSON<T>(json: string): AssembledTransaction<T>;
Expand Down Expand Up @@ -679,7 +679,7 @@ SAC spec is used instead of downloading Wasm, since a SAC has no Wasm
executable on-chain.

```ts
static from(options: ClientOptions): Promise<Client>;
static from<T = unknown>(options: ClientOptions): Promise<Client & T>;
```

**Parameters**
Expand All @@ -694,14 +694,24 @@ A Promise that resolves to a Client instance.

- If the provided options object does not contain both rpcUrl and contractId.

**Source:** [src/contract/client.ts:192](https://github.com/stellar/js-stellar-sdk/blob/main/src/contract/client.ts#L192)
**Example**

```ts
interface MyContract {
increment: (opts?: MethodOptions) => Promise<AssembledTransaction<number>>;
}
const client = await contract.Client.from<MyContract>(options);
const tx = await client.increment(); // typed
```

**Source:** [src/contract/client.ts:237](https://github.com/stellar/js-stellar-sdk/blob/main/src/contract/client.ts#L237)

### `Client.fromWasm(wasm, options)`

Generates a Client instance from the provided ClientOptions and the contract's wasm binary.

```ts
static fromWasm(wasm: Buffer, options: ClientOptions): Promise<Client>;
static fromWasm<T = unknown>(wasm: Buffer, options: ClientOptions): Promise<Client & T>;
```

**Parameters**
Expand All @@ -717,15 +727,25 @@ A Promise that resolves to a Client instance.

- If the contract spec cannot be obtained from the provided wasm binary.

**Source:** [src/contract/client.ts:176](https://github.com/stellar/js-stellar-sdk/blob/main/src/contract/client.ts#L176)
**Example**

```ts
interface MyContract {
increment: (opts?: MethodOptions) => Promise<AssembledTransaction<number>>;
}
const client = await contract.Client.fromWasm<MyContract>(wasm, options);
const tx = await client.increment(); // typed
```

**Source:** [src/contract/client.ts:204](https://github.com/stellar/js-stellar-sdk/blob/main/src/contract/client.ts#L204)

### `Client.fromWasmHash(wasmHash, options, format)`

Generates a Client instance from the provided ClientOptions and the contract's wasm hash.
The wasmHash can be provided in either hex or base64 format.

```ts
static fromWasmHash(wasmHash: string | Buffer<ArrayBufferLike>, options: ClientOptions, format: "base64" | "hex" = "hex"): Promise<Client>;
static fromWasmHash<T = unknown>(wasmHash: string | Buffer<ArrayBufferLike>, options: ClientOptions, format: "base64" | "hex" = "hex"): Promise<Client & T>;
```

**Parameters**
Expand All @@ -742,7 +762,17 @@ A Promise that resolves to a Client instance.

- If the provided options object does not contain an rpcUrl.

**Source:** [src/contract/client.ts:148](https://github.com/stellar/js-stellar-sdk/blob/main/src/contract/client.ts#L148)
**Example**

```ts
interface MyContract {
increment: (opts?: MethodOptions) => Promise<AssembledTransaction<number>>;
}
const client = await contract.Client.fromWasmHash<MyContract>(hash, options);
const tx = await client.increment(); // typed
```

**Source:** [src/contract/client.ts:162](https://github.com/stellar/js-stellar-sdk/blob/main/src/contract/client.ts#L162)

### `client.options`

Expand Down Expand Up @@ -770,7 +800,7 @@ txFromJSON<T>(json: string): AssembledTransaction<T>;

- **`json`** — `string` (required)

**Source:** [src/contract/client.ts:222](https://github.com/stellar/js-stellar-sdk/blob/main/src/contract/client.ts#L222)
**Source:** [src/contract/client.ts:269](https://github.com/stellar/js-stellar-sdk/blob/main/src/contract/client.ts#L269)

### `client.txFromXDR(xdrBase64)`

Expand All @@ -782,7 +812,7 @@ txFromXDR<T>(xdrBase64: string): AssembledTransaction<T>;

- **`xdrBase64`** — `string` (required)

**Source:** [src/contract/client.ts:235](https://github.com/stellar/js-stellar-sdk/blob/main/src/contract/client.ts#L235)
**Source:** [src/contract/client.ts:282](https://github.com/stellar/js-stellar-sdk/blob/main/src/contract/client.ts#L282)

## contract.DEFAULT_TIMEOUT

Expand Down
Loading
Loading