Skip to content

Commit

Permalink
feat(#629): Add configurable destination for lazy distributor
Browse files Browse the repository at this point in the history
  • Loading branch information
ChewingGlass committed Apr 11, 2024
1 parent c1704f4 commit 7490d2a
Show file tree
Hide file tree
Showing 20 changed files with 445 additions and 30 deletions.
1 change: 1 addition & 0 deletions packages/distributor-oracle/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ export class OracleServer {
(decoded.name !== "setCurrentRewardsV0" &&
decoded.name !== "distributeRewardsV0" &&
decoded.name !== "distributeCompressionRewardsV0" &&
decoded.name !== "distributeCustom" &&
decoded.name !== "initializeRecipientV0" &&
decoded.name !== "initializeCompressionRecipientV0" &&
decoded.name !== "setCurrentRewardsWrapperV0" &&
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Idl, Program } from "@coral-xyz/anchor";
import { LazyDistributor } from "@helium/idls/lib/types/lazy_distributor";
import { Asset, AssetProof, proofArgsAndAccounts } from "@helium/spl-utils";
import { PublicKey } from "@solana/web3.js";
import { recipientKey } from "../pdas";

export async function updateCompressionDestination<IDL extends Idl>({
program,
assetId,
lazyDistributor,
rewardsMint,
payer,
destination,
...rest
}: {
program: Program<LazyDistributor>;
assetId: PublicKey;
rewardsMint?: PublicKey;
assetEndpoint?: string;
lazyDistributor: PublicKey;
owner?: PublicKey;
payer?: PublicKey;
destination: PublicKey | null;
getAssetFn?: (url: string, assetId: PublicKey) => Promise<Asset | undefined>;
getAssetProofFn?: (
url: string,
assetId: PublicKey
) => Promise<AssetProof | undefined>;
}) {
const {
asset: {
ownership: { owner },
},
args,
accounts,
remainingAccounts,
} = await proofArgsAndAccounts({
connection: program.provider.connection,
assetId,
...rest,
});

return program.methods
.updateCompressionDestinationV0({
...args,
})
.accounts({
...accounts,
owner,
recipient: recipientKey(lazyDistributor, assetId)[0],
destination: destination == null ? PublicKey.default : destination,
})
.remainingAccounts(remainingAccounts);
}
1 change: 1 addition & 0 deletions packages/lazy-distributor-sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { PublicKey } from "@solana/web3.js";
import { PROGRAM_ID } from "./constants";
import { lazyDistributorResolvers } from "./resolvers";

export { updateCompressionDestination } from "./functions/updateCompressionDestination";
export { distributeCompressionRewards } from "./functions/distributeCompressionRewards";
export { initializeCompressionRecipient } from "./functions/initializeCompressionRecipient";

Expand Down
40 changes: 40 additions & 0 deletions packages/lazy-distributor-sdk/src/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ export const lazyDistributorResolvers = combineResolvers(
mint: 'common.rewardsMint',
owner: 'common.owner',
}),
ataResolver({
instruction: 'distributeCustomDestinationV0',
account: 'common.destinationAccount',
mint: 'common.rewardsMint',
owner: 'common.owner',
}),
circuitBreakerResolvers,
resolveIndividual(async ({ path, accounts, idlIx }) => {
if (path[path.length - 1] === 'targetMetadata') {
Expand Down Expand Up @@ -101,6 +107,40 @@ export const lazyDistributorResolvers = combineResolvers(
resolved,
accounts,
};
},
async ({ accounts, provider, idlIx }) => {
let resolved = 0;
if (
idlIx.name === 'updateDestinationV0' &&
// @ts-ignore
(!accounts.recipientMintAccount ||
// @ts-ignore
!accounts.owner)
) {
// @ts-ignore
const recipient = accounts.recipient as PublicKey;
const recipientAcc = await provider.connection.getAccountInfo(recipient);
const recipientMint = new PublicKey(
recipientAcc!.data.subarray(8 + 32, 8 + 32 + 32)
);
const recipientMintAccount = (
await provider.connection.getTokenLargestAccounts(recipientMint)
).value[0].address;
const recipientMintTokenAccount = await getAccount(
provider.connection,
recipientMintAccount
);
// @ts-ignore
accounts.owner = recipientMintTokenAccount.owner;
// @ts-ignore
accounts.recipientMintAccount = recipientMintAccount;
resolved += 1;
}

return {
accounts,
resolved,
};
},
async ({ accounts, provider, idlIx }) => {
let resolved = 0;
Expand Down
12 changes: 12 additions & 0 deletions packages/sus/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,18 @@ export async function sus({
"This transaction is attempting to steal your locked HNT positions",
});
}
if (
instructions.some(
(ix) => ix.parsed?.name === "updateDestinationV0"
)
) {
warningsByTx[index].push({
severity: "warning",
shortMessage: "Rewards Destination Changed",
message:
"This transaction will change the destination wallet of your mining rewards",
});
}
if (
(
await Promise.all(
Expand Down
3 changes: 3 additions & 0 deletions programs/lazy-distributor/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ pub enum ErrorCode {

#[msg("Approver signature required")]
InvalidApproverSignature,

#[msg("This recipient uses a custom destination. Use distribute_custom_destination_v0")]
CustomDestination,
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub struct DistributeRewardsCommonV0<'info> {
#[account(
mut,
has_one = lazy_distributor,
constraint = recipient.current_rewards.iter().flatten().count() >= ((lazy_distributor.oracles.len() + 1) / 2)
constraint = recipient.current_rewards.iter().flatten().count() >= ((lazy_distributor.oracles.len() + 1) / 2),
)]
pub recipient: Box<Account<'info, RecipientV0>>,
pub rewards_mint: Box<Account<'info, Mint>>,
Expand All @@ -35,8 +35,8 @@ pub struct DistributeRewardsCommonV0<'info> {
bump = circuit_breaker.bump_seed
)]
pub circuit_breaker: Box<Account<'info, AccountWindowedCircuitBreakerV0>>,
/// TODO: Should this be permissioned? Should the owner have to sign to receive rewards?
/// CHECK: Just required for ATA
/// CHECK: Checked by either verifying the owner via nft, or via destination on recipient
#[account(mut)]
pub owner: AccountInfo<'info>,
#[account(
init_if_needed,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub struct DistributeCompressionRewardsArgsV0 {
#[derive(Accounts)]
pub struct DistributeCompressionRewardsV0<'info> {
pub common: DistributeRewardsCommonV0<'info>,
/// CHECK: THe merkle tree
/// CHECK: The merkle tree
pub merkle_tree: UncheckedAccount<'info>,
pub compression_program: Program<'info, SplAccountCompression>,
pub token_program: Program<'info, Token>,
Expand All @@ -27,6 +27,12 @@ pub fn handler<'info>(
ctx: Context<'_, '_, '_, 'info, DistributeCompressionRewardsV0<'info>>,
args: DistributeCompressionRewardsArgsV0,
) -> Result<()> {
require_neq!(
ctx.accounts.common.recipient.destination,
Pubkey::default(),
ErrorCode::CustomDestination
);

verify_compressed_nft(VerifyCompressedNftArgs {
data_hash: args.data_hash,
creator_hash: args.creator_hash,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use super::*;
use anchor_lang::prelude::*;

#[derive(Accounts)]
pub struct DistributeCustomDestinationV0<'info> {
pub common: DistributeRewardsCommonV0<'info>,
}

pub fn handler(ctx: Context<DistributeCustomDestinationV0>) -> Result<()> {
require_eq!(
ctx.accounts.common.owner.key(),
ctx.accounts.common.recipient.destination
);

distribute_impl(&mut ctx.accounts.common)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::common::*;
use crate::error::ErrorCode;
use anchor_lang::prelude::*;
use anchor_spl::token::TokenAccount;

Expand All @@ -8,11 +9,16 @@ pub struct DistributeRewardsV0<'info> {
#[account(
token::mint = common.recipient.asset,
constraint = recipient_mint_account.amount > 0,
constraint = recipient_mint_account.owner == common.owner.key()
constraint = recipient_mint_account.owner == common.owner.key(),
)]
pub recipient_mint_account: Box<Account<'info, TokenAccount>>,
}

pub fn handler<'info>(ctx: Context<'_, '_, '_, 'info, DistributeRewardsV0<'info>>) -> Result<()> {
require_neq!(
ctx.accounts.common.recipient.destination,
Pubkey::default(),
ErrorCode::CustomDestination
);
distribute_impl(&mut ctx.accounts.common)
}
3 changes: 3 additions & 0 deletions programs/lazy-distributor/src/instructions/distribute/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
pub mod common;
pub mod distribute_compression_rewards_v0;
pub mod distribute_custom_destination_v0;
pub mod distribute_rewards_v0;

pub use common::*;
pub use distribute_compression_rewards_v0::*;
pub use distribute_custom_destination_v0::*;
pub use distribute_rewards_v0::*;
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ pub fn handler<'info>(
current_rewards: vec![None; ctx.accounts.lazy_distributor.oracles.len()],
lazy_distributor: ctx.accounts.lazy_distributor.key(),
bump_seed: ctx.bumps["recipient"],
destination: Pubkey::default(),
});

