From b48d809360c95efcb963c5fb08ffcf42c21b3b30a08eb0ff4984caad93e7661b Mon Sep 17 00:00:00 2001 From: Minimons Date: Tue, 9 Jun 2026 21:27:56 +0200 Subject: [PATCH] Move hubd API to Impl temporarily --- build.gradle.kts | 1 - .../com/r35157/libs/math/UtilsBigDecimal.java | 13 ++ .../com/r35157/libs/math/UtilsDouble.java | 7 + .../libs/notification/AddressedNotifier.java | 14 ++ .../libs/notification/BoundNotifier.java | 7 + .../notification/NotificationDestination.java | 3 + .../notification/NotificationMessage.java | 4 + .../valuetypes/WellKnownCurrencyTypes.java | 52 +++++ .../libs/valuetypes/basic/CurrencyType.java | 16 ++ .../valuetypes/basic/SemanticVersion.java | 70 ++++++ .../valuetypes/basic/SmtpConfiguration.java | 7 + .../libs/valuetypes/basic/TradingPair.java | 6 + .../com/r35157/nenjim/hubd/NenjimHub.java | 18 ++ .../com/r35157/nenjim/hubd/ctx/Context.java | 14 ++ .../nenjim/hubd/journal/JournalManager.java | 5 + .../com/r35157/nenjim/hubd/module/Module.java | 4 + .../com/r35157/libs/raydium/Raydium.tjava | 220 ++++++++++++++++++ .../raydium/RaydiumConcentratedPoolInfo.tjava | 30 +++ .../RaydiumConcentratedPoolState.tjava | 32 +++ .../RaydiumConcentratedPositionState.tjava | 12 + ...RaydiumLiquidityPoolPositionStandard.tjava | 29 +++ .../raydium/RaydiumLiquidityPoolPrice.tjava | 18 ++ .../RaydiumLiquidityPoolTokenAmounts.tjava | 28 +++ .../r35157/libs/solana/SPLTokenHolding.tjava | 30 +++ .../r35157/libs/solana/SPLTokenSupply.tjava | 24 ++ .../libs/solana/SolanaAccountInfo.tjava | 24 ++ .../r35157/libs/solana/SolanaBlockChain.tjava | 146 ++++++++++++ .../r35157/libs/solana/SolanaConstants.tjava | 19 ++ .../solana/SolanaProgramAddressSeed.tjava | 51 ++++ .../solana/SolanaProgramAddressSeedKind.tjava | 21 ++ .../SolanaProgramDerivedAddress.tjava | 17 ++ .../valuetypes/economic/SolanaSPLToken.tjava | 21 ++ .../economic/SolanaSPLTokenProgram.tjava | 44 ++++ .../libs/valuetypes/basic/Credentials.tjava | 6 + .../libs/valuetypes/basic/MoneyAmount.tjava | 15 ++ .../libs/valuetypes/basic/MoneyPrice.tjava | 8 + .../valuetypes/basic/NetworkEndPoint.tjava | 7 + .../r35157/libs/valuetypes/basic/Range.tjava | 43 ++++ .../nenjim/hubd/ctx/ContextManager.tjava | 9 + .../r35157/nenjim/hubd/journal/Journal.tjava | 26 +++ .../nenjim/hubd/module/Dependency.tjava | 8 + .../r35157/nenjim/hubd/module/Release.tjava | 14 ++ 42 files changed, 1142 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/r35157/libs/math/UtilsBigDecimal.java create mode 100644 src/main/java/com/r35157/libs/math/UtilsDouble.java create mode 100644 src/main/java/com/r35157/libs/notification/AddressedNotifier.java create mode 100644 src/main/java/com/r35157/libs/notification/BoundNotifier.java create mode 100644 src/main/java/com/r35157/libs/notification/NotificationDestination.java create mode 100644 src/main/java/com/r35157/libs/notification/NotificationMessage.java create mode 100644 src/main/java/com/r35157/libs/solana/valuetypes/WellKnownCurrencyTypes.java create mode 100644 src/main/java/com/r35157/libs/valuetypes/basic/CurrencyType.java create mode 100644 src/main/java/com/r35157/libs/valuetypes/basic/SemanticVersion.java create mode 100644 src/main/java/com/r35157/libs/valuetypes/basic/SmtpConfiguration.java create mode 100644 src/main/java/com/r35157/libs/valuetypes/basic/TradingPair.java create mode 100644 src/main/java/com/r35157/nenjim/hubd/NenjimHub.java create mode 100644 src/main/java/com/r35157/nenjim/hubd/ctx/Context.java create mode 100644 src/main/java/com/r35157/nenjim/hubd/journal/JournalManager.java create mode 100644 src/main/java/com/r35157/nenjim/hubd/module/Module.java create mode 100644 src/main/tjava/com/r35157/libs/raydium/Raydium.tjava create mode 100644 src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPoolInfo.tjava create mode 100644 src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPoolState.tjava create mode 100644 src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPositionState.tjava create mode 100644 src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolPositionStandard.tjava create mode 100644 src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolPrice.tjava create mode 100644 src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolTokenAmounts.tjava create mode 100644 src/main/tjava/com/r35157/libs/solana/SPLTokenHolding.tjava create mode 100644 src/main/tjava/com/r35157/libs/solana/SPLTokenSupply.tjava create mode 100644 src/main/tjava/com/r35157/libs/solana/SolanaAccountInfo.tjava create mode 100644 src/main/tjava/com/r35157/libs/solana/SolanaBlockChain.tjava create mode 100644 src/main/tjava/com/r35157/libs/solana/SolanaConstants.tjava create mode 100644 src/main/tjava/com/r35157/libs/solana/SolanaProgramAddressSeed.tjava create mode 100644 src/main/tjava/com/r35157/libs/solana/SolanaProgramAddressSeedKind.tjava create mode 100644 src/main/tjava/com/r35157/libs/solana/valuetypes/SolanaProgramDerivedAddress.tjava create mode 100644 src/main/tjava/com/r35157/libs/solana/valuetypes/economic/SolanaSPLToken.tjava create mode 100644 src/main/tjava/com/r35157/libs/solana/valuetypes/economic/SolanaSPLTokenProgram.tjava create mode 100644 src/main/tjava/com/r35157/libs/valuetypes/basic/Credentials.tjava create mode 100644 src/main/tjava/com/r35157/libs/valuetypes/basic/MoneyAmount.tjava create mode 100644 src/main/tjava/com/r35157/libs/valuetypes/basic/MoneyPrice.tjava create mode 100644 src/main/tjava/com/r35157/libs/valuetypes/basic/NetworkEndPoint.tjava create mode 100644 src/main/tjava/com/r35157/libs/valuetypes/basic/Range.tjava create mode 100644 src/main/tjava/com/r35157/nenjim/hubd/ctx/ContextManager.tjava create mode 100644 src/main/tjava/com/r35157/nenjim/hubd/journal/Journal.tjava create mode 100644 src/main/tjava/com/r35157/nenjim/hubd/module/Dependency.tjava create mode 100644 src/main/tjava/com/r35157/nenjim/hubd/module/Release.tjava diff --git a/build.gradle.kts b/build.gradle.kts index 202c7c6..7b7d0b7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -45,7 +45,6 @@ dependencies { implementation("com.fasterxml.jackson.core:jackson-databind:2.17.2") implementation("org.slf4j:slf4j-api:2.0.18") - implementation("com.r35157.nenjim:hubd-api:0.1-dev") } java { diff --git a/src/main/java/com/r35157/libs/math/UtilsBigDecimal.java b/src/main/java/com/r35157/libs/math/UtilsBigDecimal.java new file mode 100644 index 0000000..f5b8442 --- /dev/null +++ b/src/main/java/com/r35157/libs/math/UtilsBigDecimal.java @@ -0,0 +1,13 @@ +package com.r35157.libs.math; + +import java.math.BigDecimal; +import java.math.MathContext; + +public interface UtilsBigDecimal { + BigDecimal min(BigDecimal a, BigDecimal b); + BigDecimal max(BigDecimal a, BigDecimal b); + BigDecimal sqrt(BigDecimal value, MathContext mc); + + BigDecimal TWO = new BigDecimal("2"); + BigDecimal THREE = new BigDecimal("3"); +} diff --git a/src/main/java/com/r35157/libs/math/UtilsDouble.java b/src/main/java/com/r35157/libs/math/UtilsDouble.java new file mode 100644 index 0000000..dcc3d05 --- /dev/null +++ b/src/main/java/com/r35157/libs/math/UtilsDouble.java @@ -0,0 +1,7 @@ +package com.r35157.libs.math; + +import java.math.BigDecimal; + +public interface UtilsDouble { + double erf(BigDecimal x); +} diff --git a/src/main/java/com/r35157/libs/notification/AddressedNotifier.java b/src/main/java/com/r35157/libs/notification/AddressedNotifier.java new file mode 100644 index 0000000..05ff801 --- /dev/null +++ b/src/main/java/com/r35157/libs/notification/AddressedNotifier.java @@ -0,0 +1,14 @@ +package com.r35157.libs.notification; + +import java.io.IOException; + +public interface AddressedNotifier< + D extends NotificationDestination, + M extends NotificationMessage> +{ + void push(D destination, M message) throws IOException; + + default BoundNotifier bind(D destination) { + return message -> push(destination, message); + } +} \ No newline at end of file diff --git a/src/main/java/com/r35157/libs/notification/BoundNotifier.java b/src/main/java/com/r35157/libs/notification/BoundNotifier.java new file mode 100644 index 0000000..c2f2e0e --- /dev/null +++ b/src/main/java/com/r35157/libs/notification/BoundNotifier.java @@ -0,0 +1,7 @@ +package com.r35157.libs.notification; + +import java.io.IOException; + +public interface BoundNotifier { + void push(M message) throws IOException; +} \ No newline at end of file diff --git a/src/main/java/com/r35157/libs/notification/NotificationDestination.java b/src/main/java/com/r35157/libs/notification/NotificationDestination.java new file mode 100644 index 0000000..ec80aaa --- /dev/null +++ b/src/main/java/com/r35157/libs/notification/NotificationDestination.java @@ -0,0 +1,3 @@ +package com.r35157.libs.notification; + +public interface NotificationDestination {} diff --git a/src/main/java/com/r35157/libs/notification/NotificationMessage.java b/src/main/java/com/r35157/libs/notification/NotificationMessage.java new file mode 100644 index 0000000..3778b62 --- /dev/null +++ b/src/main/java/com/r35157/libs/notification/NotificationMessage.java @@ -0,0 +1,4 @@ +package com.r35157.libs.notification; + +public interface NotificationMessage { +} diff --git a/src/main/java/com/r35157/libs/solana/valuetypes/WellKnownCurrencyTypes.java b/src/main/java/com/r35157/libs/solana/valuetypes/WellKnownCurrencyTypes.java new file mode 100644 index 0000000..7c88278 --- /dev/null +++ b/src/main/java/com/r35157/libs/solana/valuetypes/WellKnownCurrencyTypes.java @@ -0,0 +1,52 @@ +package com.r35157.libs.solana.valuetypes; + +import com.r35157.libs.valuetypes.basic.CurrencyType; + +import java.util.UUID; + +/** + * Defines well-known currency types used by the Solana integration. + * + *

Each enum value wraps a {@link CurrencyType} with a stable identifier and a + * human-readable currency name. These predefined values are intended for common + * currencies that the Solana-related modules need to reference consistently.

+ */ +public enum WellKnownCurrencyTypes { + /** + * Native Solana currency. + */ + SOLANA(new CurrencyType( + UUID.fromString("019e0116-fce5-792f-a647-fa6da4dffec5"), + "Solana", + "SOL") + ), + + /** + * Syrup USDC token currency. + */ + SYRUPUSDC(new CurrencyType( + UUID.fromString("019e1d51-0600-7956-8231-f3b7058a91c2"), + "SyrupUSDC", + "SyrupUSDC") + ); + + /** + * Creates a well-known currency type entry. + * + * @param currencyType the currency type represented by this enum value + */ + WellKnownCurrencyTypes(CurrencyType currencyType) { + this.currencyType = currencyType; + } + + /** + * Returns the currency type represented by this enum value. + * + * @return the represented currency type + */ + public CurrencyType getCurrencyType() { + return currencyType; + } + + private final CurrencyType currencyType; +} \ No newline at end of file diff --git a/src/main/java/com/r35157/libs/valuetypes/basic/CurrencyType.java b/src/main/java/com/r35157/libs/valuetypes/basic/CurrencyType.java new file mode 100644 index 0000000..dd51822 --- /dev/null +++ b/src/main/java/com/r35157/libs/valuetypes/basic/CurrencyType.java @@ -0,0 +1,16 @@ +package com.r35157.libs.valuetypes.basic; + +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +public record CurrencyType( + UUID id, + String name, + String symbol +) { + @Override + public @NotNull String toString() { + return symbol; + } +} diff --git a/src/main/java/com/r35157/libs/valuetypes/basic/SemanticVersion.java b/src/main/java/com/r35157/libs/valuetypes/basic/SemanticVersion.java new file mode 100644 index 0000000..d8cc612 --- /dev/null +++ b/src/main/java/com/r35157/libs/valuetypes/basic/SemanticVersion.java @@ -0,0 +1,70 @@ +package com.r35157.libs.valuetypes.basic; + +import org.jetbrains.annotations.NotNull; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Semantic Versioning (SemVer): + * A version number has the form MAJOR.MINOR.PATCH. + * - Increment MAJOR for incompatible API changes, + * - Increment MINOR for added functionality in a backward-compatible way, + * - Increment PATCH for backward-compatible bug fixes or improvements. + * TODO: Not the whole specification is implemented yet! + */ +public record SemanticVersion(int major, int minor, int patch) { + /** + * Creates a SemanticVersion and validates that all components are valid. + * + * @throws IllegalArgumentException if any of {@code major}, {@code minor}, or {@code patch} is negative + */ + public SemanticVersion { + initializationGuardClause(major, minor, patch); + } + + public static SemanticVersion of(int major, int minor, int patch) { + return new SemanticVersion(major, minor, patch); + } + + public static SemanticVersion of(int major, int minor) { + return of(major, minor, 0); + } + + public static SemanticVersion of(int major) { + return of(major, 0); + } + + public static SemanticVersion of(@NotNull String versionStr) { + final String s = versionStr.trim(); + + final Matcher m = SEMVER_REGEX.matcher(s); + if (!m.matches()) { + throw new IllegalArgumentException("Invalid semantic version: '" + versionStr + "'!"); + } + + try { + final int major = Integer.parseInt(m.group(1)); + final int minor = m.group(2) != null ? Integer.parseInt(m.group(2)) : 0; + final int patch = m.group(3) != null ? Integer.parseInt(m.group(3)) : 0; + return of(major, minor, patch); + } catch (NumberFormatException e) { + // Happens only with overruns + throw new IllegalArgumentException("Invalid semantic version: '" + versionStr + "'!", e); + } + } + + @Override + public @NotNull String toString() { + return "%d.%d.%d".formatted(major, minor, patch); + } + + private void initializationGuardClause(int major, int minor, int patch) throws IllegalArgumentException { + if (major < 0) throw new IllegalArgumentException("Version element 'major' cannot be negative - was '" + major + "'!"); + if (minor < 0) throw new IllegalArgumentException("Version element 'minor' cannot be negative - was '" + minor + "'!"); + if (patch < 0) throw new IllegalArgumentException("Version element 'patch' cannot be negative - was '" + patch + "'!"); + } + + private static final Pattern SEMVER_REGEX = + Pattern.compile("^(\\d+)(?:\\.(\\d+)(?:\\.(\\d+))?)?$"); +} diff --git a/src/main/java/com/r35157/libs/valuetypes/basic/SmtpConfiguration.java b/src/main/java/com/r35157/libs/valuetypes/basic/SmtpConfiguration.java new file mode 100644 index 0000000..b577cd1 --- /dev/null +++ b/src/main/java/com/r35157/libs/valuetypes/basic/SmtpConfiguration.java @@ -0,0 +1,7 @@ +package com.r35157.libs.valuetypes.basic; + +public record SmtpConfiguration( + NetworkEndPoint networkEndPoint, + Credentials credentials +) { +} \ No newline at end of file diff --git a/src/main/java/com/r35157/libs/valuetypes/basic/TradingPair.java b/src/main/java/com/r35157/libs/valuetypes/basic/TradingPair.java new file mode 100644 index 0000000..c028ff6 --- /dev/null +++ b/src/main/java/com/r35157/libs/valuetypes/basic/TradingPair.java @@ -0,0 +1,6 @@ +package com.r35157.libs.valuetypes.basic; + +public record TradingPair( + CurrencyType base, // The thing you are buying or selling. + CurrencyType quote // The currency/unit used to price the base asset. +) { } diff --git a/src/main/java/com/r35157/nenjim/hubd/NenjimHub.java b/src/main/java/com/r35157/nenjim/hubd/NenjimHub.java new file mode 100644 index 0000000..2685456 --- /dev/null +++ b/src/main/java/com/r35157/nenjim/hubd/NenjimHub.java @@ -0,0 +1,18 @@ +package com.r35157.nenjim.hubd; + +import com.r35157.nenjim.hubd.journal.Journal; +import org.jetbrains.annotations.NotNull; + +public interface NenjimHub { + /** + * A no-operation (noop). This method is suppoted to do nothing. + */ + void noop(); + + /** + * + * @param journal + */ + void monitorJournal(@NotNull Journal journal); +} + diff --git a/src/main/java/com/r35157/nenjim/hubd/ctx/Context.java b/src/main/java/com/r35157/nenjim/hubd/ctx/Context.java new file mode 100644 index 0000000..fa5dec4 --- /dev/null +++ b/src/main/java/com/r35157/nenjim/hubd/ctx/Context.java @@ -0,0 +1,14 @@ +package com.r35157.nenjim.hubd.ctx; + +import com.r35157.libs.valuetypes.basic.SemanticVersion; + +public record Context( +) { + public String getName() { + return "Some Context"; + } + + public SemanticVersion getVersion(String fqPackageName) { + return null; // new SemanticVersion(0, 1, 0); + } +} diff --git a/src/main/java/com/r35157/nenjim/hubd/journal/JournalManager.java b/src/main/java/com/r35157/nenjim/hubd/journal/JournalManager.java new file mode 100644 index 0000000..761cf0f --- /dev/null +++ b/src/main/java/com/r35157/nenjim/hubd/journal/JournalManager.java @@ -0,0 +1,5 @@ +package com.r35157.nenjim.hubd.journal; + +public interface JournalManager { + Journal getJournal(String moduleName); +} diff --git a/src/main/java/com/r35157/nenjim/hubd/module/Module.java b/src/main/java/com/r35157/nenjim/hubd/module/Module.java new file mode 100644 index 0000000..ec7899f --- /dev/null +++ b/src/main/java/com/r35157/nenjim/hubd/module/Module.java @@ -0,0 +1,4 @@ +package com.r35157.nenjim.hubd.module; + +public record Module() { +} diff --git a/src/main/tjava/com/r35157/libs/raydium/Raydium.tjava b/src/main/tjava/com/r35157/libs/raydium/Raydium.tjava new file mode 100644 index 0000000..11213da --- /dev/null +++ b/src/main/tjava/com/r35157/libs/raydium/Raydium.tjava @@ -0,0 +1,220 @@ +package com.r35157.libs.raydium; + +import com.r35157.libs.valuetypes.basic.MoneyAmount; +import com.r35157.libs.valuetypes.basic.Range; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.Set; + +public interface Raydium { + /** + * Fetches the current price for a Raydium liquidity pool. + * + *

The price is fetched from Raydium using the supplied pool id. The pool id may refer + * to any Raydium liquidity pool supported by the implementation.

+ * + * @param poolId the Raydium liquidity pool id + * @return the current pool price expressed as a Solana amount + * @throws IOException if the price could not be fetched or the response could not be parsed + * @throws InterruptedException if the calling thread is interrupted while fetching the price + */ + ΩSolanaAmountΩ fetchPoolPrice(ΩRaydiumLiquidityPoolIdΩ poolId) throws IOException, InterruptedException; + + /** + * Fetches the Raydium liquidity pool ids where the supplied Solana owner address has positions. + * + *

The returned set contains unique pool ids and does not guarantee iteration order.

+ * + *

An implementation may discover pools through multiple Raydium position mechanisms, + * including standard liquidity pool token holdings and concentrated liquidity position NFTs. + * The returned ids are aggregated under the common {@code ΩRaydiumLiquidityPoolIdΩ} ValueTag.

+ * + * @param ownerAddress the Solana owner address to inspect + * @return the unique Raydium liquidity pool ids where the owner has a position + * @throws IOException if liquidity pool ids could not be fetched or parsed + * @throws InterruptedException if the calling thread is interrupted while fetching liquidity pool ids + */ + Set<ΩRaydiumLiquidityPoolIdΩ> fetchLiquidityPoolIds(ΩSolanaAddressΩ ownerAddress) throws IOException, InterruptedException; + + /** + * Fetches the Raydium concentrated liquidity position NFT ids owned by a Solana address. + * + *

The supplied owner address is inspected for token holdings that are candidates for + * Raydium concentrated liquidity position NFTs. The implementation verifies the candidates + * against Raydium's concentrated liquidity program before returning them.

+ * + *

The returned set contains position NFT ids, not pool ids. Callers can use the returned + * NFT ids to fetch concentrated position state and then determine which pools the positions + * belong to.

+ * + * @param ownerAddress the Solana owner address to inspect + * @return the Raydium concentrated liquidity position NFT ids owned by the address + * @throws IOException if the position NFT ids could not be fetched or verified + * @throws InterruptedException if the calling thread is interrupted while fetching position NFT ids + */ + Set<ΩRaydiumLiquidityPoolPositionNftIdΩ> fetchConcentratedPositionNftIds( + ΩSolanaAddressΩ ownerAddress + ) throws IOException, InterruptedException; + + /** + * Fetches the total token amounts held by a standard Raydium liquidity pool. + * + *

The returned amounts represent the pool-level token reserves for token A and + * token B. They do not describe a single liquidity provider's share of the pool.

+ * + * @param poolId the Raydium standard liquidity pool id + * @return the total token amounts held by the standard liquidity pool + * @throws IOException if the pool information could not be fetched or parsed + * @throws InterruptedException if the calling thread is interrupted while fetching the pool information + */ + RaydiumLiquidityPoolTokenAmounts fetchStandardLiquidityPoolTokenAmounts( + ΩRaydiumLiquidityPoolStandardIdΩ poolId + ) throws IOException, InterruptedException; + + /** + * Fetches the token amounts represented by an owner's position in a standard Raydium liquidity pool. + * + *

The returned amounts describe the owner's calculated share of token A and token B in the + * standard liquidity pool. The calculation is based on the owner's liquidity pool token holdings, + * the LP mint supply and the pool-level token reserves returned by Raydium.

+ * + *

Important: The returned amounts should be treated as an estimated pool-share + * view, not as an exact withdraw preview. Raydium's UI may show lower amounts when removing + * liquidity. For example, for pool {@code 8os8bnXoy5voKv3uBPPuVGyqWZGJaa2RRri5RbLUwPCY}, + * the pool-share calculation returned approximately {@code 2.1594651 EVE / 31.867153 USDT}, + * while Raydium's 100% withdraw preview showed approximately {@code 2.1062559 EVE / 31.55046 USDT}.

+ * + *

// TODO To match Raydium's withdraw preview more precisely, this method should eventually use + * on-chain pool state and vault balances, excluding protocol, fund and creator fee buckets. + * Raydium's CPMM pool state contains fee fields and a {@code vault_amount_without_fee(...)} + * calculation that appears relevant for this. Until that is implemented, this method represents + * an estimate based on Raydium REST pool amounts and Solana LP supply.

+ * + *

This method is specific to standard liquidity pools. Concentrated liquidity positions are + * represented differently and should use concentrated-position-specific methods.

+ * + * @param ownerAddress the Solana owner address whose standard pool position should be inspected + * @param poolId the Raydium standard liquidity pool id + * @return the estimated token amounts represented by the owner's standard pool position + * @throws IOException if the position information could not be fetched, calculated or parsed + * @throws InterruptedException if the calling thread is interrupted while fetching the position information + */ + RaydiumLiquidityPoolTokenAmounts fetchStandardLiquidityPoolPositionTokenAmounts( + ΩSolanaAddressΩ ownerAddress, + ΩRaydiumLiquidityPoolStandardIdΩ poolId + ) throws IOException, InterruptedException; + + /** + * Fetches the raw Raydium concentrated position state for a position NFT. + * + *

The supplied NFT id identifies a concentrated liquidity position on Raydium. The returned + * state is a low-level Raydium representation of that position, including the pool id, tick + * boundaries and liquidity value decoded from the position account.

+ * + *

This method does not calculate token amounts, price ranges or higher-level strategy values. + * Higher-level modules may use the returned state together with pool state, current price and CLMM + * math to calculate those values.

+ * + * @param positionNftId the Raydium concentrated liquidity position NFT id + * @return the raw Raydium concentrated position state for the supplied position NFT + * @throws IOException if the position state could not be fetched or decoded + * @throws InterruptedException if the calling thread is interrupted while fetching the position state + */ + RaydiumConcentratedPositionState fetchConcentratedPositionState( + ΩRaydiumLiquidityPoolPositionNftIdΩ positionNftId + ) throws IOException, InterruptedException; + + /** + * Fetches basic information about a Raydium concentrated liquidity pool. + * + *

The returned information contains the pool id, token mint addresses, + * token decimals and the current pool price as reported by Raydium. This is + * low-level pool information that can be combined with concentrated position + * state to interpret ticks, price ranges and liquidity values.

+ * + *

This method does not fetch an owner's position and does not calculate token + * amounts for a position.

+ * + * @param poolId the Raydium concentrated liquidity pool id + * @return basic information about the concentrated liquidity pool + * @throws IOException if the pool information could not be fetched or parsed + * @throws InterruptedException if the calling thread is interrupted while fetching the pool information + */ + RaydiumConcentratedPoolInfo fetchConcentratedPoolInfo( + ΩRaydiumLiquidityPoolConcentratedIdΩ poolId + ) throws IOException, InterruptedException; + + /** + * Fetches the raw on-chain state for a Raydium concentrated liquidity pool. + * + *

The returned state is decoded from the Raydium concentrated pool account on Solana. + * It contains low-level CLMM state such as active liquidity, the current square-root price + * in Q64.64 format and the current tick index.

+ * + *

This method is different from {@link #fetchConcentratedPoolInfo(ΩRaydiumLiquidityPoolConcentratedIdΩ)}, + * which fetches pool information from Raydium's REST API. This method is intended for calculations + * that need fresher or more precise on-chain CLMM state than the REST API price field can provide.

+ * + *

This method does not fetch token metadata, token decimals or position state. Those values + * must be supplied separately when needed for higher-level calculations.

+ * + * @param poolId the Raydium concentrated liquidity pool id + * @return the raw on-chain state for the concentrated liquidity pool + * @throws IOException if the pool state could not be fetched or decoded + * @throws InterruptedException if the calling thread is interrupted while fetching the pool state + */ + RaydiumConcentratedPoolState fetchConcentratedPoolState( + ΩRaydiumLiquidityPoolConcentratedIdΩ poolId + ) throws IOException, InterruptedException; + + /** + * Calculates the price range represented by a Raydium concentrated liquidity position. + * + *

The returned range is calculated from the position's lower and upper tick indexes, + * adjusted by the token decimals from the concentrated pool information.

+ * + *

Raydium concentrated liquidity ranges are treated as lower-inclusive and + * upper-exclusive. This avoids overlapping ownership at shared boundaries between + * adjacent tick ranges.

+ * + * @param positionState the concentrated position state containing the lower and upper tick indexes + * @param poolInfo the concentrated pool information containing token decimals and pool context + * @param decimalPlaces the number decimal places in the resulting price currency + * @return the price range represented by the concentrated liquidity position + */ + Range<ΩPriceΩ> calculateConcentratedPositionPriceRange( + RaydiumConcentratedPositionState positionState, + RaydiumConcentratedPoolInfo poolInfo, + int decimalPlaces + ); + + /** + * Calculates the token amounts represented by a Raydium concentrated liquidity position using + * raw on-chain pool state for the current square-root price. + * + *

The returned amounts are calculated from the position liquidity, the position tick range, + * token decimals from the concentrated pool information, and the current square-root price from + * the supplied on-chain pool state. This can be more precise than using the REST API price field + * from {@link RaydiumConcentratedPoolInfo}.

+ * + *

If the current pool price is below the position range, the position is represented as + * token A. If the current pool price is above the position range, the position is represented + * as token B. If the current pool price is inside the position range, the position is + * represented as a mix of token A and token B.

+ * + *

This method performs only the mathematical conversion from Raydium concentrated position + * state and pool state to token amounts. It does not fetch position state, pool information or + * pool state.

+ * + * @param positionState the concentrated position state containing tick indexes and liquidity + * @param poolInfo the concentrated pool information containing token mints and decimals + * @param poolState the on-chain concentrated pool state containing current square-root price and tick + * @return the token amounts represented by the concentrated liquidity position + */ + RaydiumLiquidityPoolTokenAmounts calculateConcentratedPositionTokenAmounts( + RaydiumConcentratedPositionState positionState, + RaydiumConcentratedPoolInfo poolInfo, + RaydiumConcentratedPoolState poolState + ); +} diff --git a/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPoolInfo.tjava b/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPoolInfo.tjava new file mode 100644 index 0000000..4f8683f --- /dev/null +++ b/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPoolInfo.tjava @@ -0,0 +1,30 @@ +package com.r35157.libs.raydium; + +import java.math.BigDecimal; + +/** + * Represents basic information about a Raydium concentrated liquidity pool. + * + *

This record contains the pool-level information needed to interpret + * concentrated position state. The mint addresses and decimals are required when + * converting Raydium tick indexes into human-readable prices.

+ * + *

This record is intentionally a low-level Raydium model. It does not describe + * an owner's position and does not calculate token amounts for a position.

+ * + * @param poolId the Raydium concentrated liquidity pool id + * @param mintA the SPL mint address of token A + * @param mintADecimals the number of decimals used by token A + * @param mintB the SPL mint address of token B + * @param mintBDecimals the number of decimals used by token B + * @param priceEstimate the pool price estimate as reported by Raydium + */ +public record RaydiumConcentratedPoolInfo( + ΩRaydiumLiquidityPoolConcentratedIdΩ poolId, + ΩSPLMintAddressΩ mintA, + ΩamountDecimalsΩ mintADecimals, + ΩSPLMintAddressΩ mintB, + ΩamountDecimalsΩ mintBDecimals, + ΩPriceΩ priceEstimate +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPoolState.tjava b/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPoolState.tjava new file mode 100644 index 0000000..97ebca3 --- /dev/null +++ b/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPoolState.tjava @@ -0,0 +1,32 @@ +package com.r35157.libs.raydium; + +import java.math.BigInteger; + +/** + * Represents raw on-chain state for a Raydium concentrated liquidity pool. + * + *

This record contains low-level CLMM pool state decoded from the Raydium + * concentrated liquidity pool account on Solana. It is different from + * {@link RaydiumConcentratedPoolInfo}, which represents pool information fetched + * from Raydium's REST API.

+ * + *

The {@code sqrtPriceX64} field represents the current square-root price in + * Raydium's fixed-point Q64.64 format. This value can be used for more precise + * concentrated liquidity calculations than using the REST API price field.

+ * + *

This record does not contain token mint metadata, decimals or display prices. + * Those values belong in {@link RaydiumConcentratedPoolInfo} or in higher-level + * calculations that combine pool state with pool information.

+ * + * @param poolId the Raydium concentrated liquidity pool id + * @param liquidity the currently active liquidity in the pool + * @param sqrtPriceX64 the current square-root price in Q64.64 fixed-point format + * @param tickCurrent the current Raydium liquidity tick index + */ +public record RaydiumConcentratedPoolState( + ΩRaydiumLiquidityPoolConcentratedIdΩ poolId, + ΩRaydiumLiquidityΩ liquidity, + ΩRaydiumSqrtPriceX64Ω sqrtPriceX64, + ΩraydiumLiquidityTickIndexΩ tickCurrent +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPositionState.tjava b/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPositionState.tjava new file mode 100644 index 0000000..ccc5496 --- /dev/null +++ b/src/main/tjava/com/r35157/libs/raydium/RaydiumConcentratedPositionState.tjava @@ -0,0 +1,12 @@ +package com.r35157.libs.raydium; + +import java.math.BigInteger; + +public record RaydiumConcentratedPositionState( + ΩRaydiumLiquidityPoolPositionNftIdΩ nftId, + ΩRaydiumLiquidityPoolConcentratedIdΩ poolId, + ΩraydiumLiquidityTickIndexΩ tickLowerIndex, + ΩraydiumLiquidityTickIndexΩ tickUpperIndex, + ΩRaydiumLiquidityΩ liquidity +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolPositionStandard.tjava b/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolPositionStandard.tjava new file mode 100644 index 0000000..2991e8f --- /dev/null +++ b/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolPositionStandard.tjava @@ -0,0 +1,29 @@ +package com.r35157.libs.raydium; + +import java.math.BigDecimal; + +/** + * Represents a standard Raydium liquidity pool position. + * + *

A standard liquidity position is represented by ownership of liquidity pool + * tokens. The liquidity pool token mint identifies the LP token, while the SPL + * token account identifies where the owner holds the LP token balance.

+ * + *

This record describes both the account holding the LP tokens and the amount + * of LP tokens held in that account. It does not represent a concentrated + * liquidity NFT position.

+ * + * @param positionId the Raydium liquidity pool position id + * @param lpMintId the SPL mint address of the Raydium liquidity pool token + * @param lpAccount the Raydium liquidity pool account associated with the position + * @param lpTokenAccount the SPL token account holding the liquidity pool tokens + * @param lpTokenAmount the amount of liquidity pool tokens held in the SPL token account + */ +public record RaydiumLiquidityPoolPositionStandard( + ΩRaydiumLiquidityPoolPositionIdΩ positionId, + ΩRaydiumLiquidityPoolPositionMintIdΩ lpMintId, + ΩRaydiumLiquidityPoolAccountΩ lpAccount, + ΩSPLTokenAccountΩ lpTokenAccount, + ΩAmountΩ lpTokenAmount +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolPrice.tjava b/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolPrice.tjava new file mode 100644 index 0000000..24bd54d --- /dev/null +++ b/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolPrice.tjava @@ -0,0 +1,18 @@ +package com.r35157.libs.raydium; + +import com.r35157.libs.valuetypes.basic.MoneyAmount; + +/** + * Represents the price of a Raydium liquidity pool. + * + *

The pool id identifies the Raydium liquidity pool for which the price applies. + * The amount contains the price value returned or calculated for that pool.

+ * + * @param poolId the Raydium liquidity pool id that the price belongs to + * @param amount the price amount for the liquidity pool + */ +public record RaydiumLiquidityPoolPrice( + ΩRaydiumLiquidityPoolIdΩ poolId, + MoneyAmount amount +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolTokenAmounts.tjava b/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolTokenAmounts.tjava new file mode 100644 index 0000000..c229b1e --- /dev/null +++ b/src/main/tjava/com/r35157/libs/raydium/RaydiumLiquidityPoolTokenAmounts.tjava @@ -0,0 +1,28 @@ +package com.r35157.libs.raydium; + +import java.math.BigDecimal; + +/** + * Represents token amounts for a Raydium liquidity pool or liquidity position. + * + *

The pool id identifies the Raydium liquidity pool that the amounts belong to. + * The two mint addresses identify the tokens in the pool, and the corresponding + * amounts describe how much of each token is represented.

+ * + *

This record can be used for total pool amounts as well as calculated position + * amounts, depending on the method returning it.

+ * + * @param poolId the Raydium liquidity pool id + * @param mintA the SPL mint address of token A + * @param amountA the amount of token A + * @param mintB the SPL mint address of token B + * @param amountB the amount of token B + */ +public record RaydiumLiquidityPoolTokenAmounts( + ΩRaydiumLiquidityPoolIdΩ poolId, + ΩSPLMintAddressΩ mintA, + ΩAmountΩ amountA, + ΩSPLMintAddressΩ mintB, + ΩAmountΩ amountB +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/solana/SPLTokenHolding.tjava b/src/main/tjava/com/r35157/libs/solana/SPLTokenHolding.tjava new file mode 100644 index 0000000..9a082ee --- /dev/null +++ b/src/main/tjava/com/r35157/libs/solana/SPLTokenHolding.tjava @@ -0,0 +1,30 @@ +package com.r35157.libs.solana; + +import java.math.BigDecimal; + +/** + * Represents an SPL token holding owned by a Solana address. + * + *

The holding describes one SPL token account and the token mint it belongs to. + * The amount is represented both as a UI amount and as the raw on-chain amount, + * together with the number of decimals used by the token mint.

+ * + *

The program id identifies which SPL token program owns the token account, + * for example the original SPL Token Program or the Token-2022 Program.

+ * + * @param tokenAccount the SPL token account holding the token balance + * @param mintAddress the SPL mint address of the token + * @param uiAmount the human-readable token amount + * @param rawAmount the raw on-chain token amount before decimal conversion + * @param decimals the number of decimals used by the token mint + * @param programId the SPL token program id that owns the token account + */ +public record SPLTokenHolding( + ΩSPLTokenAccountΩ tokenAccount, + ΩSPLMintAddressΩ mintAddress, + ΩAmountΩ uiAmount, + ΩRawAmountΩ rawAmount, + ΩamountDecimalsΩ decimals, + ΩSPLProgramIdΩ programId +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/solana/SPLTokenSupply.tjava b/src/main/tjava/com/r35157/libs/solana/SPLTokenSupply.tjava new file mode 100644 index 0000000..f858e7d --- /dev/null +++ b/src/main/tjava/com/r35157/libs/solana/SPLTokenSupply.tjava @@ -0,0 +1,24 @@ +package com.r35157.libs.solana; + +import java.math.BigDecimal; + +/** + * Represents the total supply of an SPL token mint. + * + *

The supply is represented both as a UI amount and as the raw on-chain amount, + * together with the number of decimals used by the token mint.

+ * + * @param mintAddress the SPL mint address whose supply was fetched + * @param uiAmount the human-readable token supply + * @param rawAmount the raw on-chain token supply before decimal conversion + * @param decimals the number of decimals used by the token mint + * @param programId the SPL token program id for the mint + */ +public record SPLTokenSupply( + ΩSPLMintAddressΩ mintAddress, + ΩAmountΩ uiAmount, + ΩRawAmountΩ rawAmount, + ΩamountDecimalsΩ decimals, + ΩSPLProgramIdΩ programId +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/solana/SolanaAccountInfo.tjava b/src/main/tjava/com/r35157/libs/solana/SolanaAccountInfo.tjava new file mode 100644 index 0000000..ad51767 --- /dev/null +++ b/src/main/tjava/com/r35157/libs/solana/SolanaAccountInfo.tjava @@ -0,0 +1,24 @@ +package com.r35157.libs.solana; + +/** + * Represents basic information about a Solana account. + * + *

The address identifies the account that was queried. The owner identifies + * the Solana program that owns the account. The account data is represented as + * a Base64 encoded string, matching the encoding returned by the Solana RPC + * account-info response.

+ * + *

For normal wallet/system accounts, the data may be empty. Program-owned + * accounts, token accounts, mint accounts and application-specific state + * accounts may contain encoded account data.

+ * + * @param address the Solana account address + * @param owner the Solana program id that owns the account + * @param dataBase64 the account data encoded as Base64, or an empty string if the account has no data + */ +public record SolanaAccountInfo( + ΩSolanaAddressΩ address, + ΩSolanaProgramIdΩ owner, + String dataBase64 +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/solana/SolanaBlockChain.tjava b/src/main/tjava/com/r35157/libs/solana/SolanaBlockChain.tjava new file mode 100644 index 0000000..4e22891 --- /dev/null +++ b/src/main/tjava/com/r35157/libs/solana/SolanaBlockChain.tjava @@ -0,0 +1,146 @@ +package com.r35157.libs.solana; + +import com.r35157.libs.solana.valuetypes.SolanaProgramDerivedAddress; +import com.r35157.libs.solana.valuetypes.economic.SolanaSPLTokenProgram; +import com.r35157.libs.valuetypes.basic.MoneyAmount; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Provides read-oriented access to the Solana blockchain. + * + *

This interface exposes the Solana operations needed by higher-level + * integrations. It can fetch native SOL balances, SPL token holdings, NFT-like + * token holding candidates, account information and program derived addresses.

+ * + *

The interface is intentionally generic and does not contain Raydium-specific + * logic. Higher-level integrations are expected to interpret Solana accounts, + * token holdings and derived addresses according to their own domain rules.

+ */ +public interface SolanaBlockChain { + /** + * Fetches the native SOL balance for a Solana address. + * + * @param address the Solana address to inspect + * @return the native SOL balance for the address + * @throws IOException if the balance could not be fetched or parsed + * @throws InterruptedException if the calling thread is interrupted while fetching the balance + */ + ΩSolanaAmountΩ getBalanceInSolana(ΩSolanaAddressΩ address) throws IOException, InterruptedException; + + /** + * Fetches the native SOL balance for a Solana address in lamports. + * + *

Lamports are the smallest unit of native SOL.

+ * + * @param address the Solana address to inspect + * @return the native SOL balance for the address in lamports + * @throws IOException if the balance could not be fetched or parsed + * @throws InterruptedException if the calling thread is interrupted while fetching the balance + */ + ΩlamportsΩ getBalanceInLamport(ΩSolanaAddressΩ address) throws IOException, InterruptedException; + + /** + * Fetches SPL token holdings owned by a Solana address for a specific token program. + * + *

The supplied token program decides which token accounts are inspected. For example, + * callers may query the original SPL Token Program or the Token-2022 Program depending + * on which token accounts they need to discover.

+ * + * @param ownerAddress the Solana owner address whose token holdings should be inspected + * @param splProgramId the SPL token program to query + * @return a map of SPL mint addresses to token holding information + * @throws IOException if the token holdings could not be fetched or parsed + * @throws InterruptedException if the calling thread is interrupted while fetching token holdings + */ + Map<ΩSPLMintAddressΩ, SPLTokenHolding> getSPLTokenHoldings( + ΩSolanaAddressΩ ownerAddress, + SolanaSPLTokenProgram splProgramId + ) throws IOException, InterruptedException; + + /** + * Fetches NFT-like token mint address candidates owned by a Solana address for a specific token program. + * + *

This method identifies token holdings that look like NFTs within the supplied token + * program. A returned address is only a candidate. Higher-level integrations are responsible + * for deciding whether the returned address has domain-specific meaning.

+ * + * // TODO This method currently identifies candidates from the owner's token holdings only. + * // A token with zero decimals and an owner balance of one is not guaranteed to be a real NFT, + * // because the mint's total supply may still be greater than one. A future implementation + * // should verify the mint supply, for example by using Solana getTokenSupply, before treating + * // the result as a confirmed NFT. + * + * @param ownerAddress the Solana owner address whose NFT-like holdings should be inspected + * @param splProgram the SPL token program to query + * @return the NFT-like Solana mint address candidates owned by the address + * @throws IOException if the NFT candidate addresses could not be fetched or parsed + * @throws InterruptedException if the calling thread is interrupted while fetching NFT candidate addresses + */ + Set<ΩSolanaNFTAddressΩ> getSolanaNFTCandidateAddresses( + ΩSolanaAddressΩ ownerAddress, + SolanaSPLTokenProgram splProgram + ) throws IOException, InterruptedException; + + /** + * Finds a Solana program derived address for a program id and a set of seeds. + * + *

The seeds describe the logical inputs used to derive the address. The implementation + * is responsible for converting each seed into the byte representation required by Solana.

+ * + * @param programId the Solana program id used to derive the address + * @param seeds the seeds used when deriving the program address + * @return the derived Solana address together with its bump value + */ + SolanaProgramDerivedAddress findProgramAddress( + ΩSolanaProgramIdΩ programId, + List seeds + ); + + /** + * Fetches account information for a Solana account address. + * + *

If the account does not exist, this method returns {@code null}. If the account exists, + * the returned value contains the account address, the owning Solana program id and the + * account data encoded as Base64.

+ * + * @param accountAddress the Solana account address to inspect + * @return account information, or {@code null} if the account does not exist + * @throws IOException if the account information could not be fetched or parsed + * @throws InterruptedException if the calling thread is interrupted while fetching account information + */ + SolanaAccountInfo getAccountInfo(ΩSolanaAddressΩ accountAddress) + throws IOException, InterruptedException; + + /** + * Encodes a raw 32-byte Solana address into its textual Solana address representation. + * + *

This method is intended for callers that have obtained raw Solana address bytes from + * account data and need the normal string representation used elsewhere in the API.

+ * + * @param addressBytes the raw 32-byte Solana address + * @return the encoded Solana address + * @throws IllegalArgumentException if the supplied byte array is not a valid Solana address length + */ + ΩSolanaAddressΩ encodeSolanaAddress(byte[] addressBytes); + + /** + * Fetches the total supply of an SPL token mint for a specific token program. + * + *

The supplied token program identifies which SPL token program owns the mint, + * for example the original SPL Token Program or the Token-2022 Program.

+ * + * @param mintAddress the SPL mint address whose supply should be fetched + * @param splProgram the SPL token program to query + * @return the SPL token supply for the mint + * @throws IOException if the token supply could not be fetched or parsed + * @throws InterruptedException if the calling thread is interrupted while fetching token supply + */ + SPLTokenSupply getSPLTokenSupply( + ΩSPLMintAddressΩ mintAddress, + SolanaSPLTokenProgram splProgram + ) throws IOException, InterruptedException; +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/solana/SolanaConstants.tjava b/src/main/tjava/com/r35157/libs/solana/SolanaConstants.tjava new file mode 100644 index 0000000..4bedf26 --- /dev/null +++ b/src/main/tjava/com/r35157/libs/solana/SolanaConstants.tjava @@ -0,0 +1,19 @@ +package com.r35157.libs.solana; + +/** + * Contains Solana-related constants used by the Solana integration. + * + *

This class is intended for shared constants that are part of the public + * Solana API surface or are useful across Solana-related modules.

+ */ +public class SolanaConstants { + /** + * The default Solana JSON-RPC endpoint used by the integration. + */ + public static final String RPC_URL = "https://api.mainnet.solana.com"; + + /** + * The SPL mint address for Syrup USDC on Solana. + */ + public static final String SPL_TOKEN_SYRUPUSDC = "AvZZF1YaZDziPY2RCK4oJrRVrbN3mTD9NL24hPeaZeUj"; +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/solana/SolanaProgramAddressSeed.tjava b/src/main/tjava/com/r35157/libs/solana/SolanaProgramAddressSeed.tjava new file mode 100644 index 0000000..6f15eb1 --- /dev/null +++ b/src/main/tjava/com/r35157/libs/solana/SolanaProgramAddressSeed.tjava @@ -0,0 +1,51 @@ +package com.r35157.libs.solana; + +/** + * Represents a seed used when deriving a Solana program derived address. + * + *

A seed has a kind and a textual value. The kind tells the Solana + * implementation how the value should be converted to bytes before it is used + * in program address derivation.

+ * + *

This keeps callers from having to perform encoding or decoding themselves. + * For example, a caller can provide a UTF-8 seed or a Solana address seed, and + * the implementation decides how each seed is converted to the byte format + * required by Solana.

+ * + * @param kind the kind of seed + * @param value the textual seed value + */ +public record SolanaProgramAddressSeed( + SolanaProgramAddressSeedKind kind, + String value +) { + /** + * Creates a UTF-8 seed. + * + * @param value the text value to encode as UTF-8 + * @return a program address seed representing the supplied UTF-8 text + */ + public static SolanaProgramAddressSeed utf8(String value) { + return new SolanaProgramAddressSeed( + SolanaProgramAddressSeedKind.UTF8, + value + ); + } + + /** + * Creates a Solana address seed. + * + *

The supplied address is represented textually here. The implementation + * deriving the program address is responsible for decoding the address into + * its raw Solana address bytes.

+ * + * @param address the Solana address to use as a seed + * @return a program address seed representing the supplied Solana address + */ + public static SolanaProgramAddressSeed solanaAddress(ΩSolanaAddressΩ address) { + return new SolanaProgramAddressSeed( + SolanaProgramAddressSeedKind.SOLANA_ADDRESS, + address + ); + } +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/solana/SolanaProgramAddressSeedKind.tjava b/src/main/tjava/com/r35157/libs/solana/SolanaProgramAddressSeedKind.tjava new file mode 100644 index 0000000..a544850 --- /dev/null +++ b/src/main/tjava/com/r35157/libs/solana/SolanaProgramAddressSeedKind.tjava @@ -0,0 +1,21 @@ +package com.r35157.libs.solana; + +/** + * Defines how a Solana program address seed should be converted to bytes. + * + *

Program derived addresses are calculated from byte seeds. This enum allows + * callers to describe the meaning of a seed without doing the byte conversion + * themselves.

+ */ +public enum SolanaProgramAddressSeedKind { + /** + * A seed that should be encoded as UTF-8 text. + */ + UTF8, + + /** + * A seed that represents a Solana address and should be decoded from its + * textual Solana address representation to raw address bytes. + */ + SOLANA_ADDRESS +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/solana/valuetypes/SolanaProgramDerivedAddress.tjava b/src/main/tjava/com/r35157/libs/solana/valuetypes/SolanaProgramDerivedAddress.tjava new file mode 100644 index 0000000..f56ef8c --- /dev/null +++ b/src/main/tjava/com/r35157/libs/solana/valuetypes/SolanaProgramDerivedAddress.tjava @@ -0,0 +1,17 @@ +package com.r35157.libs.solana.valuetypes; + +/** + * Represents a Solana program derived address and its bump value. + * + *

A program derived address is a Solana address generated deterministically + * from a program id and a set of seeds. The bump value is the extra seed value + * used to find a valid program derived address for those inputs.

+ * + * @param address the derived Solana address + * @param bump the bump value used when deriving the address + */ +public record SolanaProgramDerivedAddress( + ΩSolanaAddressΩ address, + int bump +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/solana/valuetypes/economic/SolanaSPLToken.tjava b/src/main/tjava/com/r35157/libs/solana/valuetypes/economic/SolanaSPLToken.tjava new file mode 100644 index 0000000..1461b30 --- /dev/null +++ b/src/main/tjava/com/r35157/libs/solana/valuetypes/economic/SolanaSPLToken.tjava @@ -0,0 +1,21 @@ +package com.r35157.libs.solana.valuetypes.economic; + +import com.r35157.libs.valuetypes.basic.CurrencyType; + +/** + * Represents an SPL token known to the Solana integration. + * + *

The currency type describes the economic identity of the token, while the + * mint address identifies the SPL token mint on Solana. The token address is the + * Solana address associated with the token in this model.

+ * + * @param currencyType the currency type represented by this SPL token + * @param mintAddress the SPL mint address of the token + * @param tokenAddress the Solana address associated with the token + */ +public record SolanaSPLToken ( + CurrencyType currencyType, + ΩSPLMintAddressΩ mintAddress, + ΩSolanaAddressΩ tokenAddress +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/solana/valuetypes/economic/SolanaSPLTokenProgram.tjava b/src/main/tjava/com/r35157/libs/solana/valuetypes/economic/SolanaSPLTokenProgram.tjava new file mode 100644 index 0000000..9171c50 --- /dev/null +++ b/src/main/tjava/com/r35157/libs/solana/valuetypes/economic/SolanaSPLTokenProgram.tjava @@ -0,0 +1,44 @@ +package com.r35157.libs.solana.valuetypes.economic; + +/** + * Represents a Solana SPL token program supported by this integration. + * + *

Solana has more than one token program. The original SPL Token Program is + * used by many existing tokens, while the Token-2022 Program supports newer token + * functionality and extensions.

+ * + *

The program address is used when querying token accounts owned by a wallet, + * for example when discovering SPL token holdings or NFT-like token holdings + * under a specific token program.

+ */ +public enum SolanaSPLTokenProgram { + /** + * The original Solana SPL Token Program. + */ + SPL_TOKEN_PROGRAM("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), + + /** + * The Solana Token-2022 Program. + */ + TOKEN_2022_PROGRAM("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"); + + /** + * Creates a Solana SPL token program entry. + * + * @param address the Solana program address for the token program + */ + SolanaSPLTokenProgram(ΩSPLProgramIdΩ address) { + this.address = address; + } + + /** + * Returns the Solana program address for this token program. + * + * @return the SPL token program address + */ + public ΩSPLProgramIdΩ getAddress() { + return address; + } + + private final ΩSPLProgramIdΩ address; +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/valuetypes/basic/Credentials.tjava b/src/main/tjava/com/r35157/libs/valuetypes/basic/Credentials.tjava new file mode 100644 index 0000000..cee99f0 --- /dev/null +++ b/src/main/tjava/com/r35157/libs/valuetypes/basic/Credentials.tjava @@ -0,0 +1,6 @@ +package com.r35157.libs.valuetypes.basic; + +public record Credentials( + ΩUserNameΩ userName, + ΩPasswordΩ password +) {} diff --git a/src/main/tjava/com/r35157/libs/valuetypes/basic/MoneyAmount.tjava b/src/main/tjava/com/r35157/libs/valuetypes/basic/MoneyAmount.tjava new file mode 100644 index 0000000..4685432 --- /dev/null +++ b/src/main/tjava/com/r35157/libs/valuetypes/basic/MoneyAmount.tjava @@ -0,0 +1,15 @@ +package com.r35157.libs.valuetypes.basic; + +import org.jetbrains.annotations.NotNull; + +import java.math.BigDecimal; + +public record MoneyAmount( + ΩAmountΩ amount, + CurrencyType currencyType +) { + @Override + public @NotNull String toString() { + return amount + " " + currencyType(); + } +} diff --git a/src/main/tjava/com/r35157/libs/valuetypes/basic/MoneyPrice.tjava b/src/main/tjava/com/r35157/libs/valuetypes/basic/MoneyPrice.tjava new file mode 100644 index 0000000..4480d0d --- /dev/null +++ b/src/main/tjava/com/r35157/libs/valuetypes/basic/MoneyPrice.tjava @@ -0,0 +1,8 @@ +package com.r35157.libs.valuetypes.basic; + +import java.math.BigDecimal; + +public record MoneyPrice( + ΩPriceΩ price, + CurrencyType currencyType +) { } diff --git a/src/main/tjava/com/r35157/libs/valuetypes/basic/NetworkEndPoint.tjava b/src/main/tjava/com/r35157/libs/valuetypes/basic/NetworkEndPoint.tjava new file mode 100644 index 0000000..28632c8 --- /dev/null +++ b/src/main/tjava/com/r35157/libs/valuetypes/basic/NetworkEndPoint.tjava @@ -0,0 +1,7 @@ +package com.r35157.libs.valuetypes.basic; + +public record NetworkEndPoint( + ΩHostnameΩ hostName, + ΩportNumberΩ portNumber +) { +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/libs/valuetypes/basic/Range.tjava b/src/main/tjava/com/r35157/libs/valuetypes/basic/Range.tjava new file mode 100644 index 0000000..e1b7ed9 --- /dev/null +++ b/src/main/tjava/com/r35157/libs/valuetypes/basic/Range.tjava @@ -0,0 +1,43 @@ +package com.r35157.libs.valuetypes.basic; + +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +public record Range>( + T from, + boolean fromIncluding, + T to, + boolean toIncluding +) { + public Range { + Objects.requireNonNull(from, "from"); + Objects.requireNonNull(to, "to"); + + if (from.compareTo(to) > 0) { + throw new IllegalArgumentException("Range from-value must not be greater than to-value."); + } + } + + public boolean contains(T value) { + Objects.requireNonNull(value, "value"); + + int fromComparison = value.compareTo(from); + int toComparison = value.compareTo(to); + + boolean afterFrom = fromIncluding + ? fromComparison >= 0 + : fromComparison > 0; + + boolean beforeTo = toIncluding + ? toComparison <= 0 + : toComparison < 0; + + return afterFrom && beforeTo; + } + + @Override + public @NotNull String toString() { + return (fromIncluding() ? "[" : "(") + from + "," + to + (toIncluding() ? "]" : ")"); + } +} \ No newline at end of file diff --git a/src/main/tjava/com/r35157/nenjim/hubd/ctx/ContextManager.tjava b/src/main/tjava/com/r35157/nenjim/hubd/ctx/ContextManager.tjava new file mode 100644 index 0000000..720afae --- /dev/null +++ b/src/main/tjava/com/r35157/nenjim/hubd/ctx/ContextManager.tjava @@ -0,0 +1,9 @@ +package com.r35157.nenjim.hubd.ctx; + +import java.util.Set; + +public interface ContextManager { + Context getDefault(); + Context get(ΩContextIdΩ contextName); + Set<ΩContextIdΩ> getList(); +} diff --git a/src/main/tjava/com/r35157/nenjim/hubd/journal/Journal.tjava b/src/main/tjava/com/r35157/nenjim/hubd/journal/Journal.tjava new file mode 100644 index 0000000..b95ac3a --- /dev/null +++ b/src/main/tjava/com/r35157/nenjim/hubd/journal/Journal.tjava @@ -0,0 +1,26 @@ +package com.r35157.nenjim.hubd.journal; + +import com.r35157.libs.valuetypes.basic.SemanticVersion; +import com.r35157.nenjim.hubd.module.Dependency; +import com.r35157.nenjim.hubd.module.Release; +import org.jetbrains.annotations.NotNull; + +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +public record Journal( + @NotNull ΩJournalIdΩ id, + @NotNull String name, + @NotNull Set releases +) { + public Release getRelease(SemanticVersion version) { + Set dependencies = new HashSet<>(); + + return new Release( + new SemanticVersion(0, 1, 0), + new Date(), + null, + dependencies); + } +} diff --git a/src/main/tjava/com/r35157/nenjim/hubd/module/Dependency.tjava b/src/main/tjava/com/r35157/nenjim/hubd/module/Dependency.tjava new file mode 100644 index 0000000..f4d023b --- /dev/null +++ b/src/main/tjava/com/r35157/nenjim/hubd/module/Dependency.tjava @@ -0,0 +1,8 @@ +package com.r35157.nenjim.hubd.module; + +import org.jetbrains.annotations.NotNull; + +public record Dependency( + @NotNull ΩJournalIdΩ dependencyId +) { +} diff --git a/src/main/tjava/com/r35157/nenjim/hubd/module/Release.tjava b/src/main/tjava/com/r35157/nenjim/hubd/module/Release.tjava new file mode 100644 index 0000000..e3ef498 --- /dev/null +++ b/src/main/tjava/com/r35157/nenjim/hubd/module/Release.tjava @@ -0,0 +1,14 @@ +package com.r35157.nenjim.hubd.module; + +import com.r35157.libs.valuetypes.basic.SemanticVersion; +import org.jetbrains.annotations.NotNull; + +import java.util.Date; +import java.util.Set; + +public record Release( + @NotNull SemanticVersion version, + @NotNull Date releaseDate, + @NotNull ΩChecksumΩ checksum, + @NotNull Set dependencies +) {}