Skip to content
Draft
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
6 changes: 5 additions & 1 deletion packages/typescript/src/experimental/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@
Experimental_SuiClientTypes,
SuiClientRegistration,
} from './types.js';
import { ClientMiddlewareStack } from './middleware.js';

Check failure on line 13 in packages/typescript/src/experimental/client.ts

View workflow job for this annotation

GitHub Actions / Lint, Build, and Test

Dependency cycle via ../client/index.js:4=>./client.js:20

export abstract class Experimental_BaseClient {
network: Experimental_SuiClientTypes.Network;
cache = new ClientCache();
middleware = new ClientMiddlewareStack();
root: Experimental_BaseClient;

constructor({ network }: Experimental_SuiClientTypes.SuiClientOptions) {
constructor({ network, root }: Experimental_SuiClientTypes.SuiClientOptions) {
this.network = network;
this.root = root ?? this;
}

abstract core: Experimental_CoreClient;
Expand Down
83 changes: 83 additions & 0 deletions packages/typescript/src/experimental/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { SuiClient } from '../client/index.js';

Check failure on line 4 in packages/typescript/src/experimental/middleware.ts

View workflow job for this annotation

GitHub Actions / Lint, Build, and Test

Dependency cycle via ./client.js:20=>../experimental/client.js:6
import type { Experimental_SuiClientTypes } from './types.js';

type MiddlewareFunction = <Args extends unknown[], Return extends unknown>(
scope: string,
method: string,
args: Args,
next: (...args: Args) => Promise<Return>,
) => Promise<Return>;

export class ClientMiddlewareStack {
#middleware: MiddlewareFunction[] = [];

use(middleware: MiddlewareFunction) {
if (this.#middleware.includes(middleware)) {
throw new Error(`Middleware ${middleware.name} is already registered`);
}
this.#middleware.push(middleware);
}

wrap<Args extends unknown[], Return extends unknown>(
scope: string,
name: string,
fn: (...args: Args) => Promise<Return>,
) {
const stack = this;
return function <T>(this: T, ...args: Args) {
return stack.run(scope, name, fn, this, args);
};
}

run<Args extends unknown[], Return extends unknown>(
scope: string,
name: string,
fn: (...args: Args) => Promise<Return>,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be the last arg

ctx: unknown,
args: Args,
) {
const createNext = (i: number) => {
let calledNext = false;

return async (...args: Args): Promise<Return> => {
if (calledNext) {
throw new Error(`next() was call multiple times`);
}

calledNext = true;

if (i >= this.#middleware.length) {
return fn.apply(ctx, args);
}

const middleware = this.#middleware[i];
return middleware(scope, name, args, async (...args) => createNext(i + 1)(...args));
};
};

return createNext(0)(...args);
}
}

new SuiClient({

Check failure on line 65 in packages/typescript/src/experimental/middleware.ts

View workflow job for this annotation

GitHub Actions / Localnet

test/e2e/checkpoint.test.ts

TypeError: SuiClient is not a constructor ❯ src/experimental/middleware.ts:65:1 ❯ src/experimental/client.ts:13:1

Check failure on line 65 in packages/typescript/src/experimental/middleware.ts

View workflow job for this annotation

GitHub Actions / Localnet

test/e2e/coin-metadata.test.ts

TypeError: SuiClient is not a constructor ❯ src/experimental/middleware.ts:65:1 ❯ src/experimental/client.ts:13:1

Check failure on line 65 in packages/typescript/src/experimental/middleware.ts

View workflow job for this annotation

GitHub Actions / Localnet

test/e2e/coin-read.test.ts

TypeError: SuiClient is not a constructor ❯ src/experimental/middleware.ts:65:1 ❯ src/experimental/client.ts:13:1

Check failure on line 65 in packages/typescript/src/experimental/middleware.ts

View workflow job for this annotation

GitHub Actions / Localnet

test/e2e/coin-with-balance.test.ts

TypeError: SuiClient is not a constructor ❯ src/experimental/middleware.ts:65:1 ❯ src/experimental/client.ts:13:1

Check failure on line 65 in packages/typescript/src/experimental/middleware.ts

View workflow job for this annotation

GitHub Actions / Localnet

test/e2e/dev-inspect.test.ts

TypeError: SuiClient is not a constructor ❯ src/experimental/middleware.ts:65:1 ❯ src/experimental/client.ts:13:1

Check failure on line 65 in packages/typescript/src/experimental/middleware.ts

View workflow job for this annotation

GitHub Actions / Localnet

test/e2e/dynamic-fields.test.ts

TypeError: SuiClient is not a constructor ❯ src/experimental/middleware.ts:65:1 ❯ src/experimental/client.ts:13:1

Check failure on line 65 in packages/typescript/src/experimental/middleware.ts

View workflow job for this annotation

GitHub Actions / Localnet

test/e2e/entry-point-string.test.ts

TypeError: SuiClient is not a constructor ❯ src/experimental/middleware.ts:65:1 ❯ src/experimental/client.ts:13:1

Check failure on line 65 in packages/typescript/src/experimental/middleware.ts

View workflow job for this annotation

GitHub Actions / Localnet

test/e2e/id-entry-args.test.ts

TypeError: SuiClient is not a constructor ❯ src/experimental/middleware.ts:65:1 ❯ src/experimental/client.ts:13:1

Check failure on line 65 in packages/typescript/src/experimental/middleware.ts

View workflow job for this annotation

GitHub Actions / Localnet

test/e2e/invalid-ids.test.ts

TypeError: SuiClient is not a constructor ❯ src/experimental/middleware.ts:65:1 ❯ src/experimental/client.ts:13:1

Check failure on line 65 in packages/typescript/src/experimental/middleware.ts

View workflow job for this annotation

GitHub Actions / Localnet

test/e2e/multisig.test.ts

TypeError: SuiClient is not a constructor ❯ src/experimental/middleware.ts:65:1 ❯ src/experimental/client.ts:13:1
network: 'mainnet',
url: 'https://fullnode.mainnet.sui.io',
}).middleware.use(async (scope, name, args, next) => {
if (scope === 'core' && name === 'getObjects') {
const [options] = args as unknown as [Experimental_SuiClientTypes.GetObjectsOptions];

return next(
...([
{
...options,
objectIds: await resolveMvrTypes(options.objectIds),
},
] as never),
);
}

return next(...args);
});
52 changes: 28 additions & 24 deletions packages/typescript/src/experimental/transports/jsonRPC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import { batch } from '../../transactions/plugins/utils.js';
import { Transaction } from '../../transactions/Transaction.js';
import { normalizeStructTag } from '../../utils/sui-types.js';
import { Experimental_CoreClient } from '../core.js';

Check failure on line 18 in packages/typescript/src/experimental/transports/jsonRPC.ts

View workflow job for this annotation

GitHub Actions / Lint, Build, and Test

Dependency cycle via ./client.js:7=>./middleware.js:13=>../client/index.js:4=>./client.js:20
import { ObjectError } from '../errors.js';
import type { Experimental_SuiClientTypes } from '../types.js';

Expand All @@ -23,37 +23,41 @@
#jsonRpcClient: SuiClient;

constructor(jsonRpcClient: SuiClient) {
super({ network: jsonRpcClient.network });
super({ network: jsonRpcClient.network, root: jsonRpcClient.root });
this.#jsonRpcClient = jsonRpcClient;
}

async getObjects(options: Experimental_SuiClientTypes.GetObjectsOptions) {
const batches = batch(options.objectIds, 50);
const results: Experimental_SuiClientTypes.GetObjectsResponse['objects'] = [];

for (const batch of batches) {
const objects = await this.#jsonRpcClient.multiGetObjects({
ids: batch,
options: {
showOwner: true,
showType: true,
showBcs: true,
},
});
getObjects = this.root.middleware.wrap(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love making all of these instance properties... We could also do

getObjects(options) {
  return this.root.middleware.run('core', 'getObjects', this, [options], (options) => {
     // contents
  })
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah neither do I, I'd probably do it this way

'core',
'getObjects',
async (options: Experimental_SuiClientTypes.GetObjectsOptions) => {
const batches = batch(options.objectIds, 50);
const results: Experimental_SuiClientTypes.GetObjectsResponse['objects'] = [];

for (const batch of batches) {
const objects = await this.#jsonRpcClient.multiGetObjects({
ids: batch,
options: {
showOwner: true,
showType: true,
showBcs: true,
},
});

for (const [idx, object] of objects.entries()) {
if (object.error) {
results.push(ObjectError.fromResponse(object.error, batch[idx]));
} else {
results.push(parseObject(object.data!));
for (const [idx, object] of objects.entries()) {
if (object.error) {
results.push(ObjectError.fromResponse(object.error, batch[idx]));
} else {
results.push(parseObject(object.data!));
}
}
}
}

return {
objects: results,
};
}
return {
objects: results,
};
},
);
async getOwnedObjects(options: Experimental_SuiClientTypes.GetOwnedObjectsOptions) {
const objects = await this.#jsonRpcClient.getOwnedObjects({
owner: options.address,
Expand Down
1 change: 1 addition & 0 deletions packages/typescript/src/experimental/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export namespace Experimental_SuiClientTypes {

export interface SuiClientOptions {
network: Network;
root?: Experimental_BaseClient;
}

export interface CoreClientMethodOptions {
Expand Down
Loading