private static void ScanTransactions() { string lastIncomingTxHash = null; var transactionsOut = GetAllPaidTransactions(); while (true) { try { // get upto 25 last incoming deposit transactions up to the hash supplied. var incomingTransactions = DepositAccount.GetIncomingTransactionsAsync(lastIncomingTxHash).Result; // if no transactions, break. if (incomingTransactions?.data?.Count == 0 || incomingTransactions?.data == null) { break; } // scan all incoming transactions to see if they need to be paid out assets foreach (var t in incomingTransactions.data) { // set last hash to the hash of the last transaction in the previous list. (null on the first go round) // you can only retrieve 25 txs at a time. providing the hash of the last transaction // in the previous list will give the next 25 transactions that happened prior to the hash provided lastIncomingTxHash = t.meta.hash.data; // assume unpaid var paid = false; // loop all outgoing and unconfirmed outgoing transactions foreach (var confTx in transactionsOut) { // if message is not null, could contain a payout hash if (confTx?.transaction?.message?.payload == null) { continue; } // if the hash in the outgoing message matches the hash of the deposit transaction being checked, // its been paid, so print out, declare paid and stop looping through outgoing txs if (Encoding.UTF8.GetString(CryptoBytes.FromHexString(confTx.transaction.message.payload)) != (t.transaction.type == 4100 ? t.meta.innerHash.data : t.meta.hash.data)) { continue; } // print out paid hash Console.WriteLine("hash of paid tx:"); Console.WriteLine(Encoding.UTF8.GetString(CryptoBytes.FromHexString(confTx.transaction.message.payload))); Console.WriteLine(); paid = true; break; } // if paid, continue to check the next incoming transaction if (paid) { continue; } // if the transaction isnt a transfer transaction, skip it. // othertrans is support for multisig transfers. if (t.transaction.type != 257 && t.transaction?.otherTrans?.type != 257) { continue; } // if deposit account does an accidental payment to itself it will pay itself out, so ignore any transactions to self on the off chance it happens. // manually convert tx signer to address in case public key of deposit address is not yet known. if (new Address(Con.GetNetworkVersion().ToEncoded(new PublicKey(t.transaction.type == 4100 ? t.transaction.otherTrans?.signer : t.transaction?.signer))).Encoded == DepositAccount.Address.Encoded) { continue; } // create the recipient var recipient = Con.GetNetworkVersion().ToEncoded( new PublicKey(t.transaction.type == 4100 ? t.transaction.otherTrans?.signer : t.transaction?.signer)); // declare double wholeAssetQuantity = 0.0; // get cost of asset var xemPerMosaic = double.Parse(ConfigurationManager.AppSettings["xemPerMosaic"]); // calculate quantity of asset to return. wholeAssetQuantity += (t.transaction.type == 4100 ? t.transaction.otherTrans.amount : t.transaction.amount) / xemPerMosaic; // if transaction contains a mosaic of type xem, calculate whole assets to be paid out and include. // xem can be sent both as version one and two type transactions ie. attached as a mosaic. in some cases people may send xem as a mosaic // of type xem so catch it if they do. if ((t.transaction.type == 4100 ? t.transaction.otherTrans.mosaics : t.transaction.mosaics) != null && (t.transaction.type == 4100 ? t.transaction.otherTrans.mosaics : t.transaction.mosaics).Exists( e => e.mosaicId.namespaceId == "nem" && e.mosaicId.name == "xem")) { var mosaic = (t.transaction.type == 4100 ? t.transaction.otherTrans.mosaics : t.transaction.mosaics) .Single(e => e.mosaicId.namespaceId == "nem" && e.mosaicId.name == "xem"); wholeAssetQuantity += mosaic.quantity / xemPerMosaic; } // account for asset divisibility. var assetUnits = (long)(wholeAssetQuantity * Math.Pow(10, long.Parse(MosaicToReturn.Properties[0].Value))); // print out incoming hash Console.WriteLine("incoming hash to pay: \n" + (t.transaction.type == 4100 ? t.meta.innerHash.data : t.meta.hash.data)); // payout asset ReturnAsset(recipient, RoundUp(assetUnits, 6 - int.Parse(MosaicToReturn.Properties[0].Value)), t.transaction.type == 4100 ? t.meta.innerHash.data : t.meta.hash.data); // could flood the network with transactions, so limit to 1 transactions per min, // also helps disperse transaction fees among harvesters. Thread.Sleep(10000); } } catch (Exception e) { Console.WriteLine(e); } } Console.WriteLine("all transactions checked and paid"); }