Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TokenBalance Component #97

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9712a2e
initial skeleton of the TokenBalance component
AlexNi245 Dec 3, 2021
7c843ab
first prototype of TokenBalance component
AlexNi245 Dec 4, 2021
f29734d
Merge remote-tracking branch 'origin/master' into fear/token-balance
AlexNi245 Dec 12, 2021
065c576
use useTokenBalance hook to determine token balance
AlexNi245 Dec 12, 2021
672bba0
add the ability to use own image by passing imageUrl
AlexNi245 Dec 12, 2021
2420626
fetch name and symbol from contract
AlexNi245 Dec 12, 2021
c650fb7
add documentation
AlexNi245 Dec 12, 2021
911f8d6
add story for invalid contract
AlexNi245 Dec 12, 2021
2ed6ea1
added the web-ui hooks components to dependencies
AlexNi245 Dec 13, 2021
69ec3b7
merge origin
AlexNi245 Dec 13, 2021
c2d16df
add web3-ui/hooks as a peerDependency
AlexNi245 Dec 14, 2021
ff091bd
move Headline to its own component
AlexNi245 Dec 14, 2021
d527562
fixed cross module imports
AlexNi245 Dec 14, 2021
8161bd0
get rid of @web3-ui/hooks in /components/package.json
AlexNi245 Dec 14, 2021
7c88ae1
Merge remote-tracking branch 'origin/master' into fear/token-balance
AlexNi245 Dec 14, 2021
89e25f5
added "@web3-ui/hooks" as a regular dependency
AlexNi245 Dec 14, 2021
6df8770
added changeset file
AlexNi245 Dec 14, 2021
965f3f7
Update packages/components/src/components/TokenBalance/TokenBalance.tsx
AlexNi245 Dec 14, 2021
e912a93
move logo and headline back to TokenBalance Component
AlexNi245 Dec 15, 2021
9c521d5
reverted accidentally committed files
AlexNi245 Dec 15, 2021
12b1ea2
pass props to flex component
AlexNi245 Dec 15, 2021
a3d9073
use flexprops instead of input props import
AlexNi245 Dec 15, 2021
5b2dc8f
use flexprops instead of input props import
AlexNi245 Dec 16, 2021
4f8d41a
revert config.json without commit hook
AlexNi245 Dec 16, 2021
c36708c
delete erc20.json
AlexNi245 Dec 16, 2021
34c4e46
get rid of accidentally
AlexNi245 Dec 20, 2021
9b397f7
Merge remote-tracking branch 'origin/master' into fear/token-balance
AlexNi245 Jan 11, 2022
8ef2478
move stateful logic of the TokenBalance Component to core directory.
AlexNi245 Jan 11, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
{
"$schema": "https://unpkg.com/@changesets/config@1.6.3/schema.json",
"changelog": [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you removing this, this is repo config

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this was not on purpose. Must have occurred during the upstream merge

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll revert this

"@changesets/changelog-github",
{ "repo": "Developer-DAO/web3-ui" }
],
"changelog": ["@changesets/changelog-github", { "repo": "Developer-DAO/web3-ui" }],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be changed. Can you make sure this change goes away please?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sir, I almost lost my mind on this but I got the feeling this file was formatted by the commit hook. I have pushed it multiple times just to notice that this file still has unwanted changes.

"commit": false,
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch"
}
}
6 changes: 6 additions & 0 deletions .changeset/selfish-flies-care.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@web3-ui/components': minor
'@web3-ui/hooks': minor
---

