Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/deploy-programs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ jobs:
upload_idl: true
verify: true
use-squads: true
features: "production"
priority-fee: ${{ inputs.priority-fee }}
secrets:
MAINNET_SOLANA_DEPLOY_URL: ${{ secrets.MAINNET_SOLANA_DEPLOY_URL }}
Expand Down
2 changes: 2 additions & 0 deletions programs/price_based_performance_package/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ pub enum PriceBasedPerformancePackageError {
TrancheTokenAmountZero,
#[msg("TWAP length must be greater than or equal to 1 day and less than 1 year")]
InvalidTwapLength,
#[msg("Invalid admin")]
InvalidAdmin,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use anchor_lang::prelude::*;
use anchor_spl::token::{self, Burn, Mint, Token, TokenAccount};

use super::*;

pub mod admin {
use anchor_lang::prelude::declare_id;

// MetaDAO multisig
declare_id!("6awyHMshBGVjJ3ozdSJdyyDE1CTAXUwrpNMaRGMsb4sf");
}

#[derive(Accounts)]
#[event_cpi]
pub struct BurnPerformancePackage<'info> {
#[account(
mut,
close = spill_account,
has_one = token_mint,
has_one = performance_package_token_vault
)]
pub performance_package: Box<Account<'info, PerformancePackage>>,

#[account(
mut,
associated_token::mint = token_mint,
associated_token::authority = performance_package
)]
pub performance_package_token_vault: Box<Account<'info, TokenAccount>>,

#[account(mut)]
pub admin: Signer<'info>,

/// CHECK: SOL from account closures go to this account
#[account(mut)]
pub spill_account: UncheckedAccount<'info>,

#[account(mut, address = performance_package.token_mint)]
pub token_mint: Account<'info, Mint>,

pub token_program: Program<'info, Token>,
}

impl BurnPerformancePackage<'_> {
pub fn validate(&self) -> Result<()> {
#[cfg(feature = "production")]
require_keys_eq!(
self.admin.key(),
admin::ID,
PriceBasedPerformancePackageError::InvalidAdmin
);

Ok(())
}

pub fn handle(ctx: Context<Self>) -> Result<()> {
let performance_package = &ctx.accounts.performance_package;

let seeds = &[
b"performance_package",
performance_package.create_key.as_ref(),
&[performance_package.pda_bump],
];
let signer = &[&seeds[..]];

// Burn any remaining tokens in the performance package token vault
if ctx.accounts.performance_package_token_vault.amount > 0 {
token::burn(
CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
Burn {
mint: ctx.accounts.token_mint.to_account_info(),
from: ctx
.accounts
.performance_package_token_vault
.to_account_info(),
authority: performance_package.to_account_info(),
},
signer,
),
ctx.accounts.performance_package_token_vault.amount,
)?;
}

// Performance package account gets closed using close constraint

Ok(())
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use super::*;

pub mod burn_performance_package;
pub mod change_performance_package_authority;
pub mod complete_unlock;
pub mod execute_change;
pub mod initialize_performance_package;
pub mod propose_change;
pub mod start_unlock;

pub use burn_performance_package::*;
pub use change_performance_package_authority::*;
pub use complete_unlock::*;
pub use execute_change::*;
Expand Down
5 changes: 5 additions & 0 deletions programs/price_based_performance_package/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,9 @@ pub mod price_based_performance_package {
) -> Result<()> {
ChangePerformancePackageAuthority::handle(ctx, params)
}

#[access_control(ctx.accounts.validate())]
pub fn burn_performance_package(ctx: Context<BurnPerformancePackage>) -> Result<()> {
BurnPerformancePackage::handle(ctx)
}
}
87 changes: 87 additions & 0 deletions scripts/v0.6/burnPerformancePackage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import * as anchor from "@coral-xyz/anchor";
import * as multisig from "@sqds/multisig";
import {
PRICE_BASED_PERFORMANCE_PACKAGE_PROGRAM_ID,
PriceBasedPerformancePackageClient,
METADAO_MULTISIG_VAULT,
} from "@metadaoproject/futarchy/v0.7";
import { PublicKey, TransactionMessage } from "@solana/web3.js";

// Set the performance package address before running the script
const performancePackage = new PublicKey("");

const provider = anchor.AnchorProvider.env();

// Payer MUST be a signer with permissions to propose transactions on Metadao DAO's multisig
const payer = provider.wallet["payer"];

const priceBasedPerformancePackage: PriceBasedPerformancePackageClient =
new PriceBasedPerformancePackageClient(
provider,
PRICE_BASED_PERFORMANCE_PACKAGE_PROGRAM_ID,
);

// We need both the multisig and vault addresses for the Metadao DAO
const metadaoSquadsMultisig = new PublicKey(
"8N3Tvc6B1wEVKVC6iD4s6eyaCNqX2ovj2xze2q3Q9DWH",
);
const metadaoSquadsMultisigVault = METADAO_MULTISIG_VAULT;

