diff --git a/.changeset/hungry-parents-chew.md b/.changeset/hungry-parents-chew.md new file mode 100644 index 000000000..977ef55dd --- /dev/null +++ b/.changeset/hungry-parents-chew.md @@ -0,0 +1,5 @@ +--- +"@ledgerhq/keyring-eth": patch +--- + +Implement ProvideTransactionContextTask diff --git a/.changeset/slow-eggs-act.md b/.changeset/slow-eggs-act.md new file mode 100644 index 000000000..f06298dfc --- /dev/null +++ b/.changeset/slow-eggs-act.md @@ -0,0 +1,5 @@ +--- +"@ledgerhq/context-module": patch +--- + +Improve code visibility and update command implementations diff --git a/packages/signer/keyring-eth/src/internal/app-binder/command/ProvideDomainNameCommand.ts b/packages/signer/keyring-eth/src/internal/app-binder/command/ProvideDomainNameCommand.ts index f48d3658d..2575a6954 100644 --- a/packages/signer/keyring-eth/src/internal/app-binder/command/ProvideDomainNameCommand.ts +++ b/packages/signer/keyring-eth/src/internal/app-binder/command/ProvideDomainNameCommand.ts @@ -5,25 +5,22 @@ import { type ApduBuilderArgs, ApduResponse, type Command, - CommandResult, + type CommandResult, CommandResultFactory, CommandUtils, GlobalCommandErrorHandler, } from "@ledgerhq/device-sdk-core"; export type ProvideDomainNameCommandArgs = { - /** - * The chunk of the stringified hexa representation of the domain name prefixed by its length in two bytes. - * If the index equals 0, the first two bytes are the length of the domain name, else all the bytes are the chunk data. - * @example "00064C6564676572" (hexa for "Ledger", first chunk and only chunk) - */ data: Uint8Array; - /** - * The index of the chunk. - */ isFirstChunk: boolean; }; +/** + * The length of the payload will take 2 bytes in the APDU. + */ +export const PAYLOAD_LENGTH_BYTES = 2; + /** * The command that provides a chunk of the domain name to the device. */ diff --git a/packages/signer/keyring-eth/src/internal/app-binder/command/ProvideNFTInformationCommand.ts b/packages/signer/keyring-eth/src/internal/app-binder/command/ProvideNFTInformationCommand.ts index 3b3395801..5399f6971 100644 --- a/packages/signer/keyring-eth/src/internal/app-binder/command/ProvideNFTInformationCommand.ts +++ b/packages/signer/keyring-eth/src/internal/app-binder/command/ProvideNFTInformationCommand.ts @@ -6,9 +6,9 @@ import { ApduParser, ApduResponse, type Command, - CommandErrorArgs, - CommandErrors, - CommandResult, + type CommandErrorArgs, + type CommandErrors, + type CommandResult, CommandResultFactory, CommandUtils, DeviceExchangeError, diff --git a/packages/signer/keyring-eth/src/internal/app-binder/command/ProvideTokenInformationCommand.ts b/packages/signer/keyring-eth/src/internal/app-binder/command/ProvideTokenInformationCommand.ts index 71edc9333..cbe77ef8b 100644 --- a/packages/signer/keyring-eth/src/internal/app-binder/command/ProvideTokenInformationCommand.ts +++ b/packages/signer/keyring-eth/src/internal/app-binder/command/ProvideTokenInformationCommand.ts @@ -5,8 +5,8 @@ import { type ApduBuilderArgs, ApduParser, ApduResponse, - Command, - CommandResult, + type Command, + type CommandResult, CommandResultFactory, CommandUtils, GlobalCommandErrorHandler, diff --git a/packages/signer/keyring-eth/src/internal/app-binder/command/SendEIP712StructImplemCommand.ts b/packages/signer/keyring-eth/src/internal/app-binder/command/SendEIP712StructImplemCommand.ts index 156c3ecb2..23e2414eb 100644 --- a/packages/signer/keyring-eth/src/internal/app-binder/command/SendEIP712StructImplemCommand.ts +++ b/packages/signer/keyring-eth/src/internal/app-binder/command/SendEIP712StructImplemCommand.ts @@ -5,7 +5,7 @@ import { type ApduBuilderArgs, ApduResponse, type Command, - CommandResult, + type CommandResult, CommandResultFactory, CommandUtils, GlobalCommandErrorHandler, diff --git a/packages/signer/keyring-eth/src/internal/app-binder/command/SetExternalPluginCommand.test.ts b/packages/signer/keyring-eth/src/internal/app-binder/command/SetExternalPluginCommand.test.ts index 54a003b51..f7b781b74 100644 --- a/packages/signer/keyring-eth/src/internal/app-binder/command/SetExternalPluginCommand.test.ts +++ b/packages/signer/keyring-eth/src/internal/app-binder/command/SetExternalPluginCommand.test.ts @@ -10,12 +10,13 @@ import { SetExternalPluginCommandError, } from "@internal/app-binder/command/SetExternalPluginCommand"; -/** Test payload contains: +/** + * Test payload contains: * Length of plugin name : 08 * Plugin Name : Paraswap * contract address: 0xdef171fe48cf0115b1d80b88dc8eab59176fee57 * method selector: 0xa9059cbb - * **/ + */ const SET_EXTERNAL_PLUGIN_PAYLOAD = [ 0x08, 0x50, 0x61, 0x72, 0x61, 0x73, 0x77, 0x61, 0x70, 0xde, 0xf1, 0x71, 0xfe, 0x48, 0xcf, 0x01, 0x15, 0xb1, 0xd8, 0x0b, 0x88, 0xdc, 0x8e, 0xab, 0x59, 0x17, @@ -67,24 +68,28 @@ describe("Set External plugin", () => { ${Uint8Array.from([0x6d, 0x00])} | ${"6d00"} `( "should return an error for the response status code $errorCode", - ({ apduResponseCode, errorCode }) => { + ({ + apduResponseCode, + errorCode, + }: Record<"apduResponseCode" | "errorCode", Uint8Array>) => { // GIVEN const response = new ApduResponse({ data: Uint8Array.from([]), statusCode: apduResponseCode, }); const command = new SetExternalPluginCommand({ - payload: Uint8Array.from([]), - signature: Uint8Array.from([]), + payload: "", + signature: "", }); // WHEN const result = command.parseResponse(response); // THEN expect(isSuccessCommandResult(result)).toBe(false); - // @ts-ignore - expect(result.error).toBeInstanceOf(SetExternalPluginCommandError); - // @ts-ignore - expect(result.error.errorCode).toStrictEqual(errorCode); + if (!isSuccessCommandResult(result)) { + expect(result.error).toBeInstanceOf(SetExternalPluginCommandError); + if (result.error instanceof SetExternalPluginCommandError) + expect(result.error.errorCode).toStrictEqual(errorCode); + } }, ); it("should return a global error", () => { @@ -101,10 +106,11 @@ describe("Set External plugin", () => { // then const result = command.parseResponse(apduResponse); expect(isSuccessCommandResult(result)).toBe(false); - // @ts-ignore - expect(result.error).toBeInstanceOf(GlobalCommandError); - // @ts-ignore - expect(result.error.errorCode).toStrictEqual("5515"); + if (!isSuccessCommandResult(result)) { + expect(result.error).toBeInstanceOf(GlobalCommandError); + if (result.error instanceof GlobalCommandError) + expect(result.error.errorCode).toStrictEqual("5515"); + } }); it("should return void if status is success", () => { // given diff --git a/packages/signer/keyring-eth/src/internal/app-binder/command/SetExternalPluginCommand.ts b/packages/signer/keyring-eth/src/internal/app-binder/command/SetExternalPluginCommand.ts index a428fe550..f99cbc20f 100644 --- a/packages/signer/keyring-eth/src/internal/app-binder/command/SetExternalPluginCommand.ts +++ b/packages/signer/keyring-eth/src/internal/app-binder/command/SetExternalPluginCommand.ts @@ -5,10 +5,10 @@ import { type ApduBuilderArgs, ApduParser, ApduResponse, - Command, - CommandErrorArgs, - CommandErrors, - CommandResult, + type Command, + type CommandErrorArgs, + type CommandErrors, + type CommandResult, CommandResultFactory, CommandUtils, DeviceExchangeError, @@ -21,7 +21,7 @@ type SetExternalPluginCommandArgs = { signature?: string; }; -type SetExternalPluginCommandErrorCodes = "6a80" | "6984" | "6d00"; +export type SetExternalPluginCommandErrorCodes = "6a80" | "6984" | "6d00"; const SET_EXTERNAL_PLUGIN_ERRORS: CommandErrors = { diff --git a/packages/signer/keyring-eth/src/internal/app-binder/command/SetPluginCommand.test.ts b/packages/signer/keyring-eth/src/internal/app-binder/command/SetPluginCommand.test.ts index 3a60082af..d34079b38 100644 --- a/packages/signer/keyring-eth/src/internal/app-binder/command/SetPluginCommand.test.ts +++ b/packages/signer/keyring-eth/src/internal/app-binder/command/SetPluginCommand.test.ts @@ -5,7 +5,7 @@ import { import { SetPluginCommand, - SetPluginCommandArgs, + type SetPluginCommandArgs, SetPluginCommandError, } from "./SetPluginCommand"; @@ -46,7 +46,10 @@ describe("SetPluginCommand", () => { ${Uint8Array.from([0x6d, 0x00])} | ${"6d00"} `( "should return an error for the response status code $errorCode", - ({ apduResponseCode, errorCode }) => { + ({ + apduResponseCode, + errorCode, + }: Record<"apduResponseCode" | "errorCode", Uint8Array>) => { // GIVEN const response = new ApduResponse({ data: Uint8Array.from([]), @@ -57,10 +60,11 @@ describe("SetPluginCommand", () => { const result = command.parseResponse(response); // THEN expect(isSuccessCommandResult(result)).toBe(false); - // @ts-ignore - expect(result.error).toBeInstanceOf(SetPluginCommandError); - // @ts-ignore - expect(result.error.errorCode).toStrictEqual(errorCode); + if (!isSuccessCommandResult(result)) { + expect(result.error).toBeInstanceOf(SetPluginCommandError); + if (result.error instanceof SetPluginCommandError) + expect(result.error.errorCode).toStrictEqual(errorCode); + } }, ); diff --git a/packages/signer/keyring-eth/src/internal/app-binder/command/SetPluginCommand.ts b/packages/signer/keyring-eth/src/internal/app-binder/command/SetPluginCommand.ts index feb93a327..c5c2b9954 100644 --- a/packages/signer/keyring-eth/src/internal/app-binder/command/SetPluginCommand.ts +++ b/packages/signer/keyring-eth/src/internal/app-binder/command/SetPluginCommand.ts @@ -6,9 +6,9 @@ import { ApduParser, ApduResponse, type Command, - CommandErrorArgs, - CommandErrors, - CommandResult, + type CommandErrorArgs, + type CommandErrors, + type CommandResult, CommandResultFactory, CommandUtils, DeviceExchangeError, @@ -16,7 +16,7 @@ import { isCommandErrorCode, } from "@ledgerhq/device-sdk-core"; -type SetPluginCommandErrorCodes = "6984" | "6d00"; +export type SetPluginCommandErrorCodes = "6984" | "6d00"; const SET_PLUGIN_ERRORS: CommandErrors = { "6984": { message: "The requested plugin is not installed on the device" }, diff --git a/packages/signer/keyring-eth/src/internal/app-binder/task/BuildTransactionContextTask.test.ts b/packages/signer/keyring-eth/src/internal/app-binder/task/BuildTransactionContextTask.test.ts index 7f6ed9f39..060d584f8 100644 --- a/packages/signer/keyring-eth/src/internal/app-binder/task/BuildTransactionContextTask.test.ts +++ b/packages/signer/keyring-eth/src/internal/app-binder/task/BuildTransactionContextTask.test.ts @@ -1,4 +1,7 @@ -import { ClearSignContext } from "@ledgerhq/context-module"; +import { + ClearSignContext, + ClearSignContextType, +} from "@ledgerhq/context-module"; import { Transaction } from "ethers-v6"; import { Left, Right } from "purify-ts"; @@ -61,11 +64,11 @@ describe("BuildTransactionContextTask", () => { const serializedTransaction = new Uint8Array([0x01, 0x02, 0x03]); const clearSignContexts: ClearSignContext[] = [ { - type: "token", + type: ClearSignContextType.TOKEN, payload: "payload-1", }, { - type: "nft", + type: ClearSignContextType.NFT, payload: "payload-2", }, ]; @@ -169,19 +172,19 @@ describe("BuildTransactionContextTask", () => { const serializedTransaction = new Uint8Array([0x01, 0x02, 0x03]); const clearSignContexts: ClearSignContext[] = [ { - type: "error", + type: ClearSignContextType.ERROR, error: new Error("error"), }, { - type: "token", + type: ClearSignContextType.TOKEN, payload: "payload-1", }, { - type: "error", + type: ClearSignContextType.ERROR, error: new Error("error"), }, { - type: "nft", + type: ClearSignContextType.NFT, payload: "payload-2", }, ]; diff --git a/packages/signer/keyring-eth/src/internal/app-binder/task/ProvideTransactionContextTask.test.ts b/packages/signer/keyring-eth/src/internal/app-binder/task/ProvideTransactionContextTask.test.ts new file mode 100644 index 000000000..84e4a9c55 --- /dev/null +++ b/packages/signer/keyring-eth/src/internal/app-binder/task/ProvideTransactionContextTask.test.ts @@ -0,0 +1,152 @@ +import { ClearSignContextType } from "@ledgerhq/context-module"; +import { type InternalApi } from "@ledgerhq/device-sdk-core"; + +import { ProvideDomainNameCommand } from "@internal/app-binder/command/ProvideDomainNameCommand"; +import { ProvideNFTInformationCommand } from "@internal/app-binder/command/ProvideNFTInformationCommand"; +import { ProvideTokenInformationCommand } from "@internal/app-binder/command/ProvideTokenInformationCommand"; +import { SetExternalPluginCommand } from "@internal/app-binder/command/SetExternalPluginCommand"; +import { SetPluginCommand } from "@internal/app-binder/command/SetPluginCommand"; +import { makeDeviceActionInternalApiMock } from "@internal/app-binder/device-action/__test-utils__/makeInternalApi"; + +import { + ProvideTransactionContextTask, + type ProvideTransactionContextTaskArgs, +} from "./ProvideTransactionContextTask"; + +describe("ProvideTransactionContextTask", () => { + let api: InternalApi; + + beforeEach(() => { + jest.clearAllMocks(); + api = makeDeviceActionInternalApiMock(); + }); + + describe("run", () => { + it("should send relative commands when receiving ClearSignContexts of type not domainName", async () => { + // GIVEN + const args: ProvideTransactionContextTaskArgs = { + clearSignContexts: [ + { + type: ClearSignContextType.PLUGIN, + payload: "706c7567696e", // "plugin" + }, + { + type: ClearSignContextType.EXTERNAL_PLUGIN, + payload: "65787465726e616c506c7567696e", // "externalPlugin" + }, + { + type: ClearSignContextType.NFT, + payload: "6e6674", // "nft" + }, + { + type: ClearSignContextType.TOKEN, + payload: "746f6b656e", // "token" + }, + ], + }; + const task = new ProvideTransactionContextTask(api, args); + // WHEN + await task.run(); + // THEN + expect(api.sendCommand).toHaveBeenCalledTimes(4); + expect(api.sendCommand).toHaveBeenNthCalledWith( + args.clearSignContexts.findIndex( + (c) => c.type === ClearSignContextType.PLUGIN, + ) + 1, + expect.objectContaining( + new SetPluginCommand({ payload: "706c7567696e" }), + ), + ); + expect(api.sendCommand).toHaveBeenNthCalledWith( + args.clearSignContexts.findIndex( + (c) => c.type === ClearSignContextType.EXTERNAL_PLUGIN, + ) + 1, + expect.objectContaining( + new SetExternalPluginCommand({ + payload: "65787465726e616c506c7567696e", + }), + ), + ); + expect(api.sendCommand).toHaveBeenNthCalledWith( + args.clearSignContexts.findIndex( + (c) => c.type === ClearSignContextType.NFT, + ) + 1, + expect.objectContaining( + new ProvideNFTInformationCommand({ payload: "6e6674" }), + ), + ); + expect(api.sendCommand).toHaveBeenNthCalledWith( + args.clearSignContexts.findIndex( + (c) => c.type === ClearSignContextType.TOKEN, + ) + 1, + expect.objectContaining( + new ProvideTokenInformationCommand({ payload: "746f6b656e" }), + ), + ); + }); + it("should call provideDomainNameTask when receiving a ClearSignContext of type domainName", async () => { + jest.spyOn( + ProvideTransactionContextTask.prototype, + "provideDomainNameTask", + ); + // GIVEN + const task = new ProvideTransactionContextTask(api, { + clearSignContexts: [ + { + type: ClearSignContextType.DOMAIN_NAME, + payload: "646f6d61696e4e616d65", // "domainName" + }, + ], + }); + // WHEN + await task.run(); + // THEN + expect( + ProvideTransactionContextTask.prototype.provideDomainNameTask, + ).toHaveBeenCalledTimes(1); + expect( + ProvideTransactionContextTask.prototype.provideDomainNameTask, + ).toHaveBeenCalledWith("646f6d61696e4e616d65"); + }); + }); + + describe("provideDomainNameTask", () => { + it("should send the multiple ProvideDomainNameCommand to the device", async () => { + // GIVEN + const domainName = "646f6d61696e4e616d65"; // "domainName" + const task = new ProvideTransactionContextTask(api, { + clearSignContexts: [], + }); + // WHEN + await task.provideDomainNameTask(domainName); + // THEN + expect(api.sendCommand).toHaveBeenCalledTimes(1); + expect(api.sendCommand).toHaveBeenNthCalledWith( + 1, + expect.objectContaining( + new ProvideDomainNameCommand({ + data: Uint8Array.from([ + 0x00, 0x0a, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4e, 0x61, 0x6d, + 0x65, + ]), + isFirstChunk: true, + }), + ), + ); + }); + + it("should throw an error when given empty string", async () => { + // GIVEN + const task = new ProvideTransactionContextTask(api, { + clearSignContexts: [], + }); + // WHEN & THEN + await expect( + task.provideDomainNameTask(""), + ).rejects.toThrowErrorMatchingSnapshot( + "Invalid encoded hexa string or length is null: provideDomainNameTask", + ); + expect(api.sendCommand).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/signer/keyring-eth/src/internal/app-binder/task/ProvideTransactionContextTask.ts b/packages/signer/keyring-eth/src/internal/app-binder/task/ProvideTransactionContextTask.ts new file mode 100644 index 000000000..567612687 --- /dev/null +++ b/packages/signer/keyring-eth/src/internal/app-binder/task/ProvideTransactionContextTask.ts @@ -0,0 +1,171 @@ +import type { ClearSignContextSuccess } from "@ledgerhq/context-module"; +import { + APDU_MAX_PAYLOAD, + ByteArrayBuilder, + type CommandErrorResult, + CommandResultStatus, + hexaStringToBuffer, + type InternalApi, + type SdkError, +} from "@ledgerhq/device-sdk-core"; +import { HexaStringEncodeError } from "@ledgerhq/device-sdk-core/src/api/apdu/utils/AppBuilderError.js"; +import { Just, Maybe, Nothing } from "purify-ts"; + +import { + PAYLOAD_LENGTH_BYTES, + ProvideDomainNameCommand, +} from "@internal/app-binder/command/ProvideDomainNameCommand"; +import { + ProvideNFTInformationCommand, + type ProvideNFTInformationCommandErrorCodes, +} from "@internal/app-binder/command/ProvideNFTInformationCommand"; +import { ProvideTokenInformationCommand } from "@internal/app-binder/command/ProvideTokenInformationCommand"; +import { + SetExternalPluginCommand, + type SetExternalPluginCommandErrorCodes, +} from "@internal/app-binder/command/SetExternalPluginCommand"; +import { + SetPluginCommand, + type SetPluginCommandErrorCodes, +} from "@internal/app-binder/command/SetPluginCommand"; +import { CommandResult } from "@ledgerhq/device-sdk-core"; + +export type ProvideTransactionContextTaskArgs = { + /** + * The valid clear sign contexts offerred by the `BuildTrancationContextTask`. + */ + clearSignContexts: ClearSignContextSuccess[]; +}; + +/** + * Temporary error type to be used in the `ProvideTransactionContextTask` in order to not forget to handle the error cases. + */ +export class ProvideTransactionContextTaskError implements SdkError { + readonly _tag = "ProvideTransactionContextTaskError"; + readonly originalError: Error; + + constructor(message?: string) { + this.originalError = new Error( + message ?? "Unknow error in ProvideTransactionContextTaskError", + ); + } +} + +type ErrorCodes = + | void + | SetExternalPluginCommandErrorCodes + | SetPluginCommandErrorCodes + | ProvideNFTInformationCommandErrorCodes; + +/** + * This task is responsible for providing the transaction context to the device. + * It will send the 5 necessary commands: + * - `SetPluginCommand` (single command) + * - `SetExternalPluginCommand` (single command) + * - `ProvideNFTInformationCommand` (single command) + * - `ProvideTokenInformationCommand` (single command) + * - `ProvideDomainNameCommand` (__mulpitle commands__) + * + * The method `provideDomainNameTask` is dedicated to send the multiple `ProvideDomainNameCommand`. + */ +export class ProvideTransactionContextTask { + constructor( + private api: InternalApi, + private args: ProvideTransactionContextTaskArgs, + ) {} + + async run(): Promise>> { + for (const context of this.args.clearSignContexts) { + switch (context.type) { + case "plugin": { + const res = await this.api.sendCommand( + new SetPluginCommand({ payload: context.payload }), + ); + if (res && res.status === CommandResultStatus.Error) { + return Just(res); + } + break; + } + case "externalPlugin": { + const res = await this.api.sendCommand( + new SetExternalPluginCommand({ + payload: context.payload, + // Signature should be integrated in the payload + }), + ); + if (res && res.status === CommandResultStatus.Error) { + return Just(res); + } + break; + } + case "nft": { + const res = await this.api.sendCommand( + new ProvideNFTInformationCommand({ + payload: context.payload, + }), + ); + if (res && res.status === CommandResultStatus.Error) { + return Just(res); + } + break; + } + case "token": { + const res = await this.api.sendCommand( + new ProvideTokenInformationCommand({ + payload: context.payload, + }), + ); + if (res && res.status === CommandResultStatus.Error) { + return Just(res); + } + break; + } + case "domainName": { + const res = await this.provideDomainNameTask(context.payload); + if (res.isJust()) { + return res; + } + break; + } + default: { + throw new ProvideTransactionContextTaskError("Unknown context type"); + } + } + } + return Nothing; + } + + /** + * This method is responsible for chunking the domain name if necessary and sending the multiple `ProvideDomainNameCommand` to the device. + * + * @param domainName Hexa representation of the domain name. + * @returns A promise that resolves when the command is sent. + */ + async provideDomainNameTask( + domainName: string, + ): Promise> { + const buffer = hexaStringToBuffer(domainName); + + if (buffer === null || buffer.length === 0) { + throw new HexaStringEncodeError("provideDomainNameTask"); + } + + const data = new ByteArrayBuilder(buffer.length + PAYLOAD_LENGTH_BYTES) + .add16BitUIntToData(buffer.length) + .addBufferToData(buffer) + .build(); + + for (let i = 0; i < data.length; i += APDU_MAX_PAYLOAD) { + const res = await this.api.sendCommand( + new ProvideDomainNameCommand({ + data: data.slice(i, i + APDU_MAX_PAYLOAD), + isFirstChunk: i === 0, + }), + ); + if (res && res.status === CommandResultStatus.Error) { + return Just(res); + } + } + return Nothing; + } +} diff --git a/packages/signer/keyring-eth/src/internal/app-binder/task/SendEIP712StructImplemTask.ts b/packages/signer/keyring-eth/src/internal/app-binder/task/SendEIP712StructImplemTask.ts index 0db0c1e35..bbb02555a 100644 --- a/packages/signer/keyring-eth/src/internal/app-binder/task/SendEIP712StructImplemTask.ts +++ b/packages/signer/keyring-eth/src/internal/app-binder/task/SendEIP712StructImplemTask.ts @@ -1,9 +1,9 @@ import { APDU_MAX_PAYLOAD, ByteArrayBuilder, - CommandResult, + type CommandResult, CommandResultFactory, - InternalApi, + type InternalApi, isSuccessCommandResult, } from "@ledgerhq/device-sdk-core"; diff --git a/packages/signer/keyring-eth/src/internal/app-binder/task/__snapshots__/ProvideTransactionContextTask.test.ts.snap b/packages/signer/keyring-eth/src/internal/app-binder/task/__snapshots__/ProvideTransactionContextTask.test.ts.snap new file mode 100644 index 000000000..a9545f6af --- /dev/null +++ b/packages/signer/keyring-eth/src/internal/app-binder/task/__snapshots__/ProvideTransactionContextTask.test.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ProvideTransactionContextTask provideDomainNameTask should throw an error when given empty string: Invalid encoded hexa string or length is null: provideDomainNameTask 1`] = `"Invalid encoded hexa string or length is null: provideDomainNameTask"`;