diff --git a/Cargo.lock b/Cargo.lock index b8c95e27..43b79445 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -799,6 +799,7 @@ dependencies = [ "bolt-utils", "proc-macro2", "quote", + "sha2 0.10.8", "syn 1.0.109", ] @@ -833,6 +834,7 @@ dependencies = [ name = "bolt-attribute-bolt-program" version = "0.2.4" dependencies = [ + "bolt-utils", "proc-macro2", "quote", "syn 1.0.109", @@ -842,6 +844,7 @@ dependencies = [ name = "bolt-attribute-bolt-system" version = "0.2.4" dependencies = [ + "bolt-utils", "proc-macro2", "quote", "syn 1.0.109", @@ -922,6 +925,7 @@ dependencies = [ name = "bolt-types" version = "0.2.4" dependencies = [ + "anchor-lang", "bolt-lang", ] @@ -7085,6 +7089,8 @@ dependencies = [ "anchor-lang", "bolt-component", "bolt-system", + "ephemeral-rollups-sdk", + "session-keys", "solana-security-txt", "tuple-conv", ] diff --git a/Cargo.toml b/Cargo.toml index c1779191..fa030665 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,6 +64,7 @@ which = "^7" tokio = { version = "^1", features = ["full"] } sysinfo = "^0" bytemuck_derive = "^1" +sha2 = { version = "^0.10" } [profile.release] overflow-checks = true diff --git a/clients/csharp/Solana.Unity.Bolt.Test/AccelerationTest.cs b/clients/csharp/Solana.Unity.Bolt.Test/AccelerationTest.cs index b9a3649c..ed6a6195 100644 --- a/clients/csharp/Solana.Unity.Bolt.Test/AccelerationTest.cs +++ b/clients/csharp/Solana.Unity.Bolt.Test/AccelerationTest.cs @@ -21,10 +21,12 @@ await Profiler.Run("InitializePositionComponentOnAccelerationEntity", async () = 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) { @@ -47,10 +49,13 @@ public static async Task DelegateComponent(Framework framework) { 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)); diff --git a/clients/csharp/Solana.Unity.Bolt.Test/ECSTest.cs b/clients/csharp/Solana.Unity.Bolt.Test/ECSTest.cs index 6682eb6f..9e626d17 100644 --- a/clients/csharp/Solana.Unity.Bolt.Test/ECSTest.cs +++ b/clients/csharp/Solana.Unity.Bolt.Test/ECSTest.cs @@ -108,9 +108,11 @@ public static async Task CheckPositionOnEntity1IsDefault(Framework framework) { public static async Task ApplySimpleMovementSystemUpOnEntity1(Framework framework) { var apply = new ApplyAccounts() { + CpiAuth = WorldProgram.FindCpiAuthPda(), Authority = framework.Wallet.Account.PublicKey, BoltSystem = framework.SystemSimpleMovement, World = framework.WorldPda, + Buffer = WorldProgram.FindBufferPda(framework.ComponentPositionEntity1Pda), }; var instruction = WorldProgram.Apply(apply, Bolt.World.SerializeArgs(new { direction = "Up" })); instruction.Keys.Add(AccountMeta.ReadOnly(framework.ExampleComponentPosition, false)); diff --git a/clients/csharp/Solana.Unity.Bolt.Test/Framework.cs b/clients/csharp/Solana.Unity.Bolt.Test/Framework.cs index f8311b7c..430d7707 100644 --- a/clients/csharp/Solana.Unity.Bolt.Test/Framework.cs +++ b/clients/csharp/Solana.Unity.Bolt.Test/Framework.cs @@ -96,7 +96,7 @@ public async Task SendAndConfirmInstruction(IRpcClient client, Transacti .AddInstruction(instruction) .Build(signers); - var signature = await client.SendTransactionAsync(transaction, true, Commitment.Processed); + var signature = await client.SendTransactionAsync(transaction, false, Commitment.Processed); var confirmed = await client.ConfirmTransaction(signature.Result, Commitment.Processed); if (signature.WasSuccessful && confirmed) { @@ -107,6 +107,7 @@ public async Task SendAndConfirmInstruction(IRpcClient client, Transacti if (signature.ErrorData != null) { errorMessage += "\n" + string.Join("\n", signature.ErrorData.Logs); } + Console.WriteLine(errorMessage); throw new Exception(errorMessage); } diff --git a/clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/DelegateComponent.cs b/clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/DelegateComponent.cs index ad323339..da802551 100644 --- a/clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/DelegateComponent.cs +++ b/clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/DelegateComponent.cs @@ -20,12 +20,18 @@ public class DelegateComponentInstruction { } public static async Task DelegateComponent(PublicKey payer, PublicKey entity, PublicKey componentId, string seed = "") { - var account = WorldProgram.FindComponentPda(componentId, entity, seed); - var bufferPda = WorldProgram.FindBufferPda(account, componentId); - var delegationRecord = WorldProgram.FindDelegationProgramPda("delegation", account); - var delegationMetadata = WorldProgram.FindDelegationProgramPda("delegation-metadata", account); + var componentPda = WorldProgram.FindComponentPda(componentId, entity, seed); + var componentBuffer = WorldProgram.FindBufferPda(componentPda); - byte[] discriminator = new byte[] { 90, 147, 75, 178, 85, 88, 4, 137 }; + var componentDelegationRecord = WorldProgram.FindDelegationProgramPda("delegation", componentPda); + var componentDelegationMetadata = WorldProgram.FindDelegationProgramPda("delegation-metadata", componentPda); + + var worldProgram = new PublicKey(WorldProgram.ID); + var bufferDelegationRecord = WorldProgram.FindDelegationProgramPda("delegation", componentBuffer); + var bufferDelegationMetadata = WorldProgram.FindDelegationProgramPda("delegation-metadata", componentBuffer); + var bufferBuffer = WorldProgram.FindBufferPda(componentBuffer, worldProgram); + + byte[] discriminator = new byte[] { 191, 212, 179, 193, 178, 94, 119, 93 }; uint commitFrequencyMs = 0; byte[] commitFrequencyBytes = BitConverter.GetBytes(commitFrequencyMs); if (!BitConverter.IsLittleEndian) Array.Reverse(commitFrequencyBytes); @@ -38,22 +44,27 @@ public static async Task DelegateComponent(PublicK Array.Copy(validator, 0, data, discriminator.Length + commitFrequencyBytes.Length, validator.Length); TransactionInstruction instruction = new TransactionInstruction() { - ProgramId = componentId, + ProgramId = new PublicKey(WorldProgram.ID), Keys = new List() { - AccountMeta.ReadOnly(payer, true), - AccountMeta.ReadOnly(entity, false), - AccountMeta.Writable(account, false), + AccountMeta.Writable(payer, true), + AccountMeta.Writable(componentPda, false), + AccountMeta.Writable(componentBuffer, false), AccountMeta.ReadOnly(componentId, false), - AccountMeta.Writable(bufferPda, false), - AccountMeta.Writable(delegationRecord, false), - AccountMeta.Writable(delegationMetadata, false), + AccountMeta.Writable(WorldProgram.FindBufferPda(componentPda, componentId), false), + AccountMeta.Writable(componentDelegationRecord, false), + AccountMeta.Writable(componentDelegationMetadata, false), AccountMeta.ReadOnly(WorldProgram.DelegationProgram, false), AccountMeta.ReadOnly(SystemProgram.ProgramIdKey, false), + AccountMeta.ReadOnly(entity, false), + AccountMeta.ReadOnly(worldProgram, false), + AccountMeta.Writable(bufferBuffer, false), + AccountMeta.Writable(bufferDelegationRecord, false), + AccountMeta.Writable(bufferDelegationMetadata, false), }, Data = data, }; return new DelegateComponentInstruction() { - Pda = WorldProgram.FindDelegationProgramPda(seed, entity), + Pda = componentPda, Instruction = instruction, }; } diff --git a/clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/DestroyComponent.cs b/clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/DestroyComponent.cs index 8437c6cc..c793cee0 100644 --- a/clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/DestroyComponent.cs +++ b/clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/DestroyComponent.cs @@ -30,6 +30,7 @@ public static async Task DestroyComponent(PublicKey public static async Task DestroyComponent(PublicKey authority, PublicKey receiver, PublicKey entity, PublicKey componentProgram, PublicKey componentPda) { var componentProgramData = WorldProgram.FindComponentProgramDataPda(componentProgram); var destroyComponent = new DestroyComponentAccounts() { + CpiAuth = WorldProgram.FindCpiAuthPda(), Authority = authority, Receiver = receiver, Entity = entity, diff --git a/clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/InitializeComponent.cs b/clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/InitializeComponent.cs index 6d297455..394589de 100644 --- a/clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/InitializeComponent.cs +++ b/clients/csharp/Solana.Unity.Bolt/WorldProgram/Bolt/InitializeComponent.cs @@ -30,11 +30,13 @@ public static async Task InitializeComponent(Pub public static async Task InitializeComponent(PublicKey payer, PublicKey entity, PublicKey componentId, PublicKey componentPda, PublicKey authority = null) { var initializeComponent = new InitializeComponentAccounts() { + CpiAuth = WorldProgram.FindCpiAuthPda(), Payer = payer, 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() { diff --git a/clients/csharp/Solana.Unity.Bolt/WorldProgram/Generated.cs b/clients/csharp/Solana.Unity.Bolt/WorldProgram/Generated.cs index 9d5cf637..fa614b82 100644 --- a/clients/csharp/Solana.Unity.Bolt/WorldProgram/Generated.cs +++ b/clients/csharp/Solana.Unity.Bolt/WorldProgram/Generated.cs @@ -1,568 +1,651 @@ -#pragma warning disable CS1591 - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Threading.Tasks; -using Solana.Unity; -using Solana.Unity.Programs.Abstract; -using Solana.Unity.Programs.Utilities; -using Solana.Unity.Rpc; -using Solana.Unity.Rpc.Builders; -using Solana.Unity.Rpc.Core.Http; -using Solana.Unity.Rpc.Core.Sockets; -using Solana.Unity.Rpc.Types; -using Solana.Unity.Wallet; -using World; -using World.Program; -using World.Errors; -using World.Accounts; -using World.Types; - -namespace World -{ - namespace Accounts - { - public partial class Entity - { - public static ulong ACCOUNT_DISCRIMINATOR => 1751670451238706478UL; - public static ReadOnlySpan ACCOUNT_DISCRIMINATOR_BYTES => new byte[]{46, 157, 161, 161, 254, 46, 79, 24}; - public static string ACCOUNT_DISCRIMINATOR_B58 => "8oEQa6zH67R"; - public ulong Id { get; set; } - - public static Entity Deserialize(ReadOnlySpan _data) - { - int offset = 0; - ulong accountHashValue = _data.GetU64(offset); - offset += 8; - if (accountHashValue != ACCOUNT_DISCRIMINATOR) - { - return null; - } - - Entity result = new Entity(); - result.Id = _data.GetU64(offset); - offset += 8; - return result; - } - } - - public partial class Registry - { - public static ulong ACCOUNT_DISCRIMINATOR => 15779688099924061743UL; - public static ReadOnlySpan ACCOUNT_DISCRIMINATOR_BYTES => new byte[]{47, 174, 110, 246, 184, 182, 252, 218}; - public static string ACCOUNT_DISCRIMINATOR_B58 => "8ya1XGY4XBP"; - public ulong Worlds { get; set; } - - public static Registry Deserialize(ReadOnlySpan _data) - { - int offset = 0; - ulong accountHashValue = _data.GetU64(offset); - offset += 8; - if (accountHashValue != ACCOUNT_DISCRIMINATOR) - { - return null; - } - - Registry result = new Registry(); - result.Worlds = _data.GetU64(offset); - offset += 8; - return result; - } - } - - public partial class World - { - public static ulong ACCOUNT_DISCRIMINATOR => 8978805993381703057UL; - public static ReadOnlySpan ACCOUNT_DISCRIMINATOR_BYTES => new byte[]{145, 45, 170, 174, 122, 32, 155, 124}; - public static string ACCOUNT_DISCRIMINATOR_B58 => "RHQudtaQtu1"; - public ulong Id { get; set; } - - public ulong Entities { get; set; } - - public PublicKey[] Authorities { get; set; } - - public bool Permissionless { get; set; } - - public byte[] Systems { get; set; } - - public static World Deserialize(ReadOnlySpan _data) - { - int offset = 0; - ulong accountHashValue = _data.GetU64(offset); - offset += 8; - if (accountHashValue != ACCOUNT_DISCRIMINATOR) - { - return null; - } - - World result = new World(); - result.Id = _data.GetU64(offset); - offset += 8; - result.Entities = _data.GetU64(offset); - offset += 8; - int resultAuthoritiesLength = (int)_data.GetU32(offset); - offset += 4; - result.Authorities = new PublicKey[resultAuthoritiesLength]; - for (uint resultAuthoritiesIdx = 0; resultAuthoritiesIdx < resultAuthoritiesLength; resultAuthoritiesIdx++) - { - result.Authorities[resultAuthoritiesIdx] = _data.GetPubKey(offset); - offset += 32; - } - - result.Permissionless = _data.GetBool(offset); - offset += 1; - int resultSystemsLength = (int)_data.GetU32(offset); - offset += 4; - result.Systems = _data.GetBytes(offset, resultSystemsLength); - offset += resultSystemsLength; - return result; - } - } - } - - namespace Errors - { - public enum WorldErrorKind : uint - { - InvalidAuthority = 6000U, - InvalidSystemOutput = 6001U, - WorldAccountMismatch = 6002U, - TooManyAuthorities = 6003U, - AuthorityNotFound = 6004U, - SystemNotApproved = 6005U - } - } - - namespace Types - { - } - - public partial class WorldClient : TransactionalBaseClient - { - public WorldClient(IRpcClient rpcClient, IStreamingRpcClient streamingRpcClient, PublicKey programId = null) : base(rpcClient, streamingRpcClient, programId ?? new PublicKey(WorldProgram.ID)) - { - } - - public async Task>> GetEntitysAsync(string programAddress = WorldProgram.ID, Commitment commitment = Commitment.Confirmed) - { - var list = new List{new Solana.Unity.Rpc.Models.MemCmp{Bytes = Entity.ACCOUNT_DISCRIMINATOR_B58, Offset = 0}}; - var res = await RpcClient.GetProgramAccountsAsync(programAddress, commitment, memCmpList: list); - if (!res.WasSuccessful || !(res.Result?.Count > 0)) - return new Solana.Unity.Programs.Models.ProgramAccountsResultWrapper>(res); - List resultingAccounts = new List(res.Result.Count); - resultingAccounts.AddRange(res.Result.Select(result => Entity.Deserialize(Convert.FromBase64String(result.Account.Data[0])))); - return new Solana.Unity.Programs.Models.ProgramAccountsResultWrapper>(res, resultingAccounts); - } - - public async Task>> GetRegistrysAsync(string programAddress = WorldProgram.ID, Commitment commitment = Commitment.Confirmed) - { - var list = new List{new Solana.Unity.Rpc.Models.MemCmp{Bytes = Registry.ACCOUNT_DISCRIMINATOR_B58, Offset = 0}}; - var res = await RpcClient.GetProgramAccountsAsync(programAddress, commitment, memCmpList: list); - if (!res.WasSuccessful || !(res.Result?.Count > 0)) - return new Solana.Unity.Programs.Models.ProgramAccountsResultWrapper>(res); - List resultingAccounts = new List(res.Result.Count); - resultingAccounts.AddRange(res.Result.Select(result => Registry.Deserialize(Convert.FromBase64String(result.Account.Data[0])))); - return new Solana.Unity.Programs.Models.ProgramAccountsResultWrapper>(res, resultingAccounts); - } - - public async Task>> GetWorldsAsync(string programAddress = WorldProgram.ID, Commitment commitment = Commitment.Confirmed) - { - var list = new List{new Solana.Unity.Rpc.Models.MemCmp{Bytes = World.Accounts.World.ACCOUNT_DISCRIMINATOR_B58, Offset = 0}}; - var res = await RpcClient.GetProgramAccountsAsync(programAddress, commitment, memCmpList: list); - if (!res.WasSuccessful || !(res.Result?.Count > 0)) - return new Solana.Unity.Programs.Models.ProgramAccountsResultWrapper>(res); - List resultingAccounts = new List(res.Result.Count); - resultingAccounts.AddRange(res.Result.Select(result => World.Accounts.World.Deserialize(Convert.FromBase64String(result.Account.Data[0])))); - return new Solana.Unity.Programs.Models.ProgramAccountsResultWrapper>(res, resultingAccounts); - } - - public async Task> GetEntityAsync(string accountAddress, Commitment commitment = Commitment.Finalized) - { - var res = await RpcClient.GetAccountInfoAsync(accountAddress, commitment); - if (!res.WasSuccessful) - return new Solana.Unity.Programs.Models.AccountResultWrapper(res); - var resultingAccount = Entity.Deserialize(Convert.FromBase64String(res.Result.Value.Data[0])); - return new Solana.Unity.Programs.Models.AccountResultWrapper(res, resultingAccount); - } - - public async Task> GetRegistryAsync(string accountAddress, Commitment commitment = Commitment.Finalized) - { - var res = await RpcClient.GetAccountInfoAsync(accountAddress, commitment); - if (!res.WasSuccessful) - return new Solana.Unity.Programs.Models.AccountResultWrapper(res); - var resultingAccount = Registry.Deserialize(Convert.FromBase64String(res.Result.Value.Data[0])); - return new Solana.Unity.Programs.Models.AccountResultWrapper(res, resultingAccount); - } - - public async Task> GetWorldAsync(string accountAddress, Commitment commitment = Commitment.Finalized) - { - var res = await RpcClient.GetAccountInfoAsync(accountAddress, commitment); - if (!res.WasSuccessful) - return new Solana.Unity.Programs.Models.AccountResultWrapper(res); - var resultingAccount = World.Accounts.World.Deserialize(Convert.FromBase64String(res.Result.Value.Data[0])); - return new Solana.Unity.Programs.Models.AccountResultWrapper(res, resultingAccount); - } - - public async Task SubscribeEntityAsync(string accountAddress, Action, Entity> callback, Commitment commitment = Commitment.Finalized) - { - SubscriptionState res = await StreamingRpcClient.SubscribeAccountInfoAsync(accountAddress, (s, e) => - { - Entity parsingResult = null; - if (e.Value?.Data?.Count > 0) - parsingResult = Entity.Deserialize(Convert.FromBase64String(e.Value.Data[0])); - callback(s, e, parsingResult); - }, commitment); - return res; - } - - public async Task SubscribeRegistryAsync(string accountAddress, Action, Registry> callback, Commitment commitment = Commitment.Finalized) - { - SubscriptionState res = await StreamingRpcClient.SubscribeAccountInfoAsync(accountAddress, (s, e) => - { - Registry parsingResult = null; - if (e.Value?.Data?.Count > 0) - parsingResult = Registry.Deserialize(Convert.FromBase64String(e.Value.Data[0])); - callback(s, e, parsingResult); - }, commitment); - return res; - } - - public async Task SubscribeWorldAsync(string accountAddress, Action, World.Accounts.World> callback, Commitment commitment = Commitment.Finalized) - { - SubscriptionState res = await StreamingRpcClient.SubscribeAccountInfoAsync(accountAddress, (s, e) => - { - World.Accounts.World parsingResult = null; - if (e.Value?.Data?.Count > 0) - parsingResult = World.Accounts.World.Deserialize(Convert.FromBase64String(e.Value.Data[0])); - callback(s, e, parsingResult); - }, commitment); - return res; - } - - protected override Dictionary> BuildErrorsDictionary() - { - return new Dictionary>{{6000U, new ProgramError(WorldErrorKind.InvalidAuthority, "Invalid authority for instruction")}, {6001U, new ProgramError(WorldErrorKind.InvalidSystemOutput, "Invalid system output")}, {6002U, new ProgramError(WorldErrorKind.WorldAccountMismatch, "The provided world account does not match the expected PDA.")}, {6003U, new ProgramError(WorldErrorKind.TooManyAuthorities, "Exceed the maximum number of authorities.")}, {6004U, new ProgramError(WorldErrorKind.AuthorityNotFound, "The provided authority not found")}, {6005U, new ProgramError(WorldErrorKind.SystemNotApproved, "The system is not approved in this world instance")}, }; - } - } - - namespace Program - { - public class AddAuthorityAccounts - { - public PublicKey Authority { get; set; } - - public PublicKey NewAuthority { get; set; } - - public PublicKey World { get; set; } - - public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); - } - - public class AddEntityAccounts - { - public PublicKey Payer { get; set; } - - public PublicKey Entity { get; set; } - - public PublicKey World { get; set; } - - public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); - } - - public class ApplyAccounts - { - public PublicKey BoltSystem { get; set; } - - public PublicKey Authority { get; set; } - - public PublicKey CpiAuth { get; set; } = new PublicKey("B2f2y3QTBv346wE6nWKor72AUhUvFF6mPk7TWCF2QVhi"); - public PublicKey World { get; set; } - } - - public class ApplyWithSessionAccounts - { - public PublicKey BoltSystem { get; set; } - - public PublicKey Authority { get; set; } - - public PublicKey CpiAuth { get; set; } = new PublicKey("B2f2y3QTBv346wE6nWKor72AUhUvFF6mPk7TWCF2QVhi"); - public PublicKey World { get; set; } - - public PublicKey SessionToken { get; set; } - } - - public class ApproveSystemAccounts - { - public PublicKey Authority { get; set; } - - public PublicKey World { get; set; } - - public PublicKey System { get; set; } - - public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); - } - - public class DestroyComponentAccounts - { - public PublicKey Authority { get; set; } - - public PublicKey Receiver { get; set; } - - public PublicKey ComponentProgram { get; set; } - - public PublicKey ComponentProgramData { get; set; } - - public PublicKey Entity { get; set; } - - public PublicKey Component { get; set; } - - public PublicKey CpiAuth { get; set; } = new PublicKey("B2f2y3QTBv346wE6nWKor72AUhUvFF6mPk7TWCF2QVhi"); - public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); - } - - public class InitializeComponentAccounts - { - public PublicKey Payer { get; set; } - - public PublicKey Data { get; set; } - - public PublicKey Entity { get; set; } - - public PublicKey ComponentProgram { get; set; } - - public PublicKey Authority { get; set; } - - public PublicKey CpiAuth { get; set; } = new PublicKey("B2f2y3QTBv346wE6nWKor72AUhUvFF6mPk7TWCF2QVhi"); - public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); - } - - public class InitializeNewWorldAccounts - { - public PublicKey Payer { get; set; } - - public PublicKey World { get; set; } - - public PublicKey Registry { get; set; } - - public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); - } - - public class InitializeRegistryAccounts - { - public PublicKey Registry { get; set; } - - public PublicKey Payer { get; set; } - - public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); - } - - public class RemoveAuthorityAccounts - { - public PublicKey Authority { get; set; } - - public PublicKey AuthorityToDelete { get; set; } - - public PublicKey World { get; set; } - - public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); - } - - public class RemoveSystemAccounts - { - public PublicKey Authority { get; set; } - - public PublicKey World { get; set; } - - public PublicKey System { get; set; } - - public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); - } - - public partial class WorldProgram - { - public const string ID = "WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n"; - public static Solana.Unity.Rpc.Models.TransactionInstruction AddAuthority(AddAuthorityAccounts accounts, ulong world_id, PublicKey programId = null) - { - programId ??= new(ID); - List keys = new() - {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Authority, true), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.NewAuthority, false), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.World, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; - byte[] _data = new byte[1200]; - int offset = 0; - _data.WriteU64(13217455069452700133UL, offset); - offset += 8; - _data.WriteU64(world_id, offset); - offset += 8; - byte[] resultData = new byte[offset]; - Array.Copy(_data, resultData, offset); - return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; - } - - public static Solana.Unity.Rpc.Models.TransactionInstruction AddEntity(AddEntityAccounts accounts, byte[] extra_seed, PublicKey programId = null) - { - programId ??= new(ID); - List keys = new() - {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Payer, true), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Entity, false), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.World, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; - byte[] _data = new byte[1200]; - int offset = 0; - _data.WriteU64(4121062988444201379UL, offset); - offset += 8; - if (extra_seed != null) - { - _data.WriteU8(1, offset); - offset += 1; - _data.WriteS32(extra_seed.Length, offset); - offset += 4; - _data.WriteSpan(extra_seed, offset); - offset += extra_seed.Length; - } - else - { - _data.WriteU8(0, offset); - offset += 1; - } - - byte[] resultData = new byte[offset]; - Array.Copy(_data, resultData, offset); - return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; - } - - public static Solana.Unity.Rpc.Models.TransactionInstruction Apply(ApplyAccounts accounts, byte[] args, PublicKey programId = null) - { - programId ??= new(ID); - List keys = new() - {Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.BoltSystem, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.Authority, true), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.CpiAuth, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.World, false)}; - byte[] _data = new byte[1200]; - int offset = 0; - _data.WriteU64(16258613031726085112UL, offset); - offset += 8; - _data.WriteS32(args.Length, offset); - offset += 4; - _data.WriteSpan(args, offset); - offset += args.Length; - byte[] resultData = new byte[offset]; - Array.Copy(_data, resultData, offset); - return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; - } - - public static Solana.Unity.Rpc.Models.TransactionInstruction ApplyWithSession(ApplyWithSessionAccounts accounts, byte[] args, PublicKey programId = null) - { - programId ??= new(ID); - List keys = new() - {Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.BoltSystem, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.Authority, true), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.CpiAuth, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.World, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SessionToken, false)}; - byte[] _data = new byte[1200]; - int offset = 0; - _data.WriteU64(7459768094276011477UL, offset); - offset += 8; - _data.WriteS32(args.Length, offset); - offset += 4; - _data.WriteSpan(args, offset); - offset += args.Length; - byte[] resultData = new byte[offset]; - Array.Copy(_data, resultData, offset); - return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; - } - - public static Solana.Unity.Rpc.Models.TransactionInstruction ApproveSystem(ApproveSystemAccounts accounts, PublicKey programId = null) - { - programId ??= new(ID); - List keys = new() - {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Authority, true), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.World, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.System, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; - byte[] _data = new byte[1200]; - int offset = 0; - _data.WriteU64(8777308090533520754UL, offset); - offset += 8; - byte[] resultData = new byte[offset]; - Array.Copy(_data, resultData, offset); - return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; - } - - public static Solana.Unity.Rpc.Models.TransactionInstruction DestroyComponent(DestroyComponentAccounts accounts, PublicKey programId = null) - { - programId ??= new(ID); - List keys = new() - {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Authority, true), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Receiver, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.ComponentProgram, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.ComponentProgramData, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.Entity, false), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Component, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.CpiAuth, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; - byte[] _data = new byte[1200]; - int offset = 0; - _data.WriteU64(5321952129328727336UL, offset); - offset += 8; - byte[] resultData = new byte[offset]; - Array.Copy(_data, resultData, offset); - return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; - } - - public static Solana.Unity.Rpc.Models.TransactionInstruction InitializeComponent(InitializeComponentAccounts accounts, PublicKey programId = null) - { - programId ??= new(ID); - List 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)}; - byte[] _data = new byte[1200]; - int offset = 0; - _data.WriteU64(2179155133888827172UL, offset); - offset += 8; - byte[] resultData = new byte[offset]; - Array.Copy(_data, resultData, offset); - return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; - } - - public static Solana.Unity.Rpc.Models.TransactionInstruction InitializeNewWorld(InitializeNewWorldAccounts accounts, PublicKey programId = null) - { - programId ??= new(ID); - List keys = new() - {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Payer, true), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.World, false), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Registry, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; - byte[] _data = new byte[1200]; - int offset = 0; - _data.WriteU64(7118163274173538327UL, offset); - offset += 8; - byte[] resultData = new byte[offset]; - Array.Copy(_data, resultData, offset); - return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; - } - - public static Solana.Unity.Rpc.Models.TransactionInstruction InitializeRegistry(InitializeRegistryAccounts accounts, PublicKey programId = null) - { - programId ??= new(ID); - List keys = new() - {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Registry, false), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Payer, true), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; - byte[] _data = new byte[1200]; - int offset = 0; - _data.WriteU64(4321548737212364221UL, offset); - offset += 8; - byte[] resultData = new byte[offset]; - Array.Copy(_data, resultData, offset); - return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; - } - - public static Solana.Unity.Rpc.Models.TransactionInstruction RemoveAuthority(RemoveAuthorityAccounts accounts, ulong world_id, PublicKey programId = null) - { - programId ??= new(ID); - List keys = new() - {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Authority, true), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.AuthorityToDelete, false), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.World, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; - byte[] _data = new byte[1200]; - int offset = 0; - _data.WriteU64(15585545156648003826UL, offset); - offset += 8; - _data.WriteU64(world_id, offset); - offset += 8; - byte[] resultData = new byte[offset]; - Array.Copy(_data, resultData, offset); - return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; - } - - public static Solana.Unity.Rpc.Models.TransactionInstruction RemoveSystem(RemoveSystemAccounts accounts, PublicKey programId = null) - { - programId ??= new(ID); - List keys = new() - {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Authority, true), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.World, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.System, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; - byte[] _data = new byte[1200]; - int offset = 0; - _data.WriteU64(8688994685429436634UL, offset); - offset += 8; - byte[] resultData = new byte[offset]; - Array.Copy(_data, resultData, offset); - return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; - } - } - } +#pragma warning disable CS1591 + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Threading.Tasks; +using Solana.Unity; +using Solana.Unity.Programs.Abstract; +using Solana.Unity.Programs.Utilities; +using Solana.Unity.Rpc; +using Solana.Unity.Rpc.Builders; +using Solana.Unity.Rpc.Core.Http; +using Solana.Unity.Rpc.Core.Sockets; +using Solana.Unity.Rpc.Types; +using Solana.Unity.Wallet; +using World; +using World.Program; +using World.Errors; +using World.Accounts; +using World.Types; + +namespace World +{ + namespace Accounts + { + public partial class Entity + { + public static ulong ACCOUNT_DISCRIMINATOR => 1751670451238706478UL; + public static ReadOnlySpan ACCOUNT_DISCRIMINATOR_BYTES => new byte[]{46, 157, 161, 161, 254, 46, 79, 24}; + public static string ACCOUNT_DISCRIMINATOR_B58 => "8oEQa6zH67R"; + public ulong Id { get; set; } + + public static Entity Deserialize(ReadOnlySpan _data) + { + int offset = 0; + ulong accountHashValue = _data.GetU64(offset); + offset += 8; + if (accountHashValue != ACCOUNT_DISCRIMINATOR) + { + return null; + } + + Entity result = new Entity(); + result.Id = _data.GetU64(offset); + offset += 8; + return result; + } + } + + public partial class Registry + { + public static ulong ACCOUNT_DISCRIMINATOR => 15779688099924061743UL; + public static ReadOnlySpan ACCOUNT_DISCRIMINATOR_BYTES => new byte[]{47, 174, 110, 246, 184, 182, 252, 218}; + public static string ACCOUNT_DISCRIMINATOR_B58 => "8ya1XGY4XBP"; + public ulong Worlds { get; set; } + + public static Registry Deserialize(ReadOnlySpan _data) + { + int offset = 0; + ulong accountHashValue = _data.GetU64(offset); + offset += 8; + if (accountHashValue != ACCOUNT_DISCRIMINATOR) + { + return null; + } + + Registry result = new Registry(); + result.Worlds = _data.GetU64(offset); + offset += 8; + return result; + } + } + + public partial class SessionToken + { + public static ulong ACCOUNT_DISCRIMINATOR => 1081168673100727529UL; + public static ReadOnlySpan ACCOUNT_DISCRIMINATOR_BYTES => new byte[]{233, 4, 115, 14, 46, 21, 1, 15}; + public static string ACCOUNT_DISCRIMINATOR_B58 => "fyZWTdUu1pS"; + public PublicKey Authority { get; set; } + + public PublicKey TargetProgram { get; set; } + + public PublicKey SessionSigner { get; set; } + + public long ValidUntil { get; set; } + + public static SessionToken Deserialize(ReadOnlySpan _data) + { + int offset = 0; + ulong accountHashValue = _data.GetU64(offset); + offset += 8; + if (accountHashValue != ACCOUNT_DISCRIMINATOR) + { + return null; + } + + SessionToken result = new SessionToken(); + result.Authority = _data.GetPubKey(offset); + offset += 32; + result.TargetProgram = _data.GetPubKey(offset); + offset += 32; + result.SessionSigner = _data.GetPubKey(offset); + offset += 32; + result.ValidUntil = _data.GetS64(offset); + offset += 8; + return result; + } + } + + public partial class World + { + public static ulong ACCOUNT_DISCRIMINATOR => 8978805993381703057UL; + public static ReadOnlySpan ACCOUNT_DISCRIMINATOR_BYTES => new byte[]{145, 45, 170, 174, 122, 32, 155, 124}; + public static string ACCOUNT_DISCRIMINATOR_B58 => "RHQudtaQtu1"; + public ulong Id { get; set; } + + public ulong Entities { get; set; } + + public PublicKey[] Authorities { get; set; } + + public bool Permissionless { get; set; } + + public byte[] Systems { get; set; } + + public static World Deserialize(ReadOnlySpan _data) + { + int offset = 0; + ulong accountHashValue = _data.GetU64(offset); + offset += 8; + if (accountHashValue != ACCOUNT_DISCRIMINATOR) + { + return null; + } + + World result = new World(); + result.Id = _data.GetU64(offset); + offset += 8; + result.Entities = _data.GetU64(offset); + offset += 8; + int resultAuthoritiesLength = (int)_data.GetU32(offset); + offset += 4; + result.Authorities = new PublicKey[resultAuthoritiesLength]; + for (uint resultAuthoritiesIdx = 0; resultAuthoritiesIdx < resultAuthoritiesLength; resultAuthoritiesIdx++) + { + result.Authorities[resultAuthoritiesIdx] = _data.GetPubKey(offset); + offset += 32; + } + + result.Permissionless = _data.GetBool(offset); + offset += 1; + int resultSystemsLength = (int)_data.GetU32(offset); + offset += 4; + result.Systems = _data.GetBytes(offset, resultSystemsLength); + offset += resultSystemsLength; + return result; + } + } + } + + namespace Errors + { + public enum WorldErrorKind : uint + { + InvalidAuthority = 6000U, + InvalidSystemOutput = 6001U, + WorldAccountMismatch = 6002U, + TooManyAuthorities = 6003U, + AuthorityNotFound = 6004U, + SystemNotApproved = 6005U, + InvalidComponentOwner = 6006U + } + } + + namespace Types + { + } + + public partial class WorldClient : TransactionalBaseClient + { + public WorldClient(IRpcClient rpcClient, IStreamingRpcClient streamingRpcClient, PublicKey programId = null) : base(rpcClient, streamingRpcClient, programId ?? new PublicKey(WorldProgram.ID)) + { + } + + public async Task>> GetEntitysAsync(string programAddress = WorldProgram.ID, Commitment commitment = Commitment.Confirmed) + { + var list = new List{new Solana.Unity.Rpc.Models.MemCmp{Bytes = Entity.ACCOUNT_DISCRIMINATOR_B58, Offset = 0}}; + var res = await RpcClient.GetProgramAccountsAsync(programAddress, commitment, memCmpList: list); + if (!res.WasSuccessful || !(res.Result?.Count > 0)) + return new Solana.Unity.Programs.Models.ProgramAccountsResultWrapper>(res); + List resultingAccounts = new List(res.Result.Count); + resultingAccounts.AddRange(res.Result.Select(result => Entity.Deserialize(Convert.FromBase64String(result.Account.Data[0])))); + return new Solana.Unity.Programs.Models.ProgramAccountsResultWrapper>(res, resultingAccounts); + } + + public async Task>> GetRegistrysAsync(string programAddress = WorldProgram.ID, Commitment commitment = Commitment.Confirmed) + { + var list = new List{new Solana.Unity.Rpc.Models.MemCmp{Bytes = Registry.ACCOUNT_DISCRIMINATOR_B58, Offset = 0}}; + var res = await RpcClient.GetProgramAccountsAsync(programAddress, commitment, memCmpList: list); + if (!res.WasSuccessful || !(res.Result?.Count > 0)) + return new Solana.Unity.Programs.Models.ProgramAccountsResultWrapper>(res); + List resultingAccounts = new List(res.Result.Count); + resultingAccounts.AddRange(res.Result.Select(result => Registry.Deserialize(Convert.FromBase64String(result.Account.Data[0])))); + return new Solana.Unity.Programs.Models.ProgramAccountsResultWrapper>(res, resultingAccounts); + } + + public async Task>> GetSessionTokensAsync(string programAddress = WorldProgram.ID, Commitment commitment = Commitment.Confirmed) + { + var list = new List{new Solana.Unity.Rpc.Models.MemCmp{Bytes = SessionToken.ACCOUNT_DISCRIMINATOR_B58, Offset = 0}}; + var res = await RpcClient.GetProgramAccountsAsync(programAddress, commitment, memCmpList: list); + if (!res.WasSuccessful || !(res.Result?.Count > 0)) + return new Solana.Unity.Programs.Models.ProgramAccountsResultWrapper>(res); + List resultingAccounts = new List(res.Result.Count); + resultingAccounts.AddRange(res.Result.Select(result => SessionToken.Deserialize(Convert.FromBase64String(result.Account.Data[0])))); + return new Solana.Unity.Programs.Models.ProgramAccountsResultWrapper>(res, resultingAccounts); + } + + public async Task>> GetWorldsAsync(string programAddress = WorldProgram.ID, Commitment commitment = Commitment.Confirmed) + { + var list = new List{new Solana.Unity.Rpc.Models.MemCmp{Bytes = World.Accounts.World.ACCOUNT_DISCRIMINATOR_B58, Offset = 0}}; + var res = await RpcClient.GetProgramAccountsAsync(programAddress, commitment, memCmpList: list); + if (!res.WasSuccessful || !(res.Result?.Count > 0)) + return new Solana.Unity.Programs.Models.ProgramAccountsResultWrapper>(res); + List resultingAccounts = new List(res.Result.Count); + resultingAccounts.AddRange(res.Result.Select(result => World.Accounts.World.Deserialize(Convert.FromBase64String(result.Account.Data[0])))); + return new Solana.Unity.Programs.Models.ProgramAccountsResultWrapper>(res, resultingAccounts); + } + + public async Task> GetEntityAsync(string accountAddress, Commitment commitment = Commitment.Finalized) + { + var res = await RpcClient.GetAccountInfoAsync(accountAddress, commitment); + if (!res.WasSuccessful) + return new Solana.Unity.Programs.Models.AccountResultWrapper(res); + var resultingAccount = Entity.Deserialize(Convert.FromBase64String(res.Result.Value.Data[0])); + return new Solana.Unity.Programs.Models.AccountResultWrapper(res, resultingAccount); + } + + public async Task> GetRegistryAsync(string accountAddress, Commitment commitment = Commitment.Finalized) + { + var res = await RpcClient.GetAccountInfoAsync(accountAddress, commitment); + if (!res.WasSuccessful) + return new Solana.Unity.Programs.Models.AccountResultWrapper(res); + var resultingAccount = Registry.Deserialize(Convert.FromBase64String(res.Result.Value.Data[0])); + return new Solana.Unity.Programs.Models.AccountResultWrapper(res, resultingAccount); + } + + public async Task> GetSessionTokenAsync(string accountAddress, Commitment commitment = Commitment.Finalized) + { + var res = await RpcClient.GetAccountInfoAsync(accountAddress, commitment); + if (!res.WasSuccessful) + return new Solana.Unity.Programs.Models.AccountResultWrapper(res); + var resultingAccount = SessionToken.Deserialize(Convert.FromBase64String(res.Result.Value.Data[0])); + return new Solana.Unity.Programs.Models.AccountResultWrapper(res, resultingAccount); + } + + public async Task> GetWorldAsync(string accountAddress, Commitment commitment = Commitment.Finalized) + { + var res = await RpcClient.GetAccountInfoAsync(accountAddress, commitment); + if (!res.WasSuccessful) + return new Solana.Unity.Programs.Models.AccountResultWrapper(res); + var resultingAccount = World.Accounts.World.Deserialize(Convert.FromBase64String(res.Result.Value.Data[0])); + return new Solana.Unity.Programs.Models.AccountResultWrapper(res, resultingAccount); + } + + public async Task SubscribeEntityAsync(string accountAddress, Action, Entity> callback, Commitment commitment = Commitment.Finalized) + { + SubscriptionState res = await StreamingRpcClient.SubscribeAccountInfoAsync(accountAddress, (s, e) => + { + Entity parsingResult = null; + if (e.Value?.Data?.Count > 0) + parsingResult = Entity.Deserialize(Convert.FromBase64String(e.Value.Data[0])); + callback(s, e, parsingResult); + }, commitment); + return res; + } + + public async Task SubscribeRegistryAsync(string accountAddress, Action, Registry> callback, Commitment commitment = Commitment.Finalized) + { + SubscriptionState res = await StreamingRpcClient.SubscribeAccountInfoAsync(accountAddress, (s, e) => + { + Registry parsingResult = null; + if (e.Value?.Data?.Count > 0) + parsingResult = Registry.Deserialize(Convert.FromBase64String(e.Value.Data[0])); + callback(s, e, parsingResult); + }, commitment); + return res; + } + + public async Task SubscribeSessionTokenAsync(string accountAddress, Action, SessionToken> callback, Commitment commitment = Commitment.Finalized) + { + SubscriptionState res = await StreamingRpcClient.SubscribeAccountInfoAsync(accountAddress, (s, e) => + { + SessionToken parsingResult = null; + if (e.Value?.Data?.Count > 0) + parsingResult = SessionToken.Deserialize(Convert.FromBase64String(e.Value.Data[0])); + callback(s, e, parsingResult); + }, commitment); + return res; + } + + public async Task SubscribeWorldAsync(string accountAddress, Action, World.Accounts.World> callback, Commitment commitment = Commitment.Finalized) + { + SubscriptionState res = await StreamingRpcClient.SubscribeAccountInfoAsync(accountAddress, (s, e) => + { + World.Accounts.World parsingResult = null; + if (e.Value?.Data?.Count > 0) + parsingResult = World.Accounts.World.Deserialize(Convert.FromBase64String(e.Value.Data[0])); + callback(s, e, parsingResult); + }, commitment); + return res; + } + + protected override Dictionary> BuildErrorsDictionary() + { + return new Dictionary>{{6000U, new ProgramError(WorldErrorKind.InvalidAuthority, "Invalid authority for instruction")}, {6001U, new ProgramError(WorldErrorKind.InvalidSystemOutput, "Invalid system output")}, {6002U, new ProgramError(WorldErrorKind.WorldAccountMismatch, "The provided world account does not match the expected PDA.")}, {6003U, new ProgramError(WorldErrorKind.TooManyAuthorities, "Exceed the maximum number of authorities.")}, {6004U, new ProgramError(WorldErrorKind.AuthorityNotFound, "The provided authority not found")}, {6005U, new ProgramError(WorldErrorKind.SystemNotApproved, "The system is not approved in this world instance")}, {6006U, new ProgramError(WorldErrorKind.InvalidComponentOwner, "The component owner does not match the program")}, }; + } + } + + namespace Program + { + public class AddAuthorityAccounts + { + public PublicKey Authority { get; set; } + + public PublicKey NewAuthority { get; set; } + + public PublicKey World { get; set; } + + public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); + } + + public class AddEntityAccounts + { + public PublicKey Payer { get; set; } + + public PublicKey Entity { get; set; } + + public PublicKey World { get; set; } + + public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); + } + + public class ApplyAccounts + { + public PublicKey Buffer { get; set; } + + public PublicKey BoltSystem { get; set; } + + public PublicKey Authority { get; set; } + + public PublicKey CpiAuth { get; set; } + + public PublicKey World { get; set; } + + public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); + } + + public class ApplyWithSessionAccounts + { + public PublicKey Buffer { get; set; } + + public PublicKey BoltSystem { get; set; } + + public PublicKey Authority { get; set; } + + public PublicKey CpiAuth { get; set; } + + public PublicKey World { get; set; } + + public PublicKey SessionToken { get; set; } + + public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); + } + + public class ApproveSystemAccounts + { + public PublicKey Authority { get; set; } + + public PublicKey World { get; set; } + + public PublicKey System { get; set; } + + public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); + } + + public class DestroyComponentAccounts + { + public PublicKey Authority { get; set; } + + public PublicKey Receiver { get; set; } + + public PublicKey ComponentProgram { get; set; } + + public PublicKey ComponentProgramData { get; set; } + + public PublicKey Entity { get; set; } + + public PublicKey Component { get; set; } + + public PublicKey CpiAuth { get; set; } + + public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); + } + + public class InitializeComponentAccounts + { + public PublicKey Payer { get; set; } + + public PublicKey Data { get; set; } + + public PublicKey Entity { get; set; } + + public PublicKey ComponentProgram { get; set; } + + public PublicKey Authority { get; set; } + + public PublicKey CpiAuth { get; set; } + + public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); + + public PublicKey Buffer { get; set; } + } + + public class InitializeNewWorldAccounts + { + public PublicKey Payer { get; set; } + + public PublicKey World { get; set; } + + public PublicKey Registry { get; set; } + + public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); + } + + public class InitializeRegistryAccounts + { + public PublicKey Registry { get; set; } + + public PublicKey Payer { get; set; } + + public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); + } + + public class RemoveAuthorityAccounts + { + public PublicKey Authority { get; set; } + + public PublicKey AuthorityToDelete { get; set; } + + public PublicKey World { get; set; } + + public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); + } + + public class RemoveSystemAccounts + { + public PublicKey Authority { get; set; } + + public PublicKey World { get; set; } + + public PublicKey System { get; set; } + + public PublicKey SystemProgram { get; set; } = new PublicKey("11111111111111111111111111111111"); + } + + public partial class WorldProgram + { + public const string ID = "WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n"; + public static Solana.Unity.Rpc.Models.TransactionInstruction AddAuthority(AddAuthorityAccounts accounts, ulong world_id, PublicKey programId = null) + { + programId ??= new(ID); + List keys = new() + {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Authority, true), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.NewAuthority, false), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.World, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; + byte[] _data = new byte[1200]; + int offset = 0; + _data.WriteU64(13217455069452700133UL, offset); + offset += 8; + _data.WriteU64(world_id, offset); + offset += 8; + byte[] resultData = new byte[offset]; + Array.Copy(_data, resultData, offset); + return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; + } + + public static Solana.Unity.Rpc.Models.TransactionInstruction AddEntity(AddEntityAccounts accounts, byte[] extra_seed, PublicKey programId = null) + { + programId ??= new(ID); + List keys = new() + {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Payer, true), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Entity, false), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.World, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; + byte[] _data = new byte[1200]; + int offset = 0; + _data.WriteU64(4121062988444201379UL, offset); + offset += 8; + if (extra_seed != null) + { + _data.WriteU8(1, offset); + offset += 1; + _data.WriteS32(extra_seed.Length, offset); + offset += 4; + _data.WriteSpan(extra_seed, offset); + offset += extra_seed.Length; + } + else + { + _data.WriteU8(0, offset); + offset += 1; + } + + byte[] resultData = new byte[offset]; + Array.Copy(_data, resultData, offset); + return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; + } + + public static Solana.Unity.Rpc.Models.TransactionInstruction Apply(ApplyAccounts accounts, byte[] args, PublicKey programId = null) + { + programId ??= new(ID); + List keys = new() + {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Buffer, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.BoltSystem, false), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Authority, true), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.CpiAuth, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.World, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; + byte[] _data = new byte[1200]; + int offset = 0; + _data.WriteU64(16258613031726085112UL, offset); + offset += 8; + _data.WriteS32(args.Length, offset); + offset += 4; + _data.WriteSpan(args, offset); + offset += args.Length; + byte[] resultData = new byte[offset]; + Array.Copy(_data, resultData, offset); + return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; + } + + public static Solana.Unity.Rpc.Models.TransactionInstruction ApplyWithSession(ApplyWithSessionAccounts accounts, byte[] args, PublicKey programId = null) + { + programId ??= new(ID); + List keys = new() + {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Buffer, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.BoltSystem, false), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Authority, true), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.CpiAuth, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.World, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SessionToken, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; + byte[] _data = new byte[1200]; + int offset = 0; + _data.WriteU64(7459768094276011477UL, offset); + offset += 8; + _data.WriteS32(args.Length, offset); + offset += 4; + _data.WriteSpan(args, offset); + offset += args.Length; + byte[] resultData = new byte[offset]; + Array.Copy(_data, resultData, offset); + return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; + } + + public static Solana.Unity.Rpc.Models.TransactionInstruction ApproveSystem(ApproveSystemAccounts accounts, PublicKey programId = null) + { + programId ??= new(ID); + List keys = new() + {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Authority, true), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.World, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.System, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; + byte[] _data = new byte[1200]; + int offset = 0; + _data.WriteU64(8777308090533520754UL, offset); + offset += 8; + byte[] resultData = new byte[offset]; + Array.Copy(_data, resultData, offset); + return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; + } + + public static Solana.Unity.Rpc.Models.TransactionInstruction DestroyComponent(DestroyComponentAccounts accounts, PublicKey programId = null) + { + programId ??= new(ID); + List keys = new() + {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Authority, true), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Receiver, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.ComponentProgram, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.ComponentProgramData, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.Entity, false), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Component, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.CpiAuth, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; + byte[] _data = new byte[1200]; + int offset = 0; + _data.WriteU64(5321952129328727336UL, offset); + offset += 8; + byte[] resultData = new byte[offset]; + Array.Copy(_data, resultData, offset); + return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; + } + + public static Solana.Unity.Rpc.Models.TransactionInstruction InitializeComponent(InitializeComponentAccounts accounts, PublicKey programId = null) + { + programId ??= new(ID); + List 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.Buffer, false)}; + byte[] _data = new byte[1200]; + int offset = 0; + _data.WriteU64(2179155133888827172UL, offset); + offset += 8; + byte[] resultData = new byte[offset]; + Array.Copy(_data, resultData, offset); + return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; + } + + public static Solana.Unity.Rpc.Models.TransactionInstruction InitializeNewWorld(InitializeNewWorldAccounts accounts, PublicKey programId = null) + { + programId ??= new(ID); + List keys = new() + {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Payer, true), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.World, false), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Registry, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; + byte[] _data = new byte[1200]; + int offset = 0; + _data.WriteU64(7118163274173538327UL, offset); + offset += 8; + byte[] resultData = new byte[offset]; + Array.Copy(_data, resultData, offset); + return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; + } + + public static Solana.Unity.Rpc.Models.TransactionInstruction InitializeRegistry(InitializeRegistryAccounts accounts, PublicKey programId = null) + { + programId ??= new(ID); + List keys = new() + {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Registry, false), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Payer, true), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; + byte[] _data = new byte[1200]; + int offset = 0; + _data.WriteU64(4321548737212364221UL, offset); + offset += 8; + byte[] resultData = new byte[offset]; + Array.Copy(_data, resultData, offset); + return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; + } + + public static Solana.Unity.Rpc.Models.TransactionInstruction RemoveAuthority(RemoveAuthorityAccounts accounts, ulong world_id, PublicKey programId = null) + { + programId ??= new(ID); + List keys = new() + {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Authority, true), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.AuthorityToDelete, false), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.World, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; + byte[] _data = new byte[1200]; + int offset = 0; + _data.WriteU64(15585545156648003826UL, offset); + offset += 8; + _data.WriteU64(world_id, offset); + offset += 8; + byte[] resultData = new byte[offset]; + Array.Copy(_data, resultData, offset); + return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; + } + + public static Solana.Unity.Rpc.Models.TransactionInstruction RemoveSystem(RemoveSystemAccounts accounts, PublicKey programId = null) + { + programId ??= new(ID); + List keys = new() + {Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.Authority, true), Solana.Unity.Rpc.Models.AccountMeta.Writable(accounts.World, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.System, false), Solana.Unity.Rpc.Models.AccountMeta.ReadOnly(accounts.SystemProgram, false)}; + byte[] _data = new byte[1200]; + int offset = 0; + _data.WriteU64(8688994685429436634UL, offset); + offset += 8; + byte[] resultData = new byte[offset]; + Array.Copy(_data, resultData, offset); + return new Solana.Unity.Rpc.Models.TransactionInstruction{Keys = keys, ProgramId = programId.KeyBytes, Data = resultData}; + } + } + } } \ No newline at end of file diff --git a/clients/csharp/Solana.Unity.Bolt/WorldProgram/World.cs b/clients/csharp/Solana.Unity.Bolt/WorldProgram/World.cs index 8334073c..0ed35461 100644 --- a/clients/csharp/Solana.Unity.Bolt/WorldProgram/World.cs +++ b/clients/csharp/Solana.Unity.Bolt/WorldProgram/World.cs @@ -27,6 +27,18 @@ public static Solana.Unity.Rpc.Models.TransactionInstruction AddEntity(AddEntity return AddEntity(accounts, System.Text.Encoding.UTF8.GetBytes(extraSeed), programId); } + public static PublicKey FindCpiAuthPda() { + PublicKey.TryFindProgramAddress(new[] + { + Encoding.UTF8.GetBytes("cpi_auth"), + }, new PublicKey(ID), out var pda, out _); + return pda; + } + + public static PublicKey FindBufferPda(PublicKey account) { + return FindBufferPda(account, new PublicKey(ID)); + } + public static PublicKey FindSessionTokenPda(PublicKey sessionSigner, PublicKey authority) { PublicKey.TryFindProgramAddress(new[] @@ -223,6 +235,8 @@ public static Solana.Unity.Rpc.Models.TransactionInstruction ApplySystem( Solana.Unity.Rpc.Models.TransactionInstruction instruction; if (sessionToken != null) { var apply = new ApplyWithSessionAccounts() { + CpiAuth = WorldProgram.FindCpiAuthPda(), + Buffer = FindBufferPda(componentPdas[0]), BoltSystem = system, Authority = authority, World = world, @@ -231,6 +245,8 @@ public static Solana.Unity.Rpc.Models.TransactionInstruction ApplySystem( instruction = ApplyWithSession(apply, args, programId); } else { var apply = new ApplyAccounts() { + CpiAuth = WorldProgram.FindCpiAuthPda(), + Buffer = FindBufferPda(componentPdas[0]), BoltSystem = system, Authority = authority, World = world, diff --git a/clients/typescript/src/delegation/delegate.ts b/clients/typescript/src/delegation/delegate.ts index 017e442c..223bcd93 100644 --- a/clients/typescript/src/delegation/delegate.ts +++ b/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; @@ -85,7 +87,7 @@ export function createDelegateInstruction( const keys: web3.AccountMeta[] = [ { pubkey: accounts.payer, - isWritable: false, + isWritable: true, isSigner: true, }, { @@ -177,17 +179,54 @@ export async function DelegateComponent({ componentPda: PublicKey; }> { const componentPda = FindComponentPda({ componentId, entity, seed }); - const delegateComponentIx = createDelegateInstruction({ - payer, - entity, - account: componentPda, - ownerProgram: componentId, - buffer, - delegationRecord, - delegationMetadata, - delegationProgram, - systemProgram, - }); + const componentProgram = componentId; + const componentBuffer = FindBufferPda(componentPda); + systemProgram = systemProgram ?? web3.SystemProgram.programId; + buffer = + buffer ?? + delegateBufferPdaFromDelegatedAccountAndOwnerProgram( + componentPda, + componentProgram, + ); + delegationRecord = + delegationRecord ?? delegationRecordPdaFromDelegatedAccount(componentPda); + delegationMetadata = + delegationMetadata ?? + delegationMetadataPdaFromDelegatedAccount(componentPda); + delegationProgram = delegationProgram ?? new PublicKey(DELEGATION_PROGRAM_ID); + const worldProgram = new PublicKey(worldIdl.address); + + const bufferDelegationRecord = + delegationRecordPdaFromDelegatedAccount(componentBuffer); + + const bufferDelegationMetadata = + delegationMetadataPdaFromDelegatedAccount(componentBuffer); + + const bufferBuffer = delegateBufferPdaFromDelegatedAccountAndOwnerProgram( + componentBuffer, + worldProgram, + ); + + const program = new Program(worldIdl as Idl) as unknown as Program; + const delegateComponentIx = await program.methods + .delegateComponent(0, null) + .accounts({ + payer, + component: componentPda, + componentBuffer, + componentProgram, + buffer, + delegationRecord, + delegationMetadata, + delegationProgram, + systemProgram, + entity, + worldProgram, + bufferBuffer, + bufferDelegationRecord, + bufferDelegationMetadata, + }) + .instruction(); return { instruction: delegateComponentIx, diff --git a/clients/typescript/src/generated/idl/world.json b/clients/typescript/src/generated/idl/world.json index 3fc9bfcc..1e7d2377 100644 --- a/clients/typescript/src/generated/idl/world.json +++ b/clients/typescript/src/generated/idl/world.json @@ -116,11 +116,16 @@ 225 ], "accounts": [ + { + "name": "buffer", + "writable": true + }, { "name": "bolt_system" }, { "name": "authority", + "writable": true, "signer": true }, { @@ -128,6 +133,10 @@ }, { "name": "world" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ @@ -150,11 +159,16 @@ 103 ], "accounts": [ + { + "name": "buffer", + "writable": true + }, { "name": "bolt_system" }, { "name": "authority", + "writable": true, "signer": true }, { @@ -165,6 +179,10 @@ }, { "name": "session_token" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ @@ -206,6 +224,86 @@ ], "args": [] }, + { + "name": "delegate_component", + "discriminator": [ + 191, + 212, + 179, + 193, + 178, + 94, + 119, + 93 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "component", + "writable": true + }, + { + "name": "component_buffer", + "writable": true + }, + { + "name": "component_program" + }, + { + "name": "buffer", + "writable": true + }, + { + "name": "delegation_record", + "writable": true + }, + { + "name": "delegation_metadata", + "writable": true + }, + { + "name": "delegation_program" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "entity" + }, + { + "name": "world_program" + }, + { + "name": "buffer_buffer", + "writable": true + }, + { + "name": "buffer_delegation_record", + "writable": true + }, + { + "name": "buffer_delegation_metadata", + "writable": true + } + ], + "args": [ + { + "name": "commit_frequency_ms", + "type": "u32" + }, + { + "name": "validator", + "type": { + "option": "pubkey" + } + } + ] + }, { "name": "destroy_component", "discriminator": [ @@ -288,6 +386,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": [] @@ -505,6 +626,19 @@ 218 ] }, + { + "name": "SessionToken", + "discriminator": [ + 233, + 4, + 115, + 14, + 46, + 21, + 1, + 15 + ] + }, { "name": "World", "discriminator": [ @@ -549,6 +683,16 @@ "code": 6005, "name": "SystemNotApproved", "msg": "The system is not approved in this world instance" + }, + { + "code": 6006, + "name": "InvalidComponentOwner", + "msg": "The component owner does not match the program" + }, + { + "code": 6007, + "name": "InvalidBufferAccount", + "msg": "Invalid buffer account" } ], "types": [ @@ -576,6 +720,30 @@ ] } }, + { + "name": "SessionToken", + "type": { + "kind": "struct", + "fields": [ + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "target_program", + "type": "pubkey" + }, + { + "name": "session_signer", + "type": "pubkey" + }, + { + "name": "valid_until", + "type": "i64" + } + ] + } + }, { "name": "World", "type": { diff --git a/clients/typescript/src/generated/index.ts b/clients/typescript/src/generated/index.ts index d5c57844..851ec3ee 100644 --- a/clients/typescript/src/generated/index.ts +++ b/clients/typescript/src/generated/index.ts @@ -6,12 +6,13 @@ */ import { PublicKey } from "@solana/web3.js"; -import { type World as WorldProgram } from "./types/world"; import idl from "./idl/world.json"; import gpl_session from "./idl/gpl_session.json"; export * from "./accounts"; export * from "./errors"; export * from "./instructions"; +// Re-export the IDL type under a distinct name to avoid name collisions with account types +export type { World as WorldIdl } from "./types"; /** * Program address @@ -28,7 +29,5 @@ export const PROGRAM_ADDRESS = "WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n"; * @category generated */ export const PROGRAM_ID = new PublicKey(PROGRAM_ADDRESS); - -export default WorldProgram; export { idl as worldIdl }; export { gpl_session as sessionIdl }; diff --git a/clients/typescript/src/generated/instructions/apply.ts b/clients/typescript/src/generated/instructions/apply.ts index 3210443a..fd63ad8e 100644 --- a/clients/typescript/src/generated/instructions/apply.ts +++ b/clients/typescript/src/generated/instructions/apply.ts @@ -7,7 +7,7 @@ import * as beet from "@metaplex-foundation/beet"; import * as web3 from "@solana/web3.js"; -import { CPI_AUTH_ADDRESS } from "../../world/transactions"; +import { FindCpiAuthPda } from "../../index"; /** * @category Instructions @@ -98,7 +98,7 @@ export function createApplyInstruction( isSigner: false, }, { - pubkey: CPI_AUTH_ADDRESS, + pubkey: FindCpiAuthPda(), isWritable: false, isSigner: false, }, diff --git a/clients/typescript/src/generated/instructions/apply2.ts b/clients/typescript/src/generated/instructions/apply2.ts deleted file mode 100644 index 1983156d..00000000 --- a/clients/typescript/src/generated/instructions/apply2.ts +++ /dev/null @@ -1,133 +0,0 @@ -/** - * This code was GENERATED using the solita package. - * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. - * - * See: https://github.com/metaplex-foundation/solita - */ - -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; - -/** - * @category Instructions - * @category Apply2 - * @category generated - */ -export interface Apply2InstructionArgs { - args: Uint8Array; -} -/** - * @category Instructions - * @category Apply2 - * @category generated - */ -export const apply2Struct = new beet.FixableBeetArgsStruct< - Apply2InstructionArgs & { - instructionDiscriminator: number[] /* size: 8 */; - } ->( - [ - ["instructionDiscriminator", beet.uniformFixedSizeArray(beet.u8, 8)], - ["args", beet.bytes], - ], - "Apply2InstructionArgs", -); -/** - * Accounts required by the _apply2_ instruction - * - * @property [] boltSystem - * @property [] componentProgram1 - * @property [_writable_] boltComponent1 - * @property [] componentProgram2 - * @property [_writable_] boltComponent2 - * @property [] authority - * @property [] instructionSysvarAccount - * @category Instructions - * @category Apply2 - * @category generated - */ -export interface Apply2InstructionAccounts { - boltSystem: web3.PublicKey; - componentProgram1: web3.PublicKey; - boltComponent1: web3.PublicKey; - componentProgram2: web3.PublicKey; - boltComponent2: web3.PublicKey; - authority: web3.PublicKey; - instructionSysvarAccount: web3.PublicKey; - anchorRemainingAccounts?: web3.AccountMeta[]; -} - -export const apply2InstructionDiscriminator = [ - 120, 32, 116, 154, 158, 159, 208, 73, -]; - -/** - * Creates a _Apply2_ instruction. - * - * @param accounts that will be accessed while the instruction is processed - * @param args to provide as instruction data to the program - * - * @category Instructions - * @category Apply2 - * @category generated - */ -export function createApply2Instruction( - accounts: Apply2InstructionAccounts, - args: Apply2InstructionArgs, - programId = new web3.PublicKey("WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n"), -) { - const [data] = apply2Struct.serialize({ - instructionDiscriminator: apply2InstructionDiscriminator, - ...args, - }); - const keys: web3.AccountMeta[] = [ - { - pubkey: accounts.boltSystem, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.componentProgram1, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.boltComponent1, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.componentProgram2, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.boltComponent2, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.authority, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.instructionSysvarAccount, - isWritable: false, - isSigner: false, - }, - ]; - - if (accounts.anchorRemainingAccounts != null) { - for (const acc of accounts.anchorRemainingAccounts) { - keys.push(acc); - } - } - - const ix = new web3.TransactionInstruction({ - programId, - keys, - data, - }); - return ix; -} diff --git a/clients/typescript/src/generated/instructions/apply3.ts b/clients/typescript/src/generated/instructions/apply3.ts deleted file mode 100644 index d80f54c5..00000000 --- a/clients/typescript/src/generated/instructions/apply3.ts +++ /dev/null @@ -1,147 +0,0 @@ -/** - * This code was GENERATED using the solita package. - * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. - * - * See: https://github.com/metaplex-foundation/solita - */ - -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; - -/** - * @category Instructions - * @category Apply3 - * @category generated - */ -export interface Apply3InstructionArgs { - args: Uint8Array; -} -/** - * @category Instructions - * @category Apply3 - * @category generated - */ -export const apply3Struct = new beet.FixableBeetArgsStruct< - Apply3InstructionArgs & { - instructionDiscriminator: number[] /* size: 8 */; - } ->( - [ - ["instructionDiscriminator", beet.uniformFixedSizeArray(beet.u8, 8)], - ["args", beet.bytes], - ], - "Apply3InstructionArgs", -); -/** - * Accounts required by the _apply3_ instruction - * - * @property [] boltSystem - * @property [] componentProgram1 - * @property [_writable_] boltComponent1 - * @property [] componentProgram2 - * @property [_writable_] boltComponent2 - * @property [] componentProgram3 - * @property [_writable_] boltComponent3 - * @property [] authority - * @property [] instructionSysvarAccount - * @category Instructions - * @category Apply3 - * @category generated - */ -export interface Apply3InstructionAccounts { - boltSystem: web3.PublicKey; - componentProgram1: web3.PublicKey; - boltComponent1: web3.PublicKey; - componentProgram2: web3.PublicKey; - boltComponent2: web3.PublicKey; - componentProgram3: web3.PublicKey; - boltComponent3: web3.PublicKey; - authority: web3.PublicKey; - instructionSysvarAccount: web3.PublicKey; - anchorRemainingAccounts?: web3.AccountMeta[]; -} - -export const apply3InstructionDiscriminator = [ - 254, 146, 49, 7, 236, 131, 105, 221, -]; - -/** - * Creates a _Apply3_ instruction. - * - * @param accounts that will be accessed while the instruction is processed - * @param args to provide as instruction data to the program - * - * @category Instructions - * @category Apply3 - * @category generated - */ -export function createApply3Instruction( - accounts: Apply3InstructionAccounts, - args: Apply3InstructionArgs, - programId = new web3.PublicKey("WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n"), -) { - const [data] = apply3Struct.serialize({ - instructionDiscriminator: apply3InstructionDiscriminator, - ...args, - }); - const keys: web3.AccountMeta[] = [ - { - pubkey: accounts.boltSystem, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.componentProgram1, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.boltComponent1, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.componentProgram2, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.boltComponent2, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.componentProgram3, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.boltComponent3, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.authority, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.instructionSysvarAccount, - isWritable: false, - isSigner: false, - }, - ]; - - if (accounts.anchorRemainingAccounts != null) { - for (const acc of accounts.anchorRemainingAccounts) { - keys.push(acc); - } - } - - const ix = new web3.TransactionInstruction({ - programId, - keys, - data, - }); - return ix; -} diff --git a/clients/typescript/src/generated/instructions/apply4.ts b/clients/typescript/src/generated/instructions/apply4.ts deleted file mode 100644 index 5f8c65c5..00000000 --- a/clients/typescript/src/generated/instructions/apply4.ts +++ /dev/null @@ -1,161 +0,0 @@ -/** - * This code was GENERATED using the solita package. - * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. - * - * See: https://github.com/metaplex-foundation/solita - */ - -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; - -/** - * @category Instructions - * @category Apply4 - * @category generated - */ -export interface Apply4InstructionArgs { - args: Uint8Array; -} -/** - * @category Instructions - * @category Apply4 - * @category generated - */ -export const apply4Struct = new beet.FixableBeetArgsStruct< - Apply4InstructionArgs & { - instructionDiscriminator: number[] /* size: 8 */; - } ->( - [ - ["instructionDiscriminator", beet.uniformFixedSizeArray(beet.u8, 8)], - ["args", beet.bytes], - ], - "Apply4InstructionArgs", -); -/** - * Accounts required by the _apply4_ instruction - * - * @property [] boltSystem - * @property [] componentProgram1 - * @property [_writable_] boltComponent1 - * @property [] componentProgram2 - * @property [_writable_] boltComponent2 - * @property [] componentProgram3 - * @property [_writable_] boltComponent3 - * @property [] componentProgram4 - * @property [_writable_] boltComponent4 - * @property [] authority - * @property [] instructionSysvarAccount - * @category Instructions - * @category Apply4 - * @category generated - */ -export interface Apply4InstructionAccounts { - boltSystem: web3.PublicKey; - componentProgram1: web3.PublicKey; - boltComponent1: web3.PublicKey; - componentProgram2: web3.PublicKey; - boltComponent2: web3.PublicKey; - componentProgram3: web3.PublicKey; - boltComponent3: web3.PublicKey; - componentProgram4: web3.PublicKey; - boltComponent4: web3.PublicKey; - authority: web3.PublicKey; - instructionSysvarAccount: web3.PublicKey; - anchorRemainingAccounts?: web3.AccountMeta[]; -} - -export const apply4InstructionDiscriminator = [ - 223, 104, 24, 79, 252, 196, 14, 109, -]; - -/** - * Creates a _Apply4_ instruction. - * - * @param accounts that will be accessed while the instruction is processed - * @param args to provide as instruction data to the program - * - * @category Instructions - * @category Apply4 - * @category generated - */ -export function createApply4Instruction( - accounts: Apply4InstructionAccounts, - args: Apply4InstructionArgs, - programId = new web3.PublicKey("WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n"), -) { - const [data] = apply4Struct.serialize({ - instructionDiscriminator: apply4InstructionDiscriminator, - ...args, - }); - const keys: web3.AccountMeta[] = [ - { - pubkey: accounts.boltSystem, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.componentProgram1, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.boltComponent1, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.componentProgram2, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.boltComponent2, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.componentProgram3, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.boltComponent3, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.componentProgram4, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.boltComponent4, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.authority, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.instructionSysvarAccount, - isWritable: false, - isSigner: false, - }, - ]; - - if (accounts.anchorRemainingAccounts != null) { - for (const acc of accounts.anchorRemainingAccounts) { - keys.push(acc); - } - } - - const ix = new web3.TransactionInstruction({ - programId, - keys, - data, - }); - return ix; -} diff --git a/clients/typescript/src/generated/instructions/apply5.ts b/clients/typescript/src/generated/instructions/apply5.ts deleted file mode 100644 index f8e1695d..00000000 --- a/clients/typescript/src/generated/instructions/apply5.ts +++ /dev/null @@ -1,175 +0,0 @@ -/** - * This code was GENERATED using the solita package. - * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. - * - * See: https://github.com/metaplex-foundation/solita - */ - -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; - -/** - * @category Instructions - * @category Apply5 - * @category generated - */ -export interface Apply5InstructionArgs { - args: Uint8Array; -} -/** - * @category Instructions - * @category Apply5 - * @category generated - */ -export const apply5Struct = new beet.FixableBeetArgsStruct< - Apply5InstructionArgs & { - instructionDiscriminator: number[] /* size: 8 */; - } ->( - [ - ["instructionDiscriminator", beet.uniformFixedSizeArray(beet.u8, 8)], - ["args", beet.bytes], - ], - "Apply5InstructionArgs", -); -/** - * Accounts required by the _apply5_ instruction - * - * @property [] boltSystem - * @property [] componentProgram1 - * @property [_writable_] boltComponent1 - * @property [] componentProgram2 - * @property [_writable_] boltComponent2 - * @property [] componentProgram3 - * @property [_writable_] boltComponent3 - * @property [] componentProgram4 - * @property [_writable_] boltComponent4 - * @property [] componentProgram5 - * @property [_writable_] boltComponent5 - * @property [] authority - * @property [] instructionSysvarAccount - * @category Instructions - * @category Apply5 - * @category generated - */ -export interface Apply5InstructionAccounts { - boltSystem: web3.PublicKey; - componentProgram1: web3.PublicKey; - boltComponent1: web3.PublicKey; - componentProgram2: web3.PublicKey; - boltComponent2: web3.PublicKey; - componentProgram3: web3.PublicKey; - boltComponent3: web3.PublicKey; - componentProgram4: web3.PublicKey; - boltComponent4: web3.PublicKey; - componentProgram5: web3.PublicKey; - boltComponent5: web3.PublicKey; - authority: web3.PublicKey; - instructionSysvarAccount: web3.PublicKey; - anchorRemainingAccounts?: web3.AccountMeta[]; -} - -export const apply5InstructionDiscriminator = [ - 70, 164, 214, 28, 136, 116, 84, 153, -]; - -/** - * Creates a _Apply5_ instruction. - * - * @param accounts that will be accessed while the instruction is processed - * @param args to provide as instruction data to the program - * - * @category Instructions - * @category Apply5 - * @category generated - */ -export function createApply5Instruction( - accounts: Apply5InstructionAccounts, - args: Apply5InstructionArgs, - programId = new web3.PublicKey("WorLD15A7CrDwLcLy4fRqtaTb9fbd8o8iqiEMUDse2n"), -) { - const [data] = apply5Struct.serialize({ - instructionDiscriminator: apply5InstructionDiscriminator, - ...args, - }); - const keys: web3.AccountMeta[] = [ - { - pubkey: accounts.boltSystem, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.componentProgram1, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.boltComponent1, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.componentProgram2, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.boltComponent2, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.componentProgram3, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.boltComponent3, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.componentProgram4, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.boltComponent4, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.componentProgram5, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.boltComponent5, - isWritable: true, - isSigner: false, - }, - { - pubkey: accounts.authority, - isWritable: false, - isSigner: false, - }, - { - pubkey: accounts.instructionSysvarAccount, - isWritable: false, - isSigner: false, - }, - ]; - - if (accounts.anchorRemainingAccounts != null) { - for (const acc of accounts.anchorRemainingAccounts) { - keys.push(acc); - } - } - - const ix = new web3.TransactionInstruction({ - programId, - keys, - data, - }); - return ix; -} diff --git a/clients/typescript/src/generated/instructions/index.ts b/clients/typescript/src/generated/instructions/index.ts index abde1fd2..1db8f0ff 100644 --- a/clients/typescript/src/generated/instructions/index.ts +++ b/clients/typescript/src/generated/instructions/index.ts @@ -7,10 +7,6 @@ export * from "./addEntity"; export * from "./apply"; -export * from "./apply2"; -export * from "./apply3"; -export * from "./apply4"; -export * from "./apply5"; export * from "./initializeComponent"; export * from "./initializeNewWorld"; export * from "./initializeRegistry"; diff --git a/clients/typescript/src/generated/instructions/initializeComponent.ts b/clients/typescript/src/generated/instructions/initializeComponent.ts index 0d14c217..99b6dad5 100644 --- a/clients/typescript/src/generated/instructions/initializeComponent.ts +++ b/clients/typescript/src/generated/instructions/initializeComponent.ts @@ -7,7 +7,7 @@ import * as beet from "@metaplex-foundation/beet"; import * as web3 from "@solana/web3.js"; -import { CPI_AUTH_ADDRESS } from "../../world/transactions"; +import { FindCpiAuthPda } from "../../index"; /** * @category Instructions @@ -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[]; } @@ -89,7 +90,7 @@ export function createInitializeComponentInstruction( isSigner: false, }, { - pubkey: CPI_AUTH_ADDRESS, + pubkey: FindCpiAuthPda(), isWritable: false, isSigner: false, }, @@ -98,6 +99,11 @@ export function createInitializeComponentInstruction( isWritable: false, isSigner: false, }, + { + pubkey: accounts.buffer, + isWritable: true, + isSigner: false, + }, ]; if (accounts.anchorRemainingAccounts != null) { diff --git a/clients/typescript/src/generated/types/world.ts b/clients/typescript/src/generated/types/world.ts index ac6ef36c..95a43fb1 100644 --- a/clients/typescript/src/generated/types/world.ts +++ b/clients/typescript/src/generated/types/world.ts @@ -89,11 +89,16 @@ export type World = { name: "apply"; discriminator: [248, 243, 145, 24, 105, 50, 162, 225]; accounts: [ + { + name: "buffer"; + writable: true; + }, { name: "boltSystem"; }, { name: "authority"; + writable: true; signer: true; }, { @@ -102,6 +107,10 @@ export type World = { { name: "world"; }, + { + name: "systemProgram"; + address: "11111111111111111111111111111111"; + }, ]; args: [ { @@ -114,11 +123,16 @@ export type World = { name: "applyWithSession"; discriminator: [213, 69, 29, 230, 142, 107, 134, 103]; accounts: [ + { + name: "buffer"; + writable: true; + }, { name: "boltSystem"; }, { name: "authority"; + writable: true; signer: true; }, { @@ -130,6 +144,10 @@ export type World = { { name: "sessionToken"; }, + { + name: "systemProgram"; + address: "11111111111111111111111111111111"; + }, ]; args: [ { @@ -161,6 +179,77 @@ export type World = { ]; args: []; }, + { + name: "delegateComponent"; + discriminator: [191, 212, 179, 193, 178, 94, 119, 93]; + accounts: [ + { + name: "payer"; + writable: true; + signer: true; + }, + { + name: "component"; + writable: true; + }, + { + name: "componentBuffer"; + writable: true; + }, + { + name: "componentProgram"; + }, + { + name: "buffer"; + writable: true; + }, + { + name: "delegationRecord"; + writable: true; + }, + { + name: "delegationMetadata"; + writable: true; + }, + { + name: "delegationProgram"; + }, + { + name: "systemProgram"; + address: "11111111111111111111111111111111"; + }, + { + name: "entity"; + }, + { + name: "worldProgram"; + }, + { + name: "bufferBuffer"; + writable: true; + }, + { + name: "bufferDelegationRecord"; + writable: true; + }, + { + name: "bufferDelegationMetadata"; + writable: true; + }, + ]; + args: [ + { + name: "commitFrequencyMs"; + type: "u32"; + }, + { + name: "validator"; + type: { + option: "pubkey"; + }; + }, + ]; + }, { name: "destroyComponent"; discriminator: [40, 197, 69, 196, 67, 95, 219, 73]; @@ -226,6 +315,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: []; }, @@ -367,6 +472,10 @@ export type World = { name: "registry"; discriminator: [47, 174, 110, 246, 184, 182, 252, 218]; }, + { + name: "sessionToken"; + discriminator: [233, 4, 115, 14, 46, 21, 1, 15]; + }, { name: "world"; discriminator: [145, 45, 170, 174, 122, 32, 155, 124]; @@ -403,6 +512,16 @@ export type World = { name: "systemNotApproved"; msg: "The system is not approved in this world instance"; }, + { + code: 6006; + name: "invalidComponentOwner"; + msg: "The component owner does not match the program"; + }, + { + code: 6007; + name: "invalidBufferAccount"; + msg: "Invalid buffer account"; + }, ]; types: [ { @@ -429,6 +548,30 @@ export type World = { ]; }; }, + { + name: "sessionToken"; + type: { + kind: "struct"; + fields: [ + { + name: "authority"; + type: "pubkey"; + }, + { + name: "targetProgram"; + type: "pubkey"; + }, + { + name: "sessionSigner"; + type: "pubkey"; + }, + { + name: "validUntil"; + type: "i64"; + }, + ]; + }; + }, { name: "world"; type: { diff --git a/clients/typescript/src/index.ts b/clients/typescript/src/index.ts index 7eb33778..5c0399e9 100644 --- a/clients/typescript/src/index.ts +++ b/clients/typescript/src/index.ts @@ -99,6 +99,20 @@ export function FindComponentProgramDataPda({ )[0]; } +export function FindBufferPda(component: PublicKey) { + return PublicKey.findProgramAddressSync( + [Buffer.from("buffer"), component.toBuffer()], + WORLD_PROGRAM_ID, + )[0]; +} + +export function FindCpiAuthPda() { + return PublicKey.findProgramAddressSync( + [Buffer.from("cpi_auth")], + WORLD_PROGRAM_ID, + )[0]; +} + // TODO: seed must be Uint8Array like the other FindPda functions export function FindComponentPda({ componentId, diff --git a/clients/typescript/src/world/transactions.ts b/clients/typescript/src/world/transactions.ts index f52c5a09..89b3a752 100644 --- a/clients/typescript/src/world/transactions.ts +++ b/clients/typescript/src/world/transactions.ts @@ -17,6 +17,8 @@ import { WORLD_PROGRAM_ID, BN, FindComponentProgramDataPda, + FindBufferPda, + FindCpiAuthPda, } from "../index"; import web3 from "@solana/web3.js"; import { @@ -26,7 +28,7 @@ import { Transaction, type TransactionInstruction, } from "@solana/web3.js"; -import type WorldProgram from "../generated"; +import type { World as WorldProgram } from "../generated/types/world"; import { createInitializeRegistryInstruction, PROGRAM_ID, @@ -34,10 +36,6 @@ import { } from "../generated"; import { type Idl, Program } from "@coral-xyz/anchor"; -export const CPI_AUTH_ADDRESS = new web3.PublicKey( - "B2f2y3QTBv346wE6nWKor72AUhUvFF6mPk7TWCF2QVhi", -); - export async function InitializeRegistry({ payer, connection, @@ -154,9 +152,7 @@ export async function AddAuthority({ instruction: TransactionInstruction; transaction: Transaction; }> { - const program = new Program( - worldIdl as Idl, - ) as unknown as Program; + const program = new Program(worldIdl as Idl) as unknown as Program; const worldInstance = await World.fromAccountAddress(connection, world); const worldId = new BN(worldInstance.id); const instruction = await program.methods @@ -196,9 +192,7 @@ export async function RemoveAuthority({ instruction: TransactionInstruction; transaction: Transaction; }> { - const program = new Program( - worldIdl as Idl, - ) as unknown as Program; + const program = new Program(worldIdl as Idl) as unknown as Program; const worldInstance = await World.fromAccountAddress(connection, world); const worldId = new BN(worldInstance.id); const instruction = await program.methods @@ -235,9 +229,7 @@ export async function ApproveSystem({ instruction: TransactionInstruction; transaction: Transaction; }> { - const program = new Program( - worldIdl as Idl, - ) as unknown as Program; + const program = new Program(worldIdl as Idl) as unknown as Program; const instruction = await program.methods .approveSystem() .accounts({ @@ -272,9 +264,7 @@ export async function RemoveSystem({ instruction: TransactionInstruction; transaction: Transaction; }> { - const program = new Program( - worldIdl as Idl, - ) as unknown as Program; + const program = new Program(worldIdl as Idl) as unknown as Program; const instruction = await program.methods .removeSystem() .accounts({ @@ -358,9 +348,7 @@ export async function DestroyComponent({ instruction: TransactionInstruction; transaction: Transaction; }> { - const program = new Program( - worldIdl as Idl, - ) as unknown as Program; + const program = new Program(worldIdl as Idl) as unknown as Program; const componentProgramData = FindComponentProgramDataPda({ programId: componentId, }); @@ -375,7 +363,7 @@ export async function DestroyComponent({ componentProgram, componentProgramData, receiver, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .instruction(); const transaction = new Transaction().add(instruction); @@ -421,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); @@ -449,9 +438,7 @@ async function createApplySystemInstruction({ extraAccounts, args, }: ApplySystemInstruction): Promise { - const program = new Program( - worldIdl as Idl, - ) as unknown as Program; + const program = new Program(worldIdl as Idl) as unknown as Program; let componentCount = 0; entities.forEach(function (entity) { componentCount += entity.components.length; @@ -503,11 +490,12 @@ async function createApplySystemInstruction({ return program.methods .applyWithSession(SerializeArgs(args)) .accounts({ + buffer: FindBufferPda(remainingAccounts[1].pubkey), // First component PDA authority: authority ?? PROGRAM_ID, boltSystem: systemId, sessionToken: session.token, world, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .remainingAccounts(remainingAccounts) .instruction(); @@ -515,10 +503,11 @@ async function createApplySystemInstruction({ return program.methods .apply(SerializeArgs(args)) .accounts({ + buffer: FindBufferPda(remainingAccounts[1].pubkey), // First component PDA authority: authority ?? PROGRAM_ID, boltSystem: systemId, world, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .remainingAccounts(remainingAccounts) .instruction(); diff --git a/clients/typescript/test/framework.ts b/clients/typescript/test/framework.ts index a3b36439..962a0c25 100644 --- a/clients/typescript/test/framework.ts +++ b/clients/typescript/test/framework.ts @@ -70,9 +70,6 @@ export class Framework { this.systemSimpleMovement = anchor.workspace.SystemSimpleMovement; this.systemFly = anchor.workspace.SystemFly; this.systemApplyVelocity = anchor.workspace.SystemApplyVelocity; - this.systemWithManyComponents = anchor.workspace.SystemWithManyComponents; - this.systemWithFewComponents = anchor.workspace.SystemWithFewComponents; - this.componentLarge = anchor.workspace.Large; this.componentSmall = anchor.workspace.Small; this.systemWith1Component = anchor.workspace.With1Component; this.systemWith2Components = anchor.workspace.With2Components; diff --git a/clients/typescript/test/intermediate-level/acceleration.ts b/clients/typescript/test/intermediate-level/acceleration.ts index d020aea9..c1aa3463 100644 --- a/clients/typescript/test/intermediate-level/acceleration.ts +++ b/clients/typescript/test/intermediate-level/acceleration.ts @@ -7,9 +7,6 @@ import { } from "../../lib"; import { expect } from "chai"; import { Direction } from "../framework"; -import { convertIdlToCamelCase } from "@coral-xyz/anchor/dist/cjs/idl"; -import { sendAndConfirmTransaction, Keypair } from "@solana/web3.js"; -import fs from "fs"; import { Framework } from "../framework"; export function acceleration(framework: Framework) { @@ -54,7 +51,7 @@ export function acceleration(framework: Framework) { delegateComponent.transaction, [], { - skipPreflight: true, + skipPreflight: false, commitment: "confirmed", }, ); @@ -64,37 +61,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)); + } + }); }); } diff --git a/clients/typescript/test/intermediate-level/ecs.ts b/clients/typescript/test/intermediate-level/ecs.ts index 6659c711..1144c471 100644 --- a/clients/typescript/test/intermediate-level/ecs.ts +++ b/clients/typescript/test/intermediate-level/ecs.ts @@ -253,14 +253,6 @@ export function ecs(framework: Framework) { applySystem.transaction, ); - let transactionResponse: any; - do { - transactionResponse = - await framework.provider.connection.getTransaction(signature, { - commitment: "confirmed", - }); - } while (transactionResponse?.meta?.logMessages === undefined); - const position = await framework.exampleComponentPosition.account.position.fetch( framework.componentPositionEntity1Pda, @@ -281,7 +273,6 @@ export function ecs(framework: Framework) { await framework.provider.sendAndConfirm(addEntity.transaction); entitiesPdas.push(addEntity.entityPda); } - let componentsPdas: web3.PublicKey[] = []; for (let i = 0; i < 10; i++) { const initializeComponent = await InitializeComponent({ @@ -321,19 +312,27 @@ export function ecs(framework: Framework) { })), }); - let signature = await framework.provider.sendAndConfirm( - applySystem.transaction, - ); - - let transactionResponse: any; - do { - transactionResponse = - await framework.provider.connection.getTransaction(signature, { - commitment: "confirmed", - }); - } while (transactionResponse?.meta?.logMessages === undefined); - let report = framework.report(transactionResponse?.meta?.logMessages); - reports.push(report); + try { + let signature = await framework.provider.sendAndConfirm( + applySystem.transaction, + ); + + let transactionResponse: any; + do { + transactionResponse = + await framework.provider.connection.getTransaction(signature, { + commitment: "confirmed", + }); + } while (transactionResponse?.meta?.logMessages === undefined); + let report = framework.report(transactionResponse?.meta?.logMessages); + reports.push(report); + } catch (error) { + reports.push({ + cpiCount: 0, + totalCpiCU: 0, + totalCu: 0, + }); + } } framework.saveReport(reports); diff --git a/clients/typescript/test/low-level/ecs.ts b/clients/typescript/test/low-level/ecs.ts index 43f7b8ca..021e6ff6 100644 --- a/clients/typescript/test/low-level/ecs.ts +++ b/clients/typescript/test/low-level/ecs.ts @@ -6,9 +6,9 @@ import { FindComponentProgramDataPda, FindEntityPda, SerializeArgs, - CPI_AUTH_ADDRESS, } from "../../lib"; import { Direction } from "../framework"; +import { FindBufferPda, FindCpiAuthPda } from "../../src"; export function ecs(framework) { describe("ECS", () => { @@ -105,7 +105,7 @@ export function ecs(framework) { data: framework.componentVelocityEntity1Pda, componentProgram: componentId, authority: framework.provider.wallet.publicKey, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .instruction(); const transaction = new anchor.web3.Transaction().add(instruction); @@ -126,7 +126,7 @@ export function ecs(framework) { data: framework.componentPositionEntity1Pda, componentProgram: componentId, authority: framework.worldProgram.programId, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .instruction(); const transaction = new anchor.web3.Transaction().add(instruction); @@ -147,7 +147,7 @@ export function ecs(framework) { data: componentPda, componentProgram: componentId, authority: framework.worldProgram.programId, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .instruction(); const transaction = new anchor.web3.Transaction().add(instruction); @@ -168,7 +168,7 @@ export function ecs(framework) { data: framework.componentPositionEntity4Pda, componentProgram: componentId, authority: framework.worldProgram.programId, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .instruction(); const transaction = new anchor.web3.Transaction().add(instruction); @@ -189,10 +189,11 @@ export function ecs(framework) { const instruction = await framework.worldProgram.methods .apply(SerializeArgs({ direction: Direction.Up })) .accounts({ + buffer: FindBufferPda(framework.componentPositionEntity1Pda), authority: framework.provider.wallet.publicKey, boltSystem: framework.systemSimpleMovement.programId, world: framework.worldPda, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .remainingAccounts([ { @@ -224,10 +225,11 @@ export function ecs(framework) { const instruction = await framework.worldProgram.methods .apply(SerializeArgs({ direction: Direction.Right })) .accounts({ + buffer: FindBufferPda(framework.componentPositionEntity1Pda), authority: framework.provider.wallet.publicKey, boltSystem: framework.systemSimpleMovement.programId, world: framework.worldPda, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .remainingAccounts([ { @@ -258,10 +260,11 @@ export function ecs(framework) { const instruction = await framework.worldProgram.methods .apply(SerializeArgs()) .accounts({ + buffer: FindBufferPda(framework.componentPositionEntity1Pda), authority: framework.provider.wallet.publicKey, boltSystem: framework.systemFly.programId, world: framework.worldPda, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .remainingAccounts([ { @@ -292,10 +295,11 @@ export function ecs(framework) { const instruction = await framework.worldProgram.methods .apply(SerializeArgs()) .accounts({ + buffer: FindBufferPda(framework.componentVelocityEntity1Pda), authority: framework.provider.wallet.publicKey, boltSystem: framework.systemApplyVelocity.programId, world: framework.worldPda, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .remainingAccounts([ { @@ -345,10 +349,11 @@ export function ecs(framework) { const instruction = await framework.worldProgram.methods .apply(SerializeArgs()) .accounts({ + buffer: FindBufferPda(framework.componentVelocityEntity1Pda), authority: framework.provider.wallet.publicKey, boltSystem: framework.systemApplyVelocity.programId, world: framework.worldPda, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .remainingAccounts([ { @@ -401,10 +406,11 @@ export function ecs(framework) { const instruction = await framework.worldProgram.methods .apply(SerializeArgs()) .accounts({ + buffer: FindBufferPda(framework.componentPositionEntity4Pda), authority: framework.provider.wallet.publicKey, boltSystem: framework.systemFly.programId, world: framework.worldPda, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .remainingAccounts([ { @@ -451,7 +457,7 @@ export function ecs(framework) { component: framework.componentVelocityEntity1Pda, componentProgramData: componentProgramData, receiver: keypair.publicKey, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .instruction(); const transaction = new anchor.web3.Transaction().add(instruction); diff --git a/clients/typescript/test/low-level/permissioning/component.ts b/clients/typescript/test/low-level/permissioning/component.ts index 610cf03f..2eff1253 100644 --- a/clients/typescript/test/low-level/permissioning/component.ts +++ b/clients/typescript/test/low-level/permissioning/component.ts @@ -4,7 +4,8 @@ import { FindEntityPda, FindComponentPda, SerializeArgs, - CPI_AUTH_ADDRESS, + FindBufferPda, + FindCpiAuthPda, } from "../../../lib"; import { assert, expect } from "chai"; @@ -47,7 +48,7 @@ export function component(framework) { data: component, componentProgram: componentId, authority: framework.provider.wallet.publicKey, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .instruction(); const transaction = new anchor.web3.Transaction().add(instruction); @@ -65,10 +66,11 @@ export function component(framework) { const instruction = await framework.worldProgram.methods .apply(SerializeArgs()) .accounts({ + buffer: FindBufferPda(component), authority: keypair.publicKey, boltSystem: framework.systemFly.programId, world: framework.worldPda, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .remainingAccounts([ { @@ -115,10 +117,11 @@ export function component(framework) { const instruction = await framework.worldProgram.methods .apply(SerializeArgs()) .accounts({ + buffer: FindBufferPda(component), authority: framework.provider.wallet.publicKey, boltSystem: framework.systemFly.programId, world: framework.worldPda, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .remainingAccounts([ { diff --git a/clients/typescript/test/low-level/permissioning/world.ts b/clients/typescript/test/low-level/permissioning/world.ts index bdff5924..53eabbaa 100644 --- a/clients/typescript/test/low-level/permissioning/world.ts +++ b/clients/typescript/test/low-level/permissioning/world.ts @@ -1,5 +1,10 @@ import { expect } from "chai"; -import { anchor, CPI_AUTH_ADDRESS, SerializeArgs } from "../../../lib"; +import { + anchor, + FindBufferPda, + FindCpiAuthPda, + SerializeArgs, +} from "../../../lib"; export function world(framework) { describe("World authority", () => { @@ -101,7 +106,7 @@ export function world(framework) { world: framework.worldPda, }) .instruction(); - let transaction = new anchor.web3.Transaction().add(instruction); + const transaction = new anchor.web3.Transaction().add(instruction); await framework.provider.sendAndConfirm(transaction, [], { skipPreflight: true, }); @@ -118,10 +123,11 @@ export function world(framework) { const instruction = await framework.worldProgram.methods .apply(SerializeArgs()) .accounts({ + buffer: FindBufferPda(framework.componentPositionEntity1Pda), authority: framework.provider.wallet.publicKey, boltSystem: framework.systemFly.programId, world: framework.worldPda, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .remainingAccounts([ { @@ -166,10 +172,11 @@ export function world(framework) { const instruction = await framework.worldProgram.methods .apply(SerializeArgs()) .accounts({ + buffer: FindBufferPda(framework.componentPositionEntity1Pda), authority: framework.provider.wallet.publicKey, boltSystem: framework.systemFly.programId, world: framework.worldPda, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .remainingAccounts([ { @@ -215,22 +222,5 @@ export function world(framework) { } expect(invalid).to.equal(true); }); - - it("Check invalid component update without CPI", async () => { - let invalid = false; - try { - await framework.exampleComponentPosition.methods - .update(Buffer.from("")) - .accounts({ - boltComponent: framework.componentPositionEntity4Pda, - authority: framework.provider.wallet.publicKey, - }) - .rpc(); - } catch (error) { - expect(error.message).to.contain("Error Code: InvalidCaller"); - invalid = true; - } - expect(invalid).to.equal(true); - }); }); } diff --git a/clients/typescript/test/low-level/session.ts b/clients/typescript/test/low-level/session.ts index f613ec29..9fb21aaa 100644 --- a/clients/typescript/test/low-level/session.ts +++ b/clients/typescript/test/low-level/session.ts @@ -7,7 +7,8 @@ import { SessionProgram, FindSessionTokenPda, BN, - CPI_AUTH_ADDRESS, + FindBufferPda, + FindCpiAuthPda, } from "../../lib"; import { Keypair } from "@solana/web3.js"; @@ -32,7 +33,7 @@ export function session(framework) { authority: framework.provider.wallet.publicKey, targetProgram: framework.worldProgram.programId, sessionToken, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .instruction(); const transaction = new anchor.web3.Transaction().add(instruction); @@ -53,7 +54,7 @@ export function session(framework) { payer: sessionSigner.publicKey, entity: entity, world: framework.worldPda, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .instruction(); const transaction = new anchor.web3.Transaction().add(instruction); @@ -74,7 +75,7 @@ export function session(framework) { data: component, componentProgram: componentId, authority: framework.worldProgram.programId, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .instruction(); const transaction = new anchor.web3.Transaction().add(instruction); @@ -90,11 +91,12 @@ export function session(framework) { const instruction = await framework.worldProgram.methods .applyWithSession(SerializeArgs()) .accounts({ + buffer: FindBufferPda(component), authority: sessionSigner.publicKey, boltSystem: framework.systemFly.programId, world: framework.worldPda, sessionToken, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .remainingAccounts([ { @@ -142,7 +144,7 @@ export function session(framework) { payer: sessionSigner.publicKey, world: framework.worldPda, entity: entityWithAuthority, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .instruction(); const transaction = new anchor.web3.Transaction().add(instruction); @@ -163,7 +165,7 @@ export function session(framework) { data: componentWithAuthority, componentProgram: componentId, authority: framework.provider.wallet.publicKey, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .instruction(); const transaction = new anchor.web3.Transaction().add(instruction); @@ -179,11 +181,12 @@ export function session(framework) { const instruction = await framework.worldProgram.methods .applyWithSession(SerializeArgs()) .accounts({ + buffer: FindBufferPda(componentWithAuthority), authority: sessionSigner.publicKey, boltSystem: framework.systemFly.programId, world: framework.worldPda, sessionToken, - cpiAuth: CPI_AUTH_ADDRESS, + cpiAuth: FindCpiAuthPda(), }) .remainingAccounts([ { diff --git a/clients/typescript/yarn.lock b/clients/typescript/yarn.lock index d13a8bee..d12e8e63 100644 --- a/clients/typescript/yarn.lock +++ b/clients/typescript/yarn.lock @@ -82,7 +82,7 @@ bs58 "^5.0.0" debug "^4.3.4" -"@metaplex-foundation/beet@>=0.1.0", "@metaplex-foundation/beet@^0.7.1", "@metaplex-foundation/beet@^0.7.2": +"@metaplex-foundation/beet@^0.7.1", "@metaplex-foundation/beet@^0.7.2", "@metaplex-foundation/beet@>=0.1.0": version "0.7.2" resolved "https://registry.npmjs.org/@metaplex-foundation/beet/-/beet-0.7.2.tgz" integrity sha512-K+g3WhyFxKPc0xIvcIjNyV1eaTVJTiuaHZpig7Xx0MuYRMoJLLvhLTnUXhFdR5Tu2l2QSyKwfyXDgZlzhULqFg== @@ -126,7 +126,7 @@ dependencies: "@noble/hashes" "1.7.1" -"@noble/hashes@1.7.1", "@noble/hashes@^1.3.1", "@noble/hashes@^1.4.0": +"@noble/hashes@^1.3.1", "@noble/hashes@^1.4.0", "@noble/hashes@1.7.1": version "1.7.1" resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz" integrity sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ== @@ -202,14 +202,6 @@ dependencies: "@types/node" "*" -JSONStream@^1.3.5: - version "1.3.5" - resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" - integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== - dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" - agentkeepalive@^4.5.0: version "4.6.0" resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz" @@ -348,7 +340,7 @@ buffer-layout@^1.2.0, buffer-layout@^1.2.2: resolved "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.2.tgz" integrity sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA== -buffer@6.0.3, buffer@^6.0.3, buffer@~6.0.3: +buffer@^6.0.3, buffer@~6.0.3, buffer@6.0.3: version "6.0.3" resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== @@ -766,13 +758,13 @@ jayson@^4.1.1: "@types/connect" "^3.4.33" "@types/node" "^12.12.54" "@types/ws" "^7.4.4" - JSONStream "^1.3.5" commander "^2.20.3" delay "^5.0.0" es6-promisify "^5.0.0" eyes "^0.1.8" isomorphic-ws "^4.0.1" json-stringify-safe "^5.0.1" + JSONStream "^1.3.5" uuid "^8.3.2" ws "^7.5.10" @@ -796,6 +788,14 @@ jsonparse@^1.2.0: resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz" integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== +JSONStream@^1.3.5: + version "1.3.5" + resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + loglevel@^1.9.2: version "1.9.2" resolved "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz" @@ -1147,7 +1147,7 @@ typedoc-plugin-markdown@^3.17.1: dependencies: handlebars "^4.7.7" -typedoc@^0.25.4: +typedoc@^0.25.4, typedoc@>=0.24.0: version "0.25.13" resolved "https://registry.npmjs.org/typedoc/-/typedoc-0.25.13.tgz" integrity sha512-pQqiwiJ+Z4pigfOnnysObszLiU3mVLWAExSPf+Mu06G/qsc3wzbuM56SZQvONhHLncLUhYzOVkjFFpFfL5AzhQ== @@ -1157,7 +1157,7 @@ typedoc@^0.25.4: minimatch "^9.0.3" shiki "^0.14.7" -typescript@^4.5.5: +typescript@^4.5.5, "typescript@4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x": version "4.9.5" resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== @@ -1167,7 +1167,7 @@ uglify-js@^3.1.4: resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz" integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== -utf-8-validate@^5.0.2: +utf-8-validate@^5.0.2, utf-8-validate@>=5.0.2: version "5.0.10" resolved "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz" integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ== @@ -1255,7 +1255,7 @@ wrap-ansi@^8.1.0: string-width "^5.0.1" strip-ansi "^7.0.1" -ws@^7.5.10: +ws@*, ws@^7.5.10: version "7.5.10" resolved "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== diff --git a/crates/bolt-cli/src/templates/component/mod.rs b/crates/bolt-cli/src/templates/component/mod.rs index 0cb9dee2..cd518b21 100644 --- a/crates/bolt-cli/src/templates/component/mod.rs +++ b/crates/bolt-cli/src/templates/component/mod.rs @@ -38,18 +38,20 @@ pub fn component_type(idl: &Idl, component_id: &str) -> Result { Some(ty) => ty, None => return Err(anyhow::anyhow!("Component type not found in IDL")), }; + let component_code = component_to_rust_code(type_def, component_id); let types_code = component_types_to_rust_code(&idl.types, &component_account.name); Ok(format!( r#"use bolt_lang::*; -#[component_deserialize] -#[derive(Clone, Copy)] -{} +#[component_deserialize({component_name})] +{component_code} -{} +{types_code} "#, - component_code, types_code + component_name = component_account.name, + component_code = component_code, + types_code = types_code )) } @@ -160,8 +162,9 @@ fn component_type_to_rust_code(component_type: &IdlTypeDef) -> String { }; if let IdlTypeDefTy::Struct { fields } = &component_type.ty { code += &format!( - "#[component_deserialize]\n#[derive(Clone, Copy)]\npub struct {}{} {{\n", - component_type.name, generics + "#[component_deserialize({name})]\n#[derive(Clone, Copy)]\npub struct {name}{generics} {{\n", + name = component_type.name, + generics = generics ); code += &*component_fields_to_rust_code(fields); code += "}\n\n"; diff --git a/crates/bolt-lang/attribute/bolt-program/Cargo.toml b/crates/bolt-lang/attribute/bolt-program/Cargo.toml index 86e2a89d..88c441e8 100644 --- a/crates/bolt-lang/attribute/bolt-program/Cargo.toml +++ b/crates/bolt-lang/attribute/bolt-program/Cargo.toml @@ -15,3 +15,4 @@ proc-macro = true syn = { workspace = true } quote = { workspace = true } proc-macro2 = { workspace = true } +bolt-utils = { workspace = true } \ No newline at end of file diff --git a/crates/bolt-lang/attribute/bolt-program/src/lib.rs b/crates/bolt-lang/attribute/bolt-program/src/lib.rs index bd81804c..54a02e72 100644 --- a/crates/bolt-lang/attribute/bolt-program/src/lib.rs +++ b/crates/bolt-lang/attribute/bolt-program/src/lib.rs @@ -34,9 +34,7 @@ pub fn bolt_program(args: TokenStream, input: TokenStream) -> TokenStream { extract_type_name(&args).expect("Expected a component type in macro arguments"); let modified = modify_component_module(ast, &component_type); let additional_macro: Attribute = parse_quote! { #[program] }; - let cpi_checker = generate_cpi_checker(); TokenStream::from(quote! { - #cpi_checker #additional_macro #modified }) @@ -46,20 +44,19 @@ pub fn bolt_program(args: TokenStream, input: TokenStream) -> TokenStream { fn modify_component_module(mut module: ItemMod, component_type: &Type) -> ItemMod { let (initialize_fn, initialize_struct) = generate_initialize(component_type); let (destroy_fn, destroy_struct) = generate_destroy(component_type); - let (update_fn, update_with_session_fn, update_struct, update_with_session_struct) = - generate_update(component_type); - + let set_owner = bolt_utils::instructions::generate_set_owner(); + let set_data = bolt_utils::instructions::generate_set_data(); module.content = module.content.map(|(brace, mut items)| { items.extend( vec![ initialize_fn, initialize_struct, - update_fn, - update_struct, - update_with_session_fn, - update_with_session_struct, destroy_fn, destroy_struct, + set_owner.function, + set_owner.accounts, + set_data.function, + set_data.accounts, ] .into_iter() .map(|item| syn::parse2(item).unwrap()) @@ -119,18 +116,6 @@ fn create_check_attribute() -> Attribute { } } -/// Generates the CPI checker function. -fn generate_cpi_checker() -> TokenStream2 { - quote! { - fn cpi_checker<'info>(cpi_auth: &AccountInfo<'info>) -> Result<()> { - if !cpi_auth.is_signer || cpi_auth.key != &bolt_lang::world::World::cpi_auth_address() { - return Err(BoltError::InvalidCaller.into()); - } - Ok(()) - } - } -} - /// Generates the destroy function and struct. fn generate_destroy(component_type: &Type) -> (TokenStream2, TokenStream2) { ( @@ -160,7 +145,7 @@ fn generate_destroy(component_type: &Type) -> (TokenStream2, TokenStream2) { return Err(BoltError::InvalidAuthority.into()); } - cpi_checker(&ctx.accounts.cpi_auth.to_account_info())?; + bolt_lang::cpi::checker(&ctx.accounts.cpi_auth.to_account_info())?; Ok(()) } @@ -193,7 +178,7 @@ fn generate_initialize(component_type: &Type) -> (TokenStream2, TokenStream2) { quote! { #[automatically_derived] pub fn initialize(ctx: Context) -> Result<()> { - cpi_checker(&ctx.accounts.cpi_auth.to_account_info())?; + bolt_lang::cpi::checker(&ctx.accounts.cpi_auth.to_account_info())?; ctx.accounts.data.set_inner(<#component_type>::default()); ctx.accounts.data.bolt_metadata.authority = *ctx.accounts.authority.key; Ok(()) @@ -219,73 +204,6 @@ fn generate_initialize(component_type: &Type) -> (TokenStream2, TokenStream2) { ) } -/// Generates the instructions and related structs to inject in the component. -fn generate_update( - component_type: &Type, -) -> (TokenStream2, TokenStream2, TokenStream2, TokenStream2) { - ( - quote! { - #[automatically_derived] - pub fn update(ctx: Context, data: Vec) -> Result<()> { - require!(ctx.accounts.bolt_component.bolt_metadata.authority == World::id() || (ctx.accounts.bolt_component.bolt_metadata.authority == *ctx.accounts.authority.key && ctx.accounts.authority.is_signer), BoltError::InvalidAuthority); - - cpi_checker(&ctx.accounts.cpi_auth.to_account_info())?; - - ctx.accounts.bolt_component.set_inner(<#component_type>::try_from_slice(&data)?); - Ok(()) - } - }, - quote! { - #[automatically_derived] - pub fn update_with_session(ctx: Context, data: Vec) -> Result<()> { - if ctx.accounts.bolt_component.bolt_metadata.authority == World::id() { - require!(Clock::get()?.unix_timestamp < ctx.accounts.session_token.valid_until, bolt_lang::session_keys::SessionError::InvalidToken); - } else { - let validity_ctx = bolt_lang::session_keys::ValidityChecker { - session_token: ctx.accounts.session_token.clone(), - session_signer: ctx.accounts.authority.clone(), - authority: ctx.accounts.bolt_component.bolt_metadata.authority.clone(), - target_program: World::id(), - }; - require!(ctx.accounts.session_token.validate(validity_ctx)?, bolt_lang::session_keys::SessionError::InvalidToken); - require_eq!(ctx.accounts.bolt_component.bolt_metadata.authority, ctx.accounts.session_token.authority, bolt_lang::session_keys::SessionError::InvalidToken); - } - - cpi_checker(&ctx.accounts.cpi_auth.to_account_info())?; - - ctx.accounts.bolt_component.set_inner(<#component_type>::try_from_slice(&data)?); - Ok(()) - } - }, - quote! { - #[automatically_derived] - #[derive(Accounts)] - pub struct Update<'info> { - #[account()] - pub cpi_auth: Signer<'info>, - #[account(mut)] - pub bolt_component: Account<'info, #component_type>, - #[account()] - pub authority: Signer<'info>, - } - }, - quote! { - #[automatically_derived] - #[derive(Accounts)] - pub struct UpdateWithSession<'info> { - #[account()] - pub cpi_auth: Signer<'info>, - #[account(mut)] - pub bolt_component: Account<'info, #component_type>, - #[account()] - pub authority: Signer<'info>, - #[account(constraint = session_token.to_account_info().owner == &bolt_lang::session_keys::ID)] - pub session_token: Account<'info, bolt_lang::session_keys::SessionToken>, - } - }, - ) -} - /// Checks if the field is expecting a program. fn is_expecting_program(field: &Field) -> bool { field.ty.to_token_stream().to_string().contains("Program") diff --git a/crates/bolt-lang/attribute/component-deserialize/Cargo.toml b/crates/bolt-lang/attribute/component-deserialize/Cargo.toml index 89ff8eb3..268ca6a6 100644 --- a/crates/bolt-lang/attribute/component-deserialize/Cargo.toml +++ b/crates/bolt-lang/attribute/component-deserialize/Cargo.toml @@ -16,3 +16,4 @@ syn = { workspace = true } bolt-utils = { workspace = true } quote = { workspace = true } proc-macro2 = { workspace = true } +sha2 = { workspace = true } diff --git a/crates/bolt-lang/attribute/component-deserialize/src/lib.rs b/crates/bolt-lang/attribute/component-deserialize/src/lib.rs index 6ec8d234..6c42e34a 100644 --- a/crates/bolt-lang/attribute/component-deserialize/src/lib.rs +++ b/crates/bolt-lang/attribute/component-deserialize/src/lib.rs @@ -1,8 +1,11 @@ -use bolt_utils::add_bolt_metadata; +use bolt_utils::metadata::add_bolt_metadata; use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, Attribute, DeriveInput}; +// For computing Anchor discriminator at compile time +use sha2::{Digest, Sha256}; + /// This macro is used to defined a struct as a BOLT component and automatically implements the /// `ComponentDeserialize` and `AccountDeserialize` traits for the struct. /// @@ -14,7 +17,7 @@ use syn::{parse_macro_input, Attribute, DeriveInput}; /// } /// ``` #[proc_macro_attribute] -pub fn component_deserialize(_attr: TokenStream, item: TokenStream) -> TokenStream { +pub fn component_deserialize(attr: TokenStream, item: TokenStream) -> TokenStream { let mut input = parse_macro_input!(item as DeriveInput); // Add the AnchorDeserialize and AnchorSerialize derives to the struct @@ -38,6 +41,38 @@ pub fn component_deserialize(_attr: TokenStream, item: TokenStream) -> TokenStre } }; } + // Determine which struct name to use for Anchor discriminator calculation. + // If the attribute is written as #[component_deserialize(Position)], then use "Position". + // Otherwise, fall back to the actual type name (which may be Component style). + let discriminator_type_name: String = if !attr.is_empty() { + // Parse the attribute as a path and use its last segment as the name + if let Ok(path) = syn::parse::(attr.clone()) { + path.segments + .last() + .map(|seg| seg.ident.to_string()) + .unwrap_or_else(|| name.to_string()) + } else { + name.to_string() + } + } else { + name.to_string() + }; + + // Compute Anchor discriminator: first 8 bytes of sha256(b"account:" + type_name) + let mut hasher = Sha256::new(); + hasher.update(b"account:"); + hasher.update(discriminator_type_name.as_bytes()); + let digest = hasher.finalize(); + let discriminator_bytes: [u8; 8] = { + let mut arr = [0u8; 8]; + arr.copy_from_slice(&digest[..8]); + arr + }; + let discriminator_tokens = discriminator_bytes + .iter() + .map(|b| quote! { #b }) + .collect::>(); + let expanded = quote! { #input @@ -63,14 +98,22 @@ pub fn component_deserialize(_attr: TokenStream, item: TokenStream) -> TokenStre #[automatically_derived] impl bolt_lang::AccountSerialize for #name { - fn try_serialize(&self, _writer: &mut W) -> Result<()> { + fn try_serialize(&self, writer: &mut W) -> Result<()> { + if writer.write_all(Self::DISCRIMINATOR).is_err() { + return Err(bolt_lang::AccountDidNotSerializeErrorCode.into()); + } + if bolt_lang::AnchorSerialize::serialize(self, writer).is_err() { + return Err(bolt_lang::AccountDidNotSerializeErrorCode.into()); + } Ok(()) } } #[automatically_derived] impl anchor_lang::Discriminator for #name { - const DISCRIMINATOR: &'static [u8] = &[1, 1, 1, 1, 1, 1, 1, 1]; + const DISCRIMINATOR: &'static [u8] = &[ + #(#discriminator_tokens),* + ]; } #owner_definition diff --git a/crates/bolt-lang/attribute/component/src/lib.rs b/crates/bolt-lang/attribute/component/src/lib.rs index 2f4fa649..88d87c94 100644 --- a/crates/bolt-lang/attribute/component/src/lib.rs +++ b/crates/bolt-lang/attribute/component/src/lib.rs @@ -6,7 +6,7 @@ use syn::{ NestedMeta, }; -use bolt_utils::add_bolt_metadata; +use bolt_utils::metadata::add_bolt_metadata; use heck::ToSnakeCase; /// This Component attribute is used to automatically generate the seed and size functions diff --git a/crates/bolt-lang/attribute/system-input/src/lib.rs b/crates/bolt-lang/attribute/system-input/src/lib.rs index 2701aa8c..d7b4ade0 100644 --- a/crates/bolt-lang/attribute/system-input/src/lib.rs +++ b/crates/bolt-lang/attribute/system-input/src/lib.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use proc_macro::TokenStream; use quote::quote; @@ -61,13 +63,21 @@ pub fn system_input(_attr: TokenStream, item: TokenStream) -> TokenStream { }) .collect(); + let unique_fields = fields.iter().map(|f| f.ty.clone()).collect::>(); + let bolt_accounts = unique_fields.iter().map(|f| { + let field_type = &f; + quote! { + pub type #field_type = bolt_lang::account::BoltAccount; + } + }); + // Transform fields for the struct definition let transformed_fields = fields.iter().map(|f| { let field_name = &f.ident; let field_type = &f.ty; quote! { - #[account()] - pub #field_name: Account<'info, #field_type>, + #[account(mut)] + pub #field_name: Account<'info, bolt_accounts::#field_type>, } }); @@ -82,18 +92,12 @@ pub fn system_input(_attr: TokenStream, item: TokenStream) -> TokenStream { } }; - // Generate the try_to_vec method - let try_to_vec_fields = fields.iter().map(|f| { - let field_name = &f.ident; - quote! { - self.#field_name.try_to_vec()? - } - }); - let try_from_fields = fields.iter().enumerate().map(|(i, f)| { let field_name = &f.ident; quote! { - #field_name: Account::try_from(context.remaining_accounts.as_ref().get(#i).ok_or_else(|| ErrorCode::ConstraintAccountIsNone)?)?, + #field_name: { + Account::try_from(context.remaining_accounts.as_ref().get(#i).ok_or_else(|| ErrorCode::ConstraintAccountIsNone)?)? + }, } }); @@ -111,13 +115,9 @@ pub fn system_input(_attr: TokenStream, item: TokenStream) -> TokenStream { } }; - // Generate the implementation of try_to_vec for the struct + // Generate the implementation of try_from for the struct let output_impl = quote! { impl<'info> #name<'info> { - pub fn try_to_vec(&self) -> Result>> { - Ok(vec![#(#try_to_vec_fields,)*]) - } - fn try_from<'a, 'b>(context: &Context<'a, 'b, 'info, 'info, VariadicBoltComponents<'info>>) -> Result { Ok(Self { authority: context.accounts.authority.clone(), @@ -129,6 +129,10 @@ pub fn system_input(_attr: TokenStream, item: TokenStream) -> TokenStream { // Combine the struct definition and its implementation into the final TokenStream let output = quote! { + mod bolt_accounts { + #(#bolt_accounts)* + } + #output_struct #output_impl #output_trait diff --git a/crates/bolt-lang/attribute/system/Cargo.toml b/crates/bolt-lang/attribute/system/Cargo.toml index 0475629b..83142cce 100644 --- a/crates/bolt-lang/attribute/system/Cargo.toml +++ b/crates/bolt-lang/attribute/system/Cargo.toml @@ -14,4 +14,5 @@ proc-macro = true [dependencies] syn = { workspace = true, features = ["visit-mut"] } quote = { workspace = true } -proc-macro2 = { workspace = true } \ No newline at end of file +proc-macro2 = { workspace = true } +bolt-utils = { workspace = true } \ No newline at end of file diff --git a/crates/bolt-lang/attribute/system/src/lib.rs b/crates/bolt-lang/attribute/system/src/lib.rs index 9fc47052..04d2db71 100644 --- a/crates/bolt-lang/attribute/system/src/lib.rs +++ b/crates/bolt-lang/attribute/system/src/lib.rs @@ -3,7 +3,7 @@ use proc_macro2::Ident; use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ parse_macro_input, parse_quote, visit_mut::VisitMut, Expr, FnArg, GenericArgument, ItemFn, - ItemMod, ItemStruct, PathArguments, ReturnType, Stmt, Type, TypePath, + ItemMod, ItemStruct, PathArguments, Stmt, Type, }; #[derive(Default)] @@ -46,6 +46,8 @@ pub fn system(_attr: TokenStream, item: TokenStream) -> TokenStream { if let Some((_, ref mut items)) = ast.content { items.insert(0, syn::Item::Use(use_super)); SystemTransform::add_variadic_execute_function(items); + SystemTransform::add_set_data_function(items); + SystemTransform::add_set_owner_function(items); } let mut transform = SystemTransform; @@ -123,17 +125,6 @@ impl VisitMut for SystemTransform { // Modify the return type of the system function to Result,*> fn visit_item_fn_mut(&mut self, item_fn: &mut ItemFn) { if item_fn.sig.ident == "execute" { - // Modify the return type to Result> if necessary - if let ReturnType::Type(_, type_box) = &item_fn.sig.output { - if let Type::Path(type_path) = &**type_box { - if !Self::check_is_result_vec_u8(type_path) { - item_fn.sig.output = parse_quote! { -> Result>> }; - // Modify the return statement inside the function body - let block = &mut item_fn.block; - self.visit_stmts_mut(&mut block.stmts); - } - } - } // If second argument is not Vec, modify it to be so and use parse_args Self::modify_args(item_fn); } @@ -188,40 +179,27 @@ impl VisitMut for SystemTransform { impl SystemTransform { fn add_variadic_execute_function(content: &mut Vec) { content.push(syn::parse2(quote! { - pub fn bolt_execute<'info>(ctx: Context<'_, '_, 'info, 'info, VariadicBoltComponents<'info>>, args: Vec) -> Result>> { + pub fn bolt_execute<'info>(ctx: Context<'_, '_, 'info, 'info, VariadicBoltComponents<'info>>, args: Vec) -> Result<()> { let mut components = Components::try_from(&ctx)?; let bumps = ComponentsBumps {}; let context = Context::new(ctx.program_id, &mut components, ctx.remaining_accounts, bumps); - execute(context, args) + execute(context, args)?; + components.exit(&crate::id())?; + Ok(()) } }).unwrap()); } - // Helper function to check if a type is `Vec` or `(Vec, Vec, ...)` - fn check_is_result_vec_u8(ty: &TypePath) -> bool { - if let Some(segment) = ty.path.segments.last() { - if segment.ident == "Result" { - if let PathArguments::AngleBracketed(args) = &segment.arguments { - if let Some(GenericArgument::Type(Type::Tuple(tuple))) = args.args.first() { - return tuple.elems.iter().all(|elem| { - if let Type::Path(type_path) = elem { - if let Some(segment) = type_path.path.segments.first() { - return segment.ident == "Vec" && Self::is_u8_vec(segment); - } - } - false - }); - } else if let Some(GenericArgument::Type(Type::Path(type_path))) = - args.args.first() - { - if let Some(segment) = type_path.path.segments.first() { - return segment.ident == "Vec" && Self::is_u8_vec(segment); - } - } - } - } - } - false + fn add_set_data_function(content: &mut Vec) { + let set_data = bolt_utils::instructions::generate_set_data(); + content.push(syn::parse2(set_data.function).unwrap()); + content.push(syn::parse2(set_data.accounts).unwrap()); + } + + fn add_set_owner_function(content: &mut Vec) { + let set_owner = bolt_utils::instructions::generate_set_owner(); + content.push(syn::parse2(set_owner.function).unwrap()); + content.push(syn::parse2(set_owner.accounts).unwrap()); } // Helper function to check if a type is Vec diff --git a/crates/bolt-lang/src/account.rs b/crates/bolt-lang/src/account.rs new file mode 100644 index 00000000..2a7d8beb --- /dev/null +++ b/crates/bolt-lang/src/account.rs @@ -0,0 +1,135 @@ +use anchor_lang::prelude::*; + +/// BoltAccount used as a workaround for altering the account ownership check during deserialization. +/// P0 and P1 are the two parts of the pubkey and it's used on the Owner trait implementation. +#[derive(Clone)] +pub struct BoltAccount(T); + +impl Discriminator for BoltAccount { + const DISCRIMINATOR: &'static [u8] = T::DISCRIMINATOR; +} + +impl + anchor_lang::AccountDeserialize for BoltAccount +{ + fn try_deserialize(data: &mut &[u8]) -> Result { + Ok(BoltAccount(T::try_deserialize(data)?)) + } + + fn try_deserialize_unchecked(data: &mut &[u8]) -> Result { + Ok(BoltAccount(T::try_deserialize_unchecked(data)?)) + } +} + +impl anchor_lang::AccountSerialize + for BoltAccount +{ + fn try_serialize(&self, writer: &mut W) -> Result<()> { + self.0.try_serialize(writer) + } +} + +impl anchor_lang::Owner + for BoltAccount +{ + fn owner() -> Pubkey { + pubkey_from_array([P0, P1]) + } +} + +impl<'info, T: anchor_lang::ToAccountInfos<'info>, const P0: u128, const P1: u128> + anchor_lang::ToAccountInfos<'info> for BoltAccount +{ + fn to_account_infos(&self) -> Vec> { + self.0.to_account_infos() + } +} + +impl anchor_lang::ToAccountMetas + for BoltAccount +{ + fn to_account_metas(&self, is_signer: Option) -> Vec { + self.0.to_account_metas(is_signer) + } +} + +impl< + 'info, + T: anchor_lang::ToAccountInfos<'info> + + anchor_lang::ToAccountInfo<'info> + + anchor_lang::AccountSerialize + + anchor_lang::AccountsExit<'info>, + const P0: u128, + const P1: u128, + > anchor_lang::AccountsExit<'info> for BoltAccount +{ + fn exit(&self, _program_id: &Pubkey) -> Result<()> { + let info = self.0.to_account_info(); + let mut data = info.try_borrow_mut_data()?; + let dst: &mut [u8] = &mut data; + let mut writer = crate::bpf_writer::BpfWriter::new(dst); + self.0.try_serialize(&mut writer)?; + Ok(()) + } +} + +impl std::ops::Deref for BoltAccount { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for BoltAccount { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[cfg(feature = "idl-build")] +impl anchor_lang::IdlBuild + for BoltAccount +{ + fn create_type() -> Option { + T::create_type() + } + fn insert_types( + types: &mut std::collections::BTreeMap, + ) { + T::insert_types(types); + } + fn get_full_path() -> String { + T::get_full_path() + } +} + +pub const fn pubkey_p0(key: Pubkey) -> u128 { + let bytes = key.to_bytes(); + u128::from_le_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], + bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], + ]) +} + +pub const fn pubkey_p1(key: Pubkey) -> u128 { + let bytes = key.to_bytes(); + u128::from_le_bytes([ + bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23], + bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], bytes[31], + ]) +} + +pub const fn pubkey_from_u128s(p0: u128, p1: u128) -> Pubkey { + pubkey_from_array([p0, p1]) +} + +pub const fn pubkey_from_array(array: [u128; 2]) -> Pubkey { + let p0 = array[0].to_le_bytes(); + let p1 = array[1].to_le_bytes(); + Pubkey::new_from_array([ + p0[0], p0[1], p0[2], p0[3], p0[4], p0[5], p0[6], p0[7], p0[8], p0[9], p0[10], p0[11], + p0[12], p0[13], p0[14], p0[15], p1[0], p1[1], p1[2], p1[3], p1[4], p1[5], p1[6], p1[7], + p1[8], p1[9], p1[10], p1[11], p1[12], p1[13], p1[14], p1[15], + ]) +} diff --git a/crates/bolt-lang/src/bpf_writer.rs b/crates/bolt-lang/src/bpf_writer.rs new file mode 100644 index 00000000..ee1a25d6 --- /dev/null +++ b/crates/bolt-lang/src/bpf_writer.rs @@ -0,0 +1,47 @@ +/// Implementation from Anchor. +use solana_program::program_memory::sol_memcpy; +use std::cmp; +use std::io::{self, Write}; + +#[derive(Debug, Default)] +pub struct BpfWriter { + inner: T, + pub pos: u64, +} + +impl BpfWriter { + pub fn new(inner: T) -> Self { + Self { inner, pos: 0 } + } +} + +impl Write for BpfWriter<&mut [u8]> { + fn write(&mut self, buf: &[u8]) -> io::Result { + if self.pos >= self.inner.len() as u64 { + return Ok(0); + } + + let amt = cmp::min( + self.inner.len().saturating_sub(self.pos as usize), + buf.len(), + ); + sol_memcpy(&mut self.inner[(self.pos as usize)..], buf, amt); + self.pos += amt as u64; + Ok(amt) + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + if self.write(buf)? == buf.len() { + Ok(()) + } else { + Err(io::Error::new( + io::ErrorKind::WriteZero, + "failed to write whole buffer", + )) + } + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} diff --git a/crates/bolt-lang/src/cpi.rs b/crates/bolt-lang/src/cpi.rs new file mode 100644 index 00000000..b3f08dd9 --- /dev/null +++ b/crates/bolt-lang/src/cpi.rs @@ -0,0 +1,10 @@ +use crate::prelude::*; +use crate::BoltError; + +#[inline(always)] +pub fn checker(cpi_auth: &AccountInfo<'_>) -> Result<()> { + if !cpi_auth.is_signer || cpi_auth.key != crate::world::World::cpi_auth_address() { + return Err(BoltError::InvalidCaller.into()); + } + Ok(()) +} diff --git a/crates/bolt-lang/src/instructions/mod.rs b/crates/bolt-lang/src/instructions/mod.rs new file mode 100644 index 00000000..063447af --- /dev/null +++ b/crates/bolt-lang/src/instructions/mod.rs @@ -0,0 +1,5 @@ +mod set_owner; +pub use set_owner::*; + +mod set_data; +pub use set_data::*; diff --git a/crates/bolt-lang/src/instructions/set_data.rs b/crates/bolt-lang/src/instructions/set_data.rs new file mode 100644 index 00000000..729c8edf --- /dev/null +++ b/crates/bolt-lang/src/instructions/set_data.rs @@ -0,0 +1,15 @@ +use crate::prelude::*; + +#[inline(always)] +pub fn set_data<'info>( + cpi_auth: AccountInfo<'info>, + buffer: AccountInfo<'info>, + component: AccountInfo<'info>, +) -> Result<()> { + crate::cpi::checker(&cpi_auth)?; + let buffer_data = buffer.try_borrow_data()?; + component.realloc(buffer_data.len(), false)?; + let mut component_data = component.try_borrow_mut_data()?; + component_data.copy_from_slice(&buffer_data); + Ok(()) +} diff --git a/crates/bolt-lang/src/instructions/set_owner.rs b/crates/bolt-lang/src/instructions/set_owner.rs new file mode 100644 index 00000000..7dba1317 --- /dev/null +++ b/crates/bolt-lang/src/instructions/set_owner.rs @@ -0,0 +1,12 @@ +use crate::prelude::*; + +pub fn set_owner<'info>( + cpi_auth: AccountInfo<'info>, + component: AccountInfo<'info>, + owner: Pubkey, +) -> Result<()> { + crate::cpi::checker(&cpi_auth)?; + component.realloc(0, false)?; + component.assign(&owner); + Ok(()) +} diff --git a/crates/bolt-lang/src/lib.rs b/crates/bolt-lang/src/lib.rs index 714d1c99..0c8a17a9 100644 --- a/crates/bolt-lang/src/lib.rs +++ b/crates/bolt-lang/src/lib.rs @@ -2,11 +2,19 @@ pub mod prelude; pub use anchor_lang; pub use anchor_lang::error::ErrorCode::AccountDidNotDeserialize as AccountDidNotDeserializeErrorCode; +pub use anchor_lang::error::ErrorCode::AccountDidNotSerialize as AccountDidNotSerializeErrorCode; pub use anchor_lang::prelude::*; pub use anchor_lang::{ AccountDeserialize, AccountSerialize, AnchorDeserialize, AnchorSerialize, Bumps, Result, }; +pub mod bpf_writer; +pub mod instructions; +pub use instructions::*; + +pub mod account; +pub mod cpi; +pub use account::BoltAccount; pub use session_keys; pub use bolt_attribute_bolt_arguments::arguments; diff --git a/crates/bolt-lang/utils/src/instructions/mod.rs b/crates/bolt-lang/utils/src/instructions/mod.rs new file mode 100644 index 00000000..acfbcf5d --- /dev/null +++ b/crates/bolt-lang/utils/src/instructions/mod.rs @@ -0,0 +1,12 @@ +mod set_owner; +pub use set_owner::*; + +mod set_data; +pub use set_data::*; + +use proc_macro2::TokenStream; + +pub struct InstructionGeneration { + pub function: TokenStream, + pub accounts: TokenStream, +} diff --git a/crates/bolt-lang/utils/src/instructions/set_data.rs b/crates/bolt-lang/utils/src/instructions/set_data.rs new file mode 100644 index 00000000..b552c8c6 --- /dev/null +++ b/crates/bolt-lang/utils/src/instructions/set_data.rs @@ -0,0 +1,27 @@ +use crate::instructions::InstructionGeneration; +use quote::quote; + +pub fn generate_set_data() -> InstructionGeneration { + InstructionGeneration { + function: quote! { + #[automatically_derived] + pub fn set_data(ctx: Context) -> Result<()> { + bolt_lang::instructions::set_data(ctx.accounts.cpi_auth.to_account_info(), ctx.accounts.buffer.to_account_info(), ctx.accounts.component.to_account_info()) + } + }, + accounts: quote! { + #[automatically_derived] + #[derive(Accounts)] + pub struct SetData<'info> { + #[account()] + pub cpi_auth: Signer<'info>, + /// CHECK: buffer data check + #[account()] + pub buffer: UncheckedAccount<'info>, + /// CHECK: component data check + #[account(mut)] + pub component: UncheckedAccount<'info>, + } + }, + } +} diff --git a/crates/bolt-lang/utils/src/instructions/set_owner.rs b/crates/bolt-lang/utils/src/instructions/set_owner.rs new file mode 100644 index 00000000..c8783918 --- /dev/null +++ b/crates/bolt-lang/utils/src/instructions/set_owner.rs @@ -0,0 +1,25 @@ +use crate::instructions::InstructionGeneration; +use quote::quote; + +/// Generate the set owner function and struct. +pub fn generate_set_owner() -> InstructionGeneration { + InstructionGeneration { + function: quote! { + #[automatically_derived] + pub fn set_owner(ctx: Context, owner: Pubkey) -> Result<()> { + bolt_lang::instructions::set_owner(ctx.accounts.cpi_auth.to_account_info(), ctx.accounts.component.to_account_info(), owner) + } + }, + accounts: quote! { + #[automatically_derived] + #[derive(Accounts)] + pub struct SetOwner<'info> { + #[account()] + pub cpi_auth: Signer<'info>, + /// CHECK: This is a component account. + #[account(mut)] + pub component: UncheckedAccount<'info>, + } + }, + } +} diff --git a/crates/bolt-lang/utils/src/lib.rs b/crates/bolt-lang/utils/src/lib.rs index 1d44e75d..52684bc5 100644 --- a/crates/bolt-lang/utils/src/lib.rs +++ b/crates/bolt-lang/utils/src/lib.rs @@ -1,22 +1,2 @@ -use proc_macro2::Ident; -use syn::{DeriveInput, Field, Type, Visibility}; - -pub fn add_bolt_metadata(input: &mut DeriveInput) { - let authority_field: Field = Field { - attrs: vec![], - vis: Visibility::Public(syn::VisPublic { - pub_token: Default::default(), - }), - ident: Some(Ident::new("bolt_metadata", proc_macro2::Span::call_site())), - colon_token: Some(Default::default()), - ty: Type::Path(syn::TypePath { - qself: None, - path: syn::Path::from(Ident::new("BoltMetadata", proc_macro2::Span::call_site())), - }), - }; - if let syn::Data::Struct(ref mut data) = input.data { - if let syn::Fields::Named(ref mut fields) = data.fields { - fields.named.push(authority_field); - } - } -} +pub mod instructions; +pub mod metadata; diff --git a/crates/bolt-lang/utils/src/metadata.rs b/crates/bolt-lang/utils/src/metadata.rs new file mode 100644 index 00000000..99ea4d3d --- /dev/null +++ b/crates/bolt-lang/utils/src/metadata.rs @@ -0,0 +1,22 @@ +use proc_macro2::Ident; +use syn::{DeriveInput, Field, Type, Visibility}; + +pub fn add_bolt_metadata(input: &mut DeriveInput) { + let authority_field: Field = Field { + attrs: vec![], + vis: Visibility::Public(syn::VisPublic { + pub_token: Default::default(), + }), + ident: Some(Ident::new("bolt_metadata", proc_macro2::Span::call_site())), + colon_token: Some(Default::default()), + ty: Type::Path(syn::TypePath { + qself: None, + path: syn::Path::from(Ident::new("BoltMetadata", proc_macro2::Span::call_site())), + }), + }; + if let syn::Data::Struct(ref mut data) = input.data { + if let syn::Fields::Named(ref mut fields) = data.fields { + fields.named.insert(0, authority_field); + } + } +} diff --git a/crates/programs/bolt-component/src/lib.rs b/crates/programs/bolt-component/src/lib.rs index 8f826b46..69829ad5 100644 --- a/crates/programs/bolt-component/src/lib.rs +++ b/crates/programs/bolt-component/src/lib.rs @@ -14,40 +14,48 @@ pub mod bolt_component { Ok(()) } - pub fn update(_ctx: Context, _data: Vec) -> Result<()> { + pub fn set_owner(_ctx: Context, _owner: Pubkey) -> Result<()> { Ok(()) } - pub fn update_with_session(_ctx: Context, _data: Vec) -> Result<()> { + pub fn set_data(_ctx: Context) -> Result<()> { Ok(()) } - #[derive(Accounts)] - pub struct Update<'info> { - #[account()] - pub cpi_auth: Signer<'info>, - #[account(mut)] - /// CHECK: The component to update - pub bolt_component: UncheckedAccount<'info>, - #[account()] - /// CHECK: The authority of the component - pub authority: Signer<'info>, + pub fn delegate( + _ctx: Context, + _commit_frequency_ms: u32, + _validator: Option, + ) -> Result<()> { + Ok(()) } +} - #[derive(Accounts)] - pub struct UpdateWithSession<'info> { - #[account()] - pub cpi_auth: Signer<'info>, - #[account(mut)] - /// CHECK: The component to update - pub bolt_component: UncheckedAccount<'info>, - #[account()] - /// CHECK: The authority of the component - pub authority: Signer<'info>, - #[account()] - /// CHECK: The session token - pub session_token: UncheckedAccount<'info>, - } +#[derive(Accounts)] +pub struct DelegateInput<'info> { + #[account(mut)] + pub payer: Signer<'info>, + /// CHECK: + #[account()] + pub entity: AccountInfo<'info>, + /// CHECK: + #[account(mut)] + pub account: 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: AccountInfo<'info>, } #[derive(Accounts)] @@ -89,6 +97,27 @@ pub struct Destroy<'info> { pub system_program: Program<'info, System>, } +#[derive(Accounts)] +pub struct SetOwner<'info> { + #[account()] + pub cpi_auth: Signer<'info>, + #[account(mut)] + /// CHECK: The component to set the owner on + pub component: UncheckedAccount<'info>, +} + +#[derive(Accounts, Clone)] +pub struct SetData<'info> { + #[account()] + pub cpi_auth: Signer<'info>, + /// CHECK: buffer data check + #[account()] + pub buffer: UncheckedAccount<'info>, + /// CHECK: component data check + #[account(mut)] + pub component: UncheckedAccount<'info>, +} + #[derive(InitSpace, AnchorSerialize, AnchorDeserialize, Default, Copy, Clone)] pub struct BoltMetadata { pub authority: Pubkey, @@ -104,29 +133,3 @@ pub trait CpiContextBuilder<'a, 'b, 'c, 'info>: signer_seeds: &'a [&'b [&'c [u8]]], ) -> CpiContext<'a, 'b, 'c, 'info, Self>; } - -#[cfg(feature = "cpi")] -impl<'a, 'b, 'c, 'info> CpiContextBuilder<'a, 'b, 'c, 'info> for cpi::accounts::Update<'info> { - fn build_cpi_context( - self, - program: AccountInfo<'info>, - signer_seeds: &'a [&'b [&'c [u8]]], - ) -> CpiContext<'a, 'b, 'c, 'info, Self> { - let cpi_program = program.to_account_info(); - CpiContext::new_with_signer(cpi_program, self, signer_seeds) - } -} - -#[cfg(feature = "cpi")] -impl<'a, 'b, 'c, 'info> CpiContextBuilder<'a, 'b, 'c, 'info> - for cpi::accounts::UpdateWithSession<'info> -{ - fn build_cpi_context( - self, - program: AccountInfo<'info>, - signer_seeds: &'a [&'b [&'c [u8]]], - ) -> CpiContext<'a, 'b, 'c, 'info, Self> { - let cpi_program = program.to_account_info(); - CpiContext::new_with_signer(cpi_program, self, signer_seeds) - } -} diff --git a/crates/programs/bolt-system/src/lib.rs b/crates/programs/bolt-system/src/lib.rs index 1cc8c70d..521ea8b6 100644 --- a/crates/programs/bolt-system/src/lib.rs +++ b/crates/programs/bolt-system/src/lib.rs @@ -5,8 +5,16 @@ declare_id!("7X4EFsDJ5aYTcEjKzJ94rD8FRKgQeXC89fkpeTS4KaqP"); #[program] pub mod bolt_system { use super::*; - pub fn bolt_execute(_ctx: Context, _args: Vec) -> Result>> { - Ok(Vec::new()) + pub fn bolt_execute(_ctx: Context, _args: Vec) -> Result<()> { + Ok(()) + } + + pub fn set_data(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn set_owner(_ctx: Context, _owner: Pubkey) -> Result<()> { + Ok(()) } } @@ -16,3 +24,24 @@ pub struct BoltExecute<'info> { #[account()] pub authority: AccountInfo<'info>, } + +#[derive(Accounts, Clone)] +pub struct SetData<'info> { + #[account()] + pub cpi_auth: Signer<'info>, + /// CHECK: buffer data check + #[account()] + pub buffer: UncheckedAccount<'info>, + /// CHECK: component data check + #[account(mut)] + pub component: UncheckedAccount<'info>, +} + +#[derive(Accounts, Clone)] +pub struct SetOwner<'info> { + #[account()] + pub cpi_auth: Signer<'info>, + /// CHECK: This is a component account. + #[account(mut)] + pub component: UncheckedAccount<'info>, +} diff --git a/crates/programs/world/Cargo.toml b/crates/programs/world/Cargo.toml index 4292d2a7..25b3dcb6 100644 --- a/crates/programs/world/Cargo.toml +++ b/crates/programs/world/Cargo.toml @@ -29,3 +29,5 @@ bolt-component.workspace = true bolt-system.workspace = true solana-security-txt.workspace = true tuple-conv.workspace = true +session-keys.workspace = true +ephemeral-rollups-sdk.workspace = true \ No newline at end of file diff --git a/crates/programs/world/src/error.rs b/crates/programs/world/src/error.rs index e36ff3ed..a2dce6c1 100644 --- a/crates/programs/world/src/error.rs +++ b/crates/programs/world/src/error.rs @@ -14,4 +14,8 @@ pub enum WorldError { AuthorityNotFound, #[msg("The system is not approved in this world instance")] SystemNotApproved, + #[msg("The component owner does not match the program")] + InvalidComponentOwner, + #[msg("Invalid buffer account")] + InvalidBufferAccount, } diff --git a/crates/programs/world/src/lib.rs b/crates/programs/world/src/lib.rs index da532575..13b01ffb 100644 --- a/crates/programs/world/src/lib.rs +++ b/crates/programs/world/src/lib.rs @@ -1,9 +1,11 @@ #![allow(clippy::manual_unwrap_or_default)] -use anchor_lang::prelude::*; -use bolt_component::CpiContextBuilder; +use anchor_lang::{prelude::*, system_program}; use error::WorldError; use std::collections::BTreeSet; +static CPI_AUTH_ADDRESS: Pubkey = + Pubkey::from_str_const("B2f2y3QTBv346wE6nWKor72AUhUvFF6mPk7TWCF2QVhi"); // Seeds: ["cpi_auth", [251]] + #[cfg(not(feature = "no-entrypoint"))] use solana_security_txt::security_txt; @@ -262,9 +264,122 @@ 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_component( + ctx: Context, + commit_frequency_ms: u32, + validator: Option, + ) -> 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.world_program, + buffer: &ctx.accounts.buffer_buffer, + delegation_record: &ctx.accounts.buffer_delegation_record, + delegation_metadata: &ctx.accounts.buffer_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)?; + + bolt_component::cpi::delegate( + CpiContext::new( + ctx.accounts.component_program.to_account_info(), + bolt_component::cpi::accounts::DelegateInput { + payer: ctx.accounts.payer.to_account_info(), + entity: ctx.accounts.entity.to_account_info(), + account: ctx.accounts.component.to_account_info(), + owner_program: ctx.accounts.component_program.to_account_info(), + buffer: ctx.accounts.buffer.to_account_info(), + delegation_metadata: ctx.accounts.delegation_metadata.to_account_info(), + delegation_record: ctx.accounts.delegation_record.to_account_info(), + delegation_program: ctx.accounts.delegation_program.to_account_info(), + system_program: ctx.accounts.system_program.to_account_info(), + }, + ), + commit_frequency_ms, + validator, + )?; + Ok(()) } + #[derive(Accounts)] + pub struct DelegateComponent<'info> { + #[account(mut)] + pub payer: Signer<'info>, + /// CHECK: + #[account(mut)] + pub component: AccountInfo<'info>, + /// CHECK: + #[account(mut)] + pub component_buffer: AccountInfo<'info>, + /// CHECK:` + pub component_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>, + /// CHECK: + #[account()] + pub entity: AccountInfo<'info>, + /// CHECK:` + pub world_program: AccountInfo<'info>, + /// CHECK: + #[account(mut)] + pub buffer_buffer: AccountInfo<'info>, + /// CHECK:` + #[account(mut)] + pub buffer_delegation_record: AccountInfo<'info>, + /// CHECK:` + #[account(mut)] + pub buffer_delegation_metadata: AccountInfo<'info>, + } + pub fn destroy_component(ctx: Context) -> Result<()> { bolt_component::cpi::destroy(ctx.accounts.build(&[World::cpi_auth_seeds().as_slice()]))?; Ok(()) @@ -274,42 +389,36 @@ pub mod world { ctx: Context<'_, '_, '_, 'info, Apply<'info>>, args: Vec, ) -> Result<()> { - let (pairs, results) = apply_impl( + apply_impl( + &ctx.accounts.buffer, &ctx.accounts.authority, &ctx.accounts.world, &ctx.accounts.bolt_system, - ctx.accounts.build(), + &ctx.accounts.cpi_auth, args, - ctx.remaining_accounts.to_vec(), + None, + ctx.remaining_accounts, )?; - for ((program, component), result) in pairs.into_iter().zip(results.into_iter()) { - bolt_component::cpi::update( - build_update_context( - program, - component, - ctx.accounts.authority.clone(), - ctx.accounts.cpi_auth.clone(), - &[World::cpi_auth_seeds().as_slice()], - ), - result, - )?; - } Ok(()) } #[derive(Accounts)] pub struct Apply<'info> { + /// CHECK: buffer data check + #[account(mut)] + pub buffer: AccountInfo<'info>, /// CHECK: bolt system program check #[account()] pub bolt_system: UncheckedAccount<'info>, /// CHECK: authority check - #[account()] + #[account(mut)] pub authority: Signer<'info>, #[account()] /// CHECK: cpi auth check pub cpi_auth: UncheckedAccount<'info>, #[account()] pub world: Account<'info, World>, + pub system_program: Program<'info, System>, } impl<'info> Apply<'info> { @@ -328,46 +437,38 @@ pub mod world { ctx: Context<'_, '_, '_, 'info, ApplyWithSession<'info>>, args: Vec, ) -> Result<()> { - let (pairs, results) = apply_impl( + apply_impl( + &ctx.accounts.buffer, &ctx.accounts.authority, &ctx.accounts.world, &ctx.accounts.bolt_system, - ctx.accounts.build(), + &ctx.accounts.cpi_auth, args, - ctx.remaining_accounts.to_vec(), + Some(&ctx.accounts.session_token), + ctx.remaining_accounts, )?; - for ((program, component), result) in pairs.into_iter().zip(results.into_iter()) { - bolt_component::cpi::update_with_session( - build_update_context_with_session( - program, - component, - ctx.accounts.authority.clone(), - ctx.accounts.cpi_auth.clone(), - ctx.accounts.session_token.clone(), - &[World::cpi_auth_seeds().as_slice()], - ), - result, - )?; - } Ok(()) } #[derive(Accounts)] pub struct ApplyWithSession<'info> { + /// CHECK: buffer data check + #[account(mut)] + pub buffer: AccountInfo<'info>, /// CHECK: bolt system program check #[account()] pub bolt_system: UncheckedAccount<'info>, /// CHECK: authority check - #[account()] + #[account(mut)] pub authority: Signer<'info>, #[account()] /// CHECK: cpi auth check pub cpi_auth: UncheckedAccount<'info>, #[account()] pub world: Account<'info, World>, - #[account()] - /// CHECK: The session token - pub session_token: UncheckedAccount<'info>, + #[account(constraint = session_token.to_account_info().owner == &session_keys::ID)] + pub session_token: Account<'info, session_keys::SessionToken>, + pub system_program: Program<'info, System>, } impl<'info> ApplyWithSession<'info> { @@ -383,15 +484,17 @@ pub mod world { } } -#[allow(clippy::type_complexity)] +#[allow(clippy::type_complexity, clippy::too_many_arguments)] fn apply_impl<'info>( + buffer: &AccountInfo<'info>, authority: &Signer<'info>, world: &Account<'info, World>, bolt_system: &UncheckedAccount<'info>, - cpi_context: CpiContext<'_, '_, '_, 'info, bolt_system::cpi::accounts::BoltExecute<'info>>, + cpi_auth: &UncheckedAccount<'info>, args: Vec, - mut remaining_accounts: Vec>, -) -> Result<(Vec<(AccountInfo<'info>, AccountInfo<'info>)>, Vec>)> { + session_token: Option<&Account<'info, session_keys::SessionToken>>, + remaining_accounts: &[AccountInfo<'info>], +) -> Result<()> { if !authority.is_signer && authority.key != &ID { return Err(WorldError::InvalidAuthority.into()); } @@ -404,34 +507,237 @@ fn apply_impl<'info>( return Err(WorldError::SystemNotApproved.into()); } - let mut pairs = Vec::new(); - while remaining_accounts.len() >= 2 { - let program = remaining_accounts.remove(0); - if program.key() == ID { - break; + 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) + let unix_timestamp = Clock::get()?.unix_timestamp; + for component in remaining_accounts[..index].iter().skip(1).step_by(2) { + let data_ref = component.try_borrow_data()?; + // Expect at least Anchor discriminator (8) + BoltMetadata (32) + if data_ref.len() < 8 + 32 { + return Err(WorldError::InvalidAuthority.into()); + } + // BoltMetadata.authority is the last 32 bytes of the serialized component + let start = 8; // Skip the discriminator + let mut key_bytes = [0u8; 32]; + key_bytes.copy_from_slice(&data_ref[start..start + 32]); + let component_authority = Pubkey::new_from_array(key_bytes); + + if let Some(session_token) = session_token { + if component_authority == ID { + require!( + unix_timestamp < session_token.valid_until, + session_keys::SessionError::InvalidToken + ); + } else { + let validity_ctx = session_keys::ValidityChecker { + session_token: session_token.clone(), + session_signer: authority.clone(), + authority: component_authority, + target_program: ID, + }; + require!( + session_token.validate(validity_ctx)?, + session_keys::SessionError::InvalidToken + ); + require_eq!( + component_authority, + session_token.authority, + session_keys::SessionError::InvalidToken + ); + } + } else { + require!( + component_authority == ID + || (component_authority == *authority.key && authority.is_signer), + WorldError::InvalidAuthority + ); } - let component = remaining_accounts.remove(0); - pairs.push((program, component)); } - let mut components_accounts = pairs - .iter() - .map(|(_, component)| component) - .cloned() - .collect::>(); - components_accounts.append(&mut remaining_accounts); - let remaining_accounts = components_accounts; - - let results = bolt_system::cpi::bolt_execute( - cpi_context.with_remaining_accounts(remaining_accounts), - args, - )? - .get(); - - if results.len() != pairs.len() { - return Err(WorldError::InvalidSystemOutput.into()); - } - Ok((pairs, results)) + let cpi_auth_seeds = World::cpi_auth_seeds(); + for pair in remaining_accounts[..index].chunks(2) { + let [program, component] = pair else { continue }; + if buffer.data_len() != component.data_len() { + buffer.realloc(component.data_len(), false)?; + } + { + let mut data = buffer.try_borrow_mut_data()?; + data.copy_from_slice(component.try_borrow_data()?.as_ref()); + } + + if component.owner != bolt_system.key { + // bolt_component::set_owner(owner = bolt_system) + { + use anchor_lang::solana_program::instruction::{AccountMeta, Instruction}; + let metas = vec![ + AccountMeta::new_readonly(cpi_auth.key(), true), + AccountMeta::new(component.key(), false), + ]; + let mut data = Vec::with_capacity(8 + 32); + data.extend_from_slice(&[72_u8, 202, 120, 52, 77, 128, 96, 197]); + data.extend_from_slice(&bolt_system.key().to_bytes()); + let ix = Instruction { + program_id: program.key(), + accounts: metas, + data, + }; + let account_infos = [cpi_auth.to_account_info(), component.to_account_info()]; + anchor_lang::solana_program::program::invoke_signed( + &ix, + &account_infos, + &[cpi_auth_seeds.as_slice()], + )?; + } + + // bolt_system::set_data(buffer -> component) + { + use anchor_lang::solana_program::instruction::{AccountMeta, Instruction}; + let metas = vec![ + AccountMeta::new_readonly(cpi_auth.key(), true), + AccountMeta::new_readonly(buffer.key(), false), + AccountMeta::new(component.key(), false), + ]; + let mut data = Vec::with_capacity(8); + data.extend_from_slice(&[223_u8, 114, 91, 136, 197, 78, 153, 153]); + let ix = Instruction { + program_id: bolt_system.key(), + accounts: metas, + data, + }; + let account_infos = [ + cpi_auth.to_account_info(), + buffer.to_account_info(), + component.to_account_info(), + ]; + anchor_lang::solana_program::program::invoke_signed( + &ix, + &account_infos, + &[cpi_auth_seeds.as_slice()], + )?; + } + } + } + + // bolt_system::bolt_execute with remaining accounts (components + extra accounts) + { + use anchor_lang::solana_program::instruction::{AccountMeta, Instruction}; + let mut accounts_metas = vec![AccountMeta::new_readonly( + authority.key(), + authority.is_signer, + )]; + + // Build metas for remaining accounts in the same order as before + let extra_iter = remaining_accounts[..index] + .iter() + .skip(1) + .step_by(2) + .chain(remaining_accounts[index..].iter().skip(1)); + let extra_len = extra_iter.clone().count(); + accounts_metas.reserve(extra_len); + + let mut account_infos: Vec> = Vec::with_capacity(1 + extra_len); + account_infos.push(authority.to_account_info()); + + for ai in extra_iter { + let meta = if ai.is_writable { + AccountMeta::new(ai.key(), ai.is_signer) + } else { + AccountMeta::new_readonly(ai.key(), ai.is_signer) + }; + accounts_metas.push(meta); + account_infos.push(ai.clone()); + } + + let mut data = Vec::with_capacity(8 + 4 + args.len()); + data.extend_from_slice(&[75_u8, 206, 62, 210, 52, 215, 104, 109]); + let args_len_le = (args.len() as u32).to_le_bytes(); + data.extend_from_slice(&args_len_le); + data.extend_from_slice(&args); + let ix = Instruction { + program_id: bolt_system.key(), + accounts: accounts_metas, + data, + }; + + anchor_lang::solana_program::program::invoke(&ix, &account_infos)?; + } + + for pair in remaining_accounts[..index].chunks(2) { + let [program, component] = pair else { continue }; + if buffer.data_len() != component.data_len() { + buffer.realloc(component.data_len(), false)?; + } + { + let mut data = buffer.try_borrow_mut_data()?; + data.copy_from_slice(component.try_borrow_data()?.as_ref()); + } + + if *component.owner != program.key() { + // bolt_system::set_owner(owner = original program) + { + use anchor_lang::solana_program::instruction::{AccountMeta, Instruction}; + let metas = vec![ + AccountMeta::new_readonly(cpi_auth.key(), true), + AccountMeta::new(component.key(), false), + ]; + let mut data = Vec::with_capacity(8 + 32); + data.extend_from_slice(&[72_u8, 202, 120, 52, 77, 128, 96, 197]); + data.extend_from_slice(&program.key().to_bytes()); + let ix = Instruction { + program_id: bolt_system.key(), + accounts: metas, + data, + }; + let account_infos = [cpi_auth.to_account_info(), component.to_account_info()]; + anchor_lang::solana_program::program::invoke_signed( + &ix, + &account_infos, + &[cpi_auth_seeds.as_slice()], + )?; + } + + if *component.owner != program.key() { + return Err(WorldError::InvalidComponentOwner.into()); + } + + // bolt_component::set_data(buffer -> component) on original program + { + use anchor_lang::solana_program::instruction::{AccountMeta, Instruction}; + let metas = vec![ + AccountMeta::new_readonly(cpi_auth.key(), true), + AccountMeta::new_readonly(buffer.key(), false), + AccountMeta::new(component.key(), false), + ]; + let mut data = Vec::with_capacity(8); + data.extend_from_slice(&[223_u8, 114, 91, 136, 197, 78, 153, 153]); + let ix = Instruction { + program_id: program.key(), + accounts: metas, + data, + }; + let account_infos = [ + cpi_auth.to_account_info(), + buffer.to_account_info(), + component.to_account_info(), + ]; + anchor_lang::solana_program::program::invoke_signed( + &ix, + &account_infos, + &[cpi_auth_seeds.as_slice()], + )?; + } + } + } + + buffer.realloc(0, false)?; + + Ok(()) } #[derive(Accounts)] @@ -539,6 +845,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> { @@ -680,12 +989,12 @@ impl World { Pubkey::find_program_address(&[World::seed(), &self.id.to_be_bytes()], &crate::ID) } - pub fn cpi_auth_seeds() -> [&'static [u8]; 2] { + pub const fn cpi_auth_seeds() -> [&'static [u8]; 2] { [b"cpi_auth", &[251]] // 251 is the pre-computed bump for cpi_auth. } - pub const fn cpi_auth_address() -> Pubkey { - Pubkey::from_str_const("B2f2y3QTBv346wE6nWKor72AUhUvFF6mPk7TWCF2QVhi") // This is the pre-computed address for cpi_auth. + pub const fn cpi_auth_address() -> &'static Pubkey { + &CPI_AUTH_ADDRESS // This is the pre-computed address for cpi_auth. } } @@ -714,44 +1023,3 @@ impl SystemWhitelist { 8 + Registry::INIT_SPACE } } - -/// Builds the context for updating a component. -pub fn build_update_context<'a, 'b, 'c, 'info>( - component_program: AccountInfo<'info>, - bolt_component: AccountInfo<'info>, - authority: Signer<'info>, - cpi_auth: UncheckedAccount<'info>, - signer_seeds: &'a [&'b [&'c [u8]]], -) -> CpiContext<'a, 'b, 'c, 'info, bolt_component::cpi::accounts::Update<'info>> { - let authority = authority.to_account_info(); - let cpi_auth = cpi_auth.to_account_info(); - let cpi_program = component_program; - bolt_component::cpi::accounts::Update { - bolt_component, - authority, - cpi_auth, - } - .build_cpi_context(cpi_program, signer_seeds) -} - -/// Builds the context for updating a component. -pub fn build_update_context_with_session<'a, 'b, 'c, 'info>( - component_program: AccountInfo<'info>, - bolt_component: AccountInfo<'info>, - authority: Signer<'info>, - cpi_auth: UncheckedAccount<'info>, - session_token: UncheckedAccount<'info>, - signer_seeds: &'a [&'b [&'c [u8]]], -) -> CpiContext<'a, 'b, 'c, 'info, bolt_component::cpi::accounts::UpdateWithSession<'info>> { - let authority = authority.to_account_info(); - let cpi_auth = cpi_auth.to_account_info(); - let cpi_program = component_program; - let session_token = session_token.to_account_info(); - bolt_component::cpi::accounts::UpdateWithSession { - bolt_component, - authority, - cpi_auth, - session_token, - } - .build_cpi_context(cpi_program, signer_seeds) -} diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml index c33ef513..fc2a0e1b 100644 --- a/crates/types/Cargo.toml +++ b/crates/types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bolt-types" -version.workspace = true +version = "0.2.4" description = "Autogenerate types for the bolt language" edition = "2021" @@ -9,4 +9,5 @@ crate-type = ["cdylib", "lib"] name = "bolt_types" [dependencies] -bolt-lang.workspace = true \ No newline at end of file +bolt-lang.workspace = true +anchor-lang.workspace = true diff --git a/crates/types/src/component_Fn1JzzEdyb55fsyduWS94mYHizGhJZuhvjX6DVvrmGbQ.rs b/crates/types/src/component_Fn1JzzEdyb55fsyduWS94mYHizGhJZuhvjX6DVvrmGbQ.rs index 26430117..7b911a68 100644 --- a/crates/types/src/component_Fn1JzzEdyb55fsyduWS94mYHizGhJZuhvjX6DVvrmGbQ.rs +++ b/crates/types/src/component_Fn1JzzEdyb55fsyduWS94mYHizGhJZuhvjX6DVvrmGbQ.rs @@ -1,10 +1,8 @@ use bolt_lang::*; -#[component_deserialize] +#[component_deserialize(Position)] pub struct ComponentFn1JzzEdyb55fsyduWS94mYHizGhJZuhvjX6DVvrmGbQ { pub x: i64, pub y: i64, pub z: i64, } - -pub use ComponentFn1JzzEdyb55fsyduWS94mYHizGhJZuhvjX6DVvrmGbQ as Position; diff --git a/docs/REPORT.md b/docs/REPORT.md index e87a1c36..43e38244 100644 --- a/docs/REPORT.md +++ b/docs/REPORT.md @@ -2,8 +2,8 @@ %%{init: {"xyChart": {"width": 1200, "height": 400, "xAxis": {}}}}%% xychart title "Bolt Apply System Cost" - x-axis ["1C-CPIs:2","2C-CPIs:3","3C-CPIs:4","4C-CPIs:5","5C-CPIs:6","6C-CPIs:7","7C-CPIs:8","8C-CPIs:9","9C-CPIs:10","10C-CPIs:11"] + 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:37","10C-CPIs:40"] y-axis "CU" 5000 --> 200000 - bar [15254,24352,33653,43017,52358,61568,71006,80482,89958,99299] - bar [6162,11236,16305,21374,26443,31516,36608,41892,46984,52077] + bar [20119,34196,48290,62402,76546,90715,104998,119314,133655,0] + bar [6919,12930,18924,24914,30908,36899,42976,49058,55137,59718] ``` diff --git a/examples/system-apply-velocity/src/lib.rs b/examples/system-apply-velocity/src/lib.rs index ee1db4e5..637c7aa9 100644 --- a/examples/system-apply-velocity/src/lib.rs +++ b/examples/system-apply-velocity/src/lib.rs @@ -7,7 +7,7 @@ declare_id!("6LHhFVwif6N9Po3jHtSmMVtPjF6zRfL3xMosSzcrQAS8"); #[system] pub mod system_apply_velocity { - pub fn execute(ctx: Context, _args: Vec) -> Result { + pub fn execute(ctx: Context, _args: Vec) -> Result<()> { ctx.accounts.velocity.x = 10; let mut clock = Clock::get()?; if let Ok(clock_account_info) = ctx.sysvar_clock() { @@ -16,7 +16,7 @@ pub mod system_apply_velocity { } ctx.accounts.velocity.last_applied = clock.unix_timestamp; ctx.accounts.position.x += 10 * (ctx.accounts.velocity.x + 2) + 3; - Ok(ctx.accounts) + Ok(()) } #[system_input] diff --git a/examples/system-fly/src/lib.rs b/examples/system-fly/src/lib.rs index c605a28a..830fe057 100644 --- a/examples/system-fly/src/lib.rs +++ b/examples/system-fly/src/lib.rs @@ -6,10 +6,10 @@ declare_id!("HT2YawJjkNmqWcLNfPAMvNsLdWwPvvvbKA5bpMw4eUpq"); #[system] pub mod system_fly { - pub fn execute(ctx: Context, _args: Vec) -> Result { + pub fn execute(ctx: Context, _args: Vec) -> Result<()> { let pos = &mut ctx.accounts.position; pos.z += 1; - Ok(ctx.accounts) + Ok(()) } #[system_input] diff --git a/examples/system-simple-movement/src/lib.rs b/examples/system-simple-movement/src/lib.rs index 311996d7..881c827e 100644 --- a/examples/system-simple-movement/src/lib.rs +++ b/examples/system-simple-movement/src/lib.rs @@ -4,7 +4,7 @@ declare_id!("FSa6qoJXFBR3a7ThQkTAMrC15p6NkchPEjBdd4n6dXxA"); #[system] pub mod system_simple_movement { - pub fn execute(ctx: Context, args: Args) -> Result { + pub fn execute(ctx: Context, args: Args) -> Result<()> { // Compute the new position based on the direction let (dx, dy) = match args.direction { Direction::Left => (-1, 0), @@ -14,8 +14,7 @@ pub mod system_simple_movement { }; ctx.accounts.position.x += dx; ctx.accounts.position.y += dy; - - Ok(ctx.accounts) + Ok(()) } #[system_input] diff --git a/examples/system-with-1-component/src/lib.rs b/examples/system-with-1-component/src/lib.rs index 655831e5..29496848 100644 --- a/examples/system-with-1-component/src/lib.rs +++ b/examples/system-with-1-component/src/lib.rs @@ -6,8 +6,8 @@ declare_id!("BsVKJF2H9GN1P9WrexdgEY4ztiweKvfQo6ydLWUEw6n7"); #[system] pub mod with_1_component { - pub fn execute(ctx: Context, _args_p: Vec) -> Result { - Ok(ctx.accounts) + pub fn execute(_ctx: Context, _args_p: Vec) -> Result<()> { + Ok(()) } #[system_input] diff --git a/examples/system-with-10-components/src/lib.rs b/examples/system-with-10-components/src/lib.rs index 4997b52f..fa4f4d3b 100644 --- a/examples/system-with-10-components/src/lib.rs +++ b/examples/system-with-10-components/src/lib.rs @@ -6,8 +6,8 @@ declare_id!("C69UYWaXBQXUbhHQGtG8pB7DHSgh2z5Sm9ifyAnM1kkt"); #[system] pub mod with_10_components { - pub fn execute(ctx: Context, _args_p: Vec) -> Result { - Ok(ctx.accounts) + pub fn execute(_ctx: Context, _args_p: Vec) -> Result<()> { + Ok(()) } #[system_input] diff --git a/examples/system-with-2-components/src/lib.rs b/examples/system-with-2-components/src/lib.rs index 61102e5a..18b9dbeb 100644 --- a/examples/system-with-2-components/src/lib.rs +++ b/examples/system-with-2-components/src/lib.rs @@ -6,8 +6,8 @@ declare_id!("X5wTvz1i6ocNXzfrEB8JmhFCniojUZxqk3TXDq98fZX"); #[system] pub mod with_2_components { - pub fn execute(ctx: Context, _args_p: Vec) -> Result { - Ok(ctx.accounts) + pub fn execute(_ctx: Context, _args_p: Vec) -> Result<()> { + Ok(()) } #[system_input] diff --git a/examples/system-with-3-components/src/lib.rs b/examples/system-with-3-components/src/lib.rs index fcf53c13..92261e6a 100644 --- a/examples/system-with-3-components/src/lib.rs +++ b/examples/system-with-3-components/src/lib.rs @@ -6,8 +6,8 @@ declare_id!("9R7rvEwCuZ6iow1Ch3sdUQKib4LBvftyBmyvSnPaAZkG"); #[system] pub mod with_3_components { - pub fn execute(ctx: Context, _args_p: Vec) -> Result { - Ok(ctx.accounts) + pub fn execute(_ctx: Context, _args_p: Vec) -> Result<()> { + Ok(()) } #[system_input] diff --git a/examples/system-with-4-components/src/lib.rs b/examples/system-with-4-components/src/lib.rs index b3e77e0d..bf6428fc 100644 --- a/examples/system-with-4-components/src/lib.rs +++ b/examples/system-with-4-components/src/lib.rs @@ -6,8 +6,8 @@ declare_id!("2w9pkZoCfEciHLLDhG3zrZRprcYH7nojhyBQMnD3PtUU"); #[system] pub mod with_4_components { - pub fn execute(ctx: Context, _args_p: Vec) -> Result { - Ok(ctx.accounts) + pub fn execute(_ctx: Context, _args_p: Vec) -> Result<()> { + Ok(()) } #[system_input] diff --git a/examples/system-with-5-components/src/lib.rs b/examples/system-with-5-components/src/lib.rs index e93db98e..31e036b7 100644 --- a/examples/system-with-5-components/src/lib.rs +++ b/examples/system-with-5-components/src/lib.rs @@ -6,8 +6,8 @@ declare_id!("8KsdHMGdS4mQjpKFhc2PWBw2tyxwNbEKCnZLKp3riC5o"); #[system] pub mod with_5_components { - pub fn execute(ctx: Context, _args_p: Vec) -> Result { - Ok(ctx.accounts) + pub fn execute(_ctx: Context, _args_p: Vec) -> Result<()> { + Ok(()) } #[system_input] diff --git a/examples/system-with-6-components/src/lib.rs b/examples/system-with-6-components/src/lib.rs index 81e50f16..c877129a 100644 --- a/examples/system-with-6-components/src/lib.rs +++ b/examples/system-with-6-components/src/lib.rs @@ -6,8 +6,8 @@ declare_id!("3ndvNAg4moKeLhuWQtDmcN43PuvvGsigQWRBPthfWEN3"); #[system] pub mod with_6_components { - pub fn execute(ctx: Context, _args_p: Vec) -> Result { - Ok(ctx.accounts) + pub fn execute(_ctx: Context, _args_p: Vec) -> Result<()> { + Ok(()) } #[system_input] diff --git a/examples/system-with-7-components/src/lib.rs b/examples/system-with-7-components/src/lib.rs index 429bcf6e..cdad62fe 100644 --- a/examples/system-with-7-components/src/lib.rs +++ b/examples/system-with-7-components/src/lib.rs @@ -6,8 +6,8 @@ declare_id!("4ESiD77Gjjfuywhw8NBnryHezXtwDSA27ustL29JdX7i"); #[system] pub mod with_7_components { - pub fn execute(ctx: Context, _args_p: Vec) -> Result { - Ok(ctx.accounts) + pub fn execute(_ctx: Context, _args_p: Vec) -> Result<()> { + Ok(()) } #[system_input] diff --git a/examples/system-with-8-components/src/lib.rs b/examples/system-with-8-components/src/lib.rs index 9d34267b..8ae5e564 100644 --- a/examples/system-with-8-components/src/lib.rs +++ b/examples/system-with-8-components/src/lib.rs @@ -6,8 +6,8 @@ declare_id!("EbTAEnrVV4f8W7Fd4TxW3jLjfpyhr74wQf7rSHRQ8u78"); #[system] pub mod with_8_components { - pub fn execute(ctx: Context, _args_p: Vec) -> Result { - Ok(ctx.accounts) + pub fn execute(_ctx: Context, _args_p: Vec) -> Result<()> { + Ok(()) } #[system_input] diff --git a/examples/system-with-9-components/src/lib.rs b/examples/system-with-9-components/src/lib.rs index f051597a..2996ba5f 100644 --- a/examples/system-with-9-components/src/lib.rs +++ b/examples/system-with-9-components/src/lib.rs @@ -6,8 +6,8 @@ declare_id!("GKdPXW7pGhFNRdMPHWNsrmqc7haXQk4VFCAyZKsrgYQG"); #[system] pub mod with_9_components { - pub fn execute(ctx: Context, _args_p: Vec) -> Result { - Ok(ctx.accounts) + pub fn execute(_ctx: Context, _args_p: Vec) -> Result<()> { + Ok(()) } #[system_input] diff --git a/scripts/test.sh b/scripts/test.sh deleted file mode 100755 index ed683116..00000000 --- a/scripts/test.sh +++ /dev/null @@ -1,6 +0,0 @@ -SCRIPT_DIR=$(dirname "$0") -PROJECT_DIR="$SCRIPT_DIR/.." -pushd "$PROJECT_DIR" -cp tests/keys/* target/deploy/ -bolt test -popd \ No newline at end of file