"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AcrossPlusMessageCoder = exports.MulticallHandlerCoder = exports.LargeAccountsCoder = void 0;
const anchor_1 = require("@coral-xyz/anchor");
const idl_1 = require("@coral-xyz/anchor/dist/cjs/coder/borsh/idl");
const borsh = __importStar(require("@coral-xyz/borsh"));
const web3_js_1 = require("@solana/web3.js");
const bs58_1 = __importDefault(require("bs58"));
/**
 * Extended Anchor accounts coder to handle large account data.
 */
class LargeAccountsCoder extends anchor_1.BorshAccountsCoder {
    // Getter to access the private accountLayouts property from base class.
    getAccountLayouts() {
        return this.accountLayouts;
    }
    async encode(accountName, account) {
        const buffer = Buffer.alloc(10240); // We don't currently need anything above instruction data account reallocation limit.
        const layout = this.getAccountLayouts().get(accountName);
        if (!layout) {
            throw new Error(`Unknown account: ${accountName}`);
        }
        const len = layout.encode(account, buffer);
        const accountData = buffer.slice(0, len);
        const discriminator = this.accountDiscriminator(accountName);
        return Buffer.concat([discriminator, accountData]);
    }
}
exports.LargeAccountsCoder = LargeAccountsCoder;
/**
 * Extended version of legacy CompiledKeys to handle compilation of unsigned transactions. Base implementation is here:
 * https://github.com/solana-labs/solana-web3.js/blob/v1.95.3/src/message/compiled-keys.ts
 */
class UnsignedCompiledKeys {
    constructor(keyModeMap, payer) {
        this.keyModeMap = keyModeMap;
        this.payer = payer;
    }
    static compileUnsigned(instructions, payer) {
        const keyModeMap = new Map();
        const getOrInsertDefault = (pubkey) => {
            const address = pubkey.toBase58();
            let keyMode = keyModeMap.get(address);
            if (keyMode === undefined) {
                keyMode = {
                    isWritable: false,
                };
                keyModeMap.set(address, keyMode);
            }
            return keyMode;
        };
        if (payer !== undefined) {
            const payerKeyMode = getOrInsertDefault(payer);
            payerKeyMode.isWritable = true;
        }
        for (const ix of instructions) {
            getOrInsertDefault(ix.programId);
            for (const accountMeta of ix.keys) {
                const keyMode = getOrInsertDefault(accountMeta.pubkey);
                keyMode.isWritable ||= accountMeta.isWritable;
            }
        }
        return new UnsignedCompiledKeys(keyModeMap, payer);
    }
    getMessageComponents() {
        const mapEntries = [...this.keyModeMap.entries()];
        if (mapEntries.length > 256)
            throw new Error("Max static account keys length exceeded");
        const writableNonSigners = mapEntries.filter(([, mode]) => mode.isWritable);
        const readonlyNonSigners = mapEntries.filter(([, mode]) => !mode.isWritable);
        const header = {
            numRequiredSignatures: 0,
            numReadonlySignedAccounts: 0,
            numReadonlyUnsignedAccounts: readonlyNonSigners.length,
        };
        const staticAccountKeys = [
            ...writableNonSigners.map(([address]) => new web3_js_1.PublicKey(address)),
            ...readonlyNonSigners.map(([address]) => new web3_js_1.PublicKey(address)),
        ];
        return [header, staticAccountKeys];
    }
}
/**
 * Extended version of legacy Message to handle compilation of unsigned transactions. Base implementation is here:
 * https://github.com/solana-labs/solana-web3.js/blob/v1.95.3/src/message/legacy.ts
 */
class UnsignedMessage extends web3_js_1.Message {
    static compileUnsigned(instructions, payer) {
        const compiledKeys = UnsignedCompiledKeys.compileUnsigned(instructions, payer);
        const [header, staticAccountKeys] = compiledKeys.getMessageComponents();
        const accountKeys = new web3_js_1.MessageAccountKeys(staticAccountKeys);
        const compiledInstructions = accountKeys.compileInstructions(instructions).map((ix) => ({
            programIdIndex: ix.programIdIndex,
            accounts: ix.accountKeyIndexes,
            data: bs58_1.default.encode(ix.data),
        }));
        return new web3_js_1.Message({
            header,
            accountKeys: staticAccountKeys,
            recentBlockhash: "", // Not used as we are not signing the transaction.
            instructions: compiledInstructions,
        });
    }
}
/**
 * Helper to encode MulticallHandler transactions.
 */
class MulticallHandlerCoder {
    constructor(instructions, payerKey) {
        // Compile transaction message and keys.
        this.compiledMessage = UnsignedMessage.compileUnsigned(instructions, payerKey);
        // Setup the layout for the encoder.
        const fieldLayouts = [idl_1.IdlCoder.fieldLayout(MulticallHandlerCoder.coderArg, MulticallHandlerCoder.coderTypes)];
        this.layout = borsh.struct(fieldLayouts);
    }
    get readOnlyLen() {
        return (this.compiledMessage.header.numReadonlySignedAccounts + this.compiledMessage.header.numReadonlyUnsignedAccounts);
    }
    get compiledKeyMetas() {
        return this.compiledMessage.accountKeys.map((key, index) => {
            return {
                pubkey: key,
                isSigner: this.compiledMessage.isAccountSigner(index),
                isWritable: this.compiledMessage.isAccountWritable(index),
            };
        });
    }
    encode() {
        const buffer = Buffer.alloc(1280);
        const len = this.layout.encode({ compiledIxs: this.compiledMessage.compiledInstructions }, buffer);
        return buffer.slice(0, len);
    }
}
exports.MulticallHandlerCoder = MulticallHandlerCoder;
MulticallHandlerCoder.coderArg = {
    name: "compiledIxs",
    type: {
        vec: {
            defined: {
                name: "compiledIx",
            },
        },
    },
};
MulticallHandlerCoder.coderTypes = [
    {
        name: "compiledIx",
        type: {
            kind: "struct",
            fields: [
                {
                    name: "programIdIndex",
                    type: "u8",
                },
                {
                    name: "accountKeyIndexes",
                    type: {
                        vec: "u8",
                    },
                },
                {
                    name: "data",
                    type: "bytes",
                },
            ],
        },
    },
];
/**
 * Helper to encode Across+ messages.
 */
class AcrossPlusMessageCoder {
    constructor(acrossPlusMessage) {
        this.acrossPlusMessage = acrossPlusMessage;
    }
    encode() {
        const fieldLayouts = [idl_1.IdlCoder.fieldLayout(AcrossPlusMessageCoder.coderArg, AcrossPlusMessageCoder.coderTypes)];
        const layout = borsh.struct(fieldLayouts);
        const buffer = Buffer.alloc(12800);
        const len = layout.encode({ message: this.acrossPlusMessage }, buffer);
        return buffer.slice(0, len);
    }
}
exports.AcrossPlusMessageCoder = AcrossPlusMessageCoder;
AcrossPlusMessageCoder.coderArg = {
    name: "message",
    type: {
        defined: {
            name: "acrossPlusMessage",
        },
    },
};
AcrossPlusMessageCoder.coderTypes = [
    {
        name: "acrossPlusMessage",
        type: {
            kind: "struct",
            fields: [
                {
                    name: "handler",
                    type: "pubkey",
                },
                {
                    name: "readOnlyLen",
                    type: "u8",
                },
                {
                    name: "valueAmount",
                    type: "u64",
                },
                {
                    name: "accounts",
                    type: {
                        vec: "pubkey",
                    },
                },
                {
                    name: "handlerMessage",
                    type: "bytes",
                },
            ],
        },
    },
];
