'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var cleaners = require('cleaners');
var rfc4648 = require('rfc4648');

/**
 * A string of hex-encoded binary data.
 */
const asBase16 = cleaners.asCodec(raw => rfc4648.base16.parse(cleaners.asString(raw)), clean => rfc4648.base16.stringify(clean).toLowerCase());

/**
 * A string of base32-encoded binary data.
 */
const asBase32 = cleaners.asCodec(raw => rfc4648.base32.parse(cleaners.asString(raw), {
  loose: true
}), clean => rfc4648.base32.stringify(clean, {
  pad: false
}));

/**
 * A string of base64-encoded binary data.
 */
const asBase64 = cleaners.asCodec(raw => rfc4648.base64.parse(cleaners.asString(raw)), clean => rfc4648.base64.stringify(clean));

// ---------------------------------------------------------------------
// public Edge types
// ---------------------------------------------------------------------

const asEdgePendingVoucher = cleaners.asObject({
  voucherId: cleaners.asString,
  activates: cleaners.asDate,
  created: cleaners.asDate,
  ip: cleaners.asString,
  ipDescription: cleaners.asString,
  deviceDescription: cleaners.asOptional(cleaners.asString)
});

// ---------------------------------------------------------------------
// internal Edge types
// ---------------------------------------------------------------------

const asEdgeBox = cleaners.asObject({
  encryptionType: cleaners.asNumber,
  data_base64: asBase64,
  iv_hex: asBase16
});
const asEdgeKeyBox = cleaners.asObject({
  created: cleaners.asOptional(cleaners.asDate),
  data_base64: asBase64,
  encryptionType: cleaners.asNumber,
  iv_hex: asBase16
});
const asEdgeSnrp = cleaners.asObject({
  salt_hex: asBase16,
  n: cleaners.asNumber,
  r: cleaners.asNumber,
  p: cleaners.asNumber
});
const asEdgeLobbyRequest = cleaners.asObject({
  loginRequest: cleaners.asOptional(cleaners.asObject({
    appId: cleaners.asString
  }).withRest),
  publicKey: asBase64,
  timeout: cleaners.asOptional(cleaners.asNumber)
}).withRest;
const asEdgeLobbyReply = cleaners.asObject({
  publicKey: asBase64,
  box: asEdgeBox
});

/**
 * An array of base64-encoded hashed recovery answers.
 */
const asRecovery2Auth = cleaners.asArray(asBase64);

// ---------------------------------------------------------------------
// top-level request & response bodies
// ---------------------------------------------------------------------

const asLoginRequestBody = cleaners.asObject({
  // The request payload:
  data: cleaners.asUnknown,
  // Common fields for all login methods:
  challengeId: cleaners.asOptional(cleaners.asString),
  deviceDescription: cleaners.asOptional(cleaners.asString),
  otp: cleaners.asOptional(cleaners.asString),
  syncToken: cleaners.asOptional(cleaners.asString),
  voucherId: cleaners.asOptional(cleaners.asString),
  voucherAuth: cleaners.asOptional(asBase64),
  // Secret-key login:
  loginId: cleaners.asOptional(asBase64),
  loginAuth: cleaners.asOptional(asBase64),
  // Password login:
  userId: cleaners.asOptional(asBase64),
  passwordAuth: cleaners.asOptional(asBase64),
  // PIN login:
  pin2Id: cleaners.asOptional(asBase64),
  pin2Auth: cleaners.asOptional(asBase64),
  // Recovery login:
  recovery2Id: cleaners.asOptional(asBase64),
  recovery2Auth: cleaners.asOptional(asRecovery2Auth),
  // Messages:
  loginIds: cleaners.asOptional(cleaners.asArray(asBase64)),
  // OTP reset:
  otpResetAuth: cleaners.asOptional(cleaners.asString),
  // Legacy:
  did: cleaners.asOptional(cleaners.asString),
  l1: cleaners.asOptional(asBase64),
  lp1: cleaners.asOptional(asBase64),
  lpin1: cleaners.asOptional(asBase64),
  lra1: cleaners.asOptional(asBase64),
  recoveryAuth: cleaners.asOptional(asBase64) // lra1
});

const asLoginResponseBody = cleaners.asObject({
  // The response payload:
  results: cleaners.asOptional(cleaners.asUnknown),
  // What type of response is this (success or failure)?:
  status_code: cleaners.asNumber,
  message: cleaners.asString
});

// ---------------------------------------------------------------------
// request payloads
// ---------------------------------------------------------------------

