Skip to content

Commit

Permalink
Allow an undefined publicKey for view calls (#941)
Browse files Browse the repository at this point in the history
Before ContractClient lived in stellar-sdk, we allowed (the equivalent
of [[1]]) setting `publicKey` to `undefined`.

This is handy, if you're trying to get the result of view calls and you
only need the simulation. Our main onboarding tutorial assumes that we
have this functionality, and it definitely makes for a more incremental
build-up of mental models about how Soroban and Stellar work.

While making ContractClient, we thought maybe this added unnecessary
complexity to the interface, and further thought people would be able to
pass `Keypair.random()` if they wanted a fake account.

The trouble with the `Keypair.random()` approach is that the call to
`server.getAccount` fails, because the account has not been funded.

We _could_ fix this by doing:

    let account: Account;
    try {
      account = tx.server.getAccount(options.publicKey)
    } catch () {
      account = new Account(options.publicKey, '0')
    }

But this seems clumsy. Even if there's a better way to check that the
caught error is the error we expect (`{ code: 404, message: 'Account not
found' }`), why allow passing a publicKey if it's actually ignored? It
seems like a clearer expression of intent to allow the publicKey to be
`undefined`.

^[1]: https://github.com/stellar/soroban-cli/blob/5f367edb3cf4511794b64aed60a23c5d8b6b55e6/cmd/crates/soroban-spec-typescript/src/project_template/src/assembled-tx.ts#L247-L257
  • Loading branch information
chadoh committed Apr 25, 2024
1 parent c1d7588 commit 87db95b
Show file tree
Hide file tree
Showing 5 changed files with 22 additions and 4 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -12,6 +12,10 @@ A breaking change will get clearly marked in this log.

## [v11.3.0](https://github.com/stellar/js-stellar-sdk/compare/v11.2.2...v11.3.0)

### Fixed

* `ContractClient` new allows `publicKey` to be undefined, returning functionality that had been in the bindings previously generated by `soroban contract bindings typescript`. More details: [#941](https://github.com/stellar/js-stellar-sdk/pull/941)

### Added
* Introduces an entire suite of helpers to assist with interacting with smart contracts ([#929](https://github.com/stellar/js-stellar-sdk/pull/929)):
- `ContractClient`: generate a class from the contract specification where each Rust contract method gets a matching method in this class. Each method returns an `AssembledTransaction` that can be used to modify, simulate, decode results, and possibly sign, & submit the transaction.
Expand Down
7 changes: 6 additions & 1 deletion src/contract_client/assembled_transaction.ts
Expand Up @@ -5,6 +5,7 @@ import type {
XDR_BASE64,
} from "./types";
import {
Account,
BASE_FEE,
Contract,
Operation,
Expand All @@ -22,6 +23,8 @@ import {
} from "./utils";
import { SentTransaction } from "./sent_transaction";

export const NULL_ACCOUNT = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"

/**
* The main workhorse of {@link ContractClient}. This class is used to wrap a
* transaction-under-construction and provide high-level interfaces to the most
Expand Down Expand Up @@ -364,7 +367,9 @@ export class AssembledTransaction<T> {
const tx = new AssembledTransaction(options);
const contract = new Contract(options.contractId);

const account = await tx.server.getAccount(options.publicKey);
const account = options.publicKey
? await tx.server.getAccount(options.publicKey)
: new Account(NULL_ACCOUNT, "0");

tx.raw = new TransactionBuilder(account, {
fee: options.fee ?? BASE_FEE,
Expand Down
2 changes: 1 addition & 1 deletion src/contract_client/types.ts
Expand Up @@ -32,7 +32,7 @@ export type ContractClientOptions = {
* {@link AssembledTransaction#signAndSend} and
* {@link AssembledTransaction#signAuthEntries}.
*/
publicKey: string;
publicKey?: string;
/**
* A function to sign the transaction using the private key corresponding to
* the given `publicKey`. You do not need to provide this, for read-only
Expand Down
12 changes: 10 additions & 2 deletions test/e2e/src/test-custom-types.js
Expand Up @@ -4,17 +4,25 @@ const { Ok, Err } = require('../../../lib/rust_types')
const { clientFor } = require('./util')

test.before(async t => {
const { client, keypair } = await clientFor('customTypes')
const { client, keypair, contractId } = await clientFor('customTypes')
const publicKey = keypair.publicKey()
const addr = Address.fromString(publicKey)
t.context = { client, publicKey, addr } // eslint-disable-line no-param-reassign
t.context = { client, publicKey, addr, contractId } // eslint-disable-line no-param-reassign
});

test('hello', async t => {
const { result } = await t.context.client.hello({ hello: 'tests' })
t.is(result, 'tests')
})

test("view method with empty keypair", async (t) => {
const { client: client2 } = await clientFor('customTypes', {
keypair: undefined,
contractId: t.context.contractId
});
t.is((await client2.hello({ hello: "anonymous" })).result, "anonymous");
});

test('woid', async t => {
t.is((await t.context.client.woid()).result, null)
})
Expand Down
1 change: 1 addition & 0 deletions test/e2e/src/test-hello-world.js
@@ -1,5 +1,6 @@
const test = require('ava')
const { clientFor } = require('./util')
const { Keypair } = require('../../..')

test("hello", async (t) => {
const { client } = await clientFor('helloWorld')
Expand Down

0 comments on commit 87db95b

Please sign in to comment.