var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { SUI_CLOCK_OBJECT_ID, TransactionBlock, } from "@mysten/sui.js";
import { PublicKey, Transaction, } from "@solana/web3.js";
import { MsgExecuteContract } from "@terra-money/terra.js";
import { MsgExecuteContract as XplaMsgExecuteContract } from "@xpla/xpla.js";
import BN from "bn.js";
import { fromUint8Array } from "js-base64";
import { _submitVAAAlgorand } from "../algorand";
import { createWrappedCoin as createWrappedCoinAptos, createWrappedCoinType as createWrappedCoinTypeAptos, } from "../aptos";
import { Bridge__factory } from "../ethers-contracts";
import { createCreateWrappedInstruction } from "../solana/tokenBridge";
import { getOwnedObjectId, getPackageId, getUpgradeCapObjectId, getWrappedCoinType, publishCoin, uint8ArrayToBCS, } from "../sui";
import { callFunctionNear } from "../utils";
export function createWrappedOnEth(tokenBridgeAddress, signer, signedVAA, overrides = {}) {
    return __awaiter(this, void 0, void 0, function* () {
        const bridge = Bridge__factory.connect(tokenBridgeAddress, signer);
        const v = yield bridge.createWrapped(signedVAA, overrides);
        const receipt = yield v.wait();
        return receipt;
    });
}
export function createWrappedOnTerra(tokenBridgeAddress, walletAddress, signedVAA) {
    return __awaiter(this, void 0, void 0, function* () {
        return new MsgExecuteContract(walletAddress, tokenBridgeAddress, {
            submit_vaa: {
                data: fromUint8Array(signedVAA),
            },
        });
    });
}
export function createWrappedOnXpla(tokenBridgeAddress, walletAddress, signedVAA) {
    return new XplaMsgExecuteContract(walletAddress, tokenBridgeAddress, {
        submit_vaa: {
            data: fromUint8Array(signedVAA),
        },
    });
}
export function createWrappedOnSolana(connection, bridgeAddress, tokenBridgeAddress, payerAddress, signedVaa, commitment) {
    return __awaiter(this, void 0, void 0, function* () {
        const transaction = new Transaction().add(createCreateWrappedInstruction(tokenBridgeAddress, bridgeAddress, payerAddress, signedVaa));
        const { blockhash } = yield connection.getLatestBlockhash(commitment);
        transaction.recentBlockhash = blockhash;
        transaction.feePayer = new PublicKey(payerAddress);
        return transaction;
    });
}
export function createWrappedOnAlgorand(client, tokenBridgeId, bridgeId, senderAddr, attestVAA) {
    return __awaiter(this, void 0, void 0, function* () {
        return yield _submitVAAAlgorand(client, tokenBridgeId, bridgeId, attestVAA, senderAddr);
    });
}
export function createWrappedOnNear(provider, tokenBridge, attestVAA) {
    return __awaiter(this, void 0, void 0, function* () {
        const vaa = Buffer.from(attestVAA).toString("hex");
        const res = yield callFunctionNear(provider, tokenBridge, "deposit_estimates");
        const msgs = [
            {
                contractId: tokenBridge,
                methodName: "submit_vaa",
                args: { vaa },
                attachedDeposit: new BN(res[1]),
                gas: new BN("150000000000000"),
            },
        ];
        msgs.push(Object.assign({}, msgs[0]));
        return msgs;
    });
}
/**
 * Constructs payload to create wrapped asset type. The type is of form `{{address}}::coin::T`,
 * where address is `sha256_hash(tokenBridgeAddress | chainID | "::" | originAddress | 0xFF)`.
 *
 * Note that the typical createWrapped call is broken into two parts on Aptos because we must first
 * create the CoinType that is used by `create_wrapped_coin<CoinType>`. Since it's not possible to
 * create a resource and use it in the same transaction, this is broken into separate transactions.
 * @param tokenBridgeAddress Address of token bridge
 * @param attestVAA Bytes of attest VAA
 * @returns Transaction payload
 */