const asChangeOtpPayload = cleaners.asObject({
  otpTimeout: cleaners.asOptional(cleaners.asNumber, 7 * 24 * 60 * 60),
  // seconds
  otpKey: asBase32
});
const asChangePasswordPayload = cleaners.asObject({
  passwordAuth: asBase64,
  passwordAuthBox: asEdgeBox,
  passwordAuthSnrp: asEdgeSnrp,
  passwordBox: asEdgeBox,
  passwordKeySnrp: asEdgeSnrp
});
const asChangePin2IdPayload = cleaners.asObject({
  pin2Id: asBase64
});
const asChangePin2Payload = cleaners.asObject({
  pin2Id: cleaners.asOptional(asBase64),
  pin2Auth: cleaners.asOptional(asBase64),
  pin2Box: cleaners.asOptional(asEdgeBox),
  pin2KeyBox: cleaners.asOptional(asEdgeBox),
  pin2TextBox: asEdgeBox
});
const asChangeRecovery2IdPayload = cleaners.asObject({
  recovery2Id: asBase64
});
const asChangeRecovery2Payload = cleaners.asObject({
  recovery2Id: asBase64,
  recovery2Auth: asRecovery2Auth,
  recovery2Box: asEdgeBox,
  recovery2KeyBox: asEdgeBox,
  question2Box: asEdgeBox
});
const asChangeSecretPayload = cleaners.asObject({
  loginAuthBox: asEdgeBox,
  loginAuth: asBase64
});
const asChangeUsernamePayload = cleaners.asObject({
  userId: asBase64,
  userTextBox: asEdgeBox
});
const asChangeVouchersPayload = cleaners.asObject({
  approvedVouchers: cleaners.asOptional(cleaners.asArray(cleaners.asString)),
  rejectedVouchers: cleaners.asOptional(cleaners.asArray(cleaners.asString))
});
const asCreateKeysPayload = cleaners.asObject({
  keyBoxes: cleaners.asArray(asEdgeBox),
  newSyncKeys: cleaners.asOptional(cleaners.asArray(cleaners.asString), () => [])
});
const asCreateLoginPayload = cleaners.asObject({
  appId: cleaners.asString,
  loginId: asBase64,
  parentBox: cleaners.asOptional(asEdgeBox)
});

// ---------------------------------------------------------------------
// response payloads
// ---------------------------------------------------------------------

const asChallengeErrorPayload = cleaners.asObject({
  challengeId: cleaners.asString,
  challengeUri: cleaners.asString
});
const asCreateChallengePayload = cleaners.asObject({
  challengeId: cleaners.asString,
  challengeUri: cleaners.asOptional(cleaners.asString)
});
const asLobbyPayload = cleaners.asObject({
  request: asEdgeLobbyRequest,
  replies: cleaners.asArray(asEdgeLobbyReply)
});
const asTrue = cleaners.asValue(true);
const asLoginPayload = cleaners.asObject({
  // Identity:
  appId: cleaners.asString,
  created: cleaners.asDate,
  loginId: asBase64,
  syncToken: cleaners.asOptional(cleaners.asString),
  // Nested logins:
  children: cleaners.asOptional(cleaners.asArray(raw => asLoginPayload(raw))),
  parentBox: cleaners.asOptional(asEdgeBox),
  // 2-factor login:
  otpKey: cleaners.asOptional(cleaners.asEither(asTrue, asBase32)),
  otpResetDate: cleaners.asOptional(cleaners.asDate),
  otpTimeout: cleaners.asOptional(cleaners.asNumber),
  // Password login:
  passwordAuthBox: cleaners.asOptional(asEdgeBox),
  passwordAuthSnrp: cleaners.asOptional(asEdgeSnrp),
  passwordBox: cleaners.asOptional(cleaners.asEither(asTrue, asEdgeBox)),
  passwordKeySnrp: cleaners.asOptional(asEdgeSnrp),
  // PIN v2 login:
  pin2Box: cleaners.asOptional(cleaners.asEither(asTrue, asEdgeBox)),
  pin2KeyBox: cleaners.asOptional(asEdgeBox),
  pin2TextBox: cleaners.asOptional(asEdgeBox),
  // Recovery v2 login:
  question2Box: cleaners.asOptional(asEdgeBox),
  recovery2Box: cleaners.asOptional(cleaners.asEither(asTrue, asEdgeBox)),
  recovery2KeyBox: cleaners.asOptional(asEdgeBox),
  // Secret-key login:
  loginAuthBox: cleaners.asOptional(asEdgeBox),
  // Username:
  userId: cleaners.asOptional(asBase64),
  userTextBox: cleaners.asOptional(asEdgeBox),
  // Voucher login:
  pendingVouchers: cleaners.asOptional(cleaners.asArray(asEdgePendingVoucher), () => []),
  // Resources:
  keyBoxes: cleaners.asOptional(cleaners.asArray(asEdgeKeyBox)),
  mnemonicBox: cleaners.asOptional(asEdgeBox),
  rootKeyBox: cleaners.asOptional(asEdgeBox),
  syncKeyBox: cleaners.asOptional(asEdgeBox)
});
const asMessagesPayload = cleaners.asArray(cleaners.asObject({
  loginId: asBase64,
  otpResetPending: cleaners.asOptional(cleaners.asBoolean, false),
  pendingVouchers: cleaners.asOptional(cleaners.asArray(asEdgePendingVoucher), () => []),
  recovery2Corrupt: cleaners.asOptional(cleaners.asBoolean, false)
}));
const asOtpErrorPayload = cleaners.asObject({
  login_id: cleaners.asOptional(asBase64),
  otp_reset_auth: cleaners.asOptional(cleaners.asString),
  otp_timeout_date: cleaners.asOptional(cleaners.asDate),
  reason: cleaners.asOptional(cleaners.asValue('ip', 'otp'), 'otp'),
  voucher_activates: cleaners.asOptional(cleaners.asDate),
  voucher_auth: cleaners.asOptional(asBase64),
  voucher_id: cleaners.asOptional(cleaners.asString)
});
const asOtpResetPayload = cleaners.asObject({
  otpResetDate: cleaners.asDate
});
const asPasswordErrorPayload = cleaners.asObject({
  wait_seconds: cleaners.asOptional(cleaners.asNumber)
});
const asRecovery2InfoPayload = cleaners.asObject({
  question2Box: asEdgeBox
});
const asUsernameInfoPayload = cleaners.asObject({
  loginId: asBase64,
  // Password login:
  passwordAuthSnrp: cleaners.asOptional(asEdgeSnrp),
  // Recovery v1 login:
  questionBox: cleaners.asOptional(asEdgeBox),
  questionKeySnrp: cleaners.asOptional(asEdgeSnrp),
  recoveryAuthSnrp: cleaners.asOptional(asEdgeSnrp)
});

