Skip to content
This repository has been archived by the owner on Dec 11, 2024. It is now read-only.

Feature: transfer drop ownership #23

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
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
3 changes: 3 additions & 0 deletions programs/rarible_editions_controls/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ pub use update_platform_fee::*;

pub mod update_platform_fee_secondary_admin;
pub use update_platform_fee_secondary_admin::*;

pub mod transfer_ownership;
pub use transfer_ownership::*;
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use anchor_lang::prelude::*;
use rarible_editions::EditionsDeployment;
use crate::EditionsControls;

#[derive(Accounts)]
pub struct TransferOwnershipCtx<'info> {
/// CHECK: Only used for validation
#[account()]
pub editions_deployment: Account<'info, EditionsDeployment>,

#[account(
mut,
seeds = [b"editions_controls", editions_deployment.key().as_ref()],
bump,
constraint = editions_controls.creator == current_owner.key()
)]
pub editions_controls: Box<Account<'info, EditionsControls>>,

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

/// CHECK: Can be any account that will become the new owner
pub new_owner: UncheckedAccount<'info>,

}

pub fn handler(ctx: Context<TransferOwnershipCtx>) -> Result<()> {
// Update the creator in editions_controls only
ctx.accounts.editions_controls.creator = ctx.accounts.new_owner.key();

Ok(())
}
4 changes: 4 additions & 0 deletions programs/rarible_editions_controls/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,8 @@ pub mod rarible_editions_controls {
) -> Result<()> {
instructions::update_platform_fee_secondary_admin(ctx, input)
}

pub fn transfer_ownership(ctx: Context<TransferOwnershipCtx>) -> Result<()> {
instructions::transfer_ownership::handler(ctx)
}
}
124 changes: 124 additions & 0 deletions tests/tests/editions_controls.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1814,4 +1814,128 @@ describe("Editions Controls Test Suite", () => {
});
});
});
describe("Transferring ownership", () => {
let newOwner: Keypair;

before(async () => {
newOwner = Keypair.generate();
// Airdrop SOL to new owner
const newOwnerAirdropSignature = await provider.connection.requestAirdrop(
newOwner.publicKey,
1 * LAMPORTS_PER_SOL
);
await provider.connection.confirmTransaction(newOwnerAirdropSignature);
});

it("Should transfer ownership to a new owner", async () => {
const transferOwnershipIx = await editionsControlsProgram.methods
.transferOwnership()
.accountsStrict({
editionsDeployment: editionsPda,
editionsControls: editionsControlsPda,
currentOwner: payer.publicKey,
newOwner: newOwner.publicKey,
})
.instruction();

const transaction = new Transaction().add(transferOwnershipIx);

await provider.sendAndConfirm(transaction, [payer]);

// Verify state after ownership transfer
const editionsDecoded = await getEditions(provider.connection, editionsPda, editionsProgram);
const editionsControlsDecoded = await getEditionsControls(
provider.connection,
editionsControlsPda,
editionsControlsProgram
);

if (VERBOSE_LOGGING) {
logEditions(editionsDecoded);
logEditionsControls(editionsControlsDecoded);
}

// Verify that ownership was transferred in both programs
expect(editionsDecoded.data.creator.toBase58()).to.equal(editionsControlsPda.toBase58());
expect(editionsControlsDecoded.data.creator.toBase58()).to.equal(
newOwner.publicKey.toBase58()
);
});

it("Should not allow the old owner to perform admin actions", async () => {
// Try to add a phase with the old owner
const phaseConfig = {
maxMintsPerWallet: new anchor.BN(100),
maxMintsTotal: new anchor.BN(1000),
priceAmount: new anchor.BN(500000),
startTime: new anchor.BN(new Date().getTime() / 1000),
endTime: new anchor.BN(new Date().getTime() / 1000 + 60 * 60 * 24),
priceToken: new PublicKey("So11111111111111111111111111111111111111112"),
isPrivate: false,
merkleRoot: null,
};

const phaseIx = await editionsControlsProgram.methods
.addPhase(phaseConfig)
.accountsStrict({
editionsControls: editionsControlsPda,
creator: payer.publicKey,
payer: payer.publicKey,
systemProgram: SystemProgram.programId,
tokenProgram: TOKEN_2022_PROGRAM_ID,
raribleEditionsProgram: editionsProgram.programId,
})
.instruction();

const transaction = new Transaction().add(phaseIx);

try {
await provider.sendAndConfirm(transaction, [payer]);
throw new Error("Transaction should have failed");
} catch (error) {
const errorString = JSON.stringify(error);
// Verify that the error is due to invalid creator
expect(errorString).to.include("A raw constraint was violated.");
}
});

it("Should allow the new owner to perform admin actions", async () => {
// Try to add a phase with the new owner
const phaseConfig = {
maxMintsPerWallet: new anchor.BN(100),
maxMintsTotal: new anchor.BN(1000),
priceAmount: new anchor.BN(500000),
startTime: new anchor.BN(new Date().getTime() / 1000),
endTime: new anchor.BN(new Date().getTime() / 1000 + 60 * 60 * 24),
priceToken: new PublicKey("So11111111111111111111111111111111111111112"),
isPrivate: false,
merkleRoot: null,
};

const phaseIx = await editionsControlsProgram.methods
.addPhase(phaseConfig)
.accountsStrict({
editionsControls: editionsControlsPda,
creator: newOwner.publicKey,
payer: newOwner.publicKey,
systemProgram: SystemProgram.programId,
tokenProgram: TOKEN_2022_PROGRAM_ID,
raribleEditionsProgram: editionsProgram.programId,
})
.instruction();

const transaction = new Transaction().add(phaseIx);

await provider.sendAndConfirm(transaction, [newOwner]);

// Verify that the phase was added
const editionsControlsDecoded = await getEditionsControls(
provider.connection,
editionsControlsPda,
editionsControlsProgram
);

expect(editionsControlsDecoded.data.phases.length).to.equal(7); // Previous 6 phases + 1 new phase
});
});
});