Skip to content

Commit

Permalink
Merge pull request #16 from MoralisWeb3/moralis-scan-ep08
Browse files Browse the repository at this point in the history
Moralis scan ep08
  • Loading branch information
jermay authored Apr 17, 2021
2 parents abaf470 + 1def8bb commit 6b5d0e4
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 24 deletions.
2 changes: 2 additions & 0 deletions moralis-scan/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

[Part 7 - I Cloned Etherscan in 5 hours - Pagination Component](https://youtu.be/3x5eQdZo9i0)

[Part 8 - I Cloned Etherscan in 5 hours - Token Transactions](https://youtu.be/lzjMcjn6XCk)

# Getting Started with Create React App

This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
Expand Down
4 changes: 2 additions & 2 deletions moralis-scan/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import "./App.css";
import Home from "./components/Home";
import Header from "./Header";
import Transactions from "./components/Transactions";
import AddressResults from "./components/AddressResults";

Moralis.initialize(process.env.REACT_APP_MORALIS_APPLICATION_ID);
Moralis.serverURL = process.env.REACT_APP_MORALIS_SERVER_URL;
Expand All @@ -15,7 +15,7 @@ function App() {
<Router>
<Header />
<Switch>
<Route path="/address/:address" component={Transactions} />
<Route path="/address/:address/:transType" component={AddressResults} />
<Route exact path="/"component={Home} />
</Switch>
</Router>
Expand Down
52 changes: 52 additions & 0 deletions moralis-scan/src/components/AddressResults.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react'
import {
BrowserRouter as Router,
Switch,
Route,
NavLink,
useParams,
} from "react-router-dom";
import Transactions from './Transactions';

export default function AddressResults() {
const {address} = useParams();
return (
<div className="py-3">
<Router>
<div className="card">
<div className="card-header">
<ul className="nav mb-1">
<li className="nav-item">
<NavLink
className="nav-link"
activeClassName="active"
to={`/address/${address}/main`}
>
Transactions
</NavLink>
</li>
<li className="nav-item">
<NavLink
className="nav-link"
activeClassName="active"
to={`/address/${address}/erc20`}
>
ERC20 Token Txns
</NavLink>
</li>
</ul>
</div>
<div className="card-body">
<Switch>
<Route
exact
path="/address/:address/:transType"
component={Transactions}
/>
</Switch>
</div>
</div>
</Router>
</div>
);
}
23 changes: 15 additions & 8 deletions moralis-scan/src/components/Search.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useMemo, useState } from 'react'
import { useHistory } from 'react-router';
import { useMoralisCloudQuery } from '../hooks/cloudQuery';
import React, { useMemo, useState } from "react";
import { useHistory } from "react-router";
import { useMoralisCloudQuery } from "../hooks/cloudQuery";

export default function Search() {
const [searchTxt, setSearchTxt] = useState("");
Expand All @@ -10,11 +10,18 @@ export default function Search() {

const onSearchTextChanged = (e) => setSearchTxt(e.target.value);

const watchParams = useMemo(()=> ({
params: {address}, // query params
onSuccess: () => history.push(`/address/${address}`),
}), [address, history]);
const {loading} = useMoralisCloudQuery("searchEthAddress", watchParams);
const watchParams = useMemo(
() => ({
params: { address }, // query params
onSuccess: () => {
if (address) {
history.push(`/address/${address}/main`);
}
},
}),
[address, history]
);
const { loading } = useMoralisCloudQuery("searchEthAddress", watchParams);

const submitSearch = async (e) => {
e.preventDefault();
Expand Down
14 changes: 3 additions & 11 deletions moralis-scan/src/components/TransResults.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
import { useTransType } from "../hooks/useTransType";
import { useResultContext } from "./Paginator";

const cols = [
{ colName: "Txn Hash", key: "hash" },
{ colName: "Block", key: "block_number" },
{ colName: "Age", key: "ago" },
{ colName: "From", key: "from_address" },
{ colName: "To", key: "to_address" },
{ colName: "Value", key: "value" },
{ colName: "Txn Fee", key: "gas_price" },
];

export default function TransResults() {
const { results } = useResultContext();
if (!results) {
const { cols } = useTransType();
if (!results || !cols) {
return null;
}

Expand Down
7 changes: 4 additions & 3 deletions moralis-scan/src/components/Transactions.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React from 'react'
import { useParams } from 'react-router';
import { processTransaction } from '../queries/transactions';
import { useTransType } from '../hooks/useTransType';
import Paginator from './Paginator';
import TransResults from './TransResults';

export default function Transactions() {
const {address } = useParams();
const {methodName, postProcess} = useTransType();
if (!address) {
return null;
}
Expand All @@ -14,8 +15,8 @@ export default function Transactions() {
<div>
<Paginator
userAddress={address}
methodName="getTransactions"
options={{ postProcess: processTransaction }}
methodName={methodName}
options={{ postProcess }}
>
<TransResults />
</Paginator>
Expand Down
43 changes: 43 additions & 0 deletions moralis-scan/src/hooks/useTransType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useEffect, useState } from "react"
import { useParams } from "react-router";
import { processTokenTransfer } from "../queries/tokenTransfer";
import { processTransaction } from "../queries/transactions";

const TransTypeOptions = {
main: {
cols: [
{ colName: "Txn Hash", key: "hash" },
{ colName: "Block", key: "block_number" },
{ colName: "Age", key: "ago" },
{ colName: "From", key: "from_address" },
{ colName: "To", key: "to_address" },
{ colName: "Value", key: "value" },
{ colName: "Txn Fee", key: "gas_price" },
],
methodName: "getTransactions",
postProcess: processTransaction,
},
erc20: {
cols: [
{ colName: "Txn Hash", key: "transaction_hash" },
{ colName: "Age", key: "ago" },
{ colName: "From", key: "from_address" },
{ colName: "To", key: "to_address" },
{ colName: "Value", key: "value" },
{ colName: "Token", key: "name" },
],
methodName: "getTokenTranfers",
postProcess: processTokenTransfer,
},
};

export const useTransType = () => {
const [state, setState] = useState({});
const { transType } = useParams();

useEffect(()=>{
setState(TransTypeOptions[transType])
}, [transType])

return state;
}
89 changes: 89 additions & 0 deletions moralis-scan/src/queries/cloud-queries.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import Moralis from "moralis";

Moralis.Cloud.define("searchEthAddress", async function(request) {
const { address } = request.params
if (!address) {
return null
}

// find out if address is already watched
const query = new Moralis.Query("WatchedEthAddress");
query.equalTo("address", address)
const watchCount = await query.count();

if (watchCount > 0) {
// already watched don't sync again
return null;
}

return Moralis.Cloud.run("watchEthAddress", {address});
});

Moralis.Cloud.define("getTransactions", function(request) {
const {userAddress, pageSize, pageNum } = request.params;
const offset = (pageNum - 1) * pageSize;

const query = new Moralis.Query("EthTransactions");
query.equalTo("from_address", userAddress);
query.descending("block_number");
query.withCount();
query.skip(offset)
query.limit(pageSize);

return query.find();
});

Moralis.Cloud.define("getTokenTranfers", async (request) => {
const {userAddress, pageSize, pageNum } = request.params;
const offset = (pageNum - 1) * pageSize;
const output = {
results: [],
count: 0,
};

// count results
const matchPipeline = {
match: {
$expr: {
$or: [
{ $eq: ["$from_address", userAddress] },
{ $eq: ["$to_address", userAddress] },
],
},
},
sort: { block_number: -1 },
count: "count",
};
const query = new Moralis.Query("EthTokenTransfers");
const countResult = await query.aggregate(matchPipeline);
output.count = countResult[0].count;

// get page results
const lookupPipeline = {
...matchPipeline,
skip: offset,
limit: pageSize,
lookup: {
from: "EthTokenBalance",
let: { tokenAddress: "$token_address", userAddress },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ["$token_address", "$$tokenAddress"] },
{ $eq: ["$address", "$$userAddress"] },
],
},
},
},
],
as: "EthTokenBalance",
},
unwind: "$EthTokenBalance",
};
delete lookupPipeline.count;

output.results = await query.aggregate(lookupPipeline);
return output;
});
14 changes: 14 additions & 0 deletions moralis-scan/src/queries/tokenTransfer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { agoTxt, getEllipsisTxt, tokenValueTxt } from "./utils";

export const processTokenTransfer = (r) => ({
transaction_hash: getEllipsisTxt(r.transaction_hash),
ago: agoTxt(r.block_timestamp.valueOf()),
from_address: getEllipsisTxt(r.from_address),
to_address: getEllipsisTxt(r.to_address),
value: tokenValueTxt(
r.value,
r.EthTokenBalance?.decimals,
r.EthTokenBalance?.symbol
),
name: r.EthTokenBalance?.name || "",
});

0 comments on commit 6b5d0e4

Please sign in to comment.