Skip to content

Commit

Permalink
feat: implement profile delete functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
beeman committed Nov 3, 2024
1 parent 5a20239 commit 2464496
Show file tree
Hide file tree
Showing 23 changed files with 429 additions and 29 deletions.
2 changes: 2 additions & 0 deletions anchor/programs/pubkey-protocol/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ pub enum ProtocolError {
UnAuthorizedCommunityAuthority,
#[msg("Account is not a signer for this community")]
UnAuthorizedCommunitySigner,
#[msg("Cannot delete profile, too many identities found")]
UnableToDeleteProfile,
#[msg("Biography too long. Maximum characters allowed: 256")]
InvalidBioSize,
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod profile_authority_add;
pub mod profile_authority_remove;
pub mod profile_create;
pub mod profile_delete;
pub mod profile_identity_add;
pub mod profile_identity_remove;
pub mod profile_identity_verify;
Expand All @@ -9,6 +10,7 @@ pub mod profile_update;
pub use profile_authority_add::*;
pub use profile_authority_remove::*;
pub use profile_create::*;
pub use profile_delete::*;
pub use profile_identity_add::*;
pub use profile_identity_remove::*;
pub use profile_identity_verify::*;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use anchor_lang::prelude::*;

use crate::constants::*;
use crate::errors::*;
use crate::state::*;

#[derive(Accounts)]
#[instruction()]
pub struct ProfileDelete<'info> {
#[account(
mut,
seeds = [
PREFIX,
PROFILE,
&profile.username.as_bytes()
],
bump = profile.bump,
constraint = profile.check_if_deletable(&authority.key()) @ ProtocolError::UnableToDeleteProfile,
close = fee_payer, // close account and return lamports to payer
)]
pub profile: Account<'info, Profile>,
#[account(
mut,
seeds = [&Pointer::hash_seed(&IdentityProvider::Solana, &authority.key().to_string())],
bump = pointer.bump,
has_one = profile @ ProtocolError::UnAuthorized,
close = fee_payer
)]
pub pointer: Account<'info, Pointer>,
pub authority: Signer<'info>,
pub community: Account<'info, Community>,

#[account(
mut,
constraint = community.check_for_signer(&fee_payer.key()) @ ProtocolError::UnAuthorizedCommunitySigner,
)]
pub fee_payer: Signer<'info>,
}

pub fn profile_delete(_ctx: Context<ProfileDelete>) -> Result<()> {
Ok(())
}
4 changes: 4 additions & 0 deletions anchor/programs/pubkey-protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ pub mod pubkey_protocol {
profile::profile_create(ctx, args)
}

pub fn profile_delete(ctx: Context<ProfileDelete>) -> Result<()> {
profile::profile_delete(ctx)
}

