diff --git a/src/FluxAggregator.ts b/src/FluxAggregator.ts
index fcd1638..812b088 100644
--- a/src/FluxAggregator.ts
+++ b/src/FluxAggregator.ts
@@ -16,40 +16,11 @@ import {
SystemProgram,
} from "@solana/web3.js"
-import {
- publicKey,
- u64LEBuffer,
- uint64,
- BufferLayout,
-} from "solray/lib/util/encoding"
-
-import { decodeOracleInfo } from "./utils"
-
-// @ts-ignore
-// import BufferLayout from "buffer-layout";
-
import { AggregatorConfig, IAggregatorConfig, schema } from "./schema"
import * as encoding from "./schema"
import { deserialize, serialize } from "borsh"
import { conn } from "./context"
-export const AggregatorLayout = BufferLayout.struct([])
-
-export const OracleLayout = BufferLayout.struct([
- uint64("nextSubmitTime"),
- BufferLayout.blob(32, "description"),
- BufferLayout.u8("isInitialized"),
- uint64("withdrawable"),
- publicKey("aggregator"),
- publicKey("owner"),
-])
-
-export const SubmissionLayout = BufferLayout.struct([
- uint64("time"),
- uint64("value"),
- publicKey("oracle"),
-])
-
interface InitializeParams {
config: IAggregatorConfig
owner: Account
diff --git a/src/cli.ts b/src/cli.ts
deleted file mode 100644
index b730b1c..0000000
--- a/src/cli.ts
+++ /dev/null
@@ -1,344 +0,0 @@
-import { Command, option } from "commander"
-
-import fs from "fs"
-import path from "path"
-
-import {
- BPFLoader,
- PublicKey,
- Wallet,
- NetworkName,
- solana,
- Deployer,
- SPLToken,
- ProgramAccount,
-} from "solray"
-
-import dotenv from "dotenv"
-
-import FluxAggregator from "./FluxAggregator"
-
-import { decodeAggregatorInfo, sleep } from "./utils"
-
-import * as feed from "./feed"
-import { AggregatorConfig } from "./schema"
-
-dotenv.config()
-
-const cli = new Command()
-
-const FLUX_AGGREGATOR_SO = path.resolve(
- __dirname,
- "../build/flux_aggregator.so"
-)
-const network = (process.env.NETWORK || "local") as NetworkName
-const conn = solana.connect(network)
-
-import { AppContext } from "./context"
-
-function color(s, c = "black", b = false): string {
- // 30m Black, 31m Red, 32m Green, 33m Yellow, 34m Blue, 35m Magenta, 36m Cyanic, 37m White
- const cArr = [
- "black",
- "red",
- "green",
- "yellow",
- "blue",
- "megenta",
- "cyanic",
- "white",
- ]
-
- let cIdx = cArr.indexOf(c)
- let bold = b ? "\x1b[1m" : ""
-
- return `\x1b[${30 + (cIdx > -1 ? cIdx : 0)}m${bold}${s}\x1b[0m`
-}
-
-function error(message: string) {
- console.log("\n")
- console.error(color(message, "red"))
- console.log("\n")
- process.exit()
-}
-
-function log(message: any) {
- console.log(message)
-}
-
-cli.command("generate-wallet").action(async () => {
- const mnemonic = Wallet.generateMnemonic()
- const wallet = await Wallet.fromMnemonic(mnemonic, conn)
-
- log(`address: ${wallet.address}`)
- log(`mnemonic: ${mnemonic}`)
-})
-
-cli
- .command("airdrop
")
- .description(`request airdrop to the address`)
- .option("-m, --amount ", "request amount in sol (10e9)", "10")
- .action(async (address, opts) => {
- const dest = new PublicKey(address)
-
- const { amount } = opts
-
- log(`requesting 10 sol airdrop to: ${address}`)
- await conn.requestAirdrop(dest, amount * 1e9)
- log("airdrop success")
- })
-
-cli
- .command("deploy-program")
- .description("deploy the aggregator program")
- .action(async () => {
- const { wallet, deployer } = await AppContext.forAdmin()
-
- const programAccount = await deployer.ensure(
- AppContext.AGGREGATOR_PROGRAM,
- async () => {
- const programBinary = fs.readFileSync(FLUX_AGGREGATOR_SO)
-
- log(`deploying ${FLUX_AGGREGATOR_SO}...`)
- const bpfLoader = new BPFLoader(wallet)
-
- return bpfLoader.load(programBinary)
- }
- )
-
- log(
- `deployed aggregator program. program id: ${color(
- programAccount.publicKey.toBase58(),
- "blue"
- )}`
- )
- })
-
-cli
- .command("add-aggregator")
- .description("create an aggregator")
- .option("--feedName ", "feed pair name")
- .option("--decimals ", "submission decimals", "2")
- .option("--minSubmissions ", "minSubmissions", "1")
- .option("--maxSubmissions ", "maxSubmissions", "12")
- .option("--rewardAmount ", "rewardAmount", "1")
- .option("--restartDelay ", "restartDelay", "1")
- .action(async (opts) => {
- const {
- deployer,
- wallet,
- aggregatorProgramAccount: aggregatorProgram,
- } = await AppContext.forAdmin()
-
- const {
- feedName,
- restartDelay,
- minSubmissions,
- maxSubmissions,
- decimals,
- rewardAmount,
- } = opts
-
- const aggregator = new FluxAggregator(wallet, aggregatorProgram.publicKey)
-
- const feed = await deployer.ensure(feedName, async () => {
- return aggregator.initialize({
- config: new AggregatorConfig({
- description: feedName,
- decimals,
- minSubmissions,
- maxSubmissions,
- restartDelay,
- rewardAmount: BigInt(rewardAmount),
- }),
- owner: wallet.account,
- })
- })
-
- log(`feed initialized, pubkey: ${color(feed.publicKey.toBase58(), "blue")}`)
- })
-
-// cli
-// .command("aggregators")
-// .description("show all aggregators")
-// .action(() => {
-// // show current network
-// showNetwork()
-
-// if (!fs.existsSync(deployedPath)) {
-// error("program haven't deployed yet")
-// }
-
-// const deployed = JSON.parse(fs.readFileSync(deployedPath).toString())
-
-// if (deployed.network != network) {
-// error("deployed network not match, please try `npm run clean:deployed`, and deploy again")
-// }
-
-// if (!deployed.programId) {
-// error("program haven't deployed yet")
-// }
-
-// log(deployed.pairs)
-// })
-
-cli
- .command("add-oracle")
- .description("add an oracle to aggregator")
- .option("--aggregatorAddress ", "aggregator address")
- .option("--oracleName ", "oracle name")
- .option("--oracleOwner ", "oracle owner address")
- .action(async (opts) => {
- const { wallet, aggregator, deployer } = await AppContext.forAdmin()
-
- const { oracleName, oracleOwner, feedAddress } = opts
-
- log("add oracle...")
- const oracle = await aggregator.addOracle({
- oracleOwner: new PublicKey(oracleOwner),
- description: oracleName,
- aggregator: new PublicKey(feedAddress),
- aggregatorOwner: wallet.account,
- })
-
- log(`added oracle. pubkey: ${color(oracle.publicKey.toBase58(), "blue")}`)
- })
-
-cli
- .command("remove-oracle")
- .option("--feedAddress ", "feed to remove oracle from")
- .option("--oracleAddress ", "oracle address")
- .action(async (opts) => {
- const { feedAddress, oracleAddress } = opts
-
- const { aggregator } = await AppContext.forAdmin()
-
- await aggregator.removeOracle({
- aggregator: new PublicKey(feedAddress),
- oracle: new PublicKey(oracleAddress),
- })
- })
-
-// cli
-// .command("oracles")
-// .description("show all oracles")
-// .action(() => {
-// // show current network
-// showNetwork()
-
-// if (!fs.existsSync(deployedPath)) {
-// error("program haven't deployed yet")
-// }
-
-// const deployed = JSON.parse(fs.readFileSync(deployedPath).toString())
-
-// if (deployed.network != network) {
-// error("deployed network not match, please try `npm run clean:deployed`, and deploy again")
-// }
-
-// if (!deployed.programId) {
-// error("program haven't deployed yet")
-// }
-
-// log(deployed.oracles)
-// })
-
-cli
- .command("feed-poll")
- .description("poll current feed value")
- .option("--feedAddress ", "feed address to submit values to")
- .action(async (opts) => {
- const { feedAddress } = opts
-
- while (true) {
- const feedInfo = await conn.getAccountInfo(new PublicKey(feedAddress))
- log(decodeAggregatorInfo(feedInfo))
-
- await sleep(1000)
- }
- })
-
-cli
- .command("feed")
- .description("oracle feeds to aggregator")
- .option("--feedAddress ", "feed address to submit values to")
- .option("--oracleAddress ", "feed address to submit values to")
- .option("--pairSymbol ", "market pair to feed")
- .action(async (opts) => {
- const {
- wallet,
- aggregatorProgramAccount: aggregatorProgram,
- } = await AppContext.forOracle()
-
- const { feedAddress, oracleAddress, pairSymbol } = opts
-
- feed.start({
- oracle: new PublicKey(oracleAddress),
- oracleOwner: wallet.account,
- feed: new PublicKey(feedAddress),
- pairSymbol: pairSymbol,
- payerWallet: wallet,
- programId: aggregatorProgram.publicKey,
- })
- })
-
-cli
- .command("testToken")
- .description("create test token")
- .option("--amount ", "amount of the test token")
- .action(async (opts) => {
- const {
- wallet,
- aggregatorProgramAccount: aggregatorProgram,
- deployer,
- } = await AppContext.forAdmin()
-
- const { amount } = opts
-
- if (!amount || amount < 0) {
- error("invalid amount")
- }
-
- const spltoken = new SPLToken(wallet)
-
- log(`create test token...`)
- // 1. create token
- const token = await spltoken.initializeMint({
- mintAuthority: wallet.account.publicKey,
- decimals: 8,
- })
-
- // 2. create tokenOwner (program account)
- const tokenOwner = await ProgramAccount.forSeed(
- Buffer.from(token.publicKey.toBuffer()).slice(0, 30),
- aggregatorProgram.publicKey
- )
-
- log(`create token acount...`)
- // 3. create token account
- const tokenAccount = await spltoken.initializeAccount({
- token: token.publicKey,
- owner: tokenOwner.pubkey,
- })
-
- log(`mint ${amount} token to token account...`)
- // 4. and then, mint tokens to that account
- await spltoken.mintTo({
- token: token.publicKey,
- to: tokenAccount.publicKey,
- amount: BigInt(amount),
- authority: wallet.account,
- })
-
- log({
- token: token.publicKey.toBase58(),
- tokenAccount: tokenAccount.publicKey.toBase58(),
- tokenOwner: {
- address: tokenOwner.address,
- seed: tokenOwner.noncedSeed.toString("hex"),
- nonce: tokenOwner.nonce,
- },
- })
- })
-
-cli.parse(process.argv)
diff --git a/src/feed.ts b/src/feed.ts
deleted file mode 100644
index 8232188..0000000
--- a/src/feed.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-import { PublicKey, Account, Wallet } from "solray"
-import WebSocket from "ws"
-
-import { decodeOracleInfo, sleep } from "./utils"
-
-import FluxAggregator from "./FluxAggregator"
-
-const submitInterval = 10 * 1000
-
-interface StartParams {
- oracle: PublicKey;
- oracleOwner: Account;
- feed: PublicKey;
- pairSymbol: string;
- payerWallet: Wallet;
- programId: PublicKey;
-}
-
-export async function start(params: StartParams) {
- const {
- oracle,
- oracleOwner,
- feed,
- pairSymbol,
- payerWallet,
- programId,
- } = params
-
- console.log("connecting to wss://ws-feed.pro.coinbase.com ()")
- const ws = new WebSocket("wss://ws-feed.pro.coinbase.com")
-
- ws.on("open", () => {
- console.log(`${pairSymbol} price feed connected`)
- ws.send(JSON.stringify({
- "type": "subscribe",
- "product_ids": [
- pairSymbol.replace("/", "-").toUpperCase(),
- ],
- "channels": [
- "ticker"
- ]
- }))
- })
-
- // in penny
- let curPriceCent = 0
-
- ws.on("message", async (data) => {
- const json = JSON.parse(data)
- if (!json || !json.price) {
- return console.log(data)
- }
-
- curPriceCent = Math.floor(json.price * 100)
-
- console.log("current price:", json.price)
- })
-
- ws.on("close", (err) => {
- console.error(`websocket closed: ${err}`)
- process.exit(1)
- })
-
- const program = new FluxAggregator(payerWallet, programId)
-
- console.log(await program.oracleInfo(oracle))
- console.log({ owner: oracleOwner.publicKey.toString() })
-
- while (true) {
- if (curPriceCent == 0) {
- await sleep(1000)
- }
-
- try {
- await program.submit({
- aggregator: feed,
- oracle,
- submission: BigInt(curPriceCent),
- owner: oracleOwner,
- })
- } catch(err) {
- console.log(err)
- }
-
- console.log("submit success!")
-
- payerWallet.conn.getAccountInfo(oracle).then((accountInfo) => {
- console.log("oracle info:", decodeOracleInfo(accountInfo))
- })
-
- console.log("wait for cooldown success!")
- await sleep(submitInterval)
- }
-}
\ No newline at end of file
diff --git a/src/utils.ts b/src/utils.ts
index afe1a2a..41ae07f 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -1,11 +1,5 @@
import { Connection, PublicKey } from "@solana/web3.js"
-import {
- AggregatorLayout,
- SubmissionLayout,
- OracleLayout,
-} from "./FluxAggregator"
-
import { solana, Wallet, NetworkName, Deployer } from "solray"
export function getMedian(submissions: number[]): number {
@@ -33,67 +27,6 @@ export function sleep(ms: number): Promise {
})
}
-export function decodeAggregatorInfo(accountInfo) {
- const data = Buffer.from(accountInfo.data)
- const aggregator = AggregatorLayout.decode(data)
-
- const minSubmissionValue = aggregator.minSubmissionValue.readBigUInt64LE()
- const maxSubmissionValue = aggregator.maxSubmissionValue.readBigUInt64LE()
- const submitInterval = aggregator.submitInterval.readInt32LE()
- const description = (aggregator.description.toString() as String).trim()
-
- // decode oracles
- let submissions: any[] = []
- let submissionSpace = SubmissionLayout.span
- let latestUpdateTime = BigInt(0)
-
- for (let i = 0; i < aggregator.submissions.length / submissionSpace; i++) {
- let submission = SubmissionLayout.decode(
- aggregator.submissions.slice(
- i * submissionSpace,
- (i + 1) * submissionSpace
- )
- )
-
- submission.oracle = new PublicKey(submission.oracle)
- submission.time = submission.time.readBigInt64LE()
- submission.value = submission.value.readBigInt64LE()
-
- if (!submission.oracle.equals(new PublicKey(0))) {
- submissions.push(submission)
- }
-
- if (submission.time > latestUpdateTime) {
- latestUpdateTime = submission.time
- }
- }
-
- return {
- minSubmissionValue: minSubmissionValue,
- maxSubmissionValue: maxSubmissionValue,
- submissionValue: getMedian(submissions),
- submitInterval,
- description,
- oracles: submissions.map((s) => s.oracle.toString()),
- latestUpdateTime: new Date(Number(latestUpdateTime) * 1000),
- }
-}
-
-export function decodeOracleInfo(accountInfo) {
- const data = Buffer.from(accountInfo.data)
-
- const oracle = OracleLayout.decode(data)
-
- oracle.nextSubmitTime = oracle.nextSubmitTime.readBigUInt64LE().toString()
- oracle.description = oracle.description.toString()
- oracle.isInitialized = oracle.isInitialized != 0
- oracle.withdrawable = oracle.withdrawable.readBigUInt64LE().toString()
- oracle.aggregator = new PublicKey(oracle.aggregator).toBase58()
- oracle.owner = new PublicKey(oracle.owner).toBase58()
-
- return oracle
-}
-
export async function walletFromEnv(
key: string,
conn: Connection