diff --git a/programs/src/main/java/software/sava/anchor/programs/glam/GlamProgramAccountClient.java b/programs/src/main/java/software/sava/anchor/programs/glam/GlamProgramAccountClient.java index 1dd6726..c912a62 100644 --- a/programs/src/main/java/software/sava/anchor/programs/glam/GlamProgramAccountClient.java +++ b/programs/src/main/java/software/sava/anchor/programs/glam/GlamProgramAccountClient.java @@ -32,12 +32,12 @@ static CompletableFuture>> fetchFundAccounts(final return rpcClient.getProgramAccounts(programPublicKey, FundAccount.FACTORY); } - static CompletableFuture>> fetchFundAccount(final SolanaRpcClient rpcClient, - final String fundName, - final PublicKey programPublicKey) { + static CompletableFuture>> fetchFundAccountsByManager(final SolanaRpcClient rpcClient, + final PublicKey managerPublicKey, + final PublicKey programPublicKey) { return rpcClient.getProgramAccounts( programPublicKey, - List.of(FundAccount.createNameFilter(fundName)), + List.of(FundAccount.createManagerFilter(managerPublicKey)), FundAccount.FACTORY ); } diff --git a/programs/src/main/java/software/sava/anchor/programs/glam/GlamProgramAccountClientImpl.java b/programs/src/main/java/software/sava/anchor/programs/glam/GlamProgramAccountClientImpl.java index 0349492..7148475 100644 --- a/programs/src/main/java/software/sava/anchor/programs/glam/GlamProgramAccountClientImpl.java +++ b/programs/src/main/java/software/sava/anchor/programs/glam/GlamProgramAccountClientImpl.java @@ -16,6 +16,7 @@ import software.sava.solana.programs.clients.NativeProgramAccountClient; import software.sava.solana.programs.clients.NativeProgramClient; import software.sava.solana.programs.stake.StakeAccount; +import software.sava.solana.programs.stake.StakeProgram; import software.sava.solana.programs.stake.StakeState; import java.util.Collection; @@ -264,6 +265,36 @@ public Instruction initializeStakeAccountChecked(final PublicKey unInitializedSt return nativeProgramAccountClient.initializeStakeAccountChecked(unInitializedStakeAccount); } + @Override + public Instruction authorizeStakeAccount(final PublicKey stakeAccount, + final PublicKey stakeOrWithdrawAuthority, + final PublicKey lockupAuthority, + final StakeProgram.StakeAuthorize stakeAuthorize) { + return nativeProgramAccountClient.authorizeStakeAccount(stakeAccount, stakeOrWithdrawAuthority, lockupAuthority, stakeAuthorize); + } + + @Override + public Instruction authorizeStakeAccount(final PublicKey stakeAccount, + final PublicKey stakeOrWithdrawAuthority, + final StakeProgram.StakeAuthorize stakeAuthorize) { + return nativeProgramAccountClient.authorizeStakeAccount(stakeAccount, stakeOrWithdrawAuthority, stakeAuthorize); + } + + @Override + public Instruction authorizeStakeAccountChecked(final PublicKey stakeAccount, + final PublicKey stakeOrWithdrawAuthority, + final PublicKey newStakeOrWithdrawAuthority, + final StakeProgram.StakeAuthorize stakeAuthorize) { + return nativeProgramAccountClient.authorizeStakeAccountChecked(stakeAccount, stakeOrWithdrawAuthority, stakeAuthorize); + } + + @Override + public Instruction authorizeStakeAccountChecked(final PublicKey stakeAccount, + final PublicKey stakeOrWithdrawAuthority, + final StakeProgram.StakeAuthorize stakeAuthorize) { + return nativeProgramAccountClient.authorizeStakeAccountChecked(stakeAccount, stakeOrWithdrawAuthority, stakeAuthorize); + } + @Override public ProgramDerivedAddress findLookupTableAddress(final long recentSlot) { return nativeProgramAccountClient.findLookupTableAddress(recentSlot); diff --git a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/GlamProgram.java b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/GlamProgram.java index 0427189..73b458f 100644 --- a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/GlamProgram.java +++ b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/GlamProgram.java @@ -93,15 +93,45 @@ public static Instruction updateFund(final AccountMeta invokedGlamProgramMeta, public static final Discriminator CLOSE_FUND_DISCRIMINATOR = toDiscriminator(230, 183, 3, 112, 236, 252, 5, 185); - public static Instruction closeFund(final AccountMeta invokedGlamProgramMeta, final PublicKey fundKey, final PublicKey managerKey) { + public static Instruction closeFund(final AccountMeta invokedGlamProgramMeta, + final PublicKey fundKey, + final PublicKey openfundsKey, + final PublicKey treasuryKey, + final PublicKey managerKey, + final PublicKey systemProgramKey) { final var keys = List.of( createWrite(fundKey), - createWritableSigner(managerKey) + createWrite(openfundsKey), + createWrite(treasuryKey), + createWritableSigner(managerKey), + createRead(systemProgramKey) ); return Instruction.createInstruction(invokedGlamProgramMeta, keys, CLOSE_FUND_DISCRIMINATOR); } + public static final Discriminator CLOSE_SHARE_CLASS_DISCRIMINATOR = toDiscriminator(35, 248, 168, 150, 244, 251, 61, 91); + + public static Instruction closeShareClass(final AccountMeta invokedGlamProgramMeta, + final PublicKey fundKey, + final PublicKey shareClassKey, + final PublicKey managerKey, + final PublicKey token2022ProgramKey, + final int shareClassId) { + final var keys = List.of( + createWrite(fundKey), + createWrite(shareClassKey), + createWritableSigner(managerKey), + createRead(token2022ProgramKey) + ); + + final byte[] _data = new byte[9]; + int i = writeDiscriminator(CLOSE_SHARE_CLASS_DISCRIMINATOR, _data, 0); + _data[i] = (byte) shareClassId; + + return Instruction.createInstruction(invokedGlamProgramMeta, keys, _data); + } + public static final Discriminator SUBSCRIBE_DISCRIMINATOR = toDiscriminator(254, 28, 191, 138, 156, 179, 183, 53); public static Instruction subscribe(final AccountMeta invokedGlamProgramMeta, diff --git a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/Acl.java b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/DelegateAcl.java similarity index 75% rename from programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/Acl.java rename to programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/DelegateAcl.java index be70cc5..4a395ee 100644 --- a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/Acl.java +++ b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/DelegateAcl.java @@ -5,14 +5,14 @@ import static software.sava.core.accounts.PublicKey.readPubKey; -public record Acl(PublicKey pubkey, Permission[] permissions) implements Borsh { +public record DelegateAcl(PublicKey pubkey, Permission[] permissions) implements Borsh { - public static Acl read(final byte[] _data, final int offset) { + public static DelegateAcl read(final byte[] _data, final int offset) { int i = offset; final var pubkey = readPubKey(_data, i); i += 32; final var permissions = Borsh.readVector(Permission.class, Permission::read, _data, i); - return new Acl(pubkey, permissions); + return new DelegateAcl(pubkey, permissions); } @Override diff --git a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/EngineFieldName.java b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/EngineFieldName.java index d1efddb..63b75a0 100644 --- a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/EngineFieldName.java +++ b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/EngineFieldName.java @@ -10,7 +10,8 @@ public enum EngineFieldName implements Borsh.Enum { AssetsWeights, ShareClassAllowlist, ShareClassBlocklist, - Acls; + DelegateAcls, + IntegrationAcls; public static EngineFieldName read(final byte[] _data, final int offset) { return Borsh.read(EngineFieldName.values(), _data, offset); diff --git a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/EngineFieldValue.java b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/EngineFieldValue.java index 5ce30e1..9275cc8 100644 --- a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/EngineFieldValue.java +++ b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/EngineFieldValue.java @@ -25,7 +25,8 @@ public sealed interface EngineFieldValue extends RustEnum permits EngineFieldValue.Timestamp, EngineFieldValue.VecPubkey, EngineFieldValue.VecU32, - EngineFieldValue.VecAcl { + EngineFieldValue.VecDelegateAcl, + EngineFieldValue.VecIntegrationAcl { static EngineFieldValue read(final byte[] _data, final int offset) { final int ordinal = _data[offset] & 0xFF; @@ -45,7 +46,8 @@ static EngineFieldValue read(final byte[] _data, final int offset) { case 11 -> Timestamp.read(_data, i); case 12 -> VecPubkey.read(_data, i); case 13 -> VecU32.read(_data, i); - case 14 -> VecAcl.read(_data, i); + case 14 -> VecDelegateAcl.read(_data, i); + case 15 -> VecIntegrationAcl.read(_data, i); default -> throw new IllegalStateException(java.lang.String.format( "Unexpected ordinal [%d] for enum [EngineFieldValue]", ordinal )); @@ -265,11 +267,11 @@ public int ordinal() { } } - record VecAcl(Acl[] val) implements EngineFieldValue { + record VecDelegateAcl(DelegateAcl[] val) implements EngineFieldValue { - public static VecAcl read(final byte[] _data, final int offset) { - final var val = Borsh.readVector(Acl.class, Acl::read, _data, offset); - return new VecAcl(val); + public static VecDelegateAcl read(final byte[] _data, final int offset) { + final var val = Borsh.readVector(DelegateAcl.class, DelegateAcl::read, _data, offset); + return new VecDelegateAcl(val); } @Override @@ -289,4 +291,29 @@ public int ordinal() { return 14; } } + + record VecIntegrationAcl(IntegrationAcl[] val) implements EngineFieldValue { + + public static VecIntegrationAcl read(final byte[] _data, final int offset) { + final var val = Borsh.readVector(IntegrationAcl.class, IntegrationAcl::read, _data, offset); + return new VecIntegrationAcl(val); + } + + @Override + public int write(final byte[] _data, final int offset) { + int i = writeOrdinal(_data, offset); + i += Borsh.write(val, _data, i); + return i - offset; + } + + @Override + public int l() { + return 1 + Borsh.len(val); + } + + @Override + public int ordinal() { + return 15; + } + } } diff --git a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/FundAccount.java b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/FundAccount.java index 1e8e62f..22899f4 100644 --- a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/FundAccount.java +++ b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/FundAccount.java @@ -17,46 +17,59 @@ public record FundAccount(PublicKey _address, Discriminator discriminator, - String name, byte[] _name, - String uri, byte[] _uri, + PublicKey manager, PublicKey treasury, - PublicKey[] shareClasses, PublicKey openfunds, - String openfundsUri, byte[] _openfundsUri, - PublicKey manager, PublicKey engine, + PublicKey[] shareClasses, + String name, byte[] _name, + String uri, byte[] _uri, + String openfundsUri, byte[] _openfundsUri, EngineField[][] params) implements Borsh { - public static final int NAME_OFFSET = 8; + public static final int MANAGER_OFFSET = 8; + public static final int TREASURY_OFFSET = 40; + public static final int OPENFUNDS_OFFSET = 72; + public static final int ENGINE_OFFSET = 104; + public static final int SHARE_CLASSES_OFFSET = 136; + + public static Filter createManagerFilter(final PublicKey manager) { + return Filter.createMemCompFilter(MANAGER_OFFSET, manager); + } + + public static Filter createTreasuryFilter(final PublicKey treasury) { + return Filter.createMemCompFilter(TREASURY_OFFSET, treasury); + } - public static Filter createNameFilter(final String name) { - final byte[] bytes = name.getBytes(UTF_8); - final byte[] _data = new byte[4 + bytes.length]; - Borsh.write(bytes, _data, 0); - return Filter.createMemCompFilter(NAME_OFFSET, _data); + public static Filter createOpenfundsFilter(final PublicKey openfunds) { + return Filter.createMemCompFilter(OPENFUNDS_OFFSET, openfunds); + } + + public static Filter createEngineFilter(final PublicKey engine) { + return Filter.createMemCompFilter(ENGINE_OFFSET, engine); } public static FundAccount createRecord(final PublicKey _address, final Discriminator discriminator, - final String name, - final String uri, + final PublicKey manager, final PublicKey treasury, - final PublicKey[] shareClasses, final PublicKey openfunds, - final String openfundsUri, - final PublicKey manager, final PublicKey engine, + final PublicKey[] shareClasses, + final String name, + final String uri, + final String openfundsUri, final EngineField[][] params) { return new FundAccount(_address, discriminator, - name, name.getBytes(UTF_8), - uri, uri.getBytes(UTF_8), + manager, treasury, - shareClasses, openfunds, - openfundsUri, openfundsUri.getBytes(UTF_8), - manager, engine, + shareClasses, + name, name.getBytes(UTF_8), + uri, uri.getBytes(UTF_8), + openfundsUri, openfundsUri.getBytes(UTF_8), params); } @@ -73,65 +86,65 @@ public static FundAccount read(final PublicKey _address, final byte[] _data) { public static FundAccount read(final PublicKey _address, final byte[] _data, final int offset) { final var discriminator = parseDiscriminator(_data, offset); int i = offset + discriminator.length(); - final var name = Borsh.string(_data, i); - i += (Integer.BYTES + getInt32LE(_data, i)); - final var uri = Borsh.string(_data, i); - i += (Integer.BYTES + getInt32LE(_data, i)); + final var manager = readPubKey(_data, i); + i += 32; final var treasury = readPubKey(_data, i); i += 32; - final var shareClasses = Borsh.readPublicKeyVector(_data, i); - i += Borsh.len(shareClasses); final var openfunds = readPubKey(_data, i); i += 32; - final var openfundsUri = Borsh.string(_data, i); - i += (Integer.BYTES + getInt32LE(_data, i)); - final var manager = readPubKey(_data, i); - i += 32; final var engine = readPubKey(_data, i); i += 32; + final var shareClasses = Borsh.readPublicKeyVector(_data, i); + i += Borsh.len(shareClasses); + final var name = Borsh.string(_data, i); + i += (Integer.BYTES + getInt32LE(_data, i)); + final var uri = Borsh.string(_data, i); + i += (Integer.BYTES + getInt32LE(_data, i)); + final var openfundsUri = Borsh.string(_data, i); + i += (Integer.BYTES + getInt32LE(_data, i)); final var params = Borsh.readMultiDimensionVector(EngineField.class, EngineField::read, _data, i); return new FundAccount(_address, discriminator, - name, name.getBytes(UTF_8), - uri, uri.getBytes(UTF_8), + manager, treasury, - shareClasses, openfunds, - openfundsUri, openfundsUri.getBytes(UTF_8), - manager, engine, + shareClasses, + name, name.getBytes(UTF_8), + uri, uri.getBytes(UTF_8), + openfundsUri, openfundsUri.getBytes(UTF_8), params); } @Override public int write(final byte[] _data, final int offset) { int i = offset + discriminator.write(_data, offset); - i += Borsh.write(_name, _data, i); - i += Borsh.write(_uri, _data, i); + manager.write(_data, i); + i += 32; treasury.write(_data, i); i += 32; - i += Borsh.write(shareClasses, _data, i); openfunds.write(_data, i); i += 32; - i += Borsh.write(_openfundsUri, _data, i); - manager.write(_data, i); - i += 32; engine.write(_data, i); i += 32; + i += Borsh.write(shareClasses, _data, i); + i += Borsh.write(_name, _data, i); + i += Borsh.write(_uri, _data, i); + i += Borsh.write(_openfundsUri, _data, i); i += Borsh.write(params, _data, i); return i - offset; } @Override public int l() { - return 8 + Borsh.len(_name) - + Borsh.len(_uri) - + 32 - + Borsh.len(shareClasses) + return 8 + 32 + 32 - + Borsh.len(_openfundsUri) + 32 + 32 + + Borsh.len(shareClasses) + + Borsh.len(_name) + + Borsh.len(_uri) + + Borsh.len(_openfundsUri) + Borsh.len(params); } } diff --git a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/FundError.java b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/FundError.java index e2b68d7..ac7b45b 100644 --- a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/FundError.java +++ b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/FundError.java @@ -4,7 +4,9 @@ public enum FundError implements Borsh.Enum { - NoShareClassInFund; + NoShareClassInFund, + ShareClassNotEmpty, + CantCloseShareClasses; public static FundError read(final byte[] _data, final int offset) { return Borsh.read(FundError.values(), _data, offset); diff --git a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/FundModel.java b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/FundModel.java index 9d3527d..fa394e1 100644 --- a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/FundModel.java +++ b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/FundModel.java @@ -20,7 +20,8 @@ public record FundModel(PublicKey id, CompanyModel company, ManagerModel manager, CreatedModel created, - Acl[] acls, + DelegateAcl[] delegateAcls, + IntegrationAcl[] integrationAcls, Boolean isRawOpenfunds, FundOpenfundsModel rawOpenfunds) implements Borsh { @@ -35,7 +36,8 @@ public static FundModel createRecord(final PublicKey id, final CompanyModel company, final ManagerModel manager, final CreatedModel created, - final Acl[] acls, + final DelegateAcl[] delegateAcls, + final IntegrationAcl[] integrationAcls, final Boolean isRawOpenfunds, final FundOpenfundsModel rawOpenfunds) { return new FundModel(id, @@ -49,7 +51,8 @@ public static FundModel createRecord(final PublicKey id, company, manager, created, - acls, + delegateAcls, + integrationAcls, isRawOpenfunds, rawOpenfunds); } @@ -94,8 +97,10 @@ public static FundModel read(final byte[] _data, final int offset) { if (created != null) { i += Borsh.len(created); } - final var acls = Borsh.readVector(Acl.class, Acl::read, _data, i); - i += Borsh.len(acls); + final var delegateAcls = Borsh.readVector(DelegateAcl.class, DelegateAcl::read, _data, i); + i += Borsh.len(delegateAcls); + final var integrationAcls = Borsh.readVector(IntegrationAcl.class, IntegrationAcl::read, _data, i); + i += Borsh.len(integrationAcls); final var isRawOpenfunds = _data[i++] == 0 ? null : _data[i] == 1; if (isRawOpenfunds != null) { ++i; @@ -112,7 +117,8 @@ public static FundModel read(final byte[] _data, final int offset) { company, manager, created, - acls, + delegateAcls, + integrationAcls, isRawOpenfunds, rawOpenfunds); } @@ -131,7 +137,8 @@ public int write(final byte[] _data, final int offset) { i += Borsh.writeOptional(company, _data, i); i += Borsh.writeOptional(manager, _data, i); i += Borsh.writeOptional(created, _data, i); - i += Borsh.write(acls, _data, i); + i += Borsh.write(delegateAcls, _data, i); + i += Borsh.write(integrationAcls, _data, i); i += Borsh.writeOptional(isRawOpenfunds, _data, i); i += Borsh.writeOptional(rawOpenfunds, _data, i); return i - offset; @@ -150,7 +157,8 @@ public int l() { + Borsh.lenOptional(company) + Borsh.lenOptional(manager) + Borsh.lenOptional(created) - + Borsh.len(acls) + + Borsh.len(delegateAcls) + + Borsh.len(integrationAcls) + 2 + Borsh.lenOptional(rawOpenfunds); } diff --git a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/IntegrationAcl.java b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/IntegrationAcl.java new file mode 100644 index 0000000..f1b9884 --- /dev/null +++ b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/IntegrationAcl.java @@ -0,0 +1,27 @@ +package software.sava.anchor.programs.glam.anchor.types; + +import software.sava.core.borsh.Borsh; + +public record IntegrationAcl(IntegrationName name, IntegrationFeature[] features) implements Borsh { + + public static IntegrationAcl read(final byte[] _data, final int offset) { + int i = offset; + final var name = IntegrationName.read(_data, i); + i += Borsh.len(name); + final var features = Borsh.readVector(IntegrationFeature.class, IntegrationFeature::read, _data, i); + return new IntegrationAcl(name, features); + } + + @Override + public int write(final byte[] _data, final int offset) { + int i = offset; + i += Borsh.write(name, _data, i); + i += Borsh.write(features, _data, i); + return i - offset; + } + + @Override + public int l() { + return Borsh.len(name) + Borsh.len(features); + } +} diff --git a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/IntegrationFeature.java b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/IntegrationFeature.java new file mode 100644 index 0000000..ea98d98 --- /dev/null +++ b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/IntegrationFeature.java @@ -0,0 +1,12 @@ +package software.sava.anchor.programs.glam.anchor.types; + +import software.sava.core.borsh.Borsh; + +public enum IntegrationFeature implements Borsh.Enum { + + All; + + public static IntegrationFeature read(final byte[] _data, final int offset) { + return Borsh.read(IntegrationFeature.values(), _data, offset); + } +} \ No newline at end of file diff --git a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/IntegrationName.java b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/IntegrationName.java new file mode 100644 index 0000000..a2c3448 --- /dev/null +++ b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/IntegrationName.java @@ -0,0 +1,17 @@ +package software.sava.anchor.programs.glam.anchor.types; + +import software.sava.core.borsh.Borsh; + +// * Integration ACL +public enum IntegrationName implements Borsh.Enum { + + Drift, + StakePool, + NativeStaking, + Marinade, + Jupiter; + + public static IntegrationName read(final byte[] _data, final int offset) { + return Borsh.read(IntegrationName.values(), _data, offset); + } +} \ No newline at end of file diff --git a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/Permission.java b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/Permission.java index 615cf08..afb5ac0 100644 --- a/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/Permission.java +++ b/programs/src/main/java/software/sava/anchor/programs/glam/anchor/types/Permission.java @@ -2,6 +2,7 @@ import software.sava.core.borsh.Borsh; +// * Delegate ACL public enum Permission implements Borsh.Enum { DriftDeposit,