@fiatconnect/fiatconnect-sdk
Advanced tools
Comparing version 0.2.0 to 0.2.1-alpha.0
@@ -5,4 +5,11 @@ # Changelog | ||
### [0.2.1](https://github.com/fiatconnect/fiatconnect-sdk/compare/v0.2.0...v0.2.1) (2022-05-23) | ||
### Features | ||
* **login:** Add isLoggedIn, minor fixes ([#19](https://github.com/fiatconnect/fiatconnect-sdk/issues/19)) ([a3b7714](https://github.com/fiatconnect/fiatconnect-sdk/commit/a3b7714c36d316877427db35cee33361051f8d56)) | ||
## 0.2.0 (2022-05-16) | ||
Initial release |
@@ -1,81 +0,2 @@ | ||
import { AddFiatAccountResponse, DeleteFiatAccountRequestParams, GetFiatAccountsResponse, KycRequestParams, KycStatusResponse, QuoteErrorResponse, QuoteRequestQuery, QuoteResponse, TransferResponse, TransferStatusRequestParams, TransferStatusResponse, ClockResponse } from '@fiatconnect/fiatconnect-types'; | ||
import { Result } from 'ts-results'; | ||
import { AddFiatAccountParams, AddKycParams, ErrorResponse, FiatConectApiClient, FiatConnectClientConfig, TransferRequestParams, ClockDiffParams, ClockDiffResult } from './types'; | ||
export default class FiatConnectClient implements FiatConectApiClient { | ||
config: FiatConnectClientConfig; | ||
signingFunction: (message: string) => Promise<string>; | ||
_sessionExpiry?: Date; | ||
constructor(config: FiatConnectClientConfig, signingFunction: (message: string) => Promise<string>); | ||
_getAuthHeader(): { | ||
Authorization: string; | ||
} | undefined; | ||
_ensureLogin(): Promise<void>; | ||
/** | ||
* Logs in with the provider and initializes a session. | ||
* | ||
* @returns a Promise resolving to the literal string 'success' on a | ||
* successful login or an Error response. | ||
*/ | ||
login(): Promise<Result<'success', ErrorResponse>>; | ||
_getQuote(params: QuoteRequestQuery, inOrOut: 'in' | 'out'): Promise<Result<QuoteResponse, QuoteErrorResponse | ErrorResponse>>; | ||
/** | ||
* https://en.wikipedia.org/wiki/Network_Time_Protocol#Clock_synchronization_algorithm | ||
* | ||
* Returns the calculated difference between the client and server clocks as a number of milliseconds. | ||
* Positive values mean the server's clock is ahead of the client's. | ||
* Also returns the maximum error of the calculated difference. | ||
*/ | ||
_calculateClockDiff({ t0, t1, t2, t3 }: ClockDiffParams): ClockDiffResult; | ||
/** | ||
* Convenience method to calculate the approximate difference between server and client clocks. | ||
*/ | ||
getClockDiffApprox(): Promise<Result<ClockDiffResult, ErrorResponse>>; | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#321-get-clock | ||
*/ | ||
getClock(): Promise<Result<ClockResponse, ErrorResponse>>; | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3311-get-quotein | ||
*/ | ||
getQuoteIn(params: QuoteRequestQuery): Promise<Result<QuoteResponse, QuoteErrorResponse | ErrorResponse>>; | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3312-get-quoteout | ||
*/ | ||
getQuoteOut(params: QuoteRequestQuery): Promise<Result<QuoteResponse, QuoteErrorResponse | ErrorResponse>>; | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3321-post-kyckycschema | ||
*/ | ||
addKyc(params: AddKycParams): Promise<Result<KycStatusResponse, ErrorResponse>>; | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3323-delete-kyckycschema | ||
*/ | ||
deleteKyc(params: KycRequestParams): Promise<Result<void, ErrorResponse>>; | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3322-get-kyckycschemastatus | ||
*/ | ||
getKycStatus(params: KycRequestParams): Promise<Result<KycStatusResponse, ErrorResponse>>; | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3331-post-accountsfiataccountschema | ||
*/ | ||
addFiatAccount(params: AddFiatAccountParams): Promise<Result<AddFiatAccountResponse, ErrorResponse>>; | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3332-get-accounts | ||
*/ | ||
getFiatAccounts(): Promise<Result<GetFiatAccountsResponse, ErrorResponse>>; | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3333-delete-accountfiataccountid | ||
*/ | ||
deleteFiatAccount(params: DeleteFiatAccountRequestParams): Promise<Result<void, ErrorResponse>>; | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3341-post-transferin | ||
*/ | ||
transferIn(params: TransferRequestParams): Promise<Result<TransferResponse, ErrorResponse>>; | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3342-post-transferout | ||
*/ | ||
transferOut(params: TransferRequestParams): Promise<Result<TransferResponse, ErrorResponse>>; | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3343-get-transfertransferidstatus | ||
*/ | ||
getTransferStatus(params: TransferStatusRequestParams): Promise<Result<TransferStatusResponse, ErrorResponse>>; | ||
} | ||
export { FiatConnectClient } from './fiat-connect-client'; | ||
export * from './types'; |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const fiatconnect_types_1 = require("@fiatconnect/fiatconnect-types"); | ||
const fetch_cookie_1 = __importDefault(require("fetch-cookie")); | ||
const node_fetch_1 = __importDefault(require("node-fetch")); | ||
const siwe_1 = require("siwe"); | ||
const ts_results_1 = require("ts-results"); | ||
const NETWORK_CHAIN_IDS = { | ||
[fiatconnect_types_1.Network.Alfajores]: 44787, | ||
[fiatconnect_types_1.Network.Mainnet]: 42220, | ||
}; | ||
const SESSION_DURATION_MS = 14400000; // 4 hours | ||
const fetch = (0, fetch_cookie_1.default)(node_fetch_1.default); | ||
class FiatConnectClient { | ||
constructor(config, signingFunction) { | ||
this.config = config; | ||
this.signingFunction = signingFunction; | ||
} | ||
_getAuthHeader() { | ||
if (this.config.apiKey) { | ||
return { Authorization: `Bearer ${this.config.apiKey}` }; | ||
} | ||
} | ||
async _ensureLogin() { | ||
if (this._sessionExpiry && this._sessionExpiry > new Date()) { | ||
return; | ||
} | ||
const loginResult = await this.login(); | ||
if (!loginResult.ok) { | ||
throw new Error(`Login failed: ${loginResult.val.error}`); | ||
} | ||
} | ||
/** | ||
* Logs in with the provider and initializes a session. | ||
* | ||
* @returns a Promise resolving to the literal string 'success' on a | ||
* successful login or an Error response. | ||
*/ | ||
async login() { | ||
try { | ||
const expirationDate = new Date(Date.now() + SESSION_DURATION_MS); | ||
const siweMessage = new siwe_1.SiweMessage({ | ||
domain: new URL(this.config.baseUrl).hostname, | ||
address: this.config.accountAddress, | ||
statement: 'Sign in with Ethereum', | ||
uri: `${this.config.baseUrl}/auth/login`, | ||
version: '1', | ||
chainId: NETWORK_CHAIN_IDS[this.config.network], | ||
nonce: (0, siwe_1.generateNonce)(), | ||
expirationTime: expirationDate.toISOString(), | ||
}); | ||
const message = siweMessage.prepareMessage(); | ||
const body = { | ||
message, | ||
signature: await this.signingFunction(message), | ||
}; | ||
const response = await fetch(`${this.config.baseUrl}/auth/login`, { | ||
method: 'POST', | ||
headers: Object.assign({ 'Content-Type': 'application/json' }, this._getAuthHeader()), | ||
body: JSON.stringify(body), | ||
}); | ||
if (!response.ok) { | ||
// On a non 200 response, the response should be a JSON including an error field. | ||
const data = await response.json(); | ||
return (0, ts_results_1.Err)(data); | ||
} | ||
this._sessionExpiry = expirationDate; | ||
return (0, ts_results_1.Ok)('success'); | ||
} | ||
catch (error) { | ||
return handleError(error); | ||
} | ||
} | ||
async _getQuote(params, inOrOut) { | ||
try { | ||
const queryParams = new URLSearchParams(params).toString(); | ||
const response = await fetch(`${this.config.baseUrl}/quote/${inOrOut}?${queryParams}`, { | ||
method: 'GET', | ||
headers: this._getAuthHeader(), | ||
}); | ||
const data = await response.json(); | ||
if (!response.ok) { | ||
return (0, ts_results_1.Err)(data); | ||
} | ||
return (0, ts_results_1.Ok)(data); | ||
} | ||
catch (error) { | ||
return handleError(error); | ||
} | ||
} | ||
/** | ||
* https://en.wikipedia.org/wiki/Network_Time_Protocol#Clock_synchronization_algorithm | ||
* | ||
* Returns the calculated difference between the client and server clocks as a number of milliseconds. | ||
* Positive values mean the server's clock is ahead of the client's. | ||
* Also returns the maximum error of the calculated difference. | ||
*/ | ||
_calculateClockDiff({ t0, t1, t2, t3 }) { | ||
return { | ||
diff: Math.floor((t1 - t0 + (t2 - t3)) / 2), | ||
maxError: Math.floor((t3 - t0) / 2), | ||
}; | ||
} | ||
/** | ||
* Convenience method to calculate the approximate difference between server and client clocks. | ||
*/ | ||
async getClockDiffApprox() { | ||
const t0 = Date.now(); | ||
const clockResponse = await this.getClock(); | ||
const t3 = Date.now(); | ||
if (!clockResponse.ok) { | ||
return clockResponse; | ||
} | ||
const t1 = new Date(clockResponse.val.time).getTime(); | ||
// We can assume that t1 and t2 are sufficiently close to each other | ||
const t2 = t1; | ||
return (0, ts_results_1.Ok)(this._calculateClockDiff({ t0, t1, t2, t3 })); | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#321-get-clock | ||
*/ | ||
async getClock() { | ||
try { | ||
const response = await fetch(`${this.config.baseUrl}/clock`, { | ||
method: 'GET', | ||
headers: this._getAuthHeader(), | ||
}); | ||
const data = await response.json(); | ||
if (!response.ok) { | ||
return (0, ts_results_1.Err)(data); | ||
} | ||
return (0, ts_results_1.Ok)(data); | ||
} | ||
catch (error) { | ||
return handleError(error); | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3311-get-quotein | ||
*/ | ||
async getQuoteIn(params) { | ||
return this._getQuote(params, 'in'); | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3312-get-quoteout | ||
*/ | ||
async getQuoteOut(params) { | ||
return this._getQuote(params, 'out'); | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3321-post-kyckycschema | ||
*/ | ||
async addKyc(params) { | ||
try { | ||
await this._ensureLogin(); | ||
const response = await fetch(`${this.config.baseUrl}/kyc/${params.kycSchemaName}`, { | ||
method: 'POST', | ||
headers: Object.assign({ 'Content-Type': 'application/json' }, this._getAuthHeader()), | ||
body: JSON.stringify(params.data), | ||
}); | ||
const data = await response.json(); | ||
if (!response.ok) { | ||
return (0, ts_results_1.Err)(data); | ||
} | ||
return (0, ts_results_1.Ok)(data); | ||
} | ||
catch (error) { | ||
return handleError(error); | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3323-delete-kyckycschema | ||
*/ | ||
async deleteKyc(params) { | ||
try { | ||
await this._ensureLogin(); | ||
const response = await fetch(`${this.config.baseUrl}/kyc/${params.kycSchema}`, { | ||
method: 'DELETE', | ||
headers: this._getAuthHeader(), | ||
}); | ||
const data = await response.json(); | ||
if (!response.ok) { | ||
return (0, ts_results_1.Err)(data); | ||
} | ||
return (0, ts_results_1.Ok)(undefined); | ||
} | ||
catch (error) { | ||
return handleError(error); | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3322-get-kyckycschemastatus | ||
*/ | ||
async getKycStatus(params) { | ||
try { | ||
await this._ensureLogin(); | ||
const response = await fetch(`${this.config.baseUrl}/kyc/${params.kycSchema}`, { | ||
method: 'GET', | ||
headers: this._getAuthHeader(), | ||
}); | ||
const data = await response.json(); | ||
if (!response.ok) { | ||
return (0, ts_results_1.Err)(data); | ||
} | ||
return (0, ts_results_1.Ok)(data); | ||
} | ||
catch (error) { | ||
return handleError(error); | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3331-post-accountsfiataccountschema | ||
*/ | ||
async addFiatAccount(params) { | ||
try { | ||
await this._ensureLogin(); | ||
const response = await fetch(`${this.config.baseUrl}/accounts/${params.fiatAccountSchemaName}`, { | ||
method: 'POST', | ||
headers: Object.assign({ 'Content-Type': 'application/json' }, this._getAuthHeader()), | ||
body: JSON.stringify(params.data), | ||
}); | ||
const data = await response.json(); | ||
if (!response.ok) { | ||
return (0, ts_results_1.Err)(data); | ||
} | ||
return (0, ts_results_1.Ok)(data); | ||
} | ||
catch (error) { | ||
return handleError(error); | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3332-get-accounts | ||
*/ | ||
async getFiatAccounts() { | ||
try { | ||
await this._ensureLogin(); | ||
const response = await fetch(`${this.config.baseUrl}/accounts`, { | ||
method: 'GET', | ||
headers: this._getAuthHeader(), | ||
}); | ||
const data = await response.json(); | ||
if (!response.ok) { | ||
return (0, ts_results_1.Err)(data); | ||
} | ||
return (0, ts_results_1.Ok)(data); | ||
} | ||
catch (error) { | ||
return handleError(error); | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3333-delete-accountfiataccountid | ||
*/ | ||
async deleteFiatAccount(params) { | ||
try { | ||
await this._ensureLogin(); | ||
const response = await fetch(`${this.config.baseUrl}/accounts/${params.fiatAccountId}`, { | ||
method: 'DELETE', | ||
headers: this._getAuthHeader(), | ||
}); | ||
const data = await response.json(); | ||
if (!response.ok) { | ||
return (0, ts_results_1.Err)(data); | ||
} | ||
return (0, ts_results_1.Ok)(undefined); | ||
} | ||
catch (error) { | ||
return handleError(error); | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3341-post-transferin | ||
*/ | ||
async transferIn(params) { | ||
try { | ||
await this._ensureLogin(); | ||
const response = await fetch(`${this.config.baseUrl}/transfer/in`, { | ||
method: 'POST', | ||
headers: Object.assign({ 'Content-Type': 'application/json', 'Idempotency-Key': params.idempotencyKey }, this._getAuthHeader()), | ||
body: JSON.stringify(params.data), | ||
}); | ||
const data = await response.json(); | ||
if (!response.ok) { | ||
return (0, ts_results_1.Err)(data); | ||
} | ||
return (0, ts_results_1.Ok)(data); | ||
} | ||
catch (error) { | ||
return handleError(error); | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3342-post-transferout | ||
*/ | ||
async transferOut(params) { | ||
try { | ||
await this._ensureLogin(); | ||
const response = await fetch(`${this.config.baseUrl}/transfer/out`, { | ||
method: 'POST', | ||
headers: Object.assign({ 'Content-Type': 'application/json', 'Idempotency-Key': params.idempotencyKey }, this._getAuthHeader()), | ||
body: JSON.stringify(params.data), | ||
}); | ||
const data = await response.json(); | ||
if (!response.ok) { | ||
return (0, ts_results_1.Err)(data); | ||
} | ||
return (0, ts_results_1.Ok)(data); | ||
} | ||
catch (error) { | ||
return handleError(error); | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3343-get-transfertransferidstatus | ||
*/ | ||
async getTransferStatus(params) { | ||
try { | ||
await this._ensureLogin(); | ||
const response = await fetch(`${this.config.baseUrl}/transfer/${params.transferId}/status`, { | ||
method: 'GET', | ||
headers: this._getAuthHeader(), | ||
}); | ||
const data = await response.json(); | ||
if (!response.ok) { | ||
return (0, ts_results_1.Err)(data); | ||
} | ||
return (0, ts_results_1.Ok)(data); | ||
} | ||
catch (error) { | ||
return handleError(error); | ||
} | ||
} | ||
} | ||
exports.default = FiatConnectClient; | ||
function handleError(error) { | ||
if (error instanceof Error) { | ||
return (0, ts_results_1.Err)(Object.assign(Object.assign({}, error), { error: error.message })); | ||
} | ||
return (0, ts_results_1.Err)({ error: String(error) }); | ||
} | ||
exports.FiatConnectClient = void 0; | ||
const tslib_1 = require("tslib"); | ||
var fiat_connect_client_1 = require("./fiat-connect-client"); | ||
Object.defineProperty(exports, "FiatConnectClient", { enumerable: true, get: function () { return fiat_connect_client_1.FiatConnectClient; } }); | ||
tslib_1.__exportStar(require("./types"), exports); | ||
//# sourceMappingURL=index.js.map |
import { AccountNumber, AddFiatAccountResponse, DeleteFiatAccountRequestParams, FiatAccountSchema, FiatConnectError, GetFiatAccountsResponse, KycRequestParams, KycSchema, KycStatusResponse, Network, PersonalDataAndDocumentsKyc, QuoteErrorResponse, QuoteRequestQuery, QuoteResponse, TransferRequestBody, TransferResponse, TransferStatusRequestParams, TransferStatusResponse, ClockResponse } from '@fiatconnect/fiatconnect-types'; | ||
import { Result } from 'ts-results'; | ||
export interface FiatConectApiClient { | ||
export interface FiatConnectApiClient { | ||
getClockDiffApprox(): Promise<Result<ClockDiffResult, ErrorResponse>>; | ||
getClock(): Promise<Result<ClockResponse, ErrorResponse>>; | ||
login(): Promise<Result<'success', ErrorResponse>>; | ||
isLoggedIn(): boolean; | ||
getQuoteIn(params: QuoteRequestQuery, jwt: string): Promise<Result<QuoteResponse, QuoteErrorResponse | ErrorResponse>>; | ||
@@ -7,0 +9,0 @@ getQuoteOut(params: QuoteRequestQuery, jwt: string): Promise<Result<QuoteResponse, QuoteErrorResponse | ErrorResponse>>; |
{ | ||
"name": "@fiatconnect/fiatconnect-sdk", | ||
"version": "0.2.0", | ||
"version": "0.2.1-alpha.0", | ||
"description": "A helper libary for wallets to integrate with FiatConnect APIs", | ||
@@ -21,3 +21,3 @@ "scripts": { | ||
"main": "dist/index.js", | ||
"types": "dist/types.d.ts", | ||
"types": "dist/index.d.ts", | ||
"repository": "git@github.com:fiatconnect/fiatconnect-sdk.git", | ||
@@ -49,2 +49,3 @@ "author": "Jacob Waterman <jacob.waterman@valoraapp.com>", | ||
"ts-jest": "^27.1.3", | ||
"tslib": "^1.11.1", | ||
"typescript": "^4.6.2" | ||
@@ -58,4 +59,5 @@ }, | ||
"siwe": "^1.1.6", | ||
"ts-results": "^3.3.0" | ||
"ts-results": "^3.3.0", | ||
"tslib": "^2.4.0" | ||
} | ||
} |
450
src/index.ts
@@ -1,448 +0,2 @@ | ||
import { | ||
AddFiatAccountResponse, | ||
AuthRequestBody, | ||
DeleteFiatAccountRequestParams, | ||
GetFiatAccountsResponse, | ||
KycRequestParams, | ||
KycStatusResponse, | ||
Network, | ||
QuoteErrorResponse, | ||
QuoteRequestQuery, | ||
QuoteResponse, | ||
TransferResponse, | ||
TransferStatusRequestParams, | ||
TransferStatusResponse, | ||
ClockResponse, | ||
} from '@fiatconnect/fiatconnect-types' | ||
import fetchCookie from 'fetch-cookie' | ||
import nodeFetch from 'node-fetch' | ||
import { generateNonce, SiweMessage } from 'siwe' | ||
import { Ok, Err, Result } from 'ts-results' | ||
import { | ||
AddFiatAccountParams, | ||
AddKycParams, | ||
ErrorResponse, | ||
FiatConectApiClient, | ||
FiatConnectClientConfig, | ||
TransferRequestParams, | ||
ClockDiffParams, | ||
ClockDiffResult, | ||
} from './types' | ||
const NETWORK_CHAIN_IDS = { | ||
[Network.Alfajores]: 44787, | ||
[Network.Mainnet]: 42220, | ||
} | ||
const SESSION_DURATION_MS = 14400000 // 4 hours | ||
const fetch = fetchCookie(nodeFetch) | ||
export default class FiatConnectClient implements FiatConectApiClient { | ||
config: FiatConnectClientConfig | ||
signingFunction: (message: string) => Promise<string> | ||
_sessionExpiry?: Date | ||
constructor( | ||
config: FiatConnectClientConfig, | ||
signingFunction: (message: string) => Promise<string>, | ||
) { | ||
this.config = config | ||
this.signingFunction = signingFunction | ||
} | ||
_getAuthHeader() { | ||
if (this.config.apiKey) { | ||
return { Authorization: `Bearer ${this.config.apiKey}` } | ||
} | ||
} | ||
async _ensureLogin() { | ||
if (this._sessionExpiry && this._sessionExpiry > new Date()) { | ||
return | ||
} | ||
const loginResult = await this.login() | ||
if (!loginResult.ok) { | ||
throw new Error(`Login failed: ${loginResult.val.error}`) | ||
} | ||
} | ||
/** | ||
* Logs in with the provider and initializes a session. | ||
* | ||
* @returns a Promise resolving to the literal string 'success' on a | ||
* successful login or an Error response. | ||
*/ | ||
async login(): Promise<Result<'success', ErrorResponse>> { | ||
try { | ||
const expirationDate = new Date(Date.now() + SESSION_DURATION_MS) | ||
const siweMessage = new SiweMessage({ | ||
domain: new URL(this.config.baseUrl).hostname, | ||
address: this.config.accountAddress, | ||
statement: 'Sign in with Ethereum', | ||
uri: `${this.config.baseUrl}/auth/login`, | ||
version: '1', | ||
chainId: NETWORK_CHAIN_IDS[this.config.network], | ||
nonce: generateNonce(), | ||
expirationTime: expirationDate.toISOString(), | ||
}) | ||
const message = siweMessage.prepareMessage() | ||
const body: AuthRequestBody = { | ||
message, | ||
signature: await this.signingFunction(message), | ||
} | ||
const response = await fetch(`${this.config.baseUrl}/auth/login`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
...this._getAuthHeader(), | ||
}, | ||
body: JSON.stringify(body), | ||
}) | ||
if (!response.ok) { | ||
// On a non 200 response, the response should be a JSON including an error field. | ||
const data = await response.json() | ||
return Err(data as ErrorResponse) | ||
} | ||
this._sessionExpiry = expirationDate | ||
return Ok('success') | ||
} catch (error) { | ||
return handleError(error) | ||
} | ||
} | ||
async _getQuote( | ||
params: QuoteRequestQuery, | ||
inOrOut: 'in' | 'out', | ||
): Promise<Result<QuoteResponse, QuoteErrorResponse | ErrorResponse>> { | ||
try { | ||
const queryParams = new URLSearchParams(params).toString() | ||
const response = await fetch( | ||
`${this.config.baseUrl}/quote/${inOrOut}?${queryParams}`, | ||
{ | ||
method: 'GET', | ||
headers: this._getAuthHeader(), | ||
}, | ||
) | ||
const data = await response.json() | ||
if (!response.ok) { | ||
return Err(data as QuoteErrorResponse) | ||
} | ||
return Ok(data as QuoteResponse) | ||
} catch (error) { | ||
return handleError(error) | ||
} | ||
} | ||
/** | ||
* https://en.wikipedia.org/wiki/Network_Time_Protocol#Clock_synchronization_algorithm | ||
* | ||
* Returns the calculated difference between the client and server clocks as a number of milliseconds. | ||
* Positive values mean the server's clock is ahead of the client's. | ||
* Also returns the maximum error of the calculated difference. | ||
*/ | ||
_calculateClockDiff({ t0, t1, t2, t3 }: ClockDiffParams): ClockDiffResult { | ||
return { | ||
diff: Math.floor((t1 - t0 + (t2 - t3)) / 2), | ||
maxError: Math.floor((t3 - t0) / 2), | ||
} | ||
} | ||
/** | ||
* Convenience method to calculate the approximate difference between server and client clocks. | ||
*/ | ||
async getClockDiffApprox(): Promise<Result<ClockDiffResult, ErrorResponse>> { | ||
const t0 = Date.now() | ||
const clockResponse = await this.getClock() | ||
const t3 = Date.now() | ||
if (!clockResponse.ok) { | ||
return clockResponse | ||
} | ||
const t1 = new Date(clockResponse.val.time).getTime() | ||
// We can assume that t1 and t2 are sufficiently close to each other | ||
const t2 = t1 | ||
return Ok(this._calculateClockDiff({ t0, t1, t2, t3 })) | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#321-get-clock | ||
*/ | ||
async getClock(): Promise<Result<ClockResponse, ErrorResponse>> { | ||
try { | ||
const response = await fetch(`${this.config.baseUrl}/clock`, { | ||
method: 'GET', | ||
headers: this._getAuthHeader(), | ||
}) | ||
const data = await response.json() | ||
if (!response.ok) { | ||
return Err(data) | ||
} | ||
return Ok(data) | ||
} catch (error) { | ||
return handleError(error) | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3311-get-quotein | ||
*/ | ||
async getQuoteIn( | ||
params: QuoteRequestQuery, | ||
): Promise<Result<QuoteResponse, QuoteErrorResponse | ErrorResponse>> { | ||
return this._getQuote(params, 'in') | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3312-get-quoteout | ||
*/ | ||
async getQuoteOut( | ||
params: QuoteRequestQuery, | ||
): Promise<Result<QuoteResponse, QuoteErrorResponse | ErrorResponse>> { | ||
return this._getQuote(params, 'out') | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3321-post-kyckycschema | ||
*/ | ||
async addKyc( | ||
params: AddKycParams, | ||
): Promise<Result<KycStatusResponse, ErrorResponse>> { | ||
try { | ||
await this._ensureLogin() | ||
const response = await fetch( | ||
`${this.config.baseUrl}/kyc/${params.kycSchemaName}`, | ||
{ | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
...this._getAuthHeader(), | ||
}, | ||
body: JSON.stringify(params.data), | ||
}, | ||
) | ||
const data = await response.json() | ||
if (!response.ok) { | ||
return Err(data) | ||
} | ||
return Ok(data) | ||
} catch (error) { | ||
return handleError(error) | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3323-delete-kyckycschema | ||
*/ | ||
async deleteKyc( | ||
params: KycRequestParams, | ||
): Promise<Result<void, ErrorResponse>> { | ||
try { | ||
await this._ensureLogin() | ||
const response = await fetch( | ||
`${this.config.baseUrl}/kyc/${params.kycSchema}`, | ||
{ | ||
method: 'DELETE', | ||
headers: this._getAuthHeader(), | ||
}, | ||
) | ||
const data = await response.json() | ||
if (!response.ok) { | ||
return Err(data) | ||
} | ||
return Ok(undefined) | ||
} catch (error) { | ||
return handleError(error) | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3322-get-kyckycschemastatus | ||
*/ | ||
async getKycStatus( | ||
params: KycRequestParams, | ||
): Promise<Result<KycStatusResponse, ErrorResponse>> { | ||
try { | ||
await this._ensureLogin() | ||
const response = await fetch( | ||
`${this.config.baseUrl}/kyc/${params.kycSchema}`, | ||
{ | ||
method: 'GET', | ||
headers: this._getAuthHeader(), | ||
}, | ||
) | ||
const data = await response.json() | ||
if (!response.ok) { | ||
return Err(data) | ||
} | ||
return Ok(data) | ||
} catch (error) { | ||
return handleError(error) | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3331-post-accountsfiataccountschema | ||
*/ | ||
async addFiatAccount( | ||
params: AddFiatAccountParams, | ||
): Promise<Result<AddFiatAccountResponse, ErrorResponse>> { | ||
try { | ||
await this._ensureLogin() | ||
const response = await fetch( | ||
`${this.config.baseUrl}/accounts/${params.fiatAccountSchemaName}`, | ||
{ | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
...this._getAuthHeader(), | ||
}, | ||
body: JSON.stringify(params.data), | ||
}, | ||
) | ||
const data = await response.json() | ||
if (!response.ok) { | ||
return Err(data) | ||
} | ||
return Ok(data) | ||
} catch (error) { | ||
return handleError(error) | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3332-get-accounts | ||
*/ | ||
async getFiatAccounts(): Promise< | ||
Result<GetFiatAccountsResponse, ErrorResponse> | ||
> { | ||
try { | ||
await this._ensureLogin() | ||
const response = await fetch(`${this.config.baseUrl}/accounts`, { | ||
method: 'GET', | ||
headers: this._getAuthHeader(), | ||
}) | ||
const data = await response.json() | ||
if (!response.ok) { | ||
return Err(data) | ||
} | ||
return Ok(data) | ||
} catch (error) { | ||
return handleError(error) | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3333-delete-accountfiataccountid | ||
*/ | ||
async deleteFiatAccount( | ||
params: DeleteFiatAccountRequestParams, | ||
): Promise<Result<void, ErrorResponse>> { | ||
try { | ||
await this._ensureLogin() | ||
const response = await fetch( | ||
`${this.config.baseUrl}/accounts/${params.fiatAccountId}`, | ||
{ | ||
method: 'DELETE', | ||
headers: this._getAuthHeader(), | ||
}, | ||
) | ||
const data = await response.json() | ||
if (!response.ok) { | ||
return Err(data) | ||
} | ||
return Ok(undefined) | ||
} catch (error) { | ||
return handleError(error) | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3341-post-transferin | ||
*/ | ||
async transferIn( | ||
params: TransferRequestParams, | ||
): Promise<Result<TransferResponse, ErrorResponse>> { | ||
try { | ||
await this._ensureLogin() | ||
const response = await fetch(`${this.config.baseUrl}/transfer/in`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'Idempotency-Key': params.idempotencyKey, | ||
...this._getAuthHeader(), | ||
}, | ||
body: JSON.stringify(params.data), | ||
}) | ||
const data = await response.json() | ||
if (!response.ok) { | ||
return Err(data) | ||
} | ||
return Ok(data) | ||
} catch (error) { | ||
return handleError(error) | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3342-post-transferout | ||
*/ | ||
async transferOut( | ||
params: TransferRequestParams, | ||
): Promise<Result<TransferResponse, ErrorResponse>> { | ||
try { | ||
await this._ensureLogin() | ||
const response = await fetch(`${this.config.baseUrl}/transfer/out`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
'Idempotency-Key': params.idempotencyKey, | ||
...this._getAuthHeader(), | ||
}, | ||
body: JSON.stringify(params.data), | ||
}) | ||
const data = await response.json() | ||
if (!response.ok) { | ||
return Err(data) | ||
} | ||
return Ok(data) | ||
} catch (error) { | ||
return handleError(error) | ||
} | ||
} | ||
/** | ||
* https://github.com/fiatconnect/specification/blob/main/fiatconnect-api.md#3343-get-transfertransferidstatus | ||
*/ | ||
async getTransferStatus( | ||
params: TransferStatusRequestParams, | ||
): Promise<Result<TransferStatusResponse, ErrorResponse>> { | ||
try { | ||
await this._ensureLogin() | ||
const response = await fetch( | ||
`${this.config.baseUrl}/transfer/${params.transferId}/status`, | ||
{ | ||
method: 'GET', | ||
headers: this._getAuthHeader(), | ||
}, | ||
) | ||
const data = await response.json() | ||
if (!response.ok) { | ||
return Err(data) | ||
} | ||
return Ok(data) | ||
} catch (error) { | ||
return handleError(error) | ||
} | ||
} | ||
} | ||
function handleError(error: unknown): Err<ErrorResponse> { | ||
if (error instanceof Error) { | ||
return Err({ ...error, error: error.message }) | ||
} | ||
return Err({ error: String(error) }) | ||
} | ||
export { FiatConnectClient } from './fiat-connect-client' | ||
export * from './types' |
@@ -24,5 +24,7 @@ import { | ||
export interface FiatConectApiClient { | ||
export interface FiatConnectApiClient { | ||
getClockDiffApprox(): Promise<Result<ClockDiffResult, ErrorResponse>> | ||
getClock(): Promise<Result<ClockResponse, ErrorResponse>> | ||
login(): Promise<Result<'success', ErrorResponse>> | ||
isLoggedIn(): boolean | ||
getQuoteIn( | ||
@@ -29,0 +31,0 @@ params: QuoteRequestQuery, |
@@ -1,2 +0,2 @@ | ||
import FiatConnectClient from '../src/index' | ||
import { FiatConnectClient } from '../src/index' | ||
import { | ||
@@ -232,9 +232,21 @@ mockAddFiatAccountResponse, | ||
}) | ||
describe('isLoggedIn', () => { | ||
it('returns false when sessionExpiry does not exist', () => { | ||
expect(client.isLoggedIn()).toBeFalsy() | ||
}) | ||
it('returns false when sessionExpiry exists but is too old', () => { | ||
client._sessionExpiry = new Date('2022-05-01T00:00:00+0000') | ||
const mockNow = new Date('2022-05-02T00:00:00+0000').getTime() | ||
jest.spyOn(global.Date, 'now').mockReturnValueOnce(mockNow) | ||
expect(client.isLoggedIn()).toBeFalsy() | ||
}) | ||
it('returns true when sessionExpiry exists and is not too old', () => { | ||
client._sessionExpiry = new Date('2022-05-03T00:00:00+0000') | ||
const mockNow = new Date('2022-05-02T00:00:00+0000').getTime() | ||
jest.spyOn(global.Date, 'now').mockReturnValueOnce(mockNow) | ||
expect(client.isLoggedIn()).toBeTruthy() | ||
}) | ||
}) | ||
describe('_ensureLogin', () => { | ||
const mockLogin = jest.spyOn(client, 'login') | ||
it('calls login and returns successfully if sessionExpiry is not set', async () => { | ||
mockLogin.mockResolvedValueOnce(Ok('success')) | ||
await client._ensureLogin() | ||
expect(mockLogin).toHaveBeenCalledTimes(1) | ||
}) | ||
it('calls login and returns successfully if sessionExpiry is in the past', async () => { | ||
@@ -241,0 +253,0 @@ client._sessionExpiry = new Date('2022-04-30T23:00:00Z') |
@@ -19,2 +19,3 @@ { | ||
"emitDecoratorMetadata": true, | ||
"importHelpers": true, | ||
"experimentalDecorators": true | ||
@@ -25,2 +26,1 @@ }, | ||
} | ||
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
89240
26
2011
7
19
+ Addedtslib@^2.4.0
+ Addedtslib@2.8.1(transitive)