All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.neow3j.contract.NeoToken Maven / Gradle / Ivy

package io.neow3j.contract;

import io.neow3j.contract.exceptions.UnexpectedReturnTypeException;
import io.neow3j.crypto.ECKeyPair.ECPublicKey;
import io.neow3j.protocol.Neow3j;
import io.neow3j.protocol.core.response.NeoAccountState;
import io.neow3j.protocol.core.stackitem.StackItem;
import io.neow3j.transaction.TransactionBuilder;
import io.neow3j.types.ContractParameter;
import io.neow3j.types.Hash160;
import io.neow3j.wallet.Account;

import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static io.neow3j.types.ContractParameter.any;
import static io.neow3j.types.ContractParameter.hash160;
import static io.neow3j.types.ContractParameter.integer;
import static io.neow3j.types.ContractParameter.publicKey;
import static io.neow3j.types.StackItemType.ANY;
import static io.neow3j.types.StackItemType.ARRAY;
import static io.neow3j.types.StackItemType.BYTE_STRING;
import static io.neow3j.types.StackItemType.INTEGER;
import static io.neow3j.types.StackItemType.STRUCT;
import static java.util.Arrays.asList;

/**
 * Represents the NeoToken native contract and provides methods to invoke its functions.
 */
public class NeoToken extends FungibleToken {

    public static final String NAME = "NeoToken";
    public static final Hash160 SCRIPT_HASH = calcNativeContractHash(NAME);

    public static final int DECIMALS = 0;
    public static final String SYMBOL = "NEO";
    public static final BigInteger TOTAL_SUPPLY = new BigInteger("100000000");

    private static final String UNCLAIMED_GAS = "unclaimedGas";
    private static final String REGISTER_CANDIDATE = "registerCandidate";
    private static final String UNREGISTER_CANDIDATE = "unregisterCandidate";
    private static final String VOTE = "vote";
    private static final String GET_CANDIDATES = "getCandidates";
    private static final String GET_COMMITTEE = "getCommittee";
    private static final String GET_NEXT_BLOCK_VALIDATORS = "getNextBlockValidators";
    private static final String SET_GAS_PER_BLOCK = "setGasPerBlock";
    private static final String GET_GAS_PER_BLOCK = "getGasPerBlock";
    private static final String SET_REGISTER_PRICE = "setRegisterPrice";
    private static final String GET_REGISTER_PRICE = "getRegisterPrice";
    private static final String GET_ACCOUNT_STATE = "getAccountState";

    /**
     * Constructs a new {@code NeoToken} that uses the given {@link Neow3j} instance for
     * invocations.
     *
     * @param neow the {@link Neow3j} instance to use for invocations.
     */
    public NeoToken(Neow3j neow) {
        super(SCRIPT_HASH, neow);
    }

