borsh serialization
This commit is contained in:
parent
89c1e56aa9
commit
d45b362dc6
|
@ -6,7 +6,8 @@
|
|||
"testnetDefaultChannel": "v1.4.8",
|
||||
"scripts": {
|
||||
"solink": "ts-node src/cli.ts",
|
||||
"build:program": "solray build program"
|
||||
"build:program": "solray build program",
|
||||
"watch": "yarn ts-node-dev --respawn"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
@ -23,6 +24,7 @@
|
|||
"@tsconfig/recommended": "^1.0.1",
|
||||
"@types/node": "^14.14.12",
|
||||
"ts-node": "^9.1.1",
|
||||
"ts-node-dev": "^1.1.1",
|
||||
"typescript": "^4.1.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,10 @@ import {
|
|||
// @ts-ignore
|
||||
// import BufferLayout from "buffer-layout";
|
||||
|
||||
import { schema } from "./schema"
|
||||
import * as encoding from "./schema"
|
||||
import { deserialize, serialize } from "borsh"
|
||||
|
||||
export const AggregatorLayout = BufferLayout.struct([
|
||||
BufferLayout.blob(4, "submitInterval"),
|
||||
uint64("minSubmissionValue"),
|
||||
|
@ -132,43 +136,16 @@ export default class FluxAggregator extends BaseProgram {
|
|||
}
|
||||
|
||||
private initializeInstruction(params: InitializeInstructionParams): TransactionInstruction {
|
||||
let {
|
||||
aggregator,
|
||||
description,
|
||||
submitInterval,
|
||||
minSubmissionValue,
|
||||
maxSubmissionValue,
|
||||
submissionDecimals,
|
||||
owner,
|
||||
} = params;
|
||||
let { aggregator, owner } = params
|
||||
|
||||
// FIXME: hmm... should this throw error or what?
|
||||
description = description.substr(0, 32).toUpperCase().padEnd(32)
|
||||
const input = encoding.Initialize.serialize(params)
|
||||
// console.log(input.toString("hex"))
|
||||
|
||||
const layout = BufferLayout.struct([
|
||||
BufferLayout.u8("instruction"),
|
||||
BufferLayout.blob(4, "submitInterval"),
|
||||
uint64("minSubmissionValue"),
|
||||
uint64("maxSubmissionValue"),
|
||||
BufferLayout.u8("submissionDecimals"),
|
||||
BufferLayout.blob(32, "description"),
|
||||
]);
|
||||
|
||||
const buf = Buffer.allocUnsafe(4);
|
||||
buf.writeUInt32LE(submitInterval);
|
||||
|
||||
return this.instructionEncode(layout, {
|
||||
instruction: 0, // initialize instruction
|
||||
submitInterval: buf,
|
||||
minSubmissionValue: u64LEBuffer(minSubmissionValue),
|
||||
maxSubmissionValue: u64LEBuffer(maxSubmissionValue),
|
||||
submissionDecimals,
|
||||
description: Buffer.from(description),
|
||||
}, [
|
||||
return this.instruction(input, [
|
||||
SYSVAR_RENT_PUBKEY,
|
||||
{ write: aggregator },
|
||||
owner
|
||||
]);
|
||||
owner,
|
||||
])
|
||||
}
|
||||
|
||||
public async addOracle(params: AddOracleParams): Promise<Account> {
|
||||
|
|
|
@ -150,9 +150,9 @@ cli
|
|||
submitInterval: parseInt(submitInterval),
|
||||
minSubmissionValue: BigInt(minSubmissionValue),
|
||||
maxSubmissionValue: BigInt(maxSubmissionValue),
|
||||
description: feedName.substr(0, 32).padEnd(32),
|
||||
description: feedName,
|
||||
submissionDecimals,
|
||||
owner: wallet.account
|
||||
owner: wallet.account,
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -213,7 +213,7 @@ cli
|
|||
const { feedAddress, oracleAddress } = opts
|
||||
|
||||
const { aggregator } = await AppContext.forAdmin()
|
||||
|
||||
|
||||
await aggregator.removeOracle({
|
||||
aggregator: new PublicKey(feedAddress),
|
||||
oracle: new PublicKey(oracleAddress),
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
import { PublicKey, Account } from "solray"
|
||||
import BN from "bn.js"
|
||||
import { deserialize, serialize } from "borsh"
|
||||
|
||||
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).toUpperCase().padEnd(32)
|
||||
return Buffer.from(str, "utf8").slice(0, 32) // truncate at 32 bytes
|
||||
},
|
||||
|
||||
decode: (bytes: Uint8Array) => {
|
||||
return Buffer.from(bytes).toString("utf8").trim()
|
||||
},
|
||||
}
|
||||
|
||||
abstract class Serialization {
|
||||
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 {
|
||||
return Buffer.from(serialize(schema, this))
|
||||
}
|
||||
|
||||
constructor(data) {
|
||||
Object.assign(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class Submission {
|
||||
public time!: BN
|
||||
public value!: BN
|
||||
public oracle!: PublicKey
|
||||
|
||||
public static schema = {
|
||||
kind: "struct",
|
||||
fields: [
|
||||
["time", "u64"],
|
||||
["value", "u64"],
|
||||
["oracle", [32], pubkeyMapper],
|
||||
],
|
||||
}
|
||||
|
||||
constructor(data: any) {
|
||||
Object.assign(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
export class Aggregator extends Serialization {
|
||||
public submitInterval!: number
|
||||
public minSubmissionValue!: BN
|
||||
public maxSubmissionValue!: BN
|
||||
public submissions!: Submission[]
|
||||
|
||||
public static schema = {
|
||||
kind: "struct",
|
||||
fields: [
|
||||
["submitInterval", "u32"],
|
||||
["minSubmissionValue", "u64"],
|
||||
["maxSubmissionValue", "u64"],
|
||||
["submissionDecimals", "u8"],
|
||||
["description", [32], str32Mapper], // fixed-sized-u8-array
|
||||
["isInitialized", "u8", boolMapper], // no mapping for bool?
|
||||
["owner", [32], pubkeyMapper],
|
||||
["submissions", [Submission, 12]],
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
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: [
|
||||
["submitInterval", "u32"],
|
||||
["minSubmissionValue", "u64"],
|
||||
["maxSubmissionValue", "u64"],
|
||||
["submissionDecimals", "u8"],
|
||||
["description", [32], str32Mapper],
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
export class Instruction extends Serialization {
|
||||
public enum!: string
|
||||
|
||||
public static schema = {
|
||||
kind: "enum",
|
||||
field: "enum",
|
||||
values: [[Initialize.name, Initialize]],
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 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],
|
||||
[Submission, Submission.schema],
|
||||
[Initialize, Initialize.schema],
|
||||
[Instruction, Instruction.schema],
|
||||
] as any) as any
|
|
@ -94,7 +94,9 @@ export async function walletFromEnv(key: string, conn: Connection): Promise<Wall
|
|||
throw new Error(`Set ${key} in .env to be a mnemonic`)
|
||||
}
|
||||
|
||||
return Wallet.fromMnemonic(mnemonic, conn)
|
||||
const wallet = await Wallet.fromMnemonic(mnemonic, conn)
|
||||
console.log("using wallet:", wallet.address)
|
||||
return wallet
|
||||
}
|
||||
|
||||
export async function openDeployer(): Promise<Deployer> {
|
||||
|
|
Loading…
Reference in New Issue