import { rpc, sc, u } from "@cityofzion/neon-core";
import { getTokenInfos } from "./api";
import { getCandidates } from "./api/getCandidates";
import { TransactionBuilder, TransactionValidator, ValidationAttributes, } from "./transaction";
export class NetworkFacade {
    constructor(config) {
        this.magicNumber = 0;
        this.client =
            typeof config.node === "string"
                ? new rpc.NeoServerRpcClient(config.node)
                : config.node;
    }
    static async fromConfig(config) {
        const i = new NetworkFacade(config);
        await i.initialize();
        return i;
    }
    async initialize() {
        const response = await this.client.getVersion();
        this.magicNumber = response.protocol.network;
    }
    getRpcNode() {
        return this.client;
    }
    /**
     * Constructs and executes a transaction of multiple token transfers
     * @param intents - Token transfers
     * @param config - Configuration
     */
    async transferToken(intents, config) {
        const client = this.getRpcNode();
        const txBuilder = new TransactionBuilder();
        for (const intent of intents) {
            if (intent.decimalAmt) {
                const [tokenInfo] = await getTokenInfos([intent.contractHash], client);
                const amt = u.BigInteger.fromDecimal(intent.decimalAmt, tokenInfo.decimals);
                txBuilder.addNep17Transfer(intent.from, intent.to, intent.contractHash, amt);
            }
            else if (intent.integerAmt) {
                txBuilder.addNep17Transfer(intent.from, intent.to, intent.contractHash, intent.integerAmt);
            }
            else {
                throw new Error("no amount specified!");
            }
        }
        const txn = txBuilder.build();
        const validateResult = await this.validate(txn);
        if (!validateResult.valid) {
            throw new Error("Unable to validate transaction");
        }
        const signedTxn = await this.sign(txn, config);
        const sendResult = await this.getRpcNode().sendRawTransaction(signedTxn);
        return sendResult;
    }
    /**
     * Claims all the gas available for the specified account. Do note that GAS is automatically claimed when you perform a transaction involving NEO.
     * @param acct - The account to claim gas on
     * @param config - Configuration
     */
    async claimGas(acct, config) {
        const txn = TransactionBuilder.newBuilder().addGasClaim(acct).build();
        const validateResult = await this.validate(txn);
        if (!validateResult.valid) {
            throw new Error("Unable to validate transaction");
        }
        const signedTxn = await this.sign(txn, config);
        const sendResult = await this.getRpcNode().sendRawTransaction(signedTxn);
        return sendResult;
    }
    /**
     * Convenience method for getting list of candidates.
     */
    async getCandidates() {
        return getCandidates(this.getRpcNode());
    }
    async vote(acct, candidatePublicKey, config) {
        const txn = TransactionBuilder.newBuilder()
            .addVote(acct, candidatePublicKey)
            .build();
        const validateResult = await this.validate(txn);
        if (!validateResult.valid) {
            throw new Error("Unable to validate transaction");
        }
        const signedTxn = await this.sign(txn, config);
        const sendResult = await this.getRpcNode().sendRawTransaction(signedTxn);
        return sendResult;
    }
    /**
     * Performs validation of all attributes on the given transaction.
     * @param txn - Transaction to validate
     */
    async validate(txn) {
        const validator = new TransactionValidator(this.getRpcNode(), txn);
        return await validator.validate(ValidationAttributes.All, ValidationAttributes.All);
    }
    /**
     *  Signs a transaction according to the signing configuration. The input transaction is modified with the signatures and returned.
     * @param txn - Transaction to sign
     * @param config - Configuration
     * @returns
     */
    async sign(txn, config) {
        for (const [idx, w] of txn.witnesses.entries()) {
            const signature = await config.signingCallback(txn, {
                network: this.magicNumber,
                witnessIndex: idx,
            });
            const invocationScript = new sc.OpToken(sc.OpCode.PUSHDATA1, signature).toScript();
            w.invocationScript = u.HexString.fromHex(invocationScript);
        }
        return txn;
    }
    async invoke(contractCall) {
        return this.getRpcNode().invokeFunction(contractCall.scriptHash, contractCall.operation, contractCall.args);
    }
}
//# sourceMappingURL=NetworkFacade.js.map