Pangea x Movement Labs

Pangea x Movement Labs

Powering User Experience, Trading and AI with our fast streaming technology, replacing traditional RPC.

Movement Labs has emerged as a groundbreaking force in the blockchain ecosystem with its general-purpose chain that combines the best of several technologies: Ethereum's security as a sidechain, Celestia's data availability, decentralised shared sequencing, optimistic rollup capabilities, and the MoveVM for execution. Pangea launched with Movement mainnet on day 0. Providing our full support for Movement Bardock and Mainnet.

In this article we provide an overview of our support for Move and what you can expect from Pangea and how you can start building today.

What is Movement?

Movement Mainnet is Movement Labs' general-purpose chain designed to deliver fast transaction throughput. It achieves this through a unique architecture that includes:

  • An L2 sidechain on Ethereum
  • Celestia Data Availability
  • Shared sequencing or transaction ordering and fairness
  • Optimistic Rollup and dual-settlement with Fast Finality Service (FFS)
  • MoveVM enabling secure and efficient execution

A key distinction of Move chains are their transaction-based versioned database structure. Rather than traditional block-based organisation, Movement uses versions for each transaction, which Pangea seamlessly supports in our data model. In Pangea Transaction Versions are interchangable with Blocks for comparison in our cross chain analytics.

Pangea's Data Infrastructure for Movement

Protocols building on Movement now have access to Pangea's comprehensive data infrastructure through our API or Client SDKs. We have Rust, Nodejs and Python with Golang coming soon.

Let me demonstrate the power of Pangea with some examples in Rust with our `pangea-client` Rust library in 3 easy steps.

1. Install the client and other dependencies:

cargo add pangea-client dotenvy futures tokio -F tokio/rt-multi-thread

2. Set up authentication:

Pleasae apply for credentials here and add them to your environment.

# .env file
PANGEA_USERNAME=xxxxx
PANGEA_PASSWORD=xxxxx

3. Initialise the client and start streaming data:

// Load credentials
dotenv::dotenv_override().ok();

// Build client
let client = ClientBuilder::default()
    .build::<WsProvider>()
    .await
    .unwrap();

// Setup filter and start streaming
// ...

Listed next are 3 examples to get you up and running with Transactions, Logs and Receipts.

1. Transaction Data Access

Access transaction data with fine-grained filters using our Rust client:

use pangea_client::{
    ClientBuilder, WsProvider, Format,
    provider::MoveProvider,
    requests::movement::GetMoveTxsRequest,
    core::types::ChainId,
    query::Bound,
};
use futures::StreamExt;
use std::collections::HashSet;

#[tokio::main]
async fn main() {
    // Load credentials from .env
    dotenvy::dotenv_override().ok();

    // Initialize the client
    let client = ClientBuilder::default()
        .endpoint("movement.app.pangea.foundation")
        .build::<WsProvider>()
        .await
        .unwrap();

    // Create request filter
    let request = GetMoveTxsRequest {
        chains: HashSet::from([ChainId::MOVEMENT]),
        from_block: Bound::FromLatest(10),
        to_block: Bound::Subscribe, // Real-time
        ..Default::default()
    };

    // Make the request with JSON or Arrow
    let stream = match client
        .get_move_txs_by_format(request, Format::JsonStream, false)
        .await
    {
        Ok(stream) => stream,
        Err(e) => {
            eprintln!("Request failed\n{e}");
            return;
        }
    };

    futures::pin_mut!(stream);

    while let Some(chunk) = stream.next().await {
        let lines = String::from_utf8(chunk.unwrap()).unwrap();
        println!("token {lines}");
    };
}

2. Event Logs

Pangea offers two levels of log access: raw and decoded. Use the function calls on the client get_move_logs_by_format or get_move_logs_decoded_by_format. See:

use pangea_client::{
    ClientBuilder, WsProvider, Format,
    provider::MoveProvider,
    requests::movement::GetMoveLogsRequest,
    core::types::ChainId,
    query::Bound,
};
use futures::StreamExt;
use std::collections::HashSet;

#[tokio::main]
async fn main() {
    // Load credentials from .env
    dotenvy::dotenv_override().ok();

    // Initialize the client
    let client = ClientBuilder::default()
        .endpoint("movement.app.pangea.foundation")
        .build::<WsProvider>()
        .await
        .unwrap();

    let request = GetMoveLogsRequest {
        chains: HashSet::from([ChainId::MOVEMENT]),
        from_block: Bound::FromLatest(10),
        to_block: Bound::Subscribe,
        ..Default::default()
    };
    
    let stream = match client
        .get_move_logs_decoded_by_format(request, Format::JsonStream, true)
        .await
    {
        Ok(stream) => stream,
        Err(e) => {
            eprintln!("Request failed\n{e}");
            return;
        }
    };

    futures::pin_mut!(stream);

    while let Some(chunk) = stream.next().await {
        let lines = String::from_utf8(chunk.unwrap()).unwrap();
        println!("token {lines}");
    };

}

3. Transaction Receipts

use pangea_client::{
    ClientBuilder, WsProvider, Format,
    provider::MoveProvider,
    requests::movement::GetMoveReceiptsRequest,
    core::types::ChainId,
    query::Bound,
};
use futures::StreamExt;
use std::collections::HashSet;

