removed borsh

This commit is contained in:
dd 2021-02-22 20:04:26 -05:00
parent 1227d57174
commit 3b128fa635
4 changed files with 413 additions and 428 deletions

View File

@ -59,8 +59,7 @@
"@project-serum/sol-wallet-adapter": "^0.1.4", "@project-serum/sol-wallet-adapter": "^0.1.4",
"@solana/web3.js": "^0.90.0", "@solana/web3.js": "^0.90.0",
"bn.js": "^5.1.2", "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": [ "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 { Order } from '@project-serum/serum/lib/market';
import Wallet from '@project-serum/sol-wallet-adapter'; import Wallet from '@project-serum/sol-wallet-adapter';
import { makeCancelOrderInstruction, makeSettleFundsInstruction } from './instruction'; import { makeCancelOrderInstruction, makeSettleFundsInstruction } from './instruction';
import { Aggregator } from './schema' // import { Aggregator } from './schema'
export class MangoGroup { export class MangoGroup {
publicKey: PublicKey; publicKey: PublicKey;
@ -58,11 +58,11 @@ export class MangoGroup {
connection: Connection, connection: Connection,
): Promise<number[]> { ): Promise<number[]> {
const aggs = await Promise.all(this.oracles.map((pk) => (Aggregator.loadWithConnection(pk, connection)))) // const aggs = await Promise.all(this.oracles.map((pk) => (Aggregator.loadWithConnection(pk, connection))))
return aggs.map((agg) => (agg.answer.median.toNumber())).concat(1.0) // return aggs.map((agg) => (agg.answer.median.toNumber())).concat(1.0)
// const oracleAccs = await getMultipleAccounts(connection, this.oracles); const oracleAccs = await getMultipleAccounts(connection, this.oracles);
// return oracleAccs.map((oa) => decodeAggregatorInfo(oa.accountInfo).submissionValue).concat(1.0) return oracleAccs.map((oa) => decodeAggregatorInfo(oa.accountInfo).submissionValue).concat(1.0)
} }
getMarketIndex(spotMarket: Market): number { getMarketIndex(spotMarket: Market): number {

View File

@ -1,406 +1,406 @@
import BN from "bn.js" // import BN from "bn.js"
import { deserialize, serialize } from "borsh" // import { deserialize, serialize } from "borsh"
import { Connection, PublicKey } from '@solana/web3.js'; // import { Connection, PublicKey } from '@solana/web3.js';
//
//
// const conn = new Connection("https://devnet.solana.com", 'singleGossip') // // const conn = new Connection("https://devnet.solana.com", 'singleGossip')
//
const MAX_ORACLES = 13 // const MAX_ORACLES = 13
//
const boolMapper = { // const boolMapper = {
encode: boolToInt, // encode: boolToInt,
decode: intToBool, // decode: intToBool,
}
const pubkeyMapper = {
encode: (key: PublicKey) => {
// if (key.constructor == PublicKey) {
// // key.
// } else {
// key
// } // }
// TODO: support either account or public key //
return key.toBuffer() // const pubkeyMapper = {
}, // encode: (key: PublicKey) => {
// // if (key.constructor == PublicKey) {
decode: (buf: Uint8Array) => { // // // key.
return new PublicKey(buf) // // } else {
}, // // key
} // // }
// // TODO: support either account or public key
// support strings that can be contained in at most 32 bytes // return key.toBuffer()
const str32Mapper = { // },
encode: (str: String) => { //
str = str.substr(0, 32).padEnd(32) // decode: (buf: Uint8Array) => {
return Buffer.from(str, "utf8").slice(0, 32) // truncate at 32 bytes // return new PublicKey(buf)
}, // },
// }
decode: (bytes: Uint8Array) => { //
return Buffer.from(bytes).toString("utf8").trim() // // support strings that can be contained in at most 32 bytes
}, // const str32Mapper = {
} // encode: (str: String) => {
// str = str.substr(0, 32).padEnd(32)
const u64Date = { // return Buffer.from(str, "utf8").slice(0, 32) // truncate at 32 bytes
encode: (date: Date) => { // },
return new BN(Math.floor(date.getTime() / 1000)) //
}, // decode: (bytes: Uint8Array) => {
// return Buffer.from(bytes).toString("utf8").trim()
decode: (unixtime: BN) => { // },
return new Date(unixtime.toNumber() * 1000) // }
}, //
} // const u64Date = {
// encode: (date: Date) => {
export abstract class Serialization { // return new BN(Math.floor(date.getTime() / 1000))
public static async loadWithConnection<T>( // },
this: { new (data: any): T }, //
key: PublicKey, // decode: (unixtime: BN) => {
connection: Connection // return new Date(unixtime.toNumber() * 1000)
): Promise<T> { // },
const info = await connection.getAccountInfo(key) // }
if (!info) { //
throw new Error("account does not exist") // export abstract class Serialization {
} // public static async loadWithConnection<T>(
return deserialize(schema, this, info.data)
}
// public static async load<T>(
// this: { new (data: any): T }, // this: { new (data: any): T },
// key: PublicKey // key: PublicKey,
// connection: Connection
// ): Promise<T> { // ): Promise<T> {
// const info = await conn.getAccountInfo(key, "recent") // const info = await connection.getAccountInfo(key)
// if (!info) { // if (!info) {
// throw new Error("account does not exist") // throw new Error("account does not exist")
// } // }
// //
// return deserialize(schema, this, info.data) // return deserialize(schema, this, info.data)
// } // }
// // public static async load<T>(
public static deserialize<T>(this: { new (data: any): T }, data: Buffer): T { // // this: { new (data: any): T },
return deserialize(schema, this, data) // // key: PublicKey
} // // ): Promise<T> {
// // const info = await conn.getAccountInfo(key, "recent")
public static serialize<T extends Serialization>( // // if (!info) {
this: { new (data: any): T }, // // throw new Error("account does not exist")
data: object // // }
): Buffer { // //
return new this(data).serialize() // // return deserialize(schema, this, info.data)
} // // }
//
public serialize(): Buffer { // public static deserialize<T>(this: { new (data: any): T }, data: Buffer): T {
let buf = Buffer.from(serialize(schema, this)) // return deserialize(schema, this, data)
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") // public static serialize<T extends Serialization>(
// this: { new (data: any): T },
constructor(data) { // data: object
// this[Serialization.DATA_KEY] = data // ): Buffer {
Object.assign(this, data) // return new this(data).serialize()
} // }
} //
// public serialize(): Buffer {
class Submission { // let buf = Buffer.from(serialize(schema, this))
public updatedAt!: BN // if (buf.length == 0) {
public value!: BN // throw new Error("serialized buffer is 0. something wrong with schema")
public oracle!: PublicKey // }
// return buf
public static schema = { // }
kind: "struct", //
fields: [ // // public toJSON(pretty = true) {
["updatedAt", "u64"], // // return JSON.stringify(
["value", "u64"], // // this[Serialization.DATA_KEY],
["oracle", [32], pubkeyMapper], // // jsonReplacer,
], // // pretty ? 2 : 0
} // // )
// // }
constructor(data: any) { //
Object.assign(this, data) // // public static DATA_KEY = Symbol("DATA")
} //
} // constructor(data) {
// // this[Serialization.DATA_KEY] = data
export interface IAggregatorConfig { // Object.assign(this, data)
decimals: number // }
description: string // }
restartDelay: number //
rewardAmount: number // class Submission {
maxSubmissions: number // public updatedAt!: BN
minSubmissions: number // public value!: BN
rewardTokenAccount: PublicKey // public oracle!: PublicKey
} //
// public static schema = {
export class AggregatorConfig // kind: "struct",
extends Serialization // fields: [
implements IAggregatorConfig { // ["updatedAt", "u64"],
public decimals!: number // ["value", "u64"],
public description!: string // ["oracle", [32], pubkeyMapper],
public restartDelay!: number // ],
public rewardAmount!: number // }
public maxSubmissions!: number //
public minSubmissions!: number // constructor(data: any) {
public rewardTokenAccount!: PublicKey // Object.assign(this, data)
// }
public static schema = { // }
kind: "struct", //
fields: [ // export interface IAggregatorConfig {
["description", [32], str32Mapper], // decimals: number
["decimals", "u8"], // description: string
["restartDelay", "u8"], // restartDelay: number
["maxSubmissions", "u8"], // rewardAmount: number
["minSubmissions", "u8"], // maxSubmissions: number
["rewardAmount", "u64"], // minSubmissions: number
["rewardTokenAccount", [32], pubkeyMapper], // rewardTokenAccount: PublicKey
], // }
} //
} // export class AggregatorConfig
// extends Serialization
export class Submissions extends Serialization { // implements IAggregatorConfig {
public isInitialized!: boolean // public decimals!: number
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 description!: string
// public restartDelay!: number
public static schema = { // public rewardAmount!: number
kind: "struct", // public maxSubmissions!: number
fields: [["config", AggregatorConfig]], // public minSubmissions!: number
} // public rewardTokenAccount!: PublicKey
}
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) // public static schema = {
for (let key of Object.keys(prop)) { // kind: "struct",
this.enum = key // fields: [
this[key] = prop[key] // ["description", [32], str32Mapper],
return // ["decimals", "u8"],
} // ["restartDelay", "u8"],
// ["maxSubmissions", "u8"],
throw new Error("not an expected enum object") // ["minSubmissions", "u8"],
} // ["rewardAmount", "u64"],
// ["rewardTokenAccount", [32], pubkeyMapper],
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 class Submissions extends Serialization {
// public isInitialized!: boolean
export const schema = new Map([ // public submissions!: Submission[]
[Aggregator, Aggregator.schema], //
[Oracle, Oracle.schema], // public static size = 625
[Round, Round.schema], // public static schema = {
[Answer, Answer.schema], // kind: "struct",
[AggregatorConfig, AggregatorConfig.schema], // fields: [
[Submissions, Submissions.schema], // ["isInitialized", "u8", boolMapper],
[Submission, Submission.schema], // ["submissions", [Submission, MAX_ORACLES]],
// ],
[Instruction, Instruction.schema], // }
[Initialize, Initialize.schema], //
[AddOracle, AddOracle.schema], // // if not already submitted, and has empty spot
[Submit, Submit.schema], // public canSubmit(pk: PublicKey, cfg: AggregatorConfig): boolean {
// if (this.hadSubmitted(pk)) {
] as any) as any // 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

View File

@ -661,7 +661,7 @@
dependencies: dependencies:
"@babel/types" "^7.3.0" "@babel/types" "^7.3.0"
"@types/bn.js@^4.11.5", "@types/bn.js@^4.11.6": "@types/bn.js@^4.11.6":
version "4.11.6" version "4.11.6"
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c"
integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==
@ -1184,15 +1184,6 @@ boolbase@~1.0.0:
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= 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: brace-expansion@^1.1.7:
version "1.1.11" version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@ -1252,7 +1243,7 @@ bs-logger@0.x:
dependencies: dependencies:
fast-json-stable-stringify "2.x" fast-json-stable-stringify "2.x"
bs58@^4.0.0, bs58@^4.0.1: bs58@^4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo=
@ -4791,11 +4782,6 @@ test-exclude@^6.0.0:
glob "^7.1.4" glob "^7.1.4"
minimatch "^3.0.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: text-table@^0.2.0:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"