associate an aggregator with a particular reward faucet

This commit is contained in:
De Facto 2021-02-18 16:37:23 +08:00
parent 8cb797235b
commit 89d4d6fbd3
5 changed files with 78 additions and 18 deletions

View File

@ -40,6 +40,9 @@ pub enum Error {
#[error("No submitted value")]
NoSubmission,
#[error("Invalid faucet")]
InvalidFaucet,
#[error("Unknown error")]
UnknownError,
}

View File

@ -6,17 +6,8 @@ use crate::{
state::{Aggregator, AggregatorConfig, Authority, Oracle, Round, Submissions},
};
use solana_program::{
msg,
account_info::AccountInfo,
clock::Clock,
entrypoint::ProgramResult,
program::invoke_signed,
program_error::ProgramError,
program_pack::IsInitialized,
pubkey::Pubkey,
sysvar::{rent::Rent, Sysvar},
};
// use spl_token::state;
use solana_program::{account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, program::invoke_signed, program_error::ProgramError, program_pack::{IsInitialized, Pack}, pubkey::Pubkey, sysvar::{rent::Rent, Sysvar}};
use crate::borsh_state::{BorshState, InitBorshState};
@ -300,6 +291,7 @@ impl<'a> SubmitContext<'a> {
// Withdraw token from reward faucet to receiver account, deducting oracle's withdrawable credit.
struct WithdrawContext<'a, 'b> {
token_program: &'a AccountInfo<'a>,
aggregator: &'a AccountInfo<'a>,
faucet: &'a AccountInfo<'a>,
faucet_owner: &'a AccountInfo<'a>, // program signed
oracle: &'a AccountInfo<'a>,
@ -311,8 +303,14 @@ struct WithdrawContext<'a, 'b> {
impl<'a, 'b> WithdrawContext<'a, 'b> {
fn process(&self) -> ProgramResult {
let aggregator = Aggregator::load_initialized(self.aggregator)?;
let mut oracle = Oracle::load_initialized(self.oracle)?;
oracle.authorize(&self.oracle_owner)?;
oracle.check_aggregator(self.aggregator)?;
if !aggregator.config.reward_token_account.is_account(self.faucet) {
return Err(Error::InvalidFaucet)?
}
if oracle.withdrawable == 0 {
return Err(Error::InsufficientWithdrawable)?;
@ -323,6 +321,7 @@ impl<'a, 'b> WithdrawContext<'a, 'b> {
oracle.withdrawable = 0;
oracle.save(self.oracle)?;
// The SPL Token ensures that faucet and receiver are the same type of token
let inx = spl_token::instruction::transfer(
self.token_program.key,
self.faucet.key,
@ -407,11 +406,12 @@ impl Processor {
// _ => Err(ProgramError::InvalidInstructionData),
Instruction::Withdraw { faucet_owner_seed } => WithdrawContext {
token_program: accounts.get(0)?,
faucet: accounts.get(1)?,
faucet_owner: accounts.get(2)?,
oracle: accounts.get(3)?,
oracle_owner: accounts.get(4)?,
receiver: accounts.get(5)?,
aggregator: accounts.get(1)?,
faucet: accounts.get(2)?,
faucet_owner: accounts.get(3)?,
oracle: accounts.get(4)?,
oracle_owner: accounts.get(5)?,
receiver: accounts.get(6)?,
faucet_owner_seed: &faucet_owner_seed[..],
}

View File

@ -15,6 +15,12 @@ use solana_program::{
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, Default, PartialEq)]
pub struct PublicKey(pub [u8; 32]);
impl PublicKey {
pub fn is_account(&self, info: &AccountInfo) -> bool {
self.eq(&PublicKey(info.key.to_bytes()))
}
}
impl<'a> From<&'a AccountInfo<'a>> for PublicKey {
fn from(info: &'a AccountInfo<'a>) -> Self {
PublicKey(info.key.to_bytes())
@ -56,6 +62,9 @@ pub struct AggregatorConfig {
/// amount of tokens oracles are reward per submission
pub reward_amount: u64,
/// SPL token account from which to withdraw rewards
pub reward_token_account: PublicKey,
}
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, Default, PartialEq)]
@ -210,6 +219,17 @@ pub struct Oracle {
/// owner
pub owner: PublicKey,
}
impl Oracle {
pub fn check_aggregator(&self, account: &AccountInfo) -> ProgramResult {
if !self.aggregator.is_account(account) {
return Err(Error::AggregatorMismatch)?;
}
Ok(())
}
}
impl Authority for Oracle {
fn authority(&self) -> &PublicKey {
&self.owner

View File

@ -111,6 +111,7 @@ export class AggregatorConfig extends Serialization {
public rewardAmount!: number
public maxSubmissions!: number
public minSubmissions!: number
public rewardTokenAccount!: PublicKey
public static schema = {
kind: "struct",
@ -121,6 +122,7 @@ export class AggregatorConfig extends Serialization {
["maxSubmissions", "u8"],
["minSubmissions", "u8"],
["rewardAmount", "u64"],
["rewardTokenAccount", [32], pubkeyMapper],
],
}
}
@ -190,7 +192,7 @@ class Answer extends Serialization {
}
export class Aggregator extends Serialization {
public static size = 197
public static size = 229
public config!: AggregatorConfig
public roundSubmissions!: PublicKey

37
test.ts
View File

@ -3,7 +3,7 @@ dotenv.config()
import BN from "bn.js"
import { BPFLoader, Wallet } from "solray"
import { BPFLoader, ProgramAccount, SPLToken, Wallet } from "solray"
import { AppContext, conn, network } from "./src/context"
import fs from "fs"
@ -42,6 +42,40 @@ async function main() {
}
)
const spltoken = new SPLToken(adminWallet)
const rewardToken = await deployer.ensure("create reward token", async () => {
return spltoken.initializeMint({
mintAuthority: adminWallet.pubkey,
decimals: 8,
})
})
const rewardTokenOwner = await ProgramAccount.forSeed(
Buffer.from("solink"),
aggregatorProgram.publicKey
)
const rewardTokenAccount = await deployer.ensure(
"initialize reward token account",
async () => {
const vault = await spltoken.initializeAccount({
token: rewardToken.publicKey,
owner: rewardTokenOwner.pubkey,
})
await spltoken.mintTo({
token: rewardToken.publicKey,
to: vault.publicKey,
amount: BigInt(1e6 * 1e8), // 1M
authority: adminWallet.pubkey,
})
return vault
}
)
console.log(await spltoken.mintInfo(rewardToken.publicKey))
const program = new FluxAggregator(adminWallet, aggregatorProgram.publicKey)
let aggregator = await deployer.ensure(
@ -56,6 +90,7 @@ async function main() {
maxSubmissions: 3,
restartDelay: 0,
rewardAmount: BigInt(10),
rewardTokenAccount: rewardTokenAccount.publicKey,
}),
owner: adminWallet.account,
})