62: Add AssetAZ Currency Identity Service and migrate CurrencyType
This commit is contained in:
+11
-3
@@ -4,6 +4,8 @@ import com.r35157.libs.basic.Pair;
|
||||
import com.fanitas.evelyn.raydium.RaydiumLiquidityPoolPositionConcentrated;
|
||||
import com.fanitas.evelyn.core.DesiredPositionCalculator;
|
||||
|
||||
import com.r35157.assetaz.services.cis.CurrencyIdentityService;
|
||||
import com.r35157.assetaz.valuetypes.CurrencyType;
|
||||
import com.r35157.libs.valuetypes.basic.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -14,15 +16,21 @@ import java.math.RoundingMode;
|
||||
import java.util.*;
|
||||
|
||||
import static com.fanitas.evelyn.math.BigDecimalUtils.*;
|
||||
import static com.r35157.assetaz.services.cis.CurrencyTypeIds.SYRUPUSDC_ID;
|
||||
import static java.math.BigDecimal.ONE;
|
||||
import static java.math.BigDecimal.ZERO;
|
||||
|
||||
public class DesiredPositionCalculatorImpl implements DesiredPositionCalculator {
|
||||
|
||||
public DesiredPositionCalculatorImpl(
|
||||
@NotNull CurrencyIdentityService currencyIdentityService,
|
||||
ΩCurveWidthΩ curveWidth,
|
||||
Map<ΩRaydiumLiquidityPoolPositionNftIdΩ, RaydiumLiquidityPoolPositionConcentrated> liquidityProviderPositions
|
||||
) {
|
||||
this.currencyIdentityService = Objects.requireNonNull(
|
||||
currencyIdentityService,
|
||||
"currencyIdentityService"
|
||||
);
|
||||
this.curveWidth = curveWidth;
|
||||
this.liquidityProviderPositions = liquidityProviderPositions;
|
||||
}
|
||||
@@ -223,7 +231,7 @@ public class DesiredPositionCalculatorImpl implements DesiredPositionCalculator
|
||||
intervalPriceFrom.price().compareTo(lookupPrice.price()) >= 0;
|
||||
|
||||
if (intervalIsCompletelyBeforeCurve || intervalIsCompletelyAfterLookupLimit) {
|
||||
return new ΩSyrupAmountΩ(ZERO, WellKnownCurrencyTypes.SYRUPUSDC.getCurrencyType());
|
||||
return new ΩSyrupAmountΩ(ZERO, currencyIdentityService.resolve(SYRUPUSDC_ID));
|
||||
}
|
||||
|
||||
ΩPriceΩ effectiveIntervalFrom = max(intervalPriceFrom.price(), curveStartPrice);
|
||||
@@ -254,7 +262,7 @@ public class DesiredPositionCalculatorImpl implements DesiredPositionCalculator
|
||||
totalSyrupToDistribution.amount()
|
||||
.multiply(intervalWeight, MC)
|
||||
.divide(totalWeightInActiveCurve, MC),
|
||||
WellKnownCurrencyTypes.SYRUPUSDC.getCurrencyType()
|
||||
currencyIdentityService.resolve(SYRUPUSDC_ID)
|
||||
);
|
||||
|
||||
return dps;
|
||||
@@ -367,7 +375,7 @@ public class DesiredPositionCalculatorImpl implements DesiredPositionCalculator
|
||||
}
|
||||
|
||||
private static final MathContext MC = new MathContext(20, RoundingMode.HALF_UP);
|
||||
|
||||
private final CurrencyIdentityService currencyIdentityService;
|
||||
private final ΩCurveWidthΩ curveWidth;
|
||||
private final Map<ΩRaydiumLiquidityPoolPositionNftIdΩ, RaydiumLiquidityPoolPositionConcentrated> liquidityProviderPositions;
|
||||
}
|
||||
|
||||
@@ -22,8 +22,6 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static com.r35157.libs.valuetypes.basic.WellKnownCurrencyTypes.SOLANA;
|
||||
import static com.r35157.libs.valuetypes.basic.WellKnownCurrencyTypes.SYRUPUSDC;
|
||||
import static com.r35157.libs.solana.valuetypes.economic.SolanaSPLTokenProgram.SPL_TOKEN_PROGRAM;
|
||||
import static com.r35157.libs.solana.valuetypes.economic.SolanaSPLTokenProgram.TOKEN_2022_PROGRAM;
|
||||
|
||||
@@ -96,7 +94,7 @@ public class EvelynImpl implements Evelyn {
|
||||
}
|
||||
|
||||
private MoneyAmount add(MoneyAmount a, MoneyAmount b) {
|
||||
if(a.currencyType() != b.currencyType()) {
|
||||
if(!a.currencyType().equals(b.currencyType())) {
|
||||
String errTxt = "Cannot add " + b.currencyType()
|
||||
+ " to " + a.currencyType();
|
||||
throw new IllegalArgumentException(errTxt);
|
||||
@@ -107,7 +105,7 @@ public class EvelynImpl implements Evelyn {
|
||||
}
|
||||
|
||||
private MoneyAmount subtract(MoneyAmount a, MoneyAmount b) {
|
||||
if(a.currencyType() != b.currencyType()) {
|
||||
if(!a.currencyType().equals(b.currencyType())) {
|
||||
String errTxt = "Cannot subtract " + b.currencyType()
|
||||
+ " from " + a.currencyType();
|
||||
throw new IllegalArgumentException(errTxt);
|
||||
|
||||
@@ -3,9 +3,11 @@ package com.fanitas.evelyn.core.impl.ref;
|
||||
import com.fanitas.evelyn.core.State;
|
||||
import com.fanitas.evelyn.raydium.RaydiumLiquidityPoolPositionAccounting;
|
||||
import com.fanitas.evelyn.raydium.RaydiumLiquidityPoolPositionConcentrated;
|
||||
import com.r35157.assetaz.services.cis.CurrencyIdentityService;
|
||||
import com.r35157.assetaz.services.cis.ExternalCurrencyReference;
|
||||
import com.r35157.assetaz.valuetypes.CurrencyType;
|
||||
import com.r35157.libs.configuration.ConfigurationFormatVersionValidator;
|
||||
import com.r35157.libs.raydium.*;
|
||||
import com.r35157.libs.valuetypes.basic.WellKnownCurrencyTypes;
|
||||
import com.r35157.libs.valuetypes.basic.*;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -17,12 +19,23 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.r35157.assetaz.services.cis.ExternalCurrencyReference.SOLANA_MINT_NAMESPACE;
|
||||
import static com.r35157.assetaz.services.cis.CurrencyTypeIds.SYRUPUSDC_ID;
|
||||
|
||||
public class StateImpl implements State {
|
||||
|
||||
public StateImpl(Raydium raydium) throws IOException, InterruptedException {
|
||||
public StateImpl(
|
||||
Raydium raydium,
|
||||
CurrencyIdentityService currencyIdentityService
|
||||
) throws IOException, InterruptedException {
|
||||
this.raydium = raydium;
|
||||
this.currencyIdentityService = Objects.requireNonNull(
|
||||
currencyIdentityService,
|
||||
"currencyIdentityService"
|
||||
);
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -114,8 +127,8 @@ public class StateImpl implements State {
|
||||
RaydiumConcentratedPoolState poolState =
|
||||
raydium.fetchConcentratedPoolState(positionState.poolId());
|
||||
|
||||
CurrencyType ctSol = WellKnownCurrencyTypes.SOLANA.getCurrencyType();
|
||||
CurrencyType ctSyrup = WellKnownCurrencyTypes.SYRUPUSDC.getCurrencyType();
|
||||
CurrencyType currencyTypeA = resolveSolanaMint(poolInfo.mintA());
|
||||
CurrencyType currencyTypeB = resolveSolanaMint(poolInfo.mintB());
|
||||
|
||||
int mintBDecimals = poolInfo.mintBDecimals();
|
||||
Range<AssetPrice> priceRange = raydium.calculateConcentratedPositionPriceRange(
|
||||
@@ -131,28 +144,36 @@ public class StateImpl implements State {
|
||||
poolState
|
||||
);
|
||||
|
||||
MoneyAmount solAmount = new MoneyAmount(tokenAmounts.amountA(), ctSol);
|
||||
MoneyAmount syrupAmount = new MoneyAmount(tokenAmounts.amountB(), ctSyrup);
|
||||
MoneyAmount amountA = new MoneyAmount(tokenAmounts.amountA(), currencyTypeA);
|
||||
MoneyAmount amountB = new MoneyAmount(tokenAmounts.amountB(), currencyTypeB);
|
||||
RaydiumLiquidityPoolPositionConcentrated position =
|
||||
new RaydiumLiquidityPoolPositionConcentrated(
|
||||
positionState.poolId(),
|
||||
positionNftId,
|
||||
priceRange,
|
||||
solAmount,
|
||||
syrupAmount,
|
||||
amountA,
|
||||
amountB,
|
||||
null // The accounting info will be added from 'conf/positions.conf' later.
|
||||
);
|
||||
|
||||
positions.put(positionNftId, position);
|
||||
System.out.println(" Added '" + position.nftId() + "': "
|
||||
+ "Range:" + position.priceRange()
|
||||
+ ", Liquidity:" + solAmount + "," + syrupAmount
|
||||
+ ", Liquidity:" + amountA + "," + amountB
|
||||
);
|
||||
}
|
||||
|
||||
liquidityPositions = Map.copyOf(positions);
|
||||
}
|
||||
|
||||
private CurrencyType resolveSolanaMint(ΩSPLMintAddressΩ mintAddress) {
|
||||
return currencyIdentityService.resolve(new ExternalCurrencyReference(
|
||||
SOLANA_MINT_NAMESPACE,
|
||||
mintAddress,
|
||||
null
|
||||
));
|
||||
}
|
||||
|
||||
private void updateStateFromPositionsFile() throws IOException {
|
||||
Map<ΩRaydiumLiquidityPoolPositionNftIdΩ, RaydiumLiquidityPoolPositionAccounting> accountingEntries =
|
||||
readPositionAccounting();
|
||||
@@ -276,7 +297,7 @@ public class StateImpl implements State {
|
||||
|
||||
private ΩSyrupAmountΩ readSyrupOwnedByEvelyn(Iterator<String> lineIterator) throws IOException {
|
||||
String syrupOwnedByEvelynStr = readLineFromFile(lineIterator);
|
||||
CurrencyType ct = WellKnownCurrencyTypes.SYRUPUSDC.getCurrencyType();
|
||||
CurrencyType ct = currencyIdentityService.resolve(SYRUPUSDC_ID);
|
||||
ΩSyrupAmountΩ ma = stringToMoneyAmount(syrupOwnedByEvelynStr, ct);
|
||||
return ma;
|
||||
}
|
||||
@@ -309,8 +330,8 @@ public class StateImpl implements State {
|
||||
private static final Path PATH_POSITIONS = Path.of("conf/positions.conf");
|
||||
private static final int SUPPORTED_EVELYN_FORMAT_VERSION = 1;
|
||||
private static final int SUPPORTED_POSITIONS_FORMAT_VERSION = 1;
|
||||
|
||||
private final Raydium raydium;
|
||||
private final CurrencyIdentityService currencyIdentityService;
|
||||
|
||||
private ΩmilliSecondsΩ iterationInterval;
|
||||
private ΩSyrupAmountΩ syrupOwnedByEvelyn;
|
||||
|
||||
+22
-4
@@ -2,6 +2,7 @@ package com.r35157.assetaz.core.service.ticker.impl.ref;
|
||||
|
||||
import com.r35157.assetaz.core.service.ticker.PriceSink;
|
||||
import com.r35157.assetaz.core.service.ticker.PriceSource;
|
||||
import com.r35157.assetaz.services.cis.CurrencyIdentityService;
|
||||
import com.r35157.libs.valuetypes.basic.TradingPair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
@@ -15,21 +16,34 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.r35157.libs.valuetypes.basic.WellKnownTradingPairs.EVE_USDC;
|
||||
import static com.r35157.assetaz.services.cis.CurrencyTypeIds.EVE_ID;
|
||||
import static com.r35157.assetaz.services.cis.CurrencyTypeIds.USDC_ID;
|
||||
|
||||
/**
|
||||
* Temporary reference source that produces a constant EVE/USDC price.
|
||||
*/
|
||||
public final class HardcodedPriceSource implements PriceSource {
|
||||
public HardcodedPriceSource() {
|
||||
this(Clock.systemUTC(), OBSERVATION_DELAY_MINUTES, TimeUnit.MINUTES);
|
||||
public HardcodedPriceSource(
|
||||
@NotNull CurrencyIdentityService currencyIdentityService
|
||||
) {
|
||||
this(
|
||||
currencyIdentityService,
|
||||
Clock.systemUTC(),
|
||||
OBSERVATION_DELAY_MINUTES,
|
||||
TimeUnit.MINUTES
|
||||
);
|
||||
}
|
||||
|
||||
HardcodedPriceSource(
|
||||
@NotNull CurrencyIdentityService currencyIdentityService,
|
||||
@NotNull Clock clock,
|
||||
long observationDelay,
|
||||
@NotNull TimeUnit observationDelayUnit
|
||||
) {
|
||||
this.currencyIdentityService = Objects.requireNonNull(
|
||||
currencyIdentityService,
|
||||
"currencyIdentityService"
|
||||
);
|
||||
this.clock = Objects.requireNonNull(clock, "clock");
|
||||
if (observationDelay <= 0) {
|
||||
throw new IllegalArgumentException("observationDelay must be positive");
|
||||
@@ -43,7 +57,10 @@ public final class HardcodedPriceSource implements PriceSource {
|
||||
|
||||
@Override
|
||||
public @NotNull TradingPair getTradingPair() {
|
||||
return EVE_USDC.getTradingPair();
|
||||
return new TradingPair(
|
||||
currencyIdentityService.resolve(EVE_ID),
|
||||
currencyIdentityService.resolve(USDC_ID)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -106,6 +123,7 @@ public final class HardcodedPriceSource implements PriceSource {
|
||||
private static final long OBSERVATION_DELAY_MINUTES = 1;
|
||||
|
||||
private PriceSink priceSink;
|
||||
private final CurrencyIdentityService currencyIdentityService;
|
||||
private final Clock clock;
|
||||
private final long observationDelay;
|
||||
private final TimeUnit observationDelayUnit;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.r35157.assetaz.services.cis;
|
||||
|
||||
import com.r35157.assetaz.valuetypes.CurrencyType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Resolves AssetAZ currency identities and their identifiers in external
|
||||
* systems.
|
||||
*
|
||||
* <p>An AssetAZ currency is identified by its stable {@link UUID}. An external
|
||||
* currency is identified by the combination of
|
||||
* {@link ExternalCurrencyReference#namespace() namespace} and
|
||||
* {@link ExternalCurrencyReference#externalId() external ID}; its symbol is
|
||||
* metadata and does not participate in resolution.</p>
|
||||
*
|
||||
* <p>This interface is read-only. Implementations may reuse returned
|
||||
* {@link CurrencyType} instances, but callers must compare currency values with
|
||||
* {@link Object#equals(Object)} rather than reference identity.</p>
|
||||
*/
|
||||
public interface CurrencyIdentityService {
|
||||
/**
|
||||
* Resolves an AssetAZ currency UUID to its current currency metadata.
|
||||
*
|
||||
* @param currencyTypeId the stable AssetAZ currency UUID
|
||||
* @return the current currency value for {@code currencyTypeId}
|
||||
* @throws NullPointerException if {@code currencyTypeId} is {@code null}
|
||||
* @throws IllegalArgumentException if {@code currencyTypeId} is unknown
|
||||
*/
|
||||
@NotNull CurrencyType resolve(@NotNull UUID currencyTypeId);
|
||||
|
||||
/**
|
||||
* Resolves a namespaced external currency identifier.
|
||||
*
|
||||
* <p>Resolution uses only the reference namespace and external ID. The
|
||||
* supplied symbol is descriptive metadata and is not a lookup key.</p>
|
||||
*
|
||||
* @param externalReference the namespaced external identifier to resolve
|
||||
* @return the AssetAZ currency mapped to the external identifier
|
||||
* @throws NullPointerException if {@code externalReference} is {@code null}
|
||||
* @throws IllegalArgumentException if the external identity is unknown
|
||||
*/
|
||||
@NotNull CurrencyType resolve(
|
||||
@NotNull ExternalCurrencyReference externalReference
|
||||
);
|
||||
|
||||
/**
|
||||
* Finds every external reference associated with an AssetAZ currency.
|
||||
*
|
||||
* @param currencyTypeId the stable AssetAZ currency UUID
|
||||
* @return an immutable, possibly empty set of external references
|
||||
* @throws NullPointerException if {@code currencyTypeId} is {@code null}
|
||||
* @throws IllegalArgumentException if {@code currencyTypeId} is unknown
|
||||
*/
|
||||
@NotNull Set<ExternalCurrencyReference> findExternalReferences(
|
||||
@NotNull UUID currencyTypeId
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.r35157.assetaz.services.cis;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Stable UUID identifiers for well-known AssetAZ currencies.
|
||||
*
|
||||
* <p>This class contains identifiers only. Current names, symbols, external
|
||||
* references, and {@code CurrencyType} instances are owned by the active
|
||||
* {@link CurrencyIdentityService} implementation.</p>
|
||||
*/
|
||||
public final class CurrencyTypeIds {
|
||||
private CurrencyTypeIds() {
|
||||
}
|
||||
|
||||
/** Stable UUID for Evelyn IOU. */
|
||||
public static final UUID EVE_ID =
|
||||
UUID.fromString("019c3f9f-41d1-7a73-b1df-d4c11c7ff301");
|
||||
|
||||
/** Stable UUID for USD Coin. */
|
||||
public static final UUID USDC_ID =
|
||||
UUID.fromString("019c3f9f-41d1-7a73-b1df-d4c11c7ff302");
|
||||
|
||||
/** Stable UUID for Solana. */
|
||||
public static final UUID SOLANA_ID =
|
||||
UUID.fromString("019e0116-fce5-792f-a647-fa6da4dffec5");
|
||||
|
||||
/** Stable UUID for SyrupUSDC. */
|
||||
public static final UUID SYRUPUSDC_ID =
|
||||
UUID.fromString("019e1d51-0600-7956-8231-f3b7058a91c2");
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.r35157.assetaz.services.cis;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Identifies a currency in an external identification system.
|
||||
*
|
||||
* <p>The semantic identity is the combination of {@link #namespace()} and
|
||||
* {@link #externalId()}. {@link #symbol()} is optional display metadata and
|
||||
* must not be used as a lookup key. Equality and hash codes therefore depend
|
||||
* only on the namespace and external ID.</p>
|
||||
*
|
||||
* @param namespace the external identification system, such as
|
||||
* {@value #SOLANA_MINT_NAMESPACE}; never {@code null}
|
||||
* @param externalId the identifier within the namespace, such as a token mint
|
||||
* address; never {@code null}
|
||||
* @param symbol optional symbol metadata supplied by the external system
|
||||
*/
|
||||
public record ExternalCurrencyReference(
|
||||
@NotNull String namespace,
|
||||
@NotNull String externalId,
|
||||
@Nullable String symbol
|
||||
) {
|
||||
public ExternalCurrencyReference {
|
||||
Objects.requireNonNull(namespace, "namespace");
|
||||
Objects.requireNonNull(externalId, "externalId");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
return this == object
|
||||
|| object instanceof ExternalCurrencyReference other
|
||||
&& namespace.equals(other.namespace)
|
||||
&& externalId.equals(other.externalId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(namespace, externalId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Namespace for tokens identified by their Solana mint address.
|
||||
*/
|
||||
public static final String SOLANA_MINT_NAMESPACE = "solana-mint";
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
package com.r35157.assetaz.services.cis.impl.hc;
|
||||
|
||||
import com.r35157.assetaz.services.cis.CurrencyIdentityService;
|
||||
import com.r35157.assetaz.services.cis.ExternalCurrencyReference;
|
||||
import com.r35157.assetaz.valuetypes.CurrencyType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.r35157.assetaz.services.cis.ExternalCurrencyReference.SOLANA_MINT_NAMESPACE;
|
||||
import static com.r35157.assetaz.services.cis.CurrencyTypeIds.EVE_ID;
|
||||
import static com.r35157.assetaz.services.cis.CurrencyTypeIds.SOLANA_ID;
|
||||
import static com.r35157.assetaz.services.cis.CurrencyTypeIds.SYRUPUSDC_ID;
|
||||
import static com.r35157.assetaz.services.cis.CurrencyTypeIds.USDC_ID;
|
||||
|
||||
public final class HardcodedCurrencyIdentityService implements CurrencyIdentityService {
|
||||
public HardcodedCurrencyIdentityService() {
|
||||
this(List.of(
|
||||
entry(EVE_ID, "Evelyn IOU", "EVE",
|
||||
solanaMint("meveYG2iXYSkgSUn1T1uxcthH1EGMZdRHGgCntXZA3Y", "EVE")),
|
||||
entry(USDC_ID, "USD Coin", "USDC",
|
||||
solanaMint("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "USDC")),
|
||||
entry(SOLANA_ID, "Solana", "SOL",
|
||||
solanaMint("So11111111111111111111111111111111111111112", "SOL")),
|
||||
entry(SYRUPUSDC_ID, "SyrupUSDC", "SyrupUSDC",
|
||||
solanaMint("AvZZF1YaZDziPY2RCK4oJrRVrbN3mTD9NL24hPeaZeUj", "SyrupUSDC"))
|
||||
));
|
||||
}
|
||||
|
||||
private HardcodedCurrencyIdentityService(List<CatalogueEntry> entries) {
|
||||
Map<UUID, CurrencyType> currencies = new HashMap<>();
|
||||
Map<ExternalIdentity, UUID> externalIdentities = new HashMap<>();
|
||||
Map<ExternalIdentity, ExternalCurrencyReference> referencesByIdentity = new HashMap<>();
|
||||
Map<UUID, Set<ExternalCurrencyReference>> references = new HashMap<>();
|
||||
|
||||
for (CatalogueEntry entry : entries) {
|
||||
CurrencyType previousCurrency = currencies.putIfAbsent(
|
||||
entry.currencyType().id(),
|
||||
entry.currencyType()
|
||||
);
|
||||
if (previousCurrency != null && !sameMetadata(previousCurrency, entry.currencyType())) {
|
||||
throw new IllegalStateException(
|
||||
"Conflicting currency definitions for UUID " + entry.currencyType().id()
|
||||
);
|
||||
}
|
||||
|
||||
Set<ExternalCurrencyReference> currencyReferences = references.computeIfAbsent(
|
||||
entry.currencyType().id(),
|
||||
ignored -> new HashSet<>()
|
||||
);
|
||||
for (ExternalCurrencyReference reference : entry.externalReferences()) {
|
||||
ExternalIdentity identity = ExternalIdentity.from(reference);
|
||||
UUID previousId = externalIdentities.putIfAbsent(identity, entry.currencyType().id());
|
||||
if (previousId != null && !previousId.equals(entry.currencyType().id())) {
|
||||
throw new IllegalStateException(
|
||||
"External currency identity " + identity + " maps to both "
|
||||
+ previousId + " and " + entry.currencyType().id()
|
||||
);
|
||||
}
|
||||
ExternalCurrencyReference previousReference = referencesByIdentity.putIfAbsent(
|
||||
identity,
|
||||
reference
|
||||
);
|
||||
if (previousReference != null
|
||||
&& !Objects.equals(previousReference.symbol(), reference.symbol())) {
|
||||
throw new IllegalStateException(
|
||||
"Conflicting symbol metadata for external currency identity " + identity
|
||||
);
|
||||
}
|
||||
currencyReferences.add(reference);
|
||||
}
|
||||
}
|
||||
|
||||
Map<UUID, Set<ExternalCurrencyReference>> immutableReferences = new HashMap<>();
|
||||
currencies.keySet().forEach(id -> immutableReferences.put(
|
||||
id,
|
||||
Set.copyOf(references.getOrDefault(id, Set.of()))
|
||||
));
|
||||
|
||||
this.currencies = Map.copyOf(currencies);
|
||||
this.externalIdentities = Map.copyOf(externalIdentities);
|
||||
this.references = Map.copyOf(immutableReferences);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CurrencyType resolve(@NotNull UUID currencyTypeId) {
|
||||
Objects.requireNonNull(currencyTypeId, "currencyTypeId");
|
||||
CurrencyType currencyType = currencies.get(currencyTypeId);
|
||||
if (currencyType == null) {
|
||||
throw new IllegalArgumentException("Unknown AssetAZ currency UUID: " + currencyTypeId);
|
||||
}
|
||||
return currencyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CurrencyType resolve(
|
||||
@NotNull ExternalCurrencyReference externalReference
|
||||
) {
|
||||
Objects.requireNonNull(externalReference, "externalReference");
|
||||
ExternalIdentity identity = ExternalIdentity.from(externalReference);
|
||||
UUID currencyTypeId = externalIdentities.get(identity);
|
||||
if (currencyTypeId == null) {
|
||||
throw new IllegalArgumentException("Unknown external currency identity: " + identity);
|
||||
}
|
||||
return resolve(currencyTypeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<ExternalCurrencyReference> findExternalReferences(
|
||||
@NotNull UUID currencyTypeId
|
||||
) {
|
||||
resolve(currencyTypeId);
|
||||
return references.get(currencyTypeId);
|
||||
}
|
||||
|
||||
private static boolean sameMetadata(CurrencyType left, CurrencyType right) {
|
||||
return left.name().equals(right.name()) && left.symbol().equals(right.symbol());
|
||||
}
|
||||
|
||||
private static CatalogueEntry entry(
|
||||
UUID id,
|
||||
String name,
|
||||
String symbol,
|
||||
ExternalCurrencyReference... externalReferences
|
||||
) {
|
||||
return new CatalogueEntry(
|
||||
new CurrencyType(id, name, symbol),
|
||||
List.of(externalReferences)
|
||||
);
|
||||
}
|
||||
|
||||
private static ExternalCurrencyReference solanaMint(String mintAddress, String symbol) {
|
||||
return new ExternalCurrencyReference(SOLANA_MINT_NAMESPACE, mintAddress, symbol);
|
||||
}
|
||||
|
||||
private record CatalogueEntry(
|
||||
CurrencyType currencyType,
|
||||
List<ExternalCurrencyReference> externalReferences
|
||||
) {
|
||||
}
|
||||
|
||||
private record ExternalIdentity(String namespace, String externalId) {
|
||||
private static ExternalIdentity from(ExternalCurrencyReference reference) {
|
||||
return new ExternalIdentity(reference.namespace(), reference.externalId());
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<UUID, CurrencyType> currencies;
|
||||
private final Map<ExternalIdentity, UUID> externalIdentities;
|
||||
private final Map<UUID, Set<ExternalCurrencyReference>> references;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.r35157.assetaz.valuetypes;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Immutable AssetAZ currency identity and its current display metadata.
|
||||
*
|
||||
* <p>The {@link #id()} UUID is the complete and stable identity. The
|
||||
* {@link #name()} and {@link #symbol()} values may change without creating a
|
||||
* different currency. Equality and hash codes therefore depend only on the
|
||||
* UUID, and callers must use {@link #equals(Object)} rather than reference
|
||||
* comparison.</p>
|
||||
*
|
||||
* @param id the stable AssetAZ currency UUID; never {@code null}
|
||||
* @param name the current human-readable currency name; never {@code null}
|
||||
* @param symbol the current display symbol; never {@code null}
|
||||
*/
|
||||
public record CurrencyType(
|
||||
@NotNull UUID id,
|
||||
@NotNull String name,
|
||||
@NotNull String symbol
|
||||
) {
|
||||
public CurrencyType {
|
||||
Objects.requireNonNull(id, "id");
|
||||
Objects.requireNonNull(name, "name");
|
||||
Objects.requireNonNull(symbol, "symbol");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
return this == object
|
||||
|| object instanceof CurrencyType other
|
||||
&& id.equals(other.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String toString() {
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.r35157.jupiterperpsalarm.impl.ref;
|
||||
|
||||
import com.r35157.assetaz.services.cis.CurrencyIdentityService;
|
||||
import com.r35157.cryptowallet.solana.SolanaWallet;
|
||||
import com.r35157.cryptowallet.solana.impl.ref.SolanaWalletImpl;
|
||||
import com.r35157.libs.jupiter.perps.JupiterPerpsService;
|
||||
@@ -21,7 +22,10 @@ import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public final class JupiterPerpsAlarmImpl {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
public static void start(
|
||||
String[] args,
|
||||
CurrencyIdentityService currencyIdentityService
|
||||
) throws Exception {
|
||||
Config config;
|
||||
System.out.println("Starting Jupiter Perps Alarms...");
|
||||
try {
|
||||
@@ -57,7 +61,9 @@ public final class JupiterPerpsAlarmImpl {
|
||||
|
||||
System.out.print("Initializing dependencies... ");
|
||||
ObjectCache objectCache = new ObjectCacheImpl();
|
||||
SolanaBlockChain realSolanaBlockChain = new SolanaBlockChainImpl();
|
||||
SolanaBlockChain realSolanaBlockChain = new SolanaBlockChainImpl(
|
||||
currencyIdentityService
|
||||
);
|
||||
SolanaBlockChain solanaBlockChain = new CachedSolanaBlockChain(
|
||||
realSolanaBlockChain,
|
||||
objectCache
|
||||
@@ -83,7 +89,8 @@ public final class JupiterPerpsAlarmImpl {
|
||||
JupiterPerpsService positionIncreaseJupiter =
|
||||
new AnchorIdlJupiterPerpsServiceImpl(
|
||||
solanaBlockChain,
|
||||
positionIncreaseWallet
|
||||
positionIncreaseWallet,
|
||||
currencyIdentityService
|
||||
);
|
||||
|
||||
JupiterPerpsPositionDecreaseAlarmActionConfiguration
|
||||
@@ -106,7 +113,8 @@ public final class JupiterPerpsAlarmImpl {
|
||||
JupiterPerpsService positionDecreaseJupiter =
|
||||
new AnchorIdlJupiterPerpsServiceImpl(
|
||||
solanaBlockChain,
|
||||
positionDecreaseWallet
|
||||
positionDecreaseWallet,
|
||||
currencyIdentityService
|
||||
);
|
||||
|
||||
JupiterPerpsEntryPriceVariableRefresher entryPriceVariableRefresher =
|
||||
|
||||
+10
-4
@@ -1,6 +1,7 @@
|
||||
package com.r35157.libs.jupiter.perps.impl.anchoridl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.r35157.assetaz.services.cis.CurrencyIdentityService;
|
||||
import com.r35157.cryptowallet.solana.SolanaWallet;
|
||||
import com.r35157.libs.jupiter.perps.JupiterPerpsPosition;
|
||||
import com.r35157.libs.jupiter.perps.JupiterPerpsPositionDirection;
|
||||
@@ -18,7 +19,6 @@ import com.r35157.libs.solana.SolanaProgramAccountMemcmpFilter;
|
||||
import com.r35157.libs.solana.SolanaSignedTransaction;
|
||||
import com.r35157.libs.solana.SolanaUnsignedTransaction;
|
||||
import com.r35157.libs.valuetypes.basic.MoneyAmount;
|
||||
import com.r35157.libs.valuetypes.basic.WellKnownCurrencyTypes;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -34,19 +34,25 @@ import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.r35157.assetaz.services.cis.CurrencyTypeIds.SOLANA_ID;
|
||||
import static java.math.BigDecimal.ZERO;
|
||||
|
||||
public class AnchorIdlJupiterPerpsServiceImpl implements JupiterPerpsService {
|
||||
|
||||
public AnchorIdlJupiterPerpsServiceImpl(
|
||||
SolanaBlockChain solanaBlockChain,
|
||||
SolanaWallet wallet
|
||||
SolanaWallet wallet,
|
||||
CurrencyIdentityService currencyIdentityService
|
||||
) {
|
||||
this.solanaBlockChain = Objects.requireNonNull(
|
||||
solanaBlockChain,
|
||||
"solanaBlockChain"
|
||||
);
|
||||
this.wallet = Objects.requireNonNull(wallet, "wallet");
|
||||
this.currencyIdentityService = Objects.requireNonNull(
|
||||
currencyIdentityService,
|
||||
"currencyIdentityService"
|
||||
);
|
||||
this.positionDecoder = new AnchorIdlJupiterPerpsPositionDecoder();
|
||||
this.custodyDecoder = new AnchorIdlJupiterPerpsCustodyDecoder();
|
||||
}
|
||||
@@ -416,7 +422,7 @@ public class AnchorIdlJupiterPerpsServiceImpl implements JupiterPerpsService {
|
||||
ΩUSDCAmountΩ totalFees = ZERO; // TODO - Dummy
|
||||
ΩSolanaAmountΩ accountRent = new MoneyAmount( // TODO - Dummy
|
||||
ZERO,
|
||||
WellKnownCurrencyTypes.SOLANA.getCurrencyType()
|
||||
currencyIdentityService.resolve(SOLANA_ID)
|
||||
);
|
||||
|
||||
JupiterPerpsPosition pos = new JupiterPerpsPosition(
|
||||
@@ -808,9 +814,9 @@ public class AnchorIdlJupiterPerpsServiceImpl implements JupiterPerpsService {
|
||||
private static final int POSITION_OWNER_OFFSET = 8;
|
||||
private static final int MAX_LEVERAGE = 500;
|
||||
private static final int USDC_DECIMALS = 6;
|
||||
|
||||
private final SolanaBlockChain solanaBlockChain;
|
||||
private final SolanaWallet wallet;
|
||||
private final CurrencyIdentityService currencyIdentityService;
|
||||
private final AnchorIdlJupiterPerpsPositionDecoder positionDecoder;
|
||||
private final AnchorIdlJupiterPerpsCustodyDecoder custodyDecoder;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.r35157.libs.raydium.impl.ref;
|
||||
|
||||
import com.r35157.libs.valuetypes.basic.WellKnownTradingPairs;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.r35157.assetaz.services.cis.CurrencyIdentityService;
|
||||
import com.r35157.assetaz.services.cis.ExternalCurrencyReference;
|
||||
import com.r35157.assetaz.valuetypes.CurrencyType;
|
||||
import com.r35157.libs.raydium.Raydium;
|
||||
import com.r35157.libs.raydium.RaydiumConcentratedPositionState;
|
||||
import com.r35157.libs.raydium.RaydiumConcentratedPoolInfo;
|
||||
@@ -15,6 +17,7 @@ import com.r35157.libs.solana.SolanaBlockChain;
|
||||
import com.r35157.libs.solana.SolanaProgramAddressSeed;
|
||||
import com.r35157.libs.solana.valuetypes.SolanaProgramDerivedAddress;
|
||||
import com.r35157.libs.valuetypes.basic.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
@@ -28,15 +31,24 @@ import java.net.http.HttpResponse;
|
||||
import java.util.Base64;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.r35157.assetaz.services.cis.ExternalCurrencyReference.SOLANA_MINT_NAMESPACE;
|
||||
import static com.r35157.libs.solana.valuetypes.economic.SolanaSPLTokenProgram.SPL_TOKEN_PROGRAM;
|
||||
import static com.r35157.libs.solana.valuetypes.economic.SolanaSPLTokenProgram.TOKEN_2022_PROGRAM;
|
||||
|
||||
public class RaydiumImpl implements Raydium {
|
||||
|
||||
public RaydiumImpl(SolanaBlockChain solanaBlockChain) {
|
||||
this.solanaBlockChain = solanaBlockChain;
|
||||
public RaydiumImpl(
|
||||
SolanaBlockChain solanaBlockChain,
|
||||
CurrencyIdentityService currencyIdentityService
|
||||
) {
|
||||
this.solanaBlockChain = Objects.requireNonNull(solanaBlockChain, "solanaBlockChain");
|
||||
this.currencyIdentityService = Objects.requireNonNull(
|
||||
currencyIdentityService,
|
||||
"currencyIdentityService"
|
||||
);
|
||||
this.httpClient = HttpClient.newHttpClient();
|
||||
this.objectMapper = new ObjectMapper();
|
||||
}
|
||||
@@ -54,8 +66,11 @@ public class RaydiumImpl implements Raydium {
|
||||
throw new IOException("Could NOT find field 'price' in JSON!");
|
||||
}
|
||||
|
||||
// TODO: Find out how not to hardcode the trading pair here
|
||||
TradingPair tp = WellKnownTradingPairs.SOL_SYRUPUSDC.getTradingPair();
|
||||
ΩSPLMintAddressΩ mintA = extractMintAddress(firstPool, "mintA");
|
||||
String symbolA = extractMintSymbol(firstPool, "mintA");
|
||||
ΩSPLMintAddressΩ mintB = extractMintAddress(firstPool, "mintB");
|
||||
String symbolB = extractMintSymbol(firstPool, "mintB");
|
||||
TradingPair tp = resolveTradingPair(mintA, symbolA, mintB, symbolB);
|
||||
ΩPriceΩ p = new ΩPriceΩ(priceNode.toString());
|
||||
AssetPrice ap = new AssetPrice(p, tp);
|
||||
|
||||
@@ -263,7 +278,12 @@ public class RaydiumImpl implements Raydium {
|
||||
RaydiumConcentratedPoolInfo poolInfo,
|
||||
int decimalPlaces
|
||||
) {
|
||||
TradingPair tp = WellKnownTradingPairs.SOL_SYRUPUSDC.getTradingPair();
|
||||
TradingPair tp = resolveTradingPair(
|
||||
poolInfo.mintA(),
|
||||
null,
|
||||
poolInfo.mintB(),
|
||||
null
|
||||
);
|
||||
|
||||
ΩPriceΩ priceFrom = calculatePriceFromTick(
|
||||
positionState.tickLowerIndex(),
|
||||
@@ -509,6 +529,38 @@ public class RaydiumImpl implements Raydium {
|
||||
return addressNode.asText();
|
||||
}
|
||||
|
||||
private @Nullable String extractMintSymbol(
|
||||
JsonNode poolNode,
|
||||
String mintFieldName
|
||||
) throws IOException {
|
||||
JsonNode symbolNode = poolNode.path(mintFieldName).path("symbol");
|
||||
|
||||
if (symbolNode.isMissingNode() || symbolNode.isNull()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!symbolNode.isTextual()) {
|
||||
throw new IOException("Could NOT find textual field '" + mintFieldName + ".symbol' in pool JSON!");
|
||||
}
|
||||
|
||||
return symbolNode.asText();
|
||||
}
|
||||
|
||||
private TradingPair resolveTradingPair(
|
||||
ΩSPLMintAddressΩ mintA,
|
||||
@Nullable String symbolA,
|
||||
ΩSPLMintAddressΩ mintB,
|
||||
@Nullable String symbolB
|
||||
) {
|
||||
CurrencyType base = currencyIdentityService.resolve(
|
||||
new ExternalCurrencyReference(SOLANA_MINT_NAMESPACE, mintA, symbolA)
|
||||
);
|
||||
CurrencyType quote = currencyIdentityService.resolve(
|
||||
new ExternalCurrencyReference(SOLANA_MINT_NAMESPACE, mintB, symbolB)
|
||||
);
|
||||
return new TradingPair(base, quote);
|
||||
}
|
||||
|
||||
private ΩAmountΩ extractAmount(JsonNode poolNode, String amountFieldName) throws IOException {
|
||||
JsonNode amountNode = poolNode.path(amountFieldName);
|
||||
|
||||
@@ -957,6 +1009,7 @@ public class RaydiumImpl implements Raydium {
|
||||
private static final Base64.Decoder base64 = Base64.getDecoder();
|
||||
|
||||
private final SolanaBlockChain solanaBlockChain;
|
||||
private final CurrencyIdentityService currencyIdentityService;
|
||||
private final HttpClient httpClient;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.r35157.assetaz.services.cis.CurrencyIdentityService;
|
||||
import com.r35157.assetaz.valuetypes.CurrencyType;
|
||||
import 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.CurrencyType;
|
||||
import com.r35157.libs.valuetypes.basic.MoneyAmount;
|
||||
import com.r35157.libs.valuetypes.basic.WellKnownCurrencyTypes;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -29,11 +29,16 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static com.r35157.assetaz.services.cis.CurrencyTypeIds.SOLANA_ID;
|
||||
import static com.r35157.libs.solana.SolanaConstants.RPC_URL;
|
||||
|
||||
public class SolanaBlockChainImpl implements SolanaBlockChain {
|
||||
|
||||
public SolanaBlockChainImpl() {
|
||||
public SolanaBlockChainImpl(CurrencyIdentityService currencyIdentityService) {
|
||||
this.currencyIdentityService = Objects.requireNonNull(
|
||||
currencyIdentityService,
|
||||
"currencyIdentityService"
|
||||
);
|
||||
this.httpClient = HttpClient.newHttpClient();
|
||||
this.objectMapper = new ObjectMapper();
|
||||
}
|
||||
@@ -42,7 +47,7 @@ public class SolanaBlockChainImpl implements SolanaBlockChain {
|
||||
public ΩSolanaAmountΩ getBalanceInSolana(ΩSolanaAddressΩ address) throws IOException, InterruptedException {
|
||||
ΩlamportsΩ lamport = getBalanceInLamport(address);
|
||||
ΩAmountΩ bd = ΩAmountΩ.valueOf(lamport).divide(LAMPORTS_PER_SOL);
|
||||
CurrencyType type = WellKnownCurrencyTypes.SOLANA.getCurrencyType();
|
||||
CurrencyType type = currencyIdentityService.resolve(SOLANA_ID);
|
||||
ΩSolanaAmountΩ sa = new ΩSolanaAmountΩ(bd, type);
|
||||
|
||||
return sa;
|
||||
@@ -938,9 +943,7 @@ public class SolanaBlockChainImpl implements SolanaBlockChain {
|
||||
);
|
||||
}
|
||||
|
||||
if (!WellKnownCurrencyTypes.SOLANA
|
||||
.getCurrencyType()
|
||||
.equals(amount.currencyType())) {
|
||||
if (!currencyIdentityService.resolve(SOLANA_ID).equals(amount.currencyType())) {
|
||||
throw new IllegalArgumentException(
|
||||
"SOL transfer amount must use the SOL currency type"
|
||||
);
|
||||
@@ -1405,7 +1408,7 @@ public class SolanaBlockChainImpl implements SolanaBlockChain {
|
||||
BigInteger.valueOf(-121665)
|
||||
.multiply(BigInteger.valueOf(121666).modInverse(ED25519_P))
|
||||
.mod(ED25519_P);
|
||||
|
||||
private final CurrencyIdentityService currencyIdentityService;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final HttpClient httpClient;
|
||||
private ΩmilliSecondsΩ previousRemoteCallTime = 0L;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.r35157.libs.solana.valuetypes.economic;
|
||||
|
||||
import com.r35157.libs.valuetypes.basic.CurrencyType;
|
||||
import com.r35157.assetaz.valuetypes.CurrencyType;
|
||||
|
||||
/**
|
||||
* Represents an SPL token known to the Solana integration.
|
||||
@@ -18,4 +18,4 @@ public record SolanaSPLToken (
|
||||
ΩSPLMintAddressΩ mintAddress,
|
||||
ΩSolanaAddressΩ tokenAddress
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.r35157.libs.valuetypes.basic;
|
||||
|
||||
import com.r35157.assetaz.valuetypes.CurrencyType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.r35157.libs.valuetypes.basic;
|
||||
|
||||
import com.r35157.assetaz.valuetypes.CurrencyType;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public record MoneyPrice(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.r35157.libs.valuetypes.basic;
|
||||
|
||||
import com.r35157.assetaz.valuetypes.CurrencyType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public record TradingPair(
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
package com.r35157.libs.valuetypes.basic;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Defines well-known currencies used across the system.
|
||||
*
|
||||
* <p>Each enum value provides a stable {@link CurrencyType} identity for a
|
||||
* currency that may be referenced by multiple services and integrations.</p>
|
||||
*/
|
||||
public enum WellKnownCurrencyTypes {
|
||||
/**
|
||||
* Evelyn IOU Token
|
||||
*/
|
||||
EVE(new CurrencyType(
|
||||
UUID.fromString("019c3f9f-41d1-7a73-b1df-d4c11c7ff301"),
|
||||
"EVE",
|
||||
"EVE")
|
||||
),
|
||||
|
||||
/**
|
||||
* USD Coin
|
||||
*/
|
||||
USDC(new CurrencyType(
|
||||
UUID.fromString("019c3f9f-41d1-7a73-b1df-d4c11c7ff302"),
|
||||
"USD Coin",
|
||||
"USDC")
|
||||
),
|
||||
|
||||
/**
|
||||
* Native Solana currency
|
||||
*/
|
||||
SOLANA(new CurrencyType(
|
||||
UUID.fromString("019e0116-fce5-792f-a647-fa6da4dffec5"),
|
||||
"Solana",
|
||||
"SOL")
|
||||
),
|
||||
|
||||
/**
|
||||
* SyrupUSDC currency
|
||||
*/
|
||||
SYRUPUSDC(new CurrencyType(
|
||||
UUID.fromString("019e1d51-0600-7956-8231-f3b7058a91c2"),
|
||||
"SyrupUSDC",
|
||||
"SyrupUSDC")
|
||||
);
|
||||
|
||||
/**
|
||||
* Creates a well-known currency entry.
|
||||
*
|
||||
* @param currencyType the stable currency identity represented by the entry
|
||||
*/
|
||||
WellKnownCurrencyTypes(CurrencyType currencyType) {
|
||||
this.currencyType = currencyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the represented currency type.
|
||||
*
|
||||
* @return the represented currency type
|
||||
*/
|
||||
public CurrencyType getCurrencyType() {
|
||||
return currencyType;
|
||||
}
|
||||
|
||||
private final CurrencyType currencyType;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.r35157.libs.valuetypes.basic;
|
||||
|
||||
import static com.r35157.libs.valuetypes.basic.WellKnownCurrencyTypes.EVE;
|
||||
import static com.r35157.libs.valuetypes.basic.WellKnownCurrencyTypes.SOLANA;
|
||||
import static com.r35157.libs.valuetypes.basic.WellKnownCurrencyTypes.SYRUPUSDC;
|
||||
import static com.r35157.libs.valuetypes.basic.WellKnownCurrencyTypes.USDC;
|
||||
|
||||
public enum WellKnownTradingPairs {
|
||||
SOL_SYRUPUSDC(new TradingPair(SOLANA.getCurrencyType(), SYRUPUSDC.getCurrencyType())),
|
||||
EVE_USDC(new TradingPair(EVE.getCurrencyType(), USDC.getCurrencyType()));
|
||||
|
||||
WellKnownTradingPairs(TradingPair tradingPair) {
|
||||
this.tradingPair = tradingPair;
|
||||
}
|
||||
|
||||
public TradingPair getTradingPair() {
|
||||
return tradingPair;
|
||||
}
|
||||
|
||||
private final TradingPair tradingPair;
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import com.fanitas.evelyn.core.Evelyn;
|
||||
import com.fanitas.evelyn.core.impl.ref.EvelynImpl;
|
||||
import com.r35157.assetaz.core.service.ticker.impl.ref.HardcodedPriceSource;
|
||||
import com.r35157.assetaz.core.service.ticker.impl.ref.TickerServiceImpl;
|
||||
import com.r35157.assetaz.services.cis.CurrencyIdentityService;
|
||||
import com.r35157.assetaz.services.cis.impl.hc.HardcodedCurrencyIdentityService;
|
||||
import com.r35157.evelyn.emc.EvelynMissionControl;
|
||||
import com.r35157.evelyn.emc.impl.ref.EvelynMissionControlImpl;
|
||||
import com.r35157.jupiterperpsalarm.impl.ref.JupiterPerpsAlarmImpl;
|
||||
@@ -59,9 +61,11 @@ public class NenjimHubImpl implements NenjimHub {
|
||||
System.out.println("Nenjim is now shutdown (stopped all processes)!");
|
||||
}
|
||||
|
||||
private void startAutoRunProcesses() throws Exception {
|
||||
startAssetAZTickerService();
|
||||
startJupiterPerpsAlarm(); // TODO: Hardcoded/hacky way to auto-start but good enough for now.
|
||||
private void startAutoRunProcesses() {
|
||||
CurrencyIdentityService currencyIdentityService =
|
||||
new HardcodedCurrencyIdentityService();
|
||||
startAssetAZTickerService(currencyIdentityService);
|
||||
startJupiterPerpsAlarm(currencyIdentityService); // TODO: Hardcoded/hacky way to auto-start but good enough for now.
|
||||
Evelyn evelynProd = new EvelynImpl();
|
||||
Evelyn evelynTest = new EvelynImpl();
|
||||
startEvelynMissionControl(evelynProd, evelynTest);
|
||||
@@ -89,9 +93,13 @@ public class NenjimHubImpl implements NenjimHub {
|
||||
*/
|
||||
}
|
||||
|
||||
private void startAssetAZTickerService() {
|
||||
private void startAssetAZTickerService(
|
||||
CurrencyIdentityService currencyIdentityService
|
||||
) {
|
||||
// Nenjim creates this unstarted PriceSource first...
|
||||
HardcodedPriceSource priceSource = new HardcodedPriceSource();
|
||||
HardcodedPriceSource priceSource = new HardcodedPriceSource(
|
||||
currencyIdentityService
|
||||
);
|
||||
|
||||
// The TickerServiceImpl will ask Nenjim for implementers of the PriceSource interface in this context
|
||||
// This do not work yet - so we will just inject it in the constructor now. In the future it will
|
||||
@@ -141,12 +149,15 @@ public class NenjimHubImpl implements NenjimHub {
|
||||
shutdownLatch.await();
|
||||
}
|
||||
|
||||
private void startJupiterPerpsAlarm() {
|
||||
private void startJupiterPerpsAlarm(
|
||||
CurrencyIdentityService currencyIdentityService
|
||||
) {
|
||||
Thread thread = new Thread(() -> {
|
||||
try {
|
||||
JupiterPerpsAlarmImpl.main(new String[] {
|
||||
"--config=conf/alarms.conf"
|
||||
});
|
||||
JupiterPerpsAlarmImpl.start(
|
||||
new String[] {"--config=conf/alarms.conf"},
|
||||
currencyIdentityService
|
||||
);
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package com.r35157.libs.solana.impl.ref;
|
||||
|
||||
import com.r35157.assetaz.services.cis.CurrencyIdentityService;
|
||||
import com.r35157.assetaz.services.cis.ExternalCurrencyReference;
|
||||
import com.r35157.assetaz.valuetypes.CurrencyType;
|
||||
import com.r35157.libs.solana.SolanaLatestBlockhash;
|
||||
import com.r35157.libs.solana.SolanaUnsignedTransaction;
|
||||
import com.r35157.libs.valuetypes.basic.MoneyAmount;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -10,9 +14,11 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static com.r35157.libs.valuetypes.basic.WellKnownCurrencyTypes.SOLANA;
|
||||
import static com.r35157.assetaz.services.cis.CurrencyTypeIds.SOLANA_ID;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
@@ -32,7 +38,7 @@ class SolanaBlockChainImplTest {
|
||||
RECIPIENT,
|
||||
new MoneyAmount(
|
||||
new BigDecimal("0.123456789"),
|
||||
SOLANA.getCurrencyType()
|
||||
SOLANA
|
||||
)
|
||||
);
|
||||
|
||||
@@ -137,7 +143,7 @@ class SolanaBlockChainImplTest {
|
||||
RECIPIENT,
|
||||
new MoneyAmount(
|
||||
new BigDecimal("0.0000000001"),
|
||||
SOLANA.getCurrencyType()
|
||||
SOLANA
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -148,7 +154,7 @@ class SolanaBlockChainImplTest {
|
||||
long fee,
|
||||
AtomicReference<String> feeMessage
|
||||
) {
|
||||
return new SolanaBlockChainImpl() {
|
||||
return new SolanaBlockChainImpl(CURRENCY_IDENTITIES) {
|
||||
@Override
|
||||
public long getBalanceInLamport(String address) {
|
||||
return balance;
|
||||
@@ -181,6 +187,34 @@ class SolanaBlockChainImplTest {
|
||||
.getLong();
|
||||
}
|
||||
|
||||
private static final CurrencyType SOLANA = new CurrencyType(
|
||||
SOLANA_ID,
|
||||
"Solana",
|
||||
"SOL"
|
||||
);
|
||||
private static final CurrencyIdentityService CURRENCY_IDENTITIES =
|
||||
new CurrencyIdentityService() {
|
||||
@Override
|
||||
public @NotNull CurrencyType resolve(@NotNull UUID currencyTypeId) {
|
||||
if (!SOLANA_ID.equals(currencyTypeId)) {
|
||||
throw new IllegalArgumentException("Unknown test currency: " + currencyTypeId);
|
||||
}
|
||||
return SOLANA;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CurrencyType resolve(@NotNull ExternalCurrencyReference externalReference) {
|
||||
throw new IllegalArgumentException(
|
||||
"No external currencies configured for this test"
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<ExternalCurrencyReference> findExternalReferences(@NotNull UUID currencyTypeId) {
|
||||
resolve(currencyTypeId);
|
||||
return Set.of();
|
||||
}
|
||||
};
|
||||
private static final String SENDER =
|
||||
"So11111111111111111111111111111111111111112";
|
||||
private static final String RECIPIENT =
|
||||
|
||||
Reference in New Issue
Block a user