// ---------------------------------------------------------------------
// uncleaners
// ---------------------------------------------------------------------

// Common types:
const wasEdgeBox = cleaners.uncleaner(asEdgeBox);
const wasEdgeLobbyReply = cleaners.uncleaner(asEdgeLobbyReply);
const wasEdgeLobbyRequest = cleaners.uncleaner(asEdgeLobbyRequest);

// Top-level request / response bodies:
const wasLoginRequestBody = cleaners.uncleaner(asLoginRequestBody);
const wasLoginResponseBody = cleaners.uncleaner(asLoginResponseBody);

// Request payloads:
const wasChangeOtpPayload = cleaners.uncleaner(asChangeOtpPayload);
const wasChangePasswordPayload = cleaners.uncleaner(asChangePasswordPayload);
const wasChangePin2IdPayload = cleaners.uncleaner(asChangePin2IdPayload);
const wasChangePin2Payload = cleaners.uncleaner(asChangePin2Payload);
const wasChangeRecovery2IdPayload = cleaners.uncleaner(asChangeRecovery2IdPayload);
const wasChangeRecovery2Payload = cleaners.uncleaner(asChangeRecovery2Payload);
const wasChangeSecretPayload = cleaners.uncleaner(asChangeSecretPayload);
const wasChangeUsernamePayload = cleaners.uncleaner(asChangeUsernamePayload);
const wasChangeVouchersPayload = cleaners.uncleaner(asChangeVouchersPayload);
const wasCreateKeysPayload = cleaners.uncleaner(asCreateKeysPayload);
const wasCreateLoginPayload = cleaners.uncleaner(asCreateLoginPayload);

// Response payloads:
const wasChallengeErrorPayload = cleaners.uncleaner(asChallengeErrorPayload);
const wasCreateChallengePayload = cleaners.uncleaner(asCreateChallengePayload);
const wasLobbyPayload = cleaners.uncleaner(asLobbyPayload);
const wasLoginPayload = cleaners.uncleaner(asLoginPayload);
const wasMessagesPayload = cleaners.uncleaner(asMessagesPayload);
const wasOtpErrorPayload = cleaners.uncleaner(asOtpErrorPayload);
const wasOtpResetPayload = cleaners.uncleaner(asOtpResetPayload);
const wasPasswordErrorPayload = cleaners.uncleaner(asPasswordErrorPayload);
const wasRecovery2InfoPayload = cleaners.uncleaner(asRecovery2InfoPayload);
const wasUsernameInfoPayload = cleaners.uncleaner(asUsernameInfoPayload);

/*
 * These are errors the core knows about.
 *
 * The GUI should handle these errors in an "intelligent" way, such as by
 * displaying a localized error message or asking the user for more info.
 * All these errors have a `name` field, which the GUI can use to select
 * the appropriate response.
 *
 * Other errors are possible, of course, since the Javascript language
 * itself can generate exceptions. Those errors won't have a `type` field,
 * and the GUI should just show them with a stack trace & generic message,
 * since the program has basically crashed at that point.
 */