Ok(())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub fn handler(ctx: Context<InitializeRecipientV0>) -> Result<()> {
current_rewards: vec![None; ctx.accounts.lazy_distributor.oracles.len()],
lazy_distributor: ctx.accounts.lazy_distributor.key(),
bump_seed: ctx.bumps["recipient"],
destination: Pubkey::default(),
});

Ok(())
Expand Down
2 changes: 2 additions & 0 deletions programs/lazy-distributor/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ pub mod initialize_compression_recipient_v0;
pub mod initialize_lazy_distributor_v0;
pub mod initialize_recipient_v0;
pub mod set_current_rewards_v0;
pub mod update_destination;
pub mod update_lazy_distributor_v0;

pub use distribute::*;
pub use initialize_compression_recipient_v0::*;
pub use initialize_lazy_distributor_v0::*;
pub use initialize_recipient_v0::*;
pub use set_current_rewards_v0::*;
pub use update_destination::*;
pub use update_lazy_distributor_v0::*;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub mod update_compression_destination_v0;
pub mod update_destination_v0;

pub use update_compression_destination_v0::*;
pub use update_destination_v0::*;
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use crate::state::*;
use account_compression_cpi::program::SplAccountCompression;
use anchor_lang::prelude::*;
use shared_utils::{verify_compressed_nft, VerifyCompressedNftArgs};

