Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement advanced coinbase trading #725

Merged
merged 13 commits into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
tradePlugin,
tokenContractPlugin,
webhookPlugin,
advancedTradePlugin,
} from "@ai16z/plugin-coinbase";
import { confluxPlugin } from "@ai16z/plugin-conflux";
import { imageGenerationPlugin } from "@ai16z/plugin-image-generation";
Expand Down Expand Up @@ -400,7 +401,7 @@ export function createAgent(
: null,
...(getSecret(character, "COINBASE_API_KEY") &&
getSecret(character, "COINBASE_PRIVATE_KEY")
? [coinbaseMassPaymentsPlugin, tradePlugin, tokenContractPlugin]
? [coinbaseMassPaymentsPlugin, tradePlugin, tokenContractPlugin, advancedTradePlugin]
: []),
getSecret(character, "COINBASE_API_KEY") &&
getSecret(character, "COINBASE_PRIVATE_KEY") &&
Expand Down
1 change: 0 additions & 1 deletion packages/plugin-coinbase/advanced-sdk-ts
Submodule advanced-sdk-ts deleted from 1fba32
21 changes: 21 additions & 0 deletions packages/plugin-coinbase/advanced-sdk-ts/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'plugin:prettier/recommended',
],
plugins: ['prettier'],
rules: {
'prettier/prettier': 'error',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
ignorePatterns: ['**/dist/**', '**/node_modules/**', '**/*.md'],
env: {
node: true, // Add this line to recognize Node.js globals
es2021: true, // Optionally include modern JavaScript features
},
};
6 changes: 6 additions & 0 deletions packages/plugin-coinbase/advanced-sdk-ts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.env
src/rest/main.ts
dist/
node_modules/
.idea/
package-lock.json
5 changes: 5 additions & 0 deletions packages/plugin-coinbase/advanced-sdk-ts/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"semi": true,
"singleQuote": true,
"trailingComma": "es5"
}
9 changes: 9 additions & 0 deletions packages/plugin-coinbase/advanced-sdk-ts/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Changelog

## [0.1.0] - 2024-SEP-06

### Added

- Support for all Coinbase Advanced API REST endpoints via central client
- Custom Request and Response objects for endpoints
- Custom error types
126 changes: 126 additions & 0 deletions packages/plugin-coinbase/advanced-sdk-ts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Coinbase Advanced API TypeScript SDK

Welcome to the Coinbase Advanced API TypeScript SDK. This TypeScript project was created to allow developers to easily plug into the [Coinbase Advanced API](https://docs.cdp.coinbase.com/advanced-trade/docs/welcome).

Coinbase Advanced Trade offers a comprehensive API for traders, providing access to real-time market data, order management, and execution. Elevate your trading strategies and develop sophisticated solutions using our powerful tools and features.

For more information on all the available REST endpoints, see the [API Reference](https://docs.cdp.coinbase.com/advanced-trade/reference/).

---

## Installation

```bash
npm install
```

---

## Build and Use

To build the project, run the following command:

```bash
npm run build
```

_Note: To avoid potential issues, do not forget to build your project again after making any changes to it._

After building the project, each `.ts` file will have its `.js` counterpart generated.

To run a file, use the following command:

```
node dist/{INSERT-FILENAME}.js
```

For example, a `main.ts` file would be run like:

```bash
node dist/main.js
```

---

## Coinbase Developer Platform (CDP) API Keys

This SDK uses Cloud Developer Platform (CDP) API keys. To use this SDK, you will need to create a CDP API key and secret by following the instructions [here](https://docs.cdp.coinbase.com/advanced-trade/docs/getting-started).
Make sure to save your API key and secret in a safe place. You will not be able to retrieve your secret again.

---

## Importing the RESTClient

All the REST endpoints are available directly from the client, therefore it's all you need to import.

```
import { RESTClient } from './rest';
```

---

## Authentication

Authentication of CDP API Keys is handled automatically by the SDK when making a REST request.

After creating your CDP API keys, store them using your desired method and simply pass them into the client during initialization like:

```
const client = new RESTClient(API_KEY, API_SECRET);
```

---

## Making Requests

Here are a few examples requests:

**[List Accounts](https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getaccounts)**

```
client
.listAccounts({})
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error.message);
});
```

**[Get Product](https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getproduct)**

```
client
.getProduct({productId: "BTC-USD"})
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error.message);
});
```

**[Create Order](https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_postorder)**

_$10 Market Buy on BTC-USD_

```
client
.createOrder({
clientOrderId: "00000001",
productId: "BTC-USD",
side: OrderSide.BUY,
orderConfiguration:{
market_market_ioc: {
quote_size: "10"
}
}
})
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error.message);
});
```
34 changes: 34 additions & 0 deletions packages/plugin-coinbase/advanced-sdk-ts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "@coinbase-samples/advanced-sdk-ts",
"version": "0.1.0",
"main": "dist/main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc",
"lint": "eslint . --ext .js,.ts",
"format": "prettier --write \"**/*.{js,ts,tsx,json,css,md}\""
},
"files": [
"dist/"
],
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"jsonwebtoken": "^9.0.2",
"node-fetch": "^2.6.1"
},
"devDependencies": {
"@types/jsonwebtoken": "^9.0.7",
"@types/node-fetch": "^2.6.11",
"@typescript-eslint/eslint-plugin": "^5.59.0",
"@typescript-eslint/parser": "^5.59.0",
"dotenv": "^16.4.5",
"eslint": "^8.35.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"prettier": "^2.8.8",
"typescript": "^5.5.4"
}
}
6 changes: 6 additions & 0 deletions packages/plugin-coinbase/advanced-sdk-ts/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const BASE_URL = 'api.coinbase.com';
export const API_PREFIX = '/api/v3/brokerage';
export const ALGORITHM = 'ES256';
export const VERSION = '0.1.0';
export const USER_AGENT = `coinbase-advanced-ts/${VERSION}`;
export const JWT_ISSUER = 'cdp';
31 changes: 31 additions & 0 deletions packages/plugin-coinbase/advanced-sdk-ts/src/jwt-generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import jwt from 'jsonwebtoken';
import { BASE_URL, ALGORITHM, JWT_ISSUER } from './constants';
import crypto from 'crypto';

