Exemplo n.º 1
0
        // Note: This current implementation requires NeoScan running at port 4000
        public override List <UnspentEntry> GetClaimable(UInt160 hash, out decimal amount)
        {
            var url  = this.neoscanUrl + "/api/main_net/v1/get_claimable/" + hash.ToAddress();
            var json = RequestUtils.GetWebRequest(url);

            var root   = LunarLabs.Parser.JSON.JSONReader.ReadFromString(json);
            var result = new List <UnspentEntry>();

            amount = root.GetDecimal("unclaimed");

            root = root["claimable"];

            foreach (var child in root.Children)
            {
                var txid  = child.GetString("txid");
                var index = child.GetUInt32("n");
                var value = child.GetDecimal("unclaimed");

                result.Add(new UnspentEntry()
                {
                    hash = new UInt256(LuxUtils.ReverseHex(txid).HexToBytes()), index = index, value = value
                });
            }

            return(result);
        }
Exemplo n.º 2
0
        // Note: This current implementation requires NeoScan running at port 4000
        public override Dictionary <string, List <UnspentEntry> > GetUnspent(UInt160 hash)
        {
            var url  = this.neoscanUrl + "/api/main_net/v1/get_balance/" + hash.ToAddress();
            var json = RequestUtils.GetWebRequest(url);

            var root     = LunarLabs.Parser.JSON.JSONReader.ReadFromString(json);
            var unspents = new Dictionary <string, List <UnspentEntry> >();

            root = root["balance"];

            foreach (var child in root.Children)
            {
                var symbol = child.GetString("asset");

                List <UnspentEntry> list = new List <UnspentEntry>();
                unspents[symbol] = list;

                var unspentNode = child.GetNode("unspent");
                foreach (var entry in unspentNode.Children)
                {
                    var txid = entry.GetString("txid");
                    var val  = entry.GetDecimal("value");
                    var temp = new UnspentEntry()
                    {
                        hash = new UInt256(LuxUtils.ReverseHex(txid).HexToBytes()), value = val, index = entry.GetUInt32("n")
                    };
                    list.Add(temp);
                }
            }

            return(unspents);
        }
Exemplo n.º 3
0
        public static string SymbolFromAssetID(byte[] assetID)
        {
            var str    = assetID.ByteToHex();
            var result = SymbolFromAssetID(str);

            if (result == null)
            {
                result = SymbolFromAssetID(LuxUtils.ReverseHex(str));
            }

            return(result);
        }
Exemplo n.º 4
0
        public static byte[] GetAssetID(string symbol)
        {
            var info = GetAssetsInfo();

            foreach (var entry in info)
            {
                if (entry.Key == symbol)
                {
                    return(LuxUtils.ReverseHex(entry.Value).HexToBytes());
                }
            }

            return(null);
        }
Exemplo n.º 5
0
        protected override InteropTransaction PullPlatformTransaction(string platformName, string chainName, Hash hash)
        {
            logger.Debug($"{platformName} pull tx: {hash}");
            InteropTransaction tx = Read <InteropTransaction>(platformName, chainName, hash, StorageConst.Transaction);

            if (tx != null && tx.Hash != null)
            {
                logger.Debug($"Found tx {hash} in oracle storage");
                return(tx);
            }

            switch (platformName)
            {
            case NeoWallet.NeoPlatform:
                NeoTx   neoTx;
                UInt256 uHash = new UInt256(LuxUtils.ReverseHex(hash.ToString()).HexToBytes());
                neoTx = _cli.NeoAPI.GetTransaction(uHash);
                var coldStorage = _cli.Settings.Oracle.SwapColdStorageNeo;
                tx = NeoInterop.MakeInteropTx(logger, neoTx, _cli.NeoAPI,
                                              _cli.TokenSwapper.SwapAddresses[SwapPlatformChain.Neo], coldStorage);
                break;

            case EthereumWallet.EthereumPlatform:
            {
                var txRcpt = _cli.EthAPI.GetTransactionReceipt(hash.ToString());
                tx = EthereumInterop.MakeInteropTx(_cli.Nexus, logger, txRcpt, _cli.EthAPI,
                                                   _cli.TokenSwapper.SwapAddresses[SwapPlatformChain.Ethereum]);
                break;
            }

            case BSCWallet.BSCPlatform:
            {
                var txRcpt = _cli.BscAPI.GetTransactionReceipt(hash.ToString());
                tx = BSCInterop.MakeInteropTx(_cli.Nexus, logger, txRcpt, _cli.BscAPI,
                                              _cli.TokenSwapper.SwapAddresses[SwapPlatformChain.BSC]);
                break;
            }

            default:
                throw new OracleException("Uknown oracle platform: " + platformName);
            }

            if (!Persist <InteropTransaction>(platformName, chainName, tx.Hash, StorageConst.Transaction, tx))
            {
                logger.Error($"Oracle transaction { hash } on platform { platformName } updated!");
            }

            return(tx);
        }
Exemplo n.º 6
0
        public void GenerateInputsOutputs(UInt160 key, string symbol, IEnumerable <Transaction.Output> targets, out List <Transaction.Input> inputs, out List <Transaction.Output> outputs, decimal system_fee = 0)
        {
            var info          = GetAssetsInfo();
            var targetAssetID = LuxUtils.ReverseHex(info[symbol]).HexToBytes();

            if (targets != null)
            {
                foreach (var t in targets)
                {
                    if (t.assetID == null)
                    {
                        t.assetID = targetAssetID;
                    }
                }
            }
            // else  Console.WriteLine("ASSETID target already existed: " + symbol);
            GenerateInputsOutputs(key, targets, out inputs, out outputs, system_fee);
        }
Exemplo n.º 7
0
        private string ConvertArray(string s)
        {
            if (DebuggerUtils.IsHex(s))
            {
                var bytes = s.HexToBytes();
                s = DebuggerUtils.BytesToString(bytes);
            }
            else if (DebuggerUtils.IsValidWallet(s))
            {
                var scriptHash = s.AddressToScriptHash();
                s = DebuggerUtils.BytesToString(LuxUtils.ReverseHex(scriptHash.ByteToHex()).HexToBytes());
            }
            else
            {
                return(null);
            }

            return(s);
        }