/**
 * Thrown when the login server requires a CAPTCHA.
 *
 * After showing the WebView with the challengeUri,
 * pass the challengeId to the login method
 * (such as loginWithPassword) to complete the login.
 *
 * The challengeUri web page will signal that it is done by navigating
 * to a new location that ends with either /success or /failure,
 * such as https://login.edge.app/challenge/success
 * The login UI can use this as a signal to close the WebView.
 */
function ChallengeError(resultsJson, message = 'Login requires a CAPTCHA') {
  if (!(this instanceof ChallengeError)) throw new TypeError("Class constructor ChallengeError cannot be invoked without 'new'");
  var _this;
  function _super(message) {
    _this = new Error(message);
    Object.defineProperty(_this, "constructor", {
      value: ChallengeError,
      configurable: true,
      writable: true
    });
    return _this;
  }
  _super(message);
  _this.name = 'ChallengeError';
  _this.challengeId = resultsJson.challengeId;
  _this.challengeUri = resultsJson.challengeUri;
  return _this;
}

/**
 * Trying to spend an uneconomically small amount of money.
 */
function DustSpendError(message = 'Please send a larger amount') {
  if (!(this instanceof DustSpendError)) throw new TypeError("Class constructor DustSpendError cannot be invoked without 'new'");
  var _this2;
  function _super2(message) {
    _this2 = new Error(message);
    Object.defineProperty(_this2, "constructor", {
      value: DustSpendError,
      configurable: true,
      writable: true
    });
    return _this2;
  }
  _super2(message);
  _this2.name = 'DustSpendError';
  return _this2;
}
/**
 * Trying to spend more money than the wallet contains.
 */
function InsufficientFundsError(opts) {
  if (!(this instanceof InsufficientFundsError)) throw new TypeError("Class constructor InsufficientFundsError cannot be invoked without 'new'");
  var _this3;
  function _super3(message) {
    _this3 = new Error(message);
    Object.defineProperty(_this3, "constructor", {
      value: InsufficientFundsError,
      configurable: true,
      writable: true
    });
    return _this3;
  }
  const {
    tokenId = null,
    networkFee
  } = opts ?? {};
  _super3(`Insufficient ${tokenId ?? 'funds'}`);
  _this3.tokenId = tokenId;
  _this3.networkFee = networkFee;
  _this3.name = 'InsufficientFundsError';
  return _this3;
}

/**
 * Could not reach the server at all.
 */
function NetworkError(message = 'Cannot reach the network') {
  if (!(this instanceof NetworkError)) throw new TypeError("Class constructor NetworkError cannot be invoked without 'new'");
  var _this4;
  function _super4(message) {
    _this4 = new Error(message);
    Object.defineProperty(_this4, "constructor", {
      value: NetworkError,
      configurable: true,
      writable: true
    });
    return _this4;
  }
  _super4(message);
  _this4.name = 'NetworkError';
  return _this4;
}

/**
 * Attempting to create a MakeSpend without specifying an amount of currency to send
 */
function NoAmountSpecifiedError(message = 'Unable to create zero-amount transaction.') {
  if (!(this instanceof NoAmountSpecifiedError)) throw new TypeError("Class constructor NoAmountSpecifiedError cannot be invoked without 'new'");
  var _this5;
  function _super5(message) {
    _this5 = new Error(message);
    Object.defineProperty(_this5, "constructor", {
      value: NoAmountSpecifiedError,
      configurable: true,
      writable: true
    });
    return _this5;
  }
  _super5(message);
  _this5.name = 'NoAmountSpecifiedError';
  return _this5;
}

/**
 * The endpoint on the server is obsolete, and the app needs to be upgraded.
 */
function ObsoleteApiError(message = 'The application is too old. Please upgrade.') {
  if (!(this instanceof ObsoleteApiError)) throw new TypeError("Class constructor ObsoleteApiError cannot be invoked without 'new'");
  var _this6;
  function _super6(message) {
    _this6 = new Error(message);
    Object.defineProperty(_this6, "constructor", {
      value: ObsoleteApiError,
      configurable: true,
      writable: true
    });
    return _this6;
  }
  _super6(message);
  _this6.name = 'ObsoleteApiError';
  return _this6;
}

/**
 * The OTP token was missing / incorrect.
 *
 * The error object should include a `resetToken` member,
 * which can be used to reset OTP protection on the account.
 *
 * The error object may include a `resetDate` member,
 * which indicates that an OTP reset is already pending,
 * and when it will complete.
 */
