import { address as parseAddress } from '@solana/kit';
import { ArtifactState, } from '@hyperlane-xyz/provider-sdk/artifact';
import { TokenType, } from '@hyperlane-xyz/provider-sdk/warp';
import { ZERO_ADDRESS_HEX_32, assert, isNullish, isZeroishAddress, } from '@hyperlane-xyz/utils';
import { resolveProgram } from '../deploy/resolve-program.js';
import { getTokenInitInstruction } from '../instructions/token.js';
import { writableAccount } from '../instructions/utils.js';
import { deriveNativeCollateralPda } from '../pda.js';
import { fetchTokenAccount, routerBytesToHex } from './warp-query.js';
import { applyPostInitConfig, assertLocalDecimals, buildBaseInitData, computeWarpTokenUpdateInstructions, remoteDecimalsToScale, scaleToRemoteDecimals, } from './warp-tx.js';
import { DEFAULT_COMPUTE_UNITS } from '../constants.js';
/** Native SOL decimal precision. */
const SOL_DECIMALS = 9;
export class SvmNativeTokenReader {
    rpc;
    constructor(rpc) {
        this.rpc = rpc;
    }
    async read(programAddress) {
        const programId = parseAddress(programAddress);
        const token = await fetchTokenAccount(this.rpc, programId);
        assert(!isNullish(token), `Native token not initialized at ${programId}`);
        const remoteRouters = {};
        for (const [domain, router] of token.remoteRouters.entries()) {
            remoteRouters[domain] = { address: routerBytesToHex(router) };
        }
        const destinationGas = {};
        for (const [domain, gas] of token.destinationGas.entries()) {
            destinationGas[domain] = gas.toString();
        }
        const config = {
            type: TokenType.native,
            owner: token.owner ?? ZERO_ADDRESS_HEX_32,
            mailbox: token.mailbox,
            interchainSecurityModule: token.interchainSecurityModule
                ? {
                    artifactState: ArtifactState.UNDERIVED,
                    deployed: { address: token.interchainSecurityModule },
                }
                : undefined,
            hook: token.interchainGasPaymaster
                ? {
                    artifactState: ArtifactState.UNDERIVED,
                    deployed: { address: token.interchainGasPaymaster.programId },
                }
                : undefined,
            remoteRouters,
            destinationGas,
            decimals: token.decimals,
            scale: remoteDecimalsToScale(token.decimals, token.remoteDecimals),
        };
        return {
            artifactState: ArtifactState.DEPLOYED,
            config,
            deployed: { address: programId },
        };
    }
}
export class SvmNativeTokenWriter extends SvmNativeTokenReader {
    config;
    svmSigner;
    constructor(config, rpc, svmSigner) {
        super(rpc);
        this.config = config;
        this.svmSigner = svmSigner;
    }
    async create(artifact) {
        const receipts = [];
        const tokenConfig = artifact.config;
        const localDecimals = tokenConfig.decimals ?? SOL_DECIMALS;
        assertLocalDecimals(localDecimals);
        const remoteDecimals = scaleToRemoteDecimals(localDecimals, tokenConfig.scale);
        const { programAddress, receipts: deployReceipts } = await resolveProgram(this.config.program, this.svmSigner, this.rpc, true);
        receipts.push(...deployReceipts);
        const { address: nativeCollateralPda } = await deriveNativeCollateralPda(programAddress);
        const initData = await buildBaseInitData(tokenConfig, localDecimals, remoteDecimals);
        const initIx = await getTokenInitInstruction(programAddress, this.svmSigner.signer, initData, [writableAccount(nativeCollateralPda)]);
        receipts.push(await this.svmSigner.send({
            instructions: [initIx],
            computeUnits: DEFAULT_COMPUTE_UNITS,
            skipPreflight: true,
        }));
        receipts.push(...(await applyPostInitConfig(this.svmSigner, programAddress, tokenConfig)));
        return [
            {
                artifactState: ArtifactState.DEPLOYED,
                config: { ...tokenConfig, decimals: localDecimals },
                deployed: { address: programAddress },
            },
            receipts,
        ];
    }
    async update(artifact) {
        const programId = parseAddress(artifact.deployed.address);
        const current = await this.read(programId);
        assert(!isZeroishAddress(current.config.owner), `Cannot update native token ${programId}: token has no owner`);
        return computeWarpTokenUpdateInstructions(current.config, artifact.config, programId, parseAddress(current.config.owner), this.rpc, `native token ${programId}`);
    }
}
//# sourceMappingURL=native-token.js.map