Skip to content
Closed
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
20 changes: 20 additions & 0 deletions .pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added .yarn/cache/fsevents-patch-6b67494872-10.zip
Binary file not shown.
Binary file added .yarn/cache/fsevents-patch-afc6995412-10.zip
Binary file not shown.
2 changes: 2 additions & 0 deletions packages/composites/ondo-gm-tokenized/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# @chainlink/ondo-gm-tokenized-adapter

130 changes: 130 additions & 0 deletions packages/composites/ondo-gm-tokenized/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Chainlink Ondo GM Tokenized Composite Adapter

A composite adapter that calculates tokenized prices for Ondo GM tokens (e.g., TSLAon, SPYon, QQQon) by combining:

1. **Underlying asset prices** from Chainlink Equities Price Feeds (Data Streams)
2. **Multiplier data** from Ondo Finance API

The adapter returns `tokenizedPrice = underlyingPrice × activeMultiplier` with 8 decimal precision.

## How It Works

Ondo GM tokens are total-return trackers that track underlying equities with adjustable multipliers. This adapter:

1. Fetches the latest underlying price from Chainlink Data Streams using the provided `feedId`
2. Retrieves multiplier information from Ondo's API
3. Calculates the active multiplier based on activation logic (supports pending multiplier changes)
4. Returns the tokenized price with 8 decimal precision

## Configuration

The adapter requires the following environment variables:

| Required? | Name | Description | Options | Defaults to |
| :-------: | :----------------: | :------------------------------------------------: | :-----: | :---------: |
| ✅ | `ONDO_API_KEY` | API key for Ondo Finance API | | |
| | `STREAMS_API_KEY` | API key for Chainlink Data Streams | | |
| | `STREAMS_API_SECRET` | API secret for Chainlink Data Streams | | |
| | `ONDO_BASE_URL` | Base URL for Ondo Finance API | | `https://api.gm.ondo.finance` |

## Running

See the [Composite Adapter README](../README.md) for more information on how to get started.

### Input Parameters

| Required? | Name | Description | Options | Defaults to |
| :-------: | :----------: | :--------------------------------------------------------------------: | :--------------------------: | :---------: |
| ✅ | `symbol` | GM token symbol to get price for | `TSLAon`, `SPYon`, `QQQon` | |
| | `underlying` | Base ticker (if not provided, derived from symbol) | `TSLA`, `SPY`, `QQQ` | |
| | `feedId` | Data Streams feed ID (if not provided, derived from symbol/underlying)| `equities:TSLA:mid`, etc. | |

### Supported Symbols

The adapter includes built-in support for:

- **TSLAon** → underlying: TSLA, feedId: equities:TSLA:mid
- **SPYon** → underlying: SPY, feedId: equities:SPY:mid
- **QQQon** → underlying: QQQ, feedId: equities:QQQ:mid

Additional symbols can be supported by providing `underlying` and `feedId` parameters in the request.

### Sample Input

```json
{
"id": "1",
"data": {
"symbol": "TSLAon"
}
}
```

### Sample Output

```json
{
"jobRunID": "1",
"data": {
"tokenizedPrice": "239.12345678",
"underlyingPrice": 250.75,
"activeMultiplier": 0.95321,
"symbol": "TSLAon",
"feedId": "equities:TSLA:mid"
},
"result": "239.12345678",
"statusCode": 200
}
```

## Multiplier Activation Logic

The adapter supports automatic multiplier transitions:

1. **Current state**: Uses `sharesMultiplier` from Ondo API
2. **Pending changes**: If `newMultiplier` and `activationDateTime` are present:
- Before activation: uses current multiplier
- After activation: uses new multiplier
3. **Calculation**: `activeMultiplier = (nowUTC >= activationDateTime) ? newMultiplier : currentMultiplier`

## Data Sources

- **Underlying Prices**: Chainlink Equities Price Feeds via Data Streams
- **Multipliers**: Ondo Finance API endpoint `GET /v1/assets/{symbol}/market`

## API Endpoints

### Ondo Finance API
- **URL**: `https://api.gm.ondo.finance/v1/assets/{symbol}/market`
- **Authentication**: Bearer token via `ONDO_API_KEY`
- **Response**: Contains `primaryMarket.sharesMultiplier` and optional `newMultiplier`/`activationDateTime`

### Chainlink Data Streams
- **Authentication**: API key/secret via `STREAMS_API_KEY`/`STREAMS_API_SECRET`
- **Data**: Latest equity prices by `feedId`

## Error Handling

