import { EthAddress, Fr, L1FeeJuicePortalManager, retryUntil } from '@aztec/aztec.js';
import { FeeJuiceContract } from '@aztec/noir-contracts.js/FeeJuice';
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
export class FeeJuicePortalTestingHarnessFactory {
    config;
    constructor(config){
        this.config = config;
    }
    async createReal() {
        const { aztecNode, aztecNodeAdmin, pxeService, l1Client, wallet, logger } = this.config;
        const ethAccount = EthAddress.fromString((await l1Client.getAddresses())[0]);
        const l1ContractAddresses = (await pxeService.getNodeInfo()).l1ContractAddresses;
        const feeJuiceAddress = l1ContractAddresses.feeJuiceAddress;
        const feeJuicePortalAddress = l1ContractAddresses.feeJuicePortalAddress;
        if (feeJuiceAddress.isZero() || feeJuicePortalAddress.isZero()) {
            throw new Error('Fee Juice portal not deployed on L1');
        }
        const gasL2 = await FeeJuiceContract.at(ProtocolContractAddress.FeeJuice, wallet);
        return new GasBridgingTestHarness(aztecNode, aztecNodeAdmin, pxeService, logger, gasL2, ethAccount, feeJuicePortalAddress, feeJuiceAddress, l1ContractAddresses.feeAssetHandlerAddress, l1Client);
    }
    static create(config) {
        const factory = new FeeJuicePortalTestingHarnessFactory(config);
        return factory.createReal();
    }
}
/**
 * A Class for testing cross chain interactions, contains common interactions
 * shared between cross chain tests.
 */ export class GasBridgingTestHarness {
    aztecNode;
    aztecNodeAdmin;
    pxeService;
    logger;
    feeJuice;
    ethAccount;
    feeJuicePortalAddress;
    l1FeeJuiceAddress;
    feeAssetHandlerAddress;
    l1Client;
    l1TokenManager;
    feeJuicePortalManager;
    constructor(/** Aztec node */ aztecNode, /** Aztec node admin interface */ aztecNodeAdmin, /** Private eXecution Environment (PXE). */ pxeService, /** Logger. */ logger, /** L2 Token/Bridge contract. */ feeJuice, /** Eth account to interact with. */ ethAccount, /** Portal address. */ feeJuicePortalAddress, /** Underlying token for portal tests. */ l1FeeJuiceAddress, /** Fee asset handler address. */ feeAssetHandlerAddress, /** Viem Extended client instance. */ l1Client){
        this.aztecNode = aztecNode;
        this.aztecNodeAdmin = aztecNodeAdmin;
        this.pxeService = pxeService;
        this.logger = logger;
        this.feeJuice = feeJuice;
        this.ethAccount = ethAccount;
        this.feeJuicePortalAddress = feeJuicePortalAddress;
        this.l1FeeJuiceAddress = l1FeeJuiceAddress;
        this.feeAssetHandlerAddress = feeAssetHandlerAddress;
        this.l1Client = l1Client;
        this.feeJuicePortalManager = new L1FeeJuicePortalManager(this.feeJuicePortalAddress, this.l1FeeJuiceAddress, this.feeAssetHandlerAddress, this.l1Client, this.logger);
        this.l1TokenManager = this.feeJuicePortalManager.getTokenManager();
    }
    async mintTokensOnL1(to = this.ethAccount) {
        // const balanceBefore = await this.l1TokenManager.getL1TokenBalance(to.toString());
        await this.l1TokenManager.mint(to.toString());
    // expect(await this.l1TokenManager.getL1TokenBalance(to.toString())).toEqual(balanceBefore + amount);
    }
    async getL1FeeJuiceBalance(address) {
        return await this.l1TokenManager.getL1TokenBalance(address.toString());
    }
    sendTokensToPortalPublic(bridgeAmount, l2Address) {
        return this.feeJuicePortalManager.bridgeTokensPublic(l2Address, bridgeAmount, false);
    }
    async consumeMessageOnAztecAndClaimPrivately(owner, claimer, claim) {
        this.logger.info('Consuming messages on L2 Privately');
        const { claimAmount, claimSecret, messageLeafIndex } = claim;
        await this.feeJuice.methods.claim(owner, claimAmount, claimSecret, messageLeafIndex).send({
            from: claimer
        }).wait();
    }
    async getL2PublicBalanceOf(owner) {
        return await this.feeJuice.methods.balance_of_public(owner).simulate({
            from: owner
        });
    }
    async expectPublicBalanceOnL2(owner, expectedBalance) {
        const balance = await this.getL2PublicBalanceOf(owner);
        expect(balance).toBe(expectedBalance);
    }
    async prepareTokensOnL1(owner) {
        const bridgeAmount = await this.l1TokenManager.getMintAmount();
        await this.mintTokensOnL1();
        const claim = await this.sendTokensToPortalPublic(bridgeAmount, owner);
        const isSynced = async ()=>await this.aztecNode.isL1ToL2MessageSynced(Fr.fromHexString(claim.messageHash));
        await retryUntil(isSynced, `message ${claim.messageHash} sync`, 24, 1);
        // Progress by 2 L2 blocks so that the l1ToL2Message added above will be available to use on L2.
        await this.advanceL2Block();
        await this.advanceL2Block();
        return claim;
    }
    async bridgeFromL1ToL2(owner, claimer) {
        // Prepare the tokens on the L1 side
        const claim = await this.prepareTokensOnL1(owner);
        // Consume L1 -> L2 message and claim tokens privately on L2
        await this.consumeMessageOnAztecAndClaimPrivately(owner, claimer, claim);
    }
    async advanceL2Block() {
        const initialBlockNumber = await this.aztecNode.getBlockNumber();
        let minTxsPerBlock = undefined;
        if (this.aztecNodeAdmin) {
            ({ minTxsPerBlock } = await this.aztecNodeAdmin.getConfig());
            await this.aztecNodeAdmin.setConfig({
                minTxsPerBlock: 0
            }); // Set to 0 to ensure we can advance the block
        }
        await retryUntil(async ()=>await this.aztecNode.getBlockNumber() >= initialBlockNumber + 1);
        if (this.aztecNodeAdmin && minTxsPerBlock !== undefined) {
            await this.aztecNodeAdmin.setConfig({
                minTxsPerBlock
            });
        }
    }
} // docs:end:cross_chain_test_harness
