Skip to main content

🔹Use in Browser

Use in browser

For Web3 applications, users usually operate their data through the Ethereum wallet in the browser. However, unlike Node.js, the browser cannot access the user's private key, so signature authorization can only be performed through the Ethereum wallet, such as MetaMask, WalletConnect. After completing this step, the subsequent usage is consistent with Node.js.

Create project

We can create a react-ts template project based on Vite. If it is integrated with an existing project, you can skip this step

pnpm create vite glacier-quickstart --template react-ts

The complete sample https://github.com/Glacier-Labs/glacier-quickstart

More complex project can refer to https://github.com/Glacier-Labs/js-glacier/tree/main/apps/playground

Install SDK

Enter glacier-quickstart directory and execute the command

pnpm add @glacier-network/client

Connect wallet

Next, we create a React Hook to connect to the browser wallet MetaMask. The code is as follows:

src/useMetaMask.ts
import { useState, useEffect, useCallback } from 'react';
import { MetaMaskInpageProvider } from '@metamask/providers';

export default function useMetaMask() {
const [account, setAccount] = useState<string>();
const [provider, setProvider] = useState<MetaMaskInpageProvider>();

const connect = useCallback(async () => {
const accounts = await window.ethereum.request<string[]>({
method: 'eth_requestAccounts',
params: [],
});
if (accounts) setAccount(accounts[0]);
}, []);

const eagerConnect = useCallback(async () => {
if (!window.ethereum) return;
const accounts = await window.ethereum?.request<string[]>({
method: 'eth_accounts',
params: [],
});
if (accounts) setAccount(accounts[0]);
setProvider(window.ethereum);
}, []);

useEffect(() => {
window.ethereum?.on('accountsChanged', (args) => {
const accounts = args as string[];
if (accounts) setAccount(accounts[0]);
setProvider(window.ethereum);
});
}, []);

return {
account,
provider,
connect,
eagerConnect,
};
}

Connect Glacier

To operate the data of Glacier, you need to sign in through the Ethereum wallet. So now we create another Hook to encapsulate the relevant operations, which includs:

  • Instantiate GlacierClient
  • Create Namespace
  • Enum Namespace

The creation of Namespace will call the wallet for signature confirmation.

src/useGlacier.ts
import { useState, useCallback, useMemo, useEffect } from 'react';
import { GlacierClient, NamespaceRecord } from '@glacier-network/client';

import useMetaMask from './useMetaMask';

export default function useGlacier() {
const [spaces, setSpaces] = useState<NamespaceRecord[]>([]);
const { provider, account, connect, eagerConnect } = useMetaMask();

const client = useMemo(() => {
return new GlacierClient('https://p0.onebitdev.com/glacier-gateway', {
provider,
});
}, [provider]);

const listNamespace = useCallback(async () => {
if (!account) return setSpaces([]);
const result = await client.namespaces(account);
setSpaces(result);
}, [client, account]);

const createNamespace = useCallback(
async (name: string) => {
const result = await client.createNamespace(name);
return result;
},
[client]
);

useEffect(() => {
listNamespace();
}, [listNamespace]);

return {
client,
spaces,
account,
connect,
eagerConnect,
listNamespace,
createNamespace,
};
}

Pay attention to this part from the above codes:

const client = useMemo(() => {
return new GlacierClient('https://p0.onebitdev.com/glacier-gateway', {
provider,
});
}, [provider]);

When instantiating GlacierClient, provider, that is, window.ethereum is passed in instead of privateKey, which is also the main difference between Browser and Node.js. If you use other popular wallet connection libraries in your project, such as web3-react, you can refer to the following usage:

import { useWeb3React } from '@web3-react/core';
import { Web3Provider } from '@ethersproject/providers';

export default function App() {
const { account, library } = useWeb3React<Web3Provider>();

const client = new GlacierClient('https://p0.onebitdev.com/glacier-gateway', {
provider: library?.provider,
});
}

Write component

Finally, let's write a React component to connect the previous Hook. The code is as follows:

src/App.tsx
import { useEffect, useState } from 'react';
import { Button, Form, ListGroup } from 'react-bootstrap';
import useGlacier from './useGlacier';

export default function App() {
const [name, setName] = useState('');
const [loading, setLoading] = useState(false);
const {
account,
spaces,
connect,
eagerConnect,
createNamespace,
listNamespace,
} = useGlacier();

useEffect(() => {
eagerConnect();
}, [eagerConnect]);

const onCreate = async () => {
try {
setLoading(true);
const result = await createNamespace(name);
await listNamespace();
alert(result.insertedId);
} catch (error) {
console.trace(error);
} finally {
setLoading(false);
}
};

if (!window.ethereum) {
return (
<div className="container my-3">
<a href="https://metamask.io/" target="_blank" rel="noreferrer">
<Button>Install MetaMask</Button>
</a>
</div>
);
}

if (!account) {
return (
<div className="container my-3">
<Button variant="success" onClick={connect}>
Connect Wallet
</Button>
</div>
);
}

return (
<div className="container">
<div className="my-3">Connected: {account}</div>
<Form>
<Form.Group className="mb-3">
<Form.Control
type="text"
placeholder="Namespace"
value={name}
onChange={(e) => {
setName(e.target.value.trim());
}}
/>
</Form.Group>
<Button
variant="primary"
disabled={loading || !name}
onClick={onCreate}
>
Create Namespace
</Button>
</Form>
<div className="my-3">My Namespaces:</div>
<ListGroup>
{spaces.map((item) => (
<ListGroup.Item key={item.namespace}>{item.namespace}</ListGroup.Item>
))}
</ListGroup>
</div>
);
}

Well done! Check the following result: