Particle Network is the intent-centric, modular access layer of Web3. With Particle's Smart Wallet-as-a-Service, developers can curate a seamless user experience through modular and customizable EOA/AA embedded wallet components. Using MPC-TSS for key management, Particle can streamline user onboarding via their Web2 accounts –such as Google accounts, email addresses, and phone numbers.
Through APIs and SDKs available on both mobile and desktop platforms, developers can integrate Particle’s Wallet-as-a-Service across a variety of scenarios, with the capacity to be customized and implemented in a way that matches the specific needs of a given application.
To leverage Particle Network on alternative platforms, such as Android, iOS, React Native, Flutter, & Unity, kindly refer to Particle’s documentation.
Prerequisites
A working react project (by executing npx create-react-app project-name)
To leverage Particle Network, specifically Particle Connect, within your dApp, you'll need to first install the required libraries. In addition to this, if you'd like to use a standard Web3 library, such as ethers.js or web3.js, then you'll need to install theme too. For this guide, we'll be using ethers.js.
After successfully installing the aforementioned libraries, you'll need to head into your index.js (or .ts) file to configure Particle Connect. This specifically entails wrapping your App component with ModalProvider (imported from @particle-network/connectkit) and passing in options, which contains the parameters detailed below.
import { ModalProvider } from'@particle-network/connectkit';import { Klaytn } from'@particle-network/chains';import { evmWallets } from'@particle-network/connectors';constroot=ReactDOM.createRoot(document.getElementById('root') asHTMLElement);root.render( <React.StrictMode> <ModalProvideroptions={{ projectId:'replace with your projectId', clientKey:'replace with your clientKey', appId:'replace with your appId', chains: [ Klaytn ], wallet: { // optional: Wallet modal configuration visible:true,// Display wallet modal supportChains:[ Klaytn ], customStyle: {},// optional: Custom wallet style }, promptSettingConfig: { // optional: particle security account config// Prompt to set payment password upon social login. 0: None, 1: Once(default), 2: Always promptPaymentPasswordSettingWhenSign:1,// Prompt to set master password upon social login. 0: None(default), 1: Once, 2: Always promptMasterPasswordSettingWhenLogin:1 }, connectors:evmWallets({ projectId:'replace with your walletconnect projectId', showQrModal:false }), }}theme={'light'}language={'en'} // optional:Local language setting, default enwalletSort={['Particle Auth','Wallet']} // optional:Order of wallet categories > <App /> </ModalProvider> </React.StrictMode>);
Connecting Wallet
With your index.js file setup, you can move onto connecting your users through a central "Connect Wallet" button. To do this, you can import ConnectButton from @particle-network/connectkit alongside its corresponding css. Upon using ConnectButton within your App component, a standard "Connect Wallet" button will appear to facilitate connection.
With a wallet now successfully connected through ConnectButton, you can retrieve the users associated Klaytn address. Additionally, you can retrieve its current balance (in KLAY) through ethers.js, passing in the corresponding EIP-1193 provider object retrieved from useParticleProvider within @particle-network/connectkit.
import { useParticleProvider } from'@particle-network/connectkit';constprovider=useParticleProvider();const [address,setAddress] =useState("");const [balance,setBalance] =useState("");constconnectWallet=async() => {// this guide uses ethers version 6.3.0.constethersProvider=newethers.BrowserProvider(provider);// for ethers version below 6.3.0.// const provider = new ethers.providers.Web3Provider(web3authProvider);constethersProvider=newethers.BrowserProvider(provider);constsigner=awaitethersProvider.getSigner();// Get user's Ethereum public addressconstaddress=signer.address;// Get user's balance in etherconstbalance=ethers.formatEther(awaitethersProvider.getBalance(address) // balance is in wei );setAddress(address);setBalance(balance);return ( <divclassName="App"> <buttononClick={connectWallet}>Connect Wallet</button> <div>Wallet Address: ${address} Balance: ${balance}</div> </div> );}
Disconnecting Wallet
Once a user has logged in, you can programmatically force a logout through disconnect derived from useParticleConnect. This will disconnect the current active session from your dApp, returning the user to their initial state.
import { useParticleConnect } from'@particle-network/connectkit';const { disconnect } =useParticleConnect();functionApp() {constdisconnectUser=async () => {awaitdisconnect();refreshState();}// refresh stateconstrefreshState= () => {setAddress();setBalance();// make sure to add every other useState modifier function declared here.}return ( <divclassName="App"> <buttononClick={disconnectUser}>Disconnect</button> </div> );}
Getting User Info
While traditional Web3 wallets are offered as connection mechanisms through Particle Connect, social logins through social accounts such as your email address, Google account, phone number, etc. are also available. If a user decides to log in with a Web2 account, you'll have the ability to call getUserInfo from @particle-network/auth-core, which will return an object containing key details such as their name, email, wallet addresses, etc.
With a provider initialized (through useParticleProvider) and passed into your ethers.js instance, message signing can be initiated as usual through signer.signMessage.This will directly display a signature popup for the user to confirm. Its specific nature will depend on which connection mechanism the user chose.
// add to the existing useState hook.const [signedMessage,setSignedMessage] =useState("");constsignMessage=async(e) => {e.preventDefault();if (!provider) {console.log("provider not initialized yet");return; }// this guide uses ethers version 6.3.0.constethersProvider=newethers.BrowserProvider(provider);// for ethers version below 6.3.0.// const provider = new ethers.providers.Web3Provider(provider);constsigner=awaitethersProvider.getSigner();constoriginalMessage=e.target.message.value;constresult=awaitsigner.signMessage(originalMessage);setSignedMessage(result) }return ( <divclassName="App"> <formonSubmit={signMessage}> <inputtype="text"name="message"placeholder="Set message"required/> <inputtype="submit"value="Sign Message"/> </form> <div>SignedMessage: ${signedMessage}</div> </div> );
Sending Native Transaction
Similar to signer.signMessage, you can use the same provider mechanism to send a native transaction, with KLAY in this case. This can be done through signer.sendTransaction, passing in standard fields such as to, value, and so on.
// add to the existing useState hook.const [txHash,setTxHash] =useState();constsendKlay=async () => {if (!provider) {console.log("provider not initialized yet");return; }constdestination="paste recipient address";// this guide uses ethers version 6.3.0.constethersProvider=newethers.BrowserProvider(provider);// for ethers version below 6.3.0.// const provider = new ethers.providers.Web3Provider(provider);constsigner=awaitethersProvider.getSigner();// Submit transaction to the blockchain and wait for it to be minedconsttx=awaitsigner.sendTransaction({ to: destination, value:ethers.parseEther("0.1"), maxPriorityFeePerGas:"5000000000",// Max priority fee per gas maxFeePerGas:"6000000000000",// Max fee per gas })constreceipt=awaittx.wait();setTxHash(receipt.hash)}return ( <divclassName="App"> <buttononClick={sendKlay}>Send Klay</button> <div>Send-Klay Tx Hash : {txHash ? <a href={`https://baobab.scope.klaytn.com/tx/${txHash}`} target="_blank">Klaytnscope</a> : ' ' } </div>
</div>);
Working with a Smart Contract
Deploying a Contract
More complex transactions, such as contract deployments, are also possible through Particle, whether you're using an external Web3 wallet or the included social login embedded wallet. An example of this is shown below.
// add to the existing useState hook.const [contractAddress,setContractAddress] =useState(null);constdeployContract=async () => {if (!provider) {console.log("provider not initialized yet");return; }// this guide uses ethers version 6.3.0.constethersProvider=newethers.BrowserProvider(provider);// for ethers version below 6.3.0.// const provider = new ethers.providers.Web3Provider(provider);constsigner=awaitethersProvider.getSigner();// paste your contractABIconstcontractABI= [ {"inputs": [ {"internalType":"uint256","name":"_initNum","type":"uint256" } ],"stateMutability":"nonpayable","type":"constructor" }, {"inputs": [],"name":"retrieve","outputs": [ {"internalType":"uint256","name":"","type":"uint256" } ],"stateMutability":"view","type":"function" }, {"inputs": [ {"internalType":"uint256","name":"num","type":"uint256" } ],"name":"store","outputs": [],"stateMutability":"nonpayable","type":"function" } ]// Paste your contract byte code const contractBytecode = '608060405234801561001057600080fd5b506040516102063803806102068339818101604052810190610032919061007a565b80600081905550506100a7565b600080fd5b6000819050919050565b61005781610044565b811461006257600080fd5b50565b6000815190506100748161004e565b92915050565b6000602082840312156100905761008f61003f565b5b600061009e84828501610065565b91505092915050565b610150806100b66000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100a1565b60405180910390f35b610073600480360381019061006e91906100ed565b61007e565b005b60008054905090565b8060008190555050565b6000819050919050565b61009b81610088565b82525050565b60006020820190506100b66000830184610092565b92915050565b600080fd5b6100ca81610088565b81146100d557600080fd5b50565b6000813590506100e7816100c1565b92915050565b600060208284031215610103576101026100bc565b5b6000610111848285016100d8565b9150509291505056fea26469706673582212200370e757ac1c15a024febfa9bf6999504ac6616672ad66bd654e87765f74813e64736f6c63430008120033'
constcontractFactory=newContractFactory(contractABI, contractBytecode, signer);constcontract=awaitcontractFactory.deploy(400);// get contract addresssetContractAddress(contract.target)}return ( <divclassName="App"> <buttononClick={deployContract}>Deploy Contract</button> <div>Contract Address: {contractAddress ? contractAddress :''} </div> </div> );
Similarly, you can send write transactions directly to an existing (deployed) contract using the same ethers.js instance leveraging the Particle Connect provider derived from useParticleProvider. On the frontend, this functionality will mimic that of a contract deployment, message signature, or transaction request.
Writing to a Contract
// add to existing useState hookconst [contractTx,setContractTx] =useState();constwriteToContract=async (e) => {e.preventDefault();if (!provider) {console.log("provider not initialized yet");return; }// this guide uses ethers version 6.3.0.constethersProvider=newethers.BrowserProvider(provider);// for ethers version below 6.3.0.// const provider = new ethers.providers.Web3Provider(provider);constsigner=awaitethersProvider.getSigner();// Paste your contractABIconstcontractABI= [ {"inputs": [ {"internalType":"uint256","name":"_initNum","type":"uint256" } ],"stateMutability":"nonpayable","type":"constructor" }, {"inputs": [],"name":"retrieve","outputs": [ {"internalType":"uint256","name":"","type":"uint256" } ],"stateMutability":"view","type":"function" }, {"inputs": [ {"internalType":"uint256","name":"num","type":"uint256" } ],"name":"store","outputs": [],"stateMutability":"nonpayable","type":"function" } ]// Paste your contract addressconstcontractAddress="0x3b01E4025B428fFad9481a500BAc36396719092C";constcontract=newethers.Contract(contractAddress, contractABI, signer);constvalue=e.target.store_value.value;// Send a transaction to smart contract to update the valueconsttx=awaitcontract.store(value);// Wait for the transaction to finishconstreceipt=awaittx.wait();constresult=receipt.hash;setContractTx(result) }return ( <divclassName="App"> <formonSubmit={writeToContract}> <inputname="store_value"placeholder="Set contract value"required/> <inputtype="submit"value="Store"/> </form> <div>Write-to-contract Tx Hash: ${contractTx}</div> </div>);
Reading from a Contract
Without using the wallet itself, purely the provider, read-only methods can be called on contracts through a standard ethers.js instance. This mechanism won't deviate from the typical structure associated with such an action, the primary difference here is the usage of the integrated provider object.
// add to existing useState hookconst [contractMessage,setContractMessage] =useState();constreadFromContract=async () => {if (!provider) {console.log("provider not initialized yet");return; }// this guide uses ethers version 6.3.0.constethersProvider=newethers.BrowserProvider(provider);// for ethers version below 6.3.0.// const provider = new ethers.providers.Web3Provider(provider);// paste your contract ABIconstcontractABI= [ {"inputs": [ {"internalType":"uint256","name":"_initNum","type":"uint256" } ],"stateMutability":"nonpayable","type":"constructor" }, {"inputs": [],"name":"retrieve","outputs": [ {"internalType":"uint256","name":"","type":"uint256" } ],"stateMutability":"view","type":"function" }, {"inputs": [ {"internalType":"uint256","name":"num","type":"uint256" } ],"name":"store","outputs": [],"stateMutability":"nonpayable","type":"function" } ]// paste your contract addressconstcontractAddress="0x3b01E4025B428fFad9481a500BAc36396719092C"; constcontract=newethers.Contract(contractAddress, contractABI, ethersProvider)// Reading a message from the smart contractconstcontractMessage=awaitcontract.retrieve();setContractMessage(contractMessage.toString()) }return ( <buttononClick={readFromContract}>Read From Contract</button> <div>Read-from-contract Message: ${contractMessage}</div> )
Next Steps
For additional guides regarding Particle Network (Particle Connect, Particle Auth, and other SDKs), please refer to the Particle Network docs and the Particle Network GitHub account. Additionally, you may want to visit the Particle Network blog for additional information on Particle Network's services, upcoming releases, and tech stack. Also, you can find the full implementation of the code for this guide on GitHub.