export function createWrappedTypeOnAptos(tokenBridgeAddress, attestVAA) {
    return createWrappedCoinTypeAptos(tokenBridgeAddress, attestVAA);
}
/**
 * Constructs payload to create wrapped asset.
 *
 * Note that this function is typically called in tandem with `createWrappedTypeOnAptos` because
 * we must first create the CoinType that is used by `create_wrapped_coin<CoinType>`. Since it's
 * not possible to create a resource and use it in the same transaction, this is broken into
 * separate transactions.
 * @param tokenBridgeAddress Address of token bridge
 * @param attestVAA Bytes of attest VAA
 * @returns Transaction payload
 */
export function createWrappedOnAptos(tokenBridgeAddress, attestVAA) {
    return createWrappedCoinAptos(tokenBridgeAddress, attestVAA);
}
export function createWrappedOnSuiPrepare(provider, coreBridgeStateObjectId, tokenBridgeStateObjectId, decimals, signerAddress) {
    return __awaiter(this, void 0, void 0, function* () {
        return publishCoin(provider, coreBridgeStateObjectId, tokenBridgeStateObjectId, decimals, signerAddress);
    });
}
export function createWrappedOnSui(provider, coreBridgeStateObjectId, tokenBridgeStateObjectId, signerAddress, coinPackageId, wrappedAssetSetupType, attestVAA) {
    var _a;
    return __awaiter(this, void 0, void 0, function* () {
        const [coreBridgePackageId, tokenBridgePackageId] = yield Promise.all([
            getPackageId(provider, coreBridgeStateObjectId),
            getPackageId(provider, tokenBridgeStateObjectId),
        ]);
        // Get coin metadata
        const coinType = getWrappedCoinType(coinPackageId);
        const coinMetadataObjectId = (_a = (yield provider.getCoinMetadata({ coinType }))) === null || _a === void 0 ? void 0 : _a.id;
        if (!coinMetadataObjectId) {
            throw new Error(`Coin metadata object not found for coin type ${coinType}.`);
        }
        // WrappedAssetSetup looks like
        // 0x92d81f28c167d90f84638c654b412fe7fa8e55bdfac7f638bdcf70306289be86::create_wrapped::WrappedAssetSetup<0xa40e0511f7d6531dd2dfac0512c7fd4a874b76f5994985fb17ee04501a2bb050::coin::COIN, 0x4eb7c5bca3759ab3064b46044edb5668c9066be8a543b28b58375f041f876a80::version_control::V__0_1_1>
        const wrappedAssetSetupObjectId = yield getOwnedObjectId(provider, signerAddress, wrappedAssetSetupType);
        if (!wrappedAssetSetupObjectId) {
            throw new Error(`WrappedAssetSetup not found`);
        }
        // Get coin upgrade capability
        const coinUpgradeCapObjectId = yield getUpgradeCapObjectId(provider, signerAddress, coinPackageId);
        if (!coinUpgradeCapObjectId) {
            throw new Error(`Coin upgrade cap not found for ${coinType} under owner ${signerAddress}. You must call 'createWrappedOnSuiPrepare' first.`);
        }
        // Get TokenBridgeMessage
        const tx = new TransactionBlock();
        const [vaa] = tx.moveCall({
            target: `${coreBridgePackageId}::vaa::parse_and_verify`,
            arguments: [
                tx.object(coreBridgeStateObjectId),
                tx.pure(uint8ArrayToBCS(attestVAA)),
                tx.object(SUI_CLOCK_OBJECT_ID),
            ],
        });
        const [message] = tx.moveCall({
            target: `${tokenBridgePackageId}::vaa::verify_only_once`,
            arguments: [tx.object(tokenBridgeStateObjectId), vaa],
        });
        // Construct complete registration payload
        const versionType = wrappedAssetSetupType.split(", ")[1].replace(">", ""); // ugh
        tx.moveCall({
            target: `${tokenBridgePackageId}::create_wrapped::complete_registration`,
            arguments: [
                tx.object(tokenBridgeStateObjectId),
                tx.object(coinMetadataObjectId),
                tx.object(wrappedAssetSetupObjectId),
                tx.object(coinUpgradeCapObjectId),
                message,
            ],
            typeArguments: [coinType, versionType],
        });
        return tx;
    });
}
