internal Ledger(ContractService contractService, WalletContract contract, OperationCode operation, BitcoinPubKeyAddress utxoReceiver, BitcoinPubKeyAddress tokenReceiver = null, decimal?amount = null, string referenceCode = null)
 {
     _contract         = contract;
     Operation         = operation;
     TokenReceiver     = tokenReceiver;
     TokenReceiverHash = tokenReceiver.ExtractWitnessProgram();
     ReferenceCode     = referenceCode;
     UtxoReceiver      = utxoReceiver;
     if (operation == OperationCode.Issue && tokenReceiver == null)
     {
         TokenReceiverHash = new BitcoinPubKeyAddress(contract.OwnerPublicAddress, contractService.MainNetwork).ExtractWitnessProgram();
     }
     if (amount < 0)
     {
         throw new InvalidOperationException("The amount cannot be negative.");
     }
     if (amount.HasValue)
     {
         Amount = amount.Value;
     }
     else if (operation != OperationCode.Issue)
     {
         throw new ArgumentNullException("Amount cannot be null if the operation is not issuance of the owner address.");
     }
     else
     {
         Amount = contract.TotalSupply;
     }
 }
        public static Ledger ToLedger(this ApiTransaction transaction, WalletContract contract, ContractService service)
        {
            var ledger = new Ledger()
            {
                TxId        = transaction.txid,
                Blockheight = transaction.blockheight,
                Time        = new DateTime(1970, 1, 1).AddSeconds(transaction.blocktime)
            };
            var domain = BitConverter.ToString(Encoding.Default.GetBytes("RMUTSB.AC.TH")).Replace("-", "").ToLower();
            //var domain = "54534357414c4c4554";
            var data = transaction.GetOP_RETURN();

            try
            {
                if (data.StartsWith(domain))
                {
                    return(null);
                }
                var contractNameHex = data.Substring(0, 16);
                if (contract.NameHex == contractNameHex)
                {
                    ledger.Operation = (OperationCode)(ushort.Parse(data.Substring(16, 4), System.Globalization.NumberStyles.HexNumber));
                    var publicAddress = transaction.vin.First(v => !string.IsNullOrEmpty(v.addr)).addr;
                    var publicKey     = new BitcoinPubKeyAddress(publicAddress, service.MainNetwork);
                    ledger.TokenSenderHash      = publicKey.ExtractWitnessProgram();
                    ledger.TokenReceiverHashHex = data.Substring(20, 40);
                    ledger.Amount = BitConverterExtension.ToDecimal(data.Substring(60, 32).StringToByteArray());
                    if (data.Length > 92)
                    {
                        ledger.ReferenceCode = Encoding.Default.GetString(data.Substring(92).StringToByteArray());
                    }
                    return(ledger);
                }
                else
                {
                    return(null);
                }
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
                return(null);
            }
        }
        /// <summary>
        /// Obtain the account according to the public key.
        /// </summary>
        /// <param name="pubKeyAddress">A Bitcoin-compatible public key</param>
        /// <returns>An account according to the given key.</returns>
        public Account GetAccount(BitcoinPubKeyAddress pubKeyAddress)
        {
            var witnessProgram = pubKeyAddress.ExtractWitnessProgram();

            return(GetAccount(witnessProgram));
        }
        /// <summary>
        /// A private method to process the account balance from the ledgers in local data store.
        /// </summary>
        /// <param name="reset">If true, the existing balance will be removed and process again (Optional, default is false)</param>
        /// <returns>None.</returns>
        private void UpdateBalance(bool reset = false)
        {
            var ledgers = ledger.Find(l => l.Status == ProcessStatus.NotProcessed);

            if (reset)
            {
                db.DropCollection($"Account-{contract.NameHex}");
                ledgers = ledger.FindAll();
            }

            foreach (var entry in ledgers)
            {
                switch (entry.Operation)
                {
                case OperationCode.Transfer:
                    var fromAccount = account.FindOne(a => a.WitnessProgram == entry.TokenSenderHash);
                    if (fromAccount == null)
                    {
                        fromAccount = new Account()
                        {
                            WitnessProgram = entry.TokenSenderHash
                        };
                    }
                    else if (entry.Amount < (decimal)Math.Round(Math.Pow(0.1, contract.NoOfDecimal), contract.NoOfDecimal))
                    {
                        entry.Status = ProcessStatus.DustAmount;
                        ledger.Upsert(entry);
                    }
                    else if (fromAccount.Balance >= entry.Amount + TransferFee)
                    {
                        fromAccount.Balance -= entry.Amount + TransferFee;
                        var toAccount = account.FindOne(a => a.WitnessProgram == entry.TokenReceiverHash);
                        if (toAccount == null)
                        {
                            toAccount = new Account()
                            {
                                WitnessProgram = entry.TokenReceiverHash
                            };
                        }
                        toAccount.Balance      += entry.Amount;
                        fromAccount.Blockheight = entry.Blockheight;
                        toAccount.Blockheight   = entry.Blockheight;
                        account.Upsert(fromAccount);
                        account.Upsert(toAccount);
                        var ownerAccount = account.FindOne(a => a.WitnessProgram == ownerAddress.ExtractWitnessProgram());
                        if (ownerAccount == null)
                        {
                            ownerAccount = new Account()
                            {
                                WitnessProgram = entry.TokenReceiverHash
                            };
                        }
                        ownerAccount.Balance    += TransferFee;
                        ownerAccount.Blockheight = entry.Blockheight;
                        account.Upsert(ownerAccount);
                        account.EnsureIndex(a => a.WitnessProgram);
                        entry.Status = ProcessStatus.Processed;
                        ledger.Upsert(entry);
                    }
                    else
                    {
                        entry.Status = ProcessStatus.FailedIgnore;
                        ledger.Upsert(entry);
                    }
                    break;

                case OperationCode.Burn:
                    entry.Status = ProcessStatus.FeatureNotAvailable;
                    ledger.Upsert(entry);
                    break;

                case OperationCode.Interest:
                    entry.Status = ProcessStatus.FeatureNotAvailable;
                    ledger.Upsert(entry);
                    break;

                case OperationCode.Issue:
                    if (entry.TokenSenderHash.QuickCompare(ownerAddress.ExtractWitnessProgram()))
                    {
                        var ownerAccount = account.FindOne(a => a.WitnessProgram == ownerAddress.ExtractWitnessProgram());
                        if (ownerAccount == null)
                        {
                            ownerAccount = new Account()
                            {
                                WitnessProgram = entry.TokenReceiverHash
                            };
                        }
                        ownerAccount.Balance    += entry.Amount;
                        ownerAccount.Blockheight = entry.Blockheight;
                        account.Upsert(ownerAccount);
                        account.EnsureIndex(a => a.WitnessProgram);
                        entry.Status = ProcessStatus.Processed;
                    }
                    else
                    {
                        entry.Status = ProcessStatus.FailedIgnore;
                    }
                    ledger.Upsert(entry);
                    break;

                default:
                    entry.Status = ProcessStatus.NotDefined;
                    ledger.Upsert(entry);
                    break;
                }
            }
            account.EnsureIndex(a => a.WitnessProgram);
        }