/// <summary>
        ///     Create SLP Token MINT Output Script
        /// </summary>
        /// <param name="tokenId"></param>
        /// <param name="mintBatonVOut"></param>
        /// <param name="mintQuantity"></param>
        /// <returns></returns>
        public static Script.Script CreateMintScript(byte[] tokenId, byte mintBatonVOut, ulong mintQuantity)
        {
            if (tokenId.Length != 32)
            {
                throw new ArgumentException(
                          "Invalid tokenId! Expected 32 bytes, received " + tokenId.Length + " bytes.");
            }
            if (mintBatonVOut == 1)
            {
                throw new ArgumentException(
                          "Invalid mintBatonVOut! Expected 0 (create no baton), or 1-255 (vout), but received 1.");
            }

            // header
            var byteData = GetHeaderBytes();

            /* After Header:
             *  <transaction_type: 'MINT'> (4 bytes, ascii)
             *  <token_id> (32 bytes)
             *  <mint_baton_vout> (0 bytes or 1 byte between 0x02-0xff)
             *  <additional_token_quantity> (8 byte integer)
             */

            byteData = byteData.Concat(ScriptBuilder.GetOpPushForLength(4)).Concat("MINT".Select(x => (byte)x));
            byteData = byteData.Concat(ScriptBuilder.GetOpPushForLength(32)).Concat(tokenId);
            byteData = mintBatonVOut == 0
                ? byteData.Concat(ScriptBuilder.GetOpPushForLength(0, false))
                : byteData.Concat(ScriptBuilder.GetOpPushForLength(1)).Concat(new[] { mintBatonVOut });
            byteData = byteData.Concat(ScriptBuilder.GetOpPushForLength(8)).Concat(BitConverter.GetBytes(mintQuantity));

            return(ScriptBuilder.CreateOutputOpReturn(byteData.ToArray()));
        }
        /// <summary>
        ///     Create SLP Token GENESIS Output Script
        /// </summary>
        /// <param name="ticker"></param>
        /// <param name="name"></param>
        /// <param name="documentUrl"></param>
        /// <param name="documentHash"></param>
        /// <param name="decimals"></param>
        /// <param name="mintBatonVOut"></param>
        /// <param name="mintQuantity"></param>
        /// <returns></returns>
        public static Script.Script CreateGenesisScript(byte[] ticker, byte[] name, byte[] documentUrl, byte[] documentHash,
                                                        byte decimals, byte mintBatonVOut, ulong mintQuantity)
        {
            // header
            var byteData = GetHeaderBytes();

            /* After Header:
             *  <transaction_type: 'GENESIS'> (4 bytes, ascii)
             *  <token_ticker> (0 to ∞ bytes, suggested utf-8)
             *  <token_name> (0 to ∞ bytes, suggested utf-8)
             *  <token_document_url> (0 to ∞ bytes, suggested ascii)
             *  <token_document_hash> (0 bytes or 32 bytes)
             *  <decimals> (1 byte in range 0x00-0x09)
             *  <mint_baton_vout> (0 bytes, or 1 byte in range 0x02-0xff)
             *  <initial_token_mint_quantity> (8 byte integer)
             */

            if (documentHash.Length != 0 && documentHash.Length != 32)
            {
                throw new ArgumentException("Invalid TokenDocumentHash! Expected either 0 or 32 bytes, but received " +
                                            documentHash.Length + " bytes.");
            }

            if (0 < decimals || decimals > 9)
            {
                throw new ArgumentException("Invalid digits after decimal! Expected 0-9 inclusive, but received " +
                                            (int)decimals + ".");
            }
            if (mintBatonVOut == 1)
            {
                throw new ArgumentException(
                          "Invalid mintBatonVOut! Expected 0 (create no baton), or 1-255 (vout), but received 1.");
            }

            byteData = byteData.Concat(ScriptBuilder.GetOpPushForLength(7)).Concat("GENESIS".Select(x => (byte)x));
            byteData = byteData.Concat(ScriptBuilder.GetOpPushForLength((uint)ticker.Length, false)).Concat(ticker);
            byteData = byteData.Concat(ScriptBuilder.GetOpPushForLength((uint)name.Length, false)).Concat(name);
            byteData = byteData.Concat(ScriptBuilder.GetOpPushForLength((uint)documentUrl.Length, false))
                       .Concat(documentUrl);
            byteData = byteData.Concat(ScriptBuilder.GetOpPushForLength((uint)documentHash.Length, false))
                       .Concat(documentHash);
            byteData = byteData.Concat(ScriptBuilder.GetOpPushForLength(1)).Concat(new[] { decimals });
            byteData = mintBatonVOut == 0
                ? byteData.Concat(ScriptBuilder.GetOpPushForLength(0, false))
                : byteData.Concat(ScriptBuilder.GetOpPushForLength(1)).Concat(new[] { mintBatonVOut });
            byteData = byteData.Concat(ScriptBuilder.GetOpPushForLength(8)).Concat(BitConverter.GetBytes(mintQuantity));

            return(ScriptBuilder.CreateOutputOpReturn(byteData.ToArray()));
        }
        public static Script.Script CreateCommitScript(byte[] tokenId, byte[] forBitcoinBlockHash, ulong blockHeight,
                                                       byte[] tokenTransactionMerkleRoot, string tokenTransactionSetUrl)
        {
            // token_id must be 32 bytes
            if (tokenId.Length != 32)
            {
                throw new ArgumentException(
                          "Invalid tokenId! Expected 32 bytes, received " + tokenId.Length + " bytes.");
            }
            // for_bitcoin_block_hash must be 32 bytes
            if (forBitcoinBlockHash.Length != 32)
            {
                throw new ArgumentException("Invalid forBitcoinBlockHash! Expected 32 bytes, received " +
                                            forBitcoinBlockHash.Length + " bytes.");
            }
            // token_ must be 32 bytes
            if (tokenTransactionMerkleRoot.Length != 32)
            {
                throw new ArgumentException("Invalid tokenTransactionMerkleRoot! Expected 32 bytes, received " +
                                            tokenTransactionMerkleRoot.Length + " bytes.");
            }

            // header
            var byteData = GetHeaderBytes();

            /* After Header:
             *  <transaction_type: 'COMMIT'> (6 bytes, ascii)
             *  <token_id> (32 bytes)
             *  <for_bitcoin_block_hash> (32 bytes)
             *  <block_height> (8 byte integer)
             *  <token_txn_set_hash> (32 bytes)
             *  <txn_set_data_url> (0 to ∞ bytes, ascii)
             *  [to be determined]
             */

            byteData = byteData.Concat(ScriptBuilder.GetOpPushForLength(5)).Concat("COMMIT".Select(x => (byte)x));
            byteData = byteData.Concat(ScriptBuilder.GetOpPushForLength(32)).Concat(tokenId);
            byteData = byteData.Concat(ScriptBuilder.GetOpPushForLength(32)).Concat(forBitcoinBlockHash);
            byteData = byteData.Concat(ScriptBuilder.GetOpPushForLength(8)).Concat(BitConverter.GetBytes(blockHeight));
            byteData = byteData
                       .Concat(ScriptBuilder.GetOpPushForLength((ulong)Encoding.ASCII.GetByteCount(tokenTransactionSetUrl),
                                                                false)).Concat(Encoding.ASCII.GetBytes(tokenTransactionSetUrl));

            return(ScriptBuilder.CreateOutputOpReturn(byteData.ToArray()));
        }
        /// <summary>
        ///     Create SLP Token SEND Output Script
        /// </summary>
        /// <param name="tokenId"></param>
        /// <param name="tokenOutputQuantities"></param>
        /// <returns></returns>
        public static Script.Script CreateSendScript(byte[] tokenId, ulong[] tokenOutputQuantities)
        {
            // header
            var byteData = GetHeaderBytes();

            /* After Header:
             * <transaction_type: 'SEND'> (4 bytes, ascii)
             * <token_id> (32 bytes)
             * <token_output_quantity1> (required, 8 byte integer)
             * <token_output_quantity2> (optional, 8 byte integer)
             * ...
             * <token_output_quantity19> (optional, 8 byte integer)
             *
             */
            if (tokenId.Length != 32)
            {
                throw new ArgumentException(
                          "Invalid tokenId! Expected 32 bytes, received " + tokenId.Length + " bytes.");
            }

            if (tokenOutputQuantities.Length < 1 || tokenOutputQuantities.Length > 19)
            {
                throw new ArgumentException("Invalid tokenOutputQuantities! Expected 1-19 outputs, received " +
                                            tokenOutputQuantities.Length + " outputs.");
            }

            byteData = byteData.Concat(ScriptBuilder.GetOpPushForLength(4)).Concat("SEND".Select(x => (byte)x));

            byteData = byteData.Concat(ScriptBuilder.GetOpPushForLength(32)).Concat(tokenId);

            byteData = tokenOutputQuantities.Aggregate(byteData, (current, tokenOutputQuantity) =>
                                                       current.Concat(ScriptBuilder
                                                                      .GetOpPushForLength(8)
                                                                      .Concat(BitConverter.GetBytes(tokenOutputQuantity))));

            return(ScriptBuilder.CreateOutputOpReturn(byteData.ToArray()));
        }