#[tokio::main]
async fn main() {
    // Load credentials from .env
    dotenvy::dotenv_override().ok();

    // Initialize the client
    let client = ClientBuilder::default()
        .endpoint("movement.app.pangea.foundation")
        .build::<WsProvider>()
        .await
        .unwrap();

    let request = GetMoveReceiptsRequest {
        chains: HashSet::from([ChainId::MOVEMENT]),
        from_block: Bound::FromLatest(10),
        to_block: Bound::Subscribe,
        ..Default::default()
    };
    
    let stream = match client
        .get_move_receipts_by_format(request, Format::JsonStream, true)
        .await
    {
        Ok(stream) => stream,
        Err(e) => {
            eprintln!("Request failed\n{e}");
            return;
        }
    };

    futures::pin_mut!(stream);

    while let Some(chunk) = stream.next().await {
        let lines = String::from_utf8(chunk.unwrap()).unwrap();
        println!("token {lines}");
    };

}

The decoded logs transforms raw binary event data into structured JSON, making it immediately usable for analysis, monitoring, and application development.

3. Flexible Filtering Capabilities

Our Rust client supports extensive filtering options:

// Filter by module and event name
let request = GetLogsRequest {
    chains: HashSet::from([ChainId::MOVEMENT]),
    from_block: Bound::Exact(10000),
    to_block: Bound::Exact(10100),
    module__in: Some(vec!["transaction_fee".to_string()]),
    event_name__in: Some(vec!["FeeStatement".to_string()]),
    ..Default::default()
};

// Filter by address
let request = GetLogsRequest {
    chains: HashSet::from([ChainId::MOVEMENT]),
    from_block: Bound::Exact(10000),
    to_block: Bound::Exact(10100),
    address__in: Some(vec!["0x000000000000000000000000000000000000000000000000000000000a550c18".to_string()]),
    ..Default::default()
};

// Filter by transaction type
let request = GetTransactionsRequest {
    chains: HashSet::from([ChainId::MOVEMENT]),
    from_block: Bound::Exact(10000),
    to_block: Bound::Exact(10100),
    kind__in: Some(vec!["UserTransaction".to_string()]),
    ..Default::default()
};

How Protocols Benefit from Pangea's Support

No matter which type of protocol you're building we offer useable datasets for you to get started on day 1. No frills, just fully decoded, history and real-time streams delivered immediately.

Data-Driven DeFi Protocols

// Track and analyse on-chain liquidity in real-time
async fn monitor_liquidity_events() -> Result<(), Box<dyn std::error::Error>> {
    let client = ClientBuilder::default().build::<WsProvider>().await?;
    
    let request = GetLogsDecodedRequest {
        chains: HashSet::from([ChainId::MOVEMENT]),
        from_block: Bound::FromLatest(100),  // Last 100 versions
        to_block: Bound::Subscribe,
        module__in: Some(vec!["liquidity_pool".to_string()]),
        event_name__in: Some(vec!["LiquidityAdded".to_string(), "LiquidityRemoved".to_string()]),
        ..Default::default()
    };
    
    let stream = client
        .get_logs_decoded_by_format(request, Format::JsonStream, false)
        .await?;
    futures::pin_mut!(stream);
    
    while let Some(Ok(data)) = stream.next().await {
        // Process real-time liquidity events
        // ...
    }
    
    Ok(())
}

Tokens and NFTs

// Stream NFT transfer events
async fn track_nft_transfers() -> Result<(), Box<dyn std::error::Error>> {
    let client = ClientBuilder::default().build::<WsProvider>().await?;
    
    let request = GetLogsDecodedRequest {
        chains: HashSet::from([ChainId::MOVEMENT]),
        to_block: Bound::Subscribe,
        module__in: Some(vec!["nft".to_string()]),
        event_name__in: Some(vec!["TransferEvent".to_string()]),
        ..Default::default()
    };
    
    let stream = client
        .get_logs_decoded_by_format(request, Format::JsonStream, false)
        .await?;
    futures::pin_mut!(stream);
    
    while let Some(Ok(data)) = stream.next().await {
        // Process NFT transfer events in real-time
        // ...
    }
    
    Ok(())
}

Swap out with Interest Protocol and Yuzu

// Monitor governance proposal execution
async fn track_governance_events() -> Result<(), Box<dyn std::error::Error>> {
    let client = ClientBuilder::default().build::<WsProvider>().await?;
    
    let request = GetLogsDecodedRequest {
        chains: HashSet::from([ChainId::MOVEMENT]),
        module__in: Some(vec!["governance".to_string()]),
        event_name__in: Some(vec!["ProposalCreated".to_string(), "ProposalExecuted".to_string(), "VoteCast".to_string()]),
        ..Default::default()
    };
    
    let stream = client
        .get_logs_decoded_by_format(request, Format::JsonStream, false)
        .await?;
    futures::pin_mut!(stream);
    
    while let Some(Ok(data)) = stream.next().await {
        // Process governance events
        // ...
    }
    
    Ok(())
}

This is just the beginning

With Pangea's support for Movement you can:

  • Replace RPC with Pangea
  • Track your portfolio
  • Enhanced Move data by Pangea's unique streaming method
  • Create advanced analytics and dashboards out of the box
  • KPIs and real-time alerting for on-chain events
  • Data warehousing workflows start with useable data

Pangea's support for Bitcoin, EVM and now Move represents our commitment to providing best-in-class data infrastructure across the most innovative blockchain ecosystems. By combining Movement's high-performance architecture with Pangea's streaming method, protocols can focus on building exceptional user experiences while we handle the complexity of blockchain data access and processing.

Whether you're building DeFi applications, NFT platforms, or governance systems, Pangea provides the foundational building blocks and does them well enabling your protocol to scale confidently on Movement.

Sign up today https://pangea.foundation