From e0073dd4289d366a0d997221b53c7acbc09c8953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Prohaszka?= Date: Thu, 24 Oct 2024 16:37:10 +0200 Subject: [PATCH 1/2] feat(swap): use test dataset from cal instead of harcoded one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Prohaszka --- .../src/cal/currencies.integration.test.ts | 19 + libs/ledger-live-common/src/cal/currencies.ts | 46 ++ libs/ledger-live-common/src/cal/index.ts | 13 + .../src/cal/partners.integration.test.ts | 65 +++ .../src/cal/partners.test.ts | 177 ++++++ libs/ledger-live-common/src/cal/partners.ts | 84 +++ libs/ledger-live-common/src/exchange/index.ts | 8 +- .../providers/getProvidersData.test.ts | 113 ---- .../exchange/providers/getProvidersData.ts | 44 -- .../src/exchange/providers/sell.ts | 2 +- .../providers/swap.integration.test.ts | 215 ++++++++ .../src/exchange/providers/swap.ts | 42 +- .../exchange/testCurrencyConfig/data/coins.ts | 39 -- .../exchange/testCurrencyConfig/data/erc20.ts | 15 - .../src/exchange/testCurrencyConfig/index.ts | 33 -- .../packages/hw-app-exchange/README.md | 9 +- .../src/Exchange.integ.test.ts | 517 +++++++++--------- 17 files changed, 898 insertions(+), 543 deletions(-) create mode 100644 libs/ledger-live-common/src/cal/currencies.integration.test.ts create mode 100644 libs/ledger-live-common/src/cal/currencies.ts create mode 100644 libs/ledger-live-common/src/cal/index.ts create mode 100644 libs/ledger-live-common/src/cal/partners.integration.test.ts create mode 100644 libs/ledger-live-common/src/cal/partners.test.ts create mode 100644 libs/ledger-live-common/src/cal/partners.ts delete mode 100644 libs/ledger-live-common/src/exchange/providers/getProvidersData.test.ts delete mode 100644 libs/ledger-live-common/src/exchange/providers/getProvidersData.ts create mode 100644 libs/ledger-live-common/src/exchange/providers/swap.integration.test.ts delete mode 100644 libs/ledger-live-common/src/exchange/testCurrencyConfig/data/coins.ts delete mode 100644 libs/ledger-live-common/src/exchange/testCurrencyConfig/data/erc20.ts delete mode 100644 libs/ledger-live-common/src/exchange/testCurrencyConfig/index.ts diff --git a/libs/ledger-live-common/src/cal/currencies.integration.test.ts b/libs/ledger-live-common/src/cal/currencies.integration.test.ts new file mode 100644 index 000000000000..9e24ff03387f --- /dev/null +++ b/libs/ledger-live-common/src/cal/currencies.integration.test.ts @@ -0,0 +1,19 @@ +import { findCurrencyData } from "./currencies"; + +describe("findCurrencyData", () => { + it("returns all data in expected format", async () => { + // Given + const currencyId = "arbitrum"; + + // When + const currencies = await findCurrencyData(currencyId); + + // Then + expect(currencies).toEqual({ + config: "0345544808457468657265756d0d0345544812000000000000a4b1", + id: currencyId, + signature: + "30450221008ca557e4acc2fa290a6a44c2b0eb5232712ba69b23df93645a320bcff9789fd9022017e6e05582806a9d4b7b2aaaedbcc3471bd26e10ad686e4f313fc0b1068b5d64", + }); + }); +}); diff --git a/libs/ledger-live-common/src/cal/currencies.ts b/libs/ledger-live-common/src/cal/currencies.ts new file mode 100644 index 000000000000..f7fefa69ed5c --- /dev/null +++ b/libs/ledger-live-common/src/cal/currencies.ts @@ -0,0 +1,46 @@ +import { getEnv } from "@ledgerhq/live-env"; +import network from "@ledgerhq/live-network"; + +const CAL_BASE_URL = getEnv("CAL_SERVICE_URL"); + +type CurrencyDataResponse = { + id: string; + descriptor_exchange_app: { + data: string; + signatures: { + prod: string; + test: string; + }; + }; +}; +export type CurrencyData = { + id: string; + config: string; + signature: string; +}; + +export async function findCurrencyData( + id: string, + env: "prod" | "test" = "prod", +): Promise { + const { data: currencyData } = await network({ + method: "GET", + url: `${CAL_BASE_URL}/v1/currencies`, + params: { + output: "id,descriptor_exchange_app", + id, + }, + }); + if (!currencyData.length) { + throw new Error(`CAL currencies, missing configuration for ${id}`); + } + if (currencyData.length !== 1) { + throw new Error(`CAL currencies, multiple configurations found for ${id}`); + } + + return { + id: currencyData[0].id, + config: currencyData[0].descriptor_exchange_app.data, + signature: currencyData[0].descriptor_exchange_app.signatures[env], + }; +} diff --git a/libs/ledger-live-common/src/cal/index.ts b/libs/ledger-live-common/src/cal/index.ts new file mode 100644 index 000000000000..ede0fa12d30d --- /dev/null +++ b/libs/ledger-live-common/src/cal/index.ts @@ -0,0 +1,13 @@ +/* + * Crypto Asset List Service + * + * Use only exposed methods below outside of this module. + */ + +export { findCurrencyData, type CurrencyData } from "./currencies"; +export { + getProvidersCDNData, + getProvidersData, + type PartnerType, + type ExchangeProvider, +} from "./partners"; diff --git a/libs/ledger-live-common/src/cal/partners.integration.test.ts b/libs/ledger-live-common/src/cal/partners.integration.test.ts new file mode 100644 index 000000000000..de20d524e453 --- /dev/null +++ b/libs/ledger-live-common/src/cal/partners.integration.test.ts @@ -0,0 +1,65 @@ +import { getProvidersCDNData, getProvidersData } from "./partners"; + +describe("getProvidersCDNData", () => { + it("returns CEX data in expected format", async () => { + // Given + + // When + const partners = await getProvidersCDNData(); + + // Then + expect(partners["changelly"]).toEqual({ + continuesInProviderLiveApp: false, + displayName: "Changelly", + mainUrl: "https://changelly.com/", + needsKYC: false, + supportUrl: "https://support.changelly.com/en/support/home", + termsOfUseUrl: "https://changelly.com/terms-of-use", + type: "CEX", + }); + }); + + it("returns DEX data in expected format", async () => { + // Given + + // When + const partners = await getProvidersCDNData(); + + // Then + expect(partners["paraswap"]).toEqual({ + continuesInProviderLiveApp: true, + displayName: "Paraswap", + mainUrl: "https://www.paraswap.io/", + needsKYC: false, + supportUrl: "https://help.paraswap.io/en/", + termsOfUseUrl: "https://files.paraswap.io/tos_v4.pdf", + type: "DEX", + }); + }); +}); + +describe("getProvidersData", () => { + it("returns data in expected format", async () => { + // Given + + // When + const partners = await getProvidersData("swap"); + + // Then + expect(partners["changelly"]).toEqual({ + name: "Changelly", + publicKey: { + curve: "secp256k1", + data: Buffer.from( + "0480d7c0d3a9183597395f58dda05999328da6f18fabd5cda0aff8e8e3fc633436a2dbf48ecb23d40df7c3c7d3e774b77b4b5df0e9f7e08cf1cdf2dba788eb085b", + "hex", + ), + }, + signature: Buffer.from( + "3045022100e73339e5071b5d232e8cacecbd7c118c919122a43f8abb8b2062d4bfcd58274e022050b11605d8b7e199f791266146227c43fd11d7645b1d881f705a2f8841d21de5", + "hex", + ), + version: 1, + }); + }); +}); diff --git a/libs/ledger-live-common/src/cal/partners.test.ts b/libs/ledger-live-common/src/cal/partners.test.ts new file mode 100644 index 000000000000..30e3e083fe7c --- /dev/null +++ b/libs/ledger-live-common/src/cal/partners.test.ts @@ -0,0 +1,177 @@ +import { + transformData, + getProvidersData, + type ProvidersDataResponse, + type ExchangeProvider, +} from "./partners"; +import network from "@ledgerhq/live-network"; + +jest.mock("@ledgerhq/live-network"); + +describe("transformData", () => { + it.each([ + [ + "prod", + { + providera: { + name: "ProviderA", + publicKey: { + curve: "secp256k1", + data: Buffer.from("1234567890abcdef", "hex"), + }, + signature: Buffer.from("a1b2c3", "hex"), + version: 2, + } satisfies ExchangeProvider, + providerb: { + name: "ProviderB", + publicKey: { + curve: "secp256r1", + data: Buffer.from("abcdef1234567890", "hex"), + }, + signature: Buffer.from("d4e5f6", "hex"), + version: 2, + } satisfies ExchangeProvider, + }, + ], + [ + "test", + { + providera: { + name: "ProviderA", + publicKey: { + curve: "secp256k1", + data: Buffer.from("1234567890abcdef", "hex"), + }, + signature: Buffer.from("d1e2f3", "hex"), + version: 2, + } satisfies ExchangeProvider, + providerb: { + name: "ProviderB", + publicKey: { + curve: "secp256r1", + data: Buffer.from("abcdef1234567890", "hex"), + }, + signature: Buffer.from("a9b8c7", "hex"), + version: 2, + } satisfies ExchangeProvider, + }, + ], + ])( + "should transform providers data correctly with %p env", + (env: string, expected: Record) => { + const providersData = [ + { + name: "ProviderA", + public_key: "1234567890abcdef", + public_key_curve: "secp256k1", + service_app_version: 2, + descriptor: { + data: "09abcd", + signatures: { + prod: "a1b2c3", + test: "d1e2f3", + }, + }, + }, + { + name: "ProviderB", + public_key: "abcdef1234567890", + public_key_curve: "secp256r1", + service_app_version: 2, + descriptor: { + data: "ef0123", + signatures: { + prod: "d4e5f6", + test: "a9b8c7", + }, + }, + }, + ]; + + const result = transformData(providersData, env as "prod" | "test"); + expect(result).toEqual(expected); + }, + ); + + it("should handle empty providers data", () => { + const providersData: ProvidersDataResponse = []; + + const expected: Record = {}; + + const result = transformData(providersData, "prod"); + expect(result).toEqual(expected); + }); +}); + +describe("getProvidersData", () => { + it.each([ + [ + "prod", + { + providera: { + name: "ProviderA", + publicKey: { + curve: "secp256k1", + data: Buffer.from("1234567890abcdef", "hex"), + }, + signature: Buffer.from("a1b2c3", "hex"), + version: 2, + } satisfies ExchangeProvider, + }, + ], + [ + "test", + { + providera: { + name: "ProviderA", + publicKey: { + curve: "secp256k1", + data: Buffer.from("1234567890abcdef", "hex"), + }, + signature: Buffer.from("d1e2f3", "hex"), + version: 2, + } satisfies ExchangeProvider, + }, + ], + ])( + "should fetch and transform providers data with %p signature", + async (env: string, expected: Record) => { + const mockProvidersData: ProvidersDataResponse = [ + { + name: "ProviderA", + public_key: "1234567890abcdef", + public_key_curve: "secp256k1", + service_app_version: 2, + descriptor: { + data: "09abcd", + signatures: { + prod: "a1b2c3", + test: "d1e2f3", + }, + }, + }, + ]; + + (network as jest.Mock).mockResolvedValue({ data: mockProvidersData }); + + const result = await getProvidersData("swap", env as "prod" | "test"); + + expect(network).toHaveBeenCalledWith({ + method: "GET", + url: "https://crypto-assets-service.api.ledger.com/v1/partners", + params: { + output: "name,public_key,public_key_curve,service_app_version,descriptor", + service_name: "swap", + }, + }); + + expect(result).toEqual(expected); + }, + ); + + it("should handle errors when fetching data", async () => { + (network as jest.Mock).mockRejectedValue(new Error("Network error")); + + await expect(getProvidersData("swap")).rejects.toThrow("Network error"); + }); +}); diff --git a/libs/ledger-live-common/src/cal/partners.ts b/libs/ledger-live-common/src/cal/partners.ts new file mode 100644 index 000000000000..bbb3024b2697 --- /dev/null +++ b/libs/ledger-live-common/src/cal/partners.ts @@ -0,0 +1,84 @@ +import { getEnv } from "@ledgerhq/live-env"; +import network from "@ledgerhq/live-network"; + +const CAL_BASE_URL = getEnv("CAL_SERVICE_URL"); + +export type PartnerType = { + continuesInProviderLiveApp: boolean; + displayName: string; + mainUrl: string; + needsKYC: boolean; + supportUrl: string; + termsOfUseUrl: string; + type: "CEX" | "DEX"; + version?: number; +}; + +export async function getProvidersCDNData(): Promise> { + const { data: providers } = await network>({ + url: "https://cdn.live.ledger.com/swap-providers/data.json", + }); + + return providers; +} + +export type ExchangeProvider = { + name: string; + publicKey: { + curve: "secp256k1" | "secp256r1"; + data: Buffer; + }; + version?: number; + signature: Buffer; +}; +// Exported for test purpose only +export type ProvidersDataResponse = { + name: string; + public_key: string; + public_key_curve: string; + service_app_version: number; + descriptor: { + data: string; + signatures: { + prod: string; + test: string; + }; + }; +}[]; + +// Exported for test purpose only +export function transformData( + providersData: ProvidersDataResponse, + env: "prod" | "test", +): Record { + const transformed = {}; + providersData.forEach(provider => { + const key = provider.name.toLowerCase(); + transformed[key] = { + name: provider.name, + publicKey: { + curve: provider.public_key_curve, + data: Buffer.from(provider.public_key, "hex"), + }, + version: provider.service_app_version, + signature: Buffer.from(provider.descriptor.signatures[env], "hex"), + }; + }); + return transformed; +} + +export async function getProvidersData( + type: "swap" | "fund" | "sell", + env: "prod" | "test" = "prod", +): Promise> { + const { data: providersData } = await network({ + method: "GET", + url: `${CAL_BASE_URL}/v1/partners`, + params: { + output: "name,public_key,public_key_curve,service_app_version,descriptor", + service_name: type, + }, + }); + + return transformData(providersData, env); +} diff --git a/libs/ledger-live-common/src/exchange/index.ts b/libs/ledger-live-common/src/exchange/index.ts index 2db546b796c3..321c521a62eb 100644 --- a/libs/ledger-live-common/src/exchange/index.ts +++ b/libs/ledger-live-common/src/exchange/index.ts @@ -1,9 +1,8 @@ import { valid, gte } from "semver"; import type { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets"; import { getEnv } from "@ledgerhq/live-env"; -import { findTestExchangeCurrencyConfig } from "./testCurrencyConfig"; -import { findExchangeCurrencyData } from "./providers/swap"; import { findExchangeCurrencyConfig as findProdExchangeCurrencyConfig } from "@ledgerhq/cryptoassets"; +import { findCurrencyData } from "../cal"; // Minimum version of a currency app which has exchange capabilities, meaning it can be used // for sell/swap, and do silent signing. const exchangeSupportAppVersions = { @@ -42,9 +41,8 @@ export const getCurrencyExchangeConfig = async ( ): Promise => { let res; try { - res = getEnv("MOCK_EXCHANGE_TEST_CONFIG") - ? await findTestExchangeCurrencyConfig(currency.id) - : await findExchangeCurrencyData(currency.id); + const env = getEnv("MOCK_EXCHANGE_TEST_CONFIG") ? "test" : "prod"; + res = await findCurrencyData(currency.id, env); if (!res) { throw new Error("Missing primary config"); diff --git a/libs/ledger-live-common/src/exchange/providers/getProvidersData.test.ts b/libs/ledger-live-common/src/exchange/providers/getProvidersData.test.ts deleted file mode 100644 index 104b120fbb0f..000000000000 --- a/libs/ledger-live-common/src/exchange/providers/getProvidersData.test.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { transformData, getProvidersData, ProvidersDataResponse } from "./getProvidersData"; -import network from "@ledgerhq/live-network"; - -type TransformedProviderData = { - name: string; - publicKey: { - curve: string; - data: Buffer; - }; - signature: Buffer; - version: number; -}; - -jest.mock("@ledgerhq/live-network"); - -describe("transformData", () => { - it("should transform providers data correctly", () => { - const providersData: ProvidersDataResponse = [ - { - name: "ProviderA", - signature: "a1b2c3", - public_key: "1234567890abcdef", - public_key_curve: "secp256k1", - service_app_version: 2, - }, - { - name: "ProviderB", - signature: "d4e5f6", - public_key: "abcdef1234567890", - public_key_curve: "secp256r1", - service_app_version: 2, - }, - ]; - - const expected: Record = { - providera: { - name: "ProviderA", - publicKey: { - curve: "secp256k1", - data: Buffer.from("1234567890abcdef", "hex"), - }, - signature: Buffer.from("a1b2c3", "hex"), - version: 2, - }, - providerb: { - name: "ProviderB", - publicKey: { - curve: "secp256r1", - data: Buffer.from("abcdef1234567890", "hex"), - }, - signature: Buffer.from("d4e5f6", "hex"), - version: 2, - }, - }; - - const result = transformData(providersData); - expect(result).toEqual(expected); - }); - - it("should handle empty providers data", () => { - const providersData: ProvidersDataResponse = []; - - const expected: Record = {}; - - const result = transformData(providersData); - expect(result).toEqual(expected); - }); -}); - -describe("getProvidersData", () => { - it("should fetch and transform providers data", async () => { - const mockProvidersData: ProvidersDataResponse = [ - { - name: "ProviderA", - signature: "a1b2c3", - public_key: "1234567890abcdef", - public_key_curve: "secp256k1", - service_app_version: 2, - }, - ]; - - (network as jest.Mock).mockResolvedValue({ data: mockProvidersData }); - - const result = await getProvidersData("someType"); - - expect(network).toHaveBeenCalledWith({ - method: "GET", - url: "https://crypto-assets-service.api.ledger.com/v1/partners", - params: { - output: "name,signature,public_key,public_key_curve,service_app_version", - service_name: "someType", - }, - }); - - expect(result).toEqual({ - providera: { - name: "ProviderA", - publicKey: { - curve: "secp256k1", - data: Buffer.from("1234567890abcdef", "hex"), - }, - signature: Buffer.from("a1b2c3", "hex"), - version: 2, - }, - }); - }); - - it("should handle errors when fetching data", async () => { - (network as jest.Mock).mockRejectedValue(new Error("Network error")); - - await expect(getProvidersData("someType")).rejects.toThrow("Network error"); - }); -}); diff --git a/libs/ledger-live-common/src/exchange/providers/getProvidersData.ts b/libs/ledger-live-common/src/exchange/providers/getProvidersData.ts deleted file mode 100644 index 164e30a72401..000000000000 --- a/libs/ledger-live-common/src/exchange/providers/getProvidersData.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { ExchangeProviderNameAndSignature } from "."; -import network from "@ledgerhq/live-network"; - -export type ProvidersDataResponse = { - name: string; - signature: string; - public_key: string; - public_key_curve: string; - service_app_version: number; -}[]; - -export function transformData( - providersData: ProvidersDataResponse, -): Record { - const transformed = {}; - providersData.forEach(provider => { - const key = provider.name.toLowerCase(); - transformed[key] = { - name: provider.name, - publicKey: { - curve: provider.public_key_curve, - data: Buffer.from(provider.public_key, "hex"), - }, - version: provider.service_app_version, - signature: Buffer.from(provider.signature, "hex"), - }; - }); - return transformed; -} - -export const getProvidersData = async ( - type, -): Promise> => { - const { data: providersData } = await network({ - method: "GET", - url: "https://crypto-assets-service.api.ledger.com/v1/partners", - params: { - output: "name,signature,public_key,public_key_curve,service_app_version", - service_name: type, - }, - }); - - return transformData(providersData); -}; diff --git a/libs/ledger-live-common/src/exchange/providers/sell.ts b/libs/ledger-live-common/src/exchange/providers/sell.ts index 07a8ae3924b4..955ccfe5fce0 100644 --- a/libs/ledger-live-common/src/exchange/providers/sell.ts +++ b/libs/ledger-live-common/src/exchange/providers/sell.ts @@ -1,6 +1,6 @@ import { getEnv } from "@ledgerhq/live-env"; import { ExchangeProviderNameAndSignature } from "."; -import { getProvidersData } from "./getProvidersData"; +import { getProvidersData } from "../../cal"; const testSellProvider: ExchangeProviderNameAndSignature = { name: "SELL_TEST", diff --git a/libs/ledger-live-common/src/exchange/providers/swap.integration.test.ts b/libs/ledger-live-common/src/exchange/providers/swap.integration.test.ts new file mode 100644 index 000000000000..e8644f58e885 --- /dev/null +++ b/libs/ledger-live-common/src/exchange/providers/swap.integration.test.ts @@ -0,0 +1,215 @@ +import { fetchAndMergeProviderData, findExchangeCurrencyData } from "./swap"; + +describe("findExchangeCurrencyData", () => { + it("returns all data in expected format", async () => { + // When + const currencies = await findExchangeCurrencyData("arbitrum"); + + // Then + expect(currencies).toEqual({ + config: "0345544808457468657265756d0d0345544812000000000000a4b1", + id: "arbitrum", + signature: + "30450221008ca557e4acc2fa290a6a44c2b0eb5232712ba69b23df93645a320bcff9789fd9022017e6e05582806a9d4b7b2aaaedbcc3471bd26e10ad686e4f313fc0b1068b5d64", + }); + }); +}); + +describe("fetchAndMergeProviderData", () => { + it("returns all data in expected format", async () => { + // When + const providers = await fetchAndMergeProviderData(); + + // Then + expect(providers).toEqual({ + changelly: { + continuesInProviderLiveApp: false, + displayName: "Changelly", + mainUrl: "https://changelly.com/", + name: "Changelly", + needsKYC: false, + publicKey: { + curve: "secp256k1", + data: Buffer.from( + "0480d7c0d3a9183597395f58dda05999328da6f18fabd5cda0aff8e8e3fc633436a2dbf48ecb23d40df7c3c7d3e774b77b4b5df0e9f7e08cf1cdf2dba788eb085b", + "hex", + ), + }, + signature: Buffer.from( + "3045022100e73339e5071b5d232e8cacecbd7c118c919122a43f8abb8b2062d4bfcd58274e022050b11605d8b7e199f791266146227c43fd11d7645b1d881f705a2f8841d21de5", + "hex", + ), + supportUrl: "https://support.changelly.com/en/support/home", + termsOfUseUrl: "https://changelly.com/terms-of-use", + type: "CEX", + version: 1, + }, + changenow: { + name: "ChangeNOW", + publicKey: { + curve: "secp256k1", + data: Buffer.from( + "04f5cbcda3cf0fdb2f150de0bed95b5c0f9ef1db1c8ca5b615d33093f9c8e290eeb7985c2ca2ef765bcf63bf9ade5a5d432d6e7d959e748053b6228e4faf439fc2", + "hex", + ), + }, + signature: Buffer.from( + "3045022100a321d8023cfe4262ef46dfc581350588cfc737d4052fbeb239c7bf92cf8263140220644871cad6c1dbadf853ecafb7d2b80d6c24cd2c473f1e51ebd3bdbe86d4afde", + "hex", + ), + version: 1, + }, + cic: { + continuesInProviderLiveApp: false, + displayName: "CIC", + mainUrl: "https://criptointercambio.com/", + name: "CIC", + needsKYC: false, + publicKey: { + curve: "secp256k1", + data: Buffer.from( + "0444a71652995d15ef0d4d6fe8de21a0c8ad48bdbfea7f789319973669785ca96abca9fd0c504c3074d9b654f0e3a76dde642a03efe4ccdee3af3ca4ba4afa202d", + "hex", + ), + }, + signature: Buffer.from( + "3044022078a73433ab6289027b7a169a260f180d16346f7ab55b06a22109f68a756d691d0220190edd6e1214c3309dc1b0afe90d217b728377491561383f2ee543e2c90188eb", + "hex", + ), + supportUrl: "https://criptointercambio.com/en/about", + termsOfUseUrl: "https://criptointercambio.com/terms-of-use", + type: "CEX", + version: 1, + }, + exodus: { + continuesInProviderLiveApp: false, + displayName: "Exodus", + mainUrl: "https://www.exodus.com/", + name: "Exodus", + needsKYC: false, + publicKey: { + curve: "secp256k1", + data: Buffer.from( + "048abacf1b1027b7c5f96d77826eada263e21d2ba8a2a037a84e0679e05d997a91ff34df181f88bdaf3521c26fb70eb028622f3afd66d0c282d5bb61da38ad76c1", + "hex", + ), + }, + signature: Buffer.from( + "304402206803fd43c5e0af3bf6e4b6a049c3a10f4d44f4b7b6e2bb4cb2cfd3958cfc3192022063ad397ad75769396bdb776bc4e7a8ca9af09c7b583fa9f0b7690bb7d1cbed50", + "hex", + ), + supportUrl: "mailto:support@xopay.com", + termsOfUseUrl: "https://www.exodus.com/terms/", + type: "CEX", + version: 2, + }, + ftx: { + name: "FTX", + publicKey: { + curve: "secp256k1", + data: Buffer.from( + "04c89f3e48cde252f6cd6fcccc47c2f6ca6cf05f9f921703d31b7a7dddbf0bd6a690744662fe599f8761612021ba1fc0e8a5a4b7d5910c625b6dd09aa40762e5cd", + "hex", + ), + }, + signature: Buffer.from( + "3044022029c0fb80d6e524f811f30cc04a349fa7f8896ce1ba84010da55f7be5eb9d528802202727985361cab969ad9b4f56570f3f6120c1d77d04ba10e5d99366d8eecee8e2", + "hex", + ), + version: 1, + }, + moonpay: { + continuesInProviderLiveApp: true, + displayName: "MoonPay", + mainUrl: "https://www.moonpay.com/", + name: "MoonPay", + needsKYC: true, + publicKey: { + curve: "secp256k1", + data: Buffer.from( + "044989cad389020fadfb9d7a85d29338a450beec571347d2989fb57b99ecddbc8907cf8c229deee30fb8ac139e978cab8f6efad76bde2a9c6d6710ceda1fe0a4d8", + "hex", + ), + }, + signature: Buffer.from( + "3045022100a63e1ca73525e2e05fc9e95b906793f252fef7df14f1cd830640a1266f8e11e202205714c92b6829108dd7cc60f4e13917cdc7b4bc5048d83d4db65b19c61e7b8a41", + "hex", + ), + supportUrl: "https://support.moonpay.com/", + termsOfUseUrl: "https://www.moonpay.com/legal/terms_of_use_row", + type: "CEX", + version: 2, + }, + oneinch: { + continuesInProviderLiveApp: true, + displayName: "1inch", + mainUrl: "https://1inch.io/", + needsKYC: false, + supportUrl: "https://help.1inch.io/en/", + termsOfUseUrl: "https://1inch.io/assets/1inch_network_terms_of_use.pdf", + type: "DEX", + }, + paraswap: { + continuesInProviderLiveApp: true, + displayName: "Paraswap", + mainUrl: "https://www.paraswap.io/", + needsKYC: false, + supportUrl: "https://help.paraswap.io/en/", + termsOfUseUrl: "https://files.paraswap.io/tos_v4.pdf", + type: "DEX", + }, + ssaitest: { + name: "ssaitest", + publicKey: { + curve: "secp256k1", + data: Buffer.from( + "0414d746bc20fa933d07c342d2eb0545236be68794f7a55f4b6fac0789c25c553b0a7f78011c7e79d679f9c23ff4412e15b899925079bfeb169768b4b2447b8084", + "hex", + ), + }, + signature: Buffer.from( + "3045022100eef022406ef785114590ef28b03d598025af977a703b2e0287921fa17543e08c022057ca2e6af31f9864bb54634a42bad223d2dccf920e73ed867d59eb6759b5652d", + "hex", + ), + version: 1, + }, + thorswap: { + continuesInProviderLiveApp: false, + displayName: "THORChain", + mainUrl: "https://www.thorswap.finance/", + name: "THORSwap", + needsKYC: false, + publicKey: { + curve: "secp256r1", + data: Buffer.from( + "0480a453a91e728c5f622d966b90d15de6fdb6c267bb8147dd0e0d4e1c730d631594e724aaf2b2f526600f3713ce6bc2adbfdbaafd2121bfee64fce93fd59a9050", + "hex", + ), + }, + signature: Buffer.from( + "304402207a9676f6971575cad70e4ef4d937ebdba82c51e6a0ab5343c11fefa18dff326d0220643f0718da68ead3fd9900eb90bca782d533d1698c8ea1435ae232ddf2e94229", + "hex", + ), + supportUrl: "https://ledgerhelp.swapkit.dev/", + termsOfUseUrl: "https://docs.thorswap.finance/thorswap/resources/terms-of-service", + type: "CEX", + version: 2, + }, + wyre: { + name: "Wyre", + publicKey: { + curve: "secp256k1", + data: Buffer.from( + "04ad01a6241929a5ec331046868fbacb424696fd7c8a4d824fee61268374e9f4f87ffc5301f0e0a84cea69ffed46e14c771f9ca1eea345f6531994291c816e8ae6", + "hex", + ), + }, + signature: Buffer.from( + "304402207b49e46d458a55daee9bc8ed96e1b404c2d99dbbc3d3c3c15430026eb7e01a05022011ab86db08a4c956874a83f23d918319a073fdd9df23a1c7eed8a0a22c98b1e3", + "hex", + ), + version: 1, + }, + }); + }); +}); diff --git a/libs/ledger-live-common/src/exchange/providers/swap.ts b/libs/ledger-live-common/src/exchange/providers/swap.ts index bb865a6c50f0..db6fe60b41e0 100644 --- a/libs/ledger-live-common/src/exchange/providers/swap.ts +++ b/libs/ledger-live-common/src/exchange/providers/swap.ts @@ -1,8 +1,7 @@ import { getEnv } from "@ledgerhq/live-env"; -import { ExchangeProviderNameAndSignature, getTestProviderInfo } from "."; +import { getTestProviderInfo, type ExchangeProviderNameAndSignature } from "."; +import { findCurrencyData, getProvidersCDNData, getProvidersData } from "../../cal"; import { isIntegrationTestEnv } from "../swap/utils/isIntegrationTestEnv"; -import { getProvidersData } from "./getProvidersData"; -import network from "@ledgerhq/live-network"; export type SwapProviderConfig = { needsKYC: boolean; @@ -210,12 +209,6 @@ const DEFAULT_SWAP_PROVIDERS: Record => { - const { data: currencyData } = await network({ - method: "GET", - url: "https://crypto-assets-service.api.ledger.com/v1/currencies", - params: { - output: "id,exchange_app_config_serialized,exchange_app_signature", - id: currencyId, - }, - }); - if (!currencyData.length) { - throw new Error(`Exchange, missing configuration for ${currencyId}`); - } - if (currencyData.length !== 1) { - throw new Error(`Exchange, multiple configurations found for ${currencyId}`); - } - return { - id: currencyData[0].id, - config: currencyData[0].exchange_app_config_serialized, - signature: currencyData[0].exchange_app_signature, - } as CurrencyData; -}; - -export const getProvidersCDNData = async () => { - const providersData = await network({ - url: "https://cdn.live.ledger.com/swap-providers/data.json", - }); - return providersData.data; -}; +export const findExchangeCurrencyData = async (currencyId: string): Promise => + findCurrencyData(currencyId); export const fetchAndMergeProviderData = async () => { if (providerDataCache) { diff --git a/libs/ledger-live-common/src/exchange/testCurrencyConfig/data/coins.ts b/libs/ledger-live-common/src/exchange/testCurrencyConfig/data/coins.ts deleted file mode 100644 index cf1cc04ab375..000000000000 --- a/libs/ledger-live-common/src/exchange/testCurrencyConfig/data/coins.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * FIXME: - * Minimal config for now, need to extend it to match prod config - * cf. https://github.com/LedgerHQ/ledgerjs/tree/master/packages/cryptoassets/src/data/exchange - */ - -const coins = [ - [ - "bitcoin", - "0342544307426974636f696e00", - "3045022100cb174382302219dca359c0a4d457b2569e31a06b2c25c0088a2bd3fd6c04386a02202c6d0a5b924a414621067e316f021aa13aa5b2eee2bf36ea3cfddebc053b201b", - ], - [ - "ethereum", - "0345544808457468657265756d050345544812", - "3044022065d7931ab3144362d57e3fdcc5de921fb65024737d917f0ab1f8b173d1ed3c2e022027493568d112dc53c7177f8e5fc915d91a903780a067badf109085a73d360323", - ], - [ - "bitcoin_testnet", - Buffer.from([ - 0x3, 0x42, 0x54, 0x43, 0xc, 0x42, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x20, 0x54, 0x65, 0x73, - 0x74, 0x0, - ]).toString("hex"), - Buffer.from([ - 0x30, 0x45, 0x2, 0x21, 0x0, 0xa5, 0x37, 0xd3, 0x11, 0x8a, 0x9c, 0xd8, 0x94, 0x74, 0x4a, 0x4f, - 0xd5, 0x5c, 0xb7, 0x3d, 0x4f, 0xb2, 0x60, 0xfc, 0xf4, 0x6d, 0x8f, 0xc8, 0xed, 0x2a, 0xe6, - 0x5e, 0x6c, 0x68, 0x44, 0x55, 0xab, 0x2, 0x20, 0x39, 0x2a, 0xf5, 0x7f, 0xbc, 0x57, 0x7d, 0xf5, - 0xd1, 0xfe, 0x4d, 0x7d, 0x57, 0xee, 0xea, 0x76, 0x82, 0x44, 0xf2, 0xa5, 0x76, 0x7d, 0xd9, - 0x82, 0x90, 0x4, 0xfe, 0x6f, 0x1d, 0x0, 0x3a, 0x58, - ]).toString("hex"), - ], - [ - "ton", - "03544f4e03544f4e00", - "3045022100fe1b854d275796f89a7e24ef118767f0660b1883a27834e126ddd22ebb7fccc3022010806932659ea8bf2f46d3d9bac5fb288c2ba7e36ca690f2961a19f7b7f66a43", - ], -]; - -export default coins; diff --git a/libs/ledger-live-common/src/exchange/testCurrencyConfig/data/erc20.ts b/libs/ledger-live-common/src/exchange/testCurrencyConfig/data/erc20.ts deleted file mode 100644 index 2abcd344e527..000000000000 --- a/libs/ledger-live-common/src/exchange/testCurrencyConfig/data/erc20.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * FIXME: - * Minimal config for now, need to extend it to match prod config - * cf. https://github.com/LedgerHQ/ledgerjs/tree/master/packages/cryptoassets/data/exchange - */ - -const erc20 = [ - [ - "ethereum/erc20/usd_tether__erc20_", - "045553445408457468657265756d06045553445406", - "304402200b3a844543666bbdc91642e1d070afa60845106d77bfe2bd9b5b9d165ded6ab502200feab018278366a825e62f424a68aecedd4188a9d2493e2170e6dfd6e7113db4", - ], -]; - -export default erc20; diff --git a/libs/ledger-live-common/src/exchange/testCurrencyConfig/index.ts b/libs/ledger-live-common/src/exchange/testCurrencyConfig/index.ts deleted file mode 100644 index 5b892fc1bf61..000000000000 --- a/libs/ledger-live-common/src/exchange/testCurrencyConfig/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import erc20 from "./data/erc20"; -import coins from "./data/coins"; - -/** - * coin and token configs are extracted from the app-exchange repo and differ from the - * prod ones only on the signature (third index of each config) because the test configs are signed - * using the Ledger test private key (available in the app-exchange repo) - * https://github.com/LedgerHQ/app-exchange/blob/61da20e3a1b70742e91669221f7ee2db237f3545/test/tools/index.js#L82-L106 - */ - -const all = [...coins, ...erc20]; -const configs = {}; - -for (const [id, config, signature] of all) { - configs[id] = { - config, - signature, - }; -} - -/** - * - */ -export const findTestExchangeCurrencyConfig = ( - id: string, -): - | { - config: string; - - signature: string; - } - | null - | undefined => configs[id]; diff --git a/libs/ledgerjs/packages/hw-app-exchange/README.md b/libs/ledgerjs/packages/hw-app-exchange/README.md index df77229ace56..9638f0bda091 100644 --- a/libs/ledgerjs/packages/hw-app-exchange/README.md +++ b/libs/ledgerjs/packages/hw-app-exchange/README.md @@ -54,9 +54,16 @@ The rest of the documentation is about testing on NanoSP. ```sh docker run --privileged -v ':/app' -t -d --name ledger-app-dev-tools ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools -docker exec -it ledger-app-dev-tools bash -c 'export BOLOS_SDK="$NANOSP_SDK" && make -j DEBUG=1 TESTING=1 TEST_PUBLIC_KEY=1' +docker exec -it ledger-app-dev-tools bash -c 'export BOLOS_SDK="$" && make -j DEBUG=1 TESTING=1 TEST_PUBLIC_KEY=1' ``` +With `` possible value: +* NanoS: `NANOS_SDK` +* NanoS+: `NANOSP_SDK` +* NanoX: `NANOX_SDK` +* Stax: `STAX_SDK` +* Flex: `FLEX_SDK` + It is important to set the value of `DEBUG` to 1, as we need the fake Ledger private key in our test. ### 3. Launch speculos diff --git a/libs/ledgerjs/packages/hw-app-exchange/src/Exchange.integ.test.ts b/libs/ledgerjs/packages/hw-app-exchange/src/Exchange.integ.test.ts index 7001b8cced06..df1290f5cc17 100644 --- a/libs/ledgerjs/packages/hw-app-exchange/src/Exchange.integ.test.ts +++ b/libs/ledgerjs/packages/hw-app-exchange/src/Exchange.integ.test.ts @@ -7,7 +7,7 @@ import secp256k1 from "secp256k1"; import protobuf from "protobufjs"; import BigNumber from "bignumber.js"; -describe("Check SWAP until payload signature", () => { +describe("Check Exchange until payload signature", () => { let transport: Transport; beforeAll(async () => { @@ -17,288 +17,296 @@ describe("Check SWAP until payload signature", () => { transport.close(); }); - it("Legacy SWAP", async () => { - // Given - const exchange = new Exchange(transport, ExchangeTypes.Swap); - - // When - const transactionId = await exchange.startNewTransaction(); - - // Then - expect(transactionId).toEqual(expect.any(String)); - expect(transactionId).toHaveLength(10); - - const { partnerInfo, partnerSigned, partnerPrivKey } = - await appExchangeDatasetTest(legacySignFormat); - await exchange.setPartnerKey(partnerInfo); - - await exchange.checkPartner(partnerSigned); - - const amount = new BigNumber(100000); - const amountToWallet = new BigNumber(1000); - const encodedPayload = await generateSwapPayloadProtobuf({ - payinAddress: "0xd692Cb1346262F584D17B4B470954501f6715a82", - refundAddress: "0xDad77910DbDFdE764fC21FCD4E74D71bBACA6D8D", - payoutAddress: "bc1qer57ma0fzhqys2cmydhuj9cprf9eg0nw922a8j", - currencyFrom: "ETH", - currencyTo: "BTC", - amountToProvider: Buffer.from(amount.toString(16), "hex"), - amountToWallet: Buffer.from(amountToWallet.toString(16), "hex"), - deviceTransactionId: transactionId, - }); - const estimatedFees = new BigNumber(0); - await exchange.processTransaction(encodedPayload, estimatedFees); + describe("Check SWAP", () => { + it("Legacy SWAP", async () => { + // Given + const exchange = new Exchange(transport, ExchangeTypes.Swap); - const payloadSignature = await signMessage(encodedPayload, partnerPrivKey, "der"); - await exchange.checkTransactionSignature(payloadSignature); - }); + // When + const transactionId = await exchange.startNewTransaction(); - it("NG SWAP", async () => { - // Given - const exchange = new Exchange(transport, ExchangeTypes.SwapNg); - - // When - const transactionId = await exchange.startNewTransaction(); - - // Then - expect(transactionId).toEqual(expect.any(String)); - expect(transactionId).toHaveLength(64); - - const { partnerInfo, partnerSigned, partnerPrivKey } = - await appExchangeDatasetTest(ngSignFormat); - await exchange.setPartnerKey(partnerInfo); - - await exchange.checkPartner(partnerSigned); - - const amount = new BigNumber(100_000); - const amountToWallet = new BigNumber(100_000_000_000); - let encodedPayload = await generateSwapPayloadProtobuf({ - payinAddress: "0xd692Cb1346262F584D17B4B470954501f6715a82", - refundAddress: "0xDad77910DbDFdE764fC21FCD4E74D71bBACA6D8D", - payoutAddress: "bc1qer57ma0fzhqys2cmydhuj9cprf9eg0nw922a8j", - currencyFrom: "ETH", - currencyTo: "BTC", - amountToProvider: Buffer.from(amount.toString(16), "hex"), - amountToWallet: Buffer.from(amountToWallet.toString(16), "hex"), - deviceTransactionIdNg: Buffer.from(transactionId.padStart(32, "0"), "hex"), + // Then + expect(transactionId).toEqual(expect.any(String)); + expect(transactionId).toHaveLength(10); + + const { partnerInfo, partnerSigned, partnerPrivKey } = + await appExchangeDatasetTest(legacySignFormat); + await exchange.setPartnerKey(partnerInfo); + + await exchange.checkPartner(partnerSigned); + + const amount = new BigNumber(100000); + const amountToWallet = new BigNumber(1000); + const encodedPayload = await generateSwapPayloadProtobuf({ + payinAddress: "0xd692Cb1346262F584D17B4B470954501f6715a82", + refundAddress: "0xDad77910DbDFdE764fC21FCD4E74D71bBACA6D8D", + payoutAddress: "bc1qer57ma0fzhqys2cmydhuj9cprf9eg0nw922a8j", + currencyFrom: "ETH", + currencyTo: "BTC", + amountToProvider: Buffer.from(amount.toString(16), "hex"), + amountToWallet: Buffer.from(amountToWallet.toString(16), "hex"), + deviceTransactionId: transactionId, + }); + const estimatedFees = new BigNumber(0); + await exchange.processTransaction(encodedPayload, estimatedFees); + + const payloadSignature = await signMessage(encodedPayload, partnerPrivKey, "der"); + await exchange.checkTransactionSignature(payloadSignature); }); - encodedPayload = convertToJWSPayload(encodedPayload); - const estimatedFees = new BigNumber(0); - await exchange.processTransaction(encodedPayload, estimatedFees, "jws"); + it("NG SWAP", async () => { + // Given + const exchange = new Exchange(transport, ExchangeTypes.SwapNg); + + // When + const transactionId = await exchange.startNewTransaction(); - const payloadSignature = await signMessage(encodedPayload, partnerPrivKey, "rs"); - await exchange.checkTransactionSignature(payloadSignature); - }); + // Then + expect(transactionId).toEqual(expect.any(String)); + expect(transactionId).toHaveLength(64); + + const { partnerInfo, partnerSigned, partnerPrivKey } = + await appExchangeDatasetTest(ngSignFormat); + await exchange.setPartnerKey(partnerInfo); + + await exchange.checkPartner(partnerSigned); + + const amount = new BigNumber(100_000); + const amountToWallet = new BigNumber(100_000_000_000); + let encodedPayload = await generateSwapPayloadProtobuf({ + payinAddress: "0xd692Cb1346262F584D17B4B470954501f6715a82", + refundAddress: "0xDad77910DbDFdE764fC21FCD4E74D71bBACA6D8D", + payoutAddress: "bc1qer57ma0fzhqys2cmydhuj9cprf9eg0nw922a8j", + currencyFrom: "ETH", + currencyTo: "BTC", + amountToProvider: Buffer.from(amount.toString(16), "hex"), + amountToWallet: Buffer.from(amountToWallet.toString(16), "hex"), + deviceTransactionIdNg: Buffer.from(transactionId.padStart(32, "0"), "hex"), + }); + encodedPayload = convertToJWSPayload(encodedPayload); + + const estimatedFees = new BigNumber(0); + await exchange.processTransaction(encodedPayload, estimatedFees, "jws"); - it("NG SWAP with more than 255 bytes in process transaction", async () => { - // Given - const exchange = new Exchange(transport, ExchangeTypes.SwapNg); - - // When - const transactionId = await exchange.startNewTransaction(); - - // Then - expect(transactionId).toEqual(expect.any(String)); - expect(transactionId).toHaveLength(64); - - const { partnerInfo, partnerSigned, partnerPrivKey } = - await appExchangeDatasetTest(ngSignFormat); - await exchange.setPartnerKey(partnerInfo); - - await exchange.checkPartner(partnerSigned); - - const amount = new BigNumber(100_000); - const amountToWallet = new BigNumber(100_000_000_000); - // Extra properties have a limited size of 20 (i.e. app-exchange/src/proto/protocol.options) - let encodedPayload = await generateSwapPayloadProtobuf({ - payinAddress: "0xd692Cb1346262F584D17B4B470954501f6715a82", - payinExtraId: '{ extraInfo: "Go" }', - refundAddress: "0xDad77910DbDFdE764fC21FCD4E74D71bBACA6D8D", - refundExtraId: '{ extraInfo: "Go" }', - payoutAddress: "bc1qer57ma0fzhqys2cmydhuj9cprf9eg0nw922a8j", - payoutExtraId: "bc1qer57ma0fzhqys2c", - currencyFrom: "ETH", - currencyTo: "BTC", - amountToProvider: Buffer.from(amount.toString(16), "hex"), - amountToWallet: Buffer.from(amountToWallet.toString(16), "hex"), - deviceTransactionIdNg: Buffer.from(transactionId.padStart(32, "0"), "hex"), + const payloadSignature = await signMessage(encodedPayload, partnerPrivKey, "rs"); + await exchange.checkTransactionSignature(payloadSignature); }); - encodedPayload = convertToJWSPayload(encodedPayload); - const estimatedFees = new BigNumber(0); - await exchange.processTransaction(encodedPayload, estimatedFees, "jws"); - console.log("DEBUG - SWAP partner encoded payload:", encodedPayload.toString("hex")); + it("NG SWAP with more than 255 bytes in process transaction", async () => { + // Given + const exchange = new Exchange(transport, ExchangeTypes.SwapNg); - const payloadSignature = await signMessage(encodedPayload, partnerPrivKey, "rs"); - console.log( - "DEBUG - SWAP partner payload signature:", - Buffer.from(payloadSignature).toString("hex"), - ); - await exchange.checkTransactionSignature(payloadSignature); - }); + // When + const transactionId = await exchange.startNewTransaction(); - it("NG SWAP with prepared data", async () => { - // Given - const exchange = new Exchange(transport, ExchangeTypes.SwapNg); + // Then + expect(transactionId).toEqual(expect.any(String)); + expect(transactionId).toHaveLength(64); + + const { partnerInfo, partnerSigned, partnerPrivKey } = + await appExchangeDatasetTest(ngSignFormat); + await exchange.setPartnerKey(partnerInfo); + + await exchange.checkPartner(partnerSigned); + + const amount = new BigNumber(100_000); + const amountToWallet = new BigNumber(100_000_000_000); + // Extra properties have a limited size of 20 (i.e. app-exchange/src/proto/protocol.options) + let encodedPayload = await generateSwapPayloadProtobuf({ + payinAddress: "0xd692Cb1346262F584D17B4B470954501f6715a82", + payinExtraId: '{ extraInfo: "Go" }', + refundAddress: "0xDad77910DbDFdE764fC21FCD4E74D71bBACA6D8D", + refundExtraId: '{ extraInfo: "Go" }', + payoutAddress: "bc1qer57ma0fzhqys2cmydhuj9cprf9eg0nw922a8j", + payoutExtraId: "bc1qer57ma0fzhqys2c", + currencyFrom: "ETH", + currencyTo: "BTC", + amountToProvider: Buffer.from(amount.toString(16), "hex"), + amountToWallet: Buffer.from(amountToWallet.toString(16), "hex"), + deviceTransactionIdNg: Buffer.from(transactionId.padStart(32, "0"), "hex"), + }); + encodedPayload = convertToJWSPayload(encodedPayload); - // When - const transactionId = await exchange.startNewTransaction(); + const estimatedFees = new BigNumber(0); + await exchange.processTransaction(encodedPayload, estimatedFees, "jws"); + console.log("DEBUG - SWAP partner encoded payload:", encodedPayload.toString("hex")); - // Then - expect(transactionId).toEqual(expect.any(String)); - expect(transactionId).toHaveLength(64); + const payloadSignature = await signMessage(encodedPayload, partnerPrivKey, "rs"); + console.log( + "DEBUG - SWAP partner payload signature:", + Buffer.from(payloadSignature).toString("hex"), + ); + await exchange.checkTransactionSignature(payloadSignature); + }); - const { partnerInfo, partnerSigned, apdu } = await appExchangeDataset(ngSignFormat); - await exchange.setPartnerKey(partnerInfo); + it("NG SWAP with prepared data", async () => { + // Given + const exchange = new Exchange(transport, ExchangeTypes.SwapNg); - console.log("DEBUG - Swap partner APDU:", apdu.toString("hex")); - console.log("DEBUG - Swap partner signed:", Buffer.from(partnerSigned).toString("hex")); + // When + const transactionId = await exchange.startNewTransaction(); - await exchange.checkPartner(partnerSigned); + // Then + expect(transactionId).toEqual(expect.any(String)); + expect(transactionId).toHaveLength(64); - const encodedPayload = Buffer.from( - ".CipiYzFxYXIwc3Jycjd4Zmt2eTVsNjQzbHlkbnc5cmU1OWd0enp3ZjVtZHEaKmJjMXFhcjBzcnJyN3hma3Z5NWw2NDNseWRudzlyZTU5Z3R6endmNHRlcSoqMHhiNzk0ZjVlYTBiYTM5NDk0Y2U4Mzk2MTNmZmZiYTc0Mjc5NTc5MjY4OgNCVENCA0JBVEoCBH5SBgV0-95gAGIgNQrqDJf3R_HQ92CBRhSkdSOAGxrrfQvLuqKk9Gv4GEs=", - ); + const { partnerInfo, partnerSigned, apdu } = await appExchangeDataset(ngSignFormat); + await exchange.setPartnerKey(partnerInfo); - const estimatedFees = new BigNumber(0); - await exchange.processTransaction(encodedPayload, estimatedFees, "jws"); - console.log("DEBUG - SWAP partner encoded payload:", encodedPayload.toString("hex")); + console.log("DEBUG - Swap partner APDU:", apdu.toString("hex")); + console.log("DEBUG - Swap partner signed:", Buffer.from(partnerSigned).toString("hex")); - // const payloadSignature = await signMessage(encodedPayload, partnerPrivKey, "rs"); - const payloadSignature = Buffer.from( - "zGcNUYKM8sLxvT7zPU1C8vrMmanVlUroELnAeil4weo1LCk0zUBRse5-3Acv7I7II90xVTIxm26BnxRbZvVmTQ==", - "base64url", - ); - console.log( - "DEBUG - SWAP partner payload signature:", - Buffer.from(payloadSignature).toString("hex"), - ); - await exchange.checkTransactionSignature(payloadSignature); - }); + await exchange.checkPartner(partnerSigned); + + const encodedPayload = Buffer.from( + ".CipiYzFxYXIwc3Jycjd4Zmt2eTVsNjQzbHlkbnc5cmU1OWd0enp3ZjVtZHEaKmJjMXFhcjBzcnJyN3hma3Z5NWw2NDNseWRudzlyZTU5Z3R6endmNHRlcSoqMHhiNzk0ZjVlYTBiYTM5NDk0Y2U4Mzk2MTNmZmZiYTc0Mjc5NTc5MjY4OgNCVENCA0JBVEoCBH5SBgV0-95gAGIgNQrqDJf3R_HQ92CBRhSkdSOAGxrrfQvLuqKk9Gv4GEs=", + ); - it("NG SWAP with TON", async () => { - // Given - const exchange = new Exchange(transport, ExchangeTypes.SwapNg); - - // When - const transactionId = await exchange.startNewTransaction(); - - // Then - expect(transactionId).toEqual(expect.any(String)); - expect(transactionId).toHaveLength(64); - - const { partnerInfo, partnerSigned, apdu, partnerPrivKey } = - await appExchangeDatasetTest(ngSignFormat); - await exchange.setPartnerKey(partnerInfo); - - console.log("DEBUG - Swap partner APDU:", apdu.toString("hex")); - console.log("DEBUG - Swap partner signed:", Buffer.from(partnerSigned).toString("hex")); - - await exchange.checkPartner(partnerSigned); - - const amount = new BigNumber(100); - const amountToWallet = new BigNumber(1_000); - // Extra properties have a limited size of 20 (i.e. app-exchange/src/proto/protocol.options) - let encodedPayload = await generateSwapPayloadProtobuf({ - payinAddress: "UQAbvs2tCnsTWxCZX7JW-dqlk0vM8x_m8aJqF4wwRWGtTEZD", - refundAddress: "UQAbvs2tCnsTWxCZX7JW-dqlk0vM8x_m8aJqF4wwRWGtTEZD", - payoutAddress: "0x66c4371aE8FFeD2ec1c2EBbbcCfb7E494181E1E3", - // payinAddress: "UQCa_2bcwBt5eH9gOMgHC497nyfjgSl8hGpZ90O7B17WHA==", - // refundAddress: "UQCa_2bcwBt5eH9gOMgHC497nyfjgSl8hGpZ90O7B17WHA==", - // payoutAddress: "0xDad77910DbDFdE764fC21FCD4E74D71bBACA6D8D", - currencyFrom: "TON", - currencyTo: "ETH", - amountToProvider: Buffer.from(amount.toString(16), "hex"), - amountToWallet: Buffer.from(amountToWallet.toString(16), "hex"), - deviceTransactionIdNg: Buffer.from(transactionId.padStart(32, "0"), "hex"), + const estimatedFees = new BigNumber(0); + await exchange.processTransaction(encodedPayload, estimatedFees, "jws"); + console.log("DEBUG - SWAP partner encoded payload:", encodedPayload.toString("hex")); + + // const payloadSignature = await signMessage(encodedPayload, partnerPrivKey, "rs"); + const payloadSignature = Buffer.from( + "zGcNUYKM8sLxvT7zPU1C8vrMmanVlUroELnAeil4weo1LCk0zUBRse5-3Acv7I7II90xVTIxm26BnxRbZvVmTQ==", + "base64url", + ); + console.log( + "DEBUG - SWAP partner payload signature:", + Buffer.from(payloadSignature).toString("hex"), + ); + await exchange.checkTransactionSignature(payloadSignature); }); - encodedPayload = convertToJWSPayload(encodedPayload); - const estimatedFees = new BigNumber(0); - await exchange.processTransaction(encodedPayload, estimatedFees, "jws"); - console.log("DEBUG - SWAP partner encoded payload:", encodedPayload.toString("hex")); + it("NG SWAP with TON", async () => { + // Given + const exchange = new Exchange(transport, ExchangeTypes.SwapNg); - const payloadSignature = await signMessage(encodedPayload, partnerPrivKey, "rs"); - console.log( - "DEBUG - SWAP partner payload signature:", - Buffer.from(payloadSignature).toString("hex"), - ); - await exchange.checkTransactionSignature(payloadSignature); + // When + const transactionId = await exchange.startNewTransaction(); - const configEth = { - config: Buffer.from("0345544808457468657265756d050345544812", "hex"), - signature: Buffer.from( - "3044022065d7931ab3144362d57e3fdcc5de921fb65024737d917f0ab1f8b173d1ed3c2e022027493568d112dc53c7177f8e5fc915d91a903780a067badf109085a73d360323", - "hex", - ), - }; - let addressParameters = bip32asBuffer("44'/60'/0'/0/0"); - await exchange.validatePayoutOrAsset(configEth.config, configEth.signature, addressParameters); - - const delay = (milliseconds: number) => { - return new Promise(resolve => { - setTimeout(resolve, milliseconds); + // Then + expect(transactionId).toEqual(expect.any(String)); + expect(transactionId).toHaveLength(64); + + const { partnerInfo, partnerSigned, apdu, partnerPrivKey } = + await appExchangeDatasetTest(ngSignFormat); + await exchange.setPartnerKey(partnerInfo); + + console.log("DEBUG - Swap partner APDU:", apdu.toString("hex")); + console.log("DEBUG - Swap partner signed:", Buffer.from(partnerSigned).toString("hex")); + + await exchange.checkPartner(partnerSigned); + + const amount = new BigNumber(100); + const amountToWallet = new BigNumber(1_000); + // Extra properties have a limited size of 20 (i.e. app-exchange/src/proto/protocol.options) + let encodedPayload = await generateSwapPayloadProtobuf({ + payinAddress: "UQAbvs2tCnsTWxCZX7JW-dqlk0vM8x_m8aJqF4wwRWGtTEZD", + refundAddress: "UQAbvs2tCnsTWxCZX7JW-dqlk0vM8x_m8aJqF4wwRWGtTEZD", + payoutAddress: "0x66c4371aE8FFeD2ec1c2EBbbcCfb7E494181E1E3", + // payinAddress: "UQCa_2bcwBt5eH9gOMgHC497nyfjgSl8hGpZ90O7B17WHA==", + // refundAddress: "UQCa_2bcwBt5eH9gOMgHC497nyfjgSl8hGpZ90O7B17WHA==", + // payoutAddress: "0xDad77910DbDFdE764fC21FCD4E74D71bBACA6D8D", + currencyFrom: "TON", + currencyTo: "ETH", + amountToProvider: Buffer.from(amount.toString(16), "hex"), + amountToWallet: Buffer.from(amountToWallet.toString(16), "hex"), + deviceTransactionIdNg: Buffer.from(transactionId.padStart(32, "0"), "hex"), }); - }; - await delay(500); + encodedPayload = convertToJWSPayload(encodedPayload); - const configTon = { - config: Buffer.from("03544f4e03544f4e00", "hex"), - signature: Buffer.from( - "3045022100b35be5d1ad0d71572b5f3d72b40766521d5492fad6ed54289a64488bec3344a902205b522b7b8c7c800826bcd0bda092e84db5d1c23f6061c8b57c8efb3641d243a7", - "hex", - ), - }; - addressParameters = bip32asBuffer("44'/607'/0'/0'/0'/0'"); - await exchange.checkRefundAddress(configTon.config, configTon.signature, addressParameters); - }); + const estimatedFees = new BigNumber(0); + await exchange.processTransaction(encodedPayload, estimatedFees, "jws"); + console.log("DEBUG - SWAP partner encoded payload:", encodedPayload.toString("hex")); + + const payloadSignature = await signMessage(encodedPayload, partnerPrivKey, "rs"); + console.log( + "DEBUG - SWAP partner payload signature:", + Buffer.from(payloadSignature).toString("hex"), + ); + await exchange.checkTransactionSignature(payloadSignature); + + const configEth = { + config: Buffer.from("0345544808457468657265756d050345544812", "hex"), + signature: Buffer.from( + "3044022065d7931ab3144362d57e3fdcc5de921fb65024737d917f0ab1f8b173d1ed3c2e022027493568d112dc53c7177f8e5fc915d91a903780a067badf109085a73d360323", + "hex", + ), + }; + let addressParameters = bip32asBuffer("44'/60'/0'/0/0"); + await exchange.validatePayoutOrAsset( + configEth.config, + configEth.signature, + addressParameters, + ); - it("NG Sell", async () => { - // Given - const exchange = new Exchange(transport, ExchangeTypes.SellNg); - - // When - const transactionId = await exchange.startNewTransaction(); - - // Then - expect(transactionId).toEqual(expect.any(String)); - expect(transactionId).toHaveLength(64); - - const { partnerInfo, partnerSigned, partnerPrivKey, apdu } = - await appExchangeSellDataset(ngSignFormat); - await exchange.setPartnerKey(partnerInfo); - console.log("DEBUG - Sell partner pubkey:", partnerInfo.publicKey.toString("hex")); - console.log("DEBUG - Sell partner APDU:", apdu.toString("hex")); - console.log("DEBUG - Sell partner signed:", Buffer.from(partnerSigned).toString("hex")); - - await exchange.checkPartner(partnerSigned); - - const amount = new BigNumber(100_000); - let encodedPayload = await generateSellPayloadProtobuf({ - traderEmail: "test@ledger.fr", - inCurrency: "ETH", - inAmount: Buffer.from(amount.toString(16), "hex"), - inAddress: "0xd692Cb1346262F584D17B4B470954501f6715a82", - outCurrency: "EUR", - outAmount: { - coefficient: Buffer.from("1", "hex"), - exponent: 1, - }, - deviceTransactionId: Buffer.from(transactionId.padStart(32, "0"), "hex"), + const delay = (milliseconds: number) => { + return new Promise(resolve => { + setTimeout(resolve, milliseconds); + }); + }; + await delay(500); + + const configTon = { + config: Buffer.from("03544f4e03544f4e00", "hex"), + signature: Buffer.from( + "3045022100b35be5d1ad0d71572b5f3d72b40766521d5492fad6ed54289a64488bec3344a902205b522b7b8c7c800826bcd0bda092e84db5d1c23f6061c8b57c8efb3641d243a7", + "hex", + ), + }; + addressParameters = bip32asBuffer("44'/607'/0'/0'/0'/0'"); + await exchange.checkRefundAddress(configTon.config, configTon.signature, addressParameters); }); - encodedPayload = convertToJWSPayload(encodedPayload); + }); - const estimatedFees = new BigNumber(0); - await exchange.processTransaction(encodedPayload, estimatedFees, "jws"); - console.log("DEBUG - SELL partner encoded payload:", encodedPayload.toString("hex")); + describe("Check SELL", () => { + it("NG Sell", async () => { + // Given + const exchange = new Exchange(transport, ExchangeTypes.SellNg); - const payloadSignature = await signMessage(encodedPayload, partnerPrivKey, "rs"); - console.log( - "DEBUG - SELL partner payload signature:", - Buffer.from(payloadSignature).toString("hex"), - ); - await exchange.checkTransactionSignature(payloadSignature); + // When + const transactionId = await exchange.startNewTransaction(); + + // Then + expect(transactionId).toEqual(expect.any(String)); + expect(transactionId).toHaveLength(64); + + const { partnerInfo, partnerSigned, partnerPrivKey, apdu } = + await appExchangeSellDataset(ngSignFormat); + await exchange.setPartnerKey(partnerInfo); + console.log("DEBUG - Sell partner pubkey:", partnerInfo.publicKey.toString("hex")); + console.log("DEBUG - Sell partner APDU:", apdu.toString("hex")); + console.log("DEBUG - Sell partner signed:", Buffer.from(partnerSigned).toString("hex")); + + await exchange.checkPartner(partnerSigned); + + const amount = new BigNumber(100_000); + let encodedPayload = await generateSellPayloadProtobuf({ + traderEmail: "test@ledger.fr", + inCurrency: "ETH", + inAmount: Buffer.from(amount.toString(16), "hex"), + inAddress: "0xd692Cb1346262F584D17B4B470954501f6715a82", + outCurrency: "EUR", + outAmount: { + coefficient: Buffer.from("1", "hex"), + exponent: 1, + }, + deviceTransactionId: Buffer.from(transactionId.padStart(32, "0"), "hex"), + }); + encodedPayload = convertToJWSPayload(encodedPayload); + + const estimatedFees = new BigNumber(0); + await exchange.processTransaction(encodedPayload, estimatedFees, "jws"); + console.log("DEBUG - SELL partner encoded payload:", encodedPayload.toString("hex")); + + const payloadSignature = await signMessage(encodedPayload, partnerPrivKey, "rs"); + console.log( + "DEBUG - SELL partner payload signature:", + Buffer.from(payloadSignature).toString("hex"), + ); + await exchange.checkTransactionSignature(payloadSignature); + }); }); }); @@ -336,7 +344,6 @@ describe("Test internal sign and verification functionality", () => { "30440220471b035b40dafa095d615998c82202b2bd00fb45670b828f1dda3b68e5b24cc3022022a1c64d02b8c14e1e4cc2d05b00234642c11db3d4461ff5366f5af337cf0ced", "hex", ); - console.log("DEBUG - Test internal: message signature", Buffer.from(sig).toString("hex")); // Then const hashBuffer = await subtle.digest("SHA-256", Buffer.from(msg, "hex")); From 0fbb743800fa3ae5c15346083cbaf6c250ca2826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Prohaszka?= Date: Tue, 29 Oct 2024 17:16:40 +0100 Subject: [PATCH 2/2] fix: doc issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Prohaszka --- libs/ledgerjs/packages/hw-app-exchange/README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libs/ledgerjs/packages/hw-app-exchange/README.md b/libs/ledgerjs/packages/hw-app-exchange/README.md index 9638f0bda091..3c40b10298ef 100644 --- a/libs/ledgerjs/packages/hw-app-exchange/README.md +++ b/libs/ledgerjs/packages/hw-app-exchange/README.md @@ -58,11 +58,12 @@ docker exec -it ledger-app-dev-tools bash -c 'export BOLOS_SDK="$" && ``` With `` possible value: -* NanoS: `NANOS_SDK` -* NanoS+: `NANOSP_SDK` -* NanoX: `NANOX_SDK` -* Stax: `STAX_SDK` -* Flex: `FLEX_SDK` + +* NanoS: `NANOS_SDK` +* NanoS+: `NANOSP_SDK` +* NanoX: `NANOX_SDK` +* Stax: `STAX_SDK` +* Flex: `FLEX_SDK` It is important to set the value of `DEBUG` to 1, as we need the fake Ledger private key in our test.