"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EvmCircleBridge = void 0;
const connect_sdk_1 = require("@wormhole-foundation/connect-sdk");
const connect_sdk_evm_1 = require("@wormhole-foundation/connect-sdk-evm");
const ethers_1 = require("ethers");
const _1 = require(".");
//https://github.com/circlefin/evm-cctp-contracts
class EvmCircleBridge {
    network;
    chain;
    provider;
    contracts;
    chainId;
    circleChainId;
    msgTransmitter;
    tokenMessenger;
    tokenEventHash;
    messageSentEventHash;
    messageReceivedEventHash;
    constructor(network, chain, provider, contracts) {
        this.network = network;
        this.chain = chain;
        this.provider = provider;
        this.contracts = contracts;
        if (network === 'Devnet')
            throw new Error('CircleBridge not supported on Devnet');
        this.chainId = connect_sdk_1.nativeChainIds.networkChainToNativeChainId.get(network, chain);
        const circleChainId = connect_sdk_1.circle.circleChainId.get(network, chain);
        if (!circleChainId)
            throw new Error(`Circle chain id not found for ${network} ${chain}`);
        this.circleChainId = circleChainId;
        const msgTransmitterAddress = contracts.cctp?.messageTransmitter;
        if (!msgTransmitterAddress)
            throw new Error(`Circle Messenge Transmitter contract for domain ${chain} not found`);
        this.msgTransmitter = _1.ethers_contracts.MessageTransmitter__factory.connect(msgTransmitterAddress, provider);
        const tokenMessengerAddress = contracts.cctp?.tokenMessenger;
        if (!tokenMessengerAddress)
            throw new Error(`Circle Token Messenger contract for domain ${chain} not found`);
        this.tokenMessenger = _1.ethers_contracts.TokenMessenger__factory.connect(tokenMessengerAddress, provider);
        this.tokenEventHash =
            this.tokenMessenger.getEvent('DepositForBurn').fragment.topicHash;
        this.messageSentEventHash =
            this.msgTransmitter.getEvent('MessageSent').fragment.topicHash;
        this.messageReceivedEventHash =
            this.msgTransmitter.getEvent('MessageReceived').fragment.topicHash;
    }
    static async fromRpc(provider, config) {
        const [network, chain] = await connect_sdk_evm_1.EvmPlatform.chainFromRpc(provider);
        const conf = config[chain];
        if (conf.network !== network)
            throw new Error(`Network mismatch: ${conf.network} != ${network}`);
        return new EvmCircleBridge(network, chain, provider, conf.contracts);
    }
    async *redeem(sender, message, attestation) {
        const senderAddr = new connect_sdk_evm_1.EvmAddress(sender).toString();
        const txReq = await this.msgTransmitter.receiveMessage.populateTransaction(connect_sdk_1.CircleBridge.serialize(message), connect_sdk_1.encoding.hex.decode(attestation));
        yield this.createUnsignedTx((0, connect_sdk_evm_1.addFrom)(txReq, senderAddr), 'CircleBridge.redeem');
    }
    //alternative naming: initiateTransfer
    async *transfer(sender, recipient, amount) {
        const senderAddr = new connect_sdk_evm_1.EvmAddress(sender).toString();
        const recipientAddress = recipient.address
            .toUniversalAddress()
            .toUint8Array();
        const tokenAddr = connect_sdk_1.circle.usdcContract.get(this.network, this.chain);
        const tokenContract = connect_sdk_evm_1.EvmPlatform.getTokenImplementation(this.provider, tokenAddr);
        const allowance = await tokenContract.allowance(senderAddr, this.tokenMessenger.target);
        if (allowance < amount) {
            const txReq = await tokenContract.approve.populateTransaction(this.tokenMessenger.target, amount);
            yield this.createUnsignedTx((0, connect_sdk_evm_1.addFrom)(txReq, senderAddr), 'ERC20.approve of CircleBridge', false);
        }
        const txReq = await this.tokenMessenger.depositForBurn.populateTransaction(amount, connect_sdk_1.circle.circleChainId.get(this.network, recipient.chain), recipientAddress, tokenAddr);
        yield this.createUnsignedTx((0, connect_sdk_evm_1.addFrom)(txReq, senderAddr), 'CircleBridge.transfer');
    }
    async isTransferCompleted(message) {
        const hash = ethers_1.ethers.keccak256(ethers_1.ethers.solidityPacked(['uint32', 'uint64'], [message.sourceDomain, message.nonce]));
        const result = await this.msgTransmitter.usedNonces.staticCall(hash);
        return result.toString() === '1';
    }
    // Fetch the transaction logs and parse the CircleTransferMessage
    async parseTransactionDetails(txid) {
        const receipt = await this.provider.getTransactionReceipt(txid);
        if (!receipt)
            throw new Error(`No receipt for ${txid} on ${this.chain}`);
        const messageLogs = receipt.logs
            .filter((log) => log.topics[0] === this.messageSentEventHash)
            .map((messageLog) => {
            const { topics, data } = messageLog;
            return this.msgTransmitter.interface.parseLog({
                topics: topics.slice(),
                data: data,
            });
        })
            .filter((l) => !!l);
        if (messageLogs.length === 0)
            throw new Error(`No log message for message transmitter found in ${txid}`);
        // just taking the first one here, will there ever be >1?
        if (messageLogs.length > 1)
            console.error(`Expected 1 event to be found for transaction, got>${messageLogs.length}}`);
        const [messageLog] = messageLogs;
        const { message } = messageLog.args;
        const [circleMsg, hash] = connect_sdk_1.CircleBridge.deserialize(connect_sdk_1.encoding.hex.decode(message));
        const { payload: body } = circleMsg;
        const xferSender = body.messageSender;
        const xferReceiver = body.mintRecipient;
        const sendChain = connect_sdk_1.circle.toCircleChain(this.network, circleMsg.sourceDomain);
        const rcvChain = connect_sdk_1.circle.toCircleChain(this.network, circleMsg.destinationDomain);
        const token = { chain: sendChain, address: body.burnToken };
        return {
            from: { chain: sendChain, address: xferSender },
            to: { chain: rcvChain, address: xferReceiver },
            token: token,
            amount: body.amount,
            message: circleMsg,
            id: { hash },
        };
    }
    createUnsignedTx(txReq, description, parallelizable = false) {
        return new connect_sdk_evm_1.EvmUnsignedTransaction((0, connect_sdk_evm_1.addChainId)(txReq, this.chainId), this.network, this.chain, description, parallelizable);
    }
}
exports.EvmCircleBridge = EvmCircleBridge;
//# sourceMappingURL=circleBridge.js.map