Skip to content
This repository was archived by the owner on Mar 14, 2024. It is now read-only.

Commit 8bdd411

Browse files
authored
Merge pull request #6 from pyth-network/price-feed-metadata
add metadata to price feed
2 parents 7fd8ae7 + 5388085 commit 8bdd411

File tree

5 files changed

+194
-15
lines changed

5 files changed

+194
-15
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pythnetwork/pyth-sdk-js",
3-
"version": "0.2.0",
3+
"version": "0.3.0",
44
"description": "Pyth Network SDK in JS",
55
"homepage": "https://pyth.network",
66
"main": "lib/index.js",
@@ -13,7 +13,7 @@
1313
"test": "jest",
1414
"build": "tsc",
1515
"format": "prettier --write \"src/**/*.ts\"",
16-
"gen-ts-schema": "quicktype --src-lang schema src/schemas/price_feed.json -o src/schemas/PriceFeed.ts --raw-type any && prettier --write \"src/schemas/*.ts\"",
16+
"gen-ts-schema": "quicktype --src-lang schema src/schemas/price_feed.json -o src/schemas/PriceFeed.ts --raw-type any --converters all-objects && prettier --write \"src/schemas/*.ts\"",
1717
"lint": "eslint src/",
1818
"prepare": "npm run build",
1919
"prepublishOnly": "npm test && npm run lint",

src/__tests__/PriceFeed.test.ts

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Price, PriceFeed, PriceStatus } from "../index";
1+
import { Price, PriceFeed, PriceFeedMetadata, PriceStatus } from "../index";
22

33
beforeAll(() => {
44
jest.useFakeTimers();
@@ -42,8 +42,7 @@ test("Parsing Price Feed works as expected", () => {
4242
new Price("1", 4, "10")
4343
);
4444
expect(priceFeed.getLatestAvailablePriceWithinDuration(5)).toBeUndefined();
45-
46-
expect(priceFeed.toJson()).toStrictEqual(data);
45+
expect(priceFeed.toJson()).toEqual(data);
4746
});
4847

4948
test("getCurrentPrice returns undefined if status is not Trading", () => {
@@ -99,3 +98,37 @@ test("getLatestAvailablePrice returns prevPrice when status is not Trading", ()
9998
);
10099
expect(priceFeed.getLatestAvailablePriceWithinDuration(10)).toBeUndefined();
101100
});
101+
102+
test("getMetadata returns PriceFeedMetadata as expected", () => {
103+
const data = {
104+
conf: "1",
105+
ema_conf: "2",
106+
ema_price: "3",
107+
expo: 4,
108+
id: "abcdef0123456789",
109+
max_num_publishers: 6,
110+
metadata: {
111+
attestation_time: 7,
112+
emitter_chain: 8,
113+
sequence_number: 9,
114+
},
115+
num_publishers: 10,
116+
prev_conf: "11",
117+
prev_price: "12",
118+
prev_publish_time: 13,
119+
price: "14",
120+
product_id: "0123456789abcdef",
121+
publish_time: 16,
122+
status: PriceStatus.Unknown,
123+
};
124+
125+
const priceFeed = PriceFeed.fromJson(data);
126+
127+
expect(priceFeed.getMetadata()).toStrictEqual(
128+
PriceFeedMetadata.fromJson({
129+
attestation_time: 7,
130+
emitter_chain: 8,
131+
sequence_number: 9,
132+
})
133+
);
134+
});

src/index.ts

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { Convert, PriceFeed as JsonPriceFeed } from "./schemas/PriceFeed";
1+
import {
2+
Convert,
3+
PriceFeed as JsonPriceFeed,
4+
PriceFeedMetadata as JsonPriceFeedMetadata,
5+
} from "./schemas/PriceFeed";
26

37
export type UnixTimestamp = number;
48
export type DurationInSeconds = number;
@@ -53,6 +57,58 @@ export enum PriceStatus {
5357
Unknown = "Unknown",
5458
}
5559

60+
/**
61+
* Metadata about the price
62+
*
63+
* Represents metadata of a price feed.
64+
*/
65+
export class PriceFeedMetadata {
66+
/**
67+
* Attestation time of the price
68+
*/
69+
attestationTime: number;
70+
/**
71+
* Chain of the emitter
72+
*/
73+
emitterChain: number;
74+
/**
75+
* Sequence number of the price
76+
*/
77+
sequenceNumber: number;
78+
79+
constructor(metadata: {
80+
attestationTime: number;
81+
emitterChain: number;
82+
sequenceNumber: number;
83+
}) {
84+
this.attestationTime = metadata.attestationTime;
85+
this.emitterChain = metadata.emitterChain;
86+
this.sequenceNumber = metadata.sequenceNumber;
87+
}
88+
89+
static fromJson(json: any): PriceFeedMetadata | undefined {
90+
if (json === undefined) {
91+
return undefined;
92+
}
93+
const jsonFeed: JsonPriceFeedMetadata = Convert.toPriceFeedMetadata(json);
94+
return new PriceFeedMetadata({
95+
attestationTime: jsonFeed.attestation_time,
96+
emitterChain: jsonFeed.emitter_chain,
97+
sequenceNumber: jsonFeed.sequence_number,
98+
});
99+
}
100+
101+
toJson(): any {
102+
const jsonFeed: JsonPriceFeedMetadata = {
103+
attestation_time: this.attestationTime,
104+
emitter_chain: this.emitterChain,
105+
sequence_number: this.sequenceNumber,
106+
};
107+
// this is done to avoid sending undefined values to the server
108+
return Convert.priceFeedMetadataToJson(jsonFeed);
109+
}
110+
}
111+
56112
/**
57113
* Pyth Price Feed
58114
*
@@ -84,6 +140,10 @@ export class PriceFeed {
84140
* Maximum number of allowed publishers that can contribute to a price.
85141
*/
86142
maxNumPublishers: number;
143+
/**
144+
* Metadata about the price
145+
*/
146+
metadata?: PriceFeedMetadata;
87147
/**
88148
* Number of publishers that made up current aggregate.
89149
*/
@@ -124,6 +184,7 @@ export class PriceFeed {
124184
expo: number;
125185
id: HexString;
126186
maxNumPublishers: number;
187+
metadata?: PriceFeedMetadata;
127188
numPublishers: number;
128189
prevConf: string;
129190
prevPrice: string;
@@ -139,6 +200,7 @@ export class PriceFeed {
139200
this.expo = rawValues.expo;
140201
this.id = rawValues.id;
141202
this.maxNumPublishers = rawValues.maxNumPublishers;
203+
this.metadata = rawValues.metadata;
142204
this.numPublishers = rawValues.numPublishers;
143205
this.prevConf = rawValues.prevConf;
144206
this.prevPrice = rawValues.prevPrice;
@@ -158,6 +220,7 @@ export class PriceFeed {
158220
expo: jsonFeed.expo,
159221
id: jsonFeed.id,
160222
maxNumPublishers: jsonFeed.max_num_publishers,
223+
metadata: PriceFeedMetadata.fromJson(jsonFeed.metadata),
161224
numPublishers: jsonFeed.num_publishers,
162225
prevConf: jsonFeed.prev_conf,
163226
prevPrice: jsonFeed.prev_price,
@@ -177,6 +240,7 @@ export class PriceFeed {
177240
expo: this.expo,
178241
id: this.id,
179242
max_num_publishers: this.maxNumPublishers,
243+
metadata: this.metadata?.toJson(),
180244
num_publishers: this.numPublishers,
181245
prev_conf: this.prevConf,
182246
prev_price: this.prevPrice,
@@ -259,18 +323,30 @@ export class PriceFeed {
259323
* @returns a struct containing the latest available price, confidence interval and the exponent for
260324
* both numbers, or `undefined` if no price update occurred within `duration` seconds of the current time.
261325
*/
262-
getLatestAvailablePriceWithinDuration(duration: DurationInSeconds): Price | undefined {
326+
getLatestAvailablePriceWithinDuration(
327+
duration: DurationInSeconds
328+
): Price | undefined {
263329
const [price, timestamp] = this.getLatestAvailablePriceUnchecked();
264330

265331
const currentTime: UnixTimestamp = Math.floor(Date.now() / 1000);
266-
332+
267333
// This checks the absolute difference as a sanity check
268334
// for the cases that the system time is behind or price
269-
// feed timestamp happen to be in the future (a bug).
335+
// feed timestamp happen to be in the future (a bug).
270336
if (Math.abs(currentTime - timestamp) > duration) {
271337
return undefined;
272338
}
273339

274340
return price;
275341
}
342+
343+
/**
344+
* Get the price feed metadata.
345+
*
346+
* @returns a struct containing the attestation time, emitter chain, and the sequence number.
347+
* Returns `undefined` if metadata is currently unavailable.
348+
*/
349+
getMetadata(): PriceFeedMetadata | undefined {
350+
return this.metadata;
351+
}
276352
}

src/schemas/PriceFeed.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ export interface PriceFeed {
3535
* Maximum number of allowed publishers that can contribute to a price.
3636
*/
3737
max_num_publishers: number;
38+
/**
39+
* Metadata about the price
40+
*/
41+
metadata?: PriceFeedMetadata;
3842
/**
3943
* Number of publishers that made up current aggregate.
4044
*/
@@ -69,6 +73,26 @@ export interface PriceFeed {
6973
status: PriceStatus;
7074
}
7175

76+
/**
77+
* Metadata about the price
78+
*
79+
* Represents metadata of a price feed.
80+
*/
81+
export interface PriceFeedMetadata {
82+
/**
83+
* Attestation time of the price
84+
*/
85+
attestation_time: number;
86+
/**
87+
* Chain of the emitter
88+
*/
89+
emitter_chain: number;
90+
/**
91+
* Sequence number of the price
92+
*/
93+
sequence_number: number;
94+
}
95+
7296
/**
7397
* Status of price (Trading is valid).
7498
*
@@ -91,6 +115,14 @@ export class Convert {
91115
public static priceFeedToJson(value: PriceFeed): any {
92116
return uncast(value, r("PriceFeed"));
93117
}
118+
119+
public static toPriceFeedMetadata(json: any): PriceFeedMetadata {
120+
return cast(json, r("PriceFeedMetadata"));
121+
}
122+
123+
public static priceFeedMetadataToJson(value: PriceFeedMetadata): any {
124+
return uncast(value, r("PriceFeedMetadata"));
125+
}
94126
}
95127

96128
function invalidValue(typ: any, val: any, key: any = ""): never {
@@ -249,6 +281,11 @@ const typeMap: any = {
249281
{ json: "expo", js: "expo", typ: 0 },
250282
{ json: "id", js: "id", typ: "" },
251283
{ json: "max_num_publishers", js: "max_num_publishers", typ: 0 },
284+
{
285+
json: "metadata",
286+
js: "metadata",
287+
typ: u(undefined, r("PriceFeedMetadata")),
288+
},
252289
{ json: "num_publishers", js: "num_publishers", typ: 0 },
253290
{ json: "prev_conf", js: "prev_conf", typ: "" },
254291
{ json: "prev_price", js: "prev_price", typ: "" },
@@ -260,5 +297,13 @@ const typeMap: any = {
260297
],
261298
"any"
262299
),
300+
PriceFeedMetadata: o(
301+
[
302+
{ json: "attestation_time", js: "attestation_time", typ: 0 },
303+
{ json: "emitter_chain", js: "emitter_chain", typ: 0 },
304+
{ json: "sequence_number", js: "sequence_number", typ: 0 },
305+
],
306+
"any"
307+
),
263308
PriceStatus: ["Auction", "Halted", "Trading", "Unknown"],
264309
};

src/schemas/price_feed.json

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@
9494
"$ref": "#/definitions/PriceStatus"
9595
}
9696
]
97+
},
98+
"metadata": {
99+
"description": "Metadata about the price",
100+
"allOf": [
101+
{
102+
"$ref": "#/definitions/PriceFeedMetadata"
103+
}
104+
]
97105
}
98106
},
99107
"definitions": {
@@ -103,12 +111,29 @@
103111
"PriceStatus": {
104112
"description": "Represents availability status of a price feed.",
105113
"type": "string",
106-
"enum": [
107-
"Unknown",
108-
"Trading",
109-
"Halted",
110-
"Auction"
111-
]
114+
"enum": ["Unknown", "Trading", "Halted", "Auction"]
115+
},
116+
"PriceFeedMetadata": {
117+
"description": "Represents metadata of a price feed.",
118+
"type": "object",
119+
"required": ["attestation_time", "emitter_chain", "sequence_number"],
120+
"properties": {
121+
"attestation_time": {
122+
"description": "Attestation time of the price",
123+
"type": "integer",
124+
"format": "int64"
125+
},
126+
"emitter_chain": {
127+
"description": "Chain of the emitter",
128+
"type": "integer",
129+
"format": "int16"
130+
},
131+
"sequence_number": {
132+
"description": "Sequence number of the price",
133+
"type": "integer",
134+
"format": "int64"
135+
}
136+
}
112137
}
113138
}
114139
}

0 commit comments

Comments
 (0)