public async static Task <EthereumTransactionInfo> GetEthTransactionDetails(ConnectionState connectionState, CancellationToken token, TlsClientProtocol tls) { byte[] apdu = { 0x80, 0xE0, 0x00, 0x00 }; byte[] response; try { response = await SendWrappedAPDU(apdu, 29, connectionState, token, tls); } catch (Non9000SwException) { throw new TransactionNotActiveException(); } EthereumTransactionInfo transactionInfo = new EthereumTransactionInfo(); transactionInfo.type = Helper.MakeUint16(response, 0); if (response[2] != 0x00) { transactionInfo.transactionTooBigToDisplay = true; } else { transactionInfo.transactionTooBigToDisplay = false; } transactionInfo.currentOffset = Helper.MakeUint16(response, 3); transactionInfo.address = new byte[20]; Array.Copy(response, 5, transactionInfo.address, 0, 20); transactionInfo.remainingTime = Helper.MakeUint32(response, 25); return(transactionInfo); }
public static string ParseEthereumTransaction(EthereumTransactionInfo info, byte[] input) { string htmlOutput = ""; string details = ""; Dictionary <int, string> chainIDDict = new Dictionary <int, string>() { { 1, "Ethereum mainnet" }, { 2, "Morden(disused), Expanse mainnet" }, { 3, "Ropsten" }, { 4, "Rinkeby" }, { 30, "Rootstock mainnet" }, { 31, "Rootstock testnet" }, { 42, "Kovan" }, { 61, "Ethereum Classic mainnet" }, { 62, "Ethereum Classic testnet" }, { 1337, "Geth private chains (default)" }, }; if (info.transactionTooBigToDisplay == true) { throw new EthParserException("Transaction too big to display."); } if (info.type == 0x6666) { throw new EthParserException("A message is being signed. Cannot display."); } else { RLPCollection collection = RLP.Decode(input); if (collection.Count != 1) { throw new EthParserException("Invalid transaction."); } collection = (RLPCollection)collection[0]; if (collection.Count != 9) { throw new EthParserException("Invalid transaction."); } string fromAddress = "0x" + string.Join(string.Empty, Array.ConvertAll(info.address, b => b.ToString("x2"))); string nonce = new BigInteger(collection[0].RLPData).ToString(10); string gasPrice = ""; var gasPriceInGwei = new BigInteger(collection[1].RLPData).DivideAndRemainder(new BigInteger("1000000000")); var gasPriceInGweiRemainder = gasPriceInGwei[1].ToString(10).PadLeft(9, '0').TrimEnd('0'); if (gasPriceInGweiRemainder == "") { gasPrice = gasPriceInGwei[0].ToString(10) + " GWEI"; } else { gasPrice = gasPriceInGwei[0].ToString(10) + "." + gasPriceInGweiRemainder + " GWEI"; } string gasLimit = new BigInteger(collection[2].RLPData).ToString(10); string toAddress = "0x" + string.Join(string.Empty, Array.ConvertAll(collection[3].RLPData, b => b.ToString("x2"))); string value = ""; var valueInEth = new BigInteger(collection[4].RLPData).DivideAndRemainder(new BigInteger("1000000000000000000")); var valueInEthRemainder = valueInEth[1].ToString(10).PadLeft(18, '0').TrimEnd('0'); if (valueInEthRemainder == "") { value = valueInEth[0].ToString(10) + " ETH"; } else { value = valueInEth[0].ToString(10) + "." + valueInEthRemainder + " ETH"; } string data = "None"; if (collection[5].RLPData != null) { data = "0x" + string.Join(string.Empty, Array.ConvertAll(collection[5].RLPData, b => b.ToString("x2"))); } int v = collection[6].RLPData.ToIntFromRLPDecoded(); string chainID = ""; if (chainIDDict.ContainsKey(v)) { chainID = chainIDDict[v]; } else { chainID = v.ToString(); } if ((collection[7].RLPData != null) || (collection[8].RLPData != null)) { throw new EthParserException("Invalid transaction."); } details += "<b>Send to: </b>" + toAddress + "<br>"; details += "<b>Send from: </b>" + fromAddress + "<br>"; details += "<b>Value: </b>" + value + "<br>"; details += "<b>ChainID: </b>" + chainID + "<br>"; details += "<b>Gas limit: </b>" + gasLimit + "<br>"; details += "<b>Gas price: </b>" + gasPrice + "<br>"; details += "<b>Nonce: </b>" + nonce + "<br>"; details += "<b>Data: </b>" + data + "<br><br>"; } htmlOutput += details; htmlOutput += "Time remaining to confirm: <b>" + (info.remainingTime / 1000).ToString() + "</b> seconds. <br><br>"; return(htmlOutput); }