The adapter includes comprehensive error handling for:
- Invalid or unknown symbols
- Missing environment variables
- API communication failures
- Data validation errors
- Network timeouts and retries

## Extensibility

To add new GM tokens:
1. Add symbol mapping to `ASSET_CONFIG` in the code, or
2. Provide `underlying` and `feedId` parameters in requests

Example for custom symbol:
```json
{
"data": {
"symbol": "AAPLon",
"underlying": "AAPL",
"feedId": "equities:AAPL:mid"
}
}
```
44 changes: 44 additions & 0 deletions packages/composites/ondo-gm-tokenized/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "@chainlink/ondo-gm-tokenized-adapter",
"version": "0.0.0",
"description": "Chainlink Ondo GM Tokenized composite adapter. Calculates tokenized price by combining underlying asset prices from Chainlink Data Streams with multipliers from Ondo API.",
"keywords": [
"Chainlink",
"LINK",
"Ondo",
"GM Tokens",
"Tokenized Assets",
"Equities",
"Data Streams",
"blockchain",
"oracle",
"composite"
],
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"repository": {
"url": "https://github.com/smartcontractkit/external-adapters-js",
"type": "git"
},
"license": "MIT",
"scripts": {
"clean": "rm -rf dist && rm -f tsconfig.tsbuildinfo",
"prepack": "yarn build",
"build": "tsc -b",
"server": "node -e 'require(\"./index.js\").server()'",
"server:dist": "node -e 'require(\"./dist/index.js\").server()'",
"start": "yarn server:dist"
},
"dependencies": {
"@chainlink/ea-bootstrap": "workspace:*",
"tslib": "^2.3.1"
},
"devDependencies": {
"@types/jest": "27.5.2",
"@types/node": "16.11.51",
"typescript": "5.0.4"
}
}
21 changes: 21 additions & 0 deletions packages/composites/ondo-gm-tokenized/schemas/env.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$id": "https://external-adapters.chainlinklabs.com/schemas/example-composite-adapter.json",
"title": "Chainlink Composite External Adapter for Example Data Provider",
"required": ["ETHEREUM_RPC_URL"],
"type": "object",
"properties": {
"ETHEREUM_RPC_URL": {
"type": "string",
"format": "uri"
},
"OPTION": {
"default": true,
"type": "boolean"
}
},
"allOf": [
{
"$ref": "https://external-adapters.chainlinklabs.com/schemas/ea-bootstrap.json"
}
]
}
31 changes: 31 additions & 0 deletions packages/composites/ondo-gm-tokenized/src/adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
APIEndpoint,
Builder,
ExecuteFactory,
ExecuteWithConfig,
} from '@chainlink/ea-bootstrap'
import { AdapterRequest } from '@chainlink/ea-bootstrap'
import { makeConfig, Config } from './config'
import * as endpoints from './endpoint'

export const execute: ExecuteWithConfig<Config, endpoints.TInputParameters> = async (
request,
context,
config,
) => {
return Builder.buildSelector<Config, endpoints.TInputParameters>(
request,
context,
config,
endpoints,
)
}

export const endpointSelector = (
request: AdapterRequest,
): APIEndpoint<Config, endpoints.TInputParameters> =>
Builder.selectEndpoint<Config, endpoints.TInputParameters>(request, makeConfig(), endpoints)

export const makeExecute: ExecuteFactory<Config, endpoints.TInputParameters> = (config) => {
return async (request, context) => execute(request, context, config || makeConfig())
}
26 changes: 26 additions & 0 deletions packages/composites/ondo-gm-tokenized/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Config as BaseConfig, Requester, util } from '@chainlink/ea-bootstrap'

export const NAME = 'ONDO_GM_TOKENIZED'
export const DEFAULT_ENDPOINT = 'ondo_gm_tokenized'

export interface Config extends BaseConfig {
ondoApiKey: string
streamsApiKey?: string
streamsApiSecret?: string
baseUrl: string
streamsBaseUrl?: string
}

export const makeConfig = (prefix?: string): Config => {
const config = Requester.getDefaultConfig(prefix)

return {
...config,
defaultEndpoint: DEFAULT_ENDPOINT,
ondoApiKey: util.getRequiredEnv('ONDO_API_KEY'),
streamsApiKey: util.getRequiredEnv('STREAMS_API_KEY'),
streamsApiSecret: util.getRequiredEnv('STREAMS_API_SECRET'),
baseUrl: util.getEnv('ONDO_BASE_URL') || 'https://api.gm.ondo.finance',
streamsBaseUrl: util.getEnv('STREAMS_BASE_URL') || 'https://api.dataengine.chain.link',
}
}
Loading
Loading