From f039cab61f15f85d882125846aa8a3dd826ebce7 Mon Sep 17 00:00:00 2001 From: dd Date: Mon, 22 Feb 2021 18:02:49 -0500 Subject: [PATCH] removed solray --- package.json | 3 +- src/client.ts | 10 +- src/schema.ts | 811 +++++++++++++++++++++++++------------------------- 3 files changed, 411 insertions(+), 413 deletions(-) diff --git a/package.json b/package.json index 8777162..c14b1f8 100644 --- a/package.json +++ b/package.json @@ -60,8 +60,7 @@ "@solana/web3.js": "^0.90.0", "bn.js": "^5.1.2", "buffer-layout": "^1.2.0", - "borsh": "https://github.com/defactojob/borsh-js#field-mapper", - "solray": "git+https://github.com/defactojob/solray" + "borsh": "https://github.com/defactojob/borsh-js#field-mapper" }, "browserslist": [ ">0.2%", diff --git a/src/client.ts b/src/client.ts index 1082749..100d011 100644 --- a/src/client.ts +++ b/src/client.ts @@ -25,7 +25,7 @@ import { SRM_DECIMALS, TOKEN_PROGRAM_ID } from '@project-serum/serum/lib/token-i import { Order } from '@project-serum/serum/lib/market'; import Wallet from '@project-serum/sol-wallet-adapter'; import { makeCancelOrderInstruction, makeSettleFundsInstruction } from './instruction'; -// import { Aggregator } from './schema' +import { Aggregator } from './schema' export class MangoGroup { publicKey: PublicKey; @@ -58,11 +58,11 @@ export class MangoGroup { connection: Connection, ): Promise { - // const aggs = await Promise.all(this.oracles.map((pk) => (Aggregator.loadWithConnection(pk, connection)))) - // return aggs.map((agg) => (agg.answer.median.toNumber())).concat(1.0) + const aggs = await Promise.all(this.oracles.map((pk) => (Aggregator.loadWithConnection(pk, connection)))) + return aggs.map((agg) => (agg.answer.median.toNumber())).concat(1.0) - const oracleAccs = await getMultipleAccounts(connection, this.oracles); - return oracleAccs.map((oa) => decodeAggregatorInfo(oa.accountInfo).submissionValue).concat(1.0) + // const oracleAccs = await getMultipleAccounts(connection, this.oracles); + // return oracleAccs.map((oa) => decodeAggregatorInfo(oa.accountInfo).submissionValue).concat(1.0) } getMarketIndex(spotMarket: Market): number { diff --git a/src/schema.ts b/src/schema.ts index 8efeb7e..0d1e3ce 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -1,407 +1,406 @@ -// import { PublicKey } from "solray" -// import BN from "bn.js" -// import { deserialize, serialize } from "borsh" -// import {Connection} from "@solana/web3.js"; +import BN from "bn.js" +import { deserialize, serialize } from "borsh" +import { Connection, PublicKey } from '@solana/web3.js'; + + +// const conn = new Connection("https://devnet.solana.com", 'singleGossip') + +const MAX_ORACLES = 13 + +const boolMapper = { + encode: boolToInt, + decode: intToBool, +} + +const pubkeyMapper = { + encode: (key: PublicKey) => { + // if (key.constructor == PublicKey) { + // // key. + // } else { + // key + // } + // TODO: support either account or public key + return key.toBuffer() + }, + + decode: (buf: Uint8Array) => { + return new PublicKey(buf) + }, +} + +// support strings that can be contained in at most 32 bytes +const str32Mapper = { + encode: (str: String) => { + str = str.substr(0, 32).padEnd(32) + return Buffer.from(str, "utf8").slice(0, 32) // truncate at 32 bytes + }, + + decode: (bytes: Uint8Array) => { + return Buffer.from(bytes).toString("utf8").trim() + }, +} + +const u64Date = { + encode: (date: Date) => { + return new BN(Math.floor(date.getTime() / 1000)) + }, + + decode: (unixtime: BN) => { + return new Date(unixtime.toNumber() * 1000) + }, +} + +export abstract class Serialization { + public static async loadWithConnection( + this: { new (data: any): T }, + key: PublicKey, + connection: Connection + ): Promise { + const info = await connection.getAccountInfo(key) + if (!info) { + throw new Error("account does not exist") + } + + return deserialize(schema, this, info.data) + } + // public static async load( + // this: { new (data: any): T }, + // key: PublicKey + // ): Promise { + // const info = await conn.getAccountInfo(key, "recent") + // if (!info) { + // throw new Error("account does not exist") + // } + // + // return deserialize(schema, this, info.data) + // } + + public static deserialize(this: { new (data: any): T }, data: Buffer): T { + return deserialize(schema, this, data) + } + + public static serialize( + this: { new (data: any): T }, + data: object + ): Buffer { + return new this(data).serialize() + } + + public serialize(): Buffer { + let buf = Buffer.from(serialize(schema, this)) + if (buf.length == 0) { + throw new Error("serialized buffer is 0. something wrong with schema") + } + return buf + } + + // public toJSON(pretty = true) { + // return JSON.stringify( + // this[Serialization.DATA_KEY], + // jsonReplacer, + // pretty ? 2 : 0 + // ) + // } + + // public static DATA_KEY = Symbol("DATA") + + constructor(data) { + // this[Serialization.DATA_KEY] = data + Object.assign(this, data) + } +} + +class Submission { + public updatedAt!: BN + public value!: BN + public oracle!: PublicKey + + public static schema = { + kind: "struct", + fields: [ + ["updatedAt", "u64"], + ["value", "u64"], + ["oracle", [32], pubkeyMapper], + ], + } + + constructor(data: any) { + Object.assign(this, data) + } +} + +export interface IAggregatorConfig { + decimals: number + description: string + restartDelay: number + rewardAmount: number + maxSubmissions: number + minSubmissions: number + rewardTokenAccount: PublicKey +} + +export class AggregatorConfig + extends Serialization + implements IAggregatorConfig { + public decimals!: number + public description!: string + public restartDelay!: number + public rewardAmount!: number + public maxSubmissions!: number + public minSubmissions!: number + public rewardTokenAccount!: PublicKey + + public static schema = { + kind: "struct", + fields: [ + ["description", [32], str32Mapper], + ["decimals", "u8"], + ["restartDelay", "u8"], + ["maxSubmissions", "u8"], + ["minSubmissions", "u8"], + ["rewardAmount", "u64"], + ["rewardTokenAccount", [32], pubkeyMapper], + ], + } +} + +export class Submissions extends Serialization { + public isInitialized!: boolean + public submissions!: Submission[] + + public static size = 625 + public static schema = { + kind: "struct", + fields: [ + ["isInitialized", "u8", boolMapper], + ["submissions", [Submission, MAX_ORACLES]], + ], + } + + // if not already submitted, and has empty spot + public canSubmit(pk: PublicKey, cfg: AggregatorConfig): boolean { + if (this.hadSubmitted(pk)) { + return false + } + + let emptyIndex = this.submissions.findIndex((s) => { + return s.updatedAt.isZero() + }) + + return emptyIndex > 0 && emptyIndex < cfg.maxSubmissions + } + + public hadSubmitted(pk: PublicKey): boolean { + return !!this.submissions.find((s) => { + return s.oracle.equals(pk) + }) + } +} + +export class Round extends Serialization { + public id!: BN + public createdAt!: BN + public updatedAt!: BN + + public static schema = { + kind: "struct", + fields: [ + ["id", "u64"], + ["createdAt", "u64"], + ["updatedAt", "u64"], + ], + } +} + +export class Answer extends Serialization { + public roundID!: BN + public median!: BN + public createdAt!: BN + public updatedAt!: BN + + public static schema = { + kind: "struct", + fields: [ + ["roundID", "u64"], + ["median", "u64"], + ["createdAt", "u64"], + ["updatedAt", "u64"], + ], + } +} + +export class Aggregator extends Serialization { + public static size = 229 + + public config!: AggregatorConfig + public roundSubmissions!: PublicKey + public answerSubmissions!: PublicKey + public answer!: Answer + public round!: Round + + public static schema = { + kind: "struct", + fields: [ + ["config", AggregatorConfig], + ["isInitialized", "u8", boolMapper], + ["owner", [32], pubkeyMapper], + ["round", Round], + ["roundSubmissions", [32], pubkeyMapper], + ["answer", Answer], + ["answerSubmissions", [32], pubkeyMapper], + ], + } + + +} + +abstract class InstructionSerialization extends Serialization { + public serialize(): Buffer { + return new Instruction({ [this.constructor.name]: this }).serialize() + } +} + +export class Initialize extends InstructionSerialization { + // public submitInterval!: number + // public minSubmissionValue!: number + // public maxSubmissionValue!: number + // public submissionDecimals!: number + // /// A short description of what is being reported + // public description!: string + + public static schema = { + kind: "struct", + fields: [["config", AggregatorConfig]], + } +} + +export class Configure extends InstructionSerialization { + public static schema = { + kind: "struct", + fields: [["config", AggregatorConfig]], + } +} + +export class AddOracle extends InstructionSerialization { + public static schema = { + kind: "struct", + fields: [["description", [32], str32Mapper]], + } +} + +export class RemoveOracle extends InstructionSerialization { + public static schema = { + kind: "struct", + fields: [], + } +} + +export class Withdraw extends InstructionSerialization { + public static schema = { + kind: "struct", + fields: [["faucetOwnerSeed", ["u8"]]], + } +} + +export class Submit extends InstructionSerialization { + public static schema = { + kind: "struct", + fields: [ + ["round_id", "u64"], + ["value", "u64"], + ], + } +} + +export class Instruction extends Serialization { + public enum!: string + + public static schema = { + kind: "enum", + field: "enum", + values: [ + [Initialize.name, Initialize], + [Configure.name, Configure], + [AddOracle.name, AddOracle], + [RemoveOracle.name, RemoveOracle], + [Submit.name, Submit], + ], + } + + public constructor(prop: { [key: string]: any }) { + super({}) + // deserializer calls the construction with `{ [enum]: value }`, so we need + // to figure out the enum type + // + // expect only one key-value (what a retarded interface) + for (let key of Object.keys(prop)) { + this.enum = key + this[key] = prop[key] + return + } + + throw new Error("not an expected enum object") + } + + public get value() { + return this[this.enum] + } +} + +function intToBool(i: number) { + if (i == 0) { + return false + } else { + return true + } +} + +function boolToInt(t: boolean) { + if (t) { + return 1 + } else { + return 0 + } +} + +export class Oracle extends Serialization { + public static size = 113 + public allowStartRound!: BN + public withdrawable!: BN + + public static schema = { + kind: "struct", + fields: [ + ["description", [32], str32Mapper], + ["isInitialized", "u8", boolMapper], + ["withdrawable", "u64"], + ["allowStartRound", "u64"], + ["aggregator", [32], pubkeyMapper], + ["owner", [32], pubkeyMapper], + ], + } + + public canStartNewRound(round: BN): boolean { + return this.allowStartRound.lte(round) + } +} + +// if there is optional or variable length items, what is: borsh_utils::get_packed_len::()? // -// -// // const conn = new Connection("https://devnet.solana.com", 'singleGossip') -// -// const MAX_ORACLES = 13 -// -// const boolMapper = { -// encode: boolToInt, -// decode: intToBool, -// } -// -// const pubkeyMapper = { -// encode: (key: PublicKey) => { -// // if (key.constructor == PublicKey) { -// // // key. -// // } else { -// // key -// // } -// // TODO: support either account or public key -// return key.toBuffer() -// }, -// -// decode: (buf: Uint8Array) => { -// return new PublicKey(buf) -// }, -// } -// -// // support strings that can be contained in at most 32 bytes -// const str32Mapper = { -// encode: (str: String) => { -// str = str.substr(0, 32).padEnd(32) -// return Buffer.from(str, "utf8").slice(0, 32) // truncate at 32 bytes -// }, -// -// decode: (bytes: Uint8Array) => { -// return Buffer.from(bytes).toString("utf8").trim() -// }, -// } -// -// const u64Date = { -// encode: (date: Date) => { -// return new BN(Math.floor(date.getTime() / 1000)) -// }, -// -// decode: (unixtime: BN) => { -// return new Date(unixtime.toNumber() * 1000) -// }, -// } -// -// export abstract class Serialization { -// public static async loadWithConnection( -// this: { new (data: any): T }, -// key: PublicKey, -// connection: Connection -// ): Promise { -// const info = await connection.getAccountInfo(key) -// if (!info) { -// throw new Error("account does not exist") -// } -// -// return deserialize(schema, this, info.data) -// } -// // public static async load( -// // this: { new (data: any): T }, -// // key: PublicKey -// // ): Promise { -// // const info = await conn.getAccountInfo(key, "recent") -// // if (!info) { -// // throw new Error("account does not exist") -// // } -// // -// // return deserialize(schema, this, info.data) -// // } -// -// public static deserialize(this: { new (data: any): T }, data: Buffer): T { -// return deserialize(schema, this, data) -// } -// -// public static serialize( -// this: { new (data: any): T }, -// data: object -// ): Buffer { -// return new this(data).serialize() -// } -// -// public serialize(): Buffer { -// let buf = Buffer.from(serialize(schema, this)) -// if (buf.length == 0) { -// throw new Error("serialized buffer is 0. something wrong with schema") -// } -// return buf -// } -// -// // public toJSON(pretty = true) { -// // return JSON.stringify( -// // this[Serialization.DATA_KEY], -// // jsonReplacer, -// // pretty ? 2 : 0 -// // ) -// // } -// -// // public static DATA_KEY = Symbol("DATA") -// -// constructor(data) { -// // this[Serialization.DATA_KEY] = data -// Object.assign(this, data) -// } -// } -// -// class Submission { -// public updatedAt!: BN -// public value!: BN -// public oracle!: PublicKey -// -// public static schema = { -// kind: "struct", -// fields: [ -// ["updatedAt", "u64"], -// ["value", "u64"], -// ["oracle", [32], pubkeyMapper], -// ], -// } -// -// constructor(data: any) { -// Object.assign(this, data) -// } -// } -// -// export interface IAggregatorConfig { -// decimals: number -// description: string -// restartDelay: number -// rewardAmount: number -// maxSubmissions: number -// minSubmissions: number -// rewardTokenAccount: PublicKey -// } -// -// export class AggregatorConfig -// extends Serialization -// implements IAggregatorConfig { -// public decimals!: number -// public description!: string -// public restartDelay!: number -// public rewardAmount!: number -// public maxSubmissions!: number -// public minSubmissions!: number -// public rewardTokenAccount!: PublicKey -// -// public static schema = { -// kind: "struct", -// fields: [ -// ["description", [32], str32Mapper], -// ["decimals", "u8"], -// ["restartDelay", "u8"], -// ["maxSubmissions", "u8"], -// ["minSubmissions", "u8"], -// ["rewardAmount", "u64"], -// ["rewardTokenAccount", [32], pubkeyMapper], -// ], -// } -// } -// -// export class Submissions extends Serialization { -// public isInitialized!: boolean -// public submissions!: Submission[] -// -// public static size = 625 -// public static schema = { -// kind: "struct", -// fields: [ -// ["isInitialized", "u8", boolMapper], -// ["submissions", [Submission, MAX_ORACLES]], -// ], -// } -// -// // if not already submitted, and has empty spot -// public canSubmit(pk: PublicKey, cfg: AggregatorConfig): boolean { -// if (this.hadSubmitted(pk)) { -// return false -// } -// -// let emptyIndex = this.submissions.findIndex((s) => { -// return s.updatedAt.isZero() -// }) -// -// return emptyIndex > 0 && emptyIndex < cfg.maxSubmissions -// } -// -// public hadSubmitted(pk: PublicKey): boolean { -// return !!this.submissions.find((s) => { -// return s.oracle.equals(pk) -// }) -// } -// } -// -// export class Round extends Serialization { -// public id!: BN -// public createdAt!: BN -// public updatedAt!: BN -// -// public static schema = { -// kind: "struct", -// fields: [ -// ["id", "u64"], -// ["createdAt", "u64"], -// ["updatedAt", "u64"], -// ], -// } -// } -// -// export class Answer extends Serialization { -// public roundID!: BN -// public median!: BN -// public createdAt!: BN -// public updatedAt!: BN -// -// public static schema = { -// kind: "struct", -// fields: [ -// ["roundID", "u64"], -// ["median", "u64"], -// ["createdAt", "u64"], -// ["updatedAt", "u64"], -// ], -// } -// } -// -// export class Aggregator extends Serialization { -// public static size = 229 -// -// public config!: AggregatorConfig -// public roundSubmissions!: PublicKey -// public answerSubmissions!: PublicKey -// public answer!: Answer -// public round!: Round -// -// public static schema = { -// kind: "struct", -// fields: [ -// ["config", AggregatorConfig], -// ["isInitialized", "u8", boolMapper], -// ["owner", [32], pubkeyMapper], -// ["round", Round], -// ["roundSubmissions", [32], pubkeyMapper], -// ["answer", Answer], -// ["answerSubmissions", [32], pubkeyMapper], -// ], -// } -// -// -// } -// -// abstract class InstructionSerialization extends Serialization { -// public serialize(): Buffer { -// return new Instruction({ [this.constructor.name]: this }).serialize() -// } -// } -// -// export class Initialize extends InstructionSerialization { -// // public submitInterval!: number -// // public minSubmissionValue!: number -// // public maxSubmissionValue!: number -// // public submissionDecimals!: number -// // /// A short description of what is being reported -// // public description!: string -// -// public static schema = { -// kind: "struct", -// fields: [["config", AggregatorConfig]], -// } -// } -// -// export class Configure extends InstructionSerialization { -// public static schema = { -// kind: "struct", -// fields: [["config", AggregatorConfig]], -// } -// } -// -// export class AddOracle extends InstructionSerialization { -// public static schema = { -// kind: "struct", -// fields: [["description", [32], str32Mapper]], -// } -// } -// -// export class RemoveOracle extends InstructionSerialization { -// public static schema = { -// kind: "struct", -// fields: [], -// } -// } -// -// export class Withdraw extends InstructionSerialization { -// public static schema = { -// kind: "struct", -// fields: [["faucetOwnerSeed", ["u8"]]], -// } -// } -// -// export class Submit extends InstructionSerialization { -// public static schema = { -// kind: "struct", -// fields: [ -// ["round_id", "u64"], -// ["value", "u64"], -// ], -// } -// } -// -// export class Instruction extends Serialization { -// public enum!: string -// -// public static schema = { -// kind: "enum", -// field: "enum", -// values: [ -// [Initialize.name, Initialize], -// [Configure.name, Configure], -// [AddOracle.name, AddOracle], -// [RemoveOracle.name, RemoveOracle], -// [Submit.name, Submit], -// ], -// } -// -// public constructor(prop: { [key: string]: any }) { -// super({}) -// // deserializer calls the construction with `{ [enum]: value }`, so we need -// // to figure out the enum type -// // -// // expect only one key-value (what a retarded interface) -// for (let key of Object.keys(prop)) { -// this.enum = key -// this[key] = prop[key] -// return -// } -// -// throw new Error("not an expected enum object") -// } -// -// public get value() { -// return this[this.enum] -// } -// } -// -// function intToBool(i: number) { -// if (i == 0) { -// return false -// } else { -// return true -// } -// } -// -// function boolToInt(t: boolean) { -// if (t) { -// return 1 -// } else { -// return 0 -// } -// } -// -// export class Oracle extends Serialization { -// public static size = 113 -// public allowStartRound!: BN -// public withdrawable!: BN -// -// public static schema = { -// kind: "struct", -// fields: [ -// ["description", [32], str32Mapper], -// ["isInitialized", "u8", boolMapper], -// ["withdrawable", "u64"], -// ["allowStartRound", "u64"], -// ["aggregator", [32], pubkeyMapper], -// ["owner", [32], pubkeyMapper], -// ], -// } -// -// public canStartNewRound(round: BN): boolean { -// return this.allowStartRound.lte(round) -// } -// } -// -// // if there is optional or variable length items, what is: borsh_utils::get_packed_len::()? -// // -// // would panic given variable sized types -// -// export const schema = new Map([ -// [Aggregator, Aggregator.schema], -// [Oracle, Oracle.schema], -// [Round, Round.schema], -// [Answer, Answer.schema], -// [AggregatorConfig, AggregatorConfig.schema], -// [Submissions, Submissions.schema], -// [Submission, Submission.schema], -// -// [Instruction, Instruction.schema], -// [Initialize, Initialize.schema], -// [AddOracle, AddOracle.schema], -// [Submit, Submit.schema], -// -// ] as any) as any +// would panic given variable sized types + +export const schema = new Map([ + [Aggregator, Aggregator.schema], + [Oracle, Oracle.schema], + [Round, Round.schema], + [Answer, Answer.schema], + [AggregatorConfig, AggregatorConfig.schema], + [Submissions, Submissions.schema], + [Submission, Submission.schema], + + [Instruction, Instruction.schema], + [Initialize, Initialize.schema], + [AddOracle, AddOracle.schema], + [Submit, Submit.schema], + +] as any) as any