function OtpError(resultsJson, message = 'Invalid OTP token') {
  if (!(this instanceof OtpError)) throw new TypeError("Class constructor OtpError cannot be invoked without 'new'");
  var _this7;
  function _super7(message) {
    _this7 = new Error(message);
    Object.defineProperty(_this7, "constructor", {
      value: OtpError,
      configurable: true,
      writable: true
    });
    return _this7;
  }
  _super7(message);
  _this7.name = 'OtpError';
  _this7.reason = 'otp';
  const clean = cleaners.asMaybe(asOtpErrorPayload)(resultsJson);
  if (clean == null) return;
  if (clean.login_id != null) {
    _this7.loginId = rfc4648.base64.stringify(clean.login_id);
  }
  _this7.resetToken = clean.otp_reset_auth;
  _this7.reason = clean.reason;
  _this7.resetDate = clean.otp_timeout_date;
  _this7.voucherActivates = clean.voucher_activates;
  if (clean.voucher_auth != null) {
    _this7.voucherAuth = rfc4648.base64.stringify(clean.voucher_auth);
  }
  _this7.voucherId = clean.voucher_id;
  return _this7;
}

/**
 * The provided authentication is incorrect.
 *
 * Reasons could include:
 * - Password login: wrong password
 * - PIN login: wrong PIN
 * - Recovery login: wrong answers
 *
 * The error object may include a `wait` member,
 * which is the number of seconds the user must wait before trying again.
 */
function PasswordError(resultsJson, message = 'Invalid password') {
  if (!(this instanceof PasswordError)) throw new TypeError("Class constructor PasswordError cannot be invoked without 'new'");
  var _this8;
  function _super8(message) {
    _this8 = new Error(message);
    Object.defineProperty(_this8, "constructor", {
      value: PasswordError,
      configurable: true,
      writable: true
    });
    return _this8;
  }
  _super8(message);
  _this8.name = 'PasswordError';
  const clean = cleaners.asMaybe(asPasswordErrorPayload)(resultsJson);
  if (clean == null) return;
  _this8.wait = clean.wait_seconds;
  return _this8;
}

/**
 * Trying to spend funds that are not yet confirmed.
 */
function PendingFundsError(message = 'Not enough confirmed funds') {
  if (!(this instanceof PendingFundsError)) throw new TypeError("Class constructor PendingFundsError cannot be invoked without 'new'");
  var _this9;
  function _super9(message) {
    _this9 = new Error(message);
    Object.defineProperty(_this9, "constructor", {
      value: PendingFundsError,
      configurable: true,
      writable: true
    });
    return _this9;
  }
  _super9(message);
  _this9.name = 'PendingFundsError';
  return _this9;
}

/**
 * Attempting to shape shift between two wallets of same currency.
 */
function SameCurrencyError(message = 'Wallets can not be the same currency') {
  if (!(this instanceof SameCurrencyError)) throw new TypeError("Class constructor SameCurrencyError cannot be invoked without 'new'");
  var _this10;
  function _super10(message) {
    _this10 = new Error(message);
    Object.defineProperty(_this10, "constructor", {
      value: SameCurrencyError,
      configurable: true,
      writable: true
    });
    return _this10;
  }
  _super10(message);
  _this10.name = 'SameCurrencyError';
  return _this10;
}

/**
 * Trying to spend to an address of the source wallet
 */
function SpendToSelfError(message = 'Spending to self') {
  if (!(this instanceof SpendToSelfError)) throw new TypeError("Class constructor SpendToSelfError cannot be invoked without 'new'");
  var _this11;
  function _super11(message) {
    _this11 = new Error(message);
    Object.defineProperty(_this11, "constructor", {
      value: SpendToSelfError,
      configurable: true,
      writable: true
    });
    return _this11;
  }
  _super11(message);
  _this11.name = 'SpendToSelfError';
  return _this11;
}

/**
 * Trying to swap an amount that is either too low or too high.
 * @param nativeMax the maximum supported amount, in the currency specified
 * by the direction (defaults to "from" currency)
 */
function SwapAboveLimitError(swapInfo, nativeMax, direction = 'from') {
  if (!(this instanceof SwapAboveLimitError)) throw new TypeError("Class constructor SwapAboveLimitError cannot be invoked without 'new'");
  var _this12;
  function _super12(message) {
    _this12 = new Error(message);
    Object.defineProperty(_this12, "constructor", {
      value: SwapAboveLimitError,
      configurable: true,
      writable: true
    });
    return _this12;
  }
  _super12('Amount is too high');
  _this12.name = 'SwapAboveLimitError';
  _this12.pluginId = swapInfo.pluginId;
  _this12.nativeMax = nativeMax;
  _this12.direction = direction;
  return _this12;
}

/**
 * Trying to swap an amount that is either too low or too high.
 * @param nativeMin the minimum supported amount, in the currency specified
 * by the direction (defaults to "from" currency)
 */
function SwapBelowLimitError(swapInfo, nativeMin, direction = 'from') {
  if (!(this instanceof SwapBelowLimitError)) throw new TypeError("Class constructor SwapBelowLimitError cannot be invoked without 'new'");
  var _this13;
  function _super13(message) {
    _this13 = new Error(message);
    Object.defineProperty(_this13, "constructor", {
      value: SwapBelowLimitError,
      configurable: true,
      writable: true
    });
    return _this13;
  }
  _super13('Amount is too low');
  _this13.name = 'SwapBelowLimitError';
  _this13.pluginId = swapInfo.pluginId;
  _this13.nativeMin = nativeMin;
  _this13.direction = direction;
  return _this13;
}

