Browse Source

:sparkles: Add allow undelegation ix (#68)

Gabriele Picco 1 year ago
parent
commit
a5670ff171

+ 14 - 2
Cargo.lock

@@ -1587,14 +1587,26 @@ checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
 
 [[package]]
 name = "delegation-program-sdk"
-version = "0.0.2"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8d4c66a4e72ca5581f8f14cc69bcded51da1d1c8e548e2b394bc00bb76b0b7d"
+checksum = "f8c618cde8084c987bdf7434d06253b5c79d032aa487fc996965d5dc59e3dbd6"
 dependencies = [
  "borsh 0.10.3",
+ "delegation-sdk-attribute-delegate",
  "solana-program",
 ]
 
+[[package]]
+name = "delegation-sdk-attribute-delegate"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c95b9e42aee58a38bcd7f3f0014a832e66ea31157f4f8102ae85a4a37aa4eeb5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "der"
 version = "0.5.1"

+ 1 - 1
Cargo.toml

@@ -51,7 +51,7 @@ anyhow = "1.0.32"
 heck = "0.5.0"
 clap = { version = "4.2.4", features = ["derive"] }
 ahash = "=0.8.11"
-delegation-program-sdk = "=0.0.2"
+delegation-program-sdk = "=0.1.3"
 
 [profile.release]
 overflow-checks = true

+ 1 - 1
clients/bolt-sdk/package.json

@@ -8,7 +8,7 @@
   "dependencies": {
     "@metaplex-foundation/beet": "^0.7.1",
     "@metaplex-foundation/beet-solana": "^0.4.0",
-    "@magicblock-labs/delegation-program": "0.1.1"
+    "@magicblock-labs/delegation-program": "0.1.3"
   },
   "devDependencies": {
     "@metaplex-foundation/solita": "^0.20.1",

+ 0 - 51
clients/bolt-sdk/src/delegation/accounts.ts

@@ -1,51 +0,0 @@
-import { PublicKey } from "@solana/web3.js";
-import {
-  DELEGATED_ACCOUNT_SEEDS,
-  DELEGATION_PROGRAM_ID,
-  SEED_BUFFER_PDA,
-  SEED_COMMIT_STATE_RECORD_PDA,
-  SEED_DELEGATION_PDA,
-  SEED_STATE_DIFF_PDA,
-} from "@magicblock-labs/delegation-program";
-
-export function getDelegationAccounts(
-  accountToDelegate: PublicKey,
-  ownerProgram: PublicKey,
-  ownedBuffer: boolean = true
-) {
-  const pdaBytes = accountToDelegate.toBytes();
-
-  const [delegationPda] = PublicKey.findProgramAddressSync(
-    [Buffer.from(SEED_DELEGATION_PDA), pdaBytes],
-    new PublicKey(DELEGATION_PROGRAM_ID)
-  );
-
-  const [delegatedAccountSeedsPda] = PublicKey.findProgramAddressSync(
-    [Buffer.from(DELEGATED_ACCOUNT_SEEDS), pdaBytes],
-    new PublicKey(DELEGATION_PROGRAM_ID)
-  );
-
-  const [bufferPda] = PublicKey.findProgramAddressSync(
-    [Buffer.from(SEED_BUFFER_PDA), pdaBytes],
-    ownedBuffer
-      ? new PublicKey(ownerProgram)
-      : new PublicKey(DELEGATION_PROGRAM_ID)
-  );
-
-  const [commitStateRecordPda] = PublicKey.findProgramAddressSync(
-    [Buffer.from(SEED_COMMIT_STATE_RECORD_PDA), pdaBytes],
-    new PublicKey(DELEGATION_PROGRAM_ID)
-  );
-
-  const [commitStatePda] = PublicKey.findProgramAddressSync(
-    [Buffer.from(SEED_STATE_DIFF_PDA), pdaBytes],
-    new PublicKey(DELEGATION_PROGRAM_ID)
-  );
-  return {
-    delegationPda,
-    delegatedAccountSeedsPda,
-    bufferPda,
-    commitStateRecordPda,
-    commitStatePda,
-  };
-}

+ 75 - 0
clients/bolt-sdk/src/delegation/allow_undelegation.ts

@@ -0,0 +1,75 @@
+import * as beet from "@metaplex-foundation/beet";
+import * as web3 from "@solana/web3.js";
+import {
+  DelegateAccounts,
+  DELEGATION_PROGRAM_ID,
+} from "@magicblock-labs/delegation-program";
+
+export const allowUndelegationStruct = new beet.BeetArgsStruct<{
+  instructionDiscriminator: number[] /* size: 8 */;
+}>(
+  [["instructionDiscriminator", beet.uniformFixedSizeArray(beet.u8, 8)]],
+  "allowUndelegationInstructionArgs"
+);
+
+export interface AllowUndelegationInstructionAccounts {
+  delegatedAccount: web3.PublicKey;
+  ownerProgram: web3.PublicKey;
+  buffer?: web3.PublicKey;
+}
+
+export const allowUndelegateInstructionDiscriminator = [
+  255, 66, 82, 208, 247, 5, 210, 126,
+];
+
+/**
+ * Creates a Delegate instruction.
+ */
+
+export function createAllowUndelegationInstruction(
+  accounts: AllowUndelegationInstructionAccounts
+) {
+  const [data] = allowUndelegationStruct.serialize({
+    instructionDiscriminator: allowUndelegateInstructionDiscriminator,
+  });
+
+  const { delegationPda, delegationMetadata, bufferPda } = DelegateAccounts(
+    accounts.delegatedAccount,
+    accounts.ownerProgram
+  );
+
+  const keys: web3.AccountMeta[] = [
+    {
+      pubkey: accounts.delegatedAccount,
+      isWritable: false,
+      isSigner: false,
+    },
+    {
+      pubkey: delegationPda,
+      isWritable: false,
+      isSigner: false,
+    },
+    {
+      pubkey: delegationMetadata,
+      isWritable: true,
+      isSigner: false,
+    },
+    {
+      pubkey: bufferPda,
+      isWritable: false,
+      isSigner: false,
+    },
+    {
+      pubkey: new web3.PublicKey(DELEGATION_PROGRAM_ID),
+      isWritable: true,
+      isSigner: false,
+    },
+  ];
+
+  const programId = accounts.ownerProgram;
+  return new web3.TransactionInstruction({
+    programId,
+    keys,
+    data,
+  });
+}

+ 10 - 6
clients/bolt-sdk/src/delegation/delegate.ts

@@ -1,7 +1,9 @@
 import * as beet from "@metaplex-foundation/beet";
 import * as web3 from "@solana/web3.js";
-import { getDelegationAccounts } from "./accounts";
-import { DELEGATION_PROGRAM_ID } from "@magicblock-labs/delegation-program";
+import {
+  DelegateAccounts,
+  DELEGATION_PROGRAM_ID,
+} from "@magicblock-labs/delegation-program";
 
 export interface DelegateInstructionArgs {
   validUntil: beet.bignum;
@@ -33,7 +35,7 @@ export interface DelegateInstructionAccounts {
   ownerProgram: web3.PublicKey;
   buffer?: web3.PublicKey;
   delegationRecord?: web3.PublicKey;
-  delegateAccountSeeds?: web3.PublicKey;
+  delegationMetadata?: web3.PublicKey;
   delegationProgram?: web3.PublicKey;
   systemProgram?: web3.PublicKey;
 }
@@ -58,8 +60,10 @@ export function createDelegateInstruction(
     commitFrequencyMs,
   });
 
-  const { delegationPda, delegatedAccountSeedsPda, bufferPda } =
-    getDelegationAccounts(accounts.account, accounts.ownerProgram);
+  const { delegationPda, delegationMetadata, bufferPda } = DelegateAccounts(
+    accounts.account,
+    accounts.ownerProgram
+  );
 
   const keys: web3.AccountMeta[] = [
     {
@@ -93,7 +97,7 @@ export function createDelegateInstruction(
       isSigner: false,
     },
     {
-      pubkey: accounts.delegateAccountSeeds ?? delegatedAccountSeedsPda,
+      pubkey: accounts.delegationMetadata ?? delegationMetadata,
       isWritable: true,
       isSigner: false,
     },

+ 1 - 1
clients/bolt-sdk/src/index.ts

@@ -4,8 +4,8 @@ import { PROGRAM_ID } from "./generated";
 export * from "./generated/accounts";
 export * from "./generated/instructions";
 export * from "./world/transactions";
-export * from "./delegation/accounts";
 export * from "./delegation/delegate";
+export * from "./delegation/allow_undelegation";
 export {
   createCommitInstruction,
   createUndelegateInstruction,

+ 4 - 4
clients/bolt-sdk/yarn.lock

@@ -84,10 +84,10 @@
     wrap-ansi "^8.1.0"
     wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
 
-"@magicblock-labs/delegation-program@0.1.1":
-  version "0.1.1"
-  resolved "https://registry.yarnpkg.com/@magicblock-labs/delegation-program/-/delegation-program-0.1.1.tgz#71a5fb25ccf88ea7746ea70473fb109ff6b67433"
-  integrity sha512-4He8V7jkrGy8MTp6qAz2h5y70+6y4cmvQc+7Km6B7WDLODlymPu9zZEl1/bmY24bOeKVKfrEEE7pDVa8qr3BwQ==
+"@magicblock-labs/delegation-program@0.1.2":
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/@magicblock-labs/delegation-program/-/delegation-program-0.1.2.tgz#7b298398259d80a368eb9e91cb911e5a6e542391"
+  integrity sha512-9EGtcBWGAXGCOjTJVyzxydMpM8GGk8QjzQlgpV0q96BAbmpEO9C6b/N3lvMRTR1FfDuBn43a3Pa5YEiHyUu8Eg==
   dependencies:
     "@metaplex-foundation/beet" "^0.7.2"
     "@solana/web3.js" "^1.92.3"

+ 47 - 3
crates/bolt-lang/attribute/delegate/src/lib.rs

@@ -34,6 +34,7 @@ pub fn delegate(args: TokenStream, input: TokenStream) -> TokenStream {
 fn modify_component_module(mut module: ItemMod, component_type: &Type) -> ItemMod {
     let (delegate_fn, delegate_struct) = generate_delegate(component_type);
     let (undelegate_fn, undelegate_struct) = generate_undelegate();
+    let (allow_undelegate_fn, allow_undelegate_struct) = generate_allow_undelegate();
     module.content = module.content.map(|(brace, mut items)| {
         items.extend(
             vec![
@@ -41,6 +42,8 @@ fn modify_component_module(mut module: ItemMod, component_type: &Type) -> ItemMo
                 delegate_struct,
                 undelegate_fn,
                 undelegate_struct,
+                allow_undelegate_fn,
+                allow_undelegate_struct,
             ]
             .into_iter()
             .map(|item| syn::parse2(item).unwrap())
@@ -51,6 +54,47 @@ fn modify_component_module(mut module: ItemMod, component_type: &Type) -> ItemMo
     module
 }
 
+/// Generates the allow_undelegate function and struct.
+fn generate_allow_undelegate() -> (TokenStream2, TokenStream2) {
+    (
+        quote! {
+            #[automatically_derived]
+            pub fn allow_undelegation(ctx: Context<AllowUndelegation>) -> Result<()> {
+                ::bolt_lang::allow_undelegation(
+                    &ctx.accounts.base_account,
+                    &ctx.accounts.delegation_record,
+                    &ctx.accounts.delegation_metadata,
+                    &ctx.accounts.buffer,
+                    &ctx.accounts.delegation_program,
+                    &id(),
+                )?;
+                Ok(())
+            }
+        },
+        quote! {
+            #[automatically_derived]
+            #[derive(Accounts)]
+            pub struct AllowUndelegation<'info> {
+                #[account()]
+                /// CHECK: The delegated component
+                pub base_account: AccountInfo<'info>,
+                #[account()]
+                /// CHECK: delegation record
+                pub delegation_record: AccountInfo<'info>,
+                #[account(mut)]
+                /// CHECK: delegation metadata
+                pub delegation_metadata: AccountInfo<'info>,
+                #[account()]
+                /// CHECK: singer buffer to enforce CPI
+                pub buffer: AccountInfo<'info>,
+                #[account()]
+                /// CHECK:`
+                pub delegation_program: AccountInfo<'info>,
+            }
+        },
+    )
+}
+
 /// Generates the undelegate function and struct.
 fn generate_undelegate() -> (TokenStream2, TokenStream2) {
     (
@@ -63,7 +107,7 @@ fn generate_undelegate() -> (TokenStream2, TokenStream2) {
                     &ctx.accounts.payer,
                     &ctx.accounts.system_program,
                 ];
-                undelegate_account(
+                ::bolt_lang::undelegate_account(
                     delegated_account,
                     &id(),
                     buffer,
@@ -77,7 +121,7 @@ fn generate_undelegate() -> (TokenStream2, TokenStream2) {
         quote! {
             #[automatically_derived]
             #[derive(Accounts)]
-                pub struct InitializeAfterUndelegation<'info> {
+            pub struct InitializeAfterUndelegation<'info> {
                 /// CHECK:`
                 #[account(mut)]
                 pub base_account: AccountInfo<'info>,
@@ -115,7 +159,7 @@ fn generate_delegate(component_type: &Type) -> (TokenStream2, TokenStream2) {
 
                 let pda_seeds: &[&[u8]] = &[<#component_type>::seed(), &entity.key.to_bytes()];
 
-                delegate_account(
+                ::bolt_lang::delegate_account(
                     payer,
                     account,
                     owner_program,

+ 1 - 1
crates/bolt-lang/src/lib.rs

@@ -20,7 +20,7 @@ pub use world;
 pub use world::program::World;
 pub use world::Entity;
 
-pub use delegation_program_sdk::{delegate_account, undelegate_account};
+pub use delegation_program_sdk::{allow_undelegation, delegate_account, undelegate_account};
 
 pub use serde;
 

+ 8 - 1
tests/bolt.ts

@@ -19,6 +19,7 @@ import {
   InitializeComponent,
   InitializeNewWorld,
   ApplySystem,
+  createAllowUndelegationInstruction,
 } from "../clients/bolt-sdk";
 
 enum Direction {
@@ -514,13 +515,19 @@ describe("bolt", () => {
   });
 
   it("Check component undelegation", async () => {
+    const allowUndelegateIx = createAllowUndelegationInstruction({
+      delegatedAccount: componentPositionEntity1Pda,
+      ownerProgram: exampleComponentPosition.programId,
+    });
     const delegateIx = createUndelegateInstruction({
       payer: provider.wallet.publicKey,
       delegatedAccount: componentPositionEntity1Pda,
       ownerProgram: exampleComponentPosition.programId,
       reimbursement: provider.wallet.publicKey,
     });
-    const tx = new anchor.web3.Transaction().add(delegateIx);
+    const tx = new anchor.web3.Transaction()
+      .add(allowUndelegateIx)
+      .add(delegateIx);
     await provider.sendAndConfirm(tx);
     const acc = await provider.connection.getAccountInfo(
       componentPositionEntity1Pda

BIN
tests/fixtures/delegation.so