diff --git a/.changeset/all-ways-lie.md b/.changeset/all-ways-lie.md new file mode 100644 index 000000000..71ca554ba --- /dev/null +++ b/.changeset/all-ways-lie.md @@ -0,0 +1,5 @@ +--- +'@mysten/sui': minor +--- + +Add ExecutingSigner class diff --git a/packages/typescript/src/cryptography/index.ts b/packages/typescript/src/cryptography/index.ts index a5603c56b..e3bdb6d6d 100644 --- a/packages/typescript/src/cryptography/index.ts +++ b/packages/typescript/src/cryptography/index.ts @@ -28,6 +28,7 @@ export { type ParsedKeypair, type SignatureWithBytes, Signer, + ExecutingSigner, Keypair, decodeSuiPrivateKey, encodeSuiPrivateKey, diff --git a/packages/typescript/src/cryptography/keypair.ts b/packages/typescript/src/cryptography/keypair.ts index caa7196b7..3a80269bf 100644 --- a/packages/typescript/src/cryptography/keypair.ts +++ b/packages/typescript/src/cryptography/keypair.ts @@ -84,6 +84,7 @@ export abstract class Signer { transaction, client, }: SignAndExecuteOptions): Promise { + transaction.setSenderIfNotSet(this.toSuiAddress()); const bytes = await transaction.build({ client }); const { signature } = await this.signTransaction(bytes); const response = await client.core.executeTransaction({ @@ -109,6 +110,42 @@ export abstract class Signer { abstract getPublicKey(): PublicKey; } +export class ExecutingSigner extends Signer { + #client: ClientWithCoreApi; + #signer: Signer; + + constructor({ signer, client }: { signer: Signer; client: ClientWithCoreApi }) { + super(); + + this.#client = client; + this.#signer = signer; + } + + sign(bytes: Uint8Array) { + return this.#signer.sign(bytes); + } + + getKeyScheme() { + return this.#signer.getKeyScheme(); + } + + getPublicKey() { + return this.#signer.getPublicKey(); + } + + async signAndExecuteTransaction({ + transaction, + }: Omit< + SignAndExecuteOptions, + 'client' + >): Promise { + return this.#signer.signAndExecuteTransaction({ + transaction, + client: this.#client, + }); + } +} + export abstract class Keypair extends Signer { /** * This returns the Bech32 secret key string for this keypair. diff --git a/packages/typescript/test/e2e/executing-signer.test.ts b/packages/typescript/test/e2e/executing-signer.test.ts new file mode 100644 index 000000000..1012db079 --- /dev/null +++ b/packages/typescript/test/e2e/executing-signer.test.ts @@ -0,0 +1,42 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +import { beforeEach, describe, expect, it } from 'vitest'; + +import { Transaction } from '../../src/transactions'; +import { normalizeSuiObjectId } from '../../src/utils'; +import { setup, TestToolbox } from './utils/setup'; +import { ExecutingSigner } from '../../src/cryptography'; + +export const SUI_CLOCK_OBJECT_ID = normalizeSuiObjectId('0x6'); + +describe('ExecutingSigner', () => { + let toolbox: TestToolbox; + + beforeEach(async () => { + toolbox = await setup(); + }); + + it('executes without a client', async () => { + const tx = new Transaction(); + + tx.transferObjects([tx.splitCoins(tx.gas, [1])], toolbox.address()); + + const signer = new ExecutingSigner({ + signer: toolbox.keypair, + client: toolbox.client, + }); + + const result = await signer.signAndExecuteTransaction({ + transaction: tx, + }); + + expect(result.effects.status.success).toBe(true); + + expect( + await toolbox.keypair + .getPublicKey() + .verifyTransaction(await tx.build(), result.signatures[0]), + ).toBe(true); + }); +});