pub fn profile_identity_add(
ctx: Context<ProfileIdentityAdd>,
args: ProfileIdentityAddArgs,
Expand Down
13 changes: 13 additions & 0 deletions anchor/programs/pubkey-protocol/src/state/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,17 @@ impl Profile {
pub fn check_for_authority(&self, authority: &Pubkey) -> bool {
self.authorities.binary_search(authority).is_ok()
}

pub fn check_if_deletable(&self, authority: &Pubkey) -> bool {
// Make sure we have at most one identity
if self.identities.len() > 1 {
return false;
}
// This identity should be the authority
if !self.identities.iter().any(|i| i.provider == IdentityProvider::Solana && i.provider_id == authority.to_string()) {
return false;
}
// Make sure the authority is in the authorities list
self.authorities.binary_search(authority).is_ok()
}
}
85 changes: 85 additions & 0 deletions anchor/target/idl/pubkey_protocol.json
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,86 @@
}
]
},
{
"name": "profile_delete",
"discriminator": [
215,
16,
204,
198,
21,
186,
115,
133
],
"accounts": [
{
"name": "profile",
"writable": true,
"pda": {
"seeds": [
{
"kind": "const",
"value": [
112,
117,
98,
107,
101,
121,
95,
112,
114,
111,
116,
111,
99,
111,
108
]
},
{
"kind": "const",
"value": [
112,
114,
111,
102,
105,
108,
101
]
},
{
"kind": "account",
"path": "profile.username",
"account": "Profile"
}
]
},
"relations": [
"pointer"
]
},
{
"name": "pointer",
"writable": true
},
{
"name": "authority",
"signer": true
},
{
"name": "community"
},
{
"name": "fee_payer",
"writable": true,
"signer": true
}
],
"args": []
},
{
"name": "profile_identity_add",
"discriminator": [
Expand Down Expand Up @@ -1542,6 +1622,11 @@
},
{
"code": 6043,
"name": "UnableToDeleteProfile",
"msg": "Cannot delete profile, too many identities found"
},
{
"code": 6044,
"name": "InvalidBioSize",
"msg": "Biography too long. Maximum characters allowed: 256"
}
Expand Down
85 changes: 85 additions & 0 deletions anchor/target/types/pubkey_protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,86 @@ export type PubkeyProtocol = {
}
]
},
{
"name": "profileDelete",
"discriminator": [
215,
16,
204,
198,
21,
186,
115,
133
],
"accounts": [
{
"name": "profile",
"writable": true,
"pda": {
"seeds": [
{
"kind": "const",
"value": [
112,
117,
98,
107,
101,
121,
95,
112,
114,
111,
116,
111,
99,
111,
108
]
},
{
"kind": "const",
"value": [
112,
114,
111,
102,
105,
108,
101
]
},
{
"kind": "account",
"path": "profile.username",
"account": "profile"
}
]
},
"relations": [
"pointer"
]
},
{
"name": "pointer",
"writable": true
},
{
"name": "authority",
"signer": true
},
{
"name": "community"
},
{
"name": "feePayer",
"writable": true,
"signer": true
}
],
"args": []
},
{
"name": "profileIdentityAdd",
"discriminator": [
Expand Down Expand Up @@ -1548,6 +1628,11 @@ export type PubkeyProtocol = {
},
{
"code": 6043,
"name": "unableToDeleteProfile",
"msg": "Cannot delete profile, too many identities found"
},
{
"code": 6044,
"name": "invalidBioSize",
"msg": "Biography too long. Maximum characters allowed: 256"
}
Expand Down
41 changes: 41 additions & 0 deletions anchor/tests/pubkey-protocol-profile.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,48 @@ describe('pubkey-protocol-profile', () => {
expect(newAvatarUrl).toStrictEqual(input.newAvatarUrl)
expect(newName).toStrictEqual(input.newName)
})

it('should create and delete a profile', async () => {
const testUsername = unique('alice')
const testWallet = Keypair.generate()
const [profile] = getPubKeyProfilePda({ username: testUsername, programId: program.programId })
const [pointer] = getPubKeyPointerPda({
programId: program.programId,
provider: IdentityProvider.Solana,
providerId: testWallet.publicKey.toString(),
})

await createTestProfile({
community,
communityAuthority,
username: testUsername,
program,
profileOwner: testWallet,
})

const { authorities } = await program.account.profile.fetch(profile)
expect(authorities).toEqual([testWallet.publicKey])

// Delete the profile
await program.methods
.profileDelete()
.accountsStrict({
authority: testWallet.publicKey,
community,
feePayer: communityAuthority.publicKey,
pointer,
profile,
})
.signers([communityAuthority, testWallet])
.rpc()

const profileAfter = await program.account.profile.fetchNullable(profile)
const pointerAfter = await program.account.pointer.fetchNullable(pointer)
expect(profileAfter).toBeNull()
expect(pointerAfter).toBeNull()
})
})

describe('Authorities', () => {
it('should add an authority', async () => {
const [profile] = getPubKeyProfilePda({ username, programId: program.programId })
Expand Down
7 changes: 7 additions & 0 deletions sdk/src/lib/pubkey-protocol-sdk-interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ export interface ProfileCreateOptions {
username?: string
}

export interface ProfileDeleteOptions {
authority: PublicKeyString
community: PublicKeyString
feePayer: PublicKeyString
username: string
}

export interface ProfileGet {
nullable?: boolean
profile: PublicKeyString
Expand Down
Loading

0 comments on commit 2464496

Please sign in to comment.