/**
 * Contract module - routines to interact with the æternity contract
 *
 * High level documentation of the contracts are available at
 * https://github.com/aeternity/protocol/tree/master/contracts and
 */
import { Encoder as Calldata } from '@aeternity/aepp-calldata';
import { Tag, AensName } from '../tx/builder/constants.js';
import { BuildTxOptions } from '../tx/builder/index.js';
import { Encoded } from '../utils/encoder.js';
import { ContractCallObject as NodeContractCallObject, Event as NodeEvent } from '../apis/node/index.js';
import CompilerBase, { Aci } from './compiler/Base.js';
import Node from '../Node.js';
import { txDryRun } from '../chain.js';
import { sendTransaction, SendTransactionOptions } from '../send-transaction.js';
import { TxUnpacked } from '../tx/builder/schema.generated.js';
import { Optional } from '../utils/other.js';
interface Event extends NodeEvent {
    address: Encoded.ContractAddress;
    data: Encoded.ContractBytearray;
}
export interface ContractCallObject extends NodeContractCallObject {
    returnValue: Encoded.ContractBytearray;
    log: Event[];
}
interface DecodedEvent {
    name: string;
    args: unknown[];
    contract: {
        name: string;
        address: Encoded.ContractAddress;
    };
}
type TxData = Awaited<ReturnType<typeof sendTransaction>>;
interface SendAndProcessReturnType {
    result?: ContractCallObject;
    hash: TxData['hash'];
    tx: TxUnpacked & {
        tag: Tag.SignedTx | Tag.ContractCallTx | Tag.ContractCreateTx;
    };
    txData: TxData;
    rawTx: Encoded.Transaction;
}
/**
 * @category contract
 */
export interface ContractMethodsBase {
    [key: string]: (...args: any[]) => any;
}
type MethodsToContractApi<M extends ContractMethodsBase> = {
    [Name in keyof M]: M[Name] extends (...args: infer Args) => infer Ret ? (...args: [
        ...Args,
        ...([] | [
            Name extends 'init' ? Parameters<Contract<M>['$deploy']>[1] : Parameters<Contract<M>['$call']>[2]
        ])
    ]) => Promise<Omit<Awaited<ReturnType<Contract<M>['$call']>>, 'decodedResult'> & {
        decodedResult: Ret;
    }> : never;
};
/**
 * @category contract
 */
type ContractWithMethods<M extends ContractMethodsBase> = Contract<M> & MethodsToContractApi<M>;
type MethodNames<M extends ContractMethodsBase> = (keyof M & string) | 'init';
type MethodParameters<M extends ContractMethodsBase, Fn extends MethodNames<M>> = Fn extends 'init' ? M extends {
    init: any;
} ? Parameters<M['init']> : [] : Parameters<M[Fn]>;
interface GetContractNameByEventOptions {
    contractAddressToName?: {
        [key: Encoded.ContractAddress]: string;
    };
}
interface GetCallResultByHashReturnType<M extends ContractMethodsBase, Fn extends MethodNames<M>> {
    decodedResult: ReturnType<M[Fn]>;
    decodedEvents?: ReturnType<Contract<M>['$decodeEvents']>;
}
/**
 * Generate contract ACI object with predefined js methods for contract usage - can be used for
 * creating a reference to already deployed contracts
 * @category contract
 * @param options - Options object
 * @returns JS Contract API
 * @example
 * ```js
 * const contractIns = await Contract.initialize({ ...aeSdk.getContext(), sourceCode })
 * await contractIns.$deploy([321]) or await contractIns.init(321)
 * const callResult = await contractIns.$call('setState', [123])
 * const staticCallResult = await contractIns.$call('setState', [123], { callStatic: true })
 * ```
 * Also you can call contract like: `await contractIns.setState(123, options)`
 * Then sdk decide to make on-chain or static call (dry-run API) transaction based on function is
 * stateful or not
 */