/**
 * The swap plugin does not support this currency pair.
 */
function SwapCurrencyError(swapInfo, request) {
  if (!(this instanceof SwapCurrencyError)) throw new TypeError("Class constructor SwapCurrencyError cannot be invoked without 'new'");
  var _this14;
  function _super14(message) {
    _this14 = new Error(message);
    Object.defineProperty(_this14, "constructor", {
      value: SwapCurrencyError,
      configurable: true,
      writable: true
    });
    return _this14;
  }
  const {
    fromWallet,
    toWallet,
    fromTokenId,
    toTokenId
  } = request;
  const fromPluginId = fromWallet.currencyConfig.currencyInfo.pluginId;
  const toPluginId = toWallet.currencyConfig.currencyInfo.pluginId;
  const fromString = `${fromPluginId}:${String(fromTokenId)}`;
  const toString = `${toPluginId}:${String(toTokenId)}`;
  _super14(`${swapInfo.displayName} does not support ${fromString} to ${toString}`);
  _this14.name = 'SwapCurrencyError';
  _this14.pluginId = swapInfo.pluginId;
  _this14.fromTokenId = fromTokenId ?? null;
  _this14.toTokenId = toTokenId ?? null;
  return _this14;
}
/**
 * The user is not allowed to swap these coins for some reason
 * (no KYC, restricted IP address, etc...).
 * @param reason A string giving the reason for the denial.
 * - 'geoRestriction': The IP address is in a restricted region
 * - 'noVerification': The user needs to provide KYC credentials
 * - 'needsActivation': The user needs to log into the service.
 */
function SwapPermissionError(swapInfo, reason) {
  if (!(this instanceof SwapPermissionError)) throw new TypeError("Class constructor SwapPermissionError cannot be invoked without 'new'");
  var _this15;
  function _super15(message) {
    _this15 = new Error(message);
    Object.defineProperty(_this15, "constructor", {
      value: SwapPermissionError,
      configurable: true,
      writable: true
    });
    return _this15;
  }
  if (reason != null) _super15(reason);else _super15('You are not allowed to make this trade');
  _this15.name = 'SwapPermissionError';
  _this15.pluginId = swapInfo.pluginId;
  _this15.reason = reason;
  return _this15;
}

/**
 * Cannot find a login with that id.
 *
 * Reasons could include:
 * - Password login: wrong username
 * - PIN login: wrong PIN key
 * - Recovery login: wrong username, or wrong recovery key
 */
function UsernameError(message = 'Invalid username') {
  if (!(this instanceof UsernameError)) throw new TypeError("Class constructor UsernameError cannot be invoked without 'new'");
  var _this16;
  function _super16(message) {
    _this16 = new Error(message);
    Object.defineProperty(_this16, "constructor", {
      value: UsernameError,
      configurable: true,
      writable: true
    });
    return _this16;
  }
  _super16(message);
  _this16.name = 'UsernameError';
  return _this16;
}
function asMaybeError(name) {
  return function asError(raw) {
    if (raw instanceof Error && raw.name === name) {
      const typeHack = raw;
      return typeHack;
    }
  };
}
const asMaybeChallengeError = asMaybeError('ChallengeError');
const asMaybeDustSpendError = asMaybeError('DustSpendError');
const asMaybeInsufficientFundsError = asMaybeError('InsufficientFundsError');
const asMaybeNetworkError = asMaybeError('NetworkError');
const asMaybeNoAmountSpecifiedError = asMaybeError('NoAmountSpecifiedError');
const asMaybeObsoleteApiError = asMaybeError('ObsoleteApiError');
const asMaybeOtpError = asMaybeError('OtpError');
const asMaybePasswordError = asMaybeError('PasswordError');
const asMaybePendingFundsError = asMaybeError('PendingFundsError');
const asMaybeSameCurrencyError = asMaybeError('SameCurrencyError');
const asMaybeSpendToSelfError = asMaybeError('SpendToSelfError');
const asMaybeSwapAboveLimitError = asMaybeError('SwapAboveLimitError');
const asMaybeSwapBelowLimitError = asMaybeError('SwapBelowLimitError');
const asMaybeSwapCurrencyError = asMaybeError('SwapCurrencyError');
const asMaybeSwapPermissionError = asMaybeError('SwapPermissionError');
const asMaybeUsernameError = asMaybeError('UsernameError');

