@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.
- package/dist/index.d.ts +110 -0
- package/dist/index.js +283 -0
- package/package.json +35 -0
package/dist/index.d.ts
ADDED
@@ -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
|
+
}
|