Skip to content

Commit

Permalink
issue #133 - added mosaic Factory and convenience for the use
Browse files Browse the repository at this point in the history
BlockchainApi now has convenience methods to use currency and harvest
mosaics

Lock funds builder now allows override for currency mosaic

Couple minor improvements and tests
  • Loading branch information
tonowie committed Dec 15, 2019
1 parent 2417a75 commit fc20ab6
Show file tree
Hide file tree
Showing 11 changed files with 404 additions and 22 deletions.
45 changes: 43 additions & 2 deletions src/main/java/io/proximax/sdk/BlockchainApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
import io.proximax.sdk.infrastructure.TransactionHttp;
import io.proximax.sdk.model.account.Account;
import io.proximax.sdk.model.blockchain.NetworkType;
import io.proximax.sdk.model.mosaic.MosaicFactory;
import io.proximax.sdk.model.mosaic.NetworkCurrencyMosaic;
import io.proximax.sdk.model.mosaic.NetworkHarvestMosaic;
import io.proximax.sdk.model.transaction.AggregateTransaction;
import io.proximax.sdk.model.transaction.SignedTransaction;
import io.proximax.sdk.model.transaction.Transaction;
Expand All @@ -42,8 +45,17 @@ public class BlockchainApi {
/** default fee calculation strategy */
public static final FeeCalculationStrategy DEFAULT_FEE_CALCULATION_STRATEGY = FeeCalculationStrategy.MEDIUM;

// defaults for the mosaic factories
private static final MosaicFactory DEFAULT_NETWORK_CURRENCY_FACTORY = NetworkCurrencyMosaic.FACTORY;
private static final MosaicFactory DEFAULT_NETWORK_HARVEST_FACTORY = NetworkHarvestMosaic.FACTORY;


/** URL of the node */
private final URL url;
/** currency mosaic factory */
private final MosaicFactory currencyMosaicFactory;
/** harvest mosaic factory */
private final MosaicFactory harvestMosaicFactory;
/** network type of the node */
private NetworkType networkType;
/** network generation hash used for signing */
Expand All @@ -55,7 +67,7 @@ public class BlockchainApi {
* @param url URL of the node
*/
public BlockchainApi(URL url) {
this.url = url;
this(url, null);
}

/**
Expand All @@ -65,10 +77,25 @@ public BlockchainApi(URL url) {
* @param networkType network type of the node
*/
public BlockchainApi(URL url, NetworkType networkType) {
this(url, networkType, DEFAULT_NETWORK_CURRENCY_FACTORY, DEFAULT_NETWORK_HARVEST_FACTORY);
}

/**
* create new instance that connects to specified node
*
* @param url URL of the node
* @param networkType network type of the node
* @param currencyMosaicFactory factory to create network currency mosaic and provide information about the mosaic
* @param harvestMosaicFactory factory to create network harvest mosaic and provide information about the mosaic
*/
public BlockchainApi(URL url, NetworkType networkType, MosaicFactory currencyMosaicFactory, MosaicFactory harvestMosaicFactory) {
this.url = url;
this.networkType = networkType;
this.currencyMosaicFactory = currencyMosaicFactory;
this.harvestMosaicFactory = harvestMosaicFactory;
}


/**
* check that the network type matches what is reported by the node
*
Expand Down Expand Up @@ -217,7 +244,7 @@ public synchronized String getNetworkGenerationHash() {
*
* @return network type of the node
*/
private NetworkType queryForNetworkType() {
protected NetworkType queryForNetworkType() {
return createBlockchainRepository().getNetworkType().timeout(30, TimeUnit.SECONDS).blockingFirst();
}

Expand All @@ -230,4 +257,18 @@ public TransactionBuilderFactory transact() {
fac.setFeeCalculationStrategy(DEFAULT_FEE_CALCULATION_STRATEGY);
return fac;
}

/**
* @return factory for network currency mosaic
*/
public MosaicFactory networkCurrencyMosaic() {
return currencyMosaicFactory;
}

/**
* @return factory for network harvest mosaic
*/
public MosaicFactory networkHarvestMosaic() {
return harvestMosaicFactory;
}
}
122 changes: 122 additions & 0 deletions src/main/java/io/proximax/sdk/model/mosaic/MosaicFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright 2019 ProximaX Limited. All rights reserved.
* Use of this source code is governed by the Apache 2.0
* license that can be found in the LICENSE file.
*/
package io.proximax.sdk.model.mosaic;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Objects;
import java.util.Optional;

import io.proximax.sdk.model.transaction.UInt64Id;

/**
* Factory that produces Mosaic instances for amounts specified as
* <ul>
* <li>token value regardless of the divisibility - call {@link #create(BigDecimal)} with amount 1.5 to create mosaic
* for 1.5 token</li>
* <li>absolute value taking divisibility into account - call {@link #createAbsolute(BigInteger)} with amount 1_500_000
* to create mosaic for 1.5 token assuming token has divisibility 6</li>
* </ul>
*/
public class MosaicFactory {
private final UInt64Id mosaicId;
private final BigInteger initialSupply;
private final MosaicProperties properties;
// cache the multiplier for performance reasons
private final BigDecimal divisibilityMultiplier;

/**
*
* @param mosaicId
* @param initialSupply
* @param supplyMutable
* @param transferable
* @param divisibility
* @param duration
*/
public MosaicFactory(UInt64Id mosaicId, BigInteger initialSupply, boolean supplyMutable, boolean transferable,
int divisibility, Optional<BigInteger> duration) {
this.mosaicId = mosaicId;
this.initialSupply = initialSupply;
this.divisibilityMultiplier = BigDecimal.valueOf(Math.pow(10, divisibility));
this.properties = new MosaicProperties(supplyMutable, transferable, divisibility, duration);
}

/**
* @return the mosaic ID
*/
public UInt64Id getMosaicId() {
return mosaicId;
}

/**
* @return the initialSupply
*/
public BigInteger getInitialSupply() {
return initialSupply;
}

/**
* @return the mosaic properties
*/
public MosaicProperties getProperties() {
return properties;
}

/**
* Create mosaic based on specified amount relative to the divisibility of the mosaic. That means that 1.5 token with
* divisibility 6 needs to be specified here as 1.5
*
* @param amount amount to send
* @return a mosaic instance
*/
public Mosaic create(BigDecimal amount) {
// create instance of the mosaic
return new Mosaic(mosaicId, getAbsoluteAmount(amount));
}

/**
* Create mosaic with using the base unit as an amount. That means that 1.5 token with divisibility 6 needs to be
* specified as amount 1_500_000
*
* @param amount amount to send
* @return a XPX instance
*/
public Mosaic createAbsolute(BigInteger amount) {
return new Mosaic(mosaicId, amount);
}

/**
* calculate absolute amount based on specified amount relative to the divisibility of the mosaic. That means that
* 1.5 token with divisibility 6 needs to be specified here as 1.5 and result will be 1_500_000
*
* @param amount amount to send
* @return absolute amount
*/
public BigInteger getAbsoluteAmount(BigDecimal amount) {
// multiply specified amount by the 10^divisibility to get the absolute amount
return divisibilityMultiplier.multiply(amount).toBigInteger();
}

@Override
public int hashCode() {
return Objects.hash(divisibilityMultiplier, initialSupply, mosaicId, properties);
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MosaicFactory other = (MosaicFactory) obj;
return Objects.equals(divisibilityMultiplier, other.divisibilityMultiplier)
&& Objects.equals(initialSupply, other.initialSupply) && Objects.equals(mosaicId, other.mosaicId)
&& Objects.equals(properties, other.properties);
}
}
19 changes: 19 additions & 0 deletions src/main/java/io/proximax/sdk/model/mosaic/MosaicProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.math.BigInteger;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import org.apache.commons.lang3.Validate;
Expand Down Expand Up @@ -213,4 +214,22 @@ private static Optional<BigInteger> getPropertyValue(List<MosaicPropertyDTO> mos
.map(UInt64Utils::toBigInt)
.findFirst();
}

@Override
public int hashCode() {
return Objects.hash(divisibility, duration, supplyMutable, transferable);
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MosaicProperties other = (MosaicProperties) obj;
return divisibility == other.divisibility && Objects.equals(duration, other.duration)
&& supplyMutable == other.supplyMutable && transferable == other.transferable;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Optional;

import io.proximax.sdk.model.namespace.NamespaceId;
import io.proximax.sdk.model.transaction.UInt64Id;
Expand Down Expand Up @@ -56,18 +57,21 @@ public class NetworkCurrencyMosaic extends Mosaic {
*/
public static final boolean LEVYMUTABLE = false;

/** factory that can be used to create mosaic instances */
public static final MosaicFactory FACTORY = new MosaicFactory(ID, INITIALSUPPLY, SUPPLYMUTABLE, TRANSFERABLE, DIVISIBILITY, Optional.empty());

/** one of network currency taking the divisibility into account */
public static final NetworkCurrencyMosaic ONE = NetworkCurrencyMosaic.createRelative(BigDecimal.ONE);
public static final NetworkCurrencyMosaic ONE = new NetworkCurrencyMosaic(FACTORY.getAbsoluteAmount(BigDecimal.ONE));
/** ten of network currency taking the divisibility into account */
public static final NetworkCurrencyMosaic TEN = NetworkCurrencyMosaic.createRelative(BigDecimal.TEN);
public static final NetworkCurrencyMosaic TEN = new NetworkCurrencyMosaic(FACTORY.getAbsoluteAmount(BigDecimal.TEN));

/**
* create specified amount of micro XPX
*
* @param amount amount of micro XPX
*/
public NetworkCurrencyMosaic(BigInteger amount) {
super(NetworkCurrencyMosaic.ID, amount);
super(ID, amount);
}

/**
Expand All @@ -77,8 +81,7 @@ public NetworkCurrencyMosaic(BigInteger amount) {
* @return a XPX instance
*/
public static NetworkCurrencyMosaic createRelative(BigDecimal amount) {
BigInteger relativeAmount = BigDecimal.valueOf(Math.pow(10, NetworkCurrencyMosaic.DIVISIBILITY)).multiply(amount).toBigInteger();
return new NetworkCurrencyMosaic(relativeAmount);
return new NetworkCurrencyMosaic(FACTORY.getAbsoluteAmount(amount));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Optional;

import io.proximax.sdk.model.namespace.NamespaceId;

Expand Down Expand Up @@ -50,14 +51,21 @@ public class NetworkHarvestMosaic extends Mosaic {
*/
public static final boolean SUPPLYMUTABLE = true;

/** factory that can be used to create mosaic instances */
public static final MosaicFactory FACTORY = new MosaicFactory(ID, INITIALSUPPLY, SUPPLYMUTABLE, TRANSFERABLE, DIVISIBILITY, Optional.empty());

/** one of network harvest mosaic taking the divisibility into account */
public static final NetworkHarvestMosaic ONE = new NetworkHarvestMosaic(FACTORY.getAbsoluteAmount(BigDecimal.ONE));
/** ten of network harvest mosaic taking the divisibility into account */
public static final NetworkHarvestMosaic TEN = new NetworkHarvestMosaic(FACTORY.getAbsoluteAmount(BigDecimal.TEN));

/**
* create new instance of network harvest mosaic of specified amount
*
* @param amount amount of mosaic irrespective of divisibility
*/
public NetworkHarvestMosaic(BigInteger amount) {

super(NetworkHarvestMosaic.ID, amount);
super(ID, amount);
}

/**
Expand All @@ -66,11 +74,8 @@ public NetworkHarvestMosaic(BigInteger amount) {
* @param amount amount to send
* @return a NetworkCurrencyMosaic instance
*/
public static NetworkHarvestMosaic createRelative(BigInteger amount) {

BigInteger relativeAmount = BigDecimal.valueOf(Math.pow(10, NetworkHarvestMosaic.DIVISIBILITY)).toBigInteger()
.multiply(amount);
return new NetworkHarvestMosaic(relativeAmount);
public static NetworkHarvestMosaic createRelative(BigDecimal amount) {
return new NetworkHarvestMosaic(FACTORY.getAbsoluteAmount(amount));
}

/**
Expand All @@ -80,7 +85,6 @@ public static NetworkHarvestMosaic createRelative(BigInteger amount) {
* @return a NetworkCurrencyMosaic instance
*/
public static NetworkHarvestMosaic createAbsolute(BigInteger amount) {

return new NetworkHarvestMosaic(amount);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
*/
package io.proximax.sdk.model.transaction.builder;

import java.math.BigDecimal;
import java.math.BigInteger;

import io.proximax.sdk.BlockchainApi;
import io.proximax.sdk.model.mosaic.Mosaic;
import io.proximax.sdk.model.mosaic.MosaicFactory;
import io.proximax.sdk.model.mosaic.NetworkCurrencyMosaic;
import io.proximax.sdk.model.transaction.EntityType;
import io.proximax.sdk.model.transaction.EntityVersion;
Expand Down Expand Up @@ -112,22 +115,39 @@ public SignedTransaction getSignedTransaction() {
*
* @param duration the duration in number of blocks
* @return self
* @deprecated use {@link #forAggregate(BigInteger, SignedTransaction)} instead
* @deprecated use {@link #forAggregate(MosaicFactory, BigInteger, SignedTransaction)} instead
*/
@Deprecated
public LockFundsTransactionBuilder aggregate(BigInteger duration) {
return mosaic(NetworkCurrencyMosaic.TEN).duration(duration);
}

/**
* <p>lock 10 network currency for specified duration and signed transaction. this is convenience for aggregate
* transaction locks</p>
* <p><b>NOTICE</b> this assumes that {@link NetworkCurrencyMosaic} is the network currency mosaic</p>
*
* @param duration the duration in number of blocks
* @param signedAggregateTransaction the aggregate transaction which is getting the lock created
* @return self
*/
public LockFundsTransactionBuilder forAggregate(BigInteger duration, SignedTransaction signedAggregateTransaction) {
return forAggregate(NetworkCurrencyMosaic.FACTORY, duration, signedAggregateTransaction);
}

/**
* lock 10 network currency for specified duration and signed transaction. this is convenience for aggregate
* transaction locks
*
* @param networkCurrencyFactory the factory for network currency. Available via call to
* {@link BlockchainApi#networkCurrencyMosaic()}
* @param duration the duration in number of blocks
* @param signedAggregateTransaction the aggregate transaction which is getting the lock created
* @return self
*/
public LockFundsTransactionBuilder forAggregate(BigInteger duration, SignedTransaction signedAggregateTransaction) {
return mosaic(NetworkCurrencyMosaic.TEN).duration(duration).signedTransaction(signedAggregateTransaction);
public LockFundsTransactionBuilder forAggregate(MosaicFactory networkCurrencyFactory, BigInteger duration,
SignedTransaction signedAggregateTransaction) {
Mosaic mosaicForTransfer = networkCurrencyFactory.create(BigDecimal.TEN);
return mosaic(mosaicForTransfer).duration(duration).signedTransaction(signedAggregateTransaction);
}
}
Loading

0 comments on commit fc20ab6

Please sign in to comment.