const asEdgeRepoDump = cleaners.asObject(asEdgeBox);
const asEdgeVoucherDump = cleaners.asObject({
  // Identity:
  loginId: asBase64,
  voucherAuth: asBase64,
  voucherId: cleaners.asString,
  // Login capability:
  created: cleaners.asDate,
  activates: cleaners.asDate,
  // Automatically becomes approved on this date
  status: cleaners.asValue('pending', 'approved', 'rejected'),
  // Information about the login:
  ip: cleaners.asString,
  ipDescription: cleaners.asString,
  deviceDescription: cleaners.asOptional(cleaners.asString)
});
const asEdgeLoginDump = cleaners.asObject({
  // Identity:
  appId: cleaners.asString,
  created: cleaners.asOptional(cleaners.asDate, () => new Date()),
  loginId: asBase64,
  // Nested logins:
  children: cleaners.asOptional(cleaners.asArray(raw => asEdgeLoginDump(raw)), () => []),
  parentBox: cleaners.asOptional(asEdgeBox),
  parentId: () => undefined,
  // 2-factor login:
  otpKey: cleaners.asOptional(asBase32),
  otpResetAuth: cleaners.asOptional(cleaners.asString),
  otpResetDate: cleaners.asOptional(cleaners.asDate),
  otpTimeout: cleaners.asOptional(cleaners.asNumber),
  // Password login:
  passwordAuth: cleaners.asOptional(asBase64),
  passwordAuthBox: cleaners.asOptional(asEdgeBox),
  passwordAuthSnrp: cleaners.asOptional(asEdgeSnrp),
  passwordBox: cleaners.asOptional(asEdgeBox),
  passwordKeySnrp: cleaners.asOptional(asEdgeSnrp),
  // PIN v2 login:
  pin2Id: cleaners.asOptional(asBase64),
  pin2Auth: cleaners.asOptional(asBase64),
  pin2Box: cleaners.asOptional(asEdgeBox),
  pin2KeyBox: cleaners.asOptional(asEdgeBox),
  pin2TextBox: cleaners.asOptional(asEdgeBox),
  // Recovery v2 login:
  recovery2Id: cleaners.asOptional(asBase64),
  recovery2Auth: cleaners.asOptional(asRecovery2Auth),
  question2Box: cleaners.asOptional(asEdgeBox),
  recovery2Box: cleaners.asOptional(asEdgeBox),
  recovery2KeyBox: cleaners.asOptional(asEdgeBox),
  // Secret-key login:
  loginAuth: cleaners.asOptional(asBase64),
  loginAuthBox: cleaners.asOptional(asEdgeBox),
  // Username:
  userId: cleaners.asOptional(asBase64),
  userTextBox: cleaners.asOptional(asEdgeBox),
  // Keys and assorted goodies:
  keyBoxes: cleaners.asOptional(cleaners.asArray(asEdgeKeyBox), () => []),
  mnemonicBox: cleaners.asOptional(asEdgeBox),
  rootKeyBox: cleaners.asOptional(asEdgeBox),
  syncKeyBox: cleaners.asOptional(asEdgeBox),
  vouchers: cleaners.asOptional(cleaners.asArray(asEdgeVoucherDump), () => []),
  // Obsolete:
  pinBox: cleaners.asOptional(asEdgeBox),
  pinId: cleaners.asOptional(cleaners.asString),
  pinKeyBox: cleaners.asOptional(asEdgeBox)
});
const wasEdgeLoginDump = cleaners.uncleaner(asEdgeLoginDump);
const wasEdgeRepoDump = cleaners.uncleaner(asEdgeRepoDump);