Exemplo n.º 8
0
        protected override InteropBlock PullPlatformBlock(string platformName, string chainName, Hash hash, NativeBigInt height = new NativeBigInt())
        {
            if (hash == null && height == null)
            {
                throw new OracleException($"Fetching block not possible without hash or height");
            }

            InteropBlock block = Read <InteropBlock>(platformName, chainName, hash, StorageConst.Block);

            if (height == null && block.Hash != null && block.Hash != Hash.Null)
            {
                return(block);
            }

            Tuple <InteropBlock, InteropTransaction[]> interopTuple;

            switch (platformName)
            {
            case NeoWallet.NeoPlatform:

                NeoBlock neoBlock;

                if (height == 0)
                {
                    neoBlock = _cli.NeoAPI.GetBlock(new UInt256(LuxUtils.ReverseHex(hash.ToString()).HexToBytes()));
                }
                else
                {
                    neoBlock = _cli.NeoAPI.GetBlock(height);
                }

                if (neoBlock == null)
                {
                    throw new OracleException($"Neo block is null");
                }

                interopTuple = NeoInterop.MakeInteropBlock(logger, neoBlock, _cli.NeoAPI, _cli.TokenSwapper.SwapAddresses[platformName]);
                break;

            case EthereumWallet.EthereumPlatform:

                //BlockWithTransactions ethBlock;
                //if (height == 0)
                //{
                //    //TODO MakeInteropBlock for a full block not done yet
                //    //ethBlock = _cli.EthAPI.GetBlock(hash.ToString());
                //    //interopTuple = EthereumInterop.MakeInteropBlock(logger, ethBlock, _cli.EthAPI, _cli.TokenSwapper.swapAddress);
                //}
                //else
                //{
                //}

                var hashes = _cli.Nexus.GetPlatformTokenHashes(EthereumWallet.EthereumPlatform, _cli.Nexus.RootStorage)
                             .Select(x => x.ToString().Substring(0, 40)).ToArray();

                interopTuple = EthereumInterop.MakeInteropBlock(_cli.Nexus, logger, _cli.EthAPI, height,
                                                                hashes, _cli.Settings.Oracle.EthConfirmations, _cli.TokenSwapper.SwapAddresses[platformName]);
                break;

            default:
                throw new OracleException("Uknown oracle platform: " + platformName);
            }

            if (interopTuple.Item1.Hash != Hash.Null)
            {
                var persisted = Persist <InteropBlock>(platformName, chainName, interopTuple.Item1.Hash, StorageConst.Block,
                                                       interopTuple.Item1);

                if (persisted)
                {
                    var transactions = interopTuple.Item2;

                    foreach (var tx in transactions)
                    {
                        var txPersisted = Persist <InteropTransaction>(platformName, chainName, tx.Hash, StorageConst.Transaction, tx);
                    }
                }
                else
                {
                    logger.Error($"Persisting oracle block { interopTuple.Item1.Hash } on platform { platformName } failed!");
                }
            }

            return(interopTuple.Item1);
        }
Exemplo n.º 9
0
        public Transaction GetTransaction(string hash)
        {
            var val = new UInt256(LuxUtils.ReverseHex(hash).HexToBytes());

            return(GetTransaction(val));
        }
Exemplo n.º 10
0
        public void GenerateInputsOutputs(UInt160 from_script_hash, IEnumerable <Transaction.Output> targets, out List <Transaction.Input> inputs, out List <Transaction.Output> outputs, decimal system_fee = 0)
        {
            var unspent = GetUnspent(from_script_hash);

            // filter any asset lists with zero unspent inputs
            unspent = unspent.Where(pair => pair.Value.Count > 0).ToDictionary(pair => pair.Key, pair => pair.Value);

            inputs  = new List <Transaction.Input>();
            outputs = new List <Transaction.Output>();

            var from_address = from_script_hash.ToAddress();
            var info         = GetAssetsInfo();

            // dummy tx to self
            if (targets == null)
            {
                string assetName     = "GAS";
                string assetID       = info[assetName];
                var    targetAssetID = LuxUtils.ReverseHex(assetID).HexToBytes();
                if (!unspent.ContainsKey(assetName))
                {
                    throw new NeoException($"Not enough {assetName} in address {from_address}");
                }

                var     src      = unspent[assetName][0];
                decimal selected = src.value;
                // Console.WriteLine("SENDING " + selected + " GAS to source");

                inputs.Add(new Transaction.Input()
                {
                    prevHash  = src.hash,
                    prevIndex = src.index,
                });

                outputs.Add(new Transaction.Output()
                {
                    assetID    = targetAssetID,
                    scriptHash = from_script_hash,
                    value      = selected
                });
                return;
            }

            foreach (var target in targets)
            {
                if (target.scriptHash.Equals(from_script_hash))
                {
                    throw new NeoException("Target can't be same as input");
                }
            }

            bool done_fee = false;

            foreach (var asset in info)
            {
                string assetName = asset.Key;
                string assetID   = asset.Value;

                if (!unspent.ContainsKey(assetName))
                {
                    continue;
                }

                var targetAssetID = LuxUtils.ReverseHex(assetID).HexToBytes();

                var thistargets = targets.Where(o => o.assetID.SequenceEqual(targetAssetID));

                decimal cost = -1;
                foreach (var target in thistargets)
                {
                    if (target.assetID.SequenceEqual(targetAssetID))
                    {
                        if (cost < 0)
                        {
                            cost = 0;
                        }
                        cost += target.value;
                    }
                }

                // incorporate fee in GAS utxo, if sending GAS
                bool sendfee = false;
                if (system_fee > 0 && assetName == "GAS")
                {
                    done_fee = true;
                    sendfee  = true;
                    if (cost < 0)
                    {
                        cost = 0;
                    }
                    cost += system_fee;
                }

                if (cost == -1)
                {
                    continue;
                }

                var     sources  = unspent[assetName].OrderBy(src => src.value);
                decimal selected = 0;

                // >= cost ou > cost??
                foreach (var src in sources)
                {
                    if (selected >= cost && inputs.Count > 0)
                    {
                        break;
                    }

                    selected += src.value;
                    inputs.Add(new Transaction.Input()
                    {
                        prevHash  = src.hash,
                        prevIndex = src.index,
                    });
                    // Console.WriteLine("ADD inp " + src.ToString());
                }

                if (selected < cost)
                {
                    throw new NeoException($"Not enough {assetName} in address {from_address}");
                }

                if (cost > 0)
                {
                    foreach (var target in thistargets)
                    {
                        outputs.Add(target);
                    }
                }

                if (selected > cost || cost == 0 || sendfee)  /// is sendfee needed? yes if selected == cost
                {
                    outputs.Add(new Transaction.Output()
                    {
                        assetID    = targetAssetID,
                        scriptHash = from_script_hash,
                        value      = selected - cost
                    });
                }
            }

            /*
             *          if (system_fee > 0 && !done_fee && false)
             *          {
             *              var gasID = LuxUtils.ReverseHex(info["GAS"]).HexToBytes();
             *              var gassources = unspent["GAS"];
             *              // foreach (var src in gassources)
             *              //     Console.WriteLine("SRC: " + src.ToString());
             *              decimal feeselected = 0;
             *              foreach (var src in gassources)
             *                  if (feeselected <= system_fee)
             *                  {
             *                      inputs.Add(new Transaction.Input()
             *                      {
             *                          prevHash = src.hash,
             *                          prevIndex = src.index,
             *                      });
             *                      feeselected += src.value;
             *                      Console.WriteLine("add input  " + feeselected);
             *                      break;
             *                  }
             *              outputs.Add(new Transaction.Output()
             *              {
             *                  assetID = gasID,
             *                  scriptHash = from_script_hash,
             *                  value = feeselected - system_fee
             *              });
             *          }
             *
             *          foreach (var i in inputs)
             *              Console.WriteLine("INPUT " + i);
             *          foreach (var i in output)
             *              Console.WriteLine("OUTPUT " + i);
             *          // chaining
             *          if (lastTransactions.ContainsKey(from_address))
             *          {
             *              var lastTx = lastTransactions[from_address];
             *              uint index = 0;
             *              if (lastTx.outputs != null)
             *                  foreach (var output in lastTx.outputs)
             *                  {
             *                      if (output.assetID.SequenceEqual(targetAssetID) && output.scriptHash.Equals(from_script_hash))
             *                      {
             *                          selected += output.value;
             *                          var input = new Transaction.Input()
             *                          {
             *                              prevHash = lastTx.Hash,
             *                              prevIndex = index,
             *                          };
             *                          inputs.Add(input);
             *                          break;
             *                      }
             *                      index++;
             *                  }
             *          }
             */
        }
