Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7569c48
refac: split server file, use axios instead of fetch
Tguntenaar Jan 29, 2025
85025f6
feat: cache individual rpc calls for a week
Tguntenaar Jan 30, 2025
52874ff
feat: 429
Tguntenaar Jan 30, 2025
0566625
add prisma and postgres instance as persistent cache mechanism
Tguntenaar Jan 31, 2025
1ea995e
remove tokens.json
Tguntenaar Jan 31, 2025
ef30b3b
unused imports
Tguntenaar Jan 31, 2025
093fb48
add comment
Tguntenaar Jan 31, 2025
d557c5a
test: add jest + supertest
Tguntenaar Feb 1, 2025
3a5a8ca
change near-price response back to single number
Tguntenaar Feb 3, 2025
d52e404
test: add gh action
Tguntenaar Feb 3, 2025
5730090
feat: Script to compare results with prod, improve all-token-balance-…
Tguntenaar Feb 3, 2025
f043e00
Merge branch 'main' of github.com:NEAR-DevHub/ref-sdk-api into refac/…
Tguntenaar Feb 3, 2025
d3149c8
feat: lots of fallbacks on postgres
Tguntenaar Feb 5, 2025
722e350
feat: add single token balance history
Tguntenaar Feb 5, 2025
af6e98d
switch between archival and normal nodes, interpolate timestamp inste…
Tguntenaar Feb 6, 2025
ce9c0e5
added FASTNEAR_API_KEY and some check on process.env
Tguntenaar Feb 12, 2025
7940c48
added 429 cache to stop calling the RPC, balanceMinimum, fixed sorting
Tguntenaar Feb 12, 2025
612dfca
test: fix mock reject test
Tguntenaar Feb 12, 2025
d80c534
remove unused endpoint
Tguntenaar Feb 12, 2025
9145e0e
test: add dummy api key
Tguntenaar Feb 12, 2025
6dd16f1
update README.md
Tguntenaar Feb 12, 2025
415b15d
feat: only use fastnear, ft_metadata
Tguntenaar Feb 12, 2025
ee68cfe
test: fix the fetch from rpc tests, with a single endpoint
Tguntenaar Feb 13, 2025
8cfac81
remove comments
Tguntenaar Feb 13, 2025
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: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
HOSTNAME=127.0.0.1
PORT=3000
PIKESPEAK_KEY=
DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
FASTNEAR_API_KEY=
NEARBLOCKS_API_KEY=
60 changes: 60 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Run Tests

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest

services:
postgres:
image: postgres:14
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: testdb
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

strategy:
matrix:
node-version: [18.x]

steps:
- uses: actions/checkout@v4

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "npm"

- name: Install dependencies
run: npm ci

- name: Create .env file
run: |
touch .env
echo "PORT=3000" >> .env
echo "HOSTNAME=0.0.0.0" >> .env
echo "DATABASE_URL=postgresql://postgres:postgres@localhost:5432/testdb" >> .env

- name: Generate Prisma Client
run: npx prisma generate

- name: Run database migrations
run: npx prisma migrate deploy

- name: Run tests
run: npm test
env:
NODE_ENV: test
18 changes: 12 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
ARG NODE_VERSION=20.9.0
FROM node:${NODE_VERSION}-slim as base

LABEL fly_launch_runtime="Node.js"
LABEL fly_launch_runtime="Node.js/Prisma"

# Node.js app lives here
# Node.js/Prisma app lives here
WORKDIR /app

# Set production environment
Expand All @@ -18,28 +18,34 @@ FROM base as build

# Install packages needed to build node modules
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3
apt-get install --no-install-recommends -y build-essential node-gyp openssl pkg-config python-is-python3

# Install node modules
COPY package-lock.json package.json ./
RUN npm ci --include=dev

# Generate Prisma Client
COPY prisma .
RUN npx prisma generate

# Copy application code
COPY . .

# Build application
RUN npm run build

# Copy tokens.json to the dist directory
RUN cp src/tokens.json dist/

# Remove development dependencies
RUN npm prune --omit=dev