// This should only be run once per DAO/AMM
// It's meant to be a one-off operation that reduces liquidity to a target K (the inital pool's liquidity) and collect it as "fees"
// We're using this because we didn't track LP fee collection in the pool state, nor did we exclude those fees from liquidity
export const burnPerformancePackage = async () => {
const performancePackageAccount =
await priceBasedPerformancePackage.getPerformancePackage(
performancePackage,
);

// We call the collect fees instruction from Metadao DAO's multisig account
// It's the only one that can call the collect fees instruction
const metaDaoSquadsMultisigAccount =
await multisig.accounts.Multisig.fromAccountAddress(
anchor.getProvider().connection,
metadaoSquadsMultisig,
);

// Prepare transaction message
const burnPerformancePackageIx =
await priceBasedPerformancePackage.program.methods
.burnPerformancePackage()
.accounts({
performancePackage,
performancePackageTokenVault:
performancePackageAccount.performancePackageTokenVault,
tokenMint: performancePackageAccount.tokenMint,
admin: metadaoSquadsMultisigVault,
spillAccount: payer.publicKey,
})
.instruction();

const transactionMessage = new TransactionMessage({
instructions: [burnPerformancePackageIx],
payerKey: metadaoSquadsMultisigVault,
recentBlockhash: (await provider.connection.getLatestBlockhash()).blockhash,
});

// Create vault transaction
const vaultTxCreateSignature = await multisig.rpc.vaultTransactionCreate({
connection: anchor.getProvider().connection,
creator: payer.publicKey,
feePayer: payer.publicKey,
ephemeralSigners: 0,
multisigPda: metadaoSquadsMultisig,
transactionIndex:
BigInt(metaDaoSquadsMultisigAccount.transactionIndex.toString()) + 1n,
vaultIndex: 0,
transactionMessage,
});

console.log(
"Vault burn performance package transaction create signature:",
vaultTxCreateSignature,
);
console.log("Go ahead and execute the transaction through Squads.");
};

burnPerformancePackage().catch(console.error);
87 changes: 87 additions & 0 deletions scripts/v0.7/burnPerformancePackage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import * as anchor from "@coral-xyz/anchor";
import * as multisig from "@sqds/multisig";
import {
PRICE_BASED_PERFORMANCE_PACKAGE_PROGRAM_ID,
PriceBasedPerformancePackageClient,
METADAO_MULTISIG_VAULT,
} from "@metadaoproject/futarchy/v0.7";
import { PublicKey, TransactionMessage } from "@solana/web3.js";

// Set the performance package address before running the script
const performancePackage = new PublicKey("");

const provider = anchor.AnchorProvider.env();

// Payer MUST be a signer with permissions to propose transactions on Metadao DAO's multisig
const payer = provider.wallet["payer"];

const priceBasedPerformancePackage: PriceBasedPerformancePackageClient =
new PriceBasedPerformancePackageClient(
provider,
PRICE_BASED_PERFORMANCE_PACKAGE_PROGRAM_ID,
);

// We need both the multisig and vault addresses for the Metadao DAO
const metadaoSquadsMultisig = new PublicKey(
"8N3Tvc6B1wEVKVC6iD4s6eyaCNqX2ovj2xze2q3Q9DWH",
);
const metadaoSquadsMultisigVault = METADAO_MULTISIG_VAULT;

// This should only be run once per DAO/AMM
// It's meant to be a one-off operation that reduces liquidity to a target K (the inital pool's liquidity) and collect it as "fees"
// We're using this because we didn't track LP fee collection in the pool state, nor did we exclude those fees from liquidity
export const burnPerformancePackage = async () => {
const performancePackageAccount =
await priceBasedPerformancePackage.getPerformancePackage(
performancePackage,
);

// We call the collect fees instruction from Metadao DAO's multisig account
// It's the only one that can call the collect fees instruction
const metaDaoSquadsMultisigAccount =
await multisig.accounts.Multisig.fromAccountAddress(
anchor.getProvider().connection,
metadaoSquadsMultisig,
);

// Prepare transaction message
const burnPerformancePackageIx =
await priceBasedPerformancePackage.program.methods
.burnPerformancePackage()
.accounts({
performancePackage,
performancePackageTokenVault:
performancePackageAccount.performancePackageTokenVault,
tokenMint: performancePackageAccount.tokenMint,
admin: metadaoSquadsMultisigVault,
spillAccount: payer.publicKey,
})
.instruction();

const transactionMessage = new TransactionMessage({
instructions: [burnPerformancePackageIx],
payerKey: metadaoSquadsMultisigVault,
recentBlockhash: (await provider.connection.getLatestBlockhash()).blockhash,
});

// Create vault transaction
const vaultTxCreateSignature = await multisig.rpc.vaultTransactionCreate({
connection: anchor.getProvider().connection,
creator: payer.publicKey,
feePayer: payer.publicKey,
ephemeralSigners: 0,
multisigPda: metadaoSquadsMultisig,
transactionIndex:
BigInt(metaDaoSquadsMultisigAccount.transactionIndex.toString()) + 1n,
vaultIndex: 0,
transactionMessage,
});

console.log(
"Vault burn performance package transaction create signature:",
vaultTxCreateSignature,
);
console.log("Go ahead and execute the transaction through Squads.");
};

burnPerformancePackage().catch(console.error);
Loading
Loading