Skip to content
This repository was archived by the owner on Jul 15, 2022. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
"invariant": "^2.2.2",
"isomorphic-ws": "^4.0.1",
"json-rpc-2.0": "^0.2.19",
"jwt-decode": "^3.1.2",
"leb128": "^0.0.5",
"lodash": "^4.17.21",
"lru-cache": "5.1.1",
Expand Down
14 changes: 12 additions & 2 deletions src/env.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { $ElementType } from "utility-types";
import mapValues from "lodash/mapValues";
// set and get environment & config variables
import { Subject } from "rxjs";
import mapValues from "lodash/mapValues";
import { $ElementType } from "utility-types";
type EnvDef<V> = {
desc: string;
def: V;
Expand Down Expand Up @@ -375,6 +375,16 @@ const envDefinitions = {
parser: stringParser,
desc: "mock the server response for the exchange KYC check, options are 'open', 'pending', 'closed' or 'approved'.",
},
MOCK_SWAP_CHECK_QUOTE: {
def: "RATE_VALID",
parser: stringParser,
desc: "mock the server response for the exchange check quote, options are 'RATE_VALID', 'KYC_FAILED', 'KYC_PENDING', 'KYC_UNDEFINED', 'KYC_UPGRADE_REQUIRED', 'MFA_REQUIRED', 'OVER_TRADE_LIMIT', 'UNKNOW_USER' or 'UNKNOWN_ERROR'.",
},
MOCK_SWAP_WIDGET_BASE_URL: {
def: "",
parser: stringParser,
desc: "mock the FTX swap widget base url",
},
/**
* Note: the mocked cryptoassets config and test partner are signed with the
* Ledger test private key
Expand Down
7 changes: 7 additions & 0 deletions src/exchange/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ export type ExchangeProviderNameAndSignature = {
nameAndPubkey: Buffer;
signature: Buffer;
};

export type SwapProviderConfig = ExchangeProviderNameAndSignature & {
curve: string;
needsKYC: boolean;
needsBearerToken: boolean;
};

export const isExchangeSupportedByApp = (
appName: string,
appVersion: string
Expand Down
39 changes: 39 additions & 0 deletions src/exchange/swap/checkQuote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { CheckQuote } from "./types";

import { getEnv } from "../../env";
import { mockCheckQuote } from "./mock";
import { getSwapAPIError, getSwapAPIBaseURL } from "./";
import network from "../../network";

const checkQuote: CheckQuote = async ({ provider, quoteId, bearerToken }) => {
if (getEnv("MOCK")) {
return mockCheckQuote({ provider, quoteId, bearerToken });
}

const request = {
provider,
rateId: quoteId,
};

const { data } = await network({
method: "POST",
url: `${getSwapAPIBaseURL()}/rate/check`,
headers: {
Authorization: `Bearer ${bearerToken}`,
},
data: request,
});

const error =
data.code === 300 ? getSwapAPIError(data.code, data.message) : undefined;
if (error) {
return {
provider,
error,
};
}

return data;
};

export default checkQuote;
20 changes: 9 additions & 11 deletions src/exchange/swap/completeExchange.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import { TransportStatusError, WrongDeviceForAccount } from "@ledgerhq/errors";
import { log } from "@ledgerhq/logs";
import { from, Observable } from "rxjs";
import secp256k1 from "secp256k1";
import { TransportStatusError, WrongDeviceForAccount } from "@ledgerhq/errors";

import { delay } from "../../promise";
import ExchangeTransport from "../hw-app-exchange/Exchange";
import perFamily from "../../generated/exchange";
import { getCurrencyExchangeConfig } from "../";
import { getAccountCurrency, getMainAccount } from "../../account";
import { getAccountBridge } from "../../bridge";
import { TransactionRefusedOnDevice } from "../../errors";
import perFamily from "../../generated/exchange";
import { withDevice } from "../../hw/deviceAccess";
import { getProviderNameAndSignature } from "./";
import { getCurrencyExchangeConfig } from "../";

import { delay } from "../../promise";
import ExchangeTransport from "../hw-app-exchange/Exchange";
import type {
CompleteExchangeInputSwap,
CompleteExchangeRequestEvent,
} from "../platform/types";
import { getProviderConfig } from "./";

const withDevicePromise = (deviceId, fn) =>
withDevice(deviceId)((transport) => from(fn(transport))).toPromise();
Expand Down Expand Up @@ -45,7 +43,7 @@ const completeExchange = (

const confirmExchange = async () => {
await withDevicePromise(deviceId, async (transport) => {
const providerNameAndSignature = getProviderNameAndSignature(provider);
const providerConfig = getProviderConfig(provider);
const exchange = new ExchangeTransport(
transport,
exchangeType,
Expand Down Expand Up @@ -75,10 +73,10 @@ const completeExchange = (
const errorsKeys = Object.keys(errors);
if (errorsKeys.length > 0) throw errors[errorsKeys[0]]; // throw the first error

await exchange.setPartnerKey(providerNameAndSignature.nameAndPubkey);
await exchange.setPartnerKey(providerConfig.nameAndPubkey);
if (unsubscribed) return;

await exchange.checkPartner(providerNameAndSignature.signature);
await exchange.checkPartner(providerConfig.signature);
if (unsubscribed) return;

await exchange.processTransaction(
Expand Down
73 changes: 45 additions & 28 deletions src/exchange/swap/index.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
import type { ExchangeProviderNameAndSignature } from "../";
import getExchangeRates from "./getExchangeRates";
import getStatus from "./getStatus";
import getProviders from "./getProviders";
import getCompleteSwapHistory from "./getCompleteSwapHistory";
import getKYCStatus from "./getKYCStatus";
import submitKYC from "./submitKYC";
import initSwap from "./initSwap";
import type { SwapProviderConfig } from "../";
import { getEnv } from "../../env";
import {
JSONRPCResponseError,
JSONDecodeError,
NoIPHeaderError,
CurrencyNotSupportedError,
CurrencyDisabledError,
AccessDeniedError,
CurrencyDisabledAsInputError,
CurrencyDisabledAsOutputError,
CurrencyDisabledError,
CurrencyNotSupportedByProviderError,
CurrencyNotSupportedError,
JSONDecodeError,
JSONRPCResponseError,
NoIPHeaderError,
NotImplementedError,
TradeMethodNotSupportedError,
UnexpectedError,
NotImplementedError,
ValidationError,
AccessDeniedError,
} from "../../errors";
import checkQuote from "./checkQuote";
import getCompleteSwapHistory from "./getCompleteSwapHistory";
import getExchangeRates from "./getExchangeRates";
import getKYCStatus from "./getKYCStatus";
import getProviders from "./getProviders";
import getStatus from "./getStatus";
import initSwap from "./initSwap";
import { postSwapAccepted, postSwapCancelled } from "./postSwapState";
import submitKYC from "./submitKYC";

export const operationStatusList = {
finishedOK: ["finished"],
Expand All @@ -31,15 +33,25 @@ export const operationStatusList = {

const getSwapAPIBaseURL: () => string = () => getEnv("SWAP_API_BASE");

const swapProviders: Record<
string,
{
nameAndPubkey: Buffer;
signature: Buffer;
curve: string;
needsKYC: boolean;
}
> = {
const ftx = {
nameAndPubkey: Buffer.concat([
Buffer.from([3]),
Buffer.from("FTX", "ascii"),
Buffer.from(
"04c89f3e48cde252f6cd6fcccc47c2f6ca6cf05f9f921703d31b7a7dddbf0bd6a690744662fe599f8761612021ba1fc0e8a5a4b7d5910c625b6dd09aa40762e5cd",
"hex"
),
]),
signature: Buffer.from(
"3044022029c0fb80d6e524f811f30cc04a349fa7f8896ce1ba84010da55f7be5eb9d528802202727985361cab969ad9b4f56570f3f6120c1d77d04ba10e5d99366d8eecee8e2",
"hex"
),
curve: "secp256k1",
needsKYC: true,
needsBearerToken: true,
};

const swapProviders: Record<string, SwapProviderConfig> = {
changelly: {
nameAndPubkey: Buffer.from(
"094368616e67656c6c790480d7c0d3a9183597395f58dda05999328da6f18fabd5cda0aff8e8e3fc633436a2dbf48ecb23d40df7c3c7d3e774b77b4b5df0e9f7e08cf1cdf2dba788eb085b",
Expand All @@ -51,6 +63,7 @@ const swapProviders: Record<
),
curve: "secpk256k1",
needsKYC: false,
needsBearerToken: false,
},
wyre: {
nameAndPubkey: Buffer.from(
Expand All @@ -63,12 +76,13 @@ const swapProviders: Record<
),
curve: "secpk256k1",
needsKYC: true,
needsBearerToken: false,
},
ftx,
ftxus: ftx,
};

const getProviderNameAndSignature = (
providerName: string
): ExchangeProviderNameAndSignature => {
const getProviderConfig = (providerName: string): SwapProviderConfig => {
const res = swapProviders[providerName.toLowerCase()];

if (!res) {
Expand Down Expand Up @@ -160,14 +174,17 @@ export const getSwapAPIError = (errorCode: number, errorMessage?: string) => {

export {
getSwapAPIBaseURL,
getProviderNameAndSignature,
getProviderConfig,
getProviders,
getStatus,
getExchangeRates,
getCompleteSwapHistory,
postSwapAccepted,
postSwapCancelled,
initSwap,
getKYCStatus,
submitKYC,
checkQuote,
USStates,
countries,
};
Loading