Skip to content

Commit

Permalink
feat!: new exports ContractClient and Rpc
Browse files Browse the repository at this point in the history
- `SorobanRpc` renamed `Rpc`
- New main export `ContractClient`
- `ContractSpec` now available at `ContractClient.Spec`
- Also includes other supporting classes, functions, and types:
  - `ContractClient.AssembledTransaction`
  - `ContractClient.ClientOptions`
  - etc

These are also (maybe) available at top-level named export, if your
tooling supports the `exports` declaration:

    import { ClientOptions, AssembledTransaction } from 'stellar-sdk/Contract'
  • Loading branch information
chadoh committed May 14, 2024
1 parent 2ee6126 commit bfe370c
Show file tree
Hide file tree
Showing 24 changed files with 72 additions and 64 deletions.
17 changes: 16 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,21 @@
"/lib",
"/dist"
],
"exports": {
".": {
"browser": "./dist/stellar-sdk.min.js",
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
},
"./ContractClient": {
"types": "./lib/contract/index.d.ts",
"default": "./lib/contract/index.js"
},
"./Rpc": {
"types": "./lib/rpc/index.d.ts",
"default": "./lib/rpc/index.js"
}
},
"scripts": {
"build": "cross-env NODE_ENV=development yarn _build",
"build:prod": "cross-env NODE_ENV=production yarn _build",
Expand Down Expand Up @@ -164,4 +179,4 @@
],
"timeout": "2m"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ import {
} from "@stellar/stellar-base";
import type {
AssembledTransactionOptions,
ContractClientOptions,
Options,
MethodOptions,
Tx,
XDR_BASE64,
} from "./types";
import { Server } from "../server";
import { Api } from "../api";
import { assembleTransaction } from "../transaction";
import type { ContractClient } from "./client";
import { Server } from "../rpc/server";
import { Api } from "../rpc/api";
import { assembleTransaction } from "../rpc/transaction";
import type { Client } from "./client";
import { Err } from "./rust_result";
import {
DEFAULT_TIMEOUT,
Expand All @@ -33,7 +33,7 @@ export const NULL_ACCOUNT =
"GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF";

/**
* The main workhorse of {@link ContractClient}. This class is used to wrap a
* The main workhorse of {@link Client}. This class is used to wrap a
* transaction-under-construction and provide high-level interfaces to the most
* common workflows, while still providing access to low-level stellar-sdk
* transaction manipulation.
Expand Down Expand Up @@ -70,7 +70,7 @@ export const NULL_ACCOUNT =
* ```
*
* While that looks pretty complicated, most of the time you will use this in
* conjunction with {@link ContractClient}, which simplifies it to:
* conjunction with {@link Client}, which simplifies it to:
*
* ```ts
* const { result } = await client.myReadMethod({
Expand All @@ -94,7 +94,7 @@ export const NULL_ACCOUNT =
* const sentTx = await assembledTx.signAndSend()
* ```
*
* Here we're assuming that you're using a {@link ContractClient}, rather than
* Here we're assuming that you're using a {@link Client}, rather than
* constructing `AssembledTransaction`'s directly.
*
* Note that `sentTx`, the return value of `signAndSend`, is a
Expand All @@ -118,7 +118,7 @@ export const NULL_ACCOUNT =
*
* If you need more control over the transaction before simulating it, you can
* set various {@link MethodOptions} when constructing your
* `AssembledTransaction`. With a {@link ContractClient}, this is passed as a
* `AssembledTransaction`. With a {@link Client}, this is passed as a
* second object after the arguments (or the only object, if the method takes
* no arguments):
*
Expand Down Expand Up @@ -521,7 +521,7 @@ export class AssembledTransaction<T> {
/**
* TSDoc: You must provide this here if you did not provide one before
*/
signTransaction?: ContractClientOptions["signTransaction"];
signTransaction?: Options["signTransaction"];
} = {}): Promise<SentTransaction<T>> => {
if (!this.built) {
throw new Error("Transaction has not yet been simulated");
Expand Down Expand Up @@ -669,7 +669,7 @@ export class AssembledTransaction<T> {
* the `signAuthEntry` function from the `ContractClient` options. Must
* sign things as the given `publicKey`.
*/
signAuthEntry?: ContractClientOptions["signAuthEntry"];
signAuthEntry?: Options["signAuthEntry"];
} = {}): Promise<void> => {
if (!this.built)
throw new Error("Transaction has not yet been assembled or simulated");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Keypair, TransactionBuilder, hash } from "@stellar/stellar-base";
import type { AssembledTransaction } from "./assembled_transaction";
import type { ContractClient } from "./client";
import type { Client } from "./client";

/**
* For use with {@link ContractClient} and {@link AssembledTransaction}.
* For use with {@link Client} and {@link AssembledTransaction}.
* Implements `signTransaction` and `signAuthEntry` with signatures expected by
* those classes. This is useful for testing and maybe some simple Node
* applications. Feel free to use this as a starting point for your own
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { xdr } from "@stellar/stellar-base";
import { ContractSpec } from "../../contract_spec";
import { Spec } from "./spec";
import { AssembledTransaction } from "./assembled_transaction";
import type { ContractClientOptions, MethodOptions } from "./types";
import type { Options, MethodOptions } from "./types";

export class ContractClient {
export class Client {
/**
* Generate a class from the contract spec that where each contract method
* gets included with an identical name.
Expand All @@ -13,10 +13,10 @@ export class ContractClient {
* transaction.
*/
constructor(
/** {@link ContractSpec} to construct a Client for */
public readonly spec: ContractSpec,
/** see {@link ContractClientOptions} */
public readonly options: ContractClientOptions,
/** {@link Spec} to construct a Client for */
public readonly spec: Spec,
/** see {@link Options} */
public readonly options: Options,
) {
this.spec.funcs().forEach((xdrFn) => {
const method = xdrFn.name().toString();
Expand All @@ -34,7 +34,7 @@ export class ContractClient {
...acc,
[curr.value()]: { message: curr.doc().toString() },
}),
{} as Pick<ContractClientOptions, "errorTypes">,
{} as Pick<Options, "errorTypes">,
),
parseResultXdr: (result: xdr.ScVal) =>
spec.funcResToNative(method, result),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ export * from "./basic_node_signer";
export * from "./client";
export * from "./rust_result";
export * from "./sent_transaction";
export * from "./spec";
export * from "./types";
export * from "./utils";
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/* disable max-classes rule, because extending error shouldn't count! */
/* eslint max-classes-per-file: 0 */
import { SorobanDataBuilder, TransactionBuilder } from "@stellar/stellar-base";
import type { ContractClientOptions, MethodOptions, Tx } from "./types";
import { Server } from "../server"
import { Api } from "../api"
import type { Options, MethodOptions, Tx } from "./types";
import { Server } from "../rpc/server"
import { Api } from "../rpc/api"
import { DEFAULT_TIMEOUT, withExponentialBackoff } from "./utils";
import type { AssembledTransaction } from "./assembled_transaction";

Expand Down Expand Up @@ -53,7 +53,7 @@ export class SentTransaction<T> {
};

constructor(
public signTransaction: ContractClientOptions["signTransaction"],
public signTransaction: Options["signTransaction"],
public assembled: AssembledTransaction<T>,
) {
if (!signTransaction) {
Expand All @@ -73,7 +73,7 @@ export class SentTransaction<T> {
*/
static init = async <U>(
/** More info in {@link MethodOptions} */
signTransaction: ContractClientOptions["signTransaction"],
signTransaction: Options["signTransaction"],
/** {@link AssembledTransaction} from which this SentTransaction was initialized */
assembled: AssembledTransaction<U>,
): Promise<SentTransaction<U>> => {
Expand Down
4 changes: 2 additions & 2 deletions src/contract_spec.ts → src/contractclient/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
Contract,
scValToBigInt,
} from "@stellar/stellar-base"
import { Ok } from "./soroban/contract_client/rust_result"
import { Ok } from "./rust_result"

export interface Union<T> {
tag: string;
Expand Down Expand Up @@ -48,7 +48,7 @@ function readObj(args: object, input: xdr.ScSpecFunctionInputV0): any {
* console.log(result); // {success: true}
* ```
*/
export class ContractSpec {
export class Spec {
public entries: xdr.ScSpecEntry[] = [];

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* disable PascalCase naming convention, to avoid breaking change */
/* eslint-disable @typescript-eslint/naming-convention */
import { BASE_FEE, Memo, MemoType, Operation, Transaction, xdr } from "@stellar/stellar-base";
import type { ContractClient } from "./client";
import type { Client } from "./client";
import type { AssembledTransaction } from "./assembled_transaction";
import { DEFAULT_TIMEOUT } from "./utils";

Expand All @@ -23,7 +23,7 @@ export type Duration = bigint;
*/
export type Tx = Transaction<Memo<MemoType>, Operation[]>;

export type ContractClientOptions = {
export type Options = {
/**
* The public key of the account that will send this transaction. You can
* override this for specific methods later, like
Expand Down Expand Up @@ -74,14 +74,14 @@ export type ContractClientOptions = {
allowHttp?: boolean;
/**
* This gets filled in automatically from the ContractSpec when you
* instantiate a {@link ContractClient}.
* instantiate a {@link Client}.
*
* Background: If the contract you're calling uses the `#[contracterror]`
* macro to create an `Error` enum, then those errors get included in the
* on-chain XDR that also describes your contract's methods. Each error will
* have a specific number.
*
* A ContractClient makes method calls with an {@link AssembledTransaction}.
* A Client makes method calls with an {@link AssembledTransaction}.
* When one of these method calls encounters an error, `AssembledTransaction`
* will first attempt to parse the error as an "official" `contracterror`
* error, by using this passed-in `errorTypes` object. See
Expand Down Expand Up @@ -112,7 +112,7 @@ export type MethodOptions = {
};

export type AssembledTransactionOptions<T = string> = MethodOptions &
ContractClientOptions & {
Options & {
method: string;
args?: any[];
parseResultXdr: (xdr: xdr.ScVal) => T;
Expand Down
File renamed without changes.
6 changes: 4 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ export * as Friendbot from './friendbot';
export * as Horizon from './horizon';

// Soroban RPC-related classes to expose
export * as SorobanRpc from './soroban';
export * from './contract_spec';
export * as Rpc from './rpc';

// Soroban Contract-related classes to expose
export * as ContractClient from './contractclient'

// expose classes and functions from stellar-base
export * from '@stellar/stellar-base';
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 0 additions & 1 deletion src/soroban/index.ts → src/rpc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,5 @@ export { Server, Durability } from "./server";
export { default as AxiosClient } from "./axios";
export { parseRawSimulation, parseRawEvents } from "./parsers";
export * from "./transaction";
export * as ContractClient from "./contract_client";

export default module.exports;
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
8 changes: 3 additions & 5 deletions test/e2e/src/test-custom-types.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
const test = require('ava')
const { Address, SorobanRpc } = require('../../..')
const { Address, ContractClient } = require('../../..')
const { clientFor } = require('./util')

const { Ok, Err } = SorobanRpc.ContractClient

test.before(async t => {
const { client, keypair, contractId } = await clientFor('customTypes')
const publicKey = keypair.publicKey()
Expand Down Expand Up @@ -31,11 +29,11 @@ test('woid', async t => {
test('u32_fail_on_even', async t => {
t.deepEqual(
(await t.context.client.u32_fail_on_even({ u32_: 1 })).result,
new Ok(1)
new ContractClient.Ok(1)
)
t.deepEqual(
(await t.context.client.u32_fail_on_even({ u32_: 2 })).result,
new Err({ message: "Please provide an odd number" })
new ContractClient.Err({ message: "Please provide an odd number" })
)
})

Expand Down
6 changes: 2 additions & 4 deletions test/e2e/src/test-swap.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
const test = require('ava')
const { SorobanRpc } = require('../../..')
const { ContractClient } = require('../../..')
const { clientFor, generateFundedKeypair } = require('./util')

const { AssembledTransaction } = SorobanRpc.ContractClient

const amountAToSwap = 2n
const amountBToSwap = 1n

Expand Down Expand Up @@ -53,7 +51,7 @@ test('calling `signAndSend()` too soon throws descriptive error', async t => {
min_b_for_a: amountBToSwap,
})
const error = await t.throwsAsync(tx.signAndSend())
t.true(error instanceof AssembledTransaction.Errors.NeedsMoreSignatures, `error is not of type 'NeedsMoreSignaturesError'; instead it is of type '${error?.constructor.name}'`)
t.true(error instanceof ContractClient.AssembledTransaction.Errors.NeedsMoreSignatures, `error is not of type 'NeedsMoreSignaturesError'; instead it is of type '${error?.constructor.name}'`)
if (error) t.regex(error.message, /needsNonInvokerSigningBy/)
})

Expand Down
14 changes: 5 additions & 9 deletions test/e2e/src/util.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
const { spawnSync } = require('node:child_process')
const { ContractSpec, Keypair, SorobanRpc } = require('../../..')
const {
ContractClient,
basicNodeSigner,
} = SorobanRpc.ContractClient
const { ContractClient, Keypair } = require('../../..')

const contracts = {
customTypes: {
Expand Down Expand Up @@ -40,7 +36,7 @@ async function generateFundedKeypair() {
module.exports.generateFundedKeypair = generateFundedKeypair

/**
* Generates a ContractClient for the contract with the given name.
* Generates a Contrat.Client for the contract with the given name.
* Also generates a new account to use as as the keypair of this contract. This
* account is funded by friendbot. You can pass in an account to re-use the
* same account with multiple contract clients.
Expand All @@ -57,9 +53,9 @@ async function clientFor(contract, { keypair = generateFundedKeypair(), contract
}

keypair = await keypair // eslint-disable-line no-param-reassign
const wallet = basicNodeSigner(keypair, networkPassphrase)
const wallet = ContractClient.basicNodeSigner(keypair, networkPassphrase)

const spec = new ContractSpec(contracts[contract].xdr)
const spec = new ContractClient.Spec(contracts[contract].xdr)

const wasmHash = contracts[contract].hash;
if (!wasmHash) {
Expand All @@ -76,7 +72,7 @@ async function clientFor(contract, { keypair = generateFundedKeypair(), contract
wasmHash,
], { shell: true, encoding: "utf8" }).stdout.trim();

const client = new ContractClient(spec, {
const client = new ContractClient.Client(spec, {
networkPassphrase,
contractId,
rpcUrl,
Expand Down
Loading

0 comments on commit bfe370c

Please sign in to comment.