Skip to content

Commit 94718e7

Browse files
authored
feat: useContract hook (#93)
* feat(hooks): add `useContract` hook (#39) * feat(hooks): add storybook component for `useContract()` hook (#39)
1 parent 0aad553 commit 94718e7

File tree

5 files changed

+159
-1
lines changed

5 files changed

+159
-1
lines changed

.changeset/angry-rats-kick.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@web3-ui/hooks': minor
3+
---
4+
5+
add useContract hook

packages/hooks/src/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export { useWallet } from './useWallet';
2+
export { useContract } from './useContract';
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React from 'react';
2+
import { Web3Context } from '../Provider';
3+
import { Contract } from 'ethers';
4+
5+
export function useContract(address: string, abi) {
6+
const context = React.useContext(Web3Context);
7+
const [contract, setContract] = React.useState({});
8+
React.useEffect(() => {
9+
if (context) {
10+
const newContract = new Contract(address, abi, context.signer || undefined);
11+
const contractInterface = Object.values(newContract.interface.functions).reduce(
12+
(accumulator, funcFragment) => {
13+
return { ...accumulator, [funcFragment.name]: newContract[funcFragment.name] };
14+
},
15+
{}
16+
);
17+
setContract(contractInterface);
18+
}
19+
}, [context]);
20+
return contract;
21+
}

packages/hooks/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export { useWallet } from './hooks';
1+
export { useWallet, useContract } from './hooks';
22
export { useTokenBalance } from './hooks/useTokenBalance';
33
export { Provider, Web3Context } from './Provider';
44
export type { ProviderProps, Web3ContextType } from './Provider';
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { storiesOf } from '@storybook/react';
2+
import React from 'react';
3+
import { Provider, useWallet, useContract } from '..';
4+
import { Button, Input, Grid, GridItem } from '@chakra-ui/react';
5+
import { ethers } from 'ethers';
6+
7+
const ADDRESS = '0x7e1D33FcF1C6b6fd301e0B7305dD40E543CF7135'; // Rinkeby
8+
const ABI = [
9+
{
10+
inputs: [
11+
{
12+
internalType: 'string',
13+
name: '_greeting',
14+
type: 'string',
15+
},
16+
],
17+
stateMutability: 'nonpayable',
18+
type: 'constructor',
19+
},
20+
{
21+
inputs: [],
22+
name: 'greet',
23+
outputs: [
24+
{
25+
internalType: 'string',
26+
name: '',
27+
type: 'string',
28+
},
29+
],
30+
stateMutability: 'view',
31+
type: 'function',
32+
},
33+
{
34+
inputs: [
35+
{
36+
internalType: 'string',
37+
name: '_greeting',
38+
type: 'string',
39+
},
40+
],
41+
name: 'setGreeting',
42+
outputs: [],
43+
stateMutability: 'nonpayable',
44+
type: 'function',
45+
},
46+
{
47+
inputs: [
48+
{
49+
internalType: 'address payable',
50+
name: '_to',
51+
type: 'address',
52+
},
53+
],
54+
name: 'transferTo',
55+
outputs: [],
56+
stateMutability: 'payable',
57+
type: 'function',
58+
},
59+
];
60+
61+
const Default = () => {
62+
const { connectWallet, disconnectWallet, connected } = useWallet();
63+
const contract = useContract(ADDRESS, ABI);
64+
const [state, setState] = React.useState({
65+
newGreeting: '',
66+
toAddress: '',
67+
amount: '0',
68+
});
69+
70+
const handleGreet = async () => alert(await contract.greet());
71+
const handleChangeState =
72+
(stateName: string) =>
73+
({ target: { value } }) => {
74+
setState({ ...state, [stateName]: value });
75+
};
76+
const handleSetGreeting = async () => {
77+
await contract.setGreeting(state.newGreeting);
78+
setState({ ...state, newGreeting: '' });
79+
};
80+
const handleTransferTo = async () => {
81+
await contract.transferTo(state.toAddress, { value: ethers.utils.parseEther(state.amount) });
82+
setState({ ...state, toAddress: '', amount: '0' });
83+
};
84+
85+
if (connected) {
86+
return (
87+
<div>
88+
<Button onClick={disconnectWallet}>Disconnect wallet</Button>
89+
<h3>Contract Methods</h3>
90+
<Grid templateColumns='repeat(5, 1fr)' columnGap={5}>
91+
<GridItem colSpan={5}>
92+
<Button onClick={handleGreet}>greet</Button>
93+
</GridItem>
94+
<GridItem colSpan={5}>
95+
<Button disabled={!state.newGreeting} onClick={handleSetGreeting}>
96+
setGreeting
97+
</Button>
98+
<Input
99+
value={state.newGreeting}
100+
placeholder='New Greeting!'
101+
onChange={handleChangeState('newGreeting')}
102+
/>
103+
</GridItem>
104+
<GridItem colSpan={5}>
105+
<Button disabled={!(state.toAddress && state.amount)} onClick={handleTransferTo}>
106+
transferTo
107+
</Button>
108+
<Input
109+
value={state.toAddress}
110+
placeholder='0xjA123....'
111+
onChange={handleChangeState('toAddress')}
112+
/>
113+
<Input placeholder='0.2' value={state.amount} onChange={handleChangeState('amount')} />
114+
</GridItem>
115+
</Grid>
116+
</div>
117+
);
118+
}
119+
120+
return (
121+
<>
122+
<Button onClick={connectWallet}>Connect Wallet</Button>
123+
</>
124+
);
125+
};
126+
127+
storiesOf('Hooks/useContract', module).add('Default', () => (
128+
<Provider network='rinkeby'>
129+
<Default />
130+
</Provider>
131+
));

0 commit comments

Comments
 (0)