From 9ddd4900f2b895266b510902752ecb2569812b78 Mon Sep 17 00:00:00 2001 From: Tiago Siebler Date: Mon, 16 Sep 2024 14:04:53 +0100 Subject: [PATCH 1/4] feat(): add injectable headers, add per-client auth split --- src/index.ts | 1 + src/lib/BaseRestClient.ts | 85 ++++++++++++++++++++++++--------------- 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/src/index.ts b/src/index.ts index 1d35095..c982273 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ export * from './CBAdvancedTradeClient.js'; export * from './CBAppClient.js'; +export * from './CBCommerceClient.js'; export * from './CBExchangeClient.js'; export * from './CBInternationalClient.js'; export * from './CBPrimeClient.js'; diff --git a/src/lib/BaseRestClient.ts b/src/lib/BaseRestClient.ts index 58af6c2..5d30e28 100644 --- a/src/lib/BaseRestClient.ts +++ b/src/lib/BaseRestClient.ts @@ -17,6 +17,7 @@ import { APIIDPrefix, getRestBaseUrl, logInvalidOrderId, + REST_CLIENT_TYPE_ENUM, RestClientOptions, RestClientType, serializeParams, @@ -44,12 +45,16 @@ interface UnsignedRequest { type SignMethod = 'coinbase'; /** - * Some requests require some params to be in the query string and some in the body. - * This type anticipates both are possible in any combination. + * Some requests require some params to be in the query string, some in the body, some even in the headers. + * This type anticipates either are possible in any combination. * * The request builder will automatically handle where parameters should go. */ -type ParamsInQueryAndOrBody = { query?: object; body?: object }; +type ParamsInRequest = { + query?: object; + body?: object; + headers?: object; +}; const ENABLE_HTTP_TRACE = typeof process === 'object' && @@ -203,7 +208,7 @@ export abstract class BaseRestClient { return this._call('GET', endpoint, params, true); } - post(endpoint: string, params?: ParamsInQueryAndOrBody) { + post(endpoint: string, params?: ParamsInRequest) { return this._call('POST', endpoint, params, true); } @@ -211,19 +216,19 @@ export abstract class BaseRestClient { return this._call('GET', endpoint, params, false); } - postPrivate(endpoint: string, params?: ParamsInQueryAndOrBody) { + postPrivate(endpoint: string, params?: ParamsInRequest) { return this._call('POST', endpoint, params, false); } - deletePrivate(endpoint: string, params?: ParamsInQueryAndOrBody) { + deletePrivate(endpoint: string, params?: ParamsInRequest) { return this._call('DELETE', endpoint, params, false); } - putPrivate(endpoint: string, params?: ParamsInQueryAndOrBody) { + putPrivate(endpoint: string, params?: ParamsInRequest) { return this._call('PUT', endpoint, params, false); } - patchPrivate(endpoint: string, params?: ParamsInQueryAndOrBody) { + patchPrivate(endpoint: string, params?: ParamsInRequest) { return this._call('PATCH', endpoint, params, false); } @@ -233,7 +238,7 @@ export abstract class BaseRestClient { private async _call( method: Method, endpoint: string, - params?: ParamsInQueryAndOrBody, + params?: ParamsInRequest, isPublicApi?: boolean, ): Promise { // Sanity check to make sure it's only ever prefixed by one forward slash @@ -354,7 +359,7 @@ export abstract class BaseRestClient { /** * @private sign request and set recv window */ - private async signRequest( + private async signRequest( data: T, url: string, _endpoint: string, @@ -389,19 +394,38 @@ export abstract class BaseRestClient { : ''; if (signMethod === 'coinbase') { - const signRequestParams = - method === 'GET' - ? serializeParams( - data?.query || data, - strictParamValidation, - encodeQueryStringValues, - '?', - ) - : JSON.stringify(data?.body || data) || ''; - - res.sign = signJWT(url, method, 'ES256', apiKey, apiSecret); - res.queryParamsWithSign = signRequestParams; - return res; + const clientType = this.getClientType(); + + switch (clientType) { + case REST_CLIENT_TYPE_ENUM.advancedTrade: { + const signRequestParams = + method === 'GET' + ? serializeParams( + data?.query || data, + strictParamValidation, + encodeQueryStringValues, + '?', + ) + : JSON.stringify(data?.body || data) || ''; + + res.sign = signJWT(url, method, 'ES256', apiKey, apiSecret); + res.queryParamsWithSign = signRequestParams; + return res; + } + case REST_CLIENT_TYPE_ENUM.coinbaseApp: { + // tODO: + return res; + } + default: { + console.error( + new Date(), + neverGuard( + clientType, + `Unhandled sign client type : "${clientType}"`, + ), + ); + } + } } console.error( @@ -467,6 +491,7 @@ export abstract class BaseRestClient { deleteUndefinedValues(params); deleteUndefinedValues(params?.body); deleteUndefinedValues(params?.query); + deleteUndefinedValues(params?.headers); if (isPublicApi || !this.apiKeyName || !this.apiKeySecret) { return { @@ -484,8 +509,10 @@ export abstract class BaseRestClient { isPublicApi, ); - const authHeaders = { + const requestHeaders = { Authorization: `Bearer ${signResult.sign}`, + ...params.headers, + ...options.headers, }; const urlWithQueryParams = @@ -494,20 +521,14 @@ export abstract class BaseRestClient { if (method === 'GET' || !params?.body) { return { ...options, - headers: { - ...authHeaders, - ...options.headers, - }, + headers: requestHeaders, url: urlWithQueryParams, }; } return { ...options, - headers: { - ...authHeaders, - ...options.headers, - }, + headers: requestHeaders, url: params?.query ? urlWithQueryParams : options.url, data: signResult.originalParams.body, }; From 5cba2415d215011d776a43f6fc5144b82bebaf5e Mon Sep 17 00:00:00 2001 From: Tiago Siebler Date: Tue, 17 Sep 2024 11:24:06 +0100 Subject: [PATCH 2/4] feat(): work on per-client auth variation --- .gitignore | 2 + src/WebsocketClient.ts | 8 ++-- src/lib/BaseRestClient.ts | 89 ++++++++++++++++++++++++++++++--------- src/lib/requestUtils.ts | 4 +- 4 files changed, 77 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 9e3820e..490f098 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ dist coverage ts-adv-trade-test-private.ts +examples/ts-app-priv.ts +examples/ts-commerce.ts diff --git a/src/WebsocketClient.ts b/src/WebsocketClient.ts index 6db2f52..102c291 100644 --- a/src/WebsocketClient.ts +++ b/src/WebsocketClient.ts @@ -57,8 +57,8 @@ export class WebsocketClient extends BaseWebsocketClient { } this.RESTClientCache[clientType] = new CBAdvancedTradeClient({ - apiKeyName: this.options.apiKey, - apiPrivateKey: this.options.apiSecret, + apiKey: this.options.apiKey, + apiSecret: this.options.apiSecret, }); return this.RESTClientCache[clientType]; } @@ -69,8 +69,8 @@ export class WebsocketClient extends BaseWebsocketClient { } this.RESTClientCache[clientType] = new CBAdvancedTradeClient({ - apiKeyName: this.options.apiKey, - apiPrivateKey: this.options.apiSecret, + apiKey: this.options.apiKey, + apiSecret: this.options.apiSecret, }); return this.RESTClientCache[clientType]; throw new Error(`Unhandled WsKey: "${wsKey}"`); diff --git a/src/lib/BaseRestClient.ts b/src/lib/BaseRestClient.ts index 5d30e28..9f255b4 100644 --- a/src/lib/BaseRestClient.ts +++ b/src/lib/BaseRestClient.ts @@ -35,6 +35,7 @@ interface SignedRequest { queryParamsWithSign: string; timestamp: number; recvWindow: number; + headers: object; } interface UnsignedRequest { @@ -179,8 +180,8 @@ export abstract class BaseRestClient { this.getClientType(), ); - this.apiKeyName = this.options.apiKeyName; - this.apiKeySecret = this.options.apiPrivateKey; + this.apiKeyName = this.options.apiKey; + this.apiKeySecret = this.options.apiSecret; if (restClientOptions.cdpApiKey) { this.apiKeyName = restClientOptions.cdpApiKey.name; @@ -377,6 +378,7 @@ export abstract class BaseRestClient { recvWindow: 0, serializedParams: '', queryParamsWithSign: '', + headers: {}, }; const apiKey = this.apiKeyName; @@ -396,24 +398,65 @@ export abstract class BaseRestClient { if (signMethod === 'coinbase') { const clientType = this.getClientType(); + const signRequestParams = + method === 'GET' + ? serializeParams( + data?.query || data, + strictParamValidation, + encodeQueryStringValues, + '?', + ) + : JSON.stringify(data?.body || data) || ''; + + // https://docs.cdp.coinbase.com/product-apis/docs/welcome switch (clientType) { - case REST_CLIENT_TYPE_ENUM.advancedTrade: { - const signRequestParams = - method === 'GET' - ? serializeParams( - data?.query || data, - strictParamValidation, - encodeQueryStringValues, - '?', - ) - : JSON.stringify(data?.body || data) || ''; - - res.sign = signJWT(url, method, 'ES256', apiKey, apiSecret); - res.queryParamsWithSign = signRequestParams; + case REST_CLIENT_TYPE_ENUM.advancedTrade: + case REST_CLIENT_TYPE_ENUM.coinbaseApp: { + // Both adv trade & app API use the same JWT auth mechanism + // Advanced Trade: https://docs.cdp.coinbase.com/advanced-trade/docs/rest-api-auth + // App: https://docs.cdp.coinbase.com/coinbase-app/docs/api-key-authentication + const sign = signJWT(url, method, 'ES256', apiKey, apiSecret); + return { + ...res, + sign: sign, + queryParamsWithSign: signRequestParams, + headers: { + Authorization: `Bearer ${sign}`, + }, + }; + + // TODO: is there demand for oauth support? + // Docs: https://docs.cdp.coinbase.com/coinbase-app/docs/coinbase-app-integration + // See: https://github.com/tiagosiebler/coinbase-api/issues/24 + } + case REST_CLIENT_TYPE_ENUM.exchange: { + // TODO: hmac + // Docs: https://docs.cdp.coinbase.com/exchange/docs/rest-auth + const headers = { + 'CB-ACCESS-KEY': apiKey, + 'CB-ACCESS-SIGN': 'sign TODO:', + 'CB-ACCESS-TIMESTAMP': 'TODO:', + 'CB-ACCESS-PASSPHRASE': 'TODO:', + }; + + // TODO: is there demand for FIX + // Docs, FIX: https://docs.cdp.coinbase.com/exchange/docs/fix-connectivity return res; } - case REST_CLIENT_TYPE_ENUM.coinbaseApp: { - // tODO: + case REST_CLIENT_TYPE_ENUM.international: { + // TODO: hmac + // Docs: https://docs.cdp.coinbase.com/intx/docs/rest-auth + // TODO: is there demand for FIX + // Docs, FIX: https://docs.cdp.coinbase.com/intx/docs/fix-overview + return res; + } + case REST_CLIENT_TYPE_ENUM.prime: { + // Docs: https://docs.cdp.coinbase.com/prime/docs/rest-authentication + // TODO: is there demand for FIX + // Docs, FIX: https://docs.cdp.coinbase.com/prime/docs/fix-connectivity + return res; + } + case REST_CLIENT_TYPE_ENUM.commerce: { return res; } default: { @@ -424,6 +467,9 @@ export abstract class BaseRestClient { `Unhandled sign client type : "${clientType}"`, ), ); + throw new Error( + `Unhandled request sign for client : "${clientType}"`, + ); } } } @@ -479,7 +525,7 @@ export abstract class BaseRestClient { method: Method, endpoint: string, url: string, - params?: any, + params?: any | undefined, isPublicApi?: boolean, ): Promise { const options: AxiosRequestConfig = { @@ -510,8 +556,11 @@ export abstract class BaseRestClient { ); const requestHeaders = { - Authorization: `Bearer ${signResult.sign}`, - ...params.headers, + // request parameter headers for this request + ...params?.headers, + // auth headers for this request + ...signResult.headers, + // global headers for every request ...options.headers, }; diff --git a/src/lib/requestUtils.ts b/src/lib/requestUtils.ts index 45a5a6b..5140879 100644 --- a/src/lib/requestUtils.ts +++ b/src/lib/requestUtils.ts @@ -33,10 +33,10 @@ const exchangeBaseURLMap = { export interface RestClientOptions { /** Your API key name */ - apiKeyName?: string; + apiKey?: string; /** Your API Private Key */ - apiPrivateKey?: string; + apiSecret?: string; /** * Instead of passing the key name and private key, From df85a1c93acfe304edf465949ba492438309dd51 Mon Sep 17 00:00:00 2001 From: Tiago Siebler Date: Tue, 17 Sep 2024 12:49:40 +0100 Subject: [PATCH 3/4] feat(): flesh out hmac auth --- src/lib/BaseRestClient.ts | 165 +++++++++++++++++++++++++++++--------- src/lib/jwtNode.ts | 30 ++++--- src/lib/requestUtils.ts | 31 ++++++- 3 files changed, 175 insertions(+), 51 deletions(-) diff --git a/src/lib/BaseRestClient.ts b/src/lib/BaseRestClient.ts index 9f255b4..336b624 100644 --- a/src/lib/BaseRestClient.ts +++ b/src/lib/BaseRestClient.ts @@ -119,8 +119,9 @@ export abstract class BaseRestClient { private options: RestClientOptions; private baseUrl: string; private globalRequestOptions: AxiosRequestConfig; - private apiKeyName: string | undefined; - private apiKeySecret: string | undefined; + private apiKey: string | undefined; + private apiSecret: string | undefined; + private apiPassphrase: string | undefined; /** Defines the client type (affecting how requests & signatures behave) */ abstract getClientType(): RestClientType; @@ -141,7 +142,7 @@ export abstract class BaseRestClient { }; const VERSION = '0.1.0'; - const USER_AGENT = `coinbase-api-node/${VERSION}`; + const USER_AGENT = `${APIIDPrefix}/${VERSION}`; this.globalRequestOptions = { /** in ms == 5 minutes by default */ @@ -180,17 +181,21 @@ export abstract class BaseRestClient { this.getClientType(), ); - this.apiKeyName = this.options.apiKey; - this.apiKeySecret = this.options.apiSecret; + this.apiKey = this.options.apiKey; + this.apiSecret = this.options.apiSecret; + this.apiPassphrase = this.options.apiPassphrase; if (restClientOptions.cdpApiKey) { - this.apiKeyName = restClientOptions.cdpApiKey.name; - this.apiKeySecret = restClientOptions.cdpApiKey.privateKey; + this.apiKey = restClientOptions.cdpApiKey.name; + this.apiSecret = restClientOptions.cdpApiKey.privateKey; } - // Throw if one of the 3 values is missing, but at least one of them is set - const credentials = [this.apiKeyName, this.apiKeySecret]; + // Throw if one of these values is missing, and at least one of them is set + + const credentials = [this.apiKey, this.apiSecret]; if ( + // commerce only needs keys, not key and secret + this.getClientType() !== REST_CLIENT_TYPE_ENUM.commerce && credentials.includes(undefined) && credentials.some((v) => typeof v === 'string') ) { @@ -363,36 +368,38 @@ export abstract class BaseRestClient { private async signRequest( data: T, url: string, - _endpoint: string, + endpoint: string, method: Method, signMethod: SignMethod, ): Promise> { - const timestamp = this.getSignTimestampMs(); + const timestampInMs = this.getSignTimestampMs(); const res: SignedRequest = { originalParams: { ...data, }, sign: '', - timestamp, + timestamp: timestampInMs, recvWindow: 0, serializedParams: '', queryParamsWithSign: '', headers: {}, }; - const apiKey = this.apiKeyName; - const apiSecret = this.apiKeySecret; + const apiKey = this.apiKey; + const apiSecret = this.apiSecret; + const jwtExpiresSeconds = this.options.jwtExpiresSeconds || 120; - if (!apiKey || !apiSecret) { + if (!apiKey) { return res; } const strictParamValidation = this.options.strictParamValidation; const encodeQueryStringValues = true; - const requestBodyToSign = res.originalParams?.body - ? JSON.stringify(res.originalParams?.body) + const requestBody = data?.body || data; + const requestBodyString = requestBody + ? JSON.stringify(data?.body || data) : ''; if (signMethod === 'coinbase') { @@ -406,7 +413,7 @@ export abstract class BaseRestClient { encodeQueryStringValues, '?', ) - : JSON.stringify(data?.body || data) || ''; + : requestBodyString; // https://docs.cdp.coinbase.com/product-apis/docs/welcome switch (clientType) { @@ -415,7 +422,21 @@ export abstract class BaseRestClient { // Both adv trade & app API use the same JWT auth mechanism // Advanced Trade: https://docs.cdp.coinbase.com/advanced-trade/docs/rest-api-auth // App: https://docs.cdp.coinbase.com/coinbase-app/docs/api-key-authentication - const sign = signJWT(url, method, 'ES256', apiKey, apiSecret); + + if (!apiSecret) { + throw new Error(`No API secret provided, cannot prepare JWT.`); + } + + const sign = signJWT({ + url, + method, + algorithm: 'ES256', + timestampMs: timestampInMs, + jwtExpiresSeconds, + apiPubKey: apiKey, + apiPrivKey: apiSecret, + }); + return { ...res, sign: sign, @@ -429,35 +450,105 @@ export abstract class BaseRestClient { // Docs: https://docs.cdp.coinbase.com/coinbase-app/docs/coinbase-app-integration // See: https://github.com/tiagosiebler/coinbase-api/issues/24 } + case REST_CLIENT_TYPE_ENUM.exchange: { - // TODO: hmac // Docs: https://docs.cdp.coinbase.com/exchange/docs/rest-auth + const timestampInSeconds = timestampInMs / 1000; + + const signInput = + timestampInSeconds + method + endpoint + requestBodyString; + + if (!apiSecret) { + throw new Error(`No API secret provided, cannot sign request.`); + } + + if (!this.apiPassphrase) { + throw new Error(`No API passphrase provided, cannot sign request.`); + } + + const sign = await signMessage( + signInput, + apiSecret, + 'base64', + 'SHA-256', + ); + const headers = { + 'CB-ACCESS-SIGN': sign, + 'CB-ACCESS-TIMESTAMP': timestampInSeconds, + 'CB-ACCESS-PASSPHRASE': this.apiPassphrase, 'CB-ACCESS-KEY': apiKey, - 'CB-ACCESS-SIGN': 'sign TODO:', - 'CB-ACCESS-TIMESTAMP': 'TODO:', - 'CB-ACCESS-PASSPHRASE': 'TODO:', + }; + + return { + ...res, + sign: sign, + queryParamsWithSign: signRequestParams, + headers: { + ...headers, + }, }; // TODO: is there demand for FIX // Docs, FIX: https://docs.cdp.coinbase.com/exchange/docs/fix-connectivity - return res; - } - case REST_CLIENT_TYPE_ENUM.international: { - // TODO: hmac - // Docs: https://docs.cdp.coinbase.com/intx/docs/rest-auth - // TODO: is there demand for FIX - // Docs, FIX: https://docs.cdp.coinbase.com/intx/docs/fix-overview - return res; } + + // Docs: https://docs.cdp.coinbase.com/intx/docs/rest-auth + case REST_CLIENT_TYPE_ENUM.international: + + // Docs: https://docs.cdp.coinbase.com/prime/docs/rest-authentication case REST_CLIENT_TYPE_ENUM.prime: { - // Docs: https://docs.cdp.coinbase.com/prime/docs/rest-authentication - // TODO: is there demand for FIX + const timestampInSeconds = String(Math.floor(timestampInMs / 1000)); + + const signInput = + timestampInSeconds + method + endpoint + requestBodyString; + + if (!apiSecret) { + throw new Error(`No API secret provided, cannot sign request.`); + } + + if (!this.apiPassphrase) { + throw new Error(`No API passphrase provided, cannot sign request.`); + } + + const sign = await signMessage( + signInput, + apiSecret, + 'base64', + 'SHA-256', + ); + + const headers = { + 'CB-ACCESS-TIMESTAMP': timestampInSeconds, + 'CB-ACCESS-SIGN': sign, + 'CB-ACCESS-PASSPHRASE': this.apiPassphrase, + 'CB-ACCESS-KEY': apiKey, + }; + + return { + ...res, + sign: sign, + queryParamsWithSign: signRequestParams, + headers: { + ...headers, + }, + }; + + // For CB International, is there demand for FIX + // Docs, FIX: https://docs.cdp.coinbase.com/intx/docs/fix-overview + + // For CB Prime, is there demand for FIX // Docs, FIX: https://docs.cdp.coinbase.com/prime/docs/fix-connectivity - return res; } case REST_CLIENT_TYPE_ENUM.commerce: { - return res; + // https://docs.cdp.coinbase.com/commerce-onchain/docs/getting-started + // No auth? + return { + ...res, + headers: { + 'X-CC-Api-Key': apiKey, + }, + }; } default: { console.error( @@ -513,7 +604,7 @@ export abstract class BaseRestClient { }; } - if (!this.apiKeyName || !this.apiKeySecret) { + if (!this.apiKey || !this.apiSecret) { throw new Error(MISSING_API_KEYS_ERROR); } @@ -539,7 +630,7 @@ export abstract class BaseRestClient { deleteUndefinedValues(params?.query); deleteUndefinedValues(params?.headers); - if (isPublicApi || !this.apiKeyName || !this.apiKeySecret) { + if (isPublicApi || !this.apiKey || !this.apiSecret) { return { ...options, params: params, diff --git a/src/lib/jwtNode.ts b/src/lib/jwtNode.ts index 2ad58d5..bd7518a 100644 --- a/src/lib/jwtNode.ts +++ b/src/lib/jwtNode.ts @@ -1,14 +1,24 @@ import jwt from 'jsonwebtoken'; import { nanoid } from 'nanoid'; -export function signJWT( - url: string, - method: string, - algorithm: 'ES256', - apiPubKey: string, - apiPrivKey: string, -): string { - // +export function signJWT(params: { + url: string; + method: string; + algorithm: 'ES256'; + timestampMs: number; + jwtExpiresSeconds: number; + apiPubKey: string; + apiPrivKey: string; +}): string { + const { + url, + method, + algorithm, + timestampMs, + jwtExpiresSeconds, + apiPrivKey, + apiPubKey, + } = params; // Remove https:// but keep the rest const urlWithEndpoint = url.slice(8); @@ -16,8 +26,8 @@ export function signJWT( const payload = { iss: 'cdp', - nbf: Math.floor(Date.now() / 1000), - exp: Math.floor(Date.now() / 1000) + 120, + nbf: Math.floor(timestampMs / 1000), + exp: Math.floor(timestampMs / 1000) + jwtExpiresSeconds, sub: apiPubKey, uri, }; diff --git a/src/lib/requestUtils.ts b/src/lib/requestUtils.ts index 5140879..bdbecfa 100644 --- a/src/lib/requestUtils.ts +++ b/src/lib/requestUtils.ts @@ -32,15 +32,33 @@ const exchangeBaseURLMap = { } as const; export interface RestClientOptions { - /** Your API key name */ + /** + * Your API key name. + * + * - For the Advanced Trade or App APIs, this is your API Key Name. + */ apiKey?: string; - /** Your API Private Key */ + /** + * Your API key secret. + * + * - For the Advanced Trade or App APIs, this is your API private key (including the -----BEGIN EC PRIVATE KEY-----\n etc). + */ apiSecret?: string; /** - * Instead of passing the key name and private key, - * you can also parse the exported "cdp_api_key.json" into an object and pass it here. + * Your API passphrase (NOT your account password). Only used for the API groups that use an API passphrase: + * - Coinbase Exchange API + * - Coinbase International API + * - Coinbase Prime API + */ + apiPassphrase?: string; + + /** + * For the Advanced Trade or App APIs, instead of passing the key name and + * private key, you can also parse the exported "cdp_api_key.json" into an object and pass it here. + * + * It will automatically get parsed into the apiKey & apiSecret configuration parameters. */ cdpApiKey?: { name: string; @@ -80,6 +98,11 @@ export interface RestClientOptions { */ keepAliveMsecs?: number; + /** + * For JWT auth (adv trade & app API), seconds until jwt expires. Defaults to 120 seconds. + */ + jwtExpiresSeconds?: number; + /** Default: false. If true, we'll throw errors if any params are undefined */ strictParamValidation?: boolean; From 82e1cda8d88725add3b75ec37e9c8ccbd6bad0e8 Mon Sep 17 00:00:00 2001 From: Tiago Siebler Date: Tue, 17 Sep 2024 12:50:54 +0100 Subject: [PATCH 4/4] chore(): update examples --- examples/advanced-private-rest.ts | 4 ++-- examples/cb-app-private.ts | 4 ++-- examples/coinbase-international-client-rest.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/advanced-private-rest.ts b/examples/advanced-private-rest.ts index 8ed9c53..1afc411 100644 --- a/examples/advanced-private-rest.ts +++ b/examples/advanced-private-rest.ts @@ -3,8 +3,8 @@ import { CBAdvancedTradeClient } from '../src/index.js'; async function main() { const client = new CBAdvancedTradeClient({ // cdpApiKey: credsTradePermission, - apiKeyName: '', - apiPrivateKey: '', + apiKey: '', + apiSecret: '', }); try { diff --git a/examples/cb-app-private.ts b/examples/cb-app-private.ts index e7ae1d3..91222d0 100644 --- a/examples/cb-app-private.ts +++ b/examples/cb-app-private.ts @@ -3,8 +3,8 @@ import { CBAppClient } from '../src/index.js'; async function main() { const client = new CBAppClient({ // cdpApiKey: credsTradePermission, - apiKeyName: '', - apiPrivateKey: '', + apiKey: '', + apiSecret: '', }); const res = await client.getAccounts(); diff --git a/examples/coinbase-international-client-rest.ts b/examples/coinbase-international-client-rest.ts index affbeb3..cf8a9d8 100644 --- a/examples/coinbase-international-client-rest.ts +++ b/examples/coinbase-international-client-rest.ts @@ -1,6 +1,6 @@ import { CBInternationalClient } from '../src/index.js'; -const coinbaseInternational = new CBInternationalClient({}); +const coinbaseInternational = new CBInternationalClient(); async function main() { try {