The easiest way to access ContractKit in your React applications π₯. use-contractkit
is a React hook for managing access to ContractKit with a built-in headless modal system for connecting to your users wallet of choice.
Now your DApp can be made available to everyone in the Celo ecosystem, from Valora users to self custodied Ledger users.
By default use-contractkit is styled so that you can drop it into your application and go, however it's fully customisable so you can maintain a consistent UX throughout your application.
yarn add @celo-tools/use-contractkit
Wallet | sendTransaction | signTransaction | signTypedData | signPersonal |
---|---|---|---|---|
Plaintext private key | β | β | β | |
Ledger | β | β | β | |
WalletConnect | β | β | β | |
DappKit | β | β | ||
Celo Extension Wallet (Metamask fork) | β |
use-contractkit uses unstated-next under the hood to inject state throughout your application. unstated-next is built on top of the React Context API so you need to make sure your application is wrapped with the provider before usage.
import { ContractKitProvider } from '@celo-tools/use-contractkit';
import '@celo-tools/use-contractkit/lib/styles.css';
function WrappedApp() {
return (
<ContractKitProvider
dappName="My awesome dApp"
dappDescription="My awesome description"
dappUrl="https://example.com"
>
<App />
</ContractKitProvider>
);
}
function App() {
// your application code
}
use-contractkit provides a connect
function that will open a modal with a list of wallets your user can connect to.
import { useContractKit } from '@celo-tools/use-contractkit';
function App() {
const { connect, address } = useContractKit();
return (
<>
{account ? (
<div>Connected to {address}</div>
) : (
<button onClick={connect}>Connect wallet</button>
)}
</>
);
}
After connecting to an account the address
property will be set.
Now that we've connected to an account and have the users address, we can use the kit
to query on-chain data:
import { useContractKit } from '@celo-tools/use-contractkit';
function App() {
const { kit, address } = useContractKit();
async function getAccountSummary() {
const accounts = await kit.contracts.getAccounts();
await accounts.getAccountSummary(address);
}
return (
...
)
}
The biggest problem when developing DApps is ensuring a Web2 level experience while managing the flaky and often slow nature of blockchains. To that end we've designed use-contractkit in a way to abstract away most of that pain.
Initially connecting to a users account is one thing, handled via the connect
function we just mentioned. However once a user has connected to your DApp once we can make the experience nicer for them on repeat visits.
use-contractkit will remember a users last connected address when they navigate back to or refresh your DApp. Ensure that when developing your DApp nothing changes nothing in the UI whether or not the user has a kit.defaultAccount
property set.
import { useContractKit } from '@celo-tools/use-contractkit';
const { address } = useContractKit();
When a user refreshes or navigates back to your page, they may not necessarily have a connected account any longer, however we shouldn't need to prompt them to login again just to view the page, that can be done only when doing an action.
For that functionality we have the performActions
and getConnectedKit
methods. Usage looks a little like this for getConnectedKit
:
import { useContractKit } from '@celo-tools/use-contractkit';
function App() {
const { getConnectedKit } = useContractKit();
async function transfer() {
const kit = await getConnectedKit();
const cUSD = await kit.contracts.getStableToken();
await cUSD.transfer('0x...', 10000).sendAndWaitForReceipt();
}
return <button onClick={transfer}>Transfer</button>;
}
and this for performActions
:
import { useContractKit } from '@celo-tools/use-contractkit';
function App() {
const { performActions } = useContractKit();
async function transfer() {
await performActions(async (kit) => {
const cUSD = await kit.contracts.getStableToken();
await cUSD.transfer('0x...', 10000).sendAndWaitForReceipt();
});
}
return <button onClick={transfer}>Transfer</button>;
}
The performActions
method will also take care of displaying a modal to the user telling them to confirm any actions on their connected wallet.
use-contractkit provides a network
variable and an updateNetwork
function you can use to display the currently connected network as well as switch to a different one (ie. Alfajores, Baklava or Mainnet).
If you'd prefer your DApp to only access a specific network (maybe you're deploying your testnet website at https://test-app.dapp.name
and your mainnet version at https://app.dapp.name
) you can pass the network you want to use as a variable into the provider you wrap your application with:
import { ContractKitProvider, Alfajores } from '@celo-tools/use-contractkit';
function WrappedApp({ Component, pageProps }) {
return (
<ContractKitProvider
...
networks={[Alfajores]}
>
<App />
</ContractKitProvider>
);
}
function App () {
...
}
Be sure to check the use-contractkit example application for a showcase of how network management works in more depth. Usually you'll want to show a dropdown to your users allowing them to select the network to connect to.
import { useContractKit } from '@celo-tools/use-contractkit';
function App() {
const { network, updateNetwork } = useContractKit();
return <div>Currently connected to {network}</div>;
}
Struggling with anything use-contractkit related? Jump into the celo-org discord channel and ask for help any time.