Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 10 additions & 4 deletions .bitmap
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,31 @@
{
"models/currency": {
"scope": "atila.web-components-library",
"version": "0.0.1",
"version": "0.0.3",
"mainFile": "index.ts",
"rootDir": "src/models/Currency"
},
"ui/add-or-switch-blockchain": {
"scope": "atila.web-components-library",
"version": "0.0.3",
"mainFile": "AddOrSwitchBlockchain.tsx",
"rootDir": "src/components/AddOrSwitchBlockchain"
},
"ui/crypto-payment-form": {
"scope": "atila.web-components-library",
"version": "0.0.5",
"version": "0.0.11",
"mainFile": "CryptoPaymentForm.tsx",
"rootDir": "src/components/CryptoPaymentForm"
},
"ui/currency-display": {
"scope": "atila.web-components-library",
"version": "0.0.5",
"version": "0.0.7",
"mainFile": "CurrencyDisplay.tsx",
"rootDir": "src/components/CurrencyDisplay"
},
"utils": {
"scope": "atila.web-components-library",
"version": "0.0.3",
"version": "0.0.5",
"mainFile": "index.ts",
"rootDir": "src/services/utils"
},
Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ A library of components used to build the Atila website

You can view the components at: https://bit.dev/atila/web-components-library

For an example of what we're trying to build, see [ethereum-boilerplate/ethereum-boilerplate](https://github.com/ethereum-boilerplate/ethereum-boilerplate)

## Quickstart

This project can either be run as a React web server or a [Bit](https://bit.dev/) Server
Expand All @@ -17,6 +19,13 @@ To run as a Bit server

- `bit install`

## Using in Other Projects

- Configure `@atila` as a scoped repository: `npm config set '@atila:registry' https://node.bit.dev`
- Install component: For example, `npm i @atila/web-components-library.ui.crypto-payment-form`

![Example of using web-components-library](https://i.imgur.com/cusLOEK.png)

## Testing

`yarn test`
Expand All @@ -27,7 +36,9 @@ To test a specific file: `yarn test SomeTestFileToRun`, for example `yarn test

- Summary of this tutorial: [Getting Started with Bit](https://harmony-docs.bit.dev/getting-started/creating-components)

- Create a tsx folder and file for the component: `src/components/MyComponent/MyComponent.tsx`
- Create a tsx folder and file for the component: `src/components/MyComponent/MyComponent.tsx` or `src/components/MyComponent/index.tsx`
- If you want to use your component in other components in the project, you might have to create a seperate file called `index.ts` that exports the components created in `MyComponent.tsx`
- For example, see: `src/components/AddOrSwitchBlockchain/index.ts` and `src/components/AddOrSwitchBlockchain/AddOrSwitchBlockchain.tsx`
- Create a compositions file `src/components/MyComponent/MyComponent.compositions.tsx`
- Make sure to import stylesheets into the compostions file
- Note: We're looking into a way to automatically import the stylesheets in all compositions
Expand Down
3 changes: 3 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import './App.css';
import AddOrSwitchBlockchain from './components/AddOrSwitchBlockchain/AddOrSwitchBlockchain';
import CryptoPaymentForm, { TransactionResponsePayment } from './components/CryptoPaymentForm/CryptoPaymentForm';
import { ConfigurableCryptoPaymentForm } from './components/CryptoPaymentForm/CryptoPaymentForm.compositions';
import CurrencyDisplay from './components/CurrencyDisplay/CurrencyDisplay';
Expand All @@ -12,6 +13,8 @@ function App() {
return (
<div className="App container py-5">
<h1>Atila Web Components Library</h1>

<AddOrSwitchBlockchain chainId={97} />
<CurrencyDisplay amount={0.01} />
<hr/>
<ConfigurableCryptoPaymentForm />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { CHAIN_IDS } from '@atila/web-components-library.models.currency'
import { AddOrSwitchBlockChain } from '.';

export function SwitchBinanceSmartChainTestNet() {
return (
<AddOrSwitchBlockChain
chainId={CHAIN_IDS.CHAIN_ID_97.chainId}
showAddBlockChain={false}
showSwitchBlockChain={true} />
)
}
export function AddBinanceSmartChainMainNet() {
return (
<AddOrSwitchBlockChain
chainId={CHAIN_IDS.CHAIN_ID_56.chainId}
showAddBlockChain={true}
showSwitchBlockChain={false} />
)
}

export function SwitchEthereumTestNet() {
return (
<AddOrSwitchBlockChain chainId={CHAIN_IDS.CHAIN_ID_3.chainId} />
)
}
142 changes: 142 additions & 0 deletions src/components/AddOrSwitchBlockchain/AddOrSwitchBlockchain.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import React, { useState } from 'react';
import { Alert, Button, Spin } from 'antd'
import { BlockChain, CHAIN_IDS } from '@atila/web-components-library.models.currency';

// https://stackoverflow.com/questions/12709074/how-do-you-explicitly-set-a-new-property-on-window-in-typescript
declare global {
interface Window {
ethereum:any;
}
}

export interface AddOrSwitchBlockChainProps {
chainId: number;
showAddBlockChain: boolean;
showSwitchBlockChain: boolean;
onAddComplete?: (newBlockchain: BlockChain) => void;
onSwitchComplete?: (newBlockchain: BlockChain) => void;
}

AddOrSwitchBlockChain.defaultProps = {
showAddBlockChain: true,
showSwitchBlockChain: false
}

export function AddOrSwitchBlockChain(props: AddOrSwitchBlockChainProps) {

const { chainId, showAddBlockChain, showSwitchBlockChain, onAddComplete, onSwitchComplete } = props;

const provider = window.ethereum;

const [errorMessage, setErrorMessage] = useState("");
const [successMessage, setSuccessMessage] = useState("");
const [isPending, setIsPending] = useState("");


if(!provider){
return <Alert
className="my-2"
message="Crypto wallet is not installed, please install one. E.g. Metamask"
type="error" />;
}

const chainIdKey = `CHAIN_ID_${chainId}`;

const chainIdHex = `0x${(chainId).toString(16)}`;

const targetBlockChain: BlockChain = CHAIN_IDS[chainIdKey];

const switchBlockChain = async () => {

setErrorMessage("");

setIsPending("Switching blockchain");
try {
await provider.request({
method: 'wallet_switchEthereumChain',
// Convert numerical chain ID to hexadecimal string
params: [{ chainId: chainIdHex}],
});
setSuccessMessage(`Switched to ${targetBlockChain.networkName}`);
if (onSwitchComplete) {
onSwitchComplete(targetBlockChain)
}
} catch (error: any) {
if (error.code === 4902) {
console.log({error})
// This error code indicates that the chain has not been added to MetaMask.
setErrorMessage("This network is not available in your crypto wallet, please add it");
addBlockChain();
} else {
setErrorMessage(error.message);
}
}
setIsPending("");

}

const addBlockChain = async () => {
const provider = window.ethereum;
setErrorMessage("");

setIsPending("Adding blockchain");
try {
await provider.request({
method: 'wallet_addEthereumChain',
params: [ { chainId: chainIdHex,
chainName: targetBlockChain.networkName,
rpcUrls:[targetBlockChain.rpcUrl],
blockExplorerUrls:[targetBlockChain.blockExplorerUrl],
nativeCurrency: {
symbol: targetBlockChain.currencyCode,
decimals: 18
} }]}
);
setSuccessMessage(`Added ${targetBlockChain.networkName}`);
if (onAddComplete) {
onAddComplete(targetBlockChain)
}
}
catch (error: any) {
console.log({error});
setErrorMessage(error.message);
}

setIsPending("");
}


const AddOrSwitchButton = ({isAddMode, onClick}: {isAddMode: boolean, onClick: ()=> {}}) => {

return (
<Button onClick={onClick}>
<div>
<span>
{isAddMode ? "Add " : "Switch to "} {targetBlockChain.networkName}{' '}
</span>
<img src={targetBlockChain.logo} width="auto" height="25px" className="pb-1"/>
</div>
</Button>
)
}

return (
<div>
<Spin spinning={!!isPending} tip={isPending}>

{showAddBlockChain &&
<AddOrSwitchButton isAddMode={true} onClick={addBlockChain} />
}
{showSwitchBlockChain &&
<AddOrSwitchButton isAddMode={false} onClick={switchBlockChain} />
}
{errorMessage &&
<Alert className="my-2" message={errorMessage} type="error" />
}
{successMessage &&
<Alert className="my-2" message={successMessage} type="success" />
}
</Spin>
</div>
)
}
2 changes: 2 additions & 0 deletions src/components/AddOrSwitchBlockchain/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { AddOrSwitchBlockChain } from "./AddOrSwitchBlockchain";
export type { AddOrSwitchBlockChainProps } from "./AddOrSwitchBlockchain";
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { useState } from 'react';
import CryptoPaymentForm, { CHAIN_IDS, CRYPTO_IN_USD, MAXIMUM_DECIMAL_PLACES } from './CryptoPaymentForm';
import CryptoPaymentForm, { CRYPTO_IN_USD, MAXIMUM_DECIMAL_PLACES } from './CryptoPaymentForm';
import 'bootstrap/dist/css/bootstrap.css';
import 'antd/dist/antd.css';
import { Radio, Select } from 'antd';
import { CHAIN_IDS } from '@atila/web-components-library.models.currency';

const { Option } = Select;

Expand All @@ -18,12 +19,12 @@ const paymentAmountOptions = [

const currencyOptions = [
{
name: CHAIN_IDS.ETHEREUM.NAME,
value: CHAIN_IDS.ETHEREUM.CURRENCY_CODE,
name: CHAIN_IDS.CHAIN_ID_1.currencyName,
value: CHAIN_IDS.CHAIN_ID_1.currencyCode,
},
{
name: CHAIN_IDS.BINANCE.NAME,
value: CHAIN_IDS.BINANCE.CURRENCY_CODE,
name: CHAIN_IDS.CHAIN_ID_56.currencyName,
value: CHAIN_IDS.CHAIN_ID_56.currencyCode,
}
]

Expand Down
Loading