Using Load Network's 0xbabe2 transaction format for large data uploads - the largest EVM transaction in history
About 0xbabe2 Transaction Format
0xbabe2 is the newest data transaction format from the Bundler data protocol. Also called "Large Bundle," it's a bundle under version 0xbabe2 (address: 0xbabe2dCAf248F2F1214dF2a471D77bC849a2Ce84) that exceeds the Load Network L1 and 0xbabe1 transaction input size limits, introducing incredibly high size efficiency to data storage on Load Network.
For example, with Alphanet v0.4.0 metrics running at 500 mgas/s, a Large Bundle has a max size of 246 GB. However, to ensure a smooth DevX and optimal finalization period (aka "safe mode"), we have limited the 0xbabe2 transaction input limit to 2GB at the Bundler SDK level. If you want higher limits, you can achieve this by changing a simple constant!
In simple terms, a Large Bundle consists of n smaller chunks (standalone bundles) that are sequentially connected tail-to-head and then at the end the Large Bundle is a reference to all the sequentially related chunks, packing all of the chunks IDs in a single 0xbabe2 bundle and sending it to Load Network.
To dive deeper into the architecture design behind 0xbabe2 and how it works, check out the 0xbabe2 section in the Bundler documentation.
with the upcoming Load Network network release (Alphanet v0.5.0) reaching 1 gigagas/s – 0xbabe2 data size limit will double to 492GB, almost 0.5TB EVM transaction.
0xbabe2 Broadcasting
Broadcasting an 0xbabe2 to Load Network can be done via the Bundler Rust SDK through 2 ways: the normal 0xbabe2 broadcasting (single-wallet single-threaded) or through the multi-wallet multi-threaded method (using SuperAccount).
Single-Threaded Broadcasting
Uploading data via the single-threaded method is efficient when the data isn't very large; otherwise, it would have very high latency to finish all data chunking then bundle finalization:
Multi-Threaded 0xbabe2 broadcasting is done via a multi-wallet architecture that ensures parallel chunks settlement on Load Network, maximizing the usage of the network's data throughput. To broadcast a bundle using the multi-threaded method, you need to initiate a SuperAccount instance and fund the Chunkers:
A Super Account is a set of wallets created and stored as keystore wallets locally under your chosen directory. In Bundler terminology, each wallet is called a "chunker". Chunkers optimize the DevX of uploading Large Bundle's chunks to LN by allocating each chunk to a chunker (~4MB per chunker), moving from a single-wallet single-threaded design in data uploads to a multi-wallet multi-threaded design.
0xbabe2 Data Retrieval
0xbabe2 transaction data retrieval can be done either using the Rust SDK or the REST API. Using the REST API to resolve (chunk reconstruction until reaching final data) is faster for user usage as it does chunks streaming, resulting in near-instant data usability (e.g., rendering in browser).
use bundler::utils::core::super_account::SuperAccount;
// init SuperAccount instance
let super_account = SuperAccount::new()
.keystore_path(".bundler_keystores".to_string())
.pwd("weak-password".to_string()) // keystore pwd
.funder("private-key".to_string()) // the pk that will fund the chunkers
.build();
// create chunkers
let _chunkers = super_account.create_chunkers(Some(256)).await.unwrap(); // Some(amount) of chunkers
// fund chunkers (1 tWVM each)
let _fund = super_account.fund_chunkers().await.unwrap(); // will fund each chunker by 1 tWVM
// retrieve chunkers
let loaded_chunkers = super_account.load_chunkers(None).await.unwrap(); // None to load all chunkers
async fn send_large_bundle_multi_thread() -> Result<String, Error> {
// will fail until a tLOAD funded EOA (pk) is provided, take care about nonce if same wallet is used as in test_send_bundle_with_target
let private_key =
String::from("6f142508b4eea641e33cb2a0161221105086a84584c74245ca463a49effea30b");
let content_type = "text/plain".to_string();
let data = "~UwU~".repeat(8_000_000).as_bytes().to_vec();
let super_account = SuperAccount::new()
.keystore_path(".bundler_keystores".to_string())
.pwd("test".to_string());
let large_bundle = LargeBundle::new()
.data(data)
.private_key(private_key)
.content_type(content_type)
.super_account(super_account)
.chunk()
.build()
.unwrap()
.super_propagate_chunks()
.await
.unwrap()
.finalize()
.await
.unwrap();
println!("{:?}", large_bundle);
Ok(large_bundle)
}