export function generateToken(
requestMethod: string,
requestPath: string,
apiKey: string,
apiSecret: string
): string {
const uri = `${requestMethod} ${BASE_URL}${requestPath}`;
const payload = {
iss: JWT_ISSUER,
nbf: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 120,
sub: apiKey,
uri,
};

const header = {
alg: ALGORITHM,
kid: apiKey,
nonce: crypto.randomBytes(16).toString('hex'),
};
const options: jwt.SignOptions = {
algorithm: ALGORITHM as jwt.Algorithm,
header: header,
};

return jwt.sign(payload, apiSecret as string, options);
}
36 changes: 36 additions & 0 deletions packages/plugin-coinbase/advanced-sdk-ts/src/rest/accounts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { API_PREFIX } from '../constants';
import { RESTBase } from './rest-base';
import {
GetAccountRequest,
GetAccountResponse,
ListAccountsRequest,
ListAccountsResponse,
} from './types/accounts-types';
import { method } from './types/request-types';

// [GET] Get Account
// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getaccount
export function getAccount(
this: RESTBase,
{ accountUuid }: GetAccountRequest
): Promise<GetAccountResponse> {
return this.request({
method: method.GET,
endpoint: `${API_PREFIX}/accounts/${accountUuid}`,
isPublic: false,
});
}

// [GET] List Accounts
// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getaccounts
export function listAccounts(
this: RESTBase,
requestParams: ListAccountsRequest
): Promise<ListAccountsResponse> {
return this.request({
method: method.GET,
endpoint: `${API_PREFIX}/accounts`,
queryParams: requestParams,
isPublic: false,
});
}
53 changes: 53 additions & 0 deletions packages/plugin-coinbase/advanced-sdk-ts/src/rest/converts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { API_PREFIX } from '../constants';
import { RESTBase } from './rest-base';
import {
CommitConvertTradeRequest,
CommitConvertTradeResponse,
CreateConvertQuoteRequest,
CreateConvertQuoteResponse,
GetConvertTradeRequest,
GetConvertTradeResponse,
} from './types/converts-types';
import { method } from './types/request-types';

// [POST] Create Convert Quote
// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_createconvertquote
export function createConvertQuote(
this: RESTBase,
requestParams: CreateConvertQuoteRequest
): Promise<CreateConvertQuoteResponse> {
return this.request({
method: method.POST,
endpoint: `${API_PREFIX}/convert/quote`,
bodyParams: requestParams,
isPublic: false,
});
}

// [GET] Get Convert Trade
// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getconverttrade
export function getConvertTrade(
this: RESTBase,
{ tradeId, ...requestParams }: GetConvertTradeRequest
): Promise<GetConvertTradeResponse> {
return this.request({
method: method.GET,
endpoint: `${API_PREFIX}/convert/trade/${tradeId}`,
queryParams: requestParams,
isPublic: false,
});
}

// [POST] Commit Connvert Trade
// https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_commitconverttrade
export function commitConvertTrade(
this: RESTBase,
{ tradeId, ...requestParams }: CommitConvertTradeRequest
): Promise<CommitConvertTradeResponse> {
return this.request({
method: method.POST,
endpoint: `${API_PREFIX}/convert/trade/${tradeId}`,
bodyParams: requestParams,
isPublic: false,
});
}
17 changes: 17 additions & 0 deletions packages/plugin-coinbase/advanced-sdk-ts/src/rest/dataAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { API_PREFIX } from '../constants';
import { RESTBase } from './rest-base';

import { method } from './types/request-types';
import { GetAPIKeyPermissionsResponse } from './types/dataAPI-types';

// [GET] Get API Key Permissions
// Official Documentation: https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getapikeypermissions
export function getAPIKeyPermissions(
this: RESTBase
): Promise<GetAPIKeyPermissionsResponse> {
return this.request({
method: method.GET,
endpoint: `${API_PREFIX}/key_permissions`,
isPublic: false,
});
}
Loading
Loading