Exemplo n.º 11
0
 public static string GetStringFromScriptHash(byte[] hash)
 {
     return(LuxUtils.ReverseHex(hash.ToHexString()));
 }
Exemplo n.º 12
0
        static void Main(string[] args)
        {
            uint startBlock = 2298101;
            uint endblock   = 2299480;

            string transactions_output_filename = "phantasma_transactions.csv";
            string total_output_filename        = "phantasma_totals.csv";

            var soul_scripthash = new UInt160(LuxUtils.ReverseHex("4b4f63919b9ecfd2483f0c72ff46ed31b5bbb7a4").HexToBytes());

            Console.WriteLine("Generating sale audit. This can take several minutes, please be patient.");

            var start_time = Environment.TickCount;

            var api = new LocalRPCNode(10332, "http://neoscan.io");

            var blockCount = api.GetBlockHeight();

            var balances = new Dictionary <string, decimal>();

            var total_output       = new List <string>();
            var transaction_output = new List <string>();

            var extra_refunds = new Dictionary <string, decimal>();

            transaction_output.Add($"Tx type,Tx hash,Address,NEO sent");
            for (uint height = startBlock; height <= endblock; height++)
            {
                var block = api.GetBlock(height);

                foreach (var tx in block.transactions)
                {
                    foreach (var output in tx.outputs)
                    {
                        if (output.scriptHash == soul_scripthash)
                        {
                            Transaction other = null;
                            foreach (var input in tx.inputs)
                            {
                                other = api.GetTransaction(input.prevHash);
                                UInt160 src = other.outputs[input.prevIndex].scriptHash;

                                var src_addr = src.ToAddress();
                                var ss       = tx.type.ToString().Replace("Transaction", "");

                                var balance = balances.ContainsKey(src_addr) ? balances[src_addr] : 0;

                                if (tx.type == TransactionType.InvocationTransaction)
                                {
                                    balance += output.value;
                                }
                                else
                                {
                                    var extra = extra_refunds.ContainsKey(src_addr) ? extra_refunds[src_addr] : 0;
                                    extra += output.value;
                                    extra_refunds[src_addr] = extra;
                                }

                                balances[src_addr] = balance;

                                transaction_output.Add($"{ss},{tx.Hash},{src_addr},{output.value}");

                                break;
                            }


                            break;
                        }
                    }
                }
            }

            total_output.Add($"Address,SOUL received,NEO sent,NEO to be refunded");
            foreach (KeyValuePair <string, decimal> entry in balances.OrderBy(x => x.Value))
            {
                var total_sent    = entry.Value;
                var refund_amount = entry.Value > 10 ? entry.Value - 10 : 0;
                var token_amount  = (entry.Value > 10 ? 10 : entry.Value) * 273;

                if (extra_refunds.ContainsKey(entry.Key))
                {
                    var refund_value = extra_refunds[entry.Key];
                    refund_amount += refund_value;
                    total_sent    += refund_value;
                }

                total_output.Add($"{entry.Key},{token_amount},{total_sent},{refund_amount}");
            }

            File.WriteAllLines(total_output_filename, total_output.ToArray());
            File.WriteAllLines(transactions_output_filename, transaction_output.ToArray());

            var total_blocks = (endblock - startBlock) + 1;

            var end_time = Environment.TickCount;
            var delta    = (end_time - start_time) / 1000;

            Console.WriteLine("Finished in " + delta + " seconds, loaded " + total_blocks + " blocks");
        }
Exemplo n.º 13
0
        public ApplicationLog[] GetApplicationLog(string hash)
        {
            var val = new UInt256(LuxUtils.ReverseHex(hash).HexToBytes());

            return(GetApplicationLog(val));
        }
