Browse Source

:construction: Buffer & Component delegation

Danilo Guanabara 1 month ago
parent
commit
73c66a541d

+ 1 - 0
Cargo.lock

@@ -7089,6 +7089,7 @@ dependencies = [
  "anchor-lang",
  "bolt-component",
  "bolt-system",
+ "ephemeral-rollups-sdk",
  "session-keys",
  "solana-security-txt",
  "tuple-conv",

+ 11 - 5
clients/csharp/Solana.Unity.Bolt.Test/AccelerationTest.cs

@@ -21,10 +21,12 @@ namespace AccelerationTest {
             await Profiler.Run("DelegateComponent", async () => {
                 await DelegateComponent(framework);
             });
-            // TODO: Re-enable this test when ephemeral-validator is properly installed on the CI.
-            // await Profiler.Run("ApplySimpleMovementSystemOnAccelerator 10", async () => {
-            //     await ApplySimpleMovementSystemOnAccelerator(framework);
-            // });
+            await Profiler.Run("ApplySimpleMovementSystemOnAccelerator 10", async () => {
+                if (Environment.GetEnvironmentVariable("GITHUB_ACTIONS") != null) {
+                    return;
+                }
+                await ApplySimpleMovementSystemOnAccelerator(framework);
+            });
         }
 
         public static async Task AddAccelerationEntity(Framework framework) {
@@ -41,16 +43,20 @@ namespace AccelerationTest {
 
         public static async Task DelegateComponent(Framework framework) {
             var delegateComponent = await Bolt.World.DelegateComponent(framework.Wallet.Account.PublicKey, framework.AccelerationEntityPda, framework.ExampleComponentPosition);
-            await framework.SendAndConfirmInstruction(delegateComponent.Instruction);
+            await framework.SendAndConfirmInstruction(delegateComponent.gTransaction);
+            var delegateBuffer = await Bolt.World.DelegateBuffer();
         }
 
         public static async Task ApplySimpleMovementSystemOnAccelerator(Framework framework) {
             for (int i = 0; i < 10; i++) {
                 var apply = new ApplyAccounts() {
+                    CpiAuth = WorldProgram.FindCpiAuthPda(),
                     Authority = framework.Wallet.Account.PublicKey,
                     BoltSystem = framework.SystemSimpleMovement,
                     World = framework.WorldPda,
+                    Buffer = WorldProgram.FindBufferPda(framework.AccelerationComponentPositionPda)
                 };
+
                 var instruction = WorldProgram.Apply(apply, Bolt.World.SerializeArgs(new { direction = "Up" }));
                 instruction.Keys.Add(AccountMeta.ReadOnly(framework.ExampleComponentPosition, false));
                 instruction.Keys.Add(AccountMeta.Writable(framework.AccelerationComponentPositionPda, false));

+ 1 - 1
clients/csharp/Solana.Unity.Bolt.Test/ECSTest.cs

@@ -112,7 +112,7 @@ namespace ECSTest {
                 Authority = framework.Wallet.Account.PublicKey,
                 BoltSystem = framework.SystemSimpleMovement,
                 World = framework.WorldPda,
-                Buffer = WorldProgram.FindBufferPda(framework.Wallet.Account.PublicKey),
+                Buffer = WorldProgram.FindBufferPda(framework.ComponentPositionEntity1Pda),
             };
             var instruction = WorldProgram.Apply(apply, Bolt.World.SerializeArgs(new { direction = "Up" }));
             instruction.Keys.Add(AccountMeta.ReadOnly(framework.ExampleComponentPosition, false));

+ 2 - 1
clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/InitializeComponent.cs

@@ -35,7 +35,8 @@ namespace Bolt {
                 Entity = entity,
                 Data = componentPda,
                 ComponentProgram = componentId,
-                Authority = authority ?? new PublicKey(WorldProgram.ID)
+                Authority = authority ?? new PublicKey(WorldProgram.ID),
+                Buffer = WorldProgram.FindBufferPda(componentPda)
             };
             var instruction = WorldProgram.InitializeComponent(initializeComponent);
             return new InitializeComponentInstruction() {

+ 3 - 1
clients/csharp/Solana.Unity.Bolt/WorldProgram/Generated.cs

@@ -417,6 +417,8 @@ namespace World
             public PublicKey CpiAuth { get; set; }
 
             public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111");
+
+            public PublicKey Buffer { get; set; }
         }
 
         public class InitializeNewWorldAccounts
@@ -577,7 +579,7 @@ namespace World
             {
                 programId ??= new(ID);
                 List<Solana.Unity.Rpc.Models.AccountMeta> keys = new()
-                {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Payer, true), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Data, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.Entity, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.ComponentProgram, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.Authority, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.CpiAuth, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)};
+                {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Payer, true), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Data, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.Entity, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.ComponentProgram, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.Authority, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.CpiAuth, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Buffer, false)};
                 byte[] _data = new byte[1200];
                 int offset = 0;
                 _data.WriteU64(2179155133888827172UL, offset);

+ 2 - 2
clients/csharp/Solana.Unity.Bolt/WorldProgram/World.cs

@@ -236,7 +236,7 @@ namespace World
                 if (sessionToken != null) {
                     var apply = new ApplyWithSessionAccounts() {
                         CpiAuth = WorldProgram.FindCpiAuthPda(),
-                        Buffer = FindBufferPda(authority),
+                        Buffer = FindBufferPda(componentPdas[0]),
                         BoltSystem = system,
                         Authority = authority,
                         World = world,
@@ -246,7 +246,7 @@ namespace World
                 } else {
                     var apply = new ApplyAccounts() {
                         CpiAuth = WorldProgram.FindCpiAuthPda(),
-                        Buffer = FindBufferPda(authority),
+                        Buffer = FindBufferPda(componentPdas[0]),
                         BoltSystem = system,
                         Authority = authority,
                         World = world,

+ 37 - 5
clients/typescript/src/delegation/delegate.ts

@@ -7,12 +7,14 @@ import {
   delegationMetadataPdaFromDelegatedAccount,
   delegationRecordPdaFromDelegatedAccount,
 } from "@magicblock-labs/ephemeral-rollups-sdk";
-import { FindComponentPda } from "../index";
+import { FindBufferPda, FindComponentPda, Program } from "../index";
 import {
-  type PublicKey,
+  PublicKey,
   Transaction,
   type TransactionInstruction,
 } from "@solana/web3.js";
+import { worldIdl } from "../generated";
+import { Idl } from "@coral-xyz/anchor";
 
 export interface DelegateInstructionArgs {
   commitFrequencyMs: number;
@@ -172,11 +174,12 @@ export async function DelegateComponent({
   delegationProgram?: web3.PublicKey;
   systemProgram?: web3.PublicKey;
 }): Promise<{
-  instruction: TransactionInstruction;
+  instructions: TransactionInstruction[];
   transaction: Transaction;
   componentPda: PublicKey;
 }> {
   const componentPda = FindComponentPda({ componentId, entity, seed });
+  const componentBuffer = FindBufferPda(componentPda);
   const delegateComponentIx = createDelegateInstruction({
     payer,
     entity,
@@ -189,9 +192,38 @@ export async function DelegateComponent({
     systemProgram,
   });
 
+  const componentBufferDelegationRecord =
+    delegationRecordPdaFromDelegatedAccount(componentBuffer);
+
+  const componentBufferDelegationMetadata =
+    delegationMetadataPdaFromDelegatedAccount(componentBuffer);
+
+  const componentBufferBuffer =
+    delegateBufferPdaFromDelegatedAccountAndOwnerProgram(
+      componentBuffer,
+      new PublicKey(worldIdl.address),
+    );
+
+  const program = new Program(worldIdl as Idl) as unknown as Program;
+  const delegateBufferComponentIx = await program.methods
+    .delegateBuffer(0, null)
+    .accounts({
+      payer,
+      component: componentPda,
+      componentBuffer,
+      ownerProgram: worldIdl.address,
+      buffer: componentBufferBuffer,
+      delegationRecord: componentBufferDelegationRecord,
+      delegationMetadata: componentBufferDelegationMetadata,
+      delegationProgram: DELEGATION_PROGRAM_ID,
+    })
+    .instruction();
+
   return {
-    instruction: delegateComponentIx,
-    transaction: new Transaction().add(delegateComponentIx),
+    instructions: [delegateComponentIx, delegateBufferComponentIx], // TODO: Make it a single instruction again using the World program as a proxy.
+    transaction: new Transaction()
+      .add(delegateComponentIx)
+      .add(delegateBufferComponentIx),
     componentPda,
   };
 }

+ 90 - 40
clients/typescript/src/generated/idl/world.json

@@ -118,26 +118,7 @@
       "accounts": [
         {
           "name": "buffer",
-          "writable": true,
-          "pda": {
-            "seeds": [
-              {
-                "kind": "const",
-                "value": [
-                  98,
-                  117,
-                  102,
-                  102,
-                  101,
-                  114
-                ]
-              },
-              {
-                "kind": "account",
-                "path": "authority"
-              }
-            ]
-          }
+          "writable": true
         },
         {
           "name": "bolt_system"
@@ -180,26 +161,7 @@
       "accounts": [
         {
           "name": "buffer",
-          "writable": true,
-          "pda": {
-            "seeds": [
-              {
-                "kind": "const",
-                "value": [
-                  98,
-                  117,
-                  102,
-                  102,
-                  101,
-                  114
-                ]
-              },
-              {
-                "kind": "account",
-                "path": "authority"
-              }
-            ]
-          }
+          "writable": true
         },
         {
           "name": "bolt_system"
@@ -262,6 +224,66 @@
       ],
       "args": []
     },
+    {
+      "name": "delegate_buffer",
+      "discriminator": [
+        121,
+        134,
+        252,
+        65,
+        64,
+        83,
+        93,
+        91
+      ],
+      "accounts": [
+        {
+          "name": "payer",
+          "signer": true
+        },
+        {
+          "name": "component"
+        },
+        {
+          "name": "component_buffer",
+          "writable": true
+        },
+        {
+          "name": "owner_program"
+        },
+        {
+          "name": "buffer",
+          "writable": true
+        },
+        {
+          "name": "delegation_record",
+          "writable": true
+        },
+        {
+          "name": "delegation_metadata",
+          "writable": true
+        },
+        {
+          "name": "delegation_program"
+        },
+        {
+          "name": "system_program",
+          "address": "11111111111111111111111111111111"
+        }
+      ],
+      "args": [
+        {
+          "name": "commit_frequency_ms",
+          "type": "u32"
+        },
+        {
+          "name": "validator",
+          "type": {
+            "option": "pubkey"
+          }
+        }
+      ]
+    },
     {
       "name": "destroy_component",
       "discriminator": [
@@ -344,6 +366,29 @@
         {
           "name": "system_program",
           "address": "11111111111111111111111111111111"
+        },
+        {
+          "name": "buffer",
+          "writable": true,
+          "pda": {
+            "seeds": [
+              {
+                "kind": "const",
+                "value": [
+                  98,
+                  117,
+                  102,
+                  102,
+                  101,
+                  114
+                ]
+              },
+              {
+                "kind": "account",
+                "path": "data"
+              }
+            ]
+          }
         }
       ],
       "args": []
@@ -623,6 +668,11 @@
       "code": 6006,
       "name": "InvalidComponentOwner",
       "msg": "The component owner does not match the program"
+    },
+    {
+      "code": 6007,
+      "name": "InvalidBufferAccount",
+      "msg": "Invalid buffer account"
     }
   ],
   "types": [

+ 6 - 0
clients/typescript/src/generated/instructions/initializeComponent.ts

@@ -39,6 +39,7 @@ export interface InitializeComponentInstructionAccounts {
   entity: web3.PublicKey;
   componentProgram: web3.PublicKey;
   authority: web3.PublicKey;
+  buffer: web3.PublicKey;
   systemProgram?: web3.PublicKey;
   anchorRemainingAccounts?: web3.AccountMeta[];
 }
@@ -98,6 +99,11 @@ export function createInitializeComponentInstruction(
       isWritable: false,
       isSigner: false,
     },
+    {
+      pubkey: accounts.buffer,
+      isWritable: true,
+      isSigner: false,
+    },
   ];
 
   if (accounts.anchorRemainingAccounts != null) {

+ 72 - 24
clients/typescript/src/generated/types/world.ts

@@ -92,18 +92,6 @@ export type World = {
         {
           name: "buffer";
           writable: true;
-          pda: {
-            seeds: [
-              {
-                kind: "const";
-                value: [98, 117, 102, 102, 101, 114];
-              },
-              {
-                kind: "account";
-                path: "authority";
-              },
-            ];
-          };
         },
         {
           name: "boltSystem";
@@ -138,18 +126,6 @@ export type World = {
         {
           name: "buffer";
           writable: true;
-          pda: {
-            seeds: [
-              {
-                kind: "const";
-                value: [98, 117, 102, 102, 101, 114];
-              },
-              {
-                kind: "account";
-                path: "authority";
-              },
-            ];
-          };
         },
         {
           name: "boltSystem";
@@ -203,6 +179,57 @@ export type World = {
       ];
       args: [];
     },
+    {
+      name: "delegateBuffer";
+      discriminator: [121, 134, 252, 65, 64, 83, 93, 91];
+      accounts: [
+        {
+          name: "payer";
+          signer: true;
+        },
+        {
+          name: "component";
+        },
+        {
+          name: "componentBuffer";
+          writable: true;
+        },
+        {
+          name: "ownerProgram";
+        },
+        {
+          name: "buffer";
+          writable: true;
+        },
+        {
+          name: "delegationRecord";
+          writable: true;
+        },
+        {
+          name: "delegationMetadata";
+          writable: true;
+        },
+        {
+          name: "delegationProgram";
+        },
+        {
+          name: "systemProgram";
+          address: "11111111111111111111111111111111";
+        },
+      ];
+      args: [
+        {
+          name: "commitFrequencyMs";
+          type: "u32";
+        },
+        {
+          name: "validator";
+          type: {
+            option: "pubkey";
+          };
+        },
+      ];
+    },
     {
       name: "destroyComponent";
       discriminator: [40, 197, 69, 196, 67, 95, 219, 73];
@@ -268,6 +295,22 @@ export type World = {
           name: "systemProgram";
           address: "11111111111111111111111111111111";
         },
+        {
+          name: "buffer";
+          writable: true;
+          pda: {
+            seeds: [
+              {
+                kind: "const";
+                value: [98, 117, 102, 102, 101, 114];
+              },
+              {
+                kind: "account";
+                path: "data";
+              },
+            ];
+          };
+        },
       ];
       args: [];
     },
@@ -454,6 +497,11 @@ export type World = {
       name: "invalidComponentOwner";
       msg: "The component owner does not match the program";
     },
+    {
+      code: 6007;
+      name: "invalidBufferAccount";
+      msg: "Invalid buffer account";
+    },
   ];
   types: [
     {

+ 2 - 2
clients/typescript/src/index.ts

@@ -99,9 +99,9 @@ export function FindComponentProgramDataPda({
   )[0];
 }
 
-export function FindBufferPda(owner: PublicKey) {
+export function FindBufferPda(component: PublicKey) {
   return PublicKey.findProgramAddressSync(
-    [Buffer.from("buffer"), owner.toBuffer()],
+    [Buffer.from("buffer"), component.toBuffer()],
     WORLD_PROGRAM_ID,
   )[0];
 }

+ 3 - 2
clients/typescript/src/world/transactions.ts

@@ -409,6 +409,7 @@ export async function InitializeComponent({
     data: componentPda,
     componentProgram: componentId,
     authority: authority ?? PROGRAM_ID,
+    buffer: FindBufferPda(componentPda),
     anchorRemainingAccounts,
   });
   const transaction = new Transaction().add(instruction);
@@ -489,7 +490,7 @@ async function createApplySystemInstruction({
     return program.methods
       .applyWithSession(SerializeArgs(args))
       .accounts({
-        buffer: FindBufferPda(authority ?? PROGRAM_ID),
+        buffer: FindBufferPda(remainingAccounts[1].pubkey), // First component PDA
         authority: authority ?? PROGRAM_ID,
         boltSystem: systemId,
         sessionToken: session.token,
@@ -502,7 +503,7 @@ async function createApplySystemInstruction({
     return program.methods
       .apply(SerializeArgs(args))
       .accounts({
-        buffer: FindBufferPda(authority ?? PROGRAM_ID),
+        buffer: FindBufferPda(remainingAccounts[1].pubkey), // First component PDA
         authority: authority ?? PROGRAM_ID,
         boltSystem: systemId,
         world,

+ 31 - 31
clients/typescript/test/intermediate-level/acceleration.ts

@@ -64,37 +64,37 @@ export function acceleration(framework: Framework) {
       expect(acc?.owner.toBase58()).to.equal(DELEGATION_PROGRAM_ID.toBase58());
     });
 
-    // TODO: Re-enable this test when ephemeral-validator is properly installed on the CI.
-    // it("Apply Simple Movement System (Up) on Entity 1 on Accelerator 10 times", async () => {
-    //   for (let i = 0; i < 10; i++) {
-    //     let applySystem = await ApplySystem({
-    //       authority: framework.provider.wallet.publicKey,
-    //       systemId: framework.systemSimpleMovement.programId,
-    //       world: framework.worldPda,
-    //       entities: [
-    //         {
-    //           entity: framework.acceleratedEntityPda,
-    //           components: [
-    //             { componentId: framework.exampleComponentPosition.programId },
-    //           ],
-    //         },
-    //       ],
-    //       args: {
-    //         direction: Direction.Up,
-    //       },
-    //     });
+    it("Apply Simple Movement System (Up) on Entity 1 on Accelerator 10 times", async () => {
+      if (process.env.GITHUB_ACTIONS) return;
+      for (let i = 0; i < 10; i++) {
+        let applySystem = await ApplySystem({
+          authority: framework.provider.wallet.publicKey,
+          systemId: framework.systemSimpleMovement.programId,
+          world: framework.worldPda,
+          entities: [
+            {
+              entity: framework.acceleratedEntityPda,
+              components: [
+                { componentId: framework.exampleComponentPosition.programId },
+              ],
+            },
+          ],
+          args: {
+            direction: Direction.Up,
+          },
+        });
 
-    //     await framework.acceleratorProvider.sendAndConfirm(
-    //       applySystem.transaction,
-    //       [],
-    //       {
-    //         skipPreflight: true,
-    //         commitment: "processed",
-    //       },
-    //     );
-    //     // Wait for 50ms
-    //     await new Promise((resolve) => setTimeout(resolve, 50));
-    //   }
-    // });
+        await framework.acceleratorProvider.sendAndConfirm(
+          applySystem.transaction,
+          [],
+          {
+            skipPreflight: true,
+            commitment: "processed",
+          },
+        );
+        // Wait for 50ms
+        await new Promise((resolve) => setTimeout(resolve, 50));
+      }
+    });
   });
 }

+ 6 - 6
clients/typescript/test/low-level/ecs.ts

@@ -189,7 +189,7 @@ export function ecs(framework) {
       const instruction = await framework.worldProgram.methods
         .apply(SerializeArgs({ direction: Direction.Up }))
         .accounts({
-          buffer: FindBufferPda(framework.provider.wallet.publicKey),
+          buffer: FindBufferPda(framework.componentPositionEntity1Pda),
           authority: framework.provider.wallet.publicKey,
           boltSystem: framework.systemSimpleMovement.programId,
           world: framework.worldPda,
@@ -225,7 +225,7 @@ export function ecs(framework) {
       const instruction = await framework.worldProgram.methods
         .apply(SerializeArgs({ direction: Direction.Right }))
         .accounts({
-          buffer: FindBufferPda(framework.provider.wallet.publicKey),
+          buffer: FindBufferPda(framework.componentPositionEntity1Pda),
           authority: framework.provider.wallet.publicKey,
           boltSystem: framework.systemSimpleMovement.programId,
           world: framework.worldPda,
@@ -260,7 +260,7 @@ export function ecs(framework) {
       const instruction = await framework.worldProgram.methods
         .apply(SerializeArgs())
         .accounts({
-          buffer: FindBufferPda(framework.provider.wallet.publicKey),
+          buffer: FindBufferPda(framework.componentPositionEntity1Pda),
           authority: framework.provider.wallet.publicKey,
           boltSystem: framework.systemFly.programId,
           world: framework.worldPda,
@@ -295,7 +295,7 @@ export function ecs(framework) {
       const instruction = await framework.worldProgram.methods
         .apply(SerializeArgs())
         .accounts({
-          buffer: FindBufferPda(framework.provider.wallet.publicKey),
+          buffer: FindBufferPda(framework.componentVelocityEntity1Pda),
           authority: framework.provider.wallet.publicKey,
           boltSystem: framework.systemApplyVelocity.programId,
           world: framework.worldPda,
@@ -349,7 +349,7 @@ export function ecs(framework) {
       const instruction = await framework.worldProgram.methods
         .apply(SerializeArgs())
         .accounts({
-          buffer: FindBufferPda(framework.provider.wallet.publicKey),
+          buffer: FindBufferPda(framework.componentVelocityEntity1Pda),
           authority: framework.provider.wallet.publicKey,
           boltSystem: framework.systemApplyVelocity.programId,
           world: framework.worldPda,
@@ -406,7 +406,7 @@ export function ecs(framework) {
       const instruction = await framework.worldProgram.methods
         .apply(SerializeArgs())
         .accounts({
-          buffer: FindBufferPda(framework.provider.wallet.publicKey),
+          buffer: FindBufferPda(framework.componentPositionEntity4Pda),
           authority: framework.provider.wallet.publicKey,
           boltSystem: framework.systemFly.programId,
           world: framework.worldPda,

+ 2 - 2
clients/typescript/test/low-level/permissioning/component.ts

@@ -66,7 +66,7 @@ export function component(framework) {
       const instruction = await framework.worldProgram.methods
         .apply(SerializeArgs())
         .accounts({
-          buffer: FindBufferPda(keypair.publicKey),
+          buffer: FindBufferPda(component),
           authority: keypair.publicKey,
           boltSystem: framework.systemFly.programId,
           world: framework.worldPda,
@@ -117,7 +117,7 @@ export function component(framework) {
       const instruction = await framework.worldProgram.methods
         .apply(SerializeArgs())
         .accounts({
-          buffer: FindBufferPda(framework.provider.wallet.publicKey),
+          buffer: FindBufferPda(component),
           authority: framework.provider.wallet.publicKey,
           boltSystem: framework.systemFly.programId,
           world: framework.worldPda,

+ 2 - 2
clients/typescript/test/low-level/permissioning/world.ts

@@ -123,7 +123,7 @@ export function world(framework) {
       const instruction = await framework.worldProgram.methods
         .apply(SerializeArgs())
         .accounts({
-          buffer: FindBufferPda(framework.provider.wallet.publicKey),
+          buffer: FindBufferPda(framework.componentPositionEntity1Pda),
           authority: framework.provider.wallet.publicKey,
           boltSystem: framework.systemFly.programId,
           world: framework.worldPda,
@@ -172,7 +172,7 @@ export function world(framework) {
       const instruction = await framework.worldProgram.methods
         .apply(SerializeArgs())
         .accounts({
-          buffer: FindBufferPda(framework.provider.wallet.publicKey),
+          buffer: FindBufferPda(framework.componentPositionEntity1Pda),
           authority: framework.provider.wallet.publicKey,
           boltSystem: framework.systemFly.programId,
           world: framework.worldPda,

+ 2 - 2
clients/typescript/test/low-level/session.ts

@@ -91,7 +91,7 @@ export function session(framework) {
       const instruction = await framework.worldProgram.methods
         .applyWithSession(SerializeArgs())
         .accounts({
-          buffer: FindBufferPda(sessionSigner.publicKey),
+          buffer: FindBufferPda(component),
           authority: sessionSigner.publicKey,
           boltSystem: framework.systemFly.programId,
           world: framework.worldPda,
@@ -181,7 +181,7 @@ export function session(framework) {
       const instruction = await framework.worldProgram.methods
         .applyWithSession(SerializeArgs())
         .accounts({
-          buffer: FindBufferPda(sessionSigner.publicKey),
+          buffer: FindBufferPda(componentWithAuthority),
           authority: sessionSigner.publicKey,
           boltSystem: framework.systemFly.programId,
           world: framework.worldPda,

+ 2 - 1
crates/programs/world/Cargo.toml

@@ -29,4 +29,5 @@ bolt-component.workspace = true
 bolt-system.workspace = true
 solana-security-txt.workspace = true
 tuple-conv.workspace = true
-session-keys.workspace = true
+session-keys.workspace = true
+ephemeral-rollups-sdk.workspace = true

+ 2 - 0
crates/programs/world/src/error.rs

@@ -16,4 +16,6 @@ pub enum WorldError {
     SystemNotApproved,
     #[msg("The component owner does not match the program")]
     InvalidComponentOwner,
+    #[msg("Invalid buffer account")]
+    InvalidBufferAccount
 }

+ 82 - 28
crates/programs/world/src/lib.rs

@@ -264,9 +264,84 @@ pub mod world {
             return Err(WorldError::InvalidAuthority.into());
         }
         bolt_component::cpi::initialize(ctx.accounts.build(&[World::cpi_auth_seeds().as_slice()]))?;
+
+        // Create the buffer account only if it does not already exist.
+        // Subsequent applies reuse the same PDA and only reallocate its data.
+        if ctx.accounts.buffer.lamports() == 0 {
+            let size = 0;
+            let lamports = Rent::get()?.minimum_balance(size);
+            system_program::create_account(
+                CpiContext::new_with_signer(
+                    ctx.accounts.system_program.to_account_info(),
+                    system_program::CreateAccount {
+                        from: ctx.accounts.payer.to_account_info(),
+                        to: ctx.accounts.buffer.to_account_info(),
+                    },
+                    &[&[b"buffer", ctx.accounts.data.key.as_ref(), &[ctx.bumps.buffer]]],
+                ),
+                lamports,
+                size as u64,
+                &ID,
+            )?;
+        }
+
+        Ok(())
+    }
+
+    pub fn delegate_buffer(ctx: Context<DelegateBuffer>, commit_frequency_ms: u32, validator: Option<Pubkey>) -> Result<()> {
+        let pda_seeds: &[&[u8]] = &[b"buffer", &ctx.accounts.component.key().to_bytes()];
+
+        let del_accounts = ephemeral_rollups_sdk::cpi::DelegateAccounts {
+            payer: &ctx.accounts.payer,
+            pda: &ctx.accounts.component_buffer,
+            owner_program: &ctx.accounts.owner_program,
+            buffer: &ctx.accounts.buffer,
+            delegation_record: &ctx.accounts.delegation_record,
+            delegation_metadata: &ctx.accounts.delegation_metadata,
+            delegation_program: &ctx.accounts.delegation_program,
+            system_program: &ctx.accounts.system_program,
+        };
+
+        let config = ephemeral_rollups_sdk::cpi::DelegateConfig {
+            commit_frequency_ms,
+            validator,
+        };
+
+        ephemeral_rollups_sdk::cpi::delegate_account(
+            del_accounts,
+            pda_seeds,
+            config,
+        )?;
+
         Ok(())
     }
 
+    #[derive(Accounts)]
+    pub struct DelegateBuffer<'info> {
+        pub payer: Signer<'info>,
+        /// CHECK:
+        #[account()]
+        pub component: AccountInfo<'info>,
+        /// CHECK:
+        #[account(mut)]
+        pub component_buffer: AccountInfo<'info>,
+        /// CHECK:`
+        pub owner_program: AccountInfo<'info>,
+        /// CHECK:
+        #[account(mut)]
+        pub buffer: AccountInfo<'info>,
+        /// CHECK:`
+        #[account(mut)]
+        pub delegation_record: AccountInfo<'info>,
+        /// CHECK:`
+        #[account(mut)]
+        pub delegation_metadata: AccountInfo<'info>,
+        /// CHECK:`
+        pub delegation_program: AccountInfo<'info>,
+        /// CHECK:`
+        pub system_program: Program<'info, System>,
+    }
+
     pub fn destroy_component(ctx: Context<DestroyComponent>) -> Result<()> {
         bolt_component::cpi::destroy(ctx.accounts.build(&[World::cpi_auth_seeds().as_slice()]))?;
         Ok(())
@@ -278,14 +353,12 @@ pub mod world {
     ) -> Result<()> {
         apply_impl(
             &ctx.accounts.buffer,
-            ctx.bumps.buffer,
             &ctx.accounts.authority,
             &ctx.accounts.world,
             &ctx.accounts.bolt_system,
             &ctx.accounts.cpi_auth,
             ctx.accounts.build(),
             args,
-            &ctx.accounts.system_program,
             None,
             ctx.remaining_accounts,
         )?;
@@ -295,7 +368,7 @@ pub mod world {
     #[derive(Accounts)]
     pub struct Apply<'info> {
         /// CHECK: buffer data check
-        #[account(mut, seeds = [b"buffer", authority.key.as_ref()], bump)]
+        #[account(mut)]
         pub buffer: AccountInfo<'info>,
         /// CHECK: bolt system program check
         #[account()]
@@ -329,14 +402,12 @@ pub mod world {
     ) -> Result<()> {
         apply_impl(
             &ctx.accounts.buffer,
-            ctx.bumps.buffer,
             &ctx.accounts.authority,
             &ctx.accounts.world,
             &ctx.accounts.bolt_system,
             &ctx.accounts.cpi_auth,
             ctx.accounts.build(),
             args,
-            &ctx.accounts.system_program,
             Some(&ctx.accounts.session_token),
             ctx.remaining_accounts,
         )?;
@@ -346,7 +417,7 @@ pub mod world {
     #[derive(Accounts)]
     pub struct ApplyWithSession<'info> {
         /// CHECK: buffer data check
-        #[account(mut, seeds = [b"buffer", authority.key.as_ref()], bump)]
+        #[account(mut)]
         pub buffer: AccountInfo<'info>,
         /// CHECK: bolt system program check
         #[account()]
@@ -380,14 +451,12 @@ pub mod world {
 #[allow(clippy::type_complexity, clippy::too_many_arguments)]
 fn apply_impl<'info>(
     buffer: &AccountInfo<'info>,
-    buffer_bump: u8,
     authority: &Signer<'info>,
     world: &Account<'info, World>,
     bolt_system: &UncheckedAccount<'info>,
     cpi_auth: &UncheckedAccount<'info>,
     cpi_context: CpiContext<'_, '_, '_, 'info, bolt_system::cpi::accounts::BoltExecute<'info>>,
     args: Vec<u8>,
-    system_program: &Program<'info, System>,
     session_token: Option<&Account<'info, session_keys::SessionToken>>,
     remaining_accounts: &[AccountInfo<'info>],
 ) -> Result<()> {
@@ -403,6 +472,8 @@ fn apply_impl<'info>(
         return Err(WorldError::SystemNotApproved.into());
     }
 
+    require!(buffer.data_len() == 0, WorldError::InvalidBufferAccount);
+
     let index = remaining_accounts.iter().position(|x| x.key() == ID).unwrap_or(remaining_accounts.len());
 
     // Authority check against component metadata (partial deserialize)
@@ -451,26 +522,6 @@ fn apply_impl<'info>(
         }
     }
 
-    // Create the buffer account only if it does not already exist.
-    // Subsequent applies reuse the same PDA and only reallocate its data.
-    if buffer.lamports() == 0 {
-        let size = 0;
-        let lamports = Rent::get()?.minimum_balance(size);
-        system_program::create_account(
-            CpiContext::new_with_signer(
-                system_program.to_account_info(),
-                system_program::CreateAccount {
-                    from: authority.to_account_info(),
-                    to: buffer.to_account_info(),
-                },
-                &[&[b"buffer", authority.key.as_ref(), &[buffer_bump]]],
-            ),
-            lamports,
-            size as u64,
-            &ID,
-        )?;
-    }
-
     for pair in remaining_accounts[..index].chunks(2) {
         let [program, component] = pair else { continue };
         buffer.realloc(component.data_len(), false)?;
@@ -657,6 +708,9 @@ pub struct InitializeComponent<'info> {
     /// CHECK: cpi auth check
     pub cpi_auth: UncheckedAccount<'info>,
     pub system_program: Program<'info, System>,
+    /// CHECK: Buffer account
+    #[account(mut, seeds = [b"buffer", data.key.as_ref()], bump)]
+    pub buffer: UncheckedAccount<'info>
 }
 
 impl<'info> InitializeComponent<'info> {

+ 1 - 1
docs/REPORT.md

@@ -4,6 +4,6 @@ xychart
     title "Bolt Apply System Cost"
     x-axis ["1C-CPIs:5","2C-CPIs:9","3C-CPIs:13","4C-CPIs:17","5C-CPIs:21","6C-CPIs:25","7C-CPIs:29","8C-CPIs:33","9C-CPIs:0","10C-CPIs:0"]
     y-axis "CU" 5000 --> 200000
-    bar [25689,43441,61369,79356,97160,114989,133114,151090,0,0]
+    bar [24092,41860,59800,77799,95615,113456,131593,149581,0,0]
     bar [6920,12931,18925,24915,30909,36900,42977,49059,0,0]
 ```