Skip to content

Commit

Permalink
fix(realloc): removed payer-swap from realloc
Browse files Browse the repository at this point in the history
  • Loading branch information
ogmedia committed Jan 31, 2023
1 parent 2bdc2b9 commit fb7356f
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 88 deletions.
1 change: 1 addition & 0 deletions programs/squads-mpl/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ pub enum MsError {
MaxMembersReached,
EmptyMembers,
PartialExecution,
NotEnoughLamports,
}
64 changes: 15 additions & 49 deletions programs/squads-mpl/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
use anchor_lang::{prelude::*, solana_program::instruction::Instruction};
use anchor_lang::{
prelude::*,
solana_program::{
instruction::Instruction,
program::invoke_signed
}
};

use hex::FromHex;

use state::ms::*;
Expand Down Expand Up @@ -27,11 +34,6 @@ pub mod squads_mpl {

use std::convert::TryInto;

use anchor_lang::solana_program::{
program::{invoke, invoke_signed},
system_instruction::transfer,
};

use super::*;

// instruction to create a multisig
Expand Down Expand Up @@ -98,18 +100,7 @@ pub mod squads_mpl {
let top_up_lamports = rent_exempt_lamports
.saturating_sub(ctx.accounts.multisig.to_account_info().lamports());
if top_up_lamports > 0 {
invoke(
&transfer(
ctx.accounts.member.key,
&ctx.accounts.multisig.key(),
top_up_lamports,
),
&[
ctx.accounts.member.to_account_info().clone(),
multisig_account_info.clone(),
ctx.accounts.system_program.to_account_info().clone(),
],
)?;
return err!(MsError::NotEnoughLamports);
}
}
ctx.accounts.multisig.reload()?;
Expand Down Expand Up @@ -446,35 +437,19 @@ pub mod squads_mpl {

let ix_keys = ms_ix.keys.clone();
// create the instruction to invoke from the saved ms ix account
let mut ix: Instruction = Instruction::from(ms_ix.clone());
let ix: Instruction = Instruction::from(ms_ix.clone());
let mut ix_account_infos: Vec<AccountInfo> = Vec::<AccountInfo>::new();

// add the program account needed for the ix
ix_account_infos.push(ix_program_info.clone());

let add_member_discriminator = Vec::from_hex("0d747b827ec63922").unwrap();
let add_member_and_change_threshold_discriminator =
Vec::from_hex("72d53b2fd69d96aa").unwrap();
// loop through the provided remaining accounts
for account_index in 0..ix_keys.len() {
let ix_account_info = next_account_info(ix_iter)?.clone();

// check if this data has length of 8 or greater, and is our discriminator
if ctx.program_id == ix_program_info.key
&& (Some(add_member_discriminator.as_slice()) == ix.data.get(0..8)
|| Some(add_member_and_change_threshold_discriminator.as_slice())
== ix.data.get(0..8))
&& account_index == 1
{
// check that the ix account keys match the submitted account keys
if *ix_account_info.key != *ctx.accounts.member.key {
return err!(MsError::InvalidInstructionAccount);
}
} else {
// check that the ix account keys match the submitted account keys
if *ix_account_info.key != ix_keys[account_index].pubkey {
return err!(MsError::InvalidInstructionAccount);
}
// check that the ix account keys match the submitted account keys
if *ix_account_info.key != ix_keys[account_index].pubkey {
return err!(MsError::InvalidInstructionAccount);
}

ix_account_infos.push(ix_account_info.clone());
Expand All @@ -494,14 +469,7 @@ pub mod squads_mpl {
Some(execute_instruction.as_slice()) == ix.data.get(0..8) {
return err!(MsError::InvalidAuthorityIndex);
}
// since the add member may need to pay realloc, switch the payer
if Some(add_member_discriminator.as_slice()) == ix.data.get(0..8)
|| Some(add_member_and_change_threshold_discriminator.as_slice())
== ix.data.get(0..8)
{
ix.accounts[1] = AccountMeta::new(*ctx.accounts.member.key, true);
ix_account_infos[2] = ctx.accounts.member.to_account_info();
}

invoke_signed(&ix, &ix_account_infos, &[&ms_authority_seeds])?;
}
// if its > 1 authority, use the derived authority seeds
Expand Down Expand Up @@ -913,9 +881,7 @@ pub struct MsAuthRealloc<'info> {
signer
)]
pub multisig: Box<Account<'info, Ms>>,
// needs to sign as well to transfer lamports if needed
#[account(mut)]
pub member: Signer<'info>,

pub rent: Sysvar<'info, Rent>,
pub system_program: Program<'info, System>,
}
20 changes: 0 additions & 20 deletions sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -618,29 +618,9 @@ class Squads {
.map(({pubkey, ixItem}) => {
const ixKeys: anchor.web3.AccountMeta[] =
ixItem.keys as anchor.web3.AccountMeta[];
const addSig = anchor.utils.sha256.hash("global:add_member");
const ixDiscriminator = Buffer.from(addSig, "hex");
const addData = Buffer.concat([ixDiscriminator.slice(0, 8)]);
const addAndThreshSig = anchor.utils.sha256.hash(
"global:add_member_and_change_threshold"
);
const ixAndThreshDiscriminator = Buffer.from(addAndThreshSig, "hex");
const addAndThreshData = Buffer.concat([
ixAndThreshDiscriminator.slice(0, 8),
]);
const ixData = ixItem.data as any;

const formattedKeys = ixKeys.map((ixKey, keyInd) => {
if (
(ixData.includes(addData) || ixData.includes(addAndThreshData)) &&
keyInd === 2
) {
return {
pubkey: feePayer,
isSigner: false,
isWritable: ixKey.isWritable,
};
}
return {
pubkey: ixKey.pubkey,
isSigner: false,
Expand Down
7 changes: 0 additions & 7 deletions sdk/src/tx_builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,6 @@ export class TransactionBuilder {
.addMember(member)
.accounts({
multisig: this.multisig.publicKey,
multisigAuth: this.multisig.publicKey,
member,
})
.instruction();
return this.withInstruction(instruction);
Expand All @@ -107,8 +105,6 @@ export class TransactionBuilder {
.addMemberAndChangeThreshold(member, threshold)
.accounts({
multisig: this.multisig.publicKey,
multisigAuth: this.multisig.publicKey,
member,
})
.instruction();
return this.withInstruction(instruction);
Expand All @@ -118,7 +114,6 @@ export class TransactionBuilder {
.removeMember(member)
.accounts({
multisig: this.multisig.publicKey,
multisigAuth: this.multisig.publicKey,
})
.instruction();
return this.withInstruction(instruction);
Expand All @@ -131,7 +126,6 @@ export class TransactionBuilder {
.removeMemberAndChangeThreshold(member, threshold)
.accounts({
multisig: this.multisig.publicKey,
multisigAuth: this.multisig.publicKey,
})
.instruction();
return this.withInstruction(instruction);
Expand All @@ -141,7 +135,6 @@ export class TransactionBuilder {
.changeThreshold(threshold)
.accounts({
multisig: this.multisig.publicKey,
multisigAuth: this.multisig.publicKey,
})
.instruction();
return this.withInstruction(instruction);
Expand Down
61 changes: 49 additions & 12 deletions tests/squads-mpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import Squads, {
getProgramManagerPDA,
getAuthorityPDA,
getTxPDA,
} from "@sqds/sdk";
} from "../sdk/src/index";
import BN from "bn.js";
import { ASSOCIATED_PROGRAM_ID, TOKEN_PROGRAM_ID } from "@project-serum/anchor/dist/cjs/utils/token";
import { getExecuteProxyInstruction, getUserRolePDA, getUserDelegatePDA, getRolesManager } from "../helpers/roles";
Expand Down Expand Up @@ -122,14 +122,9 @@ describe("Programs", function(){
// test suite
describe("SMPL Basic functionality", async function(){
this.beforeAll(async function(){
console.log("Deploying programs...");
console.log("Deploying SMPL Program...");
deploySmpl();
console.log("✔ SMPL Program deployed.");
deployPm();
console.log("✔ Program Manager Program deployed.");
deployRoles();
console.log("✔ Roles Program deployed.");
console.log("Finished deploying programs.");

program = anchor.workspace.SquadsMpl as Program<SquadsMpl>;
squads = Squads.localnet(provider.wallet, {
Expand Down Expand Up @@ -389,23 +384,55 @@ describe("Programs", function(){
ixState = await squads.getInstruction(ixState.publicKey);
txState = await squads.getTransaction(txState.publicKey);

expect(ixState.executed).to.be.true;
expect(txState.executedIndex).to.equal(1);

await squads.executeInstruction(txState.publicKey, ix2State.publicKey);

ix2State = await squads.getInstruction(ix2State.publicKey);
txState = await squads.getTransaction(txState.publicKey);

expect(ix2State.executed).to.be.true;
expect(txState.executedIndex).to.equal(2);
expect(txState.status).to.have.property("executed");
});

it(`Change ms size with realloc`, async function(){
let msAccount = await squads.connection.getParsedAccountInfo(msPDA);
let msStateCheck = await squads.getMultisig(msPDA);
const startRentLamports = msAccount.value.lamports;

// get the current data size of the msAccount
const currDataSize = msAccount.value.data.length;
// get the current number of keys
const currNumKeys = msStateCheck.keys.length;
// get the number of spots left
const SIZE_WITHOUT_MEMBERS = 8 + // Anchor disriminator
2 + // threshold value
2 + // authority index
4 + // transaction index
4 + // processed internal transaction index
1 + // PDA bump
32 + // creator
1 + // allow external execute
4; // for vec length

const spotsLeft = ((currDataSize - SIZE_WITHOUT_MEMBERS) / 32) - currNumKeys;

// if there is less than 1 spot left, calculate rent needed for realloc of 10 more keys
if(spotsLeft < 1){
console.log(" MS needs more space")
// add space for 10 more keys
const neededLen = currDataSize + (10 * 32);
// rent exempt lamports
const rentExemptLamports = await squads.connection.getMinimumBalanceForRentExemption(neededLen);
// top up lamports
const topUpLamports = rentExemptLamports - msAccount.value.lamports;
if(topUpLamports > 0){
console.log(" MS needs more lamports, topping up ", topUpLamports);
const topUpTx = await createBlankTransaction(squads.connection, creator.publicKey);
const topUpIx = await createTestTransferTransaction(creator.publicKey, msPDA, topUpLamports);
topUpTx.add(topUpIx);
await provider.sendAndConfirm(topUpTx);
}
}
// 1 get the instruction to create a transction
// 2 get the instruction to add a member
// 3 get the instruction to 'activate' the tx
Expand Down Expand Up @@ -685,7 +712,13 @@ describe("Programs", function(){
});
});

describe.skip("Program upgrades", () => {
describe.skip("Program upgrades", function (){
this.beforeAll(async function(){
console.log('Deploying Program Manager Program');
deployPm();
console.log("✔ Program Manager Program deployed.");
});

it(`Create a program manager`, async function(){
const newProgramManager = await squads.createProgramManager(msPDA);
expect(newProgramManager.multisig.toBase58()).to.equal(msPDA.toBase58());
Expand Down Expand Up @@ -922,7 +955,7 @@ describe("Programs", function(){
});

// test suite for the roles program
describe("Roles Program", async function(){
describe.skip("Roles Program", async function(){
const userWithInitRole = anchor.web3.Keypair.generate();
const userWithVoteRole = anchor.web3.Keypair.generate();
const userWithExecuteRole = anchor.web3.Keypair.generate();
Expand All @@ -936,6 +969,10 @@ describe("Programs", function(){
let userWithExecuteRoleDelegatePDA;

this.beforeAll(async function(){
console.log("Deploying Roles Program...");
deployRoles();
console.log("✔ Roles Program deployed.");

rolesProgram = anchor.workspace.Roles as Program<Roles>;
[rolesManager] = await getRolesManager(msPDA, rolesProgram.programId);

Expand Down

0 comments on commit fb7356f

Please sign in to comment.