Exemplo n.º 14
0
        protected override InteropBlock PullPlatformBlock(string platformName, string chainName, Hash hash, NativeBigInt height = new NativeBigInt())
        {
            if (hash == null && height == null)
            {
                throw new OracleException($"Fetching block not possible without hash or height");
            }

            InteropBlock block = Read <InteropBlock>(platformName, chainName, hash, StorageConst.Block);

            if (height == null && block.Hash != null && block.Hash != Hash.Null)
            {
                return(block);
            }

            Tuple <InteropBlock, InteropTransaction[]> interopTuple;

            switch (platformName)
            {
            case NeoWallet.NeoPlatform:

                NeoBlock neoBlock;

                if (height == 0)
                {
                    neoBlock = _cli.NeoAPI.GetBlock(new UInt256(LuxUtils.ReverseHex(hash.ToString()).HexToBytes()));
                }
                else
                {
                    neoBlock = _cli.NeoAPI.GetBlock(height);
                }

                if (neoBlock == null)
                {
                    throw new OracleException($"Neo block is null");
                }

                var coldStorage = _cli.Settings.Oracle.SwapColdStorageNeo;
                interopTuple = NeoInterop.MakeInteropBlock(logger, neoBlock, _cli.NeoAPI,
                                                           _cli.TokenSwapper.SwapAddresses[SwapPlatformChain.Neo], coldStorage);
                break;

            case EthereumWallet.EthereumPlatform:

            {
                var hashes = _cli.Nexus.GetPlatformTokenHashes(EthereumWallet.EthereumPlatform, _cli.Nexus.RootStorage)
                             .Select(x => x.ToString().Substring(0, 40)).ToArray();

                interopTuple = EthereumInterop.MakeInteropBlock(_cli.Nexus, logger, _cli.EthAPI, height,
                                                                hashes, _cli.Settings.Oracle.EthConfirmations, _cli.TokenSwapper.SwapAddresses[SwapPlatformChain.Ethereum]);
                break;
            }

            case BSCWallet.BSCPlatform:
            {
                var hashes = _cli.Nexus.GetPlatformTokenHashes(BSCWallet.BSCPlatform, _cli.Nexus.RootStorage)
                             .Select(x => x.ToString().Substring(0, 40)).ToArray();

                interopTuple = BSCInterop.MakeInteropBlock(_cli.Nexus, logger, _cli.BscAPI, height,
                                                           hashes, _cli.Settings.Oracle.EthConfirmations, _cli.TokenSwapper.SwapAddresses[SwapPlatformChain.BSC]);
                break;
            }


            default:
                throw new OracleException("Uknown oracle platform: " + platformName);
            }

            if (interopTuple.Item1.Hash != Hash.Null)
            {
                var initialStore = Persist <InteropBlock>(platformName, chainName, interopTuple.Item1.Hash, StorageConst.Block,
                                                          interopTuple.Item1);
                var transactions = interopTuple.Item2;

                if (!initialStore)
                {
                    logger.Debug($"Oracle block { interopTuple.Item1.Hash } on platform { platformName } updated!");
                }

                foreach (var tx in transactions)
                {
                    var txInitialStore = Persist <InteropTransaction>(platformName, chainName, tx.Hash, StorageConst.Transaction, tx);
                    if (!txInitialStore)
                    {
                        logger.Debug($"Oracle block { interopTuple.Item1.Hash } on platform { platformName } updated!");
                    }
                }
            }

            return(interopTuple.Item1);
        }
Exemplo n.º 15
0
        private bool InitInvoke()
        {
            var key = paramsList.Text;
            var f   = _abi.functions[key];

            DebugParameters = new DebugParameters();

            //Get the private key used
            DebugParameters.PrivateKey = privateKeyInput.Text;

            //Get the witness mode
            CheckWitnessMode witnessMode;
            var ws = witnessComboBox.SelectedItem.ToString().Replace(" ", "");

            if (!Enum.TryParse <CheckWitnessMode>(ws, out witnessMode))
            {
                return(false);
            }
            DebugParameters.WitnessMode = witnessMode;

            //Get the trigger type
            TriggerType type;
            var         ts = triggerComboBox.SelectedItem.ToString().Replace(" ", "");

            if (!Enum.TryParse <TriggerType>(ts, out type))
            {
                return(false);
            }
            DebugParameters.TriggerType = type;

            var HasRawScript = RawScriptText.Text.Length != 0;

            //Get the arguments list
            if (!HasRawScript)
            {
                var argList = "";
                if (f.inputs != null)
                {
                    int index = 0;
                    foreach (var p in f.inputs)
                    {
                        var temp = ($"{key}_{f.name}").ToLower();
                        var name = inputGrid.Rows[index].Cells[0].Value;

                        object val;

                        // detect placeholder
                        if (inputGrid.Rows[index].Cells[1].Style.ForeColor == Color.Gray)
                        {
                            val = "";
                        }
                        else
                        {
                            val = ReadCellVal(index, 1);
                        }

                        if (val == null)
                        {
                            val = ""; // temporary hack, necessary to avoid VM crash
                        }

                        if (val != null && !val.Equals(""))
                        {
                            var param_key = (currentContractName + "_" + f.name + "_" + p.name).ToLower();
                            //Add our default running parameters for next time
                            DebugParameters.DefaultParams[param_key] = val.ToString();
                        }

                        if (index > 0)
                        {
                            argList += ",";
                        }

                        if (p.type == Emulator.Type.Array || p.type == Emulator.Type.ByteArray)
                        {
                            var s = val.ToString();

                            if (s.StartsWith("[") && s.EndsWith("]"))
                            {
                                val = s;
                            }
                            else
                            if (s.StartsWith("\"") && s.EndsWith("\""))
                            {
                                s   = s.Substring(1, s.Length - 2);
                                val = ConvertArray(s);

                                if (val == null && p.type == Emulator.Type.ByteArray)
                                {
                                    val = DebuggerUtils.BytesToString(LuxUtils.ReverseHex(LuxUtils.ByteToHex(System.Text.Encoding.UTF8.GetBytes(s))).HexToBytes());
                                }
                                else
                                {
                                    if (val == null)
                                    {
                                        ShowArgumentError(f, index, val);
                                        return(false);
                                    }
                                }
                            }
                            else
                            {
                                ShowArgumentError(f, index, val);
                                return(false);
                            }

                            var items = JSONReader.ReadFromString(val.ToString());

                            val = "";
                            int itemIndex = 0;
                            foreach (var item in items)
                            {
                                if (itemIndex > 0)
                                {
                                    val += ",";
                                }

                                if (item.Kind == LunarParser.NodeKind.String)
                                {
                                    var vv = ConvertArray(item.Value);
                                    if (vv != null)
                                    {
                                        val += vv;
                                    }
                                    else
                                    {
                                        val += "\"" + item.Value + "\"";
                                    }
                                }
                                else
                                {
                                    val += item.Value;
                                }

                                itemIndex++;
                            }

                            val = $"[{val}]";
                        }
                        else
                        {
                            switch (p.type)
                            {
                            case Emulator.Type.String:
                            {
                                var s = val.ToString();
                                if (!s.StartsWith("\"") || !s.EndsWith("\""))
                                {
                                    ShowArgumentError(f, index, val);
                                    return(false);
                                }

                                break;
                            }

                            case Emulator.Type.Integer:
                            {
                                BigInteger n;
                                var        s = val.ToString();
                                if (string.IsNullOrEmpty(s) || !BigInteger.TryParse(s, out n))
                                {
                                    ShowArgumentError(f, index, val);
                                    ResetTabs();
                                    return(false);
                                }
                                break;
                            }

                            case Emulator.Type.Boolean:
                            {
                                switch (val.ToString().ToLower())
                                {
                                case "true": val = true; break;

                                case "false": val = false; break;

                                default:
                                {
                                    ShowArgumentError(f, index, val);
                                    ResetTabs();
                                    return(false);
                                }
                                }
                                break;
                            }
                            }
                        }

                        argList += val;
                        index++;
                    }
                }
                if (key != _abi.entryPoint.name)
                {
                    if (f.inputs == null || f.inputs.Count == 0)
                    {
                        argList = "[]";
                    }
                    var operation = Char.ToLowerInvariant(key[0]) + key.Substring(1);
                    argList = $"\"{operation}\", [{argList}]";
                }

                //Set the arguments list
                try
                {
                    DebugParameters.ArgList = DebuggerUtils.GetArgsListAsNode(argList);
                }
                catch
                {
                    MessageBox.Show("Error parsing input!");
                    ResetTabs();
                    return(false);
                }
            }

            if (assetComboBox.SelectedIndex > 0)
            {
                foreach (var entry in Asset.Entries)
                {
                    if (entry.name == assetComboBox.SelectedItem.ToString())
                    {
                        BigInteger amount;
                        BigInteger.TryParse(assetAmount.Text, out amount);
                        if (amount > 0)
                        {
                            lastSentSymbol = entry.name;
                            lastSentAmount = assetAmount.Text;

                            amount *= Asset.Decimals; // fix decimals

                            //Add the transaction info
                            DebugParameters.Transaction.Add(entry.id, amount);
                        }
                        else
                        {
                            MessageBox.Show(entry.name + " amount must be greater than zero");
                            return(false);
                        }

                        break;
                    }
                }
            }

            uint timestamp;

            if (!uint.TryParse(timestampBox.Text, out timestamp))
            {
                MessageBox.Show("Invalid timestamp");
                return(false);
            }
            else
            {
                DebugParameters.Timestamp = timestamp;
            }

            DebugParameters.RawScript = HasRawScript ? RawScriptText.Text.HexToBytes() : null;

            return(true);
        }
