"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Client = void 0;
const tslib_1 = require("tslib");
const ethers_1 = require("ethers");
const utils_1 = require("ethers/lib/utils");
const splendidJson = tslib_1.__importStar(require("../out/Splendid.sol/Splendid.json"));
const merkletreejs_1 = require("merkletreejs");
const constants_1 = require("./constants");
const keccak256_1 = require("@ethersproject/keccak256");
class Client {
    constructor(splendidContractAddress, signerOProvider) {
        if (!signerOProvider) {
            throw new Error('Must provide a provider or signer');
        }
        if (!splendidContractAddress) {
            throw new Error('RPC URL passed to client must be defined');
        }
        this.splendid = new ethers_1.ethers.Contract(splendidContractAddress, splendidJson.abi, signerOProvider);
        this.splendidInterface = new ethers_1.ethers.utils.Interface(splendidJson.abi);
    }
    async createCollection({ curatorFee, initialCollectionContent, epochLength, }) {
        const seenCreatorIds = new Set();
        const initialCollectionFeeBips = initialCollectionContent.reduce((acc, currEntry) => {
            const feeBips = currEntry.currentFeeBips;
            const creatorId = currEntry.creatorId;
            // Verify that the fee BIPs is a positive number.
            if (feeBips <= 0) {
                throw new Error(`Fee bips cannot be ${feeBips} for creator ID ${creatorId} (must be positive).`);
            }
            // Verify that this creator ID is not a duplicate.
            if (seenCreatorIds.has(creatorId)) {
                throw new Error(`Creator ID ${creatorId} is duplicated in the collection.`);
            }
            // Mark this creator ID as seen.
            seenCreatorIds.add(creatorId);
            return acc + feeBips;
        }, 0);
        // Ensure the curator fee is not negative.
        const curatorFeeBigNum = (0, utils_1.parseUnits)(curatorFee);
        if (curatorFeeBigNum.isNegative()) {
            throw new Error(`Curator fee cannot be negative (provided value was ${curatorFee})`);
        }
        // Validate that the epoch length is not less than the minimum epoch length.
        const epochLengthSeconds = epochLength.toMillis() / 1000;
        if (epochLengthSeconds < constants_1.MIN_EPOCH_LENGTH_SECONDS) {
            throw new Error(`Epoch length in seconds of ${epochLengthSeconds} is less than minimum epoch length of ${constants_1.MIN_EPOCH_LENGTH_SECONDS}`);
        }
        // Create the initial collection root from the provided content.
        const merkleTree = this.createMerkleTreeFromCollectionContent(initialCollectionContent);
        const initialCollectionRoot = merkleTree.getHexRoot();
        return this.splendid.createCollection(curatorFeeBigNum, initialCollectionRoot, initialCollectionFeeBips, epochLengthSeconds);
    }
    async settleCreatorEpochRewardsFromCollection({ collectionOwner, collectionIndex, startingEpoch, startingEpochTreeNode, startingMerkleTreeLeaves, endingEpoch, endingEpochTreeNode, endingMerkleTreeLeaves, }) {
        // Validate that the collection owner is a valid Ethereum address.
        if (!ethers_1.ethers.utils.isAddress(collectionOwner)) {
            throw new Error(`Collection owner ${collectionOwner} is not a valid Ethereum address.`);
        }
        // Validate the provided epochs are not negative.
        if (startingEpoch < 0 || endingEpoch < 0) {
            throw new Error(`Epochs cannot be negative`);
        }
        // Validate the starting epoch does not exceed the ending epoch.
        if (startingEpoch > endingEpoch) {
            throw new Error(`Starting epoch ${startingEpoch} cannot exceed ending epoch ${endingEpoch}`);
        }
        // Validate the creator ID for both merkle tree nodes matches.
        if (startingEpochTreeNode.creatorId !== endingEpochTreeNode.creatorId) {
            throw new Error(`Epoch tree nodes should be the same (starting: ${startingEpochTreeNode.creatorId}, ending: ${endingEpochTreeNode.creatorId})`);
        }
        // TODO: Any other validations that should be performed? Unfortunately might require reading on-chain state.
        // Create the merkle proofs for the starting and ending epoch tree nodes.
        const startingMerkleTree = this.createMerkleTree(startingMerkleTreeLeaves);
        const startingEpochMerkleProof = this.getMerkleProof(startingMerkleTree, startingEpochTreeNode);
        const endingMerkleTree = this.createMerkleTree(endingMerkleTreeLeaves);
        const endingEpochMerkleProof = this.getMerkleProof(endingMerkleTree, endingEpochTreeNode);
        return this.splendid.settleCreatorEpochRewardsFromCollection(collectionOwner, collectionIndex, startingEpoch, startingEpochTreeNode, startingEpochMerkleProof, endingEpoch, endingEpochTreeNode, endingEpochMerkleProof);
    }
    async claimCreatorRewards({ creatorId }) {
        // TODO: Validate that creatorId exists? Requires reading on-chain state.
        return this.splendid.claimCreatorRewards(creatorId);
    }
    async claimCuratorRewards({ collectionIndex }) {
        // TODO: Validate that collectionIndx exists? Requires reading on-chain state.
        return this.splendid.claimCuratorRewards(collectionIndex);
    }
    async subscribeToCollection({ collectionOwner, collectionIndex, subscriber, payer, }) {
        // Validate that the collection owner is a valid Ethereum address.
        if (!ethers_1.ethers.utils.isAddress(collectionOwner)) {
            throw new Error(`Collection owner ${collectionOwner} is not a valid Ethereum address.`);
        }
        // Validate that the subscriber is a valid Ethereum address.
        if (!ethers_1.ethers.utils.isAddress(subscriber)) {
            throw new Error(`Subscriber ${subscriber} is not a valid Ethereum address.`);
        }
        // Validate that the payer is a valid Ethereum address.
        if (!ethers_1.ethers.utils.isAddress(payer)) {
            throw new Error(`Payer ${payer} is not a valid Ethereum address.`);
        }
        return this.splendid.subscribeToCollection(collectionOwner, collectionIndex, subscriber, payer);
    }
    async parseEventLog(data, topics) {
        return this.splendidInterface.parseLog({
            topics,
            data,
        });
    }
    createMerkleTreeFromCollectionContent(collectionContent) {
        const leaves = collectionContent.map((content) => {
            // TODO: Verify the leaf hashes match with solidity implementation.
            return this.hashLeafNode(content);
        });
        return this.createMerkleTree(leaves);
    }
    hashLeafNode(node) {
        // TODO: Verify the leaf hashes match with solidity implementation.
        return ethers_1.ethers.utils.hexlify(Buffer.from(ethers_1.ethers.utils.solidityKeccak256(['uint256', `uint224`, `uint32`], [node.creatorId, node.lastUpdatedEpoch, node.lastUpdatedEpoch]).slice(2), 'hex'));
    }
    createMerkleTree(leaves) {
        const merkleTree = new merkletreejs_1.MerkleTree(leaves, keccak256_1.keccak256, { sortPairs: true });
        return merkleTree;
    }
    getMerkleProof(tree, node) {
        const leaf = this.hashLeafNode(node);
        return tree.getHexProof(leaf);
    }
}
exports.Client = Client;
//# sourceMappingURL=splendid-client.js.map