ad back borsh

This commit is contained in:
dd 2021-02-22 20:16:59 -05:00
parent 270df3cb2c
commit 55660d048f
4 changed files with 428 additions and 413 deletions

View File

@ -60,7 +60,8 @@
"@project-serum/sol-wallet-adapter": "^0.1.4",
"@solana/web3.js": "^0.90.0",
"bn.js": "^5.1.2",
"buffer-layout": "^1.2.0"
"buffer-layout": "^1.2.0",
"borsh": "https://github.com/defactojob/borsh-js#field-mapper"
},
"browserslist": [

View File

@ -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<number[]> {
// 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 {

View File

@ -1,406 +1,406 @@
// import BN from "bn.js"
// import { deserialize, serialize } from "borsh"
// import { Connection, PublicKey } 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<T>(
this: { new (data: any): T },
key: PublicKey,
connection: Connection
): Promise<T> {
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<T>(
// this: { new (data: any): T },
// key: PublicKey
// ): Promise<T> {
// 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<T>(this: { new (data: any): T }, data: Buffer): T {
return deserialize(schema, this, data)
}
public static serialize<T extends Serialization>(
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::<Submission>()?
//
//
// // 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<T>(
// this: { new (data: any): T },
// key: PublicKey,
// connection: Connection
// ): Promise<T> {
// 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<T>(
// // this: { new (data: any): T },
// // key: PublicKey
// // ): Promise<T> {
// // 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<T>(this: { new (data: any): T }, data: Buffer): T {
// return deserialize(schema, this, data)
// }
//
// public static serialize<T extends Serialization>(
// 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::<Submission>()?
// //
// // 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

View File

@ -661,7 +661,7 @@
dependencies:
"@babel/types" "^7.3.0"
"@types/bn.js@^4.11.6":
"@types/bn.js@^4.11.5", "@types/bn.js@^4.11.6":
version "4.11.6"
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c"
integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==
@ -1184,6 +1184,15 @@ boolbase@~1.0.0:
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
"borsh@https://github.com/defactojob/borsh-js#field-mapper":
version "0.3.1"
resolved "https://github.com/defactojob/borsh-js#33a0d24af281112c0a48efb3fa503f3212443de9"
dependencies:
"@types/bn.js" "^4.11.5"
bn.js "^5.0.0"
bs58 "^4.0.0"
text-encoding-utf-8 "^1.0.2"
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@ -1243,7 +1252,7 @@ bs-logger@0.x:
dependencies:
fast-json-stable-stringify "2.x"
bs58@^4.0.1:
bs58@^4.0.0, bs58@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo=
@ -4782,6 +4791,11 @@ test-exclude@^6.0.0:
glob "^7.1.4"
minimatch "^3.0.4"
text-encoding-utf-8@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13"
integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==
text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"