forked from Qortal/qortal
WIP: more work on trade-bot
This commit is contained in:
parent
faa6e82bef
commit
04d691991a
@ -0,0 +1,33 @@
|
||||
package org.qortal.api.model;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class TradeBotCreateRequest {
|
||||
|
||||
@Schema(description = "Trade creator's public key", example = "C6wuddsBV3HzRrXUtezE7P5MoRXp5m3mEDokRDGZB6ry")
|
||||
public byte[] creatorPublicKey;
|
||||
|
||||
@Schema(description = "QORT amount paid out on successful trade", example = "80.40200000")
|
||||
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||
public long qortAmount;
|
||||
|
||||
@Schema(description = "QORT amount funding AT, including covering AT execution fees", example = "81")
|
||||
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||
public long fundingQortAmount;
|
||||
|
||||
@Schema(description = "Bitcoin amount wanted in return", example = "0.00864200")
|
||||
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||
public long bitcoinAmount;
|
||||
|
||||
@Schema(description = "Trade time window (minutes) from trade agreement to automatic refund", example = "10080")
|
||||
public Integer tradeTimeout;
|
||||
|
||||
public TradeBotCreateRequest() {
|
||||
}
|
||||
|
||||
}
|
@ -38,6 +38,7 @@ import org.qortal.api.ApiExceptionFactory;
|
||||
import org.qortal.api.model.CrossChainCancelRequest;
|
||||
import org.qortal.api.model.CrossChainSecretRequest;
|
||||
import org.qortal.api.model.CrossChainTradeRequest;
|
||||
import org.qortal.api.model.TradeBotCreateRequest;
|
||||
import org.qortal.api.model.CrossChainBitcoinP2SHStatus;
|
||||
import org.qortal.api.model.CrossChainBitcoinRedeemRequest;
|
||||
import org.qortal.api.model.CrossChainBitcoinRefundRequest;
|
||||
@ -720,6 +721,36 @@ public class CrossChainResource {
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/tradebot")
|
||||
@Operation(
|
||||
summary = "Create a trade offer",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(
|
||||
implementation = TradeBotCreateRequest.class
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "string"))
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_PUBLIC_KEY, ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE})
|
||||
public String tradeBotCreator(TradeBotCreateRequest tradeBotCreateRequest) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
byte[] unsignedBytes = TradeBot.createTrade(repository, tradeBotCreateRequest);
|
||||
|
||||
return Base58.encode(unsignedBytes);
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/tradebot/{ataddress}")
|
||||
@Operation(
|
||||
|
@ -11,14 +11,23 @@ import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.LegacyAddress;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.qortal.account.PrivateKeyAccount;
|
||||
import org.qortal.account.PublicKeyAccount;
|
||||
import org.qortal.api.model.TradeBotCreateRequest;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.crosschain.BTC;
|
||||
import org.qortal.crosschain.BTCACCT;
|
||||
import org.qortal.crypto.Crypto;
|
||||
import org.qortal.data.crosschain.CrossChainTradeData;
|
||||
import org.qortal.data.crosschain.TradeBotData;
|
||||
import org.qortal.data.transaction.BaseTransactionData;
|
||||
import org.qortal.data.transaction.DeployAtTransactionData;
|
||||
import org.qortal.group.Group;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.repository.RepositoryManager;
|
||||
import org.qortal.transaction.DeployAtTransaction;
|
||||
import org.qortal.transform.transaction.DeployAtTransactionTransformer;
|
||||
import org.qortal.utils.NTP;
|
||||
|
||||
public class TradeBot {
|
||||
|
||||
@ -38,6 +47,49 @@ public class TradeBot {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static byte[] createTrade(Repository repository, TradeBotCreateRequest tradeBotCreateRequest) {
|
||||
BTC btc = BTC.getInstance();
|
||||
NetworkParameters params = btc.getNetworkParameters();
|
||||
|
||||
byte[] tradePrivateKey = generateTradePrivateKey();
|
||||
byte[] secret = generateSecret();
|
||||
byte[] secretHash = Crypto.digest(secret);
|
||||
|
||||
byte[] tradeNativePublicKey = deriveTradeNativePublicKey(tradePrivateKey);
|
||||
byte[] tradeNativePublicKeyHash = Crypto.hash160(tradeNativePublicKey);
|
||||
|
||||
byte[] tradeForeignPublicKey = deriveTradeForeignPublicKey(tradePrivateKey);
|
||||
byte[] tradeForeignPublicKeyHash = Crypto.hash160(tradeForeignPublicKey);
|
||||
|
||||
PublicKeyAccount creator = new PublicKeyAccount(repository, tradeBotCreateRequest.creatorPublicKey);
|
||||
|
||||
// Deploy AT
|
||||
long timestamp = NTP.getTime();
|
||||
byte[] reference = creator.getLastReference();
|
||||
long fee = 0L;
|
||||
byte[] signature = null;
|
||||
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, creator.getPublicKey(), fee, signature);
|
||||
|
||||
String name = "QORT/BTC ACCT";
|
||||
String description = "QORT/BTC cross-chain trade";
|
||||
String aTType = "ACCT";
|
||||
String tags = "ACCT QORT BTC";
|
||||
byte[] creationBytes = BTCACCT.buildQortalAT(creator.getAddress(), tradeNativePublicKeyHash, secretHash, tradeBotCreateRequest.tradeTimeout, tradeBotCreateRequest.qortAmount, tradeBotCreateRequest.bitcoinAmount);
|
||||
long amount = tradeBotCreateRequest.fundingQortAmount;
|
||||
|
||||
DeployAtTransactionData deployAtTransactionData = new DeployAtTransactionData(baseTransactionData, name, description, aTType, tags, creationBytes, amount, Asset.QORT);
|
||||
DeployAtTransaction.ensureATAddress(deployAtTransactionData);
|
||||
String atAddress = deployAtTransactionData.getAtAddress();
|
||||
|
||||
TradeBotData tradeBotData = new TradeBotData(tradePrivateKey, TradeBotData.State.BOB_WAITING_FOR_MESSAGE,
|
||||
tradeNativePublicKey, tradeNativePublicKeyHash, secret, secretHash,
|
||||
tradeForeignPublicKey, tradeForeignPublicKeyHash, atAddress, null);
|
||||
repository.getCrossChainRepository().save(tradeBotData);
|
||||
|
||||
// Return to user for signing and broadcast as we don't have their Qortal private key
|
||||
return DeployAtTransactionTransformer.toBytes(deployAtTransactionData);
|
||||
}
|
||||
|
||||
public static String startResponse(Repository repository, CrossChainTradeData crossChainTradeData) throws DataException {
|
||||
BTC btc = BTC.getInstance();
|
||||
NetworkParameters params = btc.getNetworkParameters();
|
||||
@ -53,7 +105,7 @@ public class TradeBot {
|
||||
byte[] tradeForeignPublicKeyHash = Crypto.hash160(tradeForeignPublicKey);
|
||||
|
||||
TradeBotData tradeBotData = new TradeBotData(tradePrivateKey, TradeBotData.State.ALICE_WAITING_FOR_P2SH_A,
|
||||
tradeNativePublicKey, tradeNativePublicKeyHash, secret, secretHash,
|
||||
tradeNativePublicKey, tradeNativePublicKeyHash, secret, secretHash,
|
||||
tradeForeignPublicKey, tradeForeignPublicKeyHash, crossChainTradeData.qortalAtAddress, null);
|
||||
repository.getCrossChainRepository().save(tradeBotData);
|
||||
|
||||
@ -92,8 +144,8 @@ public class TradeBot {
|
||||
|
||||
for (TradeBotData tradeBotData : allTradeBotData)
|
||||
switch (tradeBotData.getState()) {
|
||||
case ALICE_START:
|
||||
handleAliceStart(repository, tradeBotData);
|
||||
case BOB_WAITING_FOR_MESSAGE:
|
||||
handleBobWaitingForMessage(repository, tradeBotData);
|
||||
break;
|
||||
}
|
||||
} catch (DataException e) {
|
||||
@ -101,7 +153,7 @@ public class TradeBot {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAliceStart(Repository repository, TradeBotData tradeBotData) {
|
||||
private void handleBobWaitingForMessage(Repository repository, TradeBotData tradeBotData) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
public class TradeBotData {
|
||||
|
||||
public enum State {
|
||||
BOB_START(0), BOB_WAITING_FOR_P2SH_A(10), BOB_WAITING_FOR_P2SH_B(20), BOB_WAITING_FOR_AT_REDEEM(30),
|
||||
ALICE_START(100), ALICE_WAITING_FOR_P2SH_A(110), ALICE_WAITING_FOR_AT_LOCK(120), ALICE_WATCH_P2SH_B(130);
|
||||
BOB_WAITING_FOR_AT_CONFIRM(10), BOB_WAITING_FOR_MESSAGE(20), BOB_WAITING_FOR_P2SH_A(30), BOB_WAITING_FOR_P2SH_B(40), BOB_WAITING_FOR_AT_REDEEM(50),
|
||||
ALICE_WAITING_FOR_P2SH_A(110), ALICE_WAITING_FOR_AT_LOCK(120), ALICE_WATCH_P2SH_B(130);
|
||||
|
||||
public final int value;
|
||||
private static final Map<Integer, State> map = stream(State.values()).collect(toMap(state -> state.value, state -> state));
|
||||
|
@ -26,7 +26,7 @@ import com.google.common.base.Utf8;
|
||||
public class DeployAtTransaction extends Transaction {
|
||||
|
||||
// Properties
|
||||
private DeployAtTransactionData deployATTransactionData;
|
||||
private DeployAtTransactionData deployAtTransactionData;
|
||||
|
||||
// Other useful constants
|
||||
public static final int MAX_NAME_SIZE = 200;
|
||||
@ -40,31 +40,31 @@ public class DeployAtTransaction extends Transaction {
|
||||
public DeployAtTransaction(Repository repository, TransactionData transactionData) {
|
||||
super(repository, transactionData);
|
||||
|
||||
this.deployATTransactionData = (DeployAtTransactionData) this.transactionData;
|
||||
this.deployAtTransactionData = (DeployAtTransactionData) this.transactionData;
|
||||
}
|
||||
|
||||
// More information
|
||||
|
||||
@Override
|
||||
public List<String> getRecipientAddresses() throws DataException {
|
||||
return Collections.singletonList(this.deployATTransactionData.getAtAddress());
|
||||
return Collections.singletonList(this.deployAtTransactionData.getAtAddress());
|
||||
}
|
||||
|
||||
/** Returns AT version from the header bytes */
|
||||
private short getVersion() {
|
||||
byte[] creationBytes = deployATTransactionData.getCreationBytes();
|
||||
byte[] creationBytes = deployAtTransactionData.getCreationBytes();
|
||||
return (short) ((creationBytes[0] << 8) | (creationBytes[1] & 0xff)); // Big-endian
|
||||
}
|
||||
|
||||
/** Make sure deployATTransactionData has an ATAddress */
|
||||
private void ensureATAddress() throws DataException {
|
||||
if (this.deployATTransactionData.getAtAddress() != null)
|
||||
public static void ensureATAddress(DeployAtTransactionData deployAtTransactionData) throws DataException {
|
||||
if (deployAtTransactionData.getAtAddress() != null)
|
||||
return;
|
||||
|
||||
// Use transaction transformer
|
||||
try {
|
||||
String atAddress = Crypto.toATAddress(TransactionTransformer.toBytesForSigning(this.deployATTransactionData));
|
||||
this.deployATTransactionData.setAtAddress(atAddress);
|
||||
String atAddress = Crypto.toATAddress(TransactionTransformer.toBytesForSigning(deployAtTransactionData));
|
||||
deployAtTransactionData.setAtAddress(atAddress);
|
||||
} catch (TransformationException e) {
|
||||
throw new DataException("Unable to generate AT address");
|
||||
}
|
||||
@ -73,9 +73,9 @@ public class DeployAtTransaction extends Transaction {
|
||||
// Navigation
|
||||
|
||||
public Account getATAccount() throws DataException {
|
||||
ensureATAddress();
|
||||
ensureATAddress(this.deployAtTransactionData);
|
||||
|
||||
return new Account(this.repository, this.deployATTransactionData.getAtAddress());
|
||||
return new Account(this.repository, this.deployAtTransactionData.getAtAddress());
|
||||
}
|
||||
|
||||
// Processing
|
||||
@ -83,30 +83,30 @@ public class DeployAtTransaction extends Transaction {
|
||||
@Override
|
||||
public ValidationResult isValid() throws DataException {
|
||||
// Check name size bounds
|
||||
int nameLength = Utf8.encodedLength(this.deployATTransactionData.getName());
|
||||
int nameLength = Utf8.encodedLength(this.deployAtTransactionData.getName());
|
||||
if (nameLength < 1 || nameLength > MAX_NAME_SIZE)
|
||||
return ValidationResult.INVALID_NAME_LENGTH;
|
||||
|
||||
// Check description size bounds
|
||||
int descriptionlength = Utf8.encodedLength(this.deployATTransactionData.getDescription());
|
||||
int descriptionlength = Utf8.encodedLength(this.deployAtTransactionData.getDescription());
|
||||
if (descriptionlength < 1 || descriptionlength > MAX_DESCRIPTION_SIZE)
|
||||
return ValidationResult.INVALID_DESCRIPTION_LENGTH;
|
||||
|
||||
// Check AT-type size bounds
|
||||
int atTypeLength = Utf8.encodedLength(this.deployATTransactionData.getAtType());
|
||||
int atTypeLength = Utf8.encodedLength(this.deployAtTransactionData.getAtType());
|
||||
if (atTypeLength < 1 || atTypeLength > MAX_AT_TYPE_SIZE)
|
||||
return ValidationResult.INVALID_AT_TYPE_LENGTH;
|
||||
|
||||
// Check tags size bounds
|
||||
int tagsLength = Utf8.encodedLength(this.deployATTransactionData.getTags());
|
||||
int tagsLength = Utf8.encodedLength(this.deployAtTransactionData.getTags());
|
||||
if (tagsLength < 1 || tagsLength > MAX_TAGS_SIZE)
|
||||
return ValidationResult.INVALID_TAGS_LENGTH;
|
||||
|
||||
// Check amount is positive
|
||||
if (this.deployATTransactionData.getAmount() <= 0)
|
||||
if (this.deployAtTransactionData.getAmount() <= 0)
|
||||
return ValidationResult.NEGATIVE_AMOUNT;
|
||||
|
||||
long assetId = this.deployATTransactionData.getAssetId();
|
||||
long assetId = this.deployAtTransactionData.getAssetId();
|
||||
AssetData assetData = this.repository.getAssetRepository().fromAssetId(assetId);
|
||||
// Check asset even exists
|
||||
if (assetData == null)
|
||||
@ -117,7 +117,7 @@ public class DeployAtTransaction extends Transaction {
|
||||
return ValidationResult.ASSET_NOT_SPENDABLE;
|
||||
|
||||
// Check asset amount is integer if asset is not divisible
|
||||
if (!assetData.isDivisible() && this.deployATTransactionData.getAmount() % Amounts.MULTIPLIER != 0)
|
||||
if (!assetData.isDivisible() && this.deployAtTransactionData.getAmount() % Amounts.MULTIPLIER != 0)
|
||||
return ValidationResult.INVALID_AMOUNT;
|
||||
|
||||
Account creator = this.getCreator();
|
||||
@ -125,15 +125,15 @@ public class DeployAtTransaction extends Transaction {
|
||||
// Check creator has enough funds
|
||||
if (assetId == Asset.QORT) {
|
||||
// Simple case: amount and fee both in QORT
|
||||
long minimumBalance = this.deployATTransactionData.getFee() + this.deployATTransactionData.getAmount();
|
||||
long minimumBalance = this.deployAtTransactionData.getFee() + this.deployAtTransactionData.getAmount();
|
||||
|
||||
if (creator.getConfirmedBalance(Asset.QORT) < minimumBalance)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
} else {
|
||||
if (creator.getConfirmedBalance(Asset.QORT) < this.deployATTransactionData.getFee())
|
||||
if (creator.getConfirmedBalance(Asset.QORT) < this.deployAtTransactionData.getFee())
|
||||
return ValidationResult.NO_BALANCE;
|
||||
|
||||
if (creator.getConfirmedBalance(assetId) < this.deployATTransactionData.getAmount())
|
||||
if (creator.getConfirmedBalance(assetId) < this.deployAtTransactionData.getAmount())
|
||||
return ValidationResult.NO_BALANCE;
|
||||
}
|
||||
|
||||
@ -142,12 +142,12 @@ public class DeployAtTransaction extends Transaction {
|
||||
return ValidationResult.INVALID_CREATION_BYTES;
|
||||
|
||||
// Check creation bytes are valid (for v2+)
|
||||
this.ensureATAddress();
|
||||
ensureATAddress(this.deployAtTransactionData);
|
||||
|
||||
// Just enough AT data to allow API to query initial balances, etc.
|
||||
String atAddress = this.deployATTransactionData.getAtAddress();
|
||||
byte[] creatorPublicKey = this.deployATTransactionData.getCreatorPublicKey();
|
||||
long creation = this.deployATTransactionData.getTimestamp();
|
||||
String atAddress = this.deployAtTransactionData.getAtAddress();
|
||||
byte[] creatorPublicKey = this.deployAtTransactionData.getCreatorPublicKey();
|
||||
long creation = this.deployAtTransactionData.getTimestamp();
|
||||
ATData skeletonAtData = new ATData(atAddress, creatorPublicKey, creation, assetId);
|
||||
|
||||
int height = this.repository.getBlockRepository().getBlockchainHeight() + 1;
|
||||
@ -157,7 +157,7 @@ public class DeployAtTransaction extends Transaction {
|
||||
QortalAtLoggerFactory loggerFactory = QortalAtLoggerFactory.getInstance();
|
||||
|
||||
try {
|
||||
new MachineState(api, loggerFactory, this.deployATTransactionData.getCreationBytes());
|
||||
new MachineState(api, loggerFactory, this.deployAtTransactionData.getCreationBytes());
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Not valid
|
||||
return ValidationResult.INVALID_CREATION_BYTES;
|
||||
@ -169,25 +169,25 @@ public class DeployAtTransaction extends Transaction {
|
||||
@Override
|
||||
public ValidationResult isProcessable() throws DataException {
|
||||
Account creator = getCreator();
|
||||
long assetId = this.deployATTransactionData.getAssetId();
|
||||
long assetId = this.deployAtTransactionData.getAssetId();
|
||||
|
||||
// Check creator has enough funds
|
||||
if (assetId == Asset.QORT) {
|
||||
// Simple case: amount and fee both in QORT
|
||||
long minimumBalance = this.deployATTransactionData.getFee() + this.deployATTransactionData.getAmount();
|
||||
long minimumBalance = this.deployAtTransactionData.getFee() + this.deployAtTransactionData.getAmount();
|
||||
|
||||
if (creator.getConfirmedBalance(Asset.QORT) < minimumBalance)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
} else {
|
||||
if (creator.getConfirmedBalance(Asset.QORT) < this.deployATTransactionData.getFee())
|
||||
if (creator.getConfirmedBalance(Asset.QORT) < this.deployAtTransactionData.getFee())
|
||||
return ValidationResult.NO_BALANCE;
|
||||
|
||||
if (creator.getConfirmedBalance(assetId) < this.deployATTransactionData.getAmount())
|
||||
if (creator.getConfirmedBalance(assetId) < this.deployAtTransactionData.getAmount())
|
||||
return ValidationResult.NO_BALANCE;
|
||||
}
|
||||
|
||||
// Check AT doesn't already exist
|
||||
if (this.repository.getATRepository().exists(this.deployATTransactionData.getAtAddress()))
|
||||
if (this.repository.getATRepository().exists(this.deployAtTransactionData.getAtAddress()))
|
||||
return ValidationResult.AT_ALREADY_EXISTS;
|
||||
|
||||
return ValidationResult.OK;
|
||||
@ -195,40 +195,40 @@ public class DeployAtTransaction extends Transaction {
|
||||
|
||||
@Override
|
||||
public void process() throws DataException {
|
||||
this.ensureATAddress();
|
||||
ensureATAddress(this.deployAtTransactionData);
|
||||
|
||||
// Deploy AT, saving into repository
|
||||
AT at = new AT(this.repository, this.deployATTransactionData);
|
||||
AT at = new AT(this.repository, this.deployAtTransactionData);
|
||||
at.deploy();
|
||||
|
||||
long assetId = this.deployATTransactionData.getAssetId();
|
||||
long assetId = this.deployAtTransactionData.getAssetId();
|
||||
|
||||
// Update creator's balance regarding initial payment to AT
|
||||
Account creator = getCreator();
|
||||
creator.modifyAssetBalance(assetId, - this.deployATTransactionData.getAmount());
|
||||
creator.modifyAssetBalance(assetId, - this.deployAtTransactionData.getAmount());
|
||||
|
||||
// Update AT's reference, which also creates AT account
|
||||
Account atAccount = this.getATAccount();
|
||||
atAccount.setLastReference(this.deployATTransactionData.getSignature());
|
||||
atAccount.setLastReference(this.deployAtTransactionData.getSignature());
|
||||
|
||||
// Update AT's balance
|
||||
atAccount.setConfirmedBalance(assetId, this.deployATTransactionData.getAmount());
|
||||
atAccount.setConfirmedBalance(assetId, this.deployAtTransactionData.getAmount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void orphan() throws DataException {
|
||||
// Delete AT from repository
|
||||
AT at = new AT(this.repository, this.deployATTransactionData);
|
||||
AT at = new AT(this.repository, this.deployAtTransactionData);
|
||||
at.undeploy();
|
||||
|
||||
long assetId = this.deployATTransactionData.getAssetId();
|
||||
long assetId = this.deployAtTransactionData.getAssetId();
|
||||
|
||||
// Update creator's balance regarding initial payment to AT
|
||||
Account creator = getCreator();
|
||||
creator.modifyAssetBalance(assetId, this.deployATTransactionData.getAmount());
|
||||
creator.modifyAssetBalance(assetId, this.deployAtTransactionData.getAmount());
|
||||
|
||||
// Delete AT's account (and hence its balance)
|
||||
this.repository.getAccountRepository().delete(this.deployATTransactionData.getAtAddress());
|
||||
this.repository.getAccountRepository().delete(this.deployAtTransactionData.getAtAddress());
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user