Skip to content

Commit

Permalink
add ProposalConfigV0.authority and update_proposal_config_v0 instruction
Browse files Browse the repository at this point in the history
  • Loading branch information
solanamonk committed Jun 10, 2024
1 parent 092d6e5 commit 0ccbe2e
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 2 deletions.
5 changes: 4 additions & 1 deletion packages/proposal-sdk/src/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ export const proposalResolvers: anchor.CustomAccountResolver<any> = combineResol
args[0].seed,
programId
)[0];
} else if (path[path.length - 1] == "owner") {
} else if (
path[path.length - 1] == "owner" ||
path[path.length - 1] == "authority"
) {
if ((provider as anchor.AnchorProvider).wallet) {
return (provider as anchor.AnchorProvider).wallet.publicKey;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub struct InitializeProposalConfigArgsV0 {
/// Optional program that will be called with `on_vote_v0` after every vote
/// Defaults to the owner of `resolution_settings`, which allows it to reactively call resolve_v0
pub on_vote_hook: Pubkey,
pub authority: Pubkey,
}

#[derive(Accounts)]
Expand Down Expand Up @@ -44,6 +45,7 @@ pub fn handler(
state_controller: args.state_controller,
on_vote_hook: args.on_vote_hook,
bump_seed: ctx.bumps["proposal_config"],
authority: args.authority,
});
Ok(())
}
2 changes: 2 additions & 0 deletions programs/proposal/src/instructions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
pub mod initialize_proposal_config_v0;
pub mod initialize_proposal_v0;
pub mod update_proposal_config_v0;
pub mod update_state_v0;
pub mod vote_v0;

pub use initialize_proposal_config_v0::*;
pub use initialize_proposal_v0::*;
pub use update_proposal_config_v0::*;
pub use update_state_v0::*;
pub use vote_v0::*;
42 changes: 42 additions & 0 deletions programs/proposal/src/instructions/update_proposal_config_v0.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use crate::state::*;
use anchor_lang::prelude::*;

#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
pub struct UpdateProposalConfigArgsV0 {
pub vote_controller: Option<Pubkey>,
pub state_controller: Option<Pubkey>,
pub on_vote_hook: Option<Pubkey>,
pub authority: Option<Pubkey>,
}

#[derive(Accounts)]
pub struct UpdateProposalConfigV0<'info> {
#[account(mut)]
pub payer: Signer<'info>,
#[account(
mut,
has_one = authority,
)]
pub proposal_config: Box<Account<'info, ProposalConfigV0>>,
pub authority: Signer<'info>,
}

pub fn handler(
ctx: Context<UpdateProposalConfigV0>,
args: UpdateProposalConfigArgsV0,
) -> Result<()> {
if let Some(vote_controller) = args.vote_controller {
ctx.accounts.proposal_config.vote_controller = vote_controller;
}
if let Some(state_controller) = args.state_controller {
ctx.accounts.proposal_config.state_controller = state_controller;
}
if let Some(on_vote_hook) = args.on_vote_hook {
ctx.accounts.proposal_config.on_vote_hook = on_vote_hook;
}
if let Some(authority) = args.authority {
ctx.accounts.proposal_config.authority = authority;
}

Ok(())
}
7 changes: 7 additions & 0 deletions programs/proposal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ pub mod proposal {
pub fn update_state_v0(ctx: Context<UpdateStateV0>, args: UpdateStateArgsV0) -> Result<()> {
update_state_v0::handler(ctx, args)
}

pub fn update_proposal_config_v0(
ctx: Context<UpdateProposalConfigV0>,
args: UpdateProposalConfigArgsV0,
) -> Result<()> {
update_proposal_config_v0::handler(ctx, args)
}
}

#[derive(Accounts)]
Expand Down
1 change: 1 addition & 0 deletions programs/proposal/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ pub struct ProposalConfigV0 {
#[max_len(32)]
pub name: String,
pub bump_seed: u8,
pub authority: Pubkey,
}

#[account]
Expand Down
129 changes: 128 additions & 1 deletion tests/proposal.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { Proposal } from "../target/types/proposal";
import { PublicKey } from "@solana/web3.js";
import { Keypair, PublicKey } from "@solana/web3.js";
import { PROGRAM_ID, init } from "@helium/proposal-sdk";
import { expect } from "chai";
import { makeid } from "./utils";
Expand All @@ -20,6 +20,32 @@ describe("proposal", () => {
});

