associate an aggregator with a particular reward faucet
This commit is contained in:
parent
8cb797235b
commit
89d4d6fbd3
|
@ -40,6 +40,9 @@ pub enum Error {
|
||||||
#[error("No submitted value")]
|
#[error("No submitted value")]
|
||||||
NoSubmission,
|
NoSubmission,
|
||||||
|
|
||||||
|
#[error("Invalid faucet")]
|
||||||
|
InvalidFaucet,
|
||||||
|
|
||||||
#[error("Unknown error")]
|
#[error("Unknown error")]
|
||||||
UnknownError,
|
UnknownError,
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,17 +6,8 @@ use crate::{
|
||||||
state::{Aggregator, AggregatorConfig, Authority, Oracle, Round, Submissions},
|
state::{Aggregator, AggregatorConfig, Authority, Oracle, Round, Submissions},
|
||||||
};
|
};
|
||||||
|
|
||||||
use solana_program::{
|
// use spl_token::state;
|
||||||
msg,
|
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}};
|
||||||
account_info::AccountInfo,
|
|
||||||
clock::Clock,
|
|
||||||
entrypoint::ProgramResult,
|
|
||||||
program::invoke_signed,
|
|
||||||
program_error::ProgramError,
|
|
||||||
program_pack::IsInitialized,
|
|
||||||
pubkey::Pubkey,
|
|
||||||
sysvar::{rent::Rent, Sysvar},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::borsh_state::{BorshState, InitBorshState};
|
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.
|
// Withdraw token from reward faucet to receiver account, deducting oracle's withdrawable credit.
|
||||||
struct WithdrawContext<'a, 'b> {
|
struct WithdrawContext<'a, 'b> {
|
||||||
token_program: &'a AccountInfo<'a>,
|
token_program: &'a AccountInfo<'a>,
|
||||||
|
aggregator: &'a AccountInfo<'a>,
|
||||||
faucet: &'a AccountInfo<'a>,
|
faucet: &'a AccountInfo<'a>,
|
||||||
faucet_owner: &'a AccountInfo<'a>, // program signed
|
faucet_owner: &'a AccountInfo<'a>, // program signed
|
||||||
oracle: &'a AccountInfo<'a>,
|
oracle: &'a AccountInfo<'a>,
|
||||||
|
@ -311,8 +303,14 @@ struct WithdrawContext<'a, 'b> {
|
||||||
|
|
||||||
impl<'a, 'b> WithdrawContext<'a, 'b> {
|
impl<'a, 'b> WithdrawContext<'a, 'b> {
|
||||||
fn process(&self) -> ProgramResult {
|
fn process(&self) -> ProgramResult {
|
||||||
|
let aggregator = Aggregator::load_initialized(self.aggregator)?;
|
||||||
let mut oracle = Oracle::load_initialized(self.oracle)?;
|
let mut oracle = Oracle::load_initialized(self.oracle)?;
|
||||||
oracle.authorize(&self.oracle_owner)?;
|
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 {
|
if oracle.withdrawable == 0 {
|
||||||
return Err(Error::InsufficientWithdrawable)?;
|
return Err(Error::InsufficientWithdrawable)?;
|
||||||
|
@ -323,6 +321,7 @@ impl<'a, 'b> WithdrawContext<'a, 'b> {
|
||||||
oracle.withdrawable = 0;
|
oracle.withdrawable = 0;
|
||||||
oracle.save(self.oracle)?;
|
oracle.save(self.oracle)?;
|
||||||
|
|
||||||
|
// The SPL Token ensures that faucet and receiver are the same type of token
|
||||||
let inx = spl_token::instruction::transfer(
|
let inx = spl_token::instruction::transfer(
|
||||||
self.token_program.key,
|
self.token_program.key,
|
||||||
self.faucet.key,
|
self.faucet.key,
|
||||||
|
@ -407,11 +406,12 @@ impl Processor {
|
||||||
// _ => Err(ProgramError::InvalidInstructionData),
|
// _ => Err(ProgramError::InvalidInstructionData),
|
||||||
Instruction::Withdraw { faucet_owner_seed } => WithdrawContext {
|
Instruction::Withdraw { faucet_owner_seed } => WithdrawContext {
|
||||||
token_program: accounts.get(0)?,
|
token_program: accounts.get(0)?,
|
||||||
faucet: accounts.get(1)?,
|
aggregator: accounts.get(1)?,
|
||||||
faucet_owner: accounts.get(2)?,
|
faucet: accounts.get(2)?,
|
||||||
oracle: accounts.get(3)?,
|
faucet_owner: accounts.get(3)?,
|
||||||
oracle_owner: accounts.get(4)?,
|
oracle: accounts.get(4)?,
|
||||||
receiver: accounts.get(5)?,
|
oracle_owner: accounts.get(5)?,
|
||||||
|
receiver: accounts.get(6)?,
|
||||||
|
|
||||||
faucet_owner_seed: &faucet_owner_seed[..],
|
faucet_owner_seed: &faucet_owner_seed[..],
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,12 @@ use solana_program::{
|
||||||
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, Default, PartialEq)]
|
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, Default, PartialEq)]
|
||||||
pub struct PublicKey(pub [u8; 32]);
|
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 {
|
impl<'a> From<&'a AccountInfo<'a>> for PublicKey {
|
||||||
fn from(info: &'a AccountInfo<'a>) -> Self {
|
fn from(info: &'a AccountInfo<'a>) -> Self {
|
||||||
PublicKey(info.key.to_bytes())
|
PublicKey(info.key.to_bytes())
|
||||||
|
@ -56,6 +62,9 @@ pub struct AggregatorConfig {
|
||||||
|
|
||||||
/// amount of tokens oracles are reward per submission
|
/// amount of tokens oracles are reward per submission
|
||||||
pub reward_amount: u64,
|
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)]
|
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, Default, PartialEq)]
|
||||||
|
@ -210,6 +219,17 @@ pub struct Oracle {
|
||||||
/// owner
|
/// owner
|
||||||
pub owner: PublicKey,
|
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 {
|
impl Authority for Oracle {
|
||||||
fn authority(&self) -> &PublicKey {
|
fn authority(&self) -> &PublicKey {
|
||||||
&self.owner
|
&self.owner
|
||||||
|
|
|
@ -111,6 +111,7 @@ export class AggregatorConfig extends Serialization {
|
||||||
public rewardAmount!: number
|
public rewardAmount!: number
|
||||||
public maxSubmissions!: number
|
public maxSubmissions!: number
|
||||||
public minSubmissions!: number
|
public minSubmissions!: number
|
||||||
|
public rewardTokenAccount!: PublicKey
|
||||||
|
|
||||||
public static schema = {
|
public static schema = {
|
||||||
kind: "struct",
|
kind: "struct",
|
||||||
|
@ -121,6 +122,7 @@ export class AggregatorConfig extends Serialization {
|
||||||
["maxSubmissions", "u8"],
|
["maxSubmissions", "u8"],
|
||||||
["minSubmissions", "u8"],
|
["minSubmissions", "u8"],
|
||||||
["rewardAmount", "u64"],
|
["rewardAmount", "u64"],
|
||||||
|
["rewardTokenAccount", [32], pubkeyMapper],
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,7 +192,7 @@ class Answer extends Serialization {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Aggregator extends Serialization {
|
export class Aggregator extends Serialization {
|
||||||
public static size = 197
|
public static size = 229
|
||||||
|
|
||||||
public config!: AggregatorConfig
|
public config!: AggregatorConfig
|
||||||
public roundSubmissions!: PublicKey
|
public roundSubmissions!: PublicKey
|
||||||
|
|
37
test.ts
37
test.ts
|
@ -3,7 +3,7 @@ dotenv.config()
|
||||||
|
|
||||||
import BN from "bn.js"
|
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 { AppContext, conn, network } from "./src/context"
|
||||||
|
|
||||||
import fs from "fs"
|
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)
|
const program = new FluxAggregator(adminWallet, aggregatorProgram.publicKey)
|
||||||
|
|
||||||
let aggregator = await deployer.ensure(
|
let aggregator = await deployer.ensure(
|
||||||
|
@ -56,6 +90,7 @@ async function main() {
|
||||||
maxSubmissions: 3,
|
maxSubmissions: 3,
|
||||||
restartDelay: 0,
|
restartDelay: 0,
|
||||||
rewardAmount: BigInt(10),
|
rewardAmount: BigInt(10),
|
||||||
|
rewardTokenAccount: rewardTokenAccount.publicKey,
|
||||||
}),
|
}),
|
||||||
owner: adminWallet.account,
|
owner: adminWallet.account,
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue