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

[MIN-1597] Minswap syncer for query price for v1, v2 and stableswap pool. #40

Merged
merged 29 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
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
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
build
examples
.github
12 changes: 12 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# "mainnet" | "testnet-preview" | "testnet-preprod"
ENVIRONMENT="testnet-preprod"

# "mainnet" | "preview" | "preprod" ref: https://ogmios.dev/getting-started/docker
OGMIOS_NETWORK="preprod"

# preprod: 7790880c9f0ff09990f253dd1bf1a55d2a2e5340bf694e5c0bb166439e513e45.10918782
# mainnet: 231d8151ca08efcdd68d05920d0622624892dd8719ddc28aef131b7f1d4315db.56553271
SYNC_START_POINT="7790880c9f0ff09990f253dd1bf1a55d2a2e5340bf694e5c0bb166439e513e45.10918782"

# prisma config database url:
DATABASE_URL="postgresql://postgres:minswap@localhost:5432/syncer?schema=public"
15 changes: 10 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,22 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
run_install: false
- uses: actions/setup-node@v4
with:
node-version: 18.x
node-version: 20.x
cache: "pnpm"
- name: Install dependencies
run: npm ci
run: pnpm install --frozen-lockfile
- name: Run build
run: npm run build
run: pnpm run build
- name: Run test
run: npm run test
run: pnpm run test
env:
BLOCKFROST_PROJECT_ID_MAINNET: ${{ secrets.BLOCKFROST_PROJECT_ID_MAINNET }}
BLOCKFROST_PROJECT_ID_TESTNET: ${{ secrets.BLOCKFROST_PROJECT_ID_TESTNET }}
- name: Check format & lint
run: npm run check-format
run: pnpm run check-format
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node-linker=hoisted
engine-strict=true
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v18
v20
11 changes: 11 additions & 0 deletions Dockerfile.syncer
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM node:20
RUN apt-get update -y && \
apt-get install -y ca-certificates tzdata

WORKDIR /app

COPY package.json pnpm-lock.yaml .npmrc ./
COPY src/syncer/postgres/prisma ./src/syncer/postgres/prisma
RUN corepack enable && corepack install
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
COPY . .
140 changes: 106 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
# Minswap SDK

The Minswap open-source providing a comprehensive suite of off-chain tools price feeds, historical data storage, and integration methods for: `Stableswap`, `AMM V1`, `AMM V2` and `LBE V2`.

## Features

- [x] Get current pair price
- [x] Get historical pair price
- [x] Pool price feed
- [x] Historical data of pool
- [x] Calculate trade price
- [x] Calculate price impact
- [x] Create orders and submit with Lucid
- [x] Syncer to sync minswap's liquidity pool data

