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); }
protected override Phantasma.Numerics.BigInteger PullFee(Timestamp time, string platform) { platform = platform.ToLower(); switch (platform) { case NeoWallet.NeoPlatform: return(Phantasma.Numerics.UnitConversion.ToBigInteger(0.1m, DomainSettings.FiatTokenDecimals)); case EthereumWallet.EthereumPlatform: CachedFee fee; if (_feeCache.TryGetValue(platform, out fee)) { if ((Timestamp.Now - fee.Time) < 60) { var logMessage = $"PullFee({platform}): Cached fee pulled: {fee.Value}, GAS limit: {_cli.Settings.Oracle.EthGasLimit}, calculated fee: {fee.Value * _cli.Settings.Oracle.EthGasLimit}"; logger.Debug(logMessage); return(fee.Value * _cli.Settings.Oracle.EthGasLimit); } } var newFee = EthereumInterop.GetNormalizedFee(_cli.Settings.Oracle.EthFeeURLs.ToArray()); fee = new CachedFee(Timestamp.Now, UnitConversion.ToBigInteger(newFee, 9)); // 9 for GWEI _feeCache[platform] = fee; var logMessage2 = $"PullFee({platform}): New fee pulled: {fee.Value}, GAS limit: {_cli.Settings.Oracle.EthGasLimit}, calculated fee: {fee.Value * _cli.Settings.Oracle.EthGasLimit}"; logger.Debug(logMessage2); return(fee.Value * _cli.Settings.Oracle.EthGasLimit); default: throw new OracleException($"Support for {platform} fee not implemented in this node"); } }
public InteropTransfers ExtractInteropTransfers(Blockchain.Nexus nexus, Logger logger, string swapAddress) { var interopTransfers = new InteropTransfers(); lock (transactions) { foreach (var txVo in transactions) { var block = txVo.Block; var txr = txVo.TransactionReceipt; var tx = txVo.Transaction; var interopAddress = EthereumInterop.ExtractInteropAddress(tx); var transferEvents = txr.DecodeAllEvents <TransferEventDTO>(); //var swapEvents = txr.DecodeAllEvents<SwapEventDTO>(); var nodeSwapAddress = EthereumWallet.EncodeAddress(swapAddress); if (transferEvents.Count > 0 || tx.Value != null && tx.Value.Value > 0) { if (!interopTransfers.ContainsKey(block.BlockHash)) { interopTransfers.Add(block.BlockHash, new Dictionary <string, List <InteropTransfer> >()); } } if (transferEvents.Count > 0) { var blockId = block.Number.ToString(); var hash = txr.TransactionHash; foreach (var evt in transferEvents) { var targetAddress = EthereumWallet.EncodeAddress(evt.Event.To); // If it's not our address, skip immediatly, don't log it if (targetAddress != nodeSwapAddress) { continue; } logger.Message($"Found ERC20 swap: {blockId} hash: {hash} to: {evt.Event.To} from: {evt.Event.From} value: {evt.Event.Value}"); var asset = EthUtils.FindSymbolFromAsset(nexus, evt.Log.Address); logger.Message("asset: " + asset); if (asset == null) { logger.Message($"Asset [{evt.Log.Address}] not supported"); continue; } var sourceAddress = EthereumWallet.EncodeAddress(evt.Event.From); var amount = PBigInteger.Parse(evt.Event.Value.ToString()); logger.Message("nodeSwapAddress: " + nodeSwapAddress); logger.Message("sourceAddress: " + sourceAddress); logger.Message("targetAddress: " + targetAddress); logger.Message("amount: " + amount); if (!interopTransfers[block.BlockHash].ContainsKey(evt.Log.TransactionHash)) { interopTransfers[block.BlockHash].Add(evt.Log.TransactionHash, new List <InteropTransfer>()); } interopTransfers[block.BlockHash][evt.Log.TransactionHash].Add ( new InteropTransfer ( EthereumWallet.EthereumPlatform, sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, // interop address asset, amount ) ); } } if (tx.Value != null && tx.Value.Value > 0) { logger.Message("ETH:"); logger.Message(block.Number.ToString()); logger.Message(tx.TransactionHash); logger.Message(tx.To); logger.Message(tx.From); logger.Message(tx.Value.ToString()); var targetAddress = EthereumWallet.EncodeAddress(tx.To); if (targetAddress != nodeSwapAddress) { continue; } if (!interopTransfers[block.BlockHash].ContainsKey(tx.TransactionHash)) { interopTransfers[block.BlockHash].Add(tx.TransactionHash, new List <InteropTransfer>()); } var sourceAddress = EthereumWallet.EncodeAddress(tx.From); var amount = PBigInteger.Parse(tx.Value.ToString()); interopTransfers[block.BlockHash][tx.TransactionHash].Add ( new InteropTransfer ( EthereumWallet.EthereumPlatform, sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, // interop address "ETH", // TODO use const amount ) ); } } transactions.Clear(); } // clear transactions after extraction was done return(interopTransfers); }
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); }
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); }
public InteropTransfers ExtractInteropTransfers(Blockchain.Nexus nexus, Logger logger, string[] swapAddresses) { var interopTransfers = new InteropTransfers(); lock (transactions) { foreach (var txVo in transactions) { var block = txVo.Block; var txr = txVo.TransactionReceipt; var tx = txVo.Transaction; Address interopAddress; try { interopAddress = EthereumInterop.ExtractInteropAddress(tx, logger); } catch (Exception e) { if (e.ToString().Contains("Header byte out of range")) { // Ignoring this exception and skipping this tx. // RecoverFromSignature() crashed and we cannot avoid it atm. // Related to EIP-1559, example of probematic tx: https://etherscan.io/tx/0xb022c146d8d1e684de0c1faae43e7ce36afb6969719adfdcafcc5bb7d5913185 // TODO Fix by updating to new Nethereum and dealing with EIP-1559 better. logger.Debug("Warning: Skipping 'Header byte out of range' tx: " + tx.TransactionHash); continue; } else { throw; } } var transferEvents = txr.DecodeAllEvents <TransferEventDTO>(); //var swapEvents = txr.DecodeAllEvents<SwapEventDTO>(); var nodeSwapAddresses = swapAddresses.Select(x => encodeHandler(x)).ToList(); //var nodeSwapAddresses = encodeHandler(swapAddress); if (transferEvents.Count > 0 || tx.Value != null && tx.Value.Value > 0) { if (!interopTransfers.ContainsKey(block.BlockHash)) { interopTransfers.Add(block.BlockHash, new Dictionary <string, List <InteropTransfer> >()); } } if (transferEvents.Count > 0) { var blockId = block.Number.ToString(); var hash = txr.TransactionHash; foreach (var evt in transferEvents) { var targetAddress = encodeHandler(evt.Event.To); // If it's not our address, skip immediatly, don't log it if (!nodeSwapAddresses.Contains(targetAddress)) { continue; } var asset = EthUtils.FindSymbolFromAsset(this.platform, nexus, evt.Log.Address); logger.Debug($@"Found {this.platform} swap: {blockId} hash: {hash} to: {evt.Event.To} from: {evt.Event.From} value: {evt.Event.Value} asset: {asset}"); if (asset == null) { logger.Debug($"Asset [{evt.Log.Address}] not supported"); continue; } var sourceAddress = encodeHandler(evt.Event.From); var amount = PBigInteger.Parse(evt.Event.Value.ToString()); //logger.Message("nodeSwapAddress: " + nodeSwapAddress); //logger.Message("sourceAddress: " + sourceAddress); //logger.Message("targetAddress: " + targetAddress); //logger.Message("amount: " + amount); if (!interopTransfers[block.BlockHash].ContainsKey(evt.Log.TransactionHash)) { interopTransfers[block.BlockHash].Add(evt.Log.TransactionHash, new List <InteropTransfer>()); } interopTransfers[block.BlockHash][evt.Log.TransactionHash].Add ( new InteropTransfer ( this.platform, sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, // interop address asset, amount ) ); } } if (tx.Value != null && tx.Value.Value > 0) { //logger.Message("ETH:"); //logger.Message(block.Number.ToString()); //logger.Message(tx.TransactionHash); //logger.Message(tx.To); //logger.Message(tx.From); //logger.Message(tx.Value.ToString()); var targetAddress = encodeHandler(tx.To); Console.WriteLine("target eth: " + targetAddress); if (!nodeSwapAddresses.Contains(targetAddress)) { continue; } if (!interopTransfers[block.BlockHash].ContainsKey(tx.TransactionHash)) { interopTransfers[block.BlockHash].Add(tx.TransactionHash, new List <InteropTransfer>()); } var sourceAddress = encodeHandler(tx.From); var amount = PBigInteger.Parse(tx.Value.ToString()); var nativeSymbol = (this.platform == "ethereum") ? "ETH" : "BNB"; interopTransfers[block.BlockHash][tx.TransactionHash].Add ( new InteropTransfer ( this.platform, sourceAddress, DomainSettings.PlatformName, targetAddress, interopAddress, // interop address nativeSymbol, amount ) ); } } transactions.Clear(); } // clear transactions after extraction was done return(interopTransfers); }