# Final stage for app image
FROM base

# Install packages needed for deployment
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y openssl && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Copy built application
COPY --from=build /app /app

Expand Down
260 changes: 245 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,260 @@
# NEAR Token Swap API

A RESTful API service for token swaps and metadata on the NEAR blockchain, built with Express.js and TypeScript.
A RESTful API service for token swaps and blockchain metadata on the NEAR network, built with Express.js and TypeScript. It leverages caching, rate limiting, and secure request handling to provide a robust service for interacting with NEAR blockchain data.

---

## Table of Contents

- [Features](#features)
- [Requirements](#requirements)
- [Installation](#installation)
- [Environment Variables](#environment-variables)
- [Running the Server](#running-the-server)
- [API Endpoints](#api-endpoints)
- [Get Token Metadata](#get-token-metadata)
- [Whitelist Tokens](#whitelist-tokens)
- [Token Swap](#token-swap)
- [Get NEAR Price](#get-near-price)
- [Fetch FT Tokens](#fetch-ft-tokens)
- [Get All Token Balance History](#get-all-token-balance-history)
- [Clear Token Balance History](#clear-token-balance-history)
- [Transactions Transfer History](#transactions-transfer-history)
- [Caching & Rate Limiting](#caching--rate-limiting)
- [RPC Requests & Fallback Logic](#rpc-requests--fallback-logic)
- [License](#license)

---

## Features

- Token metadata retrieval
- Whitelist tokens with balances and prices
- Token swap functionality
- Rate limiting
- CORS enabled
- Security headers with Helmet
- **Token Metadata Retrieval:** Get metadata for any token from a pre-defined list.
- **Whitelist Tokens:** Retrieve tokens with associated balances and prices for a given account.
- **Token Swap Functionality:** Execute swap operations with validation and default slippage.
- **NEAR Price Retrieval:** Fetch the NEAR token price via external APIs with a database fallback.
- **FT Tokens Endpoint:** Retrieve fungible token balances with caching.
- **Token Balance History:** Get historical balance data bundled by period.
- **Clear Balance History:** Delete all token balance history entries from the database.
- **Transactions Transfer History:** Retrieve transfer history information from transactions.
- **Rate Limiting & CORS:** Protect endpoints using rate limits and CORS.
- **Security:** Utilize Helmet for secure HTTP headers.
- **RPC Caching:** Use internal caching and fallback mechanisms for RPC requests.

---

## Requirements

- [Node.js](https://nodejs.org/en/) (v14+ recommended)
- [npm](https://www.npmjs.com/) or [yarn](https://yarnpkg.com/)
- A running PostgreSQL database instance

---

## Installation

1. **Clone the repository:**

```bash
git clone https://github.com/your-username/near-token-swap-api.git
cd near-token-swap-api
```

2. **Install dependencies:**

```bash
npm install
```

3. **Setup environment variables:**

Copy the provided `.env.example` file to `.env` and configure the variables accordingly.

```bash
cp .env.example .env
```

4. **Run migrations (if using Prisma):**

```bash
npx prisma migrate dev
```

---

## Environment Variables

Ensure you have a `.env` file with the following variables configured:

```
HOSTNAME=127.0.0.1
PORT=3000
PIKESPEAK_KEY=
DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
FASTNEAR_API_KEY=
NEARBLOCKS_API_KEY=
```

---

## Running the Server

Start the server with:

```bash
npm start
```

By default, the server will run on `http://127.0.0.1:3000` (or as specified by the `HOSTNAME` and `PORT` variables).

---

## API Endpoints

### Get Token Metadata

```http
GET /api/token-metadata
```
- **Endpoint:** `GET /api/token-metadata`
- **Query Parameters:**
- `token` (string, required): The token ID for which to retrieve metadata.
- **Response:** JSON object containing metadata for the token.
- **Example:**

```http
GET /api/token-metadata?token=near
```

---

### Whitelist Tokens

- **Endpoint:** `GET /api/whitelist-tokens`
- **Optional Query Parameter:**
- `account` (string): The NEAR account id to filter tokens.
- **Response:** JSON object containing whitelisted tokens along with balances and prices.
- **Example:**

```http
GET /api/whitelist-tokens?account=example.near
```

---

### Token Swap

- **Endpoint:** `GET /api/swap`
- **Required Query Parameters:**
- `accountId` (string): The account executing the swap.
- `tokenIn` (string): The ID of the token to swap from.
- `tokenOut` (string): The token to swap to.
- `amountIn` (string): The amount of `tokenIn` being swapped.
- **Optional Query Parameter:**
- `slippage` (string): The allowable slippage (default: "0.01" for 1%).
- **Response:** JSON object containing swap details or error information.
- **Example:**

```http
GET /api/swap?accountId=example.near&tokenIn=near&tokenOut=usdt&amountIn=100&slippage=0.02
```

---

### Get NEAR Price

- **Endpoint:** `GET /api/near-price`
- **Description:** Retrieves the current NEAR token price. If external sources fail, it falls back to the latest price stored in the database.
- **Response:** NEAR price as a JSON value.
- **Example:**

```http
GET /api/near-price
```

---

### Fetch FT Tokens

- **Endpoint:** `GET /api/ft-tokens`
- **Query Parameters:**
- `account_id` (string, required): The account id to fetch fungible token information.
- **Response:** JSON object with FT token details.
- **Example:**

```http
GET /api/ft-tokens?account_id=example.near
```

---

### Get All Token Balance History

- **Endpoint:** `GET /api/all-token-balance-history`
- **Query Parameters:**
- `account_id` (string, required): The account id whose token balance history is to be fetched.
- `token_id` (string, required): The token id for which balance history is required.
- `disableCache` (optional): When provided, bypasses the cached result (note: sensitive to frequent requests).
- **Response:** JSON object mapping each period to its corresponding balance history.
- **Example:**

```http
GET /api/all-token-balance-history?account_id=example.near&token_id=near
GET /api/all-token-balance-history?account_id=example.near&token_id=near&disableCache=true
```

---

### Clear Token Balance History

- **Endpoint:** `DELETE /api/clear-token-balance-history`
- **Description:** Removes all `TokenBalanceHistory` records from the database.
- **Response:** JSON message confirming deletion.
- **Example:**

```http
DELETE /api/clear-token-balance-history
```

---

### Transactions Transfer History

- **Endpoint:** `GET /api/transactions-transfer-history`
- **Query Parameters:**
- `treasuryDaoID` (string, required): The treasury DAO ID to filter transfer transactions.
- **Response:** JSON object containing the transfer history data.
- **Example:**

```http
GET /api/transactions-transfer-history?treasuryDaoID=dao.near
```

---

## Caching & Rate Limiting

- **Caching:**
- The API uses [NodeCache](https://www.npmjs.com/package/node-cache) to store short-term responses (e.g., NEAR prices and token balance histories) to reduce external API calls and recomputation.
- RPC calls (in `src/utils/fetch-from-rpc.ts`) also cache responses and skip endpoints temporarily on rate-limited (HTTP 429) responses.

- **Rate Limiting:**
- All `/api/*` endpoints are limited to 180 requests per 30 seconds per IP (or forwarded IP when available).
- This helps protect against abuse and ensures service stability.

---

## RPC Requests & Fallback Logic

- The API makes use of multiple RPC endpoints for querying the NEAR blockchain.
- In `src/utils/fetch-from-rpc.ts`, the request is:
- Cached based on a hash of the request body.
- Attempted sequentially across a list of primary or archival endpoints.
- The response is stored using Prisma if successful.
- In the event of a known error (e.g., non-existent account), the system caches the fact to avoid unnecessary calls.

Retrieve metadata for a specific token.
---

#### Query Parameters
## License

- `token` (string, required): The ID of the token for which metadata is requested.
This project is licensed under the MIT License. See [LICENSE](LICENSE) for more details.

#### Response
---

Returns a JSON object containing the metadata of the specified token.
*For additional questions or contributions, please open an issue or submit a PR on GitHub.*
Loading