#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
pub struct UpdateCompressionDestinationArgsV0 {
pub data_hash: [u8; 32],
pub creator_hash: [u8; 32],
pub root: [u8; 32],
pub index: u32,
}

#[derive(Accounts)]
#[instruction(args: UpdateCompressionDestinationArgsV0)]
pub struct UpdateCompressionDestinationV0<'info> {
#[account(mut)]
pub recipient: Box<Account<'info, RecipientV0>>,
pub owner: Signer<'info>,
/// CHECK: User provided destination
pub destination: UncheckedAccount<'info>,
/// CHECK: Checked via verify_compressed_nft
pub merkle_tree: UncheckedAccount<'info>,
pub compression_program: Program<'info, SplAccountCompression>,
}

pub fn handler<'info>(
ctx: Context<'_, '_, '_, 'info, UpdateCompressionDestinationV0<'info>>,
args: UpdateCompressionDestinationArgsV0,
) -> Result<()> {
verify_compressed_nft(VerifyCompressedNftArgs {
data_hash: args.data_hash,
creator_hash: args.creator_hash,
root: args.root,
index: args.index,
compression_program: ctx.accounts.compression_program.to_account_info(),
merkle_tree: ctx.accounts.merkle_tree.to_account_info(),
owner: ctx.accounts.owner.key(),
delegate: ctx.accounts.owner.key(),
proof_accounts: ctx.remaining_accounts.to_vec(),
})?;

ctx.accounts.recipient.destination = ctx.accounts.destination.key();

Ok(())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use crate::state::*;
use anchor_lang::prelude::*;
use anchor_spl::token::TokenAccount;

#[derive(Accounts)]
pub struct UpdateDestinationV0<'info> {
#[account(mut)]
pub recipient: Box<Account<'info, RecipientV0>>,
pub owner: Signer<'info>,
/// CHECK: User provided destination
pub destination: UncheckedAccount<'info>,
#[account(
token::mint = recipient.asset,
constraint = recipient_mint_account.amount > 0,
constraint = recipient_mint_account.owner == owner.key()
)]
pub recipient_mint_account: Box<Account<'info, TokenAccount>>,
}

pub fn handler<'info>(ctx: Context<UpdateDestinationV0>) -> Result<()> {
ctx.accounts.recipient.destination = ctx.accounts.destination.key();

Ok(())
}
17 changes: 17 additions & 0 deletions programs/lazy-distributor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,21 @@ pub mod lazy_distributor {
) -> Result<()> {
update_lazy_distributor_v0::handler(ctx, args)
}

pub fn update_compression_destination_v0<'info>(
ctx: Context<'_, '_, '_, 'info, UpdateCompressionDestinationV0<'info>>,
args: UpdateCompressionDestinationArgsV0,
) -> Result<()> {
update_compression_destination_v0::handler(ctx, args)
}

pub fn update_destination_v0(ctx: Context<UpdateDestinationV0>) -> Result<()> {
update_destination_v0::handler(ctx)
}

pub fn distribute_custom_destination_v0<'info>(
ctx: Context<'_, '_, '_, 'info, DistributeCustomDestinationV0<'info>>,
) -> Result<()> {
distribute_custom_destination_v0::handler(ctx)
}
}
2 changes: 2 additions & 0 deletions programs/lazy-distributor/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@ pub struct RecipientV0 {
pub current_config_version: u16,
pub current_rewards: Vec<Option<u64>>, // One for each oracle, matching indexes in` LazyDistrubutorV0`
pub bump_seed: u8,
/// Pubkey::Default if not being used.
pub destination: Pubkey,
}
Loading

0 comments on commit 7490d2a

Please sign in to comment.