Added token-balance component to display the users amount of a certain ERC20 token
1 change: 1 addition & 0 deletions packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"module": "dist/web3-ui-components.esm.js",
"types": "dist/web3-ui-components.cjs.d.ts",
"dependencies": {
"@web3-ui/hooks": "0.4.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more thing that came to my mind recently is that the components package should not rely on the hooks package in any way. We want both of these packages to function well together but also independently.

This is why I feel like we shouldn't do any data fetching at all inside any of our components. Here is what I propose:

  1. Move the current implementation to the core package.
  2. Make the current implementation (inside components) so that it does not handle any data fetching but just accepts the data it needs to render.

Let me know if this is unclear. More than happy to discuss this in further detail since I think this is an important topic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean this component shouldn't have any state at all and just renders given props ? Then we can remove the useTokenBalance hook from the Component.
Is that what you meant in the second point?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlexNi245 yes! The current verison of this component which does have some state and fetches data should be in the core package and not in the components package. This is what we did recently for #121 too.

tldr:

  • <TokenBalance /> in components shouldn't have any state, it should just render some data that you pass to it.
  • <TokenBalance /> in core should have state and should use the useTokenBalance hook

"@babel/runtime": "^7.16.3",
"@chakra-ui/react": "^1.7.2",
"@emotion/core": "^11.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Provider, Web3Context } from '@web3-ui/hooks';
import React, { useContext } from 'react';
import { TokenBalance, TokenBalanceProps } from './TokenBalance';
import { Button } from '@chakra-ui/react';

export default {
title: 'Components/TokenBalance',
component: TokenBalance,
};

const exampleProps: TokenBalanceProps = {
value: 0.9955,
accountAddress: '0x503828976D22510aad0201ac7EC88293211D23Da',
tokenAddress: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
};

export const TokeBalance = () => (
<Provider network='mainnet'>
<WalletContextTestWrapper child={<TokenBalance {...exampleProps} />} />
</Provider>
);

export const InvalidContract = () => (
<Provider network='mainnet'>
<WalletContextTestWrapper child={<TokenBalance {...exampleProps} tokenAddress={'0x0'} />} />
</Provider>
);

const WalletContextTestWrapper = ({ child }): JSX.Element => {
const { connected, connectWallet } = useContext(Web3Context);
return !connected ? <Button onClick={connectWallet}>Connect with Wallet</Button> : child;
};
93 changes: 93 additions & 0 deletions packages/components/src/components/TokenBalance/TokenBalance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React, { useContext, useEffect, useState } from 'react';
import { Box, Flex, FlexProps, Heading, Image } from '@chakra-ui/react';
import { ERC20ABI, useTokenBalance, Web3Context } from '@web3-ui/hooks';
import { ethers } from 'ethers';

export interface TokenBalanceProps {
/**
* The contract address of the ERC20 Token
*/
tokenAddress: string;
/**
* The owners address
*/
accountAddress: string;
/**
* The value of one token in USD
*/
value: number;
/**
* Image of the token (optional)
*/
imgUrl?: string | undefined;
}

export const TokenBalance: React.FC<TokenBalanceProps & FlexProps> = props => {
const { tokenAddress, accountAddress, value, imgUrl } = props;

const web3Context = useContext(Web3Context);
const provider = web3Context?.provider;

const [name, setName] = useState('');
const [symbol, setSymbol] = useState('');

const { balance, decimals } = useTokenBalance({ tokenAddress, accountAddress });

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

//Just display the first 3 decimal places of the balance
const calcBalance = () => {
if (balance === undefined || decimals === undefined) {
return 0;
}
return Number.parseFloat((Number.parseInt(balance) / 10 ** decimals).toFixed(3));
};
//Just display the first 3 decimal places of the value
const calcValue = () => {
const balance = calcBalance();
return (balance * value).toFixed(3);
};
//Fetch name and symbol from the smart contract
//Maybe it would make sense to provide them as props to avoid loading time ?
const getTokenNameAndSymbol = async () => {
const contract = new ethers.Contract(tokenAddress, ERC20ABI, provider!);
const [name, symbol] = await Promise.all([contract.name(), contract.symbol()]);

setName(name);
setSymbol(symbol);
};

const Headline = (name: string) => (
<Heading as='h3' size='sm'>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline components can't maintain their referential identity and are subject to constant remounting. Hoisting outside of this closure will resolve that

{name}
</Heading>
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like Jovi suggested:

Suggested change
const Headline = (name: string) => (
<Heading as='h3' size='sm'>
{name}
</Heading>
);
const Headline = (name: string) => {
return (
<Heading as='h3' size='sm'>
{name}
</Heading>
);
}


const TokenLogo = ({ tokenContractAddress }) => (
<Image
src={`https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${tokenContractAddress}/logo.png`}
/>
);

return (
<Flex borderRadius='lg' borderWidth='1px' overflow='hidden' px='4' py='2' {...props}>
<Box w='50px' pr='4'>
{imgUrl ? <Image src={imgUrl} /> : <TokenLogo tokenContractAddress={tokenAddress} />}
</Box>
<Box flex='2'>
{Headline(symbol)}
<p>{name}</p>
</Box>
<Box flex='1'>
{Headline('Balance')}
<p>{calcBalance()}</p>
</Box>
<Box flex='1'>
{Headline('Value')}
<p>{calcValue()}</p>
</Box>
</Flex>
);
};
222 changes: 222 additions & 0 deletions packages/components/src/components/TokenBalance/erc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
[
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you rename this file to ERC20.json instead?

},
{
"constant": false,
"inputs": [
{
"name": "_spender",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_from",
"type": "address"
},
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [
{
"name": "",
"type": "uint8"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_owner",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"name": "balance",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_owner",
"type": "address"
},
{
"name": "_spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"payable": true,
"stateMutability": "payable",
"type": "fallback"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "owner",
"type": "address"
},
{
"indexed": true,
"name": "spender",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "from",
"type": "address"
},
{
"indexed": true,
"name": "to",
"type": "address"
},
{
"indexed": false,
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
}
]
1 change: 1 addition & 0 deletions packages/components/src/components/TokenBalance/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './TokenBalance';
1 change: 1 addition & 0 deletions packages/components/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './Address';
export * from './NFTGallery';
export * from './NFT';
export * from './AddressInput';
export * from './TokenBalance';
10 changes: 5 additions & 5 deletions packages/hooks/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { useWallet, useContract } from './hooks';
export { useTokenBalance } from './hooks/useTokenBalance';
export { Provider, Web3Context } from './Provider';
export type { ProviderProps, Web3ContextType } from './Provider';
export { NETWORKS, CHAIN_ID_TO_NETWORK } from './constants';
export {useWallet, useContract} from './hooks';
export {useTokenBalance} from './hooks/useTokenBalance';
export {Provider, Web3Context} from './Provider';
export type {ProviderProps, Web3ContextType} from './Provider';
export {NETWORKS, CHAIN_ID_TO_NETWORK, ERC20ABI} from './constants';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these lines should not be changed. Only the last line is actually being changed. Can you reset these?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This must be also the result of the post commit hook. I reverted the first few lines just to notice that they have changed again after I committed my changes.
I can use -no--verify again to restore the first 5 lines. But maybe it's intended that the commit hook applies certain formatting rules to each file even the affected file has nothing to do with the pr

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. No worries then.