Skip to content

Commit

Permalink
✨ (keyring-eth) [DSDK-425]: Add SignTransactionDeviceAction (#227)
Browse files Browse the repository at this point in the history
  • Loading branch information
aussedatlo authored Sep 2, 2024
2 parents aa6aa1c + c148662 commit 6c46cde
Show file tree
Hide file tree
Showing 17 changed files with 1,261 additions and 149 deletions.
5 changes: 5 additions & 0 deletions .changeset/serious-snails-deny.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ledgerhq/keyring-eth": patch
---

Implement SignTransactionDeviceAction
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
ClearSignContextSuccess,
ContextModule,
} from "@ledgerhq/context-module";
import {
CommandErrorResult,
DeviceActionState,
ExecuteDeviceActionReturnType,
OpenAppDAError,
OpenAppDARequiredInteraction,
UserInteractionRequired,
} from "@ledgerhq/device-sdk-core";

import { Signature } from "@api/model/Signature";
import { Transaction } from "@api/model/Transaction";
import { TransactionOptions } from "@api/model/TransactionOptions";
import { ProvideTransactionContextTaskErrorCodes } from "@internal/app-binder/task/ProvideTransactionContextTask";
import { TransactionMapperService } from "@internal/transaction/service/mapper/TransactionMapperService";

export type SignTransactionDAOutput = Signature;

export type SignTransactionDAInput = {
readonly derivationPath: string;
readonly transaction: Transaction;
readonly mapper: TransactionMapperService;
readonly contextModule: ContextModule;
readonly options: TransactionOptions;
};

export type SignTransactionDAError =
| OpenAppDAError
| CommandErrorResult["error"]
| CommandErrorResult<ProvideTransactionContextTaskErrorCodes>["error"];

type SignTransactionDARequiredInteraction =
| OpenAppDARequiredInteraction
| UserInteractionRequired.SignTransaction;

export type SignTransactionDAIntermediateValue = {
requiredUserInteraction: SignTransactionDARequiredInteraction;
};

export type SignTransactionDAState = DeviceActionState<
SignTransactionDAOutput,
SignTransactionDAError,
SignTransactionDAIntermediateValue
>;

export type SignTransactionDAInternalState = {
readonly error: SignTransactionDAError | null;
readonly challenge: string | null;
readonly clearSignContexts: ClearSignContextSuccess[] | null;
readonly serializedTransaction: Uint8Array | null;
readonly signature: Signature | null;
};

export type SignTransactionDAReturnType = ExecuteDeviceActionReturnType<
SignTransactionDAOutput,
SignTransactionDAError,
SignTransactionDAIntermediateValue
>;
Original file line number Diff line number Diff line change
Expand Up @@ -83,20 +83,6 @@ describe("ProvideTokenInformationCommand", () => {
);
});

it("should return an error if the response is invalid", () => {
// GIVEN
const response = {
statusCode: Uint8Array.from([0x90, 0x00]),
data: new Uint8Array(),
};

// WHEN
const result = command.parseResponse(response);

// THEN
expect(isSuccessCommandResult(result)).toBe(false);
});

it("should return an error if the response is not successful", () => {
// GIVEN
const response = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
CommandResultFactory,
CommandUtils,
GlobalCommandErrorHandler,
InvalidStatusWordError,
} from "@ledgerhq/device-sdk-core";

export type ProvideTokenInformationCommandArgs = {
Expand Down Expand Up @@ -52,12 +51,7 @@ export class ProvideTokenInformationCommand
error: GlobalCommandErrorHandler.handle(response),
});
}
const tokenIndex = parser.extract8BitUInt();
if (tokenIndex === undefined) {
return CommandResultFactory({
error: new InvalidStatusWordError("tokenIndex is missing"),
});
}
const tokenIndex = parser.extract8BitUInt() ?? 0;
return CommandResultFactory({ data: { tokenIndex } });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export class SignEIP712Command
}

const v = parser.extract8BitUInt();
if (!v) {
if (v === undefined) {
return CommandResultFactory({
error: new InvalidStatusWordError("V is missing"),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export class SignPersonalMessageCommand

// The data is returned only for the last chunk
const v = parser.extract8BitUInt();
if (!v) {
if (v === undefined) {
return CommandResultFactory({ data: Nothing });
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class SignTransactionCommand

// The data is returned only for the last chunk
const v = parser.extract8BitUInt();
if (!v) {
if (v === undefined) {
return CommandResultFactory({ data: Nothing });
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import {
CommandResultFactory,
DeviceActionStatus,
OpenAppDeviceAction,
UnknownDeviceExchangeError,
UserInteractionRequired,
} from "@ledgerhq/device-sdk-core";
import { UnknownDAError } from "@ledgerhq/device-sdk-core";
import { InvalidStatusWordError } from "@ledgerhq/device-sdk-core";
import { Left, Right } from "purify-ts";
import { assign, createMachine } from "xstate";

import { SignPersonalMessageDAState } from "@api/index";
import { makeDeviceActionInternalApiMock } from "@internal/app-binder/device-action/__test-utils__/makeInternalApi";
import { setupOpenAppDAMock } from "@internal/app-binder/device-action/__test-utils__/setupOpenAppDAMock";
import { testDeviceActionStates } from "@internal/app-binder/device-action/__test-utils__/testDeviceActionStates";

import { SignPersonalMessageDeviceAction } from "./SignPersonalMessageDeviceAction";
Expand All @@ -27,32 +25,6 @@ jest.mock(
}),
);

const setupOpenAppDAMock = (error?: unknown) => {
(OpenAppDeviceAction as jest.Mock).mockImplementation(() => ({
makeStateMachine: jest.fn().mockImplementation(() =>
createMachine({
initial: "pending",
states: {
pending: {
entry: assign({
intermediateValue: {
requiredUserInteraction: UserInteractionRequired.ConfirmOpenApp,
},
}),
after: {
0: "done",
},
},
done: {
type: "final",
},
},
output: () => (error ? Left(error) : Right(undefined)),
}),
),
}));
};

describe("SignPersonalMessageDeviceAction", () => {
const signPersonalMessageMock = jest.fn();

Expand Down
Loading

0 comments on commit 6c46cde

Please sign in to comment.