implement deployer class
This commit is contained in:
parent
9c6199e270
commit
e223511bc3
|
@ -13,6 +13,7 @@
|
|||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@ltd/j-toml": "^1.6.0",
|
||||
"@solana/web3.js": "^0.90.5",
|
||||
"buffer-layout": "^1.2.0",
|
||||
"commander": "^6.2.0",
|
||||
|
|
|
@ -28,7 +28,7 @@ import { decodeOracleInfo } from "./utils"
|
|||
// @ts-ignore
|
||||
// import BufferLayout from "buffer-layout";
|
||||
|
||||
import { schema } from "./schema"
|
||||
import { AggregatorConfig, IAggregatorConfig, schema } from "./schema"
|
||||
import * as encoding from "./schema"
|
||||
import { deserialize, serialize } from "borsh"
|
||||
import { conn } from "./context"
|
||||
|
@ -51,7 +51,7 @@ export const SubmissionLayout = BufferLayout.struct([
|
|||
])
|
||||
|
||||
interface InitializeParams {
|
||||
config: encoding.AggregatorConfig
|
||||
config: IAggregatorConfig
|
||||
owner: Account
|
||||
}
|
||||
|
||||
|
@ -90,15 +90,17 @@ interface SubmitParams {
|
|||
}
|
||||
|
||||
interface WithdrawParams {
|
||||
accounts: {
|
||||
aggregator: PublicKey
|
||||
// withdraw to
|
||||
receiver: PublicKey
|
||||
// withdraw amount
|
||||
amount: bigint
|
||||
tokenAccount: PublicKey
|
||||
tokenOwner: PublicKey
|
||||
// signer
|
||||
authority: Account
|
||||
|
||||
faucet: { write: PublicKey },
|
||||
faucetOwner: PublicKey,
|
||||
oracle: { write: PublicKey },
|
||||
oracleOwner: Account,
|
||||
receiver: { write: PublicKey },
|
||||
}
|
||||
|
||||
faucetOwnerSeed: Buffer
|
||||
}
|
||||
|
||||
interface WithdrawInstructionParams extends WithdrawParams {}
|
||||
|
@ -115,7 +117,9 @@ export default class FluxAggregator extends BaseProgram {
|
|||
const answer_submissions = new Account()
|
||||
const round_submissions = new Account()
|
||||
|
||||
const input = encoding.Initialize.serialize({ config: params.config })
|
||||
const input = encoding.Initialize.serialize({
|
||||
config: new AggregatorConfig(params.config),
|
||||
})
|
||||
|
||||
await this.sendTx(
|
||||
[
|
||||
|
@ -182,96 +186,95 @@ export default class FluxAggregator extends BaseProgram {
|
|||
return oracle
|
||||
}
|
||||
|
||||
public async oracleInfo(pubkey: PublicKey) {
|
||||
const info = await this.conn.getAccountInfo(pubkey)
|
||||
return decodeOracleInfo(info)
|
||||
}
|
||||
// public async oracleInfo(pubkey: PublicKey) {
|
||||
// const info = await this.conn.getAccountInfo(pubkey)
|
||||
// return decodeOracleInfo(info)
|
||||
// }
|
||||
|
||||
public async removeOracle(params: RemoveOracleParams): Promise<void> {
|
||||
await this.sendTx(
|
||||
[this.removeOracleInstruction(params)],
|
||||
[this.account, params.authority || this.wallet.account]
|
||||
)
|
||||
}
|
||||
// public async removeOracle(params: RemoveOracleParams): Promise<void> {
|
||||
// await this.sendTx(
|
||||
// [this.removeOracleInstruction(params)],
|
||||
// [this.account, params.authority || this.wallet.account]
|
||||
// )
|
||||
// }
|
||||
|
||||
private removeOracleInstruction(
|
||||
params: RemoveOracleInstructionParams
|
||||
): TransactionInstruction {
|
||||
const { authority, aggregator, oracle } = params
|
||||
// private removeOracleInstruction(
|
||||
// params: RemoveOracleInstructionParams
|
||||
// ): TransactionInstruction {
|
||||
// const { authority, aggregator, oracle } = params
|
||||
|
||||
const layout = BufferLayout.struct([
|
||||
BufferLayout.u8("instruction"),
|
||||
BufferLayout.blob(32, "oracle"),
|
||||
])
|
||||
// const layout = BufferLayout.struct([
|
||||
// BufferLayout.u8("instruction"),
|
||||
// BufferLayout.blob(32, "oracle"),
|
||||
// ])
|
||||
|
||||
return this.instructionEncode(
|
||||
layout,
|
||||
{
|
||||
instruction: 2, // remove oracle instruction
|
||||
oracle: oracle.toBuffer(),
|
||||
},
|
||||
[
|
||||
//
|
||||
{ write: aggregator },
|
||||
authority || this.wallet.account,
|
||||
]
|
||||
)
|
||||
}
|
||||
// return this.instructionEncode(
|
||||
// layout,
|
||||
// {
|
||||
// instruction: 2, // remove oracle instruction
|
||||
// oracle: oracle.toBuffer(),
|
||||
// },
|
||||
// [
|
||||
// //
|
||||
// { write: aggregator },
|
||||
// authority || this.wallet.account,
|
||||
// ]
|
||||
// )
|
||||
// }
|
||||
|
||||
public async submit(params: SubmitParams): Promise<void> {
|
||||
const input = encoding.Submit.serialize(params)
|
||||
|
||||
let auths = [
|
||||
SYSVAR_CLOCK_PUBKEY,
|
||||
...Object.values(params.accounts),
|
||||
]
|
||||
let auths = [SYSVAR_CLOCK_PUBKEY, ...Object.values(params.accounts)]
|
||||
|
||||
await this.sendTx(
|
||||
[
|
||||
this.instruction(input, auths),
|
||||
],
|
||||
[this.instruction(input, auths)],
|
||||
[this.account, params.accounts.oracle_owner]
|
||||
)
|
||||
}
|
||||
|
||||
public async withdraw(params: WithdrawParams): Promise<void> {
|
||||
const input = encoding.Withdraw.serialize(params)
|
||||
|
||||
let auths = [SPLToken.programID, ...Object.values(params.accounts)]
|
||||
|
||||
await this.sendTx(
|
||||
[this.withdrawInstruction(params)],
|
||||
[this.account, params.authority]
|
||||
[this.instruction(input, auths)],
|
||||
[this.account, params.accounts.oracleOwner]
|
||||
)
|
||||
}
|
||||
|
||||
private withdrawInstruction(
|
||||
params: WithdrawInstructionParams
|
||||
): TransactionInstruction {
|
||||
const {
|
||||
aggregator,
|
||||
receiver,
|
||||
amount,
|
||||
tokenOwner,
|
||||
tokenAccount,
|
||||
authority,
|
||||
} = params
|
||||
// private withdrawInstruction(
|
||||
// params: WithdrawInstructionParams
|
||||
// ): TransactionInstruction {
|
||||
// const {
|
||||
// aggregator,
|
||||
// receiver,
|
||||
// amount,
|
||||
// tokenOwner,
|
||||
// tokenAccount,
|
||||
// authority,
|
||||
// } = params
|
||||
|
||||
const layout = BufferLayout.struct([
|
||||
BufferLayout.u8("instruction"),
|
||||
uint64("amount"),
|
||||
])
|
||||
// const layout = BufferLayout.struct([
|
||||
// BufferLayout.u8("instruction"),
|
||||
// uint64("amount"),
|
||||
// ])
|
||||
|
||||
return this.instructionEncode(
|
||||
layout,
|
||||
{
|
||||
instruction: 4, // withdraw instruction
|
||||
amount: u64LEBuffer(amount),
|
||||
},
|
||||
[
|
||||
{ write: aggregator },
|
||||
{ write: tokenAccount },
|
||||
{ write: receiver },
|
||||
SPLToken.programID,
|
||||
tokenOwner,
|
||||
{ write: authority },
|
||||
]
|
||||
)
|
||||
}
|
||||
// return this.instructionEncode(
|
||||
// layout,
|
||||
// {
|
||||
// instruction: 4, // withdraw instruction
|
||||
// amount: u64LEBuffer(amount),
|
||||
// },
|
||||
// [
|
||||
// { write: aggregator },
|
||||
// { write: tokenAccount },
|
||||
// { write: receiver },
|
||||
// SPLToken.programID,
|
||||
// tokenOwner,
|
||||
// { write: authority },
|
||||
// ]
|
||||
// )
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ interface SubmitterConfig {
|
|||
|
||||
export class Submitter {
|
||||
public aggregator!: Aggregator
|
||||
public oracle!: Oracle
|
||||
public roundSubmissions!: Submissions
|
||||
public answerSubmissions!: Submissions
|
||||
public program: FluxAggregator
|
||||
|
@ -40,7 +41,7 @@ export class Submitter {
|
|||
public oraclePK: PublicKey,
|
||||
private oracleOwnerWallet: Wallet,
|
||||
private priceFeed: IPriceFeed,
|
||||
private cfg: SubmitterConfig,
|
||||
private cfg: SubmitterConfig
|
||||
) {
|
||||
this.program = new FluxAggregator(this.oracleOwnerWallet, programID)
|
||||
|
||||
|
@ -66,6 +67,10 @@ export class Submitter {
|
|||
await Promise.all([this.observeAggregatorState(), this.observePriceFeed()])
|
||||
}
|
||||
|
||||
public async withdrawRewards() {
|
||||
|
||||
}
|
||||
|
||||
private async observeAggregatorState() {
|
||||
conn.onAccountChange(this.aggregatorPK, async (info) => {
|
||||
this.aggregator = Aggregator.deserialize(info.data)
|
||||
|
@ -96,9 +101,13 @@ export class Submitter {
|
|||
|
||||
this.currentValue = new BN(price.value)
|
||||
|
||||
const valueDiff = this.aggregator.answer.median.sub(this.currentValue).abs()
|
||||
const valueDiff = this.aggregator.answer.median
|
||||
.sub(this.currentValue)
|
||||
.abs()
|
||||
if (valueDiff.lten(this.cfg.minValueChangeForNewRound)) {
|
||||
this.logger.debug("price did not change enough to start a new round", { diff: valueDiff.toNumber()});
|
||||
this.logger.debug("price did not change enough to start a new round", {
|
||||
diff: valueDiff.toNumber(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -110,6 +119,9 @@ export class Submitter {
|
|||
// TODO: make it possible to be triggered by chainlink task
|
||||
// TODO: If from chainlink node, update state before running
|
||||
|
||||
this.oracle = await Oracle.load(this.oraclePK)
|
||||
this.logger.debug("oracle", { oracle: this.oracle })
|
||||
|
||||
const { round } = this.aggregator
|
||||
|
||||
if (this.canSubmitToCurrentRound) {
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
import toml from "@ltd/j-toml"
|
||||
|
||||
import fs from "fs"
|
||||
import { Oracle } from "./schema"
|
||||
|
||||
function loadJSON(file: string): any {
|
||||
return JSON.parse(fs.readFileSync(file, "utf8"))
|
||||
}
|
||||
|
||||
const aggregatorConfigDefaults = {
|
||||
decimals: 0,
|
||||
minSubmissions: 0,
|
||||
maxSubmissions: 1,
|
||||
restartDelay: 0,
|
||||
rewardAmount: 0,
|
||||
}
|
||||
|
||||
export function loadAggregatorSetup(file: string): AggregatorSetupFile {
|
||||
let obj: AggregatorSetupFile = loadJSON(file)
|
||||
|
||||
for (let key of Object.keys(obj.aggregators)) {
|
||||
obj.aggregators[key] = {
|
||||
...aggregatorConfigDefaults,
|
||||
...obj.aggregators[key],
|
||||
}
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
export interface OracleConfig {
|
||||
owner: string
|
||||
}
|
||||
|
||||
export interface AggregatorSetupConfig {
|
||||
decimals: number
|
||||
minSubmissions: number
|
||||
maxSubmissions: number
|
||||
restartDelay: number
|
||||
rewardAmount: number
|
||||
rewardTokenAccount?: string
|
||||
|
||||
oracles?: string[]
|
||||
}
|
||||
|
||||
export interface AggregatorSetupFile {
|
||||
programID: string
|
||||
|
||||
aggregators: {
|
||||
[key: string]: AggregatorSetupConfig
|
||||
}
|
||||
oracles: {
|
||||
[key: string]: OracleConfig
|
||||
}
|
||||
}
|
||||
|
||||
// //
|
||||
// export interface DeployManifest {
|
||||
// programID:
|
||||
// }
|
|
@ -0,0 +1,191 @@
|
|||
import { stateFromJSON } from "./state"
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
|
||||
import {
|
||||
Account,
|
||||
BPFLoader,
|
||||
ProgramAccount,
|
||||
PublicKey,
|
||||
SPLToken,
|
||||
Wallet,
|
||||
} from "solray"
|
||||
import {
|
||||
AggregatorSetupFile,
|
||||
AggregatorSetupConfig,
|
||||
loadAggregatorSetup,
|
||||
OracleConfig,
|
||||
} from "./config"
|
||||
import FluxAggregator from "./FluxAggregator"
|
||||
import { AggregatorConfig, IAggregatorConfig } from "./schema"
|
||||
// import { AggregatorConfig } from "./schema"
|
||||
|
||||
interface OracleDeployInfo {
|
||||
pubkey: PublicKey
|
||||
owner: PublicKey
|
||||
}
|
||||
interface AggregatorDeployInfo {
|
||||
pubkey: PublicKey
|
||||
config: IAggregatorConfig
|
||||
|
||||
oracles: {
|
||||
[key: string]: OracleDeployInfo
|
||||
}
|
||||
}
|
||||
|
||||
export interface AggregatorDeployFile {
|
||||
programID: PublicKey
|
||||
|
||||
aggregators: {
|
||||
[key: string]: AggregatorDeployInfo
|
||||
}
|
||||
}
|
||||
|
||||
const FLUX_AGGREGATOR_SO = path.resolve(
|
||||
__dirname,
|
||||
"../build/flux_aggregator.so"
|
||||
)
|
||||
|
||||
function jsonReviver(_key: string, val: any) {
|
||||
if (val && typeof val == "object") {
|
||||
if (val["type"] == "PublicKey") {
|
||||
return new PublicKey(val.base58)
|
||||
}
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
function jsonReplacer(key: string, value: any) {
|
||||
if (value && typeof value != "object") {
|
||||
return value
|
||||
}
|
||||
|
||||
if (value.constructor == PublicKey) {
|
||||
return {
|
||||
type: "PublicKey",
|
||||
base58: value.toBase58(),
|
||||
}
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
export class Deployer {
|
||||
// file backed json state
|
||||
public setup: AggregatorSetupFile
|
||||
public state: AggregatorDeployFile
|
||||
constructor(statePath: string, setupFile: string, private wallet: Wallet) {
|
||||
this.state = stateFromJSON(
|
||||
statePath,
|
||||
{
|
||||
aggregators: {},
|
||||
} as any,
|
||||
{
|
||||
replacer: jsonReplacer,
|
||||
reviver: jsonReviver,
|
||||
}
|
||||
)
|
||||
this.setup = loadAggregatorSetup(setupFile)
|
||||
}
|
||||
|
||||
async runAll() {
|
||||
await this.deployProgram()
|
||||
await this.createAggregators()
|
||||
}
|
||||
|
||||
async deployProgram() {
|
||||
if (this.state.programID) {
|
||||
console.log("program deployed")
|
||||
return
|
||||
}
|
||||
|
||||
const programBinary = fs.readFileSync(FLUX_AGGREGATOR_SO)
|
||||
|
||||
console.log(`deploying ${FLUX_AGGREGATOR_SO}...`)
|
||||
const bpfLoader = new BPFLoader(this.wallet)
|
||||
|
||||
const account = await bpfLoader.load(programBinary)
|
||||
this.state.programID = account.publicKey
|
||||
}
|
||||
|
||||
async createAggregators() {
|
||||
for (let key of Object.keys(this.setup.aggregators)) {
|
||||
const aggregatorSetup = this.setup.aggregators[key]
|
||||
|
||||
let info = this.state.aggregators[key]
|
||||
if (!info) {
|
||||
this.state.aggregators[key] = await this.createAggregator(
|
||||
key,
|
||||
aggregatorSetup
|
||||
)
|
||||
info = this.state.aggregators[key]
|
||||
}
|
||||
|
||||
console.log(`${key} aggregator deployed`)
|
||||
for (let oracleName of aggregatorSetup.oracles || []) {
|
||||
const oracleSetup = this.setup.oracles[oracleName]
|
||||
// TODO: check that key exists
|
||||
|
||||
let oinfo = info.oracles[oracleName]
|
||||
if (!oinfo) {
|
||||
oinfo = await this.createOracle(info, oracleName, oracleSetup)
|
||||
|
||||
// hmm... not triggering save
|
||||
info.oracles[oracleName] = oinfo
|
||||
}
|
||||
console.log(`${key} added oracle:`, oracleSetup.owner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get program() {
|
||||
return new FluxAggregator(this.wallet, this.state.programID)
|
||||
}
|
||||
|
||||
async createOracle(
|
||||
aggregatorInfo: AggregatorDeployInfo,
|
||||
name: string,
|
||||
setup: OracleConfig
|
||||
): Promise<OracleDeployInfo> {
|
||||
const config = {
|
||||
description: name,
|
||||
aggregator: aggregatorInfo.pubkey,
|
||||
aggregatorOwner: this.wallet.account,
|
||||
oracleOwner: new PublicKey(setup.owner),
|
||||
}
|
||||
|
||||
const account = await this.program.addOracle(config)
|
||||
|
||||
return {
|
||||
pubkey: account.publicKey,
|
||||
owner: config.oracleOwner,
|
||||
}
|
||||
}
|
||||
|
||||
async createAggregator(
|
||||
name: string,
|
||||
cfg: AggregatorSetupConfig
|
||||
): Promise<AggregatorDeployInfo> {
|
||||
const config = {
|
||||
description: name,
|
||||
decimals: cfg.decimals,
|
||||
minSubmissions: cfg.minSubmissions,
|
||||
maxSubmissions: cfg.maxSubmissions,
|
||||
restartDelay: cfg.restartDelay,
|
||||
rewardTokenAccount: new PublicKey(cfg.rewardTokenAccount || 0),
|
||||
rewardAmount: cfg.rewardAmount,
|
||||
}
|
||||
|
||||
const account = await this.program.initialize({
|
||||
// FIXME: move this into initialize method
|
||||
config: new AggregatorConfig(config),
|
||||
owner: this.wallet.account,
|
||||
})
|
||||
|
||||
return {
|
||||
pubkey: account.publicKey,
|
||||
config,
|
||||
oracles: {},
|
||||
}
|
||||
}
|
||||
}
|
|
@ -104,7 +104,19 @@ class Submission {
|
|||
}
|
||||
}
|
||||
|
||||
export class AggregatorConfig extends Serialization {
|
||||
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
|
||||
|
@ -248,7 +260,6 @@ export class AddOracle extends InstructionSerialization {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
export class RemoveOracle extends InstructionSerialization {
|
||||
public static schema = {
|
||||
kind: "struct",
|
||||
|
@ -256,6 +267,13 @@ export class RemoveOracle extends InstructionSerialization {
|
|||
}
|
||||
}
|
||||
|
||||
export class Withdraw extends InstructionSerialization {
|
||||
public static schema = {
|
||||
kind: "struct",
|
||||
fields: [["faucetOwnerSeed", ["u8"]]],
|
||||
}
|
||||
}
|
||||
|
||||
export class Submit extends InstructionSerialization {
|
||||
public static schema = {
|
||||
kind: "struct",
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import fs from "fs"
|
||||
|
||||
// stateFromJSON returns a JSON file backed object. When object mutates, will
|
||||
// serialize the object as JSON to the specified path.
|
||||
//
|
||||
// with recursive proxy support similar to:
|
||||
//
|
||||
// https://stackoverflow.com/questions/41299642/how-to-use-javascript-proxy-for-nested-objects
|
||||
export function stateFromJSON<T extends Object>(
|
||||
filepath: string,
|
||||
defaultObject: T,
|
||||
opts: {
|
||||
replacer?: Function
|
||||
reviver?: Function
|
||||
} = {}
|
||||
): T {
|
||||
let root = defaultObject
|
||||
|
||||
try {
|
||||
const data = fs.readFileSync(filepath, "utf8")
|
||||
// TODO: support custom revive
|
||||
root = JSON.parse(data, opts.reviver as any)
|
||||
console.log(`Loaded state object: ${filepath}`)
|
||||
} catch (err) {
|
||||
console.log(`Init state object: ${filepath}`)
|
||||
}
|
||||
|
||||
function save() {
|
||||
// should be sync to avoid write races
|
||||
fs.writeFileSync(filepath, JSON.stringify(root, opts.replacer as any, 2))
|
||||
}
|
||||
|
||||
const proxy = {
|
||||
get(target, key) {
|
||||
if (typeof target[key] === "object" && target[key] !== null) {
|
||||
return new Proxy(target[key], proxy)
|
||||
} else {
|
||||
return target[key]
|
||||
}
|
||||
},
|
||||
|
||||
set(obj, prop, val) {
|
||||
obj[prop] = val
|
||||
save()
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
return new Proxy(root, proxy) as T
|
||||
}
|
||||
|
||||
// import { BigNumber } from "ethers";
|
||||
// function jsonRevive(_key: string, val: any) {
|
||||
// if (val && typeof val == "object") {
|
||||
// if (val["type"] == "BigNumber") {
|
||||
// return BigNumber.from(val["hex"]);
|
||||
// }
|
||||
// }
|
||||
// return val;
|
||||
// }
|
207
test.ts
207
test.ts
|
@ -3,11 +3,11 @@ dotenv.config()
|
|||
|
||||
import BN from "bn.js"
|
||||
|
||||
import { BPFLoader, ProgramAccount, SPLToken, Wallet } from "solray"
|
||||
import { ProgramAccount, SPLToken, Wallet } from "solray"
|
||||
import { AppContext, conn, network } from "./src/context"
|
||||
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
|
||||
import { AggregatorConfig } from "./src/schema"
|
||||
import FluxAggregator from "./src/FluxAggregator"
|
||||
|
||||
|
@ -15,138 +15,121 @@ import * as encoding from "./src/schema"
|
|||
import { Account, AccountInfo, Connection, PublicKey } from "@solana/web3.js"
|
||||
import { coinbase } from "./src/PriceFeed"
|
||||
import { Submitter } from "./src/Submitter"
|
||||
import { Deployer } from "./src/deploy"
|
||||
|
||||
const FLUX_AGGREGATOR_SO = path.resolve(__dirname, "build/flux_aggregator.so")
|
||||
import { loadAggregatorSetup } from "./src/config"
|
||||
|
||||
import { stateFromJSON } from "./src/state"
|
||||
async function main() {
|
||||
|
||||
let ctx = new AppContext()
|
||||
|
||||
let deployer = await ctx.deployer()
|
||||
let adminWallet = await ctx.adminWallet()
|
||||
let oracleWallet = await ctx.oracleWallet()
|
||||
|
||||
console.log(network)
|
||||
|
||||
await conn.requestAirdrop(adminWallet.pubkey, 10 * 1e9)
|
||||
console.log((await conn.getBalance(adminWallet.pubkey)) / 1e9)
|
||||
|
||||
let aggregatorProgram = await deployer.ensure(
|
||||
"aggregatorProgram",
|
||||
async () => {
|
||||
const programBinary = fs.readFileSync(FLUX_AGGREGATOR_SO)
|
||||
|
||||
console.log(`deploying ${FLUX_AGGREGATOR_SO}...`)
|
||||
const bpfLoader = new BPFLoader(adminWallet)
|
||||
|
||||
return bpfLoader.load(programBinary)
|
||||
}
|
||||
const deployer = new Deployer(
|
||||
`deploy2.${network}.json`,
|
||||
`config/setup.${network}.json`,
|
||||
adminWallet
|
||||
)
|
||||
|
||||
const spltoken = new SPLToken(adminWallet)
|
||||
const rewardToken = await deployer.ensure("create reward token", async () => {
|
||||
return spltoken.initializeMint({
|
||||
mintAuthority: adminWallet.pubkey,
|
||||
decimals: 8,
|
||||
})
|
||||
})
|
||||
await deployer.runAll()
|
||||
console.log("done")
|
||||
|
||||
const rewardTokenOwner = await ProgramAccount.forSeed(
|
||||
Buffer.from("solink"),
|
||||
aggregatorProgram.publicKey
|
||||
)
|
||||
return
|
||||
|
||||
const rewardTokenAccount = await deployer.ensure(
|
||||
"initialize reward token account",
|
||||
async () => {
|
||||
const vault = await spltoken.initializeAccount({
|
||||
token: rewardToken.publicKey,
|
||||
owner: rewardTokenOwner.pubkey,
|
||||
})
|
||||
// let deployer = await ctx.deployer()
|
||||
|
||||
await spltoken.mintTo({
|
||||
token: rewardToken.publicKey,
|
||||
to: vault.publicKey,
|
||||
amount: BigInt(1e6 * 1e8), // 1M
|
||||
authority: adminWallet.pubkey,
|
||||
})
|
||||
// let oracleWallet = await ctx.oracleWallet()
|
||||
|
||||
return vault
|
||||
}
|
||||
)
|
||||
// console.log(network)
|
||||
|
||||
console.log(await spltoken.mintInfo(rewardToken.publicKey))
|
||||
// await conn.requestAirdrop(adminWallet.pubkey, 10 * 1e9)
|
||||
// console.log((await conn.getBalance(adminWallet.pubkey)) / 1e9)
|
||||
|
||||
const program = new FluxAggregator(adminWallet, aggregatorProgram.publicKey)
|
||||
// const spltoken = new SPLToken(adminWallet)
|
||||
// const rewardToken = await deployer.ensure("create reward token", async () => {
|
||||
// return spltoken.initializeMint({
|
||||
// mintAuthority: adminWallet.pubkey,
|
||||
// decimals: 8,
|
||||
// })
|
||||
// })
|
||||
|
||||
let aggregator = await deployer.ensure(
|
||||
"create btc:usd aggregator",
|
||||
async () => {
|
||||
let name = "btc:usd"
|
||||
return program.initialize({
|
||||
config: new AggregatorConfig({
|
||||
description: name,
|
||||
decimals: 2,
|
||||
minSubmissions: 1,
|
||||
maxSubmissions: 3,
|
||||
restartDelay: 0,
|
||||
rewardAmount: BigInt(10),
|
||||
rewardTokenAccount: rewardTokenAccount.publicKey,
|
||||
}),
|
||||
owner: adminWallet.account,
|
||||
})
|
||||
}
|
||||
)
|
||||
// const rewardTokenOwner = await ProgramAccount.forSeed(
|
||||
// Buffer.from("solink"),
|
||||
// aggregatorProgram.publicKey
|
||||
// )
|
||||
|
||||
const N_ORACLES = 4
|
||||
interface OracleRole {
|
||||
owner: Account
|
||||
oracle: PublicKey
|
||||
}
|
||||
// const rewardTokenAccount = await deployer.ensure(
|
||||
// "initialize reward token account",
|
||||
// async () => {
|
||||
// const vault = await spltoken.initializeAccount({
|
||||
// token: rewardToken.publicKey,
|
||||
// owner: rewardTokenOwner.pubkey,
|
||||
// })
|
||||
|
||||
const oracleRoles: OracleRole[] = []
|
||||
// await spltoken.mintTo({
|
||||
// token: rewardToken.publicKey,
|
||||
// to: vault.publicKey,
|
||||
// amount: BigInt(1e6 * 1e8), // 1M
|
||||
// authority: adminWallet.pubkey,
|
||||
// })
|
||||
|
||||
for (let i = 0; i < N_ORACLES; i++) {
|
||||
// TODO: probably put the desired oracles in a config file...
|
||||
let owner = await deployer.ensure(`create oracle[${i}] owner`, async () => {
|
||||
return new Account()
|
||||
})
|
||||
// return vault
|
||||
// }
|
||||
// )
|
||||
|
||||
let oracle = await deployer.ensure(
|
||||
`add oracle[${i}] to btc:usd`,
|
||||
async () => {
|
||||
return program.addOracle({
|
||||
description: "test-oracle",
|
||||
aggregator: aggregator.publicKey,
|
||||
aggregatorOwner: adminWallet.account,
|
||||
oracleOwner: owner.publicKey,
|
||||
})
|
||||
}
|
||||
)
|
||||
// console.log(await spltoken.mintInfo(rewardToken.publicKey))
|
||||
|
||||
oracleRoles.push({ owner, oracle: oracle.publicKey })
|
||||
}
|
||||
|
||||
for (const role of oracleRoles) {
|
||||
// const wallet = Wallet.from
|
||||
const owner = Wallet.fromAccount(role.owner, conn)
|
||||
await conn.requestAirdrop(owner.pubkey, 10 * 1e9)
|
||||
console.log(owner.address, await conn.getBalance(owner.pubkey))
|
||||
|
||||
const priceFeed = coinbase("BTC/USD")
|
||||
const submitter = new Submitter(
|
||||
aggregatorProgram.publicKey,
|
||||
aggregator.publicKey,
|
||||
role.oracle,
|
||||
owner,
|
||||
priceFeed,
|
||||
{
|
||||
// don't submit value unless btc changes at least a dollar
|
||||
minValueChangeForNewRound: 100,
|
||||
}
|
||||
)
|
||||
// const N_ORACLES = 4
|
||||
// interface OracleRole {
|
||||
// owner: Account
|
||||
// oracle: PublicKey
|
||||
// }
|
||||
|
||||
submitter.start()
|
||||
}
|
||||
// const oracleRoles: OracleRole[] = []
|
||||
|
||||
// for (let i = 0; i < N_ORACLES; i++) {
|
||||
// // TODO: probably put the desired oracles in a config file...
|
||||
// let owner = await deployer.ensure(`create oracle[${i}] owner`, async () => {
|
||||
// return new Account()
|
||||
// })
|
||||
|
||||
// let oracle = await deployer.ensure(
|
||||
// `add oracle[${i}] to btc:usd`,
|
||||
// async () => {
|
||||
// return program.addOracle({
|
||||
// description: "test-oracle",
|
||||
// aggregator: aggregator.publicKey,
|
||||
// aggregatorOwner: adminWallet.account,
|
||||
// oracleOwner: owner.publicKey,
|
||||
// })
|
||||
// }
|
||||
// )
|
||||
|
||||
// oracleRoles.push({ owner, oracle: oracle.publicKey })
|
||||
// }
|
||||
|
||||
// for (const role of oracleRoles) {
|
||||
// // const wallet = Wallet.from
|
||||
// const owner = Wallet.fromAccount(role.owner, conn)
|
||||
// await conn.requestAirdrop(owner.pubkey, 10 * 1e9)
|
||||
// console.log(owner.address, await conn.getBalance(owner.pubkey))
|
||||
|
||||
// const priceFeed = coinbase("BTC/USD")
|
||||
// const submitter = new Submitter(
|
||||
// aggregatorProgram.publicKey,
|
||||
// aggregator.publicKey,
|
||||
// role.oracle,
|
||||
// owner,
|
||||
// priceFeed,
|
||||
// {
|
||||
// // don't submit value unless btc changes at least a dollar
|
||||
// minValueChangeForNewRound: 100,
|
||||
// }
|
||||
// )
|
||||
|
||||
// submitter.start()
|
||||
// }
|
||||
}
|
||||
|
||||
main().catch((err) => console.log(err))
|
||||
|
|
Loading…
Reference in New Issue