exports.ChallengeError = ChallengeError;
exports.DustSpendError = DustSpendError;
exports.InsufficientFundsError = InsufficientFundsError;
exports.NetworkError = NetworkError;
exports.NoAmountSpecifiedError = NoAmountSpecifiedError;
exports.ObsoleteApiError = ObsoleteApiError;
exports.OtpError = OtpError;
exports.PasswordError = PasswordError;
exports.PendingFundsError = PendingFundsError;
exports.SameCurrencyError = SameCurrencyError;
exports.SpendToSelfError = SpendToSelfError;
exports.SwapAboveLimitError = SwapAboveLimitError;
exports.SwapBelowLimitError = SwapBelowLimitError;
exports.SwapCurrencyError = SwapCurrencyError;
exports.SwapPermissionError = SwapPermissionError;
exports.UsernameError = UsernameError;
exports.asBase16 = asBase16;
exports.asBase32 = asBase32;
exports.asBase64 = asBase64;
exports.asChallengeErrorPayload = asChallengeErrorPayload;
exports.asChangeOtpPayload = asChangeOtpPayload;
exports.asChangePasswordPayload = asChangePasswordPayload;
exports.asChangePin2IdPayload = asChangePin2IdPayload;
exports.asChangePin2Payload = asChangePin2Payload;
exports.asChangeRecovery2IdPayload = asChangeRecovery2IdPayload;
exports.asChangeRecovery2Payload = asChangeRecovery2Payload;
exports.asChangeSecretPayload = asChangeSecretPayload;
exports.asChangeUsernamePayload = asChangeUsernamePayload;
exports.asChangeVouchersPayload = asChangeVouchersPayload;
exports.asCreateChallengePayload = asCreateChallengePayload;
exports.asCreateKeysPayload = asCreateKeysPayload;
exports.asCreateLoginPayload = asCreateLoginPayload;
exports.asEdgeBox = asEdgeBox;
exports.asEdgeKeyBox = asEdgeKeyBox;
exports.asEdgeLobbyReply = asEdgeLobbyReply;
exports.asEdgeLobbyRequest = asEdgeLobbyRequest;
exports.asEdgeLoginDump = asEdgeLoginDump;
exports.asEdgePendingVoucher = asEdgePendingVoucher;
exports.asEdgeRepoDump = asEdgeRepoDump;
exports.asEdgeSnrp = asEdgeSnrp;
exports.asEdgeVoucherDump = asEdgeVoucherDump;
exports.asLobbyPayload = asLobbyPayload;
exports.asLoginPayload = asLoginPayload;
exports.asLoginRequestBody = asLoginRequestBody;
exports.asLoginResponseBody = asLoginResponseBody;
exports.asMaybeChallengeError = asMaybeChallengeError;
exports.asMaybeDustSpendError = asMaybeDustSpendError;
exports.asMaybeInsufficientFundsError = asMaybeInsufficientFundsError;
exports.asMaybeNetworkError = asMaybeNetworkError;
exports.asMaybeNoAmountSpecifiedError = asMaybeNoAmountSpecifiedError;
exports.asMaybeObsoleteApiError = asMaybeObsoleteApiError;
exports.asMaybeOtpError = asMaybeOtpError;
exports.asMaybePasswordError = asMaybePasswordError;
exports.asMaybePendingFundsError = asMaybePendingFundsError;
exports.asMaybeSameCurrencyError = asMaybeSameCurrencyError;
exports.asMaybeSpendToSelfError = asMaybeSpendToSelfError;
exports.asMaybeSwapAboveLimitError = asMaybeSwapAboveLimitError;
exports.asMaybeSwapBelowLimitError = asMaybeSwapBelowLimitError;
exports.asMaybeSwapCurrencyError = asMaybeSwapCurrencyError;
exports.asMaybeSwapPermissionError = asMaybeSwapPermissionError;
exports.asMaybeUsernameError = asMaybeUsernameError;
exports.asMessagesPayload = asMessagesPayload;
exports.asOtpErrorPayload = asOtpErrorPayload;
exports.asOtpResetPayload = asOtpResetPayload;
exports.asPasswordErrorPayload = asPasswordErrorPayload;
exports.asRecovery2Auth = asRecovery2Auth;
exports.asRecovery2InfoPayload = asRecovery2InfoPayload;
exports.asUsernameInfoPayload = asUsernameInfoPayload;
exports.wasChallengeErrorPayload = wasChallengeErrorPayload;
exports.wasChangeOtpPayload = wasChangeOtpPayload;
exports.wasChangePasswordPayload = wasChangePasswordPayload;
exports.wasChangePin2IdPayload = wasChangePin2IdPayload;
exports.wasChangePin2Payload = wasChangePin2Payload;
exports.wasChangeRecovery2IdPayload = wasChangeRecovery2IdPayload;
exports.wasChangeRecovery2Payload = wasChangeRecovery2Payload;
exports.wasChangeSecretPayload = wasChangeSecretPayload;
exports.wasChangeUsernamePayload = wasChangeUsernamePayload;
exports.wasChangeVouchersPayload = wasChangeVouchersPayload;
exports.wasCreateChallengePayload = wasCreateChallengePayload;
exports.wasCreateKeysPayload = wasCreateKeysPayload;
exports.wasCreateLoginPayload = wasCreateLoginPayload;
exports.wasEdgeBox = wasEdgeBox;
exports.wasEdgeLobbyReply = wasEdgeLobbyReply;
exports.wasEdgeLobbyRequest = wasEdgeLobbyRequest;
exports.wasEdgeLoginDump = wasEdgeLoginDump;
exports.wasEdgeRepoDump = wasEdgeRepoDump;
exports.wasLobbyPayload = wasLobbyPayload;
exports.wasLoginPayload = wasLoginPayload;
exports.wasLoginRequestBody = wasLoginRequestBody;
exports.wasLoginResponseBody = wasLoginResponseBody;
exports.wasMessagesPayload = wasMessagesPayload;
exports.wasOtpErrorPayload = wasOtpErrorPayload;
exports.wasOtpResetPayload = wasOtpResetPayload;
exports.wasPasswordErrorPayload = wasPasswordErrorPayload;
exports.wasRecovery2InfoPayload = wasRecovery2InfoPayload;
exports.wasUsernameInfoPayload = wasUsernameInfoPayload;