    /**
     * Returns the name of the NeoToken contract.
     * 

* Doesn't require a call to the Neo node. * * @return the name. */ @Override public String getName() { return NAME; } /** * Returns the symbol of the NeoToken contract. *

* Doesn't require a call to the Neo node. * * @return the symbol. */ @Override public String getSymbol() { return SYMBOL; } /** * Returns the total supply of the NeoToken contract. *

* Doesn't require a call to the Neo node. * * @return the total supply. */ @Override public BigInteger getTotalSupply() { return TOTAL_SUPPLY; } /** * Returns the number of decimals of the NEO token. *

* Doesn't require a call to the Neo node. * * @return the number of decimals. */ @Override public int getDecimals() { return DECIMALS; } // region unclaimed gas /** * Gets the amount of unclaimed GAS at the given height for the given account. * * @param account the account. * @param blockHeight the block height. * @return the amount of unclaimed GAS. * @throws IOException if there was a problem fetching information from the Neo node. */ public BigInteger unclaimedGas(Account account, long blockHeight) throws IOException { return unclaimedGas(account.getScriptHash(), blockHeight); } /** * Gets the amount of unclaimed GAS at the given height for the given account. * * @param scriptHash the account's script hash. * @param blockHeight the block height. * @return the amount of unclaimed GAS * @throws IOException if there was a problem fetching information from the Neo node. */ public BigInteger unclaimedGas(Hash160 scriptHash, long blockHeight) throws IOException { ContractParameter accParam = hash160(scriptHash); ContractParameter heightParam = integer(BigInteger.valueOf(blockHeight)); return callFuncReturningInt(UNCLAIMED_GAS, accParam, heightParam); } // endregion unclaimed gas // region candidate registration /** * Creates a transaction script for registering a committee candidate with the given * public key and initializes a {@link TransactionBuilder} based on this script. *

* Note, that the transaction has to be signed with the account corresponding to the public key. * * @param candidateKey the public key to register as a candidate. * @return a transaction builder. */ public TransactionBuilder registerCandidate(ECPublicKey candidateKey) { return invokeFunction(REGISTER_CANDIDATE, publicKey(candidateKey.getEncoded(true))); } /** * Creates a transaction script for registering a validator candidate and initializes a * {@link TransactionBuilder} based on this script. * * @param candidateKey the public key to register as a candidate. * @return a transaction builder. */ public TransactionBuilder unregisterCandidate(ECPublicKey candidateKey) { return invokeFunction(UNREGISTER_CANDIDATE, publicKey(candidateKey.getEncoded(true))); } // endregion candidate registration // region committee and candidates information /** * Gets the public keys of the current committee members. * * @return the committee members' public keys. * @throws IOException if there was a problem fetching information from the * Neo node. * @throws UnexpectedReturnTypeException if the return type is not an array or the returned * array's elements are not public keys. */ public List getCommittee() throws IOException { return callFunctionReturningListOfPublicKeys(GET_COMMITTEE); } /** * Gets the public keys of the currently registered validator candidates and their * corresponding vote count. *

* The vote count is based on the summed up NEO balances of the respective candidate's voters. * * @return the candidate public keys and their corresponding vote count. * @throws IOException if there was a problem fetching information from the * Neo node. * @throws UnexpectedReturnTypeException if the return type is not an array or the array * elements are not public keys and node counts. */ public Map getCandidates() throws IOException { StackItem arrayItem = callInvokeFunction(GET_CANDIDATES) .getInvocationResult().getStack().get(0); if (!arrayItem.getType().equals(ARRAY)) { throw new UnexpectedReturnTypeException(arrayItem.getType(), ARRAY); } Map validators = new HashMap<>(); for (StackItem valItem : arrayItem.getList()) { if (!valItem.getType().equals(STRUCT)) { throw new UnexpectedReturnTypeException(valItem.getType(), STRUCT); } ECPublicKey key = extractPublicKey(valItem.getList().get(0)); StackItem nrItem = valItem.getList().get(1); if (!nrItem.getType().equals(INTEGER)) { throw new UnexpectedReturnTypeException(nrItem.getType(), INTEGER); } validators.put(key, nrItem.getInteger()); } return validators; } /** * Checks if there is a committee candidate or member with {@code publicKey}. * * @param publicKey The candidates public key. * @return true if the public key belongs to a candidate. False otherwise. * @throws IOException if there was a problem fetching information from the Neo node. */ public boolean isCandidate(ECPublicKey publicKey) throws IOException { return getCandidates().containsKey(publicKey); } /** * Gets the public keys of the next block's validators. * * @return the validators' public keys. * @throws IOException if there was a problem fetching information from the * Neo node. * @throws UnexpectedReturnTypeException if the return type is not an array or the returned * array's elements are not public keys. */ public List getNextBlockValidators() throws IOException { return callFunctionReturningListOfPublicKeys(GET_NEXT_BLOCK_VALIDATORS); } private List callFunctionReturningListOfPublicKeys(String function) throws IOException { StackItem arrayItem = callInvokeFunction(function).getInvocationResult().getStack().get(0); if (!arrayItem.getType().equals(ARRAY)) { throw new UnexpectedReturnTypeException(arrayItem.getType(), ARRAY); } List valKeys = new ArrayList<>(); for (StackItem keyItem : arrayItem.getList()) { valKeys.add(extractPublicKey(keyItem)); } return valKeys; } private ECPublicKey extractPublicKey(StackItem keyItem) { if (!keyItem.getType().equals(BYTE_STRING)) { throw new UnexpectedReturnTypeException(keyItem.getType(), BYTE_STRING); } try { return new ECPublicKey(keyItem.getByteArray()); } catch (IllegalArgumentException e) { throw new UnexpectedReturnTypeException("Byte array return type did not contain " + "public key in expected format.", e); } } // endregion committee and candidates information // region voting /** * Creates a transaction script to vote for the given validators and initializes a * {@link TransactionBuilder} based on this script. * * @param voter the account that casts the vote. * @param candidate the candidate to vote for. If null, then the current vote of the voter is * withdrawn (see {@link NeoToken#cancelVote(Account)}). * @return a transaction builder. * @throws IOException if there was a problem fetching information from the Neo node. */ public TransactionBuilder vote(Account voter, ECPublicKey candidate) throws IOException { return vote(voter.getScriptHash(), candidate); } /** * Creates a transaction script to vote for the given validators and initializes a * {@link TransactionBuilder} based on this script. * * @param voter the account that casts the vote. * @param candidate the candidate to vote for. If null, then the current vote of the voter is * withdrawn (see {@link NeoToken#cancelVote(Hash160)}). * @return a transaction builder. * @throws IOException if there was a problem fetching information from the Neo node. */ public TransactionBuilder vote(Hash160 voter, ECPublicKey candidate) throws IOException { if (candidate == null) { return invokeFunction(VOTE, hash160(voter), any(null)); } return invokeFunction(VOTE, hash160(voter), publicKey(candidate.getEncoded(true))); } /** * Creates a transaction script to cancel the vote of {@code voter} and initializes a * transaction Builder based on the script. * * @param voter the account for which to cancel the vote. * @return a transaction builder * @throws IOException if there was a problem fetching information from the Neo node. */ public TransactionBuilder cancelVote(Hash160 voter) throws IOException { return vote(voter, null); } /** * Creates a transaction script to cancel the vote of {@code voter} and initializes a * transaction Builder based on the script. * * @param voter the account for which to cancel the vote. * @return a transaction builder * @throws IOException if there was a problem fetching information from the Neo node. */ public TransactionBuilder cancelVote(Account voter) throws IOException { return cancelVote(voter.getScriptHash()); } /** * Builds a script to vote for a candidate. * * @param voter the account that casts the vote. * @param candidate the candidate to vote for. If null, then the current vote of the voter is * withdrawn (see {@link NeoToken#cancelVote(Hash160)}). * @return the script. */ public byte[] buildVoteScript(Hash160 voter, ECPublicKey candidate) { if (candidate == null) { return buildInvokeFunctionScript(VOTE, hash160(voter), any(null)); } return buildInvokeFunctionScript(VOTE, hash160(voter), publicKey(candidate.getEncoded(true))); } // endregion voting // region network settings /** * Gets the number of GAS generated in each block. * * @return the max GAS amount per block. * @throws IOException if there was a problem fetching information from the Neo node. */ public BigInteger getGasPerBlock() throws IOException { return callFuncReturningInt(GET_GAS_PER_BLOCK); } /** * Creates a transaction script to set the number of GAS generated in each block and * initializes a {@link TransactionBuilder} based on this script. *

* This contract invocation can only be successful if it is signed by the network committee. * * @param gasPerBlock the maximum amount of GAS in one block. * @return the transaction builder. */ public TransactionBuilder setGasPerBlock(BigInteger gasPerBlock) { return invokeFunction(SET_GAS_PER_BLOCK, integer(gasPerBlock)); } /** * Gets the price to register as a candidate. * * @return the price to register as a candidate. * @throws IOException if there was a problem fetching information from the Neo node. */ public BigInteger getRegisterPrice() throws IOException { return callFuncReturningInt(GET_REGISTER_PRICE); } /** * Creates a transaction script to set the price for candidate registration and initializes a * {@link TransactionBuilder} based on this script. *

* This contract invocation can only be successful if it is signed by the network committee. * * @param registerPrice the price to register as a candidate. * @return the transaction builder. */ public TransactionBuilder setRegisterPrice(BigInteger registerPrice) { return invokeFunction(SET_REGISTER_PRICE, integer(registerPrice)); } // endregion network settings /** * Gets the state of an account. * * @param account the account to get the state from. * @return the account state. * @throws IOException if there was a problem fetching information from the Neo node. */ public NeoAccountState getAccountState(Hash160 account) throws IOException { StackItem result = callInvokeFunction(GET_ACCOUNT_STATE, asList(hash160(account))) .getInvocationResult().getStack().get(0); if (result.getType().equals(ANY)) { return NeoAccountState.withNoBalance(); } List state = result.getList(); BigInteger balance = state.get(0).getInteger(); BigInteger updateHeight = state.get(1).getInteger(); StackItem publicKeyItem = state.get(2); if (publicKeyItem.getType().equals(ANY)) { return NeoAccountState.withNoVote(balance, updateHeight); } return new NeoAccountState(balance, updateHeight, new ECPublicKey(publicKeyItem.getHexString())); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy