@reflectmoney/oracle.ts 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,110 @@
1
+ import { Connection, PublicKey, Transaction, Keypair } from '@solana/web3.js';
2
+ import { Buffer } from 'buffer';
3
+ export declare const DOPPLER_PROGRAM_ID: PublicKey;
4
+ export declare const ADMIN_PUBKEY: PublicKey;
5
+ /**
6
+ * Generic Oracle data structure
7
+ */
8
+ export interface Oracle<T> {
9
+ sequence: bigint;
10
+ payload: T;
11
+ }
12
+ /**
13
+ * Serializer interface for custom payload types
14
+ */
15
+ export interface PayloadSerializer<T> {
16
+ serialize(payload: T): Buffer;
17
+ deserialize(buffer: Buffer): T;
18
+ size(): number;
19
+ }
20
+ /**
21
+ * Built-in serializer for u64 payloads (price feeds)
22
+ */
23
+ export declare class U64Serializer implements PayloadSerializer<bigint> {
24
+ serialize(payload: bigint): Buffer;
25
+ deserialize(buffer: Buffer): bigint;
26
+ size(): number;
27
+ }
28
+ /**
29
+ * Price Feed structure matching the Rust implementation
30
+ */
31
+ export interface PriceFeed {
32
+ price: bigint;
33
+ }
34
+ /**
35
+ * Serializer for PriceFeed payloads
36
+ */
37
+ export declare class PriceFeedSerializer implements PayloadSerializer<PriceFeed> {
38
+ serialize(payload: PriceFeed): Buffer;
39
+ deserialize(buffer: Buffer): PriceFeed;
40
+ size(): number;
41
+ }
42
+ /**
43
+ * Transaction builder for Doppler oracle updates
44
+ */
45
+ export declare class TransactionBuilder {
46
+ private admin;
47
+ private oracleUpdateInstructions;
48
+ private unitPrice?;
49
+ private computeUnits;
50
+ private loadedAccountDataSize;
51
+ constructor(admin: Keypair);
52
+ /**
53
+ * Add an oracle update instruction to the transaction
54
+ */
55
+ addOracleUpdate<T>(oraclePubkey: PublicKey, oracle: Oracle<T>, serializer: PayloadSerializer<T>): this;
56
+ /**
57
+ * Set the compute unit price in micro-lamports
58
+ */
59
+ withUnitPrice(microLamports: bigint): this;
60
+ /**
61
+ * Build the final transaction
62
+ */
63
+ build(recentBlockhash: string): Transaction;
64
+ private createUpdateInstruction;
65
+ private serializeOracle;
66
+ }
67
+ /**
68
+ * Main Doppler class for interacting with oracle accounts
69
+ */
70
+ export declare class Doppler {
71
+ private connection;
72
+ private admin;
73
+ constructor(connection: Connection, admin: Keypair);
74
+ /**
75
+ * Create a new transaction builder
76
+ */
77
+ createTransactionBuilder(): TransactionBuilder;
78
+ /**
79
+ * Fetch oracle account data and deserialize it
80
+ */
81
+ fetchOracle<T>(oraclePubkey: PublicKey, serializer: PayloadSerializer<T>): Promise<Oracle<T> | null>;
82
+ /**
83
+ * Deserialize oracle data from a buffer
84
+ */
85
+ deserializeOracle<T>(data: Buffer, serializer: PayloadSerializer<T>): Oracle<T>;
86
+ /**
87
+ * Create an oracle account with a seed
88
+ */
89
+ createOracleAccount<T>(seed: string, serializer: PayloadSerializer<T>, initialOracle: Oracle<T>): Promise<PublicKey>;
90
+ /**
91
+ * Update a single oracle account
92
+ */
93
+ updateOracle<T>(oraclePubkey: PublicKey, oracle: Oracle<T>, serializer: PayloadSerializer<T>, unitPrice?: bigint): Promise<string>;
94
+ /**
95
+ * Update multiple oracle accounts in a single transaction
96
+ */
97
+ updateMultipleOracles<T>(updates: Array<{
98
+ oraclePubkey: PublicKey;
99
+ oracle: Oracle<T>;
100
+ serializer: PayloadSerializer<T>;
101
+ }>, unitPrice?: bigint): Promise<string>;
102
+ /**
103
+ * Get the admin keypair
104
+ */
105
+ getAdmin(): Keypair;
106
+ /**
107
+ * Get the connection
108
+ */
109
+ getConnection(): Connection;
110
+ }
package/dist/index.js ADDED
@@ -0,0 +1,283 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.Doppler = exports.TransactionBuilder = exports.PriceFeedSerializer = exports.U64Serializer = exports.ADMIN_PUBKEY = exports.DOPPLER_PROGRAM_ID = void 0;
13
+ const web3_js_1 = require("@solana/web3.js");
14
+ const buffer_1 = require("buffer");
15
+ const compute_budget_1 = require("@solana-program/compute-budget");
16
+ // Program ID: PRicevBH6BaeaE8qmrxrwGBZ5hSZ9vjBNue5Ygot1ML
17
+ exports.DOPPLER_PROGRAM_ID = new web3_js_1.PublicKey('PRicevBH6BaeaE8qmrxrwGBZ5hSZ9vjBNue5Ygot1ML');
18
+ // Admin public key: pRiCEzwgkSi7KTsQHdyfuRbPEuCFoK9sA5QVn2hvABV
19
+ exports.ADMIN_PUBKEY = new web3_js_1.PublicKey('pRiCEzwgkSi7KTsQHdyfuRbPEuCFoK9sA5QVn2hvABV');
20
+ // Constants for compute unit calculations
21
+ const SEQUENCE_CHECK_CU = 5;
22
+ const ADMIN_VERIFICATION_CU = 6;
23
+ const PAYLOAD_WRITE_CU = 6;
24
+ const COMPUTE_BUDGET_IX_CU = 150;
25
+ const COMPUTE_BUDGET_UNIT_PRICE_SIZE = 9;
26
+ const COMPUTE_BUDGET_UNIT_LIMIT_SIZE = 5;
27
+ const COMPUTE_BUDGET_DATA_LIMIT_SIZE = 5;
28
+ const COMPUTE_BUDGET_PROGRAM_SIZE = 22;
29
+ const ORACLE_PROGRAM_SIZE = 36;
30
+ /**
31
+ * Built-in serializer for u64 payloads (price feeds)
32
+ */
33
+ class U64Serializer {
34
+ serialize(payload) {
35
+ const buf = buffer_1.Buffer.alloc(8);
36
+ buf.writeBigUInt64LE(payload);
37
+ return buf;
38
+ }
39
+ deserialize(buffer) {
40
+ return buffer.readBigUInt64LE(0);
41
+ }
42
+ size() {
43
+ return 8;
44
+ }
45
+ }
46
+ exports.U64Serializer = U64Serializer;
47
+ /**
48
+ * Serializer for PriceFeed payloads
49
+ */
50
+ class PriceFeedSerializer {
51
+ serialize(payload) {
52
+ const buf = buffer_1.Buffer.alloc(8);
53
+ buf.writeBigUInt64LE(payload.price);
54
+ return buf;
55
+ }
56
+ deserialize(buffer) {
57
+ return {
58
+ price: buffer.readBigUInt64LE(0),
59
+ };
60
+ }
61
+ size() {
62
+ return 8;
63
+ }
64
+ }
65
+ exports.PriceFeedSerializer = PriceFeedSerializer;
66
+ /**
67
+ * Transaction builder for Doppler oracle updates
68
+ */
69
+ class TransactionBuilder {
70
+ constructor(admin) {
71
+ this.admin = admin;
72
+ this.oracleUpdateInstructions = [];
73
+ this.computeUnits = COMPUTE_BUDGET_IX_CU * 2;
74
+ this.loadedAccountDataSize = ORACLE_PROGRAM_SIZE +
75
+ COMPUTE_BUDGET_PROGRAM_SIZE +
76
+ COMPUTE_BUDGET_UNIT_LIMIT_SIZE +
77
+ COMPUTE_BUDGET_DATA_LIMIT_SIZE +
78
+ 2;
79
+ }
80
+ /**
81
+ * Add an oracle update instruction to the transaction
82
+ */
83
+ addOracleUpdate(oraclePubkey, oracle, serializer) {
84
+ const instruction = this.createUpdateInstruction(oraclePubkey, oracle, serializer);
85
+ const payloadSize = serializer.size();
86
+ const oracleSize = 8 + payloadSize; // sequence + payload
87
+ this.computeUnits +=
88
+ SEQUENCE_CHECK_CU +
89
+ ADMIN_VERIFICATION_CU +
90
+ PAYLOAD_WRITE_CU +
91
+ Math.floor(oracleSize / 4);
92
+ this.loadedAccountDataSize += oracleSize * 2;
93
+ this.oracleUpdateInstructions.push(instruction);
94
+ return this;
95
+ }
96
+ /**
97
+ * Set the compute unit price in micro-lamports
98
+ */
99
+ withUnitPrice(microLamports) {
100
+ this.unitPrice = microLamports;
101
+ return this;
102
+ }
103
+ /**
104
+ * Build the final transaction
105
+ */
106
+ build(recentBlockhash) {
107
+ const instructions = [];
108
+ let loadedAccountDataSize = this.loadedAccountDataSize;
109
+ let computeUnits = this.computeUnits;
110
+ if (this.unitPrice !== undefined) {
111
+ instructions.push(web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({
112
+ microLamports: this.unitPrice,
113
+ }));
114
+ loadedAccountDataSize += COMPUTE_BUDGET_UNIT_PRICE_SIZE;
115
+ computeUnits += COMPUTE_BUDGET_IX_CU;
116
+ }
117
+ const loadedAccountsDataSizeLimitInstruction = (0, compute_budget_1.getSetLoadedAccountsDataSizeLimitInstruction)({
118
+ accountDataSizeLimit: loadedAccountDataSize * 2
119
+ });
120
+ instructions.push(new web3_js_1.TransactionInstruction({
121
+ keys: [],
122
+ programId: new web3_js_1.PublicKey(loadedAccountsDataSizeLimitInstruction.programAddress.toString()),
123
+ data: buffer_1.Buffer.from(loadedAccountsDataSizeLimitInstruction.data),
124
+ }));
125
+ instructions.push(web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({
126
+ units: computeUnits,
127
+ }));
128
+ instructions.push(...this.oracleUpdateInstructions);
129
+ const transaction = new web3_js_1.Transaction({
130
+ feePayer: this.admin.publicKey,
131
+ recentBlockhash,
132
+ });
133
+ transaction.add(...instructions);
134
+ transaction.sign(this.admin);
135
+ return transaction;
136
+ }
137
+ createUpdateInstruction(oraclePubkey, oracle, serializer) {
138
+ const data = this.serializeOracle(oracle, serializer);
139
+ return new web3_js_1.TransactionInstruction({
140
+ programId: exports.DOPPLER_PROGRAM_ID,
141
+ keys: [
142
+ {
143
+ pubkey: this.admin.publicKey,
144
+ isSigner: true,
145
+ isWritable: false,
146
+ },
147
+ {
148
+ pubkey: oraclePubkey,
149
+ isSigner: false,
150
+ isWritable: true,
151
+ },
152
+ ],
153
+ data,
154
+ });
155
+ }
156
+ serializeOracle(oracle, serializer) {
157
+ const sequenceBuffer = buffer_1.Buffer.alloc(8);
158
+ sequenceBuffer.writeBigUInt64LE(oracle.sequence);
159
+ const payloadBuffer = serializer.serialize(oracle.payload);
160
+ return buffer_1.Buffer.concat([sequenceBuffer, payloadBuffer]);
161
+ }
162
+ }
163
+ exports.TransactionBuilder = TransactionBuilder;
164
+ /**
165
+ * Main Doppler class for interacting with oracle accounts
166
+ */
167
+ class Doppler {
168
+ constructor(connection, admin) {
169
+ this.connection = connection;
170
+ this.admin = admin;
171
+ }
172
+ /**
173
+ * Create a new transaction builder
174
+ */
175
+ createTransactionBuilder() {
176
+ return new TransactionBuilder(this.admin);
177
+ }
178
+ /**
179
+ * Fetch oracle account data and deserialize it
180
+ */
181
+ fetchOracle(oraclePubkey, serializer) {
182
+ return __awaiter(this, void 0, void 0, function* () {
183
+ try {
184
+ const accountInfo = yield this.connection.getAccountInfo(oraclePubkey);
185
+ if (!accountInfo || !accountInfo.data) {
186
+ return null;
187
+ }
188
+ return this.deserializeOracle(accountInfo.data, serializer);
189
+ }
190
+ catch (error) {
191
+ console.error('Error fetching oracle account:', error);
192
+ return null;
193
+ }
194
+ });
195
+ }
196
+ /**
197
+ * Deserialize oracle data from a buffer
198
+ */
199
+ deserializeOracle(data, serializer) {
200
+ const expectedSize = 8 + serializer.size();
201
+ if (data.length < expectedSize) {
202
+ throw new Error(`Invalid oracle data size. Expected at least ${expectedSize}, got ${data.length}`);
203
+ }
204
+ const sequence = data.readBigUInt64LE(0);
205
+ const payloadBuffer = data.subarray(8, 8 + serializer.size());
206
+ const payload = serializer.deserialize(payloadBuffer);
207
+ return { sequence, payload };
208
+ }
209
+ /**
210
+ * Create an oracle account with a seed
211
+ */
212
+ createOracleAccount(seed, serializer, initialOracle) {
213
+ return __awaiter(this, void 0, void 0, function* () {
214
+ const oracleSize = 8 + serializer.size();
215
+ const lamports = yield this.connection.getMinimumBalanceForRentExemption(oracleSize);
216
+ const oraclePubkey = yield web3_js_1.PublicKey.createWithSeed(this.admin.publicKey, seed, exports.DOPPLER_PROGRAM_ID);
217
+ const createAccountInstruction = web3_js_1.SystemProgram.createAccountWithSeed({
218
+ fromPubkey: this.admin.publicKey,
219
+ newAccountPubkey: oraclePubkey,
220
+ basePubkey: this.admin.publicKey,
221
+ seed,
222
+ lamports,
223
+ space: oracleSize,
224
+ programId: exports.DOPPLER_PROGRAM_ID,
225
+ });
226
+ const recentBlockhash = yield this.connection.getLatestBlockhash();
227
+ const transaction = new web3_js_1.Transaction({
228
+ feePayer: this.admin.publicKey,
229
+ recentBlockhash: recentBlockhash.blockhash,
230
+ });
231
+ transaction.add(createAccountInstruction);
232
+ transaction.sign(this.admin);
233
+ yield (0, web3_js_1.sendAndConfirmTransaction)(this.connection, transaction, [this.admin]);
234
+ return oraclePubkey;
235
+ });
236
+ }
237
+ /**
238
+ * Update a single oracle account
239
+ */
240
+ updateOracle(oraclePubkey, oracle, serializer, unitPrice) {
241
+ return __awaiter(this, void 0, void 0, function* () {
242
+ const recentBlockhash = yield this.connection.getLatestBlockhash();
243
+ let builder = this.createTransactionBuilder().addOracleUpdate(oraclePubkey, oracle, serializer);
244
+ if (unitPrice !== undefined) {
245
+ builder = builder.withUnitPrice(unitPrice);
246
+ }
247
+ const transaction = builder.build(recentBlockhash.blockhash);
248
+ const signature = yield (0, web3_js_1.sendAndConfirmTransaction)(this.connection, transaction, [this.admin]);
249
+ return signature;
250
+ });
251
+ }
252
+ /**
253
+ * Update multiple oracle accounts in a single transaction
254
+ */
255
+ updateMultipleOracles(updates, unitPrice) {
256
+ return __awaiter(this, void 0, void 0, function* () {
257
+ const recentBlockhash = yield this.connection.getLatestBlockhash();
258
+ let builder = this.createTransactionBuilder();
259
+ for (const update of updates) {
260
+ builder = builder.addOracleUpdate(update.oraclePubkey, update.oracle, update.serializer);
261
+ }
262
+ if (unitPrice !== undefined) {
263
+ builder = builder.withUnitPrice(unitPrice);
264
+ }
265
+ const transaction = builder.build(recentBlockhash.blockhash);
266
+ const signature = yield (0, web3_js_1.sendAndConfirmTransaction)(this.connection, transaction, [this.admin]);
267
+ return signature;
268
+ });
269
+ }
270
+ /**
271
+ * Get the admin keypair
272
+ */
273
+ getAdmin() {
274
+ return this.admin;
275
+ }
276
+ /**
277
+ * Get the connection
278
+ */
279
+ getConnection() {
280
+ return this.connection;
281
+ }
282
+ }
283
+ exports.Doppler = Doppler;
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@reflectmoney/oracle.ts",
3
+ "version": "1.0.0",
4
+ "type": "commonjs",
5
+ "author": "L0STE, stablecoinjesus @ Palindrome Engineering",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/palindrome-eng/doppler.git"
9
+ },
10
+ "dependencies": {
11
+ "@solana-program/compute-budget": "^0.9.0",
12
+ "@solana-program/token": "^0.6.0",
13
+ "@solana/buffer-layout": "^4.0.1",
14
+ "@solana/kit": "^3.0.3",
15
+ "@solana/web3.js": "^1.98.4",
16
+ "bn.js": "^5.2.2",
17
+ "buffer": "^6.0.3"
18
+ },
19
+ "devDependencies": {
20
+ "@types/bn.js": "5.1.6",
21
+ "@types/chai": "^4.3.11",
22
+ "@types/mocha": "^10.0.6",
23
+ "bs58": "^6.0.0",
24
+ "chai": "^4.3.10",
25
+ "mocha": "^10.2.0",
26
+ "ts-node": "^10.9.2"
27
+ },
28
+ "license": "MIT",
29
+ "main": "./dist/index.js",
30
+ "module": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "files": [
33
+ "/dist"
34
+ ]
35
+ }