declare class Contract<M extends ContractMethodsBase> {
    #private;
    /**
     * Compile contract
     * @returns bytecode
     */
    $compile(): Promise<Encoded.ContractBytearray>;
    $getCallResultByTxHash<Fn extends MethodNames<M>>(hash: Encoded.TxHash, fnName: Fn, options?: Parameters<Contract<M>['$decodeEvents']>[1]): Promise<GetCallResultByHashReturnType<M, Fn> & {
        result: ContractCallObject;
    }>;
    _estimateGas<Fn extends MethodNames<M>>(name: Fn, params: MethodParameters<M, Fn>, options?: Omit<Parameters<Contract<M>['$call']>[2], 'callStatic'>): Promise<number>;
    /**
     * Deploy contract
     * @param params - Contract init function arguments array
     * @param options - Options
     * @returns deploy info
     */
    $deploy(params: MethodParameters<M, 'init'>, options?: Parameters<Contract<M>['$call']>[2] & Partial<BuildTxOptions<Tag.ContractCreateTx, 'ownerId' | 'code' | 'callData'>>): Promise<Omit<SendAndProcessReturnType, 'hash'> & {
        transaction?: Encoded.TxHash;
        owner?: Encoded.AccountAddress;
        address?: Encoded.ContractAddress;
        decodedEvents?: ReturnType<Contract<M>['$decodeEvents']>;
    }>;
    /**
     * Call contract function
     * @param fn - Function name
     * @param params - Array of function arguments
     * @param options - Array of function arguments
     * @returns CallResult
     */
    $call<Fn extends MethodNames<M>>(fn: Fn, params: MethodParameters<M, Fn>, options?: Partial<BuildTxOptions<Tag.ContractCallTx, 'callerId' | 'contractId' | 'callData'>> & Parameters<Contract<M>['$decodeEvents']>[1] & Optional<SendTransactionOptions, 'onAccount' | 'onNode'> & Omit<Parameters<typeof txDryRun>[2], 'onNode'> & {
        callStatic?: boolean;
    }): Promise<SendAndProcessReturnType & Partial<GetCallResultByHashReturnType<M, Fn>>>;
    /**
     * Decode Events
     * @param events - Array of encoded events (callRes.result.log)
     * @param options - Options
     * @returns DecodedEvents
     */
    $decodeEvents(events: Event[], { omitUnknown, ...opt }?: {
        omitUnknown?: boolean;
    } & GetContractNameByEventOptions): DecodedEvent[];
    static initialize<M extends ContractMethodsBase>({ onCompiler, onNode, bytecode, aci, address, sourceCodePath, sourceCode, fileSystem, validateBytecode, ...otherOptions }: Omit<ConstructorParameters<typeof Contract>[0], 'aci' | 'address'> & {
        validateBytecode?: boolean;
        aci?: Aci;
        address?: Encoded.ContractAddress | AensName;
    }): Promise<ContractWithMethods<M>>;
    _aci: Aci;
    _name: string;
    _calldata: Calldata;
    $options: Omit<ConstructorParameters<typeof Contract>[0], 'aci'>;
    /**
     * @param options - Options
     */
    constructor({ aci, ...otherOptions }: {
        onCompiler?: CompilerBase;
        onNode: Node;
        bytecode?: Encoded.ContractBytearray;
        aci: Aci;
        address?: Encoded.ContractAddress;
        /**
         * Supported only in Ceres
         */
        name?: AensName;
        sourceCodePath?: Parameters<CompilerBase['compile']>[0];
        sourceCode?: Parameters<CompilerBase['compileBySourceCode']>[0];
        fileSystem?: Parameters<CompilerBase['compileBySourceCode']>[1];
    } & Parameters<Contract<M>['$deploy']>[1]);
}
interface ContractWithMethodsClass {
    new <M extends ContractMethodsBase>(options: ConstructorParameters<typeof Contract>[0]): ContractWithMethods<M>;
    initialize: (typeof Contract)['initialize'];
}
/**
 * @category contract
 */
declare const ContractWithMethods: ContractWithMethodsClass;
export default ContractWithMethods;
