Skip to content

Commit

Permalink
[ts-sdk] Introduce SDK Connection class (MystenLabs#8088)
Browse files Browse the repository at this point in the history
This is an idea to create a dedicated class to represent a `connection`,
meaning a bag of configuration related to how to connect to a network. I
thought about naming this `Network` but I thought that could be a little
confusing. Maybe something like `Endpoints` would be a better class
name? Open to suggestions on the naming.
  • Loading branch information
Jordan-Mysten authored Feb 22, 2023
1 parent 476f512 commit aa650aa
Show file tree
Hide file tree
Showing 19 changed files with 151 additions and 132 deletions.
6 changes: 6 additions & 0 deletions .changeset/thick-weeks-attack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@mysten/wallet-adapter-unsafe-burner": minor
"@mysten/sui.js": minor
---

Introduce new `Connection` class, which is used to define the endpoints that are used when interacting with the network.
28 changes: 24 additions & 4 deletions apps/explorer/src/utils/api/DefaultRpcClient.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { JsonRpcProvider } from '@mysten/sui.js';
import {
JsonRpcProvider,
Connection,
devnetConnection,
localnetConnection,
} from '@mysten/sui.js';

import { getEndpoint, Network } from './rpcSetting';
export enum Network {
LOCAL = 'LOCAL',
DEVNET = 'DEVNET',
TESTNET = 'TESTNET',
}

export { Network, getEndpoint };
const CONNECTIONS: Record<Network, Connection> = {
[Network.LOCAL]: localnetConnection,
[Network.DEVNET]: devnetConnection,
[Network.TESTNET]: new Connection({
fullnode: 'https://fullnode-explorer.testnet.sui.io:443',
}),
};

const defaultRpcMap: Map<Network | string, JsonRpcProvider> = new Map();
/** @deprecated This shouldn't be directly used, and instead should be used through `useRpc()`. */
export const DefaultRpcClient = (network: Network | string) => {
const existingClient = defaultRpcMap.get(network);
if (existingClient) return existingClient;

const provider = new JsonRpcProvider(getEndpoint(network));
const connection =
network in Network
? CONNECTIONS[network as Network]
: new Connection({ fullnode: network });

const provider = new JsonRpcProvider(connection);
defaultRpcMap.set(network, provider);
return provider;
};
22 changes: 0 additions & 22 deletions apps/explorer/src/utils/api/rpcSetting.ts

This file was deleted.

4 changes: 3 additions & 1 deletion apps/explorer/tests/utils/localnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

// eslint-disable-next-line import/order
import 'tsconfig-paths/register';
// eslint-disable-next-line import/order
import {
Ed25519Keypair,
JsonRpcProvider,
Expand All @@ -11,6 +12,7 @@ import {
type Keypair,
SuiExecuteTransactionResponse,
assert,
localnetConnection,
} from '@mysten/sui.js';

const addressToKeypair = new Map<string, Keypair>();
Expand All @@ -20,7 +22,7 @@ export async function mint(address: string) {
if (!keypair) {
throw new Error('missing keypair');
}
const provider = new JsonRpcProvider('http://127.0.0.1:9000', {
const provider = new JsonRpcProvider(localnetConnection, {
skipDataValidation: false,
});
const signer = new RawSigner(
Expand Down
37 changes: 19 additions & 18 deletions apps/wallet/src/ui/app/ApiProvider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { JsonRpcProvider, LocalTxnDataSerializer } from '@mysten/sui.js';
import {
Connection,
JsonRpcProvider,
LocalTxnDataSerializer,
} from '@mysten/sui.js';

import { BackgroundServiceSigner } from './background-client/BackgroundServiceSigner';
import { queryClient } from './helpers/queryClient';
Expand All @@ -17,32 +21,28 @@ type EnvInfo = {
env: API_ENV;
};

type ApiEndpoints = {
fullNode: string;
faucet?: string;
} | null;
export const API_ENV_TO_INFO: Record<API_ENV, EnvInfo> = {
[API_ENV.local]: { name: 'Local', env: API_ENV.local },
[API_ENV.devNet]: { name: 'Sui Devnet', env: API_ENV.devNet },
[API_ENV.customRPC]: { name: 'Custom RPC URL', env: API_ENV.customRPC },
[API_ENV.testNet]: { name: 'Sui Testnet', env: API_ENV.testNet },
};

export const ENV_TO_API: Record<API_ENV, ApiEndpoints> = {
[API_ENV.local]: {
fullNode: process.env.API_ENDPOINT_LOCAL_FULLNODE || '',
export const ENV_TO_API: Record<API_ENV, Connection | null> = {
[API_ENV.local]: new Connection({
fullnode: process.env.API_ENDPOINT_LOCAL_FULLNODE || '',
faucet: process.env.API_ENDPOINT_LOCAL_FAUCET || '',
},
[API_ENV.devNet]: {
fullNode: process.env.API_ENDPOINT_DEV_NET_FULLNODE || '',
}),
[API_ENV.devNet]: new Connection({
fullnode: process.env.API_ENDPOINT_DEV_NET_FULLNODE || '',
faucet: process.env.API_ENDPOINT_DEV_NET_FAUCET || '',
},
}),
[API_ENV.customRPC]: null,
[API_ENV.testNet]: {
fullNode: process.env.API_ENDPOINT_TEST_NET_FULLNODE || '',
[API_ENV.testNet]: new Connection({
fullnode: process.env.API_ENDPOINT_TEST_NET_FULLNODE || '',
// NOTE: Faucet is currently disabled for testnet:
// faucet: process.env.API_ENDPOINT_TEST_NET_FAUCET || '',
},
}),
};

function getDefaultApiEnv() {
Expand All @@ -57,7 +57,7 @@ function getDefaultAPI(env: API_ENV) {
const apiEndpoint = ENV_TO_API[env];
if (
!apiEndpoint ||
apiEndpoint.fullNode === '' ||
apiEndpoint.fullnode === '' ||
apiEndpoint.faucet === ''
) {
throw new Error(`API endpoint not found for API_ENV ${env}`);
Expand Down Expand Up @@ -90,8 +90,9 @@ export default class ApiProvider {
customRPC?: string | null
) {
this._apiFullNodeProvider = new JsonRpcProvider(
customRPC ?? getDefaultAPI(apiEnv).fullNode,
{ faucetURL: customRPC ? '' : getDefaultAPI(apiEnv).faucet }
customRPC
? new Connection({ fullnode: customRPC })
: getDefaultAPI(apiEnv)
);
this._signerByAddress.clear();
// We also clear the query client whenever set set a new API provider:
Expand Down
2 changes: 1 addition & 1 deletion apps/wallet/src/ui/app/shared/faucet/useFaucetMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function useFaucetMutation() {
return {
...mutation,
/** If the currently-configured endpoint supports faucet: */
enabled: !!api.endpoints.faucet,
enabled: !!api.connection.faucet,
/**
* is any faucet request in progress across different instances of the mutation
*/
Expand Down
6 changes: 2 additions & 4 deletions dapps/frenemies/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0

import { z } from "zod";
import { Network } from "@mysten/sui.js";

// Oops, we need to bump the round.
export const ROUND_OFFSET = 5n;
Expand All @@ -14,9 +13,8 @@ export const GAME_END_DATE = new Date(
export const gameIsOver = () => Date.now() >= GAME_END_DATE.getTime();

const ConfigSchema = z.object({
VITE_NETWORK: z
.union([z.nativeEnum(Network), z.string()])
.default(Network.LOCAL),
/** Fullnode RPC URL to use for the dapp */
VITE_NETWORK: z.string(),
/** Leaderboard object: shared, contains information about 1000 top players */
VITE_LEADERBOARD: z.string(),
/** Name Registry: shared, used when signing up (and getting a Scorecard) */
Expand Down
6 changes: 4 additions & 2 deletions dapps/frenemies/src/network/provider.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { JsonRpcProvider } from "@mysten/sui.js";
import { Connection, JsonRpcProvider } from "@mysten/sui.js";

import { config } from "../config";

const provider = new JsonRpcProvider(config.VITE_NETWORK);
const provider = new JsonRpcProvider(
new Connection({ fullnode: config.VITE_NETWORK })
);

export default provider;
22 changes: 12 additions & 10 deletions sdk/typescript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ The `JsonRpcProvider` class provides a connection to the JSON-RPC Server and sho
- DevNet: https://fullnode.devnet.sui.io

```typescript
import { JsonRpcProvider, Network } from '@mysten/sui.js';
import { JsonRpcProvider, devnetConnection } from '@mysten/sui.js';
// connect to Devnet
const provider = new JsonRpcProvider(Network.DEVNET);
const provider = new JsonRpcProvider(devnetConnection);
// get tokens from the DevNet faucet server
await provider.requestSuiFromFaucet(
'0xbff6ccc8707aa517b4f1b95750a2a8c666012df3',
Expand All @@ -95,24 +95,26 @@ await provider.requestSuiFromFaucet(
For local development, you can run `cargo run --bin sui-test-validator` to spin up a local network with a local validator, a fullnode, and a faucet server.

```typescript
import { JsonRpcProvider, Network } from '@mysten/sui.js';
import { JsonRpcProvider, localnetConnection } from '@mysten/sui.js';
// connect to local RPC server
const provider = new JsonRpcProvider(Network.LOCAL);
const provider = new JsonRpcProvider(localnetConnection);
// get tokens from the local faucet server
await provider.requestSuiFromFaucet(
'0xbff6ccc8707aa517b4f1b95750a2a8c666012df3',
);
```

You can also pass in custom URLs to your own fullnode and faucet server
You can also construct your own in custom connections, with your own URLs to your fullnode and faucet server

```typescript
import { JsonRpcProvider } from '@mysten/sui.js';
// connect to a custom RPC server
const provider = new JsonRpcProvider('https://fullnode.devnet.sui.io', {
// you can also skip providing this field if you don't plan to interact with the faucet
faucetURL: 'https://faucet.devnet.sui.io/gas',
import { JsonRpcProvider, Connection } from '@mysten/sui.js';
// Construct your connection:
const connection = new Connection({
fullnode: 'https://fullnode.devnet.sui.io',
faucet: 'https://faucet.devnet.sui.io/gas',
});
// connect to a custom RPC server
const provider = new JsonRpcProvider(connection);
// get tokens from a custom faucet server
await provider.requestSuiFromFaucet(
'0xbff6ccc8707aa517b4f1b95750a2a8c666012df3',
Expand Down
3 changes: 2 additions & 1 deletion sdk/typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"build": "node genversion.mjs && pnpm build:types && tsup ./src/index.ts --format esm,cjs --sourcemap",
"build:types": "tsc --build",
"doc": "typedoc",
"test": "pnpm test:unit",
"test": "pnpm test:typecheck && pnpm test:unit",
"test:typecheck": "tsc -p ./test",
"test:unit": "vitest run unit",
"test:e2e": "wait-on http://127.0.0.1:9123 -l --timeout 120000 && vitest run e2e",
"test:e2e:nowait": "vitest run e2e",
Expand Down
2 changes: 1 addition & 1 deletion sdk/typescript/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ export * from './signers/raw-signer';
export * from './signers/signer-with-provider';

export * from './types';
export * from './utils/api-endpoints';
export * from './utils/format';
export * from './utils/intent';
export * from './utils/verify';
export * from './rpc/connection';

export * from './framework';

Expand Down
34 changes: 11 additions & 23 deletions sdk/typescript/src/providers/json-rpc-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,14 @@ import {
WebsocketClient,
WebsocketClientOptions,
} from '../rpc/websocket-client';
import { ApiEndpoints, Network, NETWORK_TO_API } from '../utils/api-endpoints';
import { requestSuiFromFaucet } from '../rpc/faucet-client';
import { any, is, number, array } from 'superstruct';
import { UnserializedSignableTransaction } from '../signers/txn-data-serializers/txn-data-serializer';
import { LocalTxnDataSerializer } from '../signers/txn-data-serializers/local-txn-data-serializer';
import { toB64 } from '@mysten/bcs';
import { SerializedSignature } from '../cryptography/signature';
import { pkgVersion } from '../pkg-version';
import { Connection, devnetConnection } from '../rpc/connection';

export const TARGETED_RPC_VERSION = '0.27.0';

Expand Down Expand Up @@ -104,12 +104,6 @@ export type RpcProviderOptions = {
* Cache timeout in seconds for the RPC API Version
*/
versionCacheTimoutInSeconds?: number;
/**
* URL to a faucet(optional). If you initialize `JsonRpcProvider`
* with a known `Network` value, this will be populated with a default
* value
*/
faucetURL?: string;
};

const DEFAULT_OPTIONS: RpcProviderOptions = {
Expand All @@ -119,7 +113,7 @@ const DEFAULT_OPTIONS: RpcProviderOptions = {
};

export class JsonRpcProvider extends Provider {
public endpoints: ApiEndpoints;
public connection: Connection;
protected client: JsonRpcClient;
protected wsClient: WebsocketClient;
private rpcApiVersion: RpcApiVersion | undefined;
Expand All @@ -128,27 +122,20 @@ export class JsonRpcProvider extends Provider {
/**
* Establish a connection to a Sui RPC endpoint
*
* @param endpoint URL to the Sui RPC endpoint, or a `Network` enum
* @param connection The `Connection` object containing configuration for the network.
* @param options configuration options for the provider
*/
constructor(
endpoint: string | Network = Network.DEVNET,
// TODO: Probably remove the default endpoint here:
connection: Connection = devnetConnection,
public options: RpcProviderOptions = DEFAULT_OPTIONS,
) {
super();

if ((Object.values(Network) as string[]).includes(endpoint)) {
this.endpoints = NETWORK_TO_API[endpoint as Network];
} else {
this.endpoints = {
fullNode: endpoint,
faucet: options.faucetURL,
};
}
this.connection = connection;

const opts = { ...DEFAULT_OPTIONS, ...options };

this.client = new JsonRpcClient(this.endpoints.fullNode);
// TODO: uncomment this when 0.27.0 is released. We cannot do this now because
// the current Devnet(0.26.0) does not support the header. And we need to make
// a request to RPC to know which version it is running. Therefore, we do not
Expand All @@ -160,8 +147,9 @@ export class JsonRpcProvider extends Provider {
// 'Client-Target-Api-Version': TARGETED_RPC_VERSION,
// });
// TODO: add header for websocket request
this.client = new JsonRpcClient(this.connection.fullnode);
this.wsClient = new WebsocketClient(
this.endpoints.fullNode,
this.connection.websocket,
opts.skipDataValidation!,
opts.socketOptions,
);
Expand Down Expand Up @@ -189,7 +177,7 @@ export class JsonRpcProvider extends Provider {
this.rpcApiVersion &&
!lt(versionToString(this.rpcApiVersion), '0.27.0')
) {
this.client = new JsonRpcClient(this.endpoints.fullNode, {
this.client = new JsonRpcClient(this.connection.fullnode, {
'Client-Sdk-Type': 'typescript',
'Client-Sdk-Version': pkgVersion,
'Client-Target-Api-Version': TARGETED_RPC_VERSION,
Expand All @@ -210,10 +198,10 @@ export class JsonRpcProvider extends Provider {
recipient: SuiAddress,
httpHeaders?: HttpHeaders,
): Promise<FaucetResponse> {
if (!this.endpoints.faucet) {
if (!this.connection.faucet) {
throw new Error('Faucet URL is not specified');
}
return requestSuiFromFaucet(this.endpoints.faucet, recipient, httpHeaders);
return requestSuiFromFaucet(this.connection.faucet, recipient, httpHeaders);
}

// Coins
Expand Down
Loading

0 comments on commit aa650aa

Please sign in to comment.