diff --git a/README.md b/README.md index 83386de..6befc3b 100644 --- a/README.md +++ b/README.md @@ -19,18 +19,26 @@ request some airdrop: `npm run deploy` -## Create Aggregator +## Create Aggregator Owner -`npm run create:aggregator` +`npm run create:aggregatorOwner` -request some airdrop: +## Add An Aggregator -`npm run airdrop:aggregator` +`npm run add:aggregator` -## Create Oracle +## Create Oracle Owner -`npm run create:oracle` +`npm run create:oracleOwner` -request some airdrop: +## Add An Oracle -`npm run airdrop:oracle` \ No newline at end of file +`npm run add:oracle` + +## Show Aggregators/Oracles + +`npm run show:aggregators|oracles` + +## Start to feed + +`npm run feed` \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..6b4f240 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ +Google
Suche Bilder Maps Play YouTube News Gmail Drive Mehr »
Webprotokoll | Einstellungen | Anmelden



 

Erweiterte Suche

© 2020 - Datenschutzerklärung - Nutzungsbedingungen

\ No newline at end of file diff --git a/package.json b/package.json index 717a80d..f7ff661 100644 --- a/package.json +++ b/package.json @@ -6,20 +6,28 @@ "testnetDefaultChannel": "v1.4.8", "scripts": { "deploy": "cd src && ts-node cli.ts deploy", - "role-info": "cd src && ts-node cli.ts role-info", + "show:roles": "cd src && ts-node cli.ts roleinfo", + "show:aggregators": "cd src && ts-node cli.ts aggregators", + "show:aggregatorInfo": "cd src && ts-node cli.ts aggregatorInfo", + "show:oracles": "cd src && ts-node cli.ts oracles", "create:payer": "cd src && ts-node cli.ts create payer", - "create:aggregator": "cd src && ts-node cli.ts create aggregator", - "create:oracle": "cd src && ts-node cli.ts create oracle", + "create:aggregatorOwner": "cd src && ts-node cli.ts create aggregatorOwner", + "create:oracleOwner": "cd src && ts-node cli.ts create oracleOwner", "remove:payer": "cd src && ts-node cli.ts remove payer", - "remove:aggregator": "cd src && ts-node cli.ts remove aggregator", - "remove:oracle": "cd src && ts-node cli.ts remove oracle", - "airdrop:payer": "cd src && ts-node cli.ts airdrop payer", - "airdrop:aggregator": "cd src && ts-node cli.ts airdrop aggregator", - "airdrop:oracle": "cd src && ts-node cli.ts airdrop oracle", + "remove:aggregatorOwner": "cd src && ts-node cli.ts remove aggregatorOwner", + "remove:oracleOwner": "cd src && ts-node cli.ts remove oracleOwner", + "airdrop:payer": "cd src && ts-node cli.ts airdrop payer -m 10000000000", + "airdrop:aggregatorOwner": "cd src && ts-node cli.ts airdrop aggregatorOwner", + "airdrop:oracleOwner": "cd src && ts-node cli.ts airdrop oracleOwner", + "add:aggregator": "cd src && ts-node cli.ts add-aggregator", + "add:oracle": "cd src && ts-node cli.ts add-oracle", + "feed": "cd src && ts-node cli.ts feed", "localnet:update": "solana-localnet update", "localnet:up": "set -x; solana-localnet down; set -e; solana-localnet up", "localnet:down": "solana-localnet down", - "clean": "rm -rf src/deployed.md && rm -rf src/wallets/*", + "clean:roles": "rm -rf src/wallets/*", + "clean:deployed": "rm -rf src/deployed.json", + "clean": "npm run clean:deployed && npm run clean:roles", "build:program": "solray build program" }, "keywords": [], @@ -28,6 +36,7 @@ "dependencies": { "commander": "^6.2.0", "dotenv": "^8.2.0", + "inquirer": "^7.3.3", "solray": "git+https://github.com/czl1378/solray.git" }, "devDependencies": { diff --git a/src/.env b/src/.env new file mode 100644 index 0000000..9df51e7 --- /dev/null +++ b/src/.env @@ -0,0 +1 @@ +NETWORK=local \ No newline at end of file diff --git a/src/cli.ts b/src/cli.ts index e6bf562..27b977c 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,27 +1,37 @@ import { Command, option } from "commander" -import { PublicKey, Connection } from "@solana/web3.js" - -import { BPFLoader, Wallet } from "solray" - -import { - newWallet, calculatePayfees, connectTo, sleep -} from "./utils" +import inquirer from "inquirer" import fs from "fs" import path from "path" +import { Connection, PublicKey } from "@solana/web3.js" + +import { BPFLoader, Wallet, NetworkName} from "solray" +import dotenv from "dotenv" + +import FluxAggregator, { AggregatorLayout, OracleLayout } from "./FluxAggregator" + +import { newWallet, calculatePayfees, connectTo, sleep, decodeAggregatorInfo } from "./utils" + +import * as feed from "./feed" + +dotenv.config() + const cli = new Command() -const roles = ["payer", "aggregator", "oracle"] +const roles = ["payer", "aggregatorOwner", "oracleOwner"] const sofilePath = path.resolve(__dirname, "../build/flux_aggregator.so") -const deployedPath = path.resolve(__dirname, "./deployed.md") +const deployedPath = path.resolve(__dirname, "./deployed.json") + +const { NETWORK } = process.env + +const network = (NETWORK || "local") as NetworkName function checkRole(role) { if (roles.indexOf(role) < 0) { - console.error("invalid role") - return false + error("invalid role") } const walletPath = path.resolve(`./wallets/${role}.json`) @@ -42,24 +52,40 @@ function color(s, c="black", b=false): string { return `\x1b[${30 + (cIdx > -1 ? cIdx : 0)}m${bold}${s}\x1b[0m` } +function showNetwork() { + process.stdout.write(`${color(`Network: ${color(network, "blue")}`, "black", true)} \n\n`) +} + +function error(message: string) { + console.log("\n") + console.error(color(message, "red")) + console.log("\n") + process.exit() +} + +function log(message: any) { + console.log(message) +} + async function showRoleInfo(role, conn: Connection): Promise { const res = checkRole(role) if (!res) return if (!res.exist) { - return console.error(`role ${color(role, "red")} not created.`) + log(`role ${color(role, "red")} not created.`) + return } const fileData = fs.readFileSync(res.walletPath) const wallet = JSON.parse(fileData.toString()) - console.log(color(`[${role}]`, "cyanic", true)) - console.log(color("public key: ", "blue"), `${wallet.publicKey}`) - console.log(color("mnemonic: ", "blue"), `${wallet.mnemonic}`) + log(color(`[${role}]`, "cyanic", true)) + log(`${color("public key: ", "blue")} ${wallet.pubkey}`) + log(`${color("mnemonic: ", "blue")} ${wallet.mnemonic}`) process.stdout.write(`${color("balance: ", "blue")}...`) - const balance = await conn.getBalance(new PublicKey(wallet.publicKey)) + const balance = await conn.getBalance(new PublicKey(wallet.pubkey)) process.stdout.clearLine(-1) process.stdout.cursorTo(0) process.stdout.write(`${color("balance: ", "blue")}${balance} \n\n`) @@ -76,17 +102,16 @@ cli if (res.exist) { let fileData = fs.readFileSync(res.walletPath) let wallet = JSON.parse(fileData.toString()) - console.error(`role ${color(role, "red")} already created, public key: ${color(wallet.publicKey, "blue")}`) - return + error(`role ${color(role, "red")} already created, public key: ${color(wallet.pubkey, "blue")}`) } else { const wallet = await newWallet() fs.writeFileSync(res.walletPath, JSON.stringify({ - publicKey: wallet.account.publicKey.toBase58(), + pubkey: wallet.account.publicKey.toBase58(), secretKey: "["+wallet.account.secretKey.toString()+"]", mnemonic: wallet.mnemonic, })) - console.log(`create role ${color(role, "blue)")} success!`) + log(`create role ${color(role, "blue)")} success!`) } }) @@ -100,24 +125,21 @@ cli if (!res) return if (!res.exist) { - return console.error(`role ${color(role, "red")} not created.`) + error(`role [${role}] not created.`) } fs.unlinkSync(res.walletPath) - console.log(`remove role ${color(role, "blue")} success!`) + log(`remove role ${color(role, "blue")} success!`) }) cli - .command("role-info [role]") + .command("roleinfo [role]") .description(`show role info, or all if no role supplied`) - .option( - "-n, --network ", - "deploy on which network (local|devnet|mainnet), default is localnet", - "local" - ) .action(async (role, opts) => { - const { network } = opts + + // show current network + showNetwork() const conn = await connectTo(network) if (role) { @@ -132,63 +154,58 @@ cli cli .command("airdrop ") .description(`request airdrop to the role account, roles: ${roles.join("|")}`) - .option( - "-n, --network ", - "deploy on which network (local|devnet|mainnet), default is localnet", - "local" - ) .option("-m, --amount ", "request amount, default is 10e8", "100000000") .action(async (role, opts) => { + + // show current network + showNetwork() + const res = checkRole(role) if (!res) return if (!res.exist) { - return console.error(`role ${color(role, "red")} not created.`) + error(`role [${role}] not created.`) } const fileData = fs.readFileSync(res.walletPath) const wallet = JSON.parse(fileData.toString()) - console.log(`payer public key: ${color(wallet.publicKey, "blue")}, request airdop...`) + log(`payer public key: ${color(wallet.pubkey, "blue")}, request airdop...`) - const { network, amount } = opts + const { amount } = opts const conn = await connectTo(network) - await conn.requestAirdrop(new PublicKey(wallet.publicKey), amount*1) + await conn.requestAirdrop(new PublicKey(wallet.pubkey), amount*1) await sleep(1000) - const balance = await conn.getBalance(new PublicKey(wallet.publicKey)) + const balance = await conn.getBalance(new PublicKey(wallet.pubkey)) - console.log(`airdop success, balance: ${color(balance, "blue")}`) + log(`airdop success, balance: ${color(balance, "blue")}`) }) cli .command("deploy") - .description("deploy the aggregator program") - .option( - "-n,--network ", - "deploy on which network (local|devnet|mainnet), default is localnet", - "local" - ) + .description("deploy the program") .action(async (opts) => { + // show current network + showNetwork() + if (fs.existsSync(deployedPath)) { - const programId = fs.readFileSync(deployedPath).toString() - console.log("already deployed, program id: ", color(programId, "blue")) - console.log(color("if you want to deployed agian, try `npm run clean`", "red", true)) - return + const deployed = JSON.parse(fs.readFileSync(deployedPath).toString()) + log(`already deployed, program id: ${color(deployed.programId, "blue")}`) + error("if you want to deployed again, try `npm run clean:deployed`") } - const { network } = opts const res = checkRole("payer") if (!res || !res.exist) { - return console.error(`role ${color("payer", "blue")} not created`) + error(`role [payer] not created`) } const fileData = fs.readFileSync(res.walletPath) const payer = JSON.parse(fileData.toString()) if (!fs.existsSync(sofilePath)) { - return console.error(`${color("program file not exists", "red")}`) + error("program file not exists") } const programBinary = fs.readFileSync(sofilePath) @@ -196,39 +213,355 @@ cli const conn = await connectTo(network) const fees = await calculatePayfees(programBinary.length, conn) - let balance = await conn.getBalance(new PublicKey(payer.publicKey)) + let balance = await conn.getBalance(new PublicKey(payer.pubkey)) - console.log(`payer wallet: ${color(payer.publicKey, "blue")}, balance: ${color(balance, "blue")}`) - console.log(`deploy payfees: ${color(fees, "blue")}`) + log(`payer wallet: ${color(payer.pubkey, "blue")}, balance: ${color(balance, "blue")}`) + log(`deploy payfees: ${color(fees, "blue")}`) if (balance < fees) { - return console.log(color("insufficient balance to pay fees", "red")) + error("insufficient balance to pay fees") } - console.log("deploying...") + log("deploying...") const wallet = await Wallet.fromMnemonic(payer.mnemonic, conn) const bpfLoader = new BPFLoader(wallet) const programAccount = await bpfLoader.load(programBinary) - console.log(`deploy success, program id: ${color(programAccount.publicKey.toBase58(), "blue")}`) - fs.writeFileSync(deployedPath, programAccount.publicKey.toBase58()) + log(`deploy success, program id: ${color(programAccount.publicKey.toBase58(), "blue")}`) + fs.writeFileSync(deployedPath, JSON.stringify({ + network, + programId: programAccount.publicKey.toBase58() + })) }) cli - .command("init-aggregator") - .description("initialize aggregator to the program") - .option( - "-n,--network ", - "deploy on which network (local|devnet|mainnet), default is localnet", - "local" - ) - .action(async (opts) => { - const { network } = opts - const res = checkRole("aggregator") - if (!res || !res.exist) { - return console.error(`role ${color("aggregator", "blue")} not created`) + .command("add-aggregator") + .description("add an aggregator") + .action(async () => { + // 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") + } + + let res = checkRole("payer") + if (!res || !res.exist) { + error(`role ${color("payer", "blue")} not created`) + } + const payer = JSON.parse(fs.readFileSync(res.walletPath).toString()) + + res = checkRole("aggregatorOwner") + + if (!res || !res.exist) { + error(`role ${color("aggregatorOwner", "blue")} not created, please create the role first`) + } + const aggregatorOwner = JSON.parse(fs.readFileSync(res.walletPath).toString()) + + const inputs = await inquirer + .prompt([ + { message: "Pair name (eg. ETH/USD)", type: "input", name: "pairName", validate: (input) => { + if (!input) { + return "pair name cannot be empty" + } + if (deployed.pairs && deployed.pairs.some((p) => p.pairName == input)) { + return "pair name exist" + } + return true + }, transformer: (input) => { + return input.substr(0, 32).toUpperCase() + } }, + { message: "Submit interval", type: "number", name: "submitInterval", default: 6 }, + { message: "Min submission value", type: "number", name: "minSubmissionValue", default: 100 }, + { message: "Max submission value", type: "number", name: "maxSubmissionValue", default: 10e9 }, + ]) + + const { pairName, submitInterval, minSubmissionValue, maxSubmissionValue } = inputs + + const conn = await connectTo(network) + + const payerWallet = await Wallet.fromMnemonic(payer.mnemonic, conn) + const aggregatorOwnerWallet = await Wallet.fromMnemonic(aggregatorOwner.mnemonic, conn) + + const payerWalletBalance = await conn.getBalance(payerWallet.pubkey) + const fees = await calculatePayfees(AggregatorLayout.span, conn) + + if (payerWalletBalance < fees) { + error("insufficient balance to pay fees") + } + + const program = new FluxAggregator(payerWallet, new PublicKey(deployed.programId)) + + let description = pairName.substr(0, 32).toUpperCase().padEnd(32) + const aggregator = await program.initialize({ + submitInterval: submitInterval as number, + minSubmissionValue: BigInt(minSubmissionValue), + maxSubmissionValue: BigInt(maxSubmissionValue), + description, + owner: aggregatorOwnerWallet.account + }) + + log(`aggregator initialized, pubkey: ${color(aggregator.toBase58(), "blue")}, owner: ${color(aggregatorOwner.pubkey, "blue")}`) + fs.writeFileSync(deployedPath, JSON.stringify({ + ...deployed, + pairs: (deployed.pairs || []).concat([{ + pairName: description.trim(), + aggregator: aggregator.toBase58() + }]) + })) + + }) + +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") + .action(async () => { + // 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") + } + + if (!deployed.pairs) { + error("no aggregators") + } + + let res = checkRole("payer") + if (!res || !res.exist) { + error(`role ${color("payer", "blue")} not created`) + } + const payer = JSON.parse(fs.readFileSync(res.walletPath).toString()) + + res = checkRole("aggregatorOwner") + if (!res || !res.exist) { + error(`role ${color("aggregatorOwner", "blue")} not created, please create the role first`) + } + const aggregatorOwner = JSON.parse(fs.readFileSync(res.walletPath).toString()) + + res = checkRole("oracleOwner") + if (!res || !res.exist) { + error(`role ${color("oracleOwner", "blue")} not created, please create the role first`) + } + const oracleOwner = JSON.parse(fs.readFileSync(res.walletPath).toString()) + + const inputs = await inquirer + .prompt([ + { message: "Choose an aggregator", type: "list", name: "aggregator", choices: () => { + return deployed.pairs.map(p => ({ name: p.pairName.trim() + ` [${p.aggregator}]`, value: p.aggregator })) + }}, + { message: "Oracle name (eg. Solink)", type: "input", name: "oracleName", validate: (input) => { + if (!input) { + return "oracle name cannot be empty" + } + return true + } } + ]) + + const { oracleName, aggregator } = inputs + + const conn = await connectTo(network) + + const payerWallet = await Wallet.fromMnemonic(payer.mnemonic, conn) + const aggregatorOwnerWallet = await Wallet.fromMnemonic(aggregatorOwner.mnemonic, conn) + + const payerWalletBalance = await conn.getBalance(payerWallet.pubkey) + const fees = await calculatePayfees(OracleLayout.span, conn) + + if (payerWalletBalance < fees) { + error("insufficient balance to pay fees") + } + + const program = new FluxAggregator(payerWallet, new PublicKey(deployed.programId)) + + log("add oracle...") + const oracle = await program.addOracle({ + owner: new PublicKey(oracleOwner.pubkey), + description: oracleName.substr(0,32).padEnd(32), + aggregator: new PublicKey(aggregator), + aggregatorOwner: aggregatorOwnerWallet.account, + }) + + log(`add oracle success, pubkey: ${color(oracle.toBase58(), "blue")}, owner: ${color(oracleOwner.pubkey, "blue")}`) + fs.writeFileSync(deployedPath, JSON.stringify({ + ...deployed, + oracles: (deployed.oracles || []).concat([{ + name: oracleName, + aggregator, + pubkey: oracle.toBase58() + }]) + })) + }) + +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("aggregatorInfo") + .description("show aggregatorInfo") + .action(async () => { + // 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") + } + + const inputs = await inquirer + .prompt([ + { message: "Choose an aggregator", type: "list", name: "aggregator", choices: () => { + return deployed.pairs.map(p => ({ name: p.pairName.trim() + ` [${p.aggregator}]`, value: p.aggregator })) + }}, + ]) + + const { aggregator } = inputs + const conn = await connectTo(network) + + const accountInfo = await conn.getAccountInfo(new PublicKey(aggregator)) + + log(decodeAggregatorInfo(accountInfo)) + }) + +cli + .command("feed") + .description("oracle feeds to aggregator") + .action(async () => { + // 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") + } + + const inputs = await inquirer + .prompt([ + { message: "Choose an oracle", type: "list", name: "oracle", choices: () => { + return deployed.oracles.map(p => ({ name: p.name+ ` [${p.pubkey}]`, value: `${p.pubkey}|${p.aggregator}` })) + }}, + ]) + + const tmpArr = inputs.oracle.split("|") + + let res = checkRole("payer") + if (!res || !res.exist) { + error(`role ${color("payer", "blue")} not created`) + } + const payer = JSON.parse(fs.readFileSync(res.walletPath).toString()) + + res = checkRole("oracleOwner") + if (!res || !res.exist) { + error(`role ${color("oracleOwner", "blue")} not created, please create the role first`) + } + const oracleOwner = JSON.parse(fs.readFileSync(res.walletPath).toString()) + + let oracle = tmpArr[0], aggregator = tmpArr[1] + + let pair = "" + deployed.pairs.map((p) => { + if (p.aggregator == aggregator) { + pair = p.pairName + } + }) + + const conn = await connectTo(network) + + const payerWallet = await Wallet.fromMnemonic(payer.mnemonic, conn) + const oracleOwnerWallet = await Wallet.fromMnemonic(oracleOwner.mnemonic, conn) + + feed.start({ + oracle: new PublicKey(oracle), + oracleOwner: oracleOwnerWallet.account, + aggregator: new PublicKey(aggregator), + pair, + payerWallet, + programId: new PublicKey(deployed.programId) + }) }) cli.parse(process.argv) \ No newline at end of file diff --git a/src/deployed.json b/src/deployed.json new file mode 100644 index 0000000..9adaa90 --- /dev/null +++ b/src/deployed.json @@ -0,0 +1 @@ +{"network":"local","programId":"95mRqquh7vGi31i87g2tnozV9ngap9GtnkpsHp31kFt1","pairs":[{"pairName":"ETH/USD","aggregator":"GNUKT4Ug3574s6tuWXtPhC9u1VPWgx4XXVCcD8usXHGW"}],"oracles":[{"name":"Solink","aggregator":"GNUKT4Ug3574s6tuWXtPhC9u1VPWgx4XXVCcD8usXHGW","pubkey":"5fs1WPxopQfgRWujfShfh6qLuPGkHjyMYbgxtzZyNtrp"}]} \ No newline at end of file diff --git a/src/feed.ts b/src/feed.ts new file mode 100644 index 0000000..df3071a --- /dev/null +++ b/src/feed.ts @@ -0,0 +1,90 @@ +import { PublicKey, Account, Wallet } from "solray" +import WebSocket from "ws" + +import { decodeOracleInfo } from "./utils" + +import FluxAggregator from "./FluxAggregator" + +let nextSubmitTime = new Date().getTime() +let submiting = false +const submitInterval = 10 * 1000 + +interface StartParams { + oracle: PublicKey; + oracleOwner: Account; + aggregator: PublicKey; + pair: string; + payerWallet: Wallet; + programId: PublicKey; +} + +export async function start(params: StartParams) { + const { + oracle, + oracleOwner, + aggregator, + pair, + payerWallet, + programId, + } = params + + console.log("ready to feeds...") + const ws = new WebSocket("wss://ws-feed.pro.coinbase.com") + + const program = new FluxAggregator(payerWallet, programId) + + ws.on("open", () => { + console.log(`${pair} price feed connected`) + ws.send(JSON.stringify({ + "type": "subscribe", + "product_ids": [ + pair.replace("/", "-").toUpperCase(), + ], + "channels": [ + "ticker" + ] + })) + }) + + ws.on("message", (data) => { + const json = JSON.parse(data) + if (!json || !json.price) { + return console.log(data) + } + + if (submiting) return false + + console.log("new price:", json.price) + let now = new Date().getTime() + if (now < nextSubmitTime) { + console.log("submit cooling...") + return false + } + + submiting = true + + program.submit({ + aggregator, + oracle, + submission: BigInt(parseInt((json.price * 100) as any)), + owner: oracleOwner, + }).then(() => { + console.log("submit success!") + nextSubmitTime = now + submitInterval + payerWallet.conn.getAccountInfo(oracle).then((accountInfo) => { + console.log("oracle info:", decodeOracleInfo(accountInfo)) + }) + + }).catch((err) => { + console.log(err) + }).finally(() => { + submiting = false + }) + + + }) + + ws.on("close", (error) => { + console.error(error) + }) +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index ef730d8..0000000 --- a/src/index.ts +++ /dev/null @@ -1,114 +0,0 @@ -import dotenv from "dotenv" - -import { - Wallet, solana, NetworkName, BPFLoader, - PublicKey, Deployer, Account, -} from "solray" - -import { promises as fs } from "fs" -import { calculatePayfees, decodeAggregatorInfo, sleep } from "./utils" - -import FluxAggregator, { AggregatorLayout } from "./FluxAggregator" - -dotenv.config() - -const { NETWORK, SOLANA_PAYER_MNEMONIC } = process.env - -const args = process.argv.splice(2) -const cmd = args[0] - -const params = args[1] - -// so file path -const soPath = "../build/flux_aggregator.so" - -async function main() { - - if (!SOLANA_PAYER_MNEMONIC || !NETWORK) { - throw new Error("Config error.") - } - - const conn = solana.connect(NETWORK as NetworkName) - const wallet = await Wallet.fromMnemonic(SOLANA_PAYER_MNEMONIC, conn) - console.log("using wallet", wallet.address) - - let walletBalance = await conn.getBalance(wallet.pubkey) - console.log("wallet banalce:", walletBalance) - - const deployer = await Deployer.open("deploy.json") - - const programBinary = await fs.readFile(soPath) - - let fees = await calculatePayfees(programBinary.length, conn) - - const programAccount = await deployer.ensure("programAccount", async () => { - console.log("loading program... Network:", NETWORK) - - if (walletBalance < fees) { - // throw new Error("Insufficient balance to pay fees"); - // get airdrop - console.log("insufficient balance to pay fees, request airdrop...") - await conn.requestAirdrop(wallet.pubkey, fees) - await sleep(1000) - } - - const bpfLoader = new BPFLoader(wallet) - - const account = await bpfLoader.load(programBinary) - - return account - }) - - console.log("program loaded:", programAccount.publicKey.toBase58()) - - const program = new FluxAggregator(wallet, programAccount.publicKey) - - const aggregatorOwner = new Account() - console.log("initialize aggregator to owner:", aggregatorOwner.publicKey.toBase58()) - - walletBalance = await conn.getBalance(wallet.pubkey) - fees = await calculatePayfees(AggregatorLayout.span, conn) - - if (walletBalance < fees) { - console.log("insufficient balance to pay fees, request airdrop...") - await conn.requestAirdrop(wallet.pubkey, fees) - await sleep(1000) - } - - const aggregator = await program.initialize({ - submitInterval: 6, - minSubmissionValue: BigInt(1), - maxSubmissionValue: BigInt(99999), - description: "ETH/USDT".padEnd(32), - owner: aggregatorOwner - }) - console.log("aggregator initialized, pubkey:", aggregator.toBase58()) - - const oracleOwner = new Account() - console.log("add an oracle...") - const oracle = await program.addOracle({ - owner: oracleOwner.publicKey, - description: "Solink".padEnd(32), - aggregator, - aggregatorOwner, - }) - - console.log("oracle added, pubkey:", oracle.toBase58(), ", owner: ", oracleOwner.publicKey.toBase58()) - - console.log("oracle submiting...") - await program.submit({ - aggregator, - oracle, - submission: BigInt(123), - owner: oracleOwner, - }) - - console.log("submit success! get aggregator info...") - - const accountInfo = await conn.getAccountInfo(aggregator) - - console.log("aggregator info:", decodeAggregatorInfo(accountInfo)) -} - - -main().catch(err => console.log({ err })) \ No newline at end of file diff --git a/src/oracle.ts b/src/oracle.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/utils.ts b/src/utils.ts index 0b8d38e..2f8623c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,6 @@ import { Connection, BpfLoader, PublicKey } from "@solana/web3.js" -import { AggregatorLayout, SubmissionLayout } from "./FluxAggregator" +import { AggregatorLayout, SubmissionLayout, OracleLayout } from "./FluxAggregator" import { solana, Wallet, NetworkName } from "solray" @@ -92,6 +92,22 @@ export function decodeAggregatorInfo(accountInfo) { } } +export function decodeOracleInfo(accountInfo) { + const data = Buffer.from(accountInfo.data) + + const oracle = OracleLayout.decode(data) + + oracle.submission = oracle.submission.readBigUInt64LE().toString() + 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 connectTo(network: NetworkName): Promise { const conn = solana.connect(network as NetworkName) return conn diff --git a/src/wallets/aggregatorOwner.json b/src/wallets/aggregatorOwner.json new file mode 100644 index 0000000..383860e --- /dev/null +++ b/src/wallets/aggregatorOwner.json @@ -0,0 +1 @@ +{"pubkey":"2ATirbVBRtEGGY3jeEcEsTGLBVSJEvk6WLb1K7droYp2","secretKey":"[183,247,179,232,164,36,17,253,149,205,222,132,4,4,154,4,206,110,37,117,129,66,18,146,95,45,172,29,62,133,203,242,17,72,32,226,225,18,116,158,33,176,7,145,15,65,108,116,53,15,28,112,25,66,150,7,240,20,112,110,21,92,250,67]","mnemonic":"forget mushroom capable trim chapter rally long congress humor maximum title citizen"} \ No newline at end of file diff --git a/src/wallets/oracleOwner.json b/src/wallets/oracleOwner.json new file mode 100644 index 0000000..0c32054 --- /dev/null +++ b/src/wallets/oracleOwner.json @@ -0,0 +1 @@ +{"pubkey":"FUZDeYACNRvDYD9A1cmrEqfZ5GiE5rBEfc2rcXW2sXxw","secretKey":"[18,53,121,118,64,233,227,172,4,14,12,103,202,93,26,114,190,103,143,81,125,144,144,156,74,251,138,251,131,128,135,13,215,18,186,86,171,14,169,75,62,159,168,103,242,239,62,120,134,113,144,77,53,136,194,105,57,172,193,138,105,231,244,228]","mnemonic":"subway wine design chuckle tooth helmet solution butter tooth slab leopard useful"} \ No newline at end of file diff --git a/src/wallets/payer.json b/src/wallets/payer.json new file mode 100644 index 0000000..c6208c1 --- /dev/null +++ b/src/wallets/payer.json @@ -0,0 +1 @@ +{"pubkey":"79AnPHtN7P4e36CDrP9CzuVRbwRUZdVMQ7weFiktYjPv","secretKey":"[224,93,108,163,202,117,226,167,224,216,219,4,51,192,130,204,111,22,71,199,97,113,139,183,236,194,229,135,214,12,231,108,91,61,212,65,148,88,37,162,244,108,41,171,74,32,204,29,135,215,103,88,134,32,115,81,222,40,175,222,205,208,152,65]","mnemonic":"dove ribbon nut beef trouble language unfair quiz sand wash copper result"} \ No newline at end of file