Realtime Transactions

A set of Plugins that sync new transactions and contract events into your Moralis Server.

Moralis provides a set of plugins to sync new transactions and contract events to your server.

Realtime Blocks (Default)

Listens for new blocks and triggers syncing transactions.

Realtime Balances (Default)

Listens for new transactions from all user addresses and adjusts token balances (ERC20, ERC721, ERC1155).

See the plugin page for more info about plugins.

Ethereum Real-Time and Historic Transactions - Web3 Programming

Sync and Watch Contract Events

Defining

This optional plugin will get all historical events from a specific smart contract topic and listen for new events in real-time. It requires the following information:

  • description : a short description to identify this plugin

  • topic: The topic you will listen to, this could either be a definition or sha3:

    • bet(address,uint256,bool)

    • 0x20d932006281d267f137cd3921b707c2097e1f041b1291181cc3d0e86f449ebb

  • abi: If you provide the abi for this event, Moralis will automatically parse all the fields and populate the schema accordingly.

  • address: The address you will listen to for this event.

  • tableName: The name of the subclass that will be created in your Moralis database.

if you do not provide the abi the following schema will be used :

{
"objectId": String,
"block_hash": String,
"topic0": String,
"topic1": String,
"block_timestamp": Date,
"ACL": ACL,
"data": String,
"updatedAt": Date,
"transaction_hash": String,
"transaction_index": Number,
"address": String,
"log_index": Number,
"createdAt": Date,
"block_number": Number
}

This is an example of a valid abi:

{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "user",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "bet",
"type": "uint256"
},
{
"indexed": true,
"internalType": "bool",
"name": "win",
"type": "bool"
},
{
"indexed": false,
"internalType": "uint8",
"name": "side",
"type": "uint8"
}
],
"name": "bet",
"type": "event"
}

If the abi provided contains an "id" input will be parsed to "uid" due to field "id" being reserved in the Moralis.Object

Historical Event Limit

If a plugin is created that would result in retrieving 250k or more historical events then the "Sync_historical (Optional) option will be disabled and no historical data will be saved. It is possible to contract support to upgrade your account to enable saving it anyway, but think hard about whether it's actually necessary before doing so. It's possible to handle the events in real-time without saving the data to the database. You can also call the Deep Index API to selectively query the historical data. See below for more details.

Editing

Open the Plugins list, go to the "Installed" tab, and click on the "..." button to open the editor. Since making any alterations is potentially a breaking change to the event schema, saving the changes requires providing a new tableName. Boo-urns! Yes, this inconvenient, but how plugins work will be changing soon anyway when the custom plugin platform is released and this will be reexamined then (so at least there's that to look forward to!).

If a new table name is not provided you'll see the following error message.

This error message will also appear if you accidently name it the same as an existing plugin

Tutorial

Sync and Watch Address

By default only the transaction data for registered users will appear in the EthTransactions collection. To watch a specific address- like a Centralized Exchange hot wallet- add a new Sync and Watch Address plugin, enter the Address then click the "Add Plugin" button.

This starts a sync job and adds an entry to the WatchedXxxAddress collection, where "Xxx" is one of the chain names defined here. Once the transactions are synced they can be queried like any other transactions, including Live Queries.

const web3 = new Moralis.Web3();
const binanceWallet = "0x...";
// create query
const query = new Moralis.Query("EthTransactions");
query.equalTo("to_address", binanceWallet);
// subscribe for real-time updates
const subscription = await query.subscribe();
subscription.on("create", function(data) {
const amountEth = web3.utils.fromWei(data.attributes.value);
console.log(`${amountEth} deposited to Binance`);
});

Keep in mind that watching an address with a lot of transaction volume will result in a lot of data being stored if the Sync_historical option is enabled.

See the Cloud Functions and Live Query sections for more details on these topics.

Historical Transaction Limit

If an address is watched that would result in retrieving 250k or more historical transactions then the Sync_historical option will be disabled and no historical data will be saved. It is possible to contract support to upgrade your account to enable saving it anyway, but think hard about whether it's actually necessary before doing so. It's possible to handle new transactions in real-time without saving the data to the database. You can also call the Deep Index API to selectively query the historical data. See below for more details.

Watch Address From Code

The Sync and Watch Address plugin calls a Cloud Function called watchXxxAddressunder the hood, where "Xxx" are the chain names here. These Cloud Functions can also be called directly from your own code!

const results = await Moralis.Cloud.run("watchBscAddress", {address: "0x..."})

The watch address functions return no value as they start a job. They are still asynchronous though! Once the promise returns the synced transactions should be in the XxxTransactions table for the corresponding chain.

Manual Event Handling

If a plugin is created that would result in retrieving 500,000 or more historical events then the Sync_historical option will be disabled and no historical data will be fetched. The contract event (or watched address transaction) will still be monitored by Moralis, but it requires a trigger to tell Moralis what to do with the data. Until a beforeConsume trigger is defined the default behavior will be to skip the event and log a message to the Dashboard.

A skipped events are logged to the Dashboard

beforeConsume Trigger

When historical data is not synced automatically a beforeConsume trigger needs to be created on the tableName given in the plugin definition.

// always save event
Morlis.Cloud.beforeConsume(eventTableName, (event) => {
return true;
});
// only save event if transaction is confirmed
Morlis.Cloud.beforeConsume(eventTableName, (event) => {
return event && event.confirmed;
});

The callback should

  • Return Boolean

    • When true the event will be saved, otherwise it will be skipped

  • Be synchronous

    • If a Promise is returned it will NOT be awaited and a since a promise is a truthy value the event data will be saved to the database

    • Because the callback is synchronous it should be as fast as possible. Ideally the callback will make it's decision based entirely on the input data with no database lookups or other outside calls (a "pure" function). If the callback is slow it could negatively effect the performance of your entire server.

  • Have an input parameter. This will contain the properties of the event data, not a Moralis object.

Address Casing

When syncing addresses from transactions or smart contracts, Moralis will normalize them to lowercase. This makes it easier to create queries based on address as it doesn't require creating a checksum address. Just be sure when comparing an address that comes from outside sources (like Metamask or Etherscan), to convert it to lowercase first.

// this is a checksum address (note the mixed cases)
let address = '0xEDe998b7BdE2467732b748613a1Aab4e5528dE15';
// In order to make queries based on the address you can convert it to lowercase
address = address.toLowerCase();
// Now we can use the address in our queries
const query = new Moralis.Query("EthTokenBalance");
query.equalTo("owner_of", address);

Unconfirmed Transactions

Transactions on Testnet and Mainnet can take a while to be confirmed. When Moralis detects a new transaction (or event) in an unconfirmed state these get put into transaction tables like EthTransactions with confirmed: false. For aggregate collections like balances the unconfirmed transaction entries are put in separate collections postfixed with "Pending".

  • EthBalancePending

  • EthNFTOwnersPending

  • etc.

When the transaction gets confirmed the status is updated to confirmed: true and any corresponding entries in pending collections are merged into their respective main collections.

Consequences for Triggers

This means if you define an afterSave trigger on a collection with a confirmed property like EthTransactions or any "Sync and Watch Contract Event" collection, then the trigger can get fired TWICE- once for confirmed: false and again for confirmed: true! See the Trigger section for more details.