Exemplo n.º 16
0
        static void Main(string[] pargs)
        {
            //var api = new LocalRPCNode(10332, "http://neoscan.io");
            var api = new RemoteRPCNode(10332, "http://neoscan.io");

            {
                Console.WriteLine("amount " + new BigInteger("00943577".HexToBytes()));
                Console.WriteLine("to " + new UInt160("fc3f33cb3e2d79f82fadef5f407ac1576d304bc1".HexToBytes()).ToAddress());
                Console.WriteLine("from " + new UInt160("a30cadcc858aa4b89d9db098ef154c5e1ab74464".HexToBytes()).ToAddress());

                return;
            }

            {
                var snap_data = File.ReadAllLines("souls_snap.csv");
                var snap      = Snapshot.Import(snap_data);

                var avm_script = File.ReadAllBytes(@"D:\code\Crypto\PhantasmaNeo\PhantasmaContract\bin\Debug\PhantasmaContract.avm");
                var soul_token = api.GetToken("SOUL");

                var lines = new List <string>();

                uint ico_war_time   = 1527465600;
                uint ico_start_time = 1527379200;

                Console.WriteLine("Block,Stage,Tx Hash,Address,Action,NEO sent,NEO refund");
                snap.Execute(soul_token, avm_script, vm =>
                {
                    var storage = vm.GetStorage(soul_token);
                    var block   = vm.currentBlock;

                    int stage = (block.Timestamp < ico_war_time) ? ((block.Timestamp < ico_start_time) ? 0 : 1) : 2;

                    //var bytes = storage.Get("totalSupply");
                    //var n = new BigInteger(bytes);

                    foreach (var tx in block.transactions)
                    {
                        UInt160 src = null;

                        /*foreach (var input in tx.inputs)
                         * {
                         *  var input_tx = snap.GetTransaction(input.prevHash);
                         *  if (input_tx == null)
                         *  {
                         *      input_tx = api.GetTransaction(input.prevHash);
                         *  }
                         *  var output = input_tx.outputs[input.prevIndex];
                         *  src = output.scriptHash;
                         * }
                         *
                         * if (src == null)
                         * {
                         *  continue;
                         * }*/

                        decimal neo_sent = 0;
                        foreach (var output in tx.outputs)
                        {
                            if (output.scriptHash == soul_token.ScriptHash)
                            {
                                neo_sent += output.value;
                            }
                        }

                        var src_address = src != null ? src.ToAddress(): "???";

                        if (tx.Hash.ToString() == "0xc39fb2304f721382a40142ed8af9d4470850f39fabeef9bb8bad892a6b63f4d6")
                        {
                            src_address += "";
                        }

                        if (tx.type == TransactionType.ContractTransaction)
                        {
                            decimal refund = 0;
                            foreach (var output in tx.outputs)
                            {
                                if (output.scriptHash == soul_token.ScriptHash)
                                {
                                    refund += output.value;
                                }
                            }
                            Console.WriteLine($"Block #{block.Height},{stage},{tx.Hash},{src_address},contract,{neo_sent},{refund}");
                        }
                        else
                        {
                            var notifications = vm.GetNotifications(tx);

                            if (notifications != null)
                            {
                                foreach (var entry in notifications)
                                {
                                    decimal refund = 0;
                                    if (entry.Name == "refund")
                                    {
                                        var bytes = (byte[])entry.Args[1];
                                        var n     = new BigInteger(bytes);
                                        refund    = (decimal)(n / 100000000);
                                    }
                                    Console.WriteLine($"Block #{block.Height},{stage},{entry.Hash},{src_address},{entry.Name},{neo_sent},{refund}");
                                }
                            }
                        }
                    }
                });

                return;
            }

            uint roundBlock = 2320640;
            uint startBlock = 2313827;
            uint endBlock   = 2320681;

            {
                var snap   = new Snapshot(api.GetToken("SOUL"), startBlock, endBlock);
                var export = snap.Export();
                File.WriteAllLines("souls_snap.csv", export.ToArray());
                return;
            }

            //startBlock = roundBlock;

            var soul_hash     = LuxUtils.ReverseHex("ed07cffad18f1308db51920d99a2af60ac66a7b3").HexToBytes();
            var soul_hash_int = new UInt160(soul_hash);

            var startT = Environment.TickCount;

            uint maxblock = 0;

            var blockCount = api.GetBlockHeight();

            var soul_balances = new Dictionary <UInt160, BigInteger>();
            var bought        = new Dictionary <UInt160, decimal>();

            BigInteger max_supply      = 91136510; // total token amount
            BigInteger team_supply     = 14500000; // team token amount
            BigInteger advisor_supply  = 5500000;  // advisor token amount
            BigInteger platform_supply = 15000000; // company token amount
            BigInteger presale_supply  = 43503435; // presale token amount

            BigInteger total_supply = team_supply + advisor_supply + presale_supply + platform_supply;

            var whitelist = new HashSet <UInt160>();

            var maybe    = new HashSet <UInt160>();
            var sure     = new HashSet <UInt160>();
            var expected = new Dictionary <UInt160, decimal>();

            var txlist = new List <string>();

            for (uint height = startBlock; height <= endBlock; height++)
            {
                var block = api.GetBlock(height);

                //Console.WriteLine(height + " " + block.Timestamp.ToString());

                foreach (var tx in block.transactions)
                {
                    if (tx.type == TransactionType.ContractTransaction)
                    {
                        foreach (var output in tx.outputs)
                        {
                            if (output.scriptHash == soul_hash_int)
                            {
                                /*foreach (var input in tx.inputs)
                                 * {
                                 *  var input_tx = api.GetTransaction(input.prevHash);
                                 *  var outp = input_tx.outputs[input.prevIndex];
                                 *  var sender = outp.scriptHash;
                                 *  Console.WriteLine($"refund,{tx.Hash},{sender.ToAddress()},{output.value}");
                                 *  break;
                                 * }*/
                            }
                        }

                        continue;
                    }

                    if (tx.type != TransactionType.InvocationTransaction)
                    {
                        continue;
                    }

                    List <AVMInstruction> ops;
                    try
                    {
                        ops = NeoTools.Disassemble(tx.script);
                    }
                    catch
                    {
                        continue;
                    }

                    if (tx.Hash.ToString() == "0xcf530159dc7fa7d0ea38ca210b479b04da20039b3b6639cf4c06bf528d415339")
                    {
                        tx.gas += 0;
                    }

                    for (int i = 0; i < ops.Count; i++)
                    {
                        var op = ops[i];

                        if (op.opcode == OpCode.APPCALL && op.data.SequenceEqual(soul_hash))
                        {
                            var engine = new ExecutionEngine(null);
                            engine.LoadScript(tx.script);
                            engine.Execute(null);

                            var operation = engine.EvaluationStack.Peek().GetString();
                            var args      = ((IEnumerable <StackItem>)engine.EvaluationStack.Peek(1)).ToList();

                            var witnesses = new HashSet <UInt160>();
                            foreach (var input in tx.inputs)
                            {
                                var input_tx = api.GetTransaction(input.prevHash);
                                witnesses.Add(input_tx.outputs[input.prevIndex].scriptHash);
                            }

                            switch (operation)
                            {
                            case "mintTokens":
                            {
                                decimal neo_amount = 0;

                                foreach (var output in tx.outputs)
                                {
                                    if (output.scriptHash == soul_hash_int)
                                    {
                                        neo_amount += output.value;
                                    }
                                }

                                var sender     = witnesses.First();
                                var cur_bought = bought.ContainsKey(sender) ? bought[sender] : 0;

                                decimal refund = 0;

                                if (block.Height >= roundBlock)
                                {
                                    maybe.Add(sender);
                                    expected[sender] = neo_amount * 273;
                                }

                                if (!whitelist.Contains(sender))
                                {
                                    refund     = neo_amount;
                                    neo_amount = 0;
                                }
                                else
                                if (cur_bought + neo_amount > 10)
                                {
                                    var temp = neo_amount;
                                    neo_amount = 10 - cur_bought;

                                    refund = temp - neo_amount;
                                }

                                if (neo_amount > 0)
                                {
                                    BigInteger souls = (int)neo_amount * 273;

                                    /*if (souls + total_supply > max_supply)
                                     * {
                                     *  souls = max_supply - total_supply;
                                     * }*/

                                    total_supply += souls * 2;

                                    if (soul_balances.ContainsKey(sender))
                                    {
                                        soul_balances[sender] += new BigInteger((long)(souls * 100000000));
                                    }
                                    else
                                    {
                                        soul_balances[sender] = new BigInteger((long)(souls * 100000000));
                                    }

                                    Console.WriteLine(block.Height + "," + tx.Hash + ",mint," + soul_hash_int.ToAddress() + "," + sender.ToAddress() + "," + souls);
                                    cur_bought    += neo_amount;
                                    bought[sender] = cur_bought;
                                }
                                else
                                if (refund > 0)
                                {
                                    Console.WriteLine(block.Height + "," + tx.Hash + ",refund," + soul_hash_int.ToAddress() + "," + sender.ToAddress() + "," + refund);
                                }

                                break;
                            }

                            case "whitelistAddFree":
                            {
                                foreach (var addr in args)
                                {
                                    var hash = new UInt160(addr.GetByteArray());
                                    whitelist.Add(hash);
                                }
                                break;
                            }

                            case "whitelistAddFilled":
                            {
                                foreach (var addr in args)
                                {
                                    var hash = new UInt160(addr.GetByteArray());
                                    whitelist.Add(hash);

                                    bought[hash] = 2730;
                                }
                                break;
                            }

                            case "whitelistAddCap":
                            {
                                decimal cap = 0;

                                int index = 0;

                                foreach (var addr in args)
                                {
                                    if (index == 0)
                                    {
                                        var amount = addr.GetBigInteger();
                                        cap = (decimal)(amount / 100000000);
                                    }
                                    else
                                    {
                                        var hash = new UInt160(addr.GetByteArray());
                                        whitelist.Add(hash);

                                        bought[hash] = cap;
                                    }

                                    index++;
                                }
                                break;
                            }

                            case "chainSwap":
                            {
                                throw new Exception("Exploit found");
                                break;
                            }

                            case "deploy":
                            {
                                soul_balances[new UInt160("Abyd4BcStNksGLmfdHtyyPbS1xzhceDKLs".AddressToScriptHash())] = new BigInteger((long)6316538 * (long)100000000);
                                soul_balances[new UInt160("ARWHJefSbhayC2gurKkpjMHm5ReaJZLLJ3".AddressToScriptHash())] = new BigInteger((long)43503435 * (long)100000000);
                                soul_balances[new UInt160("AQFQmVQi9VReLhym1tF3UfPk4EG3VKbAwN".AddressToScriptHash())] = new BigInteger((long)15000000 * (long)100000000);

                                break;
                            }

                            case "transfer":
                            {
                                if (args.Count == 3)
                                {
                                    var from = new UInt160(args[0].GetByteArray());

                                    if (!witnesses.Contains(from))
                                    {
                                        //throw new Exception("Invalid");
                                    }

                                    if (maybe.Contains(from))
                                    {
                                        sure.Add(from);
                                    }

                                    var to    = new UInt160(args[1].GetByteArray());
                                    var value = args[2].GetBigInteger();

                                    var from_addr = from.ToAddress();
                                    var to_addr   = to.ToAddress();

                                    /*if (from == watch)
                                     * {
                                     *  Console.WriteLine("WATCH " + tx.Hash);
                                     * }
                                     * if (to == watch)
                                     * {
                                     *  Console.WriteLine("WATCH " + tx.Hash);
                                     * }*/


                                    decimal amount = (long)value;
                                    int     places = 8;
                                    while (places > 0)
                                    {
                                        amount *= 0.1m;
                                        places--;
                                    }

                                    Console.WriteLine(block.Height + "," + tx.Hash + ",transfer," + from.ToAddress() + "," + to.ToAddress() + "," + amount);
                                }

                                break;
                            }
                            }
                        }
                    }
                }
            }

            /*var lines = new List<string>();
             * foreach (var entry in soul_balances)
             * {
             *  var k = (long)entry.Value;
             *  if (k == 0)
             *  {
             *      continue;
             *  }
             *  var val = (decimal)k / 100000000m;
             *  lines.Add(entry.Key.ToAddress() + "," + val);
             * }
             * File.WriteAllLines("sale_soul.csv", lines.ToArray());*/

            var token = api.GetToken("SOUL");

            foreach (var entry in maybe)
            {
                var addr    = entry.ToAddress();
                var balance = token.BalanceOf(addr);

                if (balance >= expected[entry])
                {
                    sure.Add(entry);
                }
            }

            foreach (var entry in sure)
            {
                maybe.Remove(entry);
                Console.WriteLine(entry.ToAddress() + "," + expected[entry]);

                if (expected[entry] > 50 * 273)
                {
                    throw new Exception("AAAAAAAAA");
                }
            }

            foreach (var entry in maybe)
            {
                Console.WriteLine(entry.ToAddress() + ",0");
            }

            decimal total = 0;

            foreach (var entry in sure)
            {
                total += expected[entry];
            }
            Console.WriteLine("TOTAL " + total);

            var endT  = Environment.TickCount;
            var delta = (endT - startT) / 1000;

            Console.WriteLine("Finished in " + delta + " seconds, loaded " + maxblock + " blocks");

            Console.ReadLine();
            return;


            /*            Console.WriteLine(tkk.BalanceOf("AYxnCZePKhrijk2TQymYYUqm74nuwCftwq"));
             *          //var token = api.GetToken("SOUL");
             *          //var keys = KeyPair.FromWIF("KxnjzXUvK9BLojMrWVJF7jjdbHs37aXvHyFog4rARGNrAQ7LFjLP");
             *
             *          var token = api.GetToken("OBT");
             *          var keys = KeyPair.FromWIF("L5YiR4AdUibLeFf48W3P5P36aBTWANcDu6oQNkvpaQrrHreg4RZC");
             *
             *          var balance = token.BalanceOf(keys);
             *          Console.WriteLine(balance);
             *
             *          var transfers = new Dictionary<string, decimal>();
             *          transfers["AHXWzaYCNYBYvhSypfi2XpAiWz2cCXrDJr"] = 16.7m;
             *          transfers["AHxXMh9cPjE3cYrA9DhWNSBY2hC3a62PcH"] = 16.7m;
             *          transfers["AHY7SidKpLuNj881Mc4Vjsj1Y4jBbX5EPS"] = 16.7m;
             *
             *          var txx  = token.Transfer(keys, transfers);
             *          Console.WriteLine(txx.Hash);*/

            /*
             * uint startBlock = 2298101;
             * uint endblock = 2313169;
             *
             * var soul_hash = LuxUtils.ReverseHex("4b4f63919b9ecfd2483f0c72ff46ed31b5bbb7a4").HexToBytes();
             * var soul_hash_int = new UInt160(soul_hash);
             *
             *
             * var startT = Environment.TickCount;
             *
             * uint maxblock = 0;
             *
             * var blockCount = api.GetBlockHeight();
             *
             * var soul_balances = new Dictionary<UInt160, BigInteger>();
             * var bought = new Dictionary<UInt160, decimal>();
             *
             * BigInteger max_supply = 91136510; // total token amount
             * BigInteger team_supply = 14500000; // team token amount
             * BigInteger advisor_supply = 5500000; // advisor token amount
             * BigInteger platform_supply = 15000000; // company token amount
             * BigInteger presale_supply = 43503435; // presale token amount
             *
             * BigInteger total_supply = team_supply + advisor_supply + presale_supply + platform_supply;
             *
             * var watch = new UInt160( "AQkiyWfwxMT31epRRxWXbR7wvZJH944jqh".AddressToScriptHash());
             *
             * for (uint height = startBlock; height<=endblock; height++)
             * {
             *  var block = api.GetBlock(height);
             *
             *  //Console.WriteLine(height + " " + block.Timestamp.ToString());
             *
             *  foreach (var tx in block.transactions)
             *  {
             *      if (tx.type != TransactionType.InvocationTransaction)
             *      {
             *          continue;
             *      }
             *
             *      List<AVMInstruction> ops;
             *      try
             *      {
             *          ops = NeoTools.Disassemble(tx.script);
             *      }
             *      catch
             *      {
             *          continue;
             *      }
             *
             *      if (tx.Hash.ToString()== "0xcf530159dc7fa7d0ea38ca210b479b04da20039b3b6639cf4c06bf528d415339")
             *      {
             *          tx.gas += 0;
             *      }
             *
             *      for (int i = 0; i < ops.Count; i++)
             *      {
             *          var op = ops[i];
             *
             *          if (op.opcode == OpCode.APPCALL && op.data.SequenceEqual(soul_hash))
             *          {
             *              var engine = new ExecutionEngine(null);
             *              engine.LoadScript(tx.script);
             *              engine.Execute(null);
             *
             *              var operation = engine.EvaluationStack.Peek().GetString();
             *
             *              var witnesses = new HashSet<UInt160>();
             *              foreach (var input in tx.inputs)
             *              {
             *                  var input_tx = api.GetTransaction(input.prevHash);
             *                  witnesses.Add(input_tx.outputs[input.prevIndex].scriptHash);
             *              }
             *
             *              switch (operation)
             *              {
             *                  case "mintTokens":
             *                      {
             *                          decimal neo_amount = 0;
             *
             *                          if (block.Timestamp.ToTimestamp() < 1526947200)
             *                          {
             *                              break;
             *                          }
             *
             *                          if (block.Height> 2298690)
             *                          {
             *                              break;
             *                          }
             *
             *                          foreach (var output in tx.outputs)
             *                          {
             *                              if (output.scriptHash == soul_hash_int)
             *                              {
             *                                  neo_amount += output.value;
             *                              }
             *                          }
             *
             *                          var sender = witnesses.First();
             *                          var cur_bought = bought.ContainsKey(sender) ? bought[sender] : 0;
             *
             *                          if (cur_bought + neo_amount > 10)
             *                          {
             *                              neo_amount = 10 - cur_bought;
             *                          }
             *
             *                          foreach (var wit in witnesses)
             *                          {
             *                              if (wit == watch)
             *                              {
             *                                  Console.WriteLine("WATCH " + tx.Hash);
             *                                  break;
             *                              }
             *                          }
             *
             *                          if (neo_amount > 0)
             *                          {
             *                              BigInteger souls = (int)neo_amount * 273;
             *                              //if (souls + total_supply > max_supply)
             *                              //{
             *                                //  souls = max_supply - total_supply;
             *                              //}
             *
             *                              total_supply += souls * 2;
             *
             *                              if (soul_balances.ContainsKey(sender))
             *                              {
             *                                  soul_balances[sender] += new BigInteger((long)(souls * 100000000));
             *                              }
             *                              else
             *                              {
             *                                  soul_balances[sender] = new BigInteger((long)(souls * 100000000));
             *                              }
             *
             *                              Console.WriteLine(tx.Hash + ",mint," + soul_hash_int.ToAddress() + "," + sender.ToAddress() + "," + souls);
             *                              cur_bought += neo_amount;
             *                              bought[sender] = cur_bought;
             *                          }
             *
             *                          break;
             *                      }
             *
             *                  case "chainSwap":
             *                      {
             *                          throw new Exception("Exploit found");
             *                          break;
             *                      }
             *
             *                  case "transfer":
             *                      {
             *                          var args = (Neo.Lux.VM.Types.Array)engine.EvaluationStack.Peek(1);
             *
             *                          if (args.Count == 3)
             *                          {
             *                              var from = new UInt160(args[0].GetByteArray());
             *
             *                              if (!witnesses.Contains(from))
             *                              {
             *                                  //throw new Exception("Invalid");
             *                              }
             *
             *                              var to = new UInt160(args[1].GetByteArray());
             *                              var value = args[2].GetBigInteger();
             *
             *                              var from_addr = from.ToAddress();
             *                              var to_addr = to.ToAddress();
             *
             *                              if (from == watch)
             *                              {
             *                                  Console.WriteLine("WATCH " + tx.Hash);
             *                              }
             *                              if (to == watch)
             *                              {
             *                                  Console.WriteLine("WATCH " + tx.Hash);
             *                              }
             *
             *                              if (from_addr == "AdkLubeJgL3PCKc1Xv6CEv9PrzB4c5AKNk")
             *                              {
             *                                  value += 0;
             *                              }
             *
             *                              if (soul_balances.ContainsKey(from))
             *                              {
             *                                  var src_balance = soul_balances[from];
             *
             *                                  if (src_balance >= value)
             *                                  {
             *                                      src_balance -= value;
             *                                      soul_balances[from] = src_balance;
             *
             *                                      if (soul_balances.ContainsKey(to))
             *                                      {
             *                                          soul_balances[to] += value;
             *                                      }
             *                                      else
             *                                      {
             *                                          soul_balances[to] = value;
             *                                      }
             *                                  }
             *                              }
             *                              else
             *                              {
             *                                  throw new Exception("Invalid balance");
             *                              }
             *
             *                              decimal amount = (long)value;
             *                              int places = 8;
             *                              while (places > 0)
             *                              {
             *                                  amount *= 0.1m;
             *                                  places--;
             *                              }
             *
             *                              Console.WriteLine(tx.Hash + ",transfer," + from.ToAddress() + "," + to.ToAddress() + "," + amount);
             *                          }
             *
             *                          break;
             *                      }
             *              }
             *
             *          }
             *
             *      }
             *  }
             * }
             *
             * var lines = new List<string>();
             * foreach(var entry in soul_balances)
             * {
             *  var k = (long)entry.Value;
             *  if (k == 0)
             *  {
             *      continue;
             *  }
             *  var val = (decimal)k / 100000000m;
             *  lines.Add(entry.Key.ToAddress() + "," + val);
             * }
             * File.WriteAllLines("soul_snapshop.csv", lines.ToArray());
             *
             * var endT = Environment.TickCount;
             * var delta = (endT - startT) / 1000;
             * Console.WriteLine("Finished in " + delta + " seconds, loaded "+maxblock+" blocks");
             *
             * Console.ReadLine();
             */
        }
Exemplo n.º 17
0
        public void GenerateInputsOutputs(UInt160 from_script_hash, string symbol, IEnumerable <Transaction.Output> targets, out List <Transaction.Input> inputs, out List <Transaction.Output> outputs, decimal system_fee = 0)
        {
            var unspent = GetUnspent(from_script_hash);

            // filter any asset lists with zero unspent inputs
            unspent = unspent.Where(pair => pair.Value.Count > 0).ToDictionary(pair => pair.Key, pair => pair.Value);

            inputs  = new List <Transaction.Input>();
            outputs = new List <Transaction.Output>();

            string assetID;

            var info = GetAssetsInfo();

            if (info.ContainsKey(symbol))
            {
                assetID = info[symbol];
            }
            else
            {
                throw new NeoException($"{symbol} is not a valid blockchain asset.");
            }

            var from_address = from_script_hash.ToAddress();

            if (!unspent.ContainsKey(symbol))
            {
                throw new NeoException($"Not enough {symbol} in address {from_address}");
            }

            decimal cost = 0;

            if (targets != null)
            {
                foreach (var target in targets)
                {
                    if (target.scriptHash.Equals(from_script_hash))
                    {
                        throw new NeoException("Target can't be same as input");
                    }

                    cost += target.value;
                }
            }

            var targetAssetID = LuxUtils.ReverseHex(assetID).HexToBytes();

            var     sources  = unspent[symbol];
            decimal selected = 0;

            if (lastTransactions.ContainsKey(from_address))
            {
                var lastTx = lastTransactions[from_address];

                uint index = 0;
                foreach (var output in lastTx.outputs)
                {
                    if (output.assetID.SequenceEqual(targetAssetID) && output.scriptHash.Equals(from_script_hash))
                    {
                        selected += output.value;

                        var input = new Transaction.Input()
                        {
                            prevHash  = lastTx.Hash,
                            prevIndex = index,
                        };

                        inputs.Add(input);

                        break;
                    }

                    index++;
                }
            }

            foreach (var src in sources)
            {
                if (selected >= cost && inputs.Count > 0)
                {
                    break;
                }

                selected += src.value;

                var input = new Transaction.Input()
                {
                    prevHash  = src.hash,
                    prevIndex = src.index,
                };

                inputs.Add(input);
            }

            if (selected < cost)
            {
                throw new NeoException($"Not enough {symbol}");
            }

            if (cost > 0 && targets != null)
            {
                foreach (var target in targets)
                {
                    var output = new Transaction.Output()
                    {
                        assetID    = targetAssetID,
                        scriptHash = target.scriptHash,
                        value      = target.value
                    };
                    outputs.Add(output);
                }
            }

            if (selected > cost || cost == 0)
            {
                var left = selected - cost;

                var change = new Transaction.Output()
                {
                    assetID    = targetAssetID,
                    scriptHash = from_script_hash,
                    value      = left
                };
                outputs.Add(change);
            }
        }