We provide two adapter `BlockfrostAdapter` and `MinswapAdapter` to get the price and liquidity pool information.
- `BlockfrostAdapter`: use [Blockfrost](https://blockfrost.dev) to query the data.
- `MinswapAdapter`: use Syncer to query the data. If you want to use `MinswapAdapter` you need to run syncer by yourself.

## Install

Expand All @@ -19,74 +26,139 @@ This package depends on `lucid-cardano`, which is an ESM package, so it's also a

## Examples

### Example 1: Get current price of MIN/ADA pool
Create an adapter using either `BlockfrostAdapter` or `MinswapAdapter`:

### BlockfrostAdapter:
```ts
import { BlockFrostAPI } from "@blockfrost/blockfrost-js";
import { BlockfrostAdapter, NetworkId } from "@minswap/sdk";

const blockFrostApi = new BlockFrostAPI({
projectId: "<your_project_id>",
network: "mainnet",
})

const blockfrostAdapter = new BlockfrostAdapter(
NetworkId.MAINNET,
blockFrostApi
)
```

### MinswapAdapter:
- [Install docker compose](https://docs.docker.com/compose/install).
- Update the `.env` file to specify the exact network you want to sync.
- Run the command: `docker compose -f docker-compose.yaml up --build -d` to build.
- Run the command: `docker compose -f docker-compose.yaml logs -f` to view log.

```ts
import { BlockFrostAPI } from "@blockfrost/blockfrost-js";
import { BlockfrostAdapter } from "@minswap/sdk";

const api = new BlockfrostAdapter({
blockFrost: new BlockFrostAPI({
projectId: "<your_project_id>",
network: "mainnet",
}),
});
import { BlockfrostAdapter, MinswapAdapter, NetworkEnvironment, NetworkId, newPrismaClient, PostgresRepositoryReader } from "@minswap/sdk";

const blockFrostApi = new BlockFrostAPI({
projectId: "<your_project_id>",
network: "mainnet",
})

const prismaClient = await newPrismaClient("postgresql://postgres:minswap@postgres:5432/syncer?schema=public&connection_limit=5")

const repositoryReader = new PostgresRepositoryReader(
NetworkEnvironment.MAINNET,
prismaClient
)

const minswapAdapter = new MinswapAdapter({
networkId: NetworkId.MAINNET,
networkEnv: NetworkEnvironment.MAINNET,
blockFrostApi: blockFrostApi,
repository: repositoryReader
})
```

### Example 1: Get current price of MIN/ADA pool

#### MIN/ADA pool v1:
```ts
for (let i = 1; ; i++) {
const pools = await api.getPools({
const pools = await adapter.getV1Pools({
page: i,
});
if (pools.length === 0) {
// last page
break;
}
const minADAPool = pools.find(
const minAdaPool = pools.find(
(p) =>
p.assetA === "lovelace" &&
p.assetB ===
"29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c64d494e"
"29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c64d494e"
);
if (minADAPool) {
const [a, b] = await api.getPoolPrice({ pool: minADAPool });
if (minAdaPool) {
const [price0, price1] = await adapter.getV1PoolPrice({ pool: minAdaPool });
console.log(
`ADA/MIN price: ${a.toString()}; MIN/ADA price: ${b.toString()}`
`ADA/MIN price: ${price0.toString()}; MIN/ADA price: ${price1.toString()}`
);
// we can later use this ID to call getPoolById
console.log(`ADA/MIN pool ID: ${minADAPool.id}`);
console.log(`ADA/MIN pool ID: ${minAdaPool.id}`);
break;
}
}
```

#### MIN/ADA pool v2:
```ts
const minAdaPool = await adapter.getV2PoolByPair(
Asset.fromString("lovelace"),
Asset.fromString("29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c64d494e")
)

if (minAdaPool) {
const [a, b] = await adapter.getV2PoolPrice({ pool: minAdaPool });
console.log(
`ADA/MIN price: ${a.toString()}; MIN/ADA price: ${b.toString()}`
);
}
```

### Example 2: Get historical prices of MIN/ADA pool

#### MIN/ADA pool v1:
```ts
import { BlockFrostAPI } from "@blockfrost/blockfrost-js";
import { BlockfrostAdapter, NetworkId } from "@minswap/sdk";

const MIN_ADA_POOL_ID =
"6aa2153e1ae896a95539c9d62f76cedcdabdcdf144e564b8955f609d660cf6a2";

const api = new BlockfrostAdapter({
blockFrost: new BlockFrostAPI({
projectId: "<your_project_id>",
network: "mainnet",
}),
});
const history = await api.getPoolHistory({ id: MIN_ADA_POOL_ID });
const history = await adapter.getV1PoolHistory({ id: MIN_ADA_POOL_ID });
for (const historyPoint of history) {
const pool = await api.getPoolInTx({ txHash: historyPoint.txHash });
const pool = await adapter.getV1PoolInTx({ txHash: historyPoint.txHash });
if (!pool) {
throw new Error("pool not found");
}
const [price0, price1] = await api.getPoolPrice({
pool,
decimalsA: 6,
decimalsB: 6,
});
const [price0, price1] = await adapter.getV1PoolPrice({ pool: pool });
console.log(`${historyPoint.time}: ${price0} ADA/MIN, ${price1} MIN/ADA`);
}
```

#### MIN/ADA pool v2:
```ts
for (let i = 1; ; i++) {
const pools = await adapter.getV2PoolHistory({
assetA: Asset.fromString("lovelace"),
assetB: Asset.fromString("29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c64d494e"),
page: i,
});
if (pools.length === 0) {
// last page
break;
}

for (const pool of pools) {
const [price0, price1] = await adapter.getV2PoolPrice({ pool: pool });
console.log(
`ADA/MIN price: ${a.toString()}; MIN/ADA price: ${price1.toString()}`
);
}
}
```

### Example 3: Build Order transaction and submit

See `examples/` for more details. You can run a single file like `npm run exec examples/example.ts`.
47 changes: 47 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
services:
syncer:
build:
dockerfile: Dockerfile.syncer
restart: always
environment:
ENVIRONMENT:
OGMIOS_HOST: ogmios
OGMIOS_PORT: 1337
REDIS_URL: redis://default:minswap@redis:6379
POSTGRES_URL: postgresql://postgres:minswap@postgres:5432/syncer?schema=public&connection_limit=5
SYNC_START_POINT:
command: pnpm run syncer:start

redis:
image: redis:7
restart: always
command: --requirepass minswap
ports:
- "6379:6379"
volumes:
- redis-data:/data

postgres:
image: postgres:16
restart: always
environment:
POSTGRES_DB: syncer
POSTGRES_PASSWORD: minswap
volumes:
- ./src/syncer/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql
- postgres-data:/var/lib/postgresql/data
ports:
- "5432:5432"

ogmios:
image: cardanosolutions/cardano-node-ogmios:v6.5.0_9.0.0-${OGMIOS_NETWORK}
restart: always
ports:
- 1337:1337
volumes:
- ogmios-data:/db

volumes:
redis-data: {}
ogmios-data: {}
postgres-data: {}
10 changes: 5 additions & 5 deletions examples/example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ async function main(): Promise<void> {
address
);

const blockfrostAdapter = new BlockfrostAdapter({
networkId: NetworkId.TESTNET,
blockFrost: new BlockFrostAPI({
const blockfrostAdapter = new BlockfrostAdapter(
NetworkId.TESTNET,
new BlockFrostAPI({
projectId: blockfrostProjectId,
network: "preprod",
}),
});
})
);

const utxos = await lucid.utxosAt(address);

Expand Down
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export default {
// snapshotSerializers: [],

// The test environment that will be used for testing
// testEnvironment: "jest-environment-node",
testEnvironment: "node",

// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
Expand Down
Loading