"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SolanaCircleBridge = void 0;
const web3_js_1 = require("@solana/web3.js");
const connect_sdk_1 = require("@wormhole-foundation/connect-sdk");
const anchor_1 = require("@project-serum/anchor");
const spl_token_1 = require("@solana/spl-token");
const connect_sdk_solana_1 = require("@wormhole-foundation/connect-sdk-solana");
const utils_1 = require("./utils");
const instructions_1 = require("./utils/instructions");
class SolanaCircleBridge {
    network;
    chain;
    connection;
    contracts;
    tokenMessenger;
    messageTransmitter;
    constructor(network, chain, connection, contracts) {
        this.network = network;
        this.chain = chain;
        this.connection = connection;
        this.contracts = contracts;
        if (network === 'Devnet')
            throw new Error('CircleBridge not supported on Devnet');
        const msgTransmitterAddress = contracts.cctp?.messageTransmitter;
        if (!msgTransmitterAddress)
            throw new Error(`Circle Messenge Transmitter contract for domain ${chain} not found`);
        this.messageTransmitter = (0, utils_1.createReadOnlyMessageTransmitterProgramInterface)(new web3_js_1.PublicKey(msgTransmitterAddress), this.connection);
        const tokenMessengerAddress = contracts.cctp?.tokenMessenger;
        if (!tokenMessengerAddress)
            throw new Error(`Circle Token Messenger contract for domain ${chain} not found`);
        this.tokenMessenger = (0, utils_1.createReadOnlyTokenMessengerProgramInterface)(new web3_js_1.PublicKey(tokenMessengerAddress), this.connection);
    }
    static async fromRpc(provider, config) {
        const [network, chain] = await connect_sdk_solana_1.SolanaPlatform.chainFromRpc(provider);
        const conf = config[chain];
        if (conf.network !== network)
            throw new Error(`Network mismatch: ${conf.network} != ${network}`);
        return new SolanaCircleBridge(network, chain, provider, conf.contracts);
    }
    async *redeem(sender, message, attestation) {
        const usdc = new web3_js_1.PublicKey(connect_sdk_1.circle.usdcContract.get(this.network, this.chain));
        const senderPk = new connect_sdk_solana_1.SolanaAddress(sender).unwrap();
        const ix = await (0, instructions_1.createReceiveMessageInstruction)(this.messageTransmitter.programId, this.tokenMessenger.programId, usdc, message, attestation, senderPk);
        const transaction = new web3_js_1.Transaction();
        transaction.feePayer = senderPk;
        transaction.add(ix);
        yield this.createUnsignedTx({ transaction }, 'CircleBridge.Redeem');
    }
    async *transfer(sender, recipient, amount) {
        const usdc = new web3_js_1.PublicKey(connect_sdk_1.circle.usdcContract.get(this.network, this.chain));
        const senderPk = new connect_sdk_solana_1.SolanaAddress(sender).unwrap();
        const senderATA = (0, spl_token_1.getAssociatedTokenAddressSync)(usdc, senderPk);
        const destinationDomain = connect_sdk_1.circle.circleChainId.get(this.network, recipient.chain);
        const destinationAddress = recipient.address.toUniversalAddress();
        const ix = await (0, instructions_1.createDepositForBurnInstruction)(this.messageTransmitter.programId, this.tokenMessenger.programId, usdc, destinationDomain, senderPk, senderATA, destinationAddress, amount);
        const transaction = new web3_js_1.Transaction();
        transaction.feePayer = senderPk;
        transaction.add(ix);
        yield this.createUnsignedTx({ transaction }, 'CircleBridge.Transfer');
    }
    async isTransferCompleted(message) {
        const usedNoncesAddress = (0, instructions_1.nonceAccount)(message.nonce, message.sourceDomain, this.messageTransmitter.programId);
        const firstNonce = (0, instructions_1.calculateFirstNonce)(message.nonce);
        // usedNonces should be a [u64;100] where each bit is a nonce flag
        const { usedNonces } = 
        // @ts-ignore --
        await this.messageTransmitter.account.usedNonces.fetch(usedNoncesAddress);
        // get the nonce index based on the account's first nonce
        const nonceIndex = Number(message.nonce - firstNonce);
        // get the the u64 the nonce's flag is in
        const nonceElement = usedNonces[Math.floor(nonceIndex / 64)];
        if (!nonceElement)
            throw new Error('Invalid nonce byte index');
        // get the nonce flag index and build a bitmask
        const nonceBitIndex = nonceIndex % 64;
        const mask = new anchor_1.BN(1 << nonceBitIndex);
        // If the flag is 0 it is _not_ used
        return !nonceElement.and(mask).isZero();
    }
    // Fetch the transaction logs and parse the CircleTransferMessage
    async parseTransactionDetails(txid) {
        const tx = await this.connection.getTransaction(txid);
        if (!tx || !tx.meta)
            throw new Error('Transaction not found');
        // this log contains the cctp message information
        const messageTransmitterParser = new anchor_1.EventParser(this.messageTransmitter.programId, this.messageTransmitter.coder);
        const messageLogs = [
            ...messageTransmitterParser.parseLogs(tx.meta.logMessages || []),
        ];
        if (messageLogs.length === 0)
            throw new Error('No CircleTransferMessage found');
        const message = new Uint8Array(messageLogs[0].data['message']);
        const [msg, hash] = connect_sdk_1.CircleBridge.deserialize(message);
        const { payload: body } = msg;
        const xferSender = body.messageSender;
        const xferReceiver = body.mintRecipient;
        const sendChain = connect_sdk_1.circle.toCircleChain(this.network, msg.sourceDomain);
        const rcvChain = connect_sdk_1.circle.toCircleChain(this.network, msg.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: msg,
            id: { hash },
        };
    }
    createUnsignedTx(txReq, description, parallelizable = false) {
        return new connect_sdk_solana_1.SolanaUnsignedTransaction(txReq, this.network, this.chain, description, parallelizable);
    }
}
exports.SolanaCircleBridge = SolanaCircleBridge;
//# sourceMappingURL=circleBridge.js.map