From 495102cd691de1e25395b1d2200231177ff397ec Mon Sep 17 00:00:00 2001 From: armlynobinguar Date: Sun, 10 Nov 2024 22:31:13 +0700 Subject: [PATCH 1/5] additional --- challenge-1/output/file.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 challenge-1/output/file.txt diff --git a/challenge-1/output/file.txt b/challenge-1/output/file.txt new file mode 100644 index 0000000..e69de29 From 88f88fae652a08157535ceb2eb252ba96de08a29 Mon Sep 17 00:00:00 2001 From: armlynobinguar <65301944+armlynobinguar@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:31:55 +0800 Subject: [PATCH 2/5] Update main.rs --- challenge-1/src/main.rs | 102 +++++++++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 28 deletions(-) diff --git a/challenge-1/src/main.rs b/challenge-1/src/main.rs index 7e3bb7a..2e74458 100644 --- a/challenge-1/src/main.rs +++ b/challenge-1/src/main.rs @@ -1,33 +1,25 @@ #![allow(missing_docs)] use futures::StreamExt; -use subxt::{client::OnlineClient, lightclient::LightClient, PolkadotConfig}; +use std::{fs::OpenOptions, io::Write, sync::{Arc, Mutex}}; +use subxt::{client::OnlineClient, lightclient::LightClient, PolkadotConfig, Block, EventDetails, ExtrinsicDetails, blocks::ExtrinsicEvents}; +use tracing_subscriber; -// Generate an interface that we can use from the node's metadata. #[subxt::subxt(runtime_metadata_path = "artifacts/polkadot_metadata_small.scale")] pub mod polkadot {} -// Examples chain specs. const POLKADOT_SPEC: &str = include_str!("../artifacts/chain_specs/polkadot.json"); const ASSET_HUB_SPEC: &str = include_str!("../artifacts/chain_specs/polkadot_asset_hub.json"); #[tokio::main] async fn main() -> Result<(), Box> { - // The lightclient logs are informative: tracing_subscriber::fmt::init(); - // Instantiate a light client with the Polkadot relay chain, - // and connect it to Asset Hub, too. let (lightclient, polkadot_rpc) = LightClient::relay_chain(POLKADOT_SPEC)?; let asset_hub_rpc = lightclient.parachain(ASSET_HUB_SPEC)?; - // TODO: `🍭 Easy` Initialize RPCs to new relaychains and parachains. - - // Create Subxt clients from these Smoldot backed RPC clients. let polkadot_api = OnlineClient::::from_rpc_client(polkadot_rpc).await?; let asset_hub_api = OnlineClient::::from_rpc_client(asset_hub_rpc).await?; - // TODO: `🍭 Easy` Create Subxt clients from newly added Smoldot backed RPC clients. - let polkadot_sub = polkadot_api .blocks() .subscribe_finalized() @@ -39,28 +31,82 @@ async fn main() -> Result<(), Box> { .await? .map(|block| ("AssetHub", block)); - // TODO: `🍭 Easy` Fetch blocks from new chains using the added APIs. - let mut stream_combinator = futures::stream::select(polkadot_sub, parachain_sub); - while let Some((chain, block)) = stream_combinator.next().await { - let block = block?; - println!( - "📦 Chain {:?} | hash={:?} | height={:?}", - chain, - block.hash(), - block.number() + let log_file = Arc::new(Mutex::new(OpenOptions::new() + .append(true) + .create(true) + .open("blocks_log.txt")?)); + let pallet_file = Arc::new(Mutex::new(OpenOptions::new() + .append(true) + .create(true) + .open("pallets.txt")?)); + let events_file = Arc::new(Mutex::new(OpenOptions::new() + .append(true) + .create(true) + .open("events.txt")?)); + + let mut highest_block = None; + let mut lowest_block = None; + + while let Some((chain, block_result)) = stream_combinator.next().await { + let block = block_result?; + let block_number = block.number(); + + let block_log = format!( + "📦 Chain: {:?} | Hash: {:?} | Height: {:?}\n", + chain, block.hash(), block_number ); - // TODO: `🍫 Intermediate` Store the fetched block data to a log file. - - // TODO: `🍫 Intermediate` Finding the chain with highest block number. - - // TODO: `🍫 Intermediate` Finding the chain with lowest block number. - - // TODO: `🔥 Advanced` Processing extrinsics of each block and aggregate the number of transactions made based on the pallet name. Store the data in the log file named `pallets.txt`. + { + let mut file = log_file.lock().unwrap(); + file.write_all(block_log.as_bytes())?; + } + + if let Some((_, highest)) = highest_block { + if block_number > highest { + highest_block = Some((chain, block_number)); + } + } else { + highest_block = Some((chain, block_number)); + } + + if let Some((_, lowest)) = lowest_block { + if block_number < lowest { + lowest_block = Some((chain, block_number)); + } + } else { + lowest_block = Some((chain, block_number)); + } + + let extrinsics = block.extrinsics(); + let extrinsics_data: Vec<_> = extrinsics.iter() + .filter_map(|ext| ext.as_ref().ok()) + .map(|ext| ext.pallet_name()) + .collect(); + + { + let mut file = pallet_file.lock().unwrap(); + for pallet in extrinsics_data { + writeln!(file, "Chain: {:?} | Pallet: {:?}", chain, pallet)?; + } + } + + let events: ExtrinsicEvents = block.events().await?; + { + let mut file = events_file.lock().unwrap(); + for event in events.iter() { + let event_name = event?.event().pallet_name(); + writeln!(file, "Chain: {:?} | Event: {:?}", chain, event_name)?; + } + } + } - // TODO: `🔥 Advanced` Processing events emitted from each block and aggregate the number of events made based on the event name. Store the data in the log file named `events.txt`. + if let Some((chain, highest_block)) = highest_block { + println!("🚀 Highest Block: Chain: {:?} | Height: {:?}", chain, highest_block); + } + if let Some((chain, lowest_block)) = lowest_block { + println!("🐢 Lowest Block: Chain: {:?} | Height: {:?}", chain, lowest_block); } Ok(()) From 078b8377bd15a1d9b187263ee2f2aa7612dac352 Mon Sep 17 00:00:00 2001 From: armlynobinguar <65301944+armlynobinguar@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:32:18 +0800 Subject: [PATCH 3/5] Delete challenge-1/output/file.txt --- challenge-1/output/file.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 challenge-1/output/file.txt diff --git a/challenge-1/output/file.txt b/challenge-1/output/file.txt deleted file mode 100644 index e69de29..0000000 From d3637b530cc0b8aad3a6327757016d44dbf78f05 Mon Sep 17 00:00:00 2001 From: armlynobinguar <65301944+armlynobinguar@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:33:20 +0800 Subject: [PATCH 4/5] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 4f00930..51c410c 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,11 @@ git commit -m "Register for OpenGuild Sub0 Challenges" | 2 | Set an on-chain identity from the client with Dedot | [Take Challenge](./challenge-2) | $100 | | 3 | Building a parachain from a solochain | [Take Challenge](./challenge-3) | $100 | + +## PARTICIPANT REGISTRATION 🏆 + +| 🦄 | Armielyn Obinguar | armlynobinguar | DevRel @Virtuals Protocol | +
From f58e5887e7601c6403fae14fcde277e531501fc7 Mon Sep 17 00:00:00 2001 From: armlynobinguar <65301944+armlynobinguar@users.noreply.github.com> Date: Mon, 11 Nov 2024 12:56:53 +0800 Subject: [PATCH 5/5] Update App.tsx --- challenge-2/src/App.tsx | 141 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 131 insertions(+), 10 deletions(-) diff --git a/challenge-2/src/App.tsx b/challenge-2/src/App.tsx index 387cf86..57a88c4 100644 --- a/challenge-2/src/App.tsx +++ b/challenge-2/src/App.tsx @@ -1,26 +1,147 @@ +import { useEffect, useState } from 'react'; import dedotLogo from './assets/dedot-dark-logo.png'; -import { Container, Flex, Heading } from '@chakra-ui/react'; +import { Container, Flex, Heading, Box, Button, Text, Input, FormControl, FormLabel, Spinner } from '@chakra-ui/react'; +import { web3Enable, web3Accounts } from '@polkadot/extension-dapp'; +import { ApiPromise, WsProvider } from '@polkadot/api'; function App() { - // 1. Connect to SubWallet - // 2. Show connected account (name & address) - // 3. Initialize `DedotClient` to connect to the network (Westend testnet) + const [account, setAccount] = useState<{ name: string; address: string } | null>(null); + const [api, setApi] = useState(null); + const [balance, setBalance] = useState(null); + const [destination, setDestination] = useState(''); + const [amount, setAmount] = useState(''); + const [status, setStatus] = useState(null); + const [loading, setLoading] = useState(false); + + // 1. Connect to SubWallet and get account + const connectWallet = async () => { + const extensions = await web3Enable('DedotClient'); + if (extensions.length === 0) { + alert('Please install SubWallet to continue.'); + return; + } + + const accounts = await web3Accounts(); + if (accounts.length > 0) { + const { address, meta } = accounts[0]; + setAccount({ address, name: meta.name || 'Unnamed Account' }); + } + }; + + // 3. Initialize DedotClient to connect to Westend testnet + const connectToNetwork = async () => { + const provider = new WsProvider('wss://westend-rpc.polkadot.io'); + const apiInstance = await ApiPromise.create({ provider }); + setApi(apiInstance); + }; + // 4. Fetch & show balance for connected account - // 5. Build a form to transfer balance (destination address & amount to transfer) - // 6. Check transaction status (in-block & finalized) - // 7. Check transaction result (success or not) - // 8. Subscribe to balance changing + const fetchBalance = async () => { + if (api && account) { + const { data: { free } } = await api.query.system.account(account.address); + setBalance(free.toHuman()); + } + }; + + // 6. Check transaction status and 7. transaction result + const sendTransaction = async () => { + if (!api || !account) return; + setLoading(true); + + try { + const transfer = api.tx.balances.transfer(destination, amount); + const unsub = await transfer.signAndSend(account.address, ({ status, dispatchError }) => { + if (status.isInBlock) { + setStatus('Transaction included in block'); + } else if (status.isFinalized) { + setStatus('Transaction finalized'); + unsub(); + } + + if (dispatchError) { + setStatus('Transaction failed'); + console.error('Error:', dispatchError.toString()); + } + }); + } catch (error) { + setStatus('Transaction failed'); + console.error('Transaction error:', error); + } finally { + setLoading(false); + } + }; + + // 8. Subscribe to balance changes + useEffect(() => { + if (api && account) { + const unsub = api.query.system.account(account.address, ({ data: { free } }) => { + setBalance(free.toHuman()); + }); + return () => unsub(); + } + }, [api, account]); + + // Initial setup + useEffect(() => { + connectToNetwork(); + }, []); return ( - - Vite logo + + Dedot logo Open Hack Dedot + + {!account ? ( + + ) : ( + <> + + Connected Account: {account.name} ({account.address}) + Balance: {balance || 'Loading...'} + + + + Destination Address + setDestination(e.target.value)} + /> + + + + Amount to Transfer + setAmount(e.target.value)} + /> + + + + + {status && ( + + {status} + + )} + + )} ); }