it("Creates a proposal config", async () => {
const voteController = Keypair.generate().publicKey;
const stateController = Keypair.generate().publicKey;
const onVoteHook = Keypair.generate().publicKey;

const {
pubkeys: { proposalConfig },
} = await program.methods
.initializeProposalConfigV0({
name,
voteController,
stateController,
onVoteHook,
})
.rpcAndKeys();

const acct = await program.account.proposalConfigV0.fetch(proposalConfig!);

expect(acct.voteController.toBase58()).to.eq(voteController.toBase58());
expect(acct.stateController.toBase58()).to.eq(stateController.toBase58());
expect(acct.onVoteHook.toBase58()).to.eq(onVoteHook.toBase58());
expect(acct.authority.toBase58()).to.eq(PublicKey.default.toBase58());
});

it("Creates a proposal config with authority", async () => {
const authority = Keypair.generate().publicKey;

const {
pubkeys: { proposalConfig },
} = await program.methods
Expand All @@ -28,6 +54,7 @@ describe("proposal", () => {
voteController: me,
stateController: me,
onVoteHook: PublicKey.default,
authority,
})
.rpcAndKeys();

Expand All @@ -36,6 +63,7 @@ describe("proposal", () => {
expect(acct.voteController.toBase58()).to.eq(me.toBase58());
expect(acct.stateController.toBase58()).to.eq(me.toBase58());
expect(acct.onVoteHook.toBase58()).to.eq(PublicKey.default.toBase58());
expect(acct.authority.toBase58()).to.eq(authority.toBase58());
});

describe("with proposal config", () => {
Expand All @@ -49,10 +77,109 @@ describe("proposal", () => {
voteController: me,
stateController: me,
onVoteHook: PublicKey.default,
authority: me,
})
.rpcAndKeys());
});

it("Updates a proposal config", async () => {
const voteController = Keypair.generate().publicKey;
const stateController = Keypair.generate().publicKey;
const onVoteHook = Keypair.generate().publicKey;
const authority = Keypair.generate().publicKey;

await program.methods
.updateProposalConfigV0({
voteController,
stateController,
onVoteHook,
authority,
})
.accounts({ proposalConfig })
.rpc();

const acct = await program.account.proposalConfigV0.fetch(
proposalConfig!
);

expect(acct.voteController.toBase58()).to.eq(voteController.toBase58());
expect(acct.stateController.toBase58()).to.eq(stateController.toBase58());
expect(acct.onVoteHook.toBase58()).to.eq(onVoteHook.toBase58());
expect(acct.authority.toBase58()).to.eq(authority.toBase58());
});

it("Updates an arbitrary prop in a proposal config", async () => {
const props = [
"voteController",
"stateController",
"onVoteHook",
"authority",
];
const prop = props[Math.floor(Math.random() * props.length)];

const pubkey = Keypair.generate().publicKey;
const args = Object.fromEntries(
props.map((p) => [p, p === prop ? pubkey : null])
);

await program.methods
.updateProposalConfigV0(args as any)
.accounts({ proposalConfig })
.rpc();

const acct = await program.account.proposalConfigV0.fetch(
proposalConfig!
);

for (const p of props) {
if (p === prop) {
expect(acct[p].toBase58()).to.eq(pubkey.toBase58());
} else {
if (p === "onVoteHook") {
expect(acct[p].toBase58()).to.eq(PublicKey.default.toBase58());
} else {
expect(acct[p].toBase58()).to.eq(me.toBase58());
}
}
}
});

it("Fails to update a proposal config with a wrong authority", async () => {
let logs: string | undefined;

const authority = Keypair.generate().publicKey;

await program.methods
.updateProposalConfigV0({
voteController: null,
stateController: null,
onVoteHook: null,
authority,
})
.accounts({ proposalConfig })
.rpc();

try {
await program.methods
.updateProposalConfigV0({
voteController: me,
stateController: me,
onVoteHook: PublicKey.default,
authority: me,
})
.accounts({
proposalConfig,
})
.simulate();
} catch (err) {
logs = err.simulationResponse?.logs;
}

expect(logs).to.match(
/caused by account: proposal_config\..*ConstraintHasOne/
);
});

it("Creates a proposal", async () => {
const {
pubkeys: { proposal },
Expand Down

0 comments on commit 0ccbe2e

Please sign in to comment.