private InteropTransaction FillTransaction(string hashText, string inputAddress, Address interopAddress) { int page = 1; int maxPages = 9999; while (page <= maxPages) { var apiCall = $"get_address_abstracts/{inputAddress}/{page}"; var json = ExecuteRequest(apiCall); if (json == null) { throw new OracleException("Network read failure: " + apiCall); } var root = JSONReader.ReadFromString(json); var entries = root.GetNode("entries"); for (int i = 0; i < entries.ChildCount; i++) { var entry = entries.GetNodeByIndex(i); var txId = entry.GetString("txid"); if (hashText.Equals(txId, StringComparison.OrdinalIgnoreCase)) { var inputAsset = entry.GetString("asset"); var symbol = FindSymbolFromAsset(inputAsset); if (symbol == null) { throw new OracleException("transaction contains unknown asset: " + inputAsset); } var inputAmount = entry.GetDecimal("amount"); var sourceAddress = entry.GetString("address_from"); var destAddress = entry.GetString("address_to"); var info = nexus.GetTokenInfo(nexus.RootStorage, symbol); var amount = UnitConversion.ToBigInteger(inputAmount, info.Decimals); var txHash = Hash.Parse(hashText); var tx = new InteropTransaction(txHash, new InteropTransfer[] { new InteropTransfer("neo", NeoWallet.EncodeAddress(sourceAddress), "neo", NeoWallet.EncodeAddress(destAddress), interopAddress, symbol, amount) }); return(tx); } } page++; } throw new Exception("could not fill oracle transaction: " + hashText); }
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); }
public static InteropTransaction MakeInteropTx(Logger logger, NeoTx tx, NeoAPI api, string[] origSwapAddresses, string coldStorage) { logger.Debug("checking tx: " + tx.Hash); var swapAddresses = new List <Address>(); foreach (var addr in origSwapAddresses) { swapAddresses.Add(NeoWallet.EncodeAddress(addr)); } List <InteropTransfer> interopTransfers = new List <InteropTransfer>(); var emptyTx = new InteropTransaction(Hash.Null, interopTransfers.ToArray()); PBigInteger amount; var witness = tx.witnesses.ElementAtOrDefault(0); if (witness == null) { // tx has no witness return(emptyTx); } var interopAddress = witness.ExtractAddress(); if (tx.witnesses.Length != 1 || interopAddress == Address.Null || interopAddress == null) { //currently only one witness allowed // if ExtractAddress returns Address.Null, the tx is not properly signed return(emptyTx); } var sourceScriptHash = witness.verificationScript.Sha256().RIPEMD160(); var sourceAddress = NeoWallet.EncodeByteArray(sourceScriptHash); var sourceDecoded = NeoWallet.DecodeAddress(sourceAddress); if (sourceAddress == interopAddress || sourceDecoded == coldStorage) { logger.Warning("self send tx or cold storage transfer found, ignoring: " + tx.Hash); // self send, probably consolidation tx, ignore return(emptyTx); } //logger.Debug("interop address: " + interopAddress); //logger.Debug("xswapAddress: " + swapAddress); //logger.Debug("interop sourceAddress: " + sourceAddress); //logger.Debug("neo sourceAddress: " + NeoWallet.DecodeAddress(sourceAddress)); if (tx.attributes != null && tx.attributes.Length > 0) { foreach (var attr in tx.attributes) { if (attr.Usage == TransactionAttributeUsage.Description) { try { var text = Encoding.UTF8.GetString(attr.Data); if (Address.IsValidAddress(text)) { interopAddress = Address.FromText(text); //logger.Debug("new interop address: " + interopAddress); } } catch {} } } } if (tx.outputs.Length > 0) { foreach (var output in tx.outputs) { var targetAddress = NeoWallet.EncodeByteArray(output.scriptHash.ToArray()); //logger.Debug("interop targetAddress : " + targetAddress); //logger.Debug("neo targetAddress: " + NeoWallet.DecodeAddress(targetAddress)); //logger.Debug("interopSwapAddress: " + interopSwapAddress); //logger.Debug("targetAddress: " + targetAddress); //var swpAddress = NeoWallet.EncodeAddress(swapAddress); //logger.Debug("interop swpAddress: " + swpAddress); //logger.Debug("neo swpAddress: " + NeoWallet.DecodeAddress(swpAddress)); //if (targetAddress.ToString() == swapAddress) if (swapAddresses.Contains(targetAddress)) { var token = FindSymbolFromAsset(new UInt256(output.assetID).ToString()); CryptoCurrencyInfo tokenInfo; if (NeoTokenInfo.TryGetValue(token, out tokenInfo)) { amount = Phantasma.Numerics.UnitConversion.ToBigInteger( output.value, tokenInfo.Decimals); } else { // asset not swapable at the moment... //logger.Debug("Asset not swapable"); return(emptyTx); } //logger.Debug("UTXO " + amount); interopTransfers.Add ( new InteropTransfer ( NeoWallet.NeoPlatform, sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, // interop address token.ToString(), amount ) ); } } } if (tx.script != null && tx.script.Length > 0) // NEP5 transfers { var script = NeoDisassembler.Disassemble(tx.script, true); //logger.Debug("SCRIPT ===================="); //foreach (var entry in script.lines) //{ // logger.Debug($"{entry.name} : { entry.opcode }"); //} //logger.Debug("SCRIPT ===================="); if (script.lines.Count() < 7) { //logger.Debug("NO SCRIPT!!!!"); return(emptyTx); } var disasmEntry = script.lines.ElementAtOrDefault(6); //if ( disasmEntry == null ) //{ // logger.Debug("disasmEntry is null"); //} //if ( disasmEntry != null ) //{ // if ( disasmEntry.data == null) // logger.Debug("disasmEntry.data is 0"); //} if (disasmEntry.name != "APPCALL" || disasmEntry.data == null || disasmEntry.data.Length == 0) { //logger.Debug("NO APPCALL"); return(emptyTx); } else { var assetString = new UInt160(disasmEntry.data).ToString(); if (string.IsNullOrEmpty(assetString) || FindSymbolFromAsset(assetString) == null) { //logger.Debug("Ignore TX due to non swapable token."); return(emptyTx); } } int pos = 0; foreach (var entry in script.lines) { pos++; if (pos > 3) { // we are only interested in the first three elements break; } if (entry.data == null || entry.data.Length == 0) { logger.Debug("Ignore tx, invalid data field: " + tx); return(emptyTx); } if (pos == 1) { amount = PBigInteger.FromUnsignedArray(entry.data, true); } if (pos == 2 || pos == 3) { if (pos == 2) { if (entry.data == null || entry.data.Length == 0) { logger.Debug("Invalid op on pos 2, ignoring tx: " + tx); return(emptyTx); } var targetScriptHash = new UInt160(entry.data); //logger.Debug("neo targetAddress: " + targetScriptHash.ToAddress()); var targetAddress = NeoWallet.EncodeByteArray(entry.data); //logger.Debug("targetAddress : " + targetAddress); //logger.Debug("interopSwapAddress: " + interopSwapAddress); //logger.Debug("SwapAddress: " + swapAddress); if (swapAddresses.Contains(targetAddress)) { // found a swap, call getapplicationlog now to get transaction details and verify the tx was actually processed. ApplicationLog[] appLogs = null; try { appLogs = api.GetApplicationLog(tx.Hash); } catch (Exception e) { logger.Error("Getting application logs failed: " + e.Message); return(new InteropTransaction(Hash.Null, interopTransfers.ToArray())); } if (appLogs != null) { for (var i = 0; i < appLogs.Length; i++) { //logger.Debug("appLogs[i].contract" + appLogs[i].contract); var token = FindSymbolFromAsset(appLogs[i].contract); //logger.Debug("TOKEN::::::::::::::::::: " + token); //logger.Debug("amount: " + appLogs[i].amount + " " + token); var sadd = NeoWallet.EncodeByteArray(appLogs[i].sourceAddress.ToArray()); var tadd = NeoWallet.EncodeByteArray(appLogs[i].targetAddress.ToArray()); interopTransfers.Add ( new InteropTransfer ( "neo", // todo Pay.Chains.NeoWallet.NeoPlatform //NeoWallet.EncodeByteArray(appLogs[i].sourceAddress.ToArray()), sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, // interop address token, appLogs[i].amount ) ); } } else { logger.Warning("Neo swap is found but application log is not available for tx " + tx.Hash); } } } else { //TODO reverse swap sourceScriptHash = new UInt160(entry.data).ToArray(); sourceAddress = NeoWallet.EncodeByteArray(sourceScriptHash.ToArray()); } } } } var total = interopTransfers.Count(); if (total > 0) { logger.Message($"Found {total} swaps in neo tx {tx.Hash}"); } else { logger.Debug($"No swaps in neo tx {tx.Hash}"); } return((interopTransfers.Count() > 0) ? new InteropTransaction(Hash.Parse(tx.Hash.ToString()), interopTransfers.ToArray()) : new InteropTransaction(Hash.Null, interopTransfers.ToArray())); }