wtf
This commit is contained in:
parent
01a910e7aa
commit
7a5112147f
|
@ -51,11 +51,7 @@ Create the `btc:usd` feed (that accepts max and min u64 as valid submission valu
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn solink add-aggregator \
|
yarn solink add-aggregator \
|
||||||
--feedName btc:usd \
|
--feedName btc:usd
|
||||||
--submitInterval 6 \
|
|
||||||
--minSubmissionValue 0 \
|
|
||||||
--maxSubmissionValue 18446744073709551615 \
|
|
||||||
--submissionDecimals 2
|
|
||||||
|
|
||||||
feed initialized, pubkey: 3aTBom2uodyWkuVPiUkwCZ2HiFywdUx9tp7su7U2H4Nx
|
feed initialized, pubkey: 3aTBom2uodyWkuVPiUkwCZ2HiFywdUx9tp7su7U2H4Nx
|
||||||
```
|
```
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@solana/web3.js": "^0.87.1",
|
"@solana/web3.js": "^0.90.5",
|
||||||
"buffer-layout": "^1.2.0",
|
"buffer-layout": "^1.2.0",
|
||||||
"commander": "^6.2.0",
|
"commander": "^6.2.0",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
|
|
|
@ -16,10 +16,10 @@ thiserror = "1.0"
|
||||||
num-derive = "0.3"
|
num-derive = "0.3"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
num_enum = "0.5.1"
|
num_enum = "0.5.1"
|
||||||
|
hex = "0.4"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
solana-sdk = "1.4.8"
|
solana-sdk = "1.4.8"
|
||||||
hex = "0.4"
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "lib"]
|
crate-type = ["cdylib", "lib"]
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
//! Program entrypoint
|
//! Program entrypoint
|
||||||
|
|
||||||
use crate::{processor::Processor};
|
use crate::processor::Processor;
|
||||||
|
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey,
|
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey,
|
||||||
|
msg,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use hex;
|
||||||
|
|
||||||
entrypoint!(process_instruction);
|
entrypoint!(process_instruction);
|
||||||
|
|
||||||
// Program entrypoint's implementation
|
// Program entrypoint's implementation
|
||||||
|
@ -14,11 +17,12 @@ fn process_instruction<'a>(
|
||||||
accounts: &'a [AccountInfo<'a>],
|
accounts: &'a [AccountInfo<'a>],
|
||||||
instruction_data: &[u8],
|
instruction_data: &[u8],
|
||||||
) -> ProgramResult {
|
) -> ProgramResult {
|
||||||
if let Err(error) = Processor::process(program_id, accounts, instruction_data) {
|
msg!("instruction: {}", hex::encode(instruction_data));
|
||||||
// catch the error so we can print it
|
// if let Err(error) = Processor::process(program_id, accounts, instruction_data) {
|
||||||
// error.print::<Error>();
|
// // catch the error so we can print it
|
||||||
// msg!("{:?}", error);
|
// // error.print::<Error>();
|
||||||
return Err(error);
|
// // msg!("{:?}", error);
|
||||||
}
|
// return Err(error);
|
||||||
|
// }
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,3 +38,23 @@ pub enum Instruction {
|
||||||
faucet_owner_seed: [u8; 32],
|
faucet_owner_seed: [u8; 32],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
use solana_program::{entrypoint::ProgramResult, program_error::ProgramError};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode_instruction() -> ProgramResult {
|
||||||
|
let input = hex::decode("004254433a5553442020202020202020202020202020202020202020202020202002010c010100000000000000").map_err(|_| ProgramError::InvalidInstructionData)?;
|
||||||
|
|
||||||
|
let inx = Instruction::try_from_slice(&input)
|
||||||
|
.map_err(|_| ProgramError::InvalidInstructionData)?;
|
||||||
|
println!("{:?}", inx);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -42,8 +42,8 @@ impl<'a, 'b> Accounts<'a, 'b> {
|
||||||
struct InitializeContext<'a> {
|
struct InitializeContext<'a> {
|
||||||
rent: Rent,
|
rent: Rent,
|
||||||
aggregator: &'a AccountInfo<'a>,
|
aggregator: &'a AccountInfo<'a>,
|
||||||
aggregator_owner: &'a AccountInfo<'a>,
|
aggregator_owner: &'a AccountInfo<'a>, // signed
|
||||||
round_submissions: &'a AccountInfo<'a>, // belongs_to: aggregator
|
round_submissions: &'a AccountInfo<'a>, // belongs_to: aggregator
|
||||||
answer_submissions: &'a AccountInfo<'a>, // belongs_to: aggregator
|
answer_submissions: &'a AccountInfo<'a>, // belongs_to: aggregator
|
||||||
|
|
||||||
config: AggregatorConfig,
|
config: AggregatorConfig,
|
||||||
|
|
|
@ -187,3 +187,24 @@ impl IsInitialized for Oracle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl InitBorshState for Oracle {}
|
impl InitBorshState for Oracle {}
|
||||||
|
|
||||||
|
mod tests {
|
||||||
|
use crate::borsh_utils;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_packed_len() {
|
||||||
|
println!(
|
||||||
|
"Aggregator len: {}",
|
||||||
|
borsh_utils::get_packed_len::<Aggregator>()
|
||||||
|
);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Submissions len: {}",
|
||||||
|
borsh_utils::get_packed_len::<Submissions>()
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("Oracle len: {}", borsh_utils::get_packed_len::<Oracle>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,18 +1,27 @@
|
||||||
import {
|
import {
|
||||||
PublicKey, BaseProgram, Account,
|
PublicKey,
|
||||||
Wallet, System, SPLToken
|
BaseProgram,
|
||||||
} from "solray";
|
Account,
|
||||||
|
Wallet,
|
||||||
|
System,
|
||||||
|
SPLToken,
|
||||||
|
} from "solray"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SYSVAR_RENT_PUBKEY, SYSVAR_CLOCK_PUBKEY,
|
SYSVAR_RENT_PUBKEY,
|
||||||
TransactionInstruction, SystemProgram
|
SYSVAR_CLOCK_PUBKEY,
|
||||||
} from "@solana/web3.js";
|
TransactionInstruction,
|
||||||
|
SystemProgram,
|
||||||
import { publicKey, u64LEBuffer, uint64, BufferLayout } from "solray/lib/util/encoding";
|
} from "@solana/web3.js"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
decodeOracleInfo
|
publicKey,
|
||||||
} from "./utils"
|
u64LEBuffer,
|
||||||
|
uint64,
|
||||||
|
BufferLayout,
|
||||||
|
} from "solray/lib/util/encoding"
|
||||||
|
|
||||||
|
import { decodeOracleInfo } from "./utils"
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
// import BufferLayout from "buffer-layout";
|
// import BufferLayout from "buffer-layout";
|
||||||
|
@ -21,125 +30,147 @@ import { schema } from "./schema"
|
||||||
import * as encoding from "./schema"
|
import * as encoding from "./schema"
|
||||||
import { deserialize, serialize } from "borsh"
|
import { deserialize, serialize } from "borsh"
|
||||||
|
|
||||||
export const AggregatorLayout = BufferLayout.struct([
|
export const AggregatorLayout = BufferLayout.struct([])
|
||||||
BufferLayout.blob(4, "submitInterval"),
|
|
||||||
uint64("minSubmissionValue"),
|
|
||||||
uint64("maxSubmissionValue"),
|
|
||||||
BufferLayout.u8("submissionDecimals"),
|
|
||||||
BufferLayout.blob(32, "description"),
|
|
||||||
BufferLayout.u8("isInitialized"),
|
|
||||||
publicKey('owner'),
|
|
||||||
BufferLayout.blob(576, "submissions"),
|
|
||||||
]);
|
|
||||||
|
|
||||||
export const OracleLayout = BufferLayout.struct([
|
export const OracleLayout = BufferLayout.struct([
|
||||||
uint64("nextSubmitTime"),
|
uint64("nextSubmitTime"),
|
||||||
BufferLayout.blob(32, "description"),
|
BufferLayout.blob(32, "description"),
|
||||||
BufferLayout.u8("isInitialized"),
|
BufferLayout.u8("isInitialized"),
|
||||||
uint64("withdrawable"),
|
uint64("withdrawable"),
|
||||||
publicKey('aggregator'),
|
publicKey("aggregator"),
|
||||||
publicKey('owner'),
|
publicKey("owner"),
|
||||||
]);
|
])
|
||||||
|
|
||||||
export const SubmissionLayout = BufferLayout.struct([
|
export const SubmissionLayout = BufferLayout.struct([
|
||||||
uint64("time"),
|
uint64("time"),
|
||||||
uint64("value"),
|
uint64("value"),
|
||||||
publicKey('oracle'),
|
publicKey("oracle"),
|
||||||
]);
|
])
|
||||||
|
|
||||||
interface InitializeParams {
|
interface InitializeParams {
|
||||||
submitInterval: number;
|
config: encoding.AggregatorConfig
|
||||||
minSubmissionValue: bigint;
|
owner: Account
|
||||||
maxSubmissionValue: bigint;
|
|
||||||
submissionDecimals: number;
|
|
||||||
description: string;
|
|
||||||
owner: Account;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InitializeInstructionParams extends InitializeParams {
|
interface InitializeInstructionParams extends InitializeParams {
|
||||||
aggregator: PublicKey;
|
aggregator: PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AddOracleParams {
|
interface AddOracleParams {
|
||||||
owner: PublicKey;
|
owner: PublicKey
|
||||||
description: string;
|
description: string
|
||||||
aggregator: PublicKey;
|
aggregator: PublicKey
|
||||||
// To prove you are the aggregator owner
|
// To prove you are the aggregator owner
|
||||||
aggregatorOwner: Account;
|
aggregatorOwner: Account
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AddOracleInstructionParams extends AddOracleParams {
|
interface AddOracleInstructionParams extends AddOracleParams {
|
||||||
oracle: PublicKey;
|
oracle: PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RemoveOracleParams {
|
interface RemoveOracleParams {
|
||||||
aggregator: PublicKey;
|
aggregator: PublicKey
|
||||||
oracle: PublicKey;
|
oracle: PublicKey
|
||||||
// To prove you are the aggregator owner
|
// To prove you are the aggregator owner
|
||||||
authority?: Account;
|
authority?: Account
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RemoveOracleInstructionParams extends RemoveOracleParams {
|
interface RemoveOracleInstructionParams extends RemoveOracleParams {}
|
||||||
}
|
|
||||||
|
|
||||||
interface SubmitParams {
|
interface SubmitParams {
|
||||||
aggregator: PublicKey;
|
aggregator: PublicKey
|
||||||
oracle: PublicKey;
|
oracle: PublicKey
|
||||||
// The oracle"s index
|
// The oracle"s index
|
||||||
submission: bigint;
|
submission: bigint
|
||||||
// oracle owner
|
// oracle owner
|
||||||
owner: Account;
|
owner: Account
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SubmitInstructionParams extends SubmitParams {
|
interface SubmitInstructionParams extends SubmitParams {}
|
||||||
}
|
|
||||||
|
|
||||||
interface WithdrawParams {
|
interface WithdrawParams {
|
||||||
aggregator: PublicKey,
|
aggregator: PublicKey
|
||||||
// withdraw to
|
// withdraw to
|
||||||
receiver: PublicKey,
|
receiver: PublicKey
|
||||||
// withdraw amount
|
// withdraw amount
|
||||||
amount: bigint,
|
amount: bigint
|
||||||
tokenAccount: PublicKey,
|
tokenAccount: PublicKey
|
||||||
tokenOwner: PublicKey,
|
tokenOwner: PublicKey
|
||||||
// signer
|
// signer
|
||||||
authority: Account,
|
authority: Account
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WithdrawInstructionParams extends WithdrawParams {
|
interface WithdrawInstructionParams extends WithdrawParams {}
|
||||||
}
|
|
||||||
|
|
||||||
export default class FluxAggregator extends BaseProgram {
|
export default class FluxAggregator extends BaseProgram {
|
||||||
|
|
||||||
private sys: System
|
private sys: System
|
||||||
constructor(wallet: Wallet, programID: PublicKey) {
|
constructor(wallet: Wallet, programID: PublicKey) {
|
||||||
super(wallet, programID);
|
super(wallet, programID)
|
||||||
this.sys = new System(this.wallet);
|
this.sys = new System(this.wallet)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async initialize(params: InitializeParams): Promise<Account> {
|
public async initialize(params: InitializeParams): Promise<Account> {
|
||||||
const account = new Account();
|
const aggregator = new Account()
|
||||||
|
const answer_submissions = new Account()
|
||||||
|
const round_submissions = new Account()
|
||||||
|
|
||||||
await this.sendTx([
|
console.log({ config: params.config, program: this.programID.toString() })
|
||||||
await this.sys.createRentFreeAccountInstruction({
|
|
||||||
newPubicKey: account.publicKey,
|
|
||||||
space: AggregatorLayout.span,
|
|
||||||
programID: this.programID,
|
|
||||||
}),
|
|
||||||
this.initializeInstruction({
|
|
||||||
...params,
|
|
||||||
aggregator: account.publicKey,
|
|
||||||
})
|
|
||||||
], [this.account, account, params.owner]);
|
|
||||||
|
|
||||||
return account;
|
const input = encoding.Initialize.serialize({ config: params.config })
|
||||||
|
// console.log("init instruction", input.toString("hex"))
|
||||||
|
console.log("init instruction", input.toString("hex"))
|
||||||
|
console.log("rent", [encoding.Aggregator.size, encoding.Submissions.size])
|
||||||
|
console.log("wallet", this.account.publicKey.toString())
|
||||||
|
|
||||||
|
await this.sendTx(
|
||||||
|
[
|
||||||
|
// await this.sys.createRentFreeAccountInstruction({
|
||||||
|
// newPubicKey: aggregator.publicKey,
|
||||||
|
// space: encoding.Aggregator.size,
|
||||||
|
// programID: this.programID,
|
||||||
|
// }),
|
||||||
|
// await this.sys.createRentFreeAccountInstruction({
|
||||||
|
// newPubicKey: answer_submissions.publicKey,
|
||||||
|
// space: encoding.Submissions.size,
|
||||||
|
// programID: this.programID,
|
||||||
|
// }),
|
||||||
|
// await this.sys.createRentFreeAccountInstruction({
|
||||||
|
// newPubicKey: round_submissions.publicKey,
|
||||||
|
// space: encoding.Submissions.size,
|
||||||
|
// programID: this.programID,
|
||||||
|
// }),
|
||||||
|
this.instruction(Buffer.from("deadbeaf", "hex"), [
|
||||||
|
// SYSVAR_RENT_PUBKEY,
|
||||||
|
// { write: aggregator.publicKey },
|
||||||
|
// params.owner, // signed
|
||||||
|
// { write: round_submissions.publicKey },
|
||||||
|
// { write: answer_submissions.publicKey },
|
||||||
|
|
||||||
|
// SYSVAR_RENT_PUBKEY,
|
||||||
|
// { write: aggregator },
|
||||||
|
// params.owner, // signed
|
||||||
|
// { write: round_submissions },
|
||||||
|
// { write: answer_submissions },
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
this.account,
|
||||||
|
// aggregator,
|
||||||
|
// params.owner,
|
||||||
|
// round_submissions,
|
||||||
|
// answer_submissions,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
return aggregator
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeInstruction(params: InitializeInstructionParams): TransactionInstruction {
|
private initializeInstruction(
|
||||||
|
params: InitializeInstructionParams
|
||||||
|
): TransactionInstruction {
|
||||||
let { aggregator, owner } = params
|
let { aggregator, owner } = params
|
||||||
|
|
||||||
const input = encoding.Initialize.serialize(params)
|
const input = encoding.Initialize.serialize(params)
|
||||||
// console.log(input.toString("hex"))
|
|
||||||
|
|
||||||
return this.instruction(input, [
|
return this.instruction(input, [
|
||||||
SYSVAR_RENT_PUBKEY,
|
SYSVAR_RENT_PUBKEY,
|
||||||
|
@ -149,21 +180,24 @@ export default class FluxAggregator extends BaseProgram {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async addOracle(params: AddOracleParams): Promise<Account> {
|
public async addOracle(params: AddOracleParams): Promise<Account> {
|
||||||
const account = new Account();
|
const oracle = new Account()
|
||||||
|
|
||||||
await this.sendTx([
|
await this.sendTx(
|
||||||
await this.sys.createRentFreeAccountInstruction({
|
[
|
||||||
newPubicKey: account.publicKey,
|
await this.sys.createRentFreeAccountInstruction({
|
||||||
space: OracleLayout.span,
|
newPubicKey: oracle.publicKey,
|
||||||
programID: this.programID,
|
space: encoding.Oracle.size,
|
||||||
}),
|
programID: this.programID,
|
||||||
this.addOracleInstruction({
|
}),
|
||||||
...params,
|
this.addOracleInstruction({
|
||||||
oracle: account.publicKey,
|
...params,
|
||||||
})
|
oracle: oracle.publicKey,
|
||||||
], [this.account, account, params.aggregatorOwner]);
|
}),
|
||||||
|
],
|
||||||
|
[this.account, oracle, params.aggregatorOwner]
|
||||||
|
)
|
||||||
|
|
||||||
return account;
|
return oracle
|
||||||
}
|
}
|
||||||
|
|
||||||
public async oracleInfo(pubkey: PublicKey) {
|
public async oracleInfo(pubkey: PublicKey) {
|
||||||
|
@ -171,99 +205,100 @@ export default class FluxAggregator extends BaseProgram {
|
||||||
return decodeOracleInfo(info)
|
return decodeOracleInfo(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
private addOracleInstruction(params: AddOracleInstructionParams): TransactionInstruction {
|
private addOracleInstruction(
|
||||||
const {
|
params: AddOracleInstructionParams
|
||||||
oracle,
|
): TransactionInstruction {
|
||||||
owner,
|
const { oracle, owner, description, aggregator, aggregatorOwner } = params
|
||||||
description,
|
|
||||||
aggregator,
|
|
||||||
aggregatorOwner,
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
const layout = BufferLayout.struct([
|
const layout = BufferLayout.struct([
|
||||||
BufferLayout.u8("instruction"),
|
BufferLayout.u8("instruction"),
|
||||||
BufferLayout.blob(32, "description"),
|
BufferLayout.blob(32, "description"),
|
||||||
]);
|
])
|
||||||
|
|
||||||
return this.instructionEncode(layout, {
|
return this.instructionEncode(
|
||||||
instruction: 1, // add oracle instruction
|
layout,
|
||||||
description: Buffer.from(description),
|
{
|
||||||
}, [
|
instruction: 1, // add oracle instruction
|
||||||
{ write: oracle },
|
description: Buffer.from(description),
|
||||||
owner,
|
},
|
||||||
SYSVAR_CLOCK_PUBKEY,
|
[
|
||||||
{ write: aggregator },
|
{ write: oracle },
|
||||||
aggregatorOwner,
|
owner,
|
||||||
]);
|
SYSVAR_CLOCK_PUBKEY,
|
||||||
|
{ write: aggregator },
|
||||||
|
aggregatorOwner,
|
||||||
|
]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async removeOracle(params: RemoveOracleParams): Promise<void> {
|
public async removeOracle(params: RemoveOracleParams): Promise<void> {
|
||||||
await this.sendTx([
|
await this.sendTx(
|
||||||
this.removeOracleInstruction(params)
|
[this.removeOracleInstruction(params)],
|
||||||
], [this.account, params.authority || this.wallet.account]);
|
[this.account, params.authority || this.wallet.account]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private removeOracleInstruction(params: RemoveOracleInstructionParams): TransactionInstruction {
|
private removeOracleInstruction(
|
||||||
const {
|
params: RemoveOracleInstructionParams
|
||||||
authority,
|
): TransactionInstruction {
|
||||||
aggregator,
|
const { authority, aggregator, oracle } = params
|
||||||
oracle,
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
const layout = BufferLayout.struct([
|
const layout = BufferLayout.struct([
|
||||||
BufferLayout.u8("instruction"),
|
BufferLayout.u8("instruction"),
|
||||||
BufferLayout.blob(32, "oracle"),
|
BufferLayout.blob(32, "oracle"),
|
||||||
]);
|
])
|
||||||
|
|
||||||
return this.instructionEncode(layout, {
|
return this.instructionEncode(
|
||||||
instruction: 2, // remove oracle instruction
|
layout,
|
||||||
oracle: oracle.toBuffer()
|
{
|
||||||
}, [
|
instruction: 2, // remove oracle instruction
|
||||||
//
|
oracle: oracle.toBuffer(),
|
||||||
{ write: aggregator },
|
},
|
||||||
authority || this.wallet.account,
|
[
|
||||||
]);
|
//
|
||||||
|
{ write: aggregator },
|
||||||
|
authority || this.wallet.account,
|
||||||
|
]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async submit(params: SubmitParams): Promise<void> {
|
public async submit(params: SubmitParams): Promise<void> {
|
||||||
await this.sendTx([
|
await this.sendTx(
|
||||||
this.submitInstruction(params)
|
[this.submitInstruction(params)],
|
||||||
], [this.account, params.owner]);
|
[this.account, params.owner]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private submitInstruction(params: SubmitInstructionParams): TransactionInstruction {
|
private submitInstruction(
|
||||||
const {
|
params: SubmitInstructionParams
|
||||||
aggregator,
|
): TransactionInstruction {
|
||||||
oracle,
|
const { aggregator, oracle, submission, owner } = params
|
||||||
submission,
|
|
||||||
owner,
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
const layout = BufferLayout.struct([
|
const layout = BufferLayout.struct([
|
||||||
BufferLayout.u8("instruction"),
|
BufferLayout.u8("instruction"),
|
||||||
uint64("submission"),
|
uint64("submission"),
|
||||||
]);
|
])
|
||||||
|
|
||||||
return this.instructionEncode(layout, {
|
return this.instructionEncode(
|
||||||
instruction: 3, // submit instruction
|
layout,
|
||||||
submission: u64LEBuffer(submission),
|
{
|
||||||
}, [
|
instruction: 3, // submit instruction
|
||||||
{ write: aggregator },
|
submission: u64LEBuffer(submission),
|
||||||
SYSVAR_CLOCK_PUBKEY,
|
},
|
||||||
{ write: oracle },
|
[{ write: aggregator }, SYSVAR_CLOCK_PUBKEY, { write: oracle }, owner]
|
||||||
owner,
|
)
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async withdraw(params: WithdrawParams): Promise<void> {
|
public async withdraw(params: WithdrawParams): Promise<void> {
|
||||||
await this.sendTx([
|
await this.sendTx(
|
||||||
this.withdrawInstruction(params)
|
[this.withdrawInstruction(params)],
|
||||||
], [this.account, params.authority]);
|
[this.account, params.authority]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private withdrawInstruction(params: WithdrawInstructionParams): TransactionInstruction {
|
private withdrawInstruction(
|
||||||
|
params: WithdrawInstructionParams
|
||||||
|
): TransactionInstruction {
|
||||||
const {
|
const {
|
||||||
aggregator,
|
aggregator,
|
||||||
receiver,
|
receiver,
|
||||||
|
@ -271,24 +306,27 @@ export default class FluxAggregator extends BaseProgram {
|
||||||
tokenOwner,
|
tokenOwner,
|
||||||
tokenAccount,
|
tokenAccount,
|
||||||
authority,
|
authority,
|
||||||
} = params;
|
} = params
|
||||||
|
|
||||||
const layout = BufferLayout.struct([
|
const layout = BufferLayout.struct([
|
||||||
BufferLayout.u8("instruction"),
|
BufferLayout.u8("instruction"),
|
||||||
uint64("amount"),
|
uint64("amount"),
|
||||||
]);
|
])
|
||||||
|
|
||||||
return this.instructionEncode(layout, {
|
return this.instructionEncode(
|
||||||
instruction: 4, // withdraw instruction
|
layout,
|
||||||
amount: u64LEBuffer(amount),
|
{
|
||||||
}, [
|
instruction: 4, // withdraw instruction
|
||||||
{ write: aggregator },
|
amount: u64LEBuffer(amount),
|
||||||
{ write: tokenAccount },
|
},
|
||||||
{ write: receiver },
|
[
|
||||||
SPLToken.programID,
|
{ write: aggregator },
|
||||||
tokenOwner,
|
{ write: tokenAccount },
|
||||||
{ write: authority },
|
{ write: receiver },
|
||||||
]);
|
SPLToken.programID,
|
||||||
|
tokenOwner,
|
||||||
|
{ write: authority },
|
||||||
|
]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
126
src/cli.ts
126
src/cli.ts
|
@ -4,13 +4,19 @@ import fs from "fs"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BPFLoader, PublicKey, Wallet, NetworkName,
|
BPFLoader,
|
||||||
solana, Deployer, SPLToken, ProgramAccount
|
PublicKey,
|
||||||
|
Wallet,
|
||||||
|
NetworkName,
|
||||||
|
solana,
|
||||||
|
Deployer,
|
||||||
|
SPLToken,
|
||||||
|
ProgramAccount,
|
||||||
} from "solray"
|
} from "solray"
|
||||||
|
|
||||||
import dotenv from "dotenv"
|
import dotenv from "dotenv"
|
||||||
|
|
||||||
import FluxAggregator, { AggregatorLayout, OracleLayout } from "./FluxAggregator"
|
import FluxAggregator from "./FluxAggregator"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
decodeAggregatorInfo,
|
decodeAggregatorInfo,
|
||||||
|
@ -20,17 +26,20 @@ import {
|
||||||
} from "./utils"
|
} from "./utils"
|
||||||
|
|
||||||
import * as feed from "./feed"
|
import * as feed from "./feed"
|
||||||
|
import { AggregatorConfig } from "./schema"
|
||||||
|
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
const cli = new Command()
|
const cli = new Command()
|
||||||
|
|
||||||
const FLUX_AGGREGATOR_SO = path.resolve(__dirname, "../build/flux_aggregator.so")
|
const FLUX_AGGREGATOR_SO = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
"../build/flux_aggregator.so"
|
||||||
|
)
|
||||||
const network = (process.env.NETWORK || "local") as NetworkName
|
const network = (process.env.NETWORK || "local") as NetworkName
|
||||||
const conn = solana.connect(network)
|
const conn = solana.connect(network)
|
||||||
|
|
||||||
class AppContext {
|
class AppContext {
|
||||||
|
|
||||||
static readonly AGGREGATOR_PROGRAM = "aggregatorProgram"
|
static readonly AGGREGATOR_PROGRAM = "aggregatorProgram"
|
||||||
|
|
||||||
static async forAdmin() {
|
static async forAdmin() {
|
||||||
|
@ -47,7 +56,7 @@ class AppContext {
|
||||||
return new AppContext(deployer, wallet)
|
return new AppContext(deployer, wallet)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(public deployer: Deployer, public wallet: Wallet) { }
|
constructor(public deployer: Deployer, public wallet: Wallet) {}
|
||||||
|
|
||||||
get aggregatorProgramID() {
|
get aggregatorProgramID() {
|
||||||
return this.aggregatorProgramAccount.publicKey
|
return this.aggregatorProgramAccount.publicKey
|
||||||
|
@ -70,7 +79,16 @@ class AppContext {
|
||||||
|
|
||||||
function color(s, c = "black", b = false): string {
|
function color(s, c = "black", b = false): string {
|
||||||
// 30m Black, 31m Red, 32m Green, 33m Yellow, 34m Blue, 35m Magenta, 36m Cyanic, 37m White
|
// 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"]
|
const cArr = [
|
||||||
|
"black",
|
||||||
|
"red",
|
||||||
|
"green",
|
||||||
|
"yellow",
|
||||||
|
"blue",
|
||||||
|
"megenta",
|
||||||
|
"cyanic",
|
||||||
|
"white",
|
||||||
|
]
|
||||||
|
|
||||||
let cIdx = cArr.indexOf(c)
|
let cIdx = cArr.indexOf(c)
|
||||||
let bold = b ? "\x1b[1m" : ""
|
let bold = b ? "\x1b[1m" : ""
|
||||||
|
@ -89,14 +107,13 @@ function log(message: any) {
|
||||||
console.log(message)
|
console.log(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
cli
|
cli.command("generate-wallet").action(async () => {
|
||||||
.command("generate-wallet").action(async () => {
|
const mnemonic = Wallet.generateMnemonic()
|
||||||
const mnemonic = Wallet.generateMnemonic()
|
const wallet = await Wallet.fromMnemonic(mnemonic, conn)
|
||||||
const wallet = await Wallet.fromMnemonic(mnemonic, conn)
|
|
||||||
|
|
||||||
log(`address: ${wallet.address}`)
|
log(`address: ${wallet.address}`)
|
||||||
log(`mnemonic: ${mnemonic}`)
|
log(`mnemonic: ${mnemonic}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
cli
|
cli
|
||||||
.command("airdrop <address>")
|
.command("airdrop <address>")
|
||||||
|
@ -118,40 +135,63 @@ cli
|
||||||
.action(async () => {
|
.action(async () => {
|
||||||
const { wallet, deployer } = await AppContext.forAdmin()
|
const { wallet, deployer } = await AppContext.forAdmin()
|
||||||
|
|
||||||
const programAccount = await deployer.ensure(AppContext.AGGREGATOR_PROGRAM, async () => {
|
const programAccount = await deployer.ensure(
|
||||||
const programBinary = fs.readFileSync(FLUX_AGGREGATOR_SO)
|
AppContext.AGGREGATOR_PROGRAM,
|
||||||
|
async () => {
|
||||||
|
const programBinary = fs.readFileSync(FLUX_AGGREGATOR_SO)
|
||||||
|
|
||||||
log(`deploying ${FLUX_AGGREGATOR_SO}...`)
|
log(`deploying ${FLUX_AGGREGATOR_SO}...`)
|
||||||
const bpfLoader = new BPFLoader(wallet)
|
const bpfLoader = new BPFLoader(wallet)
|
||||||
|
|
||||||
return bpfLoader.load(programBinary)
|
return bpfLoader.load(programBinary)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
log(`deployed aggregator program. program id: ${color(programAccount.publicKey.toBase58(), "blue")}`)
|
log(
|
||||||
|
`deployed aggregator program. program id: ${color(
|
||||||
|
programAccount.publicKey.toBase58(),
|
||||||
|
"blue"
|
||||||
|
)}`
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
cli
|
cli
|
||||||
.command("add-aggregator")
|
.command("add-aggregator")
|
||||||
.description("create an aggregator")
|
.description("create an aggregator")
|
||||||
.option("--feedName <string>", "feed pair name")
|
.option("--feedName <string>", "feed pair name")
|
||||||
.option("--submitInterval <number>", "min wait time between submissions", "6")
|
.option("--decimals <number>", "submission decimals", "2")
|
||||||
.option("--submissionDecimals <number>", "submission decimals", "12")
|
.option("--minSubmissions <number>", "minSubmissions", "1")
|
||||||
.option("--minSubmissionValue <number>", "minSubmissionValue", "0")
|
.option("--maxSubmissions <number>", "maxSubmissions", "12")
|
||||||
.option("--maxSubmissionValue <number>", "maxSubmissionValue", "18446744073709551615")
|
.option("--rewardAmount <number>", "rewardAmount", "1")
|
||||||
|
.option("--restartDelay <number>", "restartDelay", "1")
|
||||||
.action(async (opts) => {
|
.action(async (opts) => {
|
||||||
const { deployer, wallet, aggregatorProgramAccount: aggregatorProgram } = await AppContext.forAdmin()
|
const {
|
||||||
|
deployer,
|
||||||
|
wallet,
|
||||||
|
aggregatorProgramAccount: aggregatorProgram,
|
||||||
|
} = await AppContext.forAdmin()
|
||||||
|
|
||||||
const { feedName, submitInterval, minSubmissionValue, maxSubmissionValue, submissionDecimals } = opts
|
const {
|
||||||
|
feedName,
|
||||||
|
restartDelay,
|
||||||
|
minSubmissions,
|
||||||
|
maxSubmissions,
|
||||||
|
decimals,
|
||||||
|
rewardAmount,
|
||||||
|
} = opts
|
||||||
|
|
||||||
const aggregator = new FluxAggregator(wallet, aggregatorProgram.publicKey)
|
const aggregator = new FluxAggregator(wallet, aggregatorProgram.publicKey)
|
||||||
|
|
||||||
const feed = await deployer.ensure(feedName, async () => {
|
const feed = await deployer.ensure(feedName, async () => {
|
||||||
return aggregator.initialize({
|
return aggregator.initialize({
|
||||||
submitInterval: parseInt(submitInterval),
|
config: new AggregatorConfig({
|
||||||
minSubmissionValue: BigInt(minSubmissionValue),
|
description: feedName,
|
||||||
maxSubmissionValue: BigInt(maxSubmissionValue),
|
decimals,
|
||||||
description: feedName,
|
minSubmissions,
|
||||||
submissionDecimals,
|
maxSubmissions,
|
||||||
|
restartDelay,
|
||||||
|
rewardAmount: BigInt(rewardAmount),
|
||||||
|
}),
|
||||||
owner: wallet.account,
|
owner: wallet.account,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -200,7 +240,7 @@ cli
|
||||||
description: oracleName.substr(0, 32).padEnd(32),
|
description: oracleName.substr(0, 32).padEnd(32),
|
||||||
aggregator: new PublicKey(feedAddress),
|
aggregator: new PublicKey(feedAddress),
|
||||||
aggregatorOwner: wallet.account,
|
aggregatorOwner: wallet.account,
|
||||||
});
|
})
|
||||||
|
|
||||||
log(`added oracle. pubkey: ${color(oracle.publicKey.toBase58(), "blue")}`)
|
log(`added oracle. pubkey: ${color(oracle.publicKey.toBase58(), "blue")}`)
|
||||||
})
|
})
|
||||||
|
@ -264,10 +304,12 @@ cli
|
||||||
.description("oracle feeds to aggregator")
|
.description("oracle feeds to aggregator")
|
||||||
.option("--feedAddress <string>", "feed address to submit values to")
|
.option("--feedAddress <string>", "feed address to submit values to")
|
||||||
.option("--oracleAddress <string>", "feed address to submit values to")
|
.option("--oracleAddress <string>", "feed address to submit values to")
|
||||||
.option("--pairSymbol <string>", "market pair to feed")
|
.option("--pairSymbol <string>", "market pair to feed")
|
||||||
.action(async (opts) => {
|
.action(async (opts) => {
|
||||||
|
const {
|
||||||
const { wallet, aggregatorProgramAccount: aggregatorProgram } = await AppContext.forOracle()
|
wallet,
|
||||||
|
aggregatorProgramAccount: aggregatorProgram,
|
||||||
|
} = await AppContext.forOracle()
|
||||||
|
|
||||||
const { feedAddress, oracleAddress, pairSymbol } = opts
|
const { feedAddress, oracleAddress, pairSymbol } = opts
|
||||||
|
|
||||||
|
@ -286,7 +328,11 @@ cli
|
||||||
.description("create test token")
|
.description("create test token")
|
||||||
.option("--amount <number>", "amount of the test token")
|
.option("--amount <number>", "amount of the test token")
|
||||||
.action(async (opts) => {
|
.action(async (opts) => {
|
||||||
const { wallet, aggregatorProgramAccount: aggregatorProgram, deployer } = await AppContext.forAdmin()
|
const {
|
||||||
|
wallet,
|
||||||
|
aggregatorProgramAccount: aggregatorProgram,
|
||||||
|
deployer,
|
||||||
|
} = await AppContext.forAdmin()
|
||||||
|
|
||||||
const { amount } = opts
|
const { amount } = opts
|
||||||
|
|
||||||
|
@ -313,7 +359,7 @@ cli
|
||||||
// 3. create token account
|
// 3. create token account
|
||||||
const tokenAccount = await spltoken.initializeAccount({
|
const tokenAccount = await spltoken.initializeAccount({
|
||||||
token: token.publicKey,
|
token: token.publicKey,
|
||||||
owner: tokenOwner.pubkey
|
owner: tokenOwner.pubkey,
|
||||||
})
|
})
|
||||||
|
|
||||||
log(`mint ${amount} token to token account...`)
|
log(`mint ${amount} token to token account...`)
|
||||||
|
@ -332,10 +378,8 @@ cli
|
||||||
address: tokenOwner.address,
|
address: tokenOwner.address,
|
||||||
seed: tokenOwner.noncedSeed.toString("hex"),
|
seed: tokenOwner.noncedSeed.toString("hex"),
|
||||||
nonce: tokenOwner.nonce,
|
nonce: tokenOwner.nonce,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
cli.parse(process.argv)
|
cli.parse(process.argv)
|
104
src/schema.ts
104
src/schema.ts
|
@ -2,6 +2,8 @@ import { PublicKey, Account } from "solray"
|
||||||
import BN from "bn.js"
|
import BN from "bn.js"
|
||||||
import { deserialize, serialize } from "borsh"
|
import { deserialize, serialize } from "borsh"
|
||||||
|
|
||||||
|
const MAX_ORACLES = 12
|
||||||
|
|
||||||
const boolMapper = {
|
const boolMapper = {
|
||||||
encode: boolToInt,
|
encode: boolToInt,
|
||||||
decode: intToBool,
|
decode: intToBool,
|
||||||
|
@ -75,23 +77,75 @@ class Submission {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Aggregator extends Serialization {
|
export class AggregatorConfig extends Serialization {
|
||||||
public submitInterval!: number
|
public decimals!: number
|
||||||
public minSubmissionValue!: BN
|
public description!: string
|
||||||
public maxSubmissionValue!: BN
|
public restartDelay!: number
|
||||||
public submissions!: Submission[]
|
public rewardAmount!: number
|
||||||
|
public maxSubmissions!: number
|
||||||
|
public minSubmissions!: number
|
||||||
|
|
||||||
public static schema = {
|
public static schema = {
|
||||||
kind: "struct",
|
kind: "struct",
|
||||||
fields: [
|
fields: [
|
||||||
["submitInterval", "u32"],
|
["description", [32], str32Mapper],
|
||||||
["minSubmissionValue", "u64"],
|
["decimals", "u8"],
|
||||||
["maxSubmissionValue", "u64"],
|
["restartDelay", "u8"],
|
||||||
["submissionDecimals", "u8"],
|
["maxSubmissions", "u8"],
|
||||||
["description", [32], str32Mapper], // fixed-sized-u8-array
|
["minSubmissions", "u8"],
|
||||||
["isInitialized", "u8", boolMapper], // no mapping for bool?
|
["rewardAmount", "u64"],
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Submissions extends Serialization {
|
||||||
|
public static size = 577
|
||||||
|
public static schema = {
|
||||||
|
kind: "struct",
|
||||||
|
fields: [
|
||||||
|
["isInitialized", "u8", boolMapper],
|
||||||
|
["submissions", [MAX_ORACLES, Submission]],
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Round extends Serialization {
|
||||||
|
public static schema = {
|
||||||
|
kind: "struct",
|
||||||
|
fields: [
|
||||||
|
["id", "u64"],
|
||||||
|
["created_at", "u64"],
|
||||||
|
["updated_at", "u64"],
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Answer extends Serialization {
|
||||||
|
public static schema = {
|
||||||
|
kind: "struct",
|
||||||
|
fields: [
|
||||||
|
["round_id", "u64"],
|
||||||
|
["created_at", "u64"],
|
||||||
|
["updated_at", "u64"],
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Aggregator extends Serialization {
|
||||||
|
public static size = 189
|
||||||
|
|
||||||
|
public config!: AggregatorConfig
|
||||||
|
// public submissions!: Submission[]
|
||||||
|
|
||||||
|
public static schema = {
|
||||||
|
kind: "struct",
|
||||||
|
fields: [
|
||||||
|
["config", AggregatorConfig],
|
||||||
|
["isInitialized", "u8", boolMapper],
|
||||||
["owner", [32], pubkeyMapper],
|
["owner", [32], pubkeyMapper],
|
||||||
["submissions", [Submission, 12]],
|
["round", Round],
|
||||||
|
["round_submissions", [32], pubkeyMapper],
|
||||||
|
["answer", Answer],
|
||||||
|
["answer_submissions", [32], pubkeyMapper],
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,22 +157,16 @@ abstract class InstructionSerialization extends Serialization {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Initialize extends InstructionSerialization {
|
export class Initialize extends InstructionSerialization {
|
||||||
public submitInterval!: number
|
// public submitInterval!: number
|
||||||
public minSubmissionValue!: number
|
// public minSubmissionValue!: number
|
||||||
public maxSubmissionValue!: number
|
// public maxSubmissionValue!: number
|
||||||
public submissionDecimals!: number
|
// public submissionDecimals!: number
|
||||||
/// A short description of what is being reported
|
// /// A short description of what is being reported
|
||||||
public description!: string
|
// public description!: string
|
||||||
|
|
||||||
public static schema = {
|
public static schema = {
|
||||||
kind: "struct",
|
kind: "struct",
|
||||||
fields: [
|
fields: [["config", AggregatorConfig]],
|
||||||
["submitInterval", "u32"],
|
|
||||||
["minSubmissionValue", "u64"],
|
|
||||||
["maxSubmissionValue", "u64"],
|
|
||||||
["submissionDecimals", "u8"],
|
|
||||||
["description", [32], str32Mapper],
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,12 +215,18 @@ function boolToInt(t: boolean) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class Oracle {
|
||||||
|
public static size = 113
|
||||||
|
}
|
||||||
|
|
||||||
// if there is optional or variable length items, what is: borsh_utils::get_packed_len::<Submission>()?
|
// if there is optional or variable length items, what is: borsh_utils::get_packed_len::<Submission>()?
|
||||||
//
|
//
|
||||||
// would panic given variable sized types
|
// would panic given variable sized types
|
||||||
|
|
||||||
export const schema = new Map([
|
export const schema = new Map([
|
||||||
[Aggregator, Aggregator.schema],
|
[Aggregator, Aggregator.schema],
|
||||||
|
[AggregatorConfig, AggregatorConfig.schema],
|
||||||
|
[Submissions, Submissions.schema],
|
||||||
[Submission, Submission.schema],
|
[Submission, Submission.schema],
|
||||||
[Initialize, Initialize.schema],
|
[Initialize, Initialize.schema],
|
||||||
[Instruction, Instruction.schema],
|
[Instruction, Instruction.schema],
|
||||||
|
|
23
src/utils.ts
23
src/utils.ts
|
@ -1,6 +1,10 @@
|
||||||
import { Connection, PublicKey } from "@solana/web3.js"
|
import { Connection, PublicKey } from "@solana/web3.js"
|
||||||
|
|
||||||
import { AggregatorLayout, SubmissionLayout, OracleLayout } from "./FluxAggregator"
|
import {
|
||||||
|
AggregatorLayout,
|
||||||
|
SubmissionLayout,
|
||||||
|
OracleLayout,
|
||||||
|
} from "./FluxAggregator"
|
||||||
|
|
||||||
import { solana, Wallet, NetworkName, Deployer } from "solray"
|
import { solana, Wallet, NetworkName, Deployer } from "solray"
|
||||||
|
|
||||||
|
@ -17,9 +21,8 @@ export function getMedian(submissions: number[]): number {
|
||||||
return values[0]
|
return values[0]
|
||||||
} else {
|
} else {
|
||||||
let i = len / 2
|
let i = len / 2
|
||||||
return len % 2 == 0 ? (values[i] + values[i-1])/2 : values[i]
|
return len % 2 == 0 ? (values[i] + values[i - 1]) / 2 : values[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sleep(ms: number): Promise<void> {
|
export function sleep(ms: number): Promise<void> {
|
||||||
|
@ -46,7 +49,10 @@ export function decodeAggregatorInfo(accountInfo) {
|
||||||
|
|
||||||
for (let i = 0; i < aggregator.submissions.length / submissionSpace; i++) {
|
for (let i = 0; i < aggregator.submissions.length / submissionSpace; i++) {
|
||||||
let submission = SubmissionLayout.decode(
|
let submission = SubmissionLayout.decode(
|
||||||
aggregator.submissions.slice(i*submissionSpace, (i+1)*submissionSpace)
|
aggregator.submissions.slice(
|
||||||
|
i * submissionSpace,
|
||||||
|
(i + 1) * submissionSpace
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
submission.oracle = new PublicKey(submission.oracle)
|
submission.oracle = new PublicKey(submission.oracle)
|
||||||
|
@ -68,8 +74,8 @@ export function decodeAggregatorInfo(accountInfo) {
|
||||||
submissionValue: getMedian(submissions),
|
submissionValue: getMedian(submissions),
|
||||||
submitInterval,
|
submitInterval,
|
||||||
description,
|
description,
|
||||||
oracles: submissions.map(s => s.oracle.toString()),
|
oracles: submissions.map((s) => s.oracle.toString()),
|
||||||
latestUpdateTime: new Date(Number(latestUpdateTime)*1000),
|
latestUpdateTime: new Date(Number(latestUpdateTime) * 1000),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +94,10 @@ export function decodeOracleInfo(accountInfo) {
|
||||||
return oracle
|
return oracle
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function walletFromEnv(key: string, conn: Connection): Promise<Wallet> {
|
export async function walletFromEnv(
|
||||||
|
key: string,
|
||||||
|
conn: Connection
|
||||||
|
): Promise<Wallet> {
|
||||||
const mnemonic = process.env[key]
|
const mnemonic = process.env[key]
|
||||||
if (!mnemonic) {
|
if (!mnemonic) {
|
||||||
throw new Error(`Set ${key} in .env to be a mnemonic`)
|
throw new Error(`Set ${key} in .env to be a mnemonic`)
|
||||||
|
|
Loading…
Reference in New Issue