From 47839e91e9458d412ae892928b5488fdb937f6a2 Mon Sep 17 00:00:00 2001 From: Louis Aussedat Date: Tue, 27 Aug 2024 15:00:55 +0200 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20(keyring-eth):=20Re?= =?UTF-8?q?place=20serialized=20with=20serializedTransaction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/SignTransactionCommand.test.ts | 4 +- .../command/SignTransactionCommand.ts | 6 +-- .../task/SendSignTransactionTask.test.ts | 50 +++++++++++-------- .../task/SendSignTransactionTask.ts | 10 ++-- .../mapper/EthersV5TransactionMapper.test.ts | 12 ++--- .../mapper/EthersV5TransactionMapper.ts | 4 +- .../mapper/EthersV6TransactionMapper.test.ts | 12 ++--- .../mapper/EthersV6TransactionMapper.ts | 4 +- .../mapper/model/TransactionMapperResult.ts | 4 +- 9 files changed, 56 insertions(+), 50 deletions(-) diff --git a/packages/signer/keyring-eth/src/internal/app-binder/command/SignTransactionCommand.test.ts b/packages/signer/keyring-eth/src/internal/app-binder/command/SignTransactionCommand.test.ts index 11f3195e2..51fd1d0f6 100644 --- a/packages/signer/keyring-eth/src/internal/app-binder/command/SignTransactionCommand.test.ts +++ b/packages/signer/keyring-eth/src/internal/app-binder/command/SignTransactionCommand.test.ts @@ -33,7 +33,7 @@ const LNX_RESPONSE_DATA_GOOD = new ApduResponse({ describe("SignTransactionCommand", () => { const defaultArgs: SignTransactionCommandArgs = { - transaction: new Uint8Array(), + serializedTransaction: new Uint8Array(), isFirstChunk: true, }; @@ -59,7 +59,7 @@ describe("SignTransactionCommand", () => { // GIVEN const command = new SignTransactionCommand({ ...defaultArgs, - transaction: new Uint8Array([0x01, 0x02, 0x03]), + serializedTransaction: new Uint8Array([0x01, 0x02, 0x03]), }); // WHEN diff --git a/packages/signer/keyring-eth/src/internal/app-binder/command/SignTransactionCommand.ts b/packages/signer/keyring-eth/src/internal/app-binder/command/SignTransactionCommand.ts index e44fd0c25..73722c4f0 100644 --- a/packages/signer/keyring-eth/src/internal/app-binder/command/SignTransactionCommand.ts +++ b/packages/signer/keyring-eth/src/internal/app-binder/command/SignTransactionCommand.ts @@ -28,7 +28,7 @@ export type SignTransactionCommandArgs = { /** * The transaction to sign in max 150 bytes chunks */ - readonly transaction: Uint8Array; + readonly serializedTransaction: Uint8Array; /** * If this is the first chunk of the message */ @@ -46,7 +46,7 @@ export class SignTransactionCommand } getApdu(): Apdu { - const { transaction, isFirstChunk } = this.args; + const { serializedTransaction, isFirstChunk } = this.args; const signEthTransactionArgs: ApduBuilderArgs = { cla: 0xe0, @@ -55,7 +55,7 @@ export class SignTransactionCommand p2: 0x00, }; const builder = new ApduBuilder(signEthTransactionArgs); - return builder.addBufferToData(transaction).build(); + return builder.addBufferToData(serializedTransaction).build(); } parseResponse( diff --git a/packages/signer/keyring-eth/src/internal/app-binder/task/SendSignTransactionTask.test.ts b/packages/signer/keyring-eth/src/internal/app-binder/task/SendSignTransactionTask.test.ts index 2db49886c..28fa4d217 100644 --- a/packages/signer/keyring-eth/src/internal/app-binder/task/SendSignTransactionTask.test.ts +++ b/packages/signer/keyring-eth/src/internal/app-binder/task/SendSignTransactionTask.test.ts @@ -83,7 +83,7 @@ describe("SendSignTransactionTask", () => { // GIVEN const args = { derivationPath: "44'/60'/0'/0/0", - transaction: SIMPLE_TRANSACTION, + serializedTransaction: SIMPLE_TRANSACTION, }; apiMock.sendCommand.mockResolvedValueOnce(resultOk); @@ -94,7 +94,10 @@ describe("SendSignTransactionTask", () => { expect(apiMock.sendCommand.mock.calls).toHaveLength(1); expect(apiMock.sendCommand.mock.calls[0]![0]).toStrictEqual( new SignTransactionCommand({ - transaction: new Uint8Array([...PATH, ...SIMPLE_TRANSACTION]), + serializedTransaction: new Uint8Array([ + ...PATH, + ...SIMPLE_TRANSACTION, + ]), isFirstChunk: true, }), ); @@ -106,7 +109,7 @@ describe("SendSignTransactionTask", () => { // GIVEN const args = { derivationPath: "44'/60'/0'/0/0", - transaction: BIG_TRANSACTION, + serializedTransaction: BIG_TRANSACTION, }; apiMock.sendCommand.mockResolvedValueOnce(resultNothing); apiMock.sendCommand.mockResolvedValueOnce(resultOk); @@ -118,19 +121,19 @@ describe("SendSignTransactionTask", () => { expect(apiMock.sendCommand.mock.calls).toHaveLength(2); expect(apiMock.sendCommand.mock.calls[0]![0]).toStrictEqual( new SignTransactionCommand({ - transaction: new Uint8Array([...PATH, ...BIG_TRANSACTION]).slice( - 0, - APDU_MAX_PAYLOAD, - ), + serializedTransaction: new Uint8Array([ + ...PATH, + ...BIG_TRANSACTION, + ]).slice(0, APDU_MAX_PAYLOAD), isFirstChunk: true, }), ); expect(apiMock.sendCommand.mock.calls[1]![0]).toStrictEqual( new SignTransactionCommand({ - transaction: new Uint8Array([...PATH, ...BIG_TRANSACTION]).slice( - APDU_MAX_PAYLOAD, - APDU_MAX_PAYLOAD * 2, - ), + serializedTransaction: new Uint8Array([ + ...PATH, + ...BIG_TRANSACTION, + ]).slice(APDU_MAX_PAYLOAD, APDU_MAX_PAYLOAD * 2), isFirstChunk: false, }), ); @@ -142,7 +145,7 @@ describe("SendSignTransactionTask", () => { // GIVEN const args = { derivationPath: "44'/60'/0'/0/0", - transaction: SIMPLE_TRANSACTION, + serializedTransaction: SIMPLE_TRANSACTION, }; apiMock.sendCommand.mockResolvedValueOnce(resultNothing); @@ -153,7 +156,10 @@ describe("SendSignTransactionTask", () => { expect(apiMock.sendCommand.mock.calls).toHaveLength(1); expect(apiMock.sendCommand.mock.calls[0]![0]).toStrictEqual( new SignTransactionCommand({ - transaction: new Uint8Array([...PATH, ...SIMPLE_TRANSACTION]), + serializedTransaction: new Uint8Array([ + ...PATH, + ...SIMPLE_TRANSACTION, + ]), isFirstChunk: true, }), ); @@ -167,7 +173,7 @@ describe("SendSignTransactionTask", () => { // GIVEN const args = { derivationPath: "44'/60'/0'/0/0", - transaction: BIG_TRANSACTION, + serializedTransaction: BIG_TRANSACTION, }; apiMock.sendCommand.mockResolvedValueOnce(resultNothing); apiMock.sendCommand.mockResolvedValueOnce( @@ -183,19 +189,19 @@ describe("SendSignTransactionTask", () => { expect(apiMock.sendCommand.mock.calls).toHaveLength(2); expect(apiMock.sendCommand.mock.calls[0]![0]).toStrictEqual( new SignTransactionCommand({ - transaction: new Uint8Array([...PATH, ...BIG_TRANSACTION]).slice( - 0, - APDU_MAX_PAYLOAD, - ), + serializedTransaction: new Uint8Array([ + ...PATH, + ...BIG_TRANSACTION, + ]).slice(0, APDU_MAX_PAYLOAD), isFirstChunk: true, }), ); expect(apiMock.sendCommand.mock.calls[1]![0]).toStrictEqual( new SignTransactionCommand({ - transaction: new Uint8Array([...PATH, ...BIG_TRANSACTION]).slice( - APDU_MAX_PAYLOAD, - APDU_MAX_PAYLOAD * 2, - ), + serializedTransaction: new Uint8Array([ + ...PATH, + ...BIG_TRANSACTION, + ]).slice(APDU_MAX_PAYLOAD, APDU_MAX_PAYLOAD * 2), isFirstChunk: false, }), ); diff --git a/packages/signer/keyring-eth/src/internal/app-binder/task/SendSignTransactionTask.ts b/packages/signer/keyring-eth/src/internal/app-binder/task/SendSignTransactionTask.ts index 08cbedb04..beaa79b5e 100644 --- a/packages/signer/keyring-eth/src/internal/app-binder/task/SendSignTransactionTask.ts +++ b/packages/signer/keyring-eth/src/internal/app-binder/task/SendSignTransactionTask.ts @@ -20,7 +20,7 @@ const PATH_SIZE = 4; type SendSignTransactionTaskArgs = { derivationPath: string; - transaction: Uint8Array; + serializedTransaction: Uint8Array; }; export class SendSignTransactionTask { @@ -30,11 +30,11 @@ export class SendSignTransactionTask { ) {} async run(): Promise> { - const { derivationPath, transaction } = this.args; + const { derivationPath, serializedTransaction } = this.args; const paths = DerivationPathUtils.splitPath(derivationPath); const builder = new ByteArrayBuilder( - transaction.length + 1 + paths.length * PATH_SIZE, + serializedTransaction.length + 1 + paths.length * PATH_SIZE, ); // add the derivation paths length builder.add8BitUIntToData(paths.length); @@ -43,7 +43,7 @@ export class SendSignTransactionTask { builder.add32BitUIntToData(path); }); // add the transaction - builder.addBufferToData(transaction); + builder.addBufferToData(serializedTransaction); const buffer = builder.build(); @@ -55,7 +55,7 @@ export class SendSignTransactionTask { for (let i = 0; i < buffer.length; i += APDU_MAX_PAYLOAD) { result = await this.api.sendCommand( new SignTransactionCommand({ - transaction: buffer.slice(i, i + APDU_MAX_PAYLOAD), + serializedTransaction: buffer.slice(i, i + APDU_MAX_PAYLOAD), isFirstChunk: i === 0, }), ); diff --git a/packages/signer/keyring-eth/src/internal/transaction/service/mapper/EthersV5TransactionMapper.test.ts b/packages/signer/keyring-eth/src/internal/transaction/service/mapper/EthersV5TransactionMapper.test.ts index 5a2fe6eaa..b85767c86 100644 --- a/packages/signer/keyring-eth/src/internal/transaction/service/mapper/EthersV5TransactionMapper.test.ts +++ b/packages/signer/keyring-eth/src/internal/transaction/service/mapper/EthersV5TransactionMapper.test.ts @@ -26,7 +26,7 @@ describe("EthersV5TransactionMapper", () => { value: EthersV5BigNumber.from(0), data: "0x", }; - const serialized = new Uint8Array([ + const serializedTransaction = new Uint8Array([ 0xc9, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01, 0x80, 0x80, ]); @@ -41,7 +41,7 @@ describe("EthersV5TransactionMapper", () => { to: undefined, data: "0x", }, - serialized, + serializedTransaction, }), ); }); @@ -56,7 +56,7 @@ describe("EthersV5TransactionMapper", () => { data: "0x", to: "0x", }; - const serialized = new Uint8Array([ + const serializedTransaction = new Uint8Array([ 0xc9, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01, 0x80, 0x80, ]); @@ -71,7 +71,7 @@ describe("EthersV5TransactionMapper", () => { to: "0x", data: "0x", }, - serialized, + serializedTransaction, }), ); }); @@ -93,7 +93,7 @@ describe("EthersV5TransactionMapper", () => { maxFeePerGas: EthersV5BigNumber.from(0), maxPriorityFeePerGas: EthersV5BigNumber.from(0), }; - const serialized = new Uint8Array([ + const serializedTransaction = new Uint8Array([ 0xc9, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01, 0x80, 0x80, ]); @@ -108,7 +108,7 @@ describe("EthersV5TransactionMapper", () => { to: undefined, data: "0x", }, - serialized, + serializedTransaction, }), ); }); diff --git a/packages/signer/keyring-eth/src/internal/transaction/service/mapper/EthersV5TransactionMapper.ts b/packages/signer/keyring-eth/src/internal/transaction/service/mapper/EthersV5TransactionMapper.ts index 29e2d00fd..c7d2051ce 100644 --- a/packages/signer/keyring-eth/src/internal/transaction/service/mapper/EthersV5TransactionMapper.ts +++ b/packages/signer/keyring-eth/src/internal/transaction/service/mapper/EthersV5TransactionMapper.ts @@ -27,7 +27,7 @@ export class EthersV5TransactionMapper implements TransactionMapper { value: transaction.value, chainId: transaction.chainId, }; - const serialized = ethers.utils.arrayify( + const serializedTransaction = ethers.utils.arrayify( ethers.utils.serializeTransaction(txUnsigned), ); @@ -37,7 +37,7 @@ export class EthersV5TransactionMapper implements TransactionMapper { to: transaction.to, data: transaction.data, }, - serialized, + serializedTransaction, }); } diff --git a/packages/signer/keyring-eth/src/internal/transaction/service/mapper/EthersV6TransactionMapper.test.ts b/packages/signer/keyring-eth/src/internal/transaction/service/mapper/EthersV6TransactionMapper.test.ts index 203260112..dbb59087c 100644 --- a/packages/signer/keyring-eth/src/internal/transaction/service/mapper/EthersV6TransactionMapper.test.ts +++ b/packages/signer/keyring-eth/src/internal/transaction/service/mapper/EthersV6TransactionMapper.test.ts @@ -19,7 +19,7 @@ describe("EthersV6TransactionMapper", () => { transaction.chainId = 1n; transaction.nonce = 0; transaction.data = "0x"; - const serialized = new Uint8Array([ + const serializedTransaction = new Uint8Array([ 2, 201, 1, 128, 128, 128, 128, 128, 128, 128, 192, ]); @@ -34,7 +34,7 @@ describe("EthersV6TransactionMapper", () => { to: undefined, data: "0x", }, - serialized, + serializedTransaction, }), ); }); @@ -46,7 +46,7 @@ describe("EthersV6TransactionMapper", () => { transaction.nonce = 0; transaction.data = "0x"; transaction.to = "0x0123456789abcdef0123456789abcdef01234567"; - const serialized = new Uint8Array([ + const serializedTransaction = new Uint8Array([ 0x02, 0xdd, 0x01, 0x80, 0x80, 0x80, 0x80, 0x94, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x80, 0x80, 0xc0, @@ -63,7 +63,7 @@ describe("EthersV6TransactionMapper", () => { to: "0x0123456789abcDEF0123456789abCDef01234567", data: "0x", }, - serialized, + serializedTransaction, }), ); }); @@ -79,7 +79,7 @@ describe("EthersV6TransactionMapper", () => { transaction.gasPrice = 0n; transaction.value = 0n; transaction.chainId = 1n; - const serialized = new Uint8Array([ + const serializedTransaction = new Uint8Array([ 0xdd, 0x80, 0x80, 0x80, 0x94, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x80, 0x80, 0x01, 0x80, 0x80, @@ -96,7 +96,7 @@ describe("EthersV6TransactionMapper", () => { to: "0x0123456789abcDEF0123456789abCDef01234567", data: "0x", }, - serialized, + serializedTransaction, }), ); }); diff --git a/packages/signer/keyring-eth/src/internal/transaction/service/mapper/EthersV6TransactionMapper.ts b/packages/signer/keyring-eth/src/internal/transaction/service/mapper/EthersV6TransactionMapper.ts index 4745a9307..c721191ff 100644 --- a/packages/signer/keyring-eth/src/internal/transaction/service/mapper/EthersV6TransactionMapper.ts +++ b/packages/signer/keyring-eth/src/internal/transaction/service/mapper/EthersV6TransactionMapper.ts @@ -11,14 +11,14 @@ import { TransactionMapper } from "./TransactionMapper"; export class EthersV6TransactionMapper implements TransactionMapper { map(transaction: Transaction): Maybe { if (this.isEthersV6Transaction(transaction)) { - const serialized = getBytes(transaction.unsignedSerialized); + const serializedTransaction = getBytes(transaction.unsignedSerialized); return Just({ subset: { chainId: Number(transaction.chainId.toString()), to: transaction.to ?? undefined, data: transaction.data, }, - serialized, + serializedTransaction, }); } diff --git a/packages/signer/keyring-eth/src/internal/transaction/service/mapper/model/TransactionMapperResult.ts b/packages/signer/keyring-eth/src/internal/transaction/service/mapper/model/TransactionMapperResult.ts index 832d67fe1..f0de464f2 100644 --- a/packages/signer/keyring-eth/src/internal/transaction/service/mapper/model/TransactionMapperResult.ts +++ b/packages/signer/keyring-eth/src/internal/transaction/service/mapper/model/TransactionMapperResult.ts @@ -7,7 +7,7 @@ export type TransactionMapperResult = { subset: TransactionSubset; /** - * serialized transaction in Uint8Array format + * serializedTransaction transaction in Uint8Array format */ - serialized: Uint8Array; + serializedTransaction: Uint8Array; }; From af96989922a5231c0ad7b5046014099b2fbd69cd Mon Sep 17 00:00:00 2001 From: Louis Aussedat Date: Wed, 28 Aug 2024 11:00:43 +0200 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=A8=20(keyring-eth):=20Add=20BuildTra?= =?UTF-8?q?nsactionContextTask?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BuildTransactionContextTask.test.ts | 210 ++++++++++++++++++ .../task/BuildTransactionContextTask.ts | 47 ++++ 2 files changed, 257 insertions(+) create mode 100644 packages/signer/keyring-eth/src/internal/app-binder/task/BuildTransactionContextTask.test.ts create mode 100644 packages/signer/keyring-eth/src/internal/app-binder/task/BuildTransactionContextTask.ts 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 new file mode 100644 index 000000000..7f6ed9f39 --- /dev/null +++ b/packages/signer/keyring-eth/src/internal/app-binder/task/BuildTransactionContextTask.test.ts @@ -0,0 +1,210 @@ +import { ClearSignContext } from "@ledgerhq/context-module"; +import { Transaction } from "ethers-v6"; +import { Left, Right } from "purify-ts"; + +import { TransactionMapperResult } from "@internal/transaction/service/mapper/model/TransactionMapperResult"; +import { TransactionMapperService } from "@internal/transaction/service/mapper/TransactionMapperService"; + +import { BuildTransactionContextTask } from "./BuildTransactionContextTask"; + +describe("BuildTransactionContextTask", () => { + const contextModuleMock = { + getContexts: jest.fn(), + getTypedDataFilters: jest.fn(), + }; + const mapperMock = { + mapTransactionToSubset: jest.fn(), + }; + const defaultOptions = { + domain: "domain-name.eth", + }; + let defaultTransaction: Transaction; + + beforeEach(() => { + jest.clearAllMocks(); + + defaultTransaction = new Transaction(); + defaultTransaction.chainId = 1n; + defaultTransaction.nonce = 0; + defaultTransaction.data = "0x"; + }); + + it("should build the transaction context without clear sign contexts", async () => { + // GIVEN + const serializedTransaction = new Uint8Array([0x01, 0x02, 0x03]); + const clearSignContexts: ClearSignContext[] = []; + const mapperResult: TransactionMapperResult = { + subset: { chainId: 1, to: undefined, data: "0x" }, + serializedTransaction, + }; + mapperMock.mapTransactionToSubset.mockReturnValueOnce(Right(mapperResult)); + contextModuleMock.getContexts.mockResolvedValueOnce(clearSignContexts); + + // WHEN + const result = await new BuildTransactionContextTask( + contextModuleMock, + mapperMock as unknown as TransactionMapperService, + defaultTransaction, + defaultOptions, + "challenge", + ).run(); + + // THEN + expect(result).toEqual({ + clearSignContexts, + serializedTransaction, + }); + }); + + it("should build the transaction context with clear sign contexts", async () => { + // GIVEN + const serializedTransaction = new Uint8Array([0x01, 0x02, 0x03]); + const clearSignContexts: ClearSignContext[] = [ + { + type: "token", + payload: "payload-1", + }, + { + type: "nft", + payload: "payload-2", + }, + ]; + const mapperResult: TransactionMapperResult = { + subset: { chainId: 1, to: undefined, data: "0x" }, + serializedTransaction, + }; + mapperMock.mapTransactionToSubset.mockReturnValueOnce(Right(mapperResult)); + contextModuleMock.getContexts.mockResolvedValueOnce(clearSignContexts); + + // WHEN + const result = await new BuildTransactionContextTask( + contextModuleMock, + mapperMock as unknown as TransactionMapperService, + defaultTransaction, + defaultOptions, + "challenge", + ).run(); + + // THEN + expect(result).toEqual({ + clearSignContexts, + serializedTransaction, + }); + }); + + it("should call the mapper with the transaction", async () => { + // GIVEN + const serializedTransaction = new Uint8Array([0x01, 0x02, 0x03]); + const clearSignContexts: ClearSignContext[] = []; + const mapperResult: TransactionMapperResult = { + subset: { chainId: 1, to: undefined, data: "0x" }, + serializedTransaction, + }; + mapperMock.mapTransactionToSubset.mockReturnValueOnce(Right(mapperResult)); + contextModuleMock.getContexts.mockResolvedValueOnce(clearSignContexts); + + // WHEN + await new BuildTransactionContextTask( + contextModuleMock, + mapperMock as unknown as TransactionMapperService, + defaultTransaction, + defaultOptions, + "challenge", + ).run(); + + // THEN + expect(mapperMock.mapTransactionToSubset).toHaveBeenCalledWith( + defaultTransaction, + ); + }); + + it("should call the context module with the correct parameters", async () => { + // GIVEN + const serializedTransaction = new Uint8Array([0x01, 0x02, 0x03]); + const clearSignContexts: ClearSignContext[] = []; + const mapperResult: TransactionMapperResult = { + subset: { chainId: 1, to: undefined, data: "0x" }, + serializedTransaction, + }; + mapperMock.mapTransactionToSubset.mockReturnValueOnce(Right(mapperResult)); + contextModuleMock.getContexts.mockResolvedValueOnce(clearSignContexts); + + // WHEN + await new BuildTransactionContextTask( + contextModuleMock, + mapperMock as unknown as TransactionMapperService, + defaultTransaction, + defaultOptions, + "challenge", + ).run(); + + // THEN + expect(contextModuleMock.getContexts).toHaveBeenCalledWith({ + challenge: "challenge", + domain: "domain-name.eth", + ...mapperResult.subset, + }); + }); + + it("should throw an error if the mapper returns an error", async () => { + // GIVEN + const error = new Error("error"); + mapperMock.mapTransactionToSubset.mockReturnValueOnce(Left(error)); + + // WHEN + const task = new BuildTransactionContextTask( + contextModuleMock, + mapperMock as unknown as TransactionMapperService, + defaultTransaction, + defaultOptions, + "challenge", + ); + + // THEN + await expect(task.run()).rejects.toThrow(error); + }); + + it("should exclude error contexts from the result", async () => { + // GIVEN + const serializedTransaction = new Uint8Array([0x01, 0x02, 0x03]); + const clearSignContexts: ClearSignContext[] = [ + { + type: "error", + error: new Error("error"), + }, + { + type: "token", + payload: "payload-1", + }, + { + type: "error", + error: new Error("error"), + }, + { + type: "nft", + payload: "payload-2", + }, + ]; + const mapperResult: TransactionMapperResult = { + subset: { chainId: 1, to: undefined, data: "0x" }, + serializedTransaction, + }; + mapperMock.mapTransactionToSubset.mockReturnValueOnce(Right(mapperResult)); + contextModuleMock.getContexts.mockResolvedValueOnce(clearSignContexts); + + // WHEN + const result = await new BuildTransactionContextTask( + contextModuleMock, + mapperMock as unknown as TransactionMapperService, + defaultTransaction, + defaultOptions, + "challenge", + ).run(); + + // THEN + expect(result).toEqual({ + clearSignContexts: [clearSignContexts[1], clearSignContexts[3]], + serializedTransaction, + }); + }); +}); diff --git a/packages/signer/keyring-eth/src/internal/app-binder/task/BuildTransactionContextTask.ts b/packages/signer/keyring-eth/src/internal/app-binder/task/BuildTransactionContextTask.ts new file mode 100644 index 000000000..798a2508a --- /dev/null +++ b/packages/signer/keyring-eth/src/internal/app-binder/task/BuildTransactionContextTask.ts @@ -0,0 +1,47 @@ +import { + ClearSignContextSuccess, + ContextModule, +} from "@ledgerhq/context-module"; + +import { Transaction } from "@api/model/Transaction"; +import { TransactionOptions } from "@api/model/TransactionOptions"; +import { TransactionMapperService } from "@internal/transaction/service/mapper/TransactionMapperService"; + +export type BuildTransactionTaskResult = { + readonly clearSignContexts: ClearSignContextSuccess[]; + readonly serializedTransaction: Uint8Array; +}; + +export class BuildTransactionContextTask { + constructor( + private contextModule: ContextModule, + private mapper: TransactionMapperService, + private transaction: Transaction, + private options: TransactionOptions, + private challenge: string, + ) {} + + async run(): Promise { + const parsed = this.mapper.mapTransactionToSubset(this.transaction); + parsed.ifLeft((err) => { + throw err; + }); + const { subset, serializedTransaction } = parsed.unsafeCoerce(); + + const clearSignContexts = await this.contextModule.getContexts({ + challenge: this.challenge, + domain: this.options.domain, + ...subset, + }); + + // TODO: for now we ignore the error contexts + // as we consider that they are warnings and not blocking + const clearSignContextsSuccess: ClearSignContextSuccess[] = + clearSignContexts.filter((context) => context.type !== "error"); + + return { + clearSignContexts: clearSignContextsSuccess, + serializedTransaction, + }; + } +} From 43eb989569e14c9f61356099545204a71c2032a8 Mon Sep 17 00:00:00 2001 From: Louis Aussedat Date: Tue, 27 Aug 2024 14:24:08 +0200 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=94=96=20(keyring-eth):=20Changeset?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/nervous-phones-cross.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/nervous-phones-cross.md diff --git a/.changeset/nervous-phones-cross.md b/.changeset/nervous-phones-cross.md new file mode 100644 index 000000000..c7b5e22bb --- /dev/null +++ b/.changeset/nervous-phones-cross.md @@ -0,0 +1,5 @@ +--- +"@ledgerhq/keyring-eth": patch +--- + +Add BuildTransactionContextTask