├── .gitignore ├── src ├── react-app-env.d.ts ├── types │ ├── common.ts │ ├── compoents.ts │ ├── route.ts │ ├── index.ts │ ├── currencies.ts │ └── web3modal.ts ├── images │ ├── loading.gif │ ├── klaytn-logo.png │ ├── web3modal-logo.png │ ├── GitHub-Mark-Light-64px.png │ └── eth.svg ├── views │ ├── Miscellaneous │ │ ├── constants │ │ │ ├── exWKLAYAddress.ts │ │ │ ├── exWKLAYData.ts │ │ │ ├── exSBTData.ts │ │ │ ├── exMulticallData.ts │ │ │ └── exWKLAYAbi.ts │ │ ├── KeccakFromString.tsx │ │ ├── components │ │ │ └── KeystoreResultForm.tsx │ │ ├── LoadKeystore.tsx │ │ └── CheckAccountKey.tsx │ ├── Kaikas │ │ ├── Common │ │ │ ├── exFunctionCall.ts │ │ │ └── FeeDelegation.tsx │ │ ├── TxTypes4 │ │ │ ├── SignMessage.tsx │ │ │ └── AddToken.tsx │ │ ├── TxTypes1 │ │ │ ├── ValueTransferLegacy.tsx │ │ │ ├── SmartContractDeployLegacy.tsx │ │ │ ├── AccountUpdateFeeDelegation.tsx │ │ │ └── AccountUpdate.tsx │ │ ├── TxTypes2 │ │ │ ├── ValueTransfer.tsx │ │ │ ├── ValueTransferFeeDelegation.tsx │ │ │ ├── ValueTransferWithMemo.tsx │ │ │ ├── ValueTransferWithMemoFeeDelegation.tsx │ │ │ ├── ValueTransferFeeDelegationWithRatio.tsx │ │ │ └── ValueTransferWithMemoFeeDelegationWithRatio.tsx │ │ └── TxTypes3 │ │ │ ├── SmartContractDeploy.tsx │ │ │ ├── SmartContractDeployFeeDelegation.tsx │ │ │ └── SmartContractDeployFeeDelegationWithRatio.tsx │ ├── Web3modal │ │ ├── web3modalComponents │ │ │ ├── index.ts │ │ │ ├── ERC20Icon.tsx │ │ │ ├── Banner.tsx │ │ │ ├── Icon.tsx │ │ │ ├── AssetRow.tsx │ │ │ ├── AccountAssets.tsx │ │ │ ├── ModalResult.tsx │ │ │ ├── Header.tsx │ │ │ └── Modal.tsx │ │ └── helpers │ │ │ └── web3.ts │ ├── Account │ │ └── components │ │ │ └── GetKeySection.tsx │ ├── Transaction │ │ ├── RLPDecoder.tsx │ │ └── TxHashDecoder.tsx │ ├── SmartContract │ │ ├── ABIDecoder.tsx │ │ ├── ABIEncoder.tsx │ │ └── FunctionSignature.tsx │ ├── Converter │ │ ├── SimpleConverter.tsx │ │ ├── ExtendedConverter.tsx │ │ └── index.tsx │ ├── BlockInfo │ │ └── BlockHashDecoder.tsx │ └── KCT │ │ └── DetectKCT.tsx ├── components │ ├── View.tsx │ ├── Row.tsx │ ├── CardSection.tsx │ ├── Loading.tsx │ ├── PrivateKeyWarning.tsx │ ├── Button.tsx │ ├── Text.tsx │ ├── FormFile.tsx │ ├── Container.tsx │ ├── LinkA.tsx │ ├── CopyButton.tsx │ ├── FormImage.tsx │ ├── CardExample.tsx │ ├── FormTextarea.tsx │ ├── FormDownload.tsx │ ├── FormInput.tsx │ ├── FormSelect.tsx │ ├── ResultForm.tsx │ ├── InputField.tsx │ ├── index.ts │ ├── FormRadio.tsx │ ├── CodeBlock.tsx │ └── FormGetKey.tsx ├── index.css ├── consts │ ├── color.ts │ ├── index.ts │ ├── urlMap.ts │ ├── style.ts │ └── util.ts ├── hooks │ ├── useToast.ts │ ├── useLayout.ts │ └── account │ │ └── useAccounts.ts ├── setupTests.ts ├── logics │ └── caverFuncntions.ts ├── reportWebVitals.ts ├── index.tsx └── App │ ├── index.tsx │ └── Navbar.tsx ├── public ├── logo.png ├── robots.txt ├── favicon.ico ├── manifest.json ├── 404.html └── index.html ├── tsconfig.json ├── .github └── workflows │ └── prod-deploy.yml ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | node_modules/ 4 | 5 | build -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/types/common.ts: -------------------------------------------------------------------------------- 1 | export type NominalType = { __type: T } 2 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klaytn/klaytn-online-toolkit/HEAD/public/logo.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klaytn/klaytn-online-toolkit/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klaytn/klaytn-online-toolkit/HEAD/src/images/loading.gif -------------------------------------------------------------------------------- /src/images/klaytn-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klaytn/klaytn-online-toolkit/HEAD/src/images/klaytn-logo.png -------------------------------------------------------------------------------- /src/images/web3modal-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klaytn/klaytn-online-toolkit/HEAD/src/images/web3modal-logo.png -------------------------------------------------------------------------------- /src/images/GitHub-Mark-Light-64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/klaytn/klaytn-online-toolkit/HEAD/src/images/GitHub-Mark-Light-64px.png -------------------------------------------------------------------------------- /src/views/Miscellaneous/constants/exWKLAYAddress.ts: -------------------------------------------------------------------------------- 1 | const baobabWKLAYAddress = '0x043c471bEe060e00A56CcD02c0Ca286808a5A436' 2 | 3 | export { baobabWKLAYAddress } 4 | -------------------------------------------------------------------------------- /src/components/View.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | const View = styled.div` 4 | display: flex; 5 | flex-direction: column; 6 | ` 7 | 8 | export default View 9 | -------------------------------------------------------------------------------- /src/components/Row.tsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import View from "./View"; 3 | 4 | const Row = styled(View)` 5 | flex-direction: row; 6 | `; 7 | 8 | export default Row; 9 | -------------------------------------------------------------------------------- /src/views/Miscellaneous/constants/exWKLAYData.ts: -------------------------------------------------------------------------------- 1 | const exposureTime = 5000 2 | const contractAddress = '0x043c471bEe060e00A56CcD02c0Ca286808a5A436' 3 | 4 | export { exposureTime, contractAddress } 5 | -------------------------------------------------------------------------------- /src/types/compoents.ts: -------------------------------------------------------------------------------- 1 | export type ResultFormType = 2 | | { 3 | success: true 4 | value?: T 5 | } 6 | | { 7 | success: false 8 | message: string 9 | } 10 | -------------------------------------------------------------------------------- /src/types/route.ts: -------------------------------------------------------------------------------- 1 | export type RouteType = { 2 | name: string 3 | path: string 4 | items: { 5 | path: string 6 | name: string 7 | component: any 8 | description: string 9 | }[] 10 | } 11 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | div { 2 | -ms-overflow-style: none; 3 | /* IE and Edge */ 4 | scrollbar-width: none; 5 | /* Firefox */ 6 | } 7 | ::-webkit-scrollbar { 8 | display: none; 9 | /* Chrome, Safari, Opera*/ 10 | } -------------------------------------------------------------------------------- /src/consts/color.ts: -------------------------------------------------------------------------------- 1 | const primary = '#1d8cf8' 2 | const error = '#c221a9' 3 | const text = '#ffffff' 4 | const landingPageCard = '#2d2d39' 5 | 6 | export default { 7 | primary, 8 | error, 9 | text, 10 | landingPageCard, 11 | } 12 | -------------------------------------------------------------------------------- /src/images/eth.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/consts/index.ts: -------------------------------------------------------------------------------- 1 | export { default as COLOR } from './color' 2 | export { default as URLMAP } from './urlMap' 3 | export { default as STYLE } from './style' 4 | export { default as UTIL } from './util' 5 | export { default as WEB3MODAL } from './web3modal' 6 | -------------------------------------------------------------------------------- /src/hooks/useToast.ts: -------------------------------------------------------------------------------- 1 | import { toast } from 'react-toastify' 2 | 3 | export type UseToastReturn = { 4 | toast: typeof toast 5 | } 6 | 7 | const useToast = (): UseToastReturn => { 8 | return { toast } 9 | } 10 | 11 | export default useToast 12 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/views/Miscellaneous/constants/exSBTData.ts: -------------------------------------------------------------------------------- 1 | const exTokenURI = 'https://cryptologos.cc/logos/klaytn-klay-logo.svg?v=023' 2 | const exTokenReceiver = '0x6CEe3d8c038ab74E2854C158d7B1b55544E814C8' 3 | const exposureTime = 5000 4 | 5 | export { exTokenURI, exTokenReceiver, exposureTime } 6 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface Window { 3 | klaytn: any 4 | ethereum: any 5 | } 6 | } 7 | 8 | export * from './common' 9 | export * from './currencies' 10 | export * from './route' 11 | export * from './compoents' 12 | export * from './web3modal' -------------------------------------------------------------------------------- /src/components/CardSection.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | import View from './View' 4 | 5 | const StyledSection = styled(View)` 6 | padding: 10px; 7 | background-color: #262626; 8 | margin-bottom: 15px; 9 | border-radius: 10px; 10 | ` 11 | 12 | export default StyledSection 13 | -------------------------------------------------------------------------------- /src/types/currencies.ts: -------------------------------------------------------------------------------- 1 | import { NominalType } from './common' 2 | 3 | export enum TokenSymbolEnum { 4 | KLAY = 'KLAY', 5 | } 6 | 7 | export type pToken = string & NominalType<'pToken'> 8 | 9 | export type uToken = string & NominalType<'uToken'> 10 | 11 | export type Token = string & NominalType<'Token'> 12 | -------------------------------------------------------------------------------- /src/logics/caverFuncntions.ts: -------------------------------------------------------------------------------- 1 | import Caver from 'caver-js' 2 | 3 | const caver = new Caver() 4 | 5 | export const generateSingleKey = caver.wallet.keyring.generateSingleKey 6 | 7 | export const createFromPrivateKey = caver.wallet.keyring.createFromPrivateKey 8 | 9 | export const keyringDecrypt = caver.wallet.keyring.decrypt 10 | -------------------------------------------------------------------------------- /src/views/Kaikas/Common/exFunctionCall.ts: -------------------------------------------------------------------------------- 1 | const exFunctionCall = { 2 | name: 'transfer', 3 | type: 'function', 4 | inputs: [ 5 | { 6 | type: 'address', 7 | name: 'recipient', 8 | }, 9 | { 10 | type: 'uint256', 11 | name: 'amount', 12 | }, 13 | ], 14 | } 15 | 16 | export default exFunctionCall 17 | -------------------------------------------------------------------------------- /src/components/Loading.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | import loadingImg from 'images/loading.gif' 3 | import FormImage from './FormImage' 4 | 5 | const Loading = ({ size = 60 }: { size?: number }): ReactElement => { 6 | return 7 | } 8 | 9 | export default Loading 10 | -------------------------------------------------------------------------------- /src/components/PrivateKeyWarning.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | 3 | import Text from './Text' 4 | 5 | const PrivateKeyWarning = (): ReactElement => ( 6 | 7 | Test purpose only! Never use your "real" private key. 8 | 9 | ) 10 | 11 | export default PrivateKeyWarning 12 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/views/Web3modal/web3modalComponents/index.ts: -------------------------------------------------------------------------------- 1 | export { default as AccountAssets } from './AccountAssets' 2 | export { default as AssetRow } from './AssetRow' 3 | export { default as Banner } from './Banner' 4 | export { default as ERC20Icon } from './ERC20Icon' 5 | export { default as Header } from './Header' 6 | export { default as Icon } from './Icon' 7 | export { default as Modal }from './Modal' 8 | export { default as ModalResult } from './ModalResult' 9 | -------------------------------------------------------------------------------- /src/components/Button.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import { Button as BaseButton, ButtonProps } from 'reactstrap' 3 | import { ReactElement } from 'react' 4 | 5 | const StyledButton = styled(BaseButton)` 6 | margin-bottom: 0; 7 | width: fit-content; 8 | min-width: 100px; 9 | ` 10 | 11 | const Button = (props: ButtonProps): ReactElement => { 12 | return 13 | } 14 | 15 | export default Button 16 | -------------------------------------------------------------------------------- /src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals' 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler): void => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry) 7 | getFID(onPerfEntry) 8 | getFCP(onPerfEntry) 9 | getLCP(onPerfEntry) 10 | getTTFB(onPerfEntry) 11 | }) 12 | } 13 | } 14 | 15 | export default reportWebVitals 16 | -------------------------------------------------------------------------------- /src/components/Text.tsx: -------------------------------------------------------------------------------- 1 | import { HTMLAttributes, ReactElement } from 'react' 2 | import styled from 'styled-components' 3 | 4 | import { COLOR } from 'consts' 5 | 6 | const StyledText = styled.div` 7 | color: ${COLOR.text}; 8 | font-stretch: normal; 9 | font-style: normal; 10 | line-height: normal; 11 | white-space: pre-wrap; 12 | word-break: break-word; 13 | margin: 0; 14 | ` 15 | 16 | const Text = (props: HTMLAttributes): ReactElement => { 17 | return 18 | } 19 | 20 | export default Text 21 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | import reportWebVitals from './reportWebVitals' 5 | import './index.css' 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ) 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals() 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "baseUrl": "src" 19 | }, 20 | "include": ["src"] 21 | } 22 | -------------------------------------------------------------------------------- /src/components/FormFile.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | import styled from 'styled-components' 3 | 4 | const StyledFile = styled.input`` 5 | 6 | type FormFileProps = { 7 | accept: string 8 | onChange: (accept?: FileList) => void 9 | placeholder?: string 10 | } 11 | 12 | const FormFile = ({ 13 | accept, 14 | placeholder, 15 | onChange, 16 | }: FormFileProps): ReactElement => ( 17 | onChange(e.target.files || undefined)} 22 | placeholder={placeholder} 23 | /> 24 | ) 25 | 26 | export default FormFile 27 | -------------------------------------------------------------------------------- /src/types/web3modal.ts: -------------------------------------------------------------------------------- 1 | export interface IAssetData { 2 | symbol: string; 3 | name: string; 4 | decimals: string; 5 | contractAddress: string; 6 | balance?: string; 7 | } 8 | 9 | export interface IChainData { 10 | name: string; 11 | short_name: string; 12 | chain: string; 13 | network: string; 14 | chain_id: number; 15 | network_id: number; 16 | rpc_url: string; 17 | native_currency: IAssetData; 18 | } 19 | 20 | export interface IGasPrice { 21 | time: number; 22 | price: number; 23 | } 24 | 25 | export interface IGasPrices { 26 | timestamp: number; 27 | slow: IGasPrice; 28 | average: IGasPrice; 29 | fast: IGasPrice; 30 | } 31 | -------------------------------------------------------------------------------- /src/components/Container.tsx: -------------------------------------------------------------------------------- 1 | import { STYLE } from 'consts' 2 | import { ReactElement, ReactNode } from 'react' 3 | import styled from 'styled-components' 4 | 5 | import View from './View' 6 | 7 | type ContainerType = { 8 | children: ReactNode 9 | } 10 | 11 | const StyledContainer = styled(View)` 12 | position: relative; 13 | width: 100%; 14 | margin: 0 auto; 15 | align-items: center; 16 | max-width: 1000px; 17 | 18 | @media ${STYLE.media.tablet} { 19 | padding: 0 20px; 20 | } 21 | ` 22 | 23 | const Container = ({ children }: ContainerType): ReactElement => { 24 | return {children} 25 | } 26 | 27 | export default Container 28 | -------------------------------------------------------------------------------- /src/views/Web3modal/web3modalComponents/ERC20Icon.tsx: -------------------------------------------------------------------------------- 1 | import * as PropTypes from 'prop-types' 2 | import Icon from './Icon' 3 | import erc20 from 'images/erc20.svg' 4 | import { ReactElement } from 'react' 5 | 6 | const ERC20Icon = (props: any): ReactElement => { 7 | const src = `https://raw.githubusercontent.com/TrustWallet/tokens/master/tokens/${props.contractAddress.toLowerCase()}.png` 8 | return 9 | } 10 | 11 | ERC20Icon.propTypes = { 12 | contractAddress: PropTypes.string, 13 | size: PropTypes.number, 14 | } 15 | 16 | ERC20Icon.defaultProps = { 17 | contractAddress: null, 18 | size: 20, 19 | } 20 | 21 | export default ERC20Icon 22 | -------------------------------------------------------------------------------- /src/components/LinkA.tsx: -------------------------------------------------------------------------------- 1 | import { COLOR } from 'consts' 2 | import { ReactElement, ReactNode } from 'react' 3 | import styled from 'styled-components' 4 | 5 | import Text from './Text' 6 | 7 | const StyledA = styled.a` 8 | color: ${COLOR.primary}; 9 | text-decoration: none; 10 | :hover { 11 | opacity: 0.7; 12 | } 13 | :visited { 14 | color: ${COLOR.primary}; 15 | } 16 | ` 17 | 18 | const LinkA = ({ 19 | link, 20 | children, 21 | }: { 22 | link: string 23 | children?: ReactNode 24 | }): ReactElement => { 25 | return ( 26 | 27 | {children || {link}} 28 | 29 | ) 30 | } 31 | 32 | export default LinkA 33 | -------------------------------------------------------------------------------- /src/views/Miscellaneous/constants/exMulticallData.ts: -------------------------------------------------------------------------------- 1 | const exposureTime = 5000 2 | const contractAddress = '0xc58580b20154462196021eac953c4e3d6e204ca4' 3 | const wklayContractAddress = '0x043c471bEe060e00A56CcD02c0Ca286808a5A436' 4 | 5 | const exAddress1 = '0x7b4777923b6e659b1ea10f426ec91addd15f9506' 6 | const exAddress2 = '0xe57329a1175a9ce04028c48d58a7963a5a16aa93' 7 | const exAddress3 = '0x12183c2e478f031f343f141f14a797a007a6dbdc' 8 | const exAddress4 = '0xe1cc95100397aab1f096f6130be7cb87ff76dcaf' 9 | const exAddress5 = '0x13b723d90b20bd2be88929b8ec2dcb2bb2d6acc5' 10 | 11 | export { 12 | exposureTime, 13 | contractAddress, 14 | wklayContractAddress, 15 | exAddress1, 16 | exAddress2, 17 | exAddress3, 18 | exAddress4, 19 | exAddress5, 20 | } 21 | -------------------------------------------------------------------------------- /src/components/CopyButton.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | import { CopyToClipboard } from 'react-copy-to-clipboard' 3 | import { ButtonProps } from 'reactstrap' 4 | 5 | import useToast from 'hooks/useToast' 6 | import Button from './Button' 7 | 8 | const CopyButton = ({ 9 | text, 10 | children, 11 | buttonProps, 12 | }: { 13 | text: string 14 | children: string 15 | buttonProps?: ButtonProps 16 | }): ReactElement => { 17 | const { toast } = useToast() 18 | 19 | return ( 20 | { 23 | toast('Copied') 24 | }} 25 | > 26 | 27 | 28 | ) 29 | } 30 | 31 | export default CopyButton 32 | -------------------------------------------------------------------------------- /src/views/Web3modal/web3modalComponents/Banner.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import logo from 'images/web3modal-logo.png' 3 | import { ReactElement } from 'react' 4 | 5 | const SBannerWrapper = styled.div` 6 | display: flex; 7 | align-items: center; 8 | position: relative; 9 | & span { 10 | color: 'lightBlue'; 11 | margin-left: 12px; 12 | } 13 | ` 14 | 15 | const SBanner = styled.div` 16 | width: 45px; 17 | height: 45px; 18 | background: url(${logo}) no-repeat; 19 | background-size: cover; 20 | background-position: center; 21 | ` 22 | 23 | const Banner = (): ReactElement => ( 24 | 25 | 26 | {`Web3Modal`} 27 | 28 | ) 29 | 30 | export default Banner 31 | -------------------------------------------------------------------------------- /src/components/FormImage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | import styled from 'styled-components' 3 | 4 | type FormImageProps = { 5 | src: string 6 | size?: number 7 | style?: React.CSSProperties 8 | } 9 | 10 | const getFormImageSize = ({ size }: { size?: number }): string => 11 | size ? `${size}px` : '100%' 12 | 13 | const StyledFormImage = styled.div` 14 | display: inline-block; 15 | background-image: url(${(props): string => props.src}); 16 | background-size: contain; 17 | background-position: center; 18 | background-repeat: no-repeat; 19 | height: ${getFormImageSize}; 20 | width: ${getFormImageSize}; 21 | ` 22 | 23 | const FormImage = (props: FormImageProps): ReactElement => { 24 | return 25 | } 26 | 27 | export default FormImage 28 | -------------------------------------------------------------------------------- /src/consts/urlMap.ts: -------------------------------------------------------------------------------- 1 | const network = { 2 | testnet: { 3 | rpc: 'https://klaytn-baobab-rpc.allthatnode.com:8551', 4 | scope: 'https://baobab.scope.klaytn.com/tx/', 5 | finder: 'https://baobab.klaytnfinder.io/tx/', 6 | finderToken: 'https://baobab.klaytnfinder.io/token/', 7 | finderNFT: 'https://baobab.klaytnfinder.io/nft/', 8 | }, 9 | mainnet: { 10 | rpc: 'https://klaytn-mainnet-rpc.allthatnode.com:8551', 11 | scope: 'https://scope.klaytn.com/tx/', 12 | finder: 'https://www.klaytnfinder.io/tx/', 13 | finderToken: 'https://www.klaytnfinder.io/token/', 14 | finderNFT: 'https://www.klaytnfinder.io/nft/', 15 | }, 16 | } 17 | 18 | const kip = { 19 | 'KIP-7': 'https://kips.klaytn.foundation/KIPs/kip-7', 20 | 'KIP-17': 'https://kips.klaytn.foundation/KIPs/kip-17', 21 | 'KIP-37': 'https://kips.klaytn.foundation/KIPs/kip-37', 22 | } 23 | 24 | export default { 25 | network, 26 | kip, 27 | } 28 | -------------------------------------------------------------------------------- /src/views/Web3modal/web3modalComponents/Icon.tsx: -------------------------------------------------------------------------------- 1 | import * as PropTypes from 'prop-types' 2 | import { ReactElement } from 'react' 3 | import styled from 'styled-components' 4 | 5 | interface IIconStyleProps { 6 | size: number 7 | } 8 | 9 | const SIcon = styled.img` 10 | width: ${({ size }): string => `${size}px`}; 11 | height: ${({ size }): string => `${size}px`}; 12 | ` 13 | 14 | const Icon = (props: any): ReactElement => { 15 | const { src, fallback, size } = props 16 | return ( 17 | (event.target.src = fallback)} 22 | /> 23 | ) 24 | } 25 | 26 | Icon.propTypes = { 27 | src: PropTypes.string, 28 | fallback: PropTypes.string, 29 | size: PropTypes.number, 30 | } 31 | 32 | Icon.defaultProps = { 33 | src: null, 34 | fallback: null, 35 | size: 20, 36 | } 37 | 38 | export default Icon 39 | -------------------------------------------------------------------------------- /src/components/CardExample.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | import styled from 'styled-components' 3 | 4 | import Row from './Row' 5 | import View from './View' 6 | import Button from './Button' 7 | import CodeBlock from './CodeBlock' 8 | 9 | const StyledContainer = styled(View)` 10 | gap: 4px; 11 | margin-bottom: 8px; 12 | ` 13 | const CardExample = ({ 14 | exValue, 15 | onClickTry, 16 | }: { 17 | exValue: string 18 | onClickTry: (value: string) => void 19 | }): ReactElement => { 20 | return ( 21 | 22 | 23 | 32 | 33 | 34 | 35 | ) 36 | } 37 | 38 | export default CardExample 39 | -------------------------------------------------------------------------------- /src/components/FormTextarea.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, TextareaHTMLAttributes } from 'react' 2 | import styled from 'styled-components' 3 | 4 | const StyledTextarea = styled.textarea` 5 | background-color: #adb5bd; 6 | color: black; 7 | padding: 10px; 8 | resize: none; 9 | font-size: 12px; 10 | :focus-visible { 11 | outline: 1px solid gray; 12 | } 13 | 14 | :read-only { 15 | color: white; 16 | background-color: #6c7379; 17 | cursor: not-allowed; 18 | } 19 | 20 | :read-only:focus-visible { 21 | outline: none; 22 | } 23 | ` 24 | type FormTextareaProps = { 25 | onChange?: (value: string) => void 26 | } & Omit, 'onChange'> 27 | 28 | const FormTextarea = (props: FormTextareaProps): ReactElement => { 29 | const { onChange, ...rest } = props 30 | return ( 31 | onChange?.(e.target.value)} 33 | {...rest} 34 | /> 35 | ) 36 | } 37 | 38 | export default FormTextarea 39 | -------------------------------------------------------------------------------- /src/components/FormDownload.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | import Button from './Button' 3 | 4 | type FormDownloadProps = { 5 | title?: string 6 | fileName: string 7 | fileData: string 8 | } 9 | 10 | const FormDownload = ({ 11 | title, 12 | fileName, 13 | fileData, 14 | }: FormDownloadProps): ReactElement => { 15 | const onClick = (): void => { 16 | const date = new Date() 17 | const filename = `${fileName}-${date.getFullYear()}-${ 18 | date.getMonth() + 1 19 | }-${date.getDate()}.json` 20 | let element = document.createElement('a') 21 | element.setAttribute( 22 | 'href', 23 | 'data:text/json;charset=utf-8,' + encodeURIComponent(fileData) 24 | ) 25 | element.setAttribute('download', filename) 26 | element.style.display = 'none' 27 | document.body.appendChild(element) 28 | element.click() 29 | document.body.removeChild(element) 30 | } 31 | 32 | return 33 | } 34 | 35 | export default FormDownload 36 | -------------------------------------------------------------------------------- /src/components/FormInput.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | import styled from 'styled-components' 3 | 4 | const StyledInput = styled.input` 5 | background-color: #adb5bd; 6 | color: black; 7 | padding: 10px; 8 | font-size: 12px; 9 | border: none; 10 | border-radius: 5px; 11 | 12 | :focus-visible { 13 | outline: 1px solid gray; 14 | } 15 | 16 | :read-only { 17 | color: white; 18 | background-color: #6c7379; 19 | cursor: not-allowed; 20 | } 21 | 22 | :read-only:focus-visible { 23 | outline: none; 24 | } 25 | ` 26 | 27 | type FormInputProps = { 28 | type?: 'text' | 'password' | 'number' 29 | value: string | number 30 | onChange: (value: string) => void 31 | placeholder?: string 32 | readonly?: boolean 33 | style?: object 34 | } 35 | 36 | const FormInput = (props: FormInputProps): ReactElement => { 37 | const { onChange, ...rest } = props 38 | return ( 39 | onChange(e.target.value)} {...rest} /> 40 | ) 41 | } 42 | 43 | export default FormInput 44 | -------------------------------------------------------------------------------- /src/components/FormSelect.tsx: -------------------------------------------------------------------------------- 1 | import _ from 'lodash' 2 | import { CSSProperties, ReactElement } from 'react' 3 | import styled from 'styled-components' 4 | 5 | const StyledSelect = styled.select` 6 | width: fit-content; 7 | border-color: gray; 8 | max-width: 100%; 9 | ` 10 | 11 | type FormSelectProps = { 12 | defaultValue: T 13 | itemList: { label: string; value: T }[] 14 | onChange: (value: T) => void 15 | containerStyle?: CSSProperties 16 | } 17 | 18 | const FormSelect = ({ 19 | defaultValue, 20 | itemList, 21 | onChange, 22 | containerStyle, 23 | }: FormSelectProps): ReactElement => ( 24 | onChange(e.target.value as T)} 26 | className="form-control" 27 | defaultValue={defaultValue as T} 28 | style={containerStyle} 29 | > 30 | {_.map(itemList, (item, i) => ( 31 | 34 | ))} 35 | 36 | ) 37 | 38 | export default FormSelect 39 | -------------------------------------------------------------------------------- /src/components/ResultForm.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useMemo } from 'react' 2 | 3 | import { COLOR } from 'consts' 4 | import { View, Label, CodeBlock, Text } from 'components' 5 | import { ResultFormType } from 'types' 6 | 7 | import CardSection from './CardSection' 8 | 9 | const ResultForm = ({ 10 | result, 11 | title, 12 | }: { 13 | result?: ResultFormType 14 | title?: string 15 | }): ReactElement => { 16 | const resultStr = useMemo(() => { 17 | if (result?.success) { 18 | return typeof result.value === 'string' 19 | ? result.value 20 | : JSON.stringify(result.value, null, 2) 21 | } 22 | return '' 23 | }, [result]) 24 | 25 | return ( 26 | <> 27 | {result && ( 28 | 29 | {result.success ? ( 30 | 31 | 32 | 33 | 34 | ) : ( 35 | {result.message} 36 | )} 37 | 38 | )} 39 | 40 | ) 41 | } 42 | 43 | export default ResultForm 44 | -------------------------------------------------------------------------------- /src/components/InputField.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement } from 'react' 2 | import { FormGroup, Input, InputGroupText } from 'reactstrap' 3 | import { InputType } from 'reactstrap/types/lib/Input' 4 | import Row from './Row' 5 | 6 | type InputFieldType = { 7 | value?: string 8 | label?: string 9 | name?: string 10 | placeholder: string 11 | type?: InputType 12 | unit?: string 13 | onChange?: React.ChangeEventHandler 14 | readOnly?: boolean 15 | accept?: string 16 | } 17 | 18 | const InputField = ({ 19 | value, 20 | label, 21 | name, 22 | placeholder, 23 | type, 24 | unit, 25 | onChange, 26 | readOnly, 27 | accept, 28 | }: InputFieldType): ReactElement => ( 29 | 30 | {} 31 | 32 | 42 | {unit && {unit}} 43 | 44 | 45 | ) 46 | 47 | export default InputField 48 | -------------------------------------------------------------------------------- /src/hooks/useLayout.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState, useMemo } from 'react' 2 | 3 | import { STYLE } from 'consts' 4 | 5 | const useLayout = (): { 6 | isDesktopWidth: boolean 7 | isUnderTabletWidth: boolean 8 | isUnderMobileWidth: boolean 9 | } => { 10 | const [isUnderMobileWidth, setIsUnderMobileWidth] = useState( 11 | window.innerWidth < STYLE.MOBILE 12 | ) 13 | const [isUnderTabletWidth, setIsUnderTabletWidth] = useState( 14 | window.innerWidth < STYLE.TABLET 15 | ) 16 | 17 | const isDesktopWidth = useMemo(() => { 18 | return false === isUnderTabletWidth && false === isUnderMobileWidth 19 | }, [isUnderTabletWidth, isUnderMobileWidth]) 20 | 21 | const updateSize = (): void => { 22 | setIsUnderMobileWidth(window.innerWidth < STYLE.MOBILE) 23 | setIsUnderTabletWidth(window.innerWidth < STYLE.TABLET) 24 | } 25 | 26 | useEffect(() => { 27 | window.addEventListener('resize', updateSize) 28 | updateSize() 29 | 30 | return (): void => { 31 | window.removeEventListener('resize', updateSize) 32 | } 33 | }, []) 34 | 35 | return { 36 | isUnderTabletWidth, 37 | isUnderMobileWidth, 38 | isDesktopWidth, 39 | } 40 | } 41 | 42 | export default useLayout 43 | -------------------------------------------------------------------------------- /src/hooks/account/useAccounts.ts: -------------------------------------------------------------------------------- 1 | import Caver, { AccountKeyForRPC, Keyring } from 'caver-js' 2 | import { useQuery } from 'react-query' 3 | 4 | type AccountInfoType = { 5 | address: string 6 | accountKey: AccountKeyForRPC 7 | klay_balance: string 8 | } 9 | 10 | export type UseAccountsReturn = { 11 | accountInfo?: AccountInfoType 12 | refetchAccountInfo: () => void 13 | } 14 | 15 | const useAccounts = ({ 16 | caver, 17 | keyring, 18 | }: { 19 | caver: Caver 20 | keyring?: Keyring 21 | }): UseAccountsReturn => { 22 | const { data: accountInfo, refetch: refetchAccountInfo } = useQuery( 23 | [keyring], 24 | async () => { 25 | if (keyring) { 26 | const accountKey = await caver.rpc.klay.getAccountKey(keyring.address) 27 | 28 | if (accountKey) { 29 | const hexBalance = await caver.rpc.klay.getBalance(keyring.address) 30 | return { 31 | address: keyring.address, 32 | accountKey, 33 | klay_balance: caver.utils.fromPeb(hexBalance, 'KLAY'), 34 | } 35 | } 36 | } 37 | }, 38 | { 39 | enabled: !!keyring, 40 | } 41 | ) 42 | 43 | return { accountInfo, refetchAccountInfo } 44 | } 45 | 46 | export default useAccounts 47 | -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as View } from './View' 2 | export { default as InputField } from './InputField' 3 | export { default as Container } from './Container' 4 | export { default as Text } from './Text' 5 | export { default as Row } from './Row' 6 | export { default as Button } from './Button' 7 | export { default as FormInput } from './FormInput' 8 | export { default as FormSelect } from './FormSelect' 9 | export { default as FormTextarea } from './FormTextarea' 10 | export { default as CopyButton } from './CopyButton' 11 | export { default as FormRadio } from './FormRadio' 12 | export { default as ResultForm } from './ResultForm' 13 | export { default as CodeBlock } from './CodeBlock' 14 | export { default as FormDownload } from './FormDownload' 15 | export { default as LinkA } from './LinkA' 16 | export { default as CardSection } from './CardSection' 17 | export { default as Loading } from './Loading' 18 | export { default as FormFile } from './FormFile' 19 | export { default as CardExample } from './CardExample' 20 | export { default as FormGetKey } from './FormGetKey' 21 | export { default as PrivateKeyWarning } from './PrivateKeyWarning' 22 | 23 | export { Card, CardHeader, CardBody, Label, FormGroup } from 'reactstrap' 24 | -------------------------------------------------------------------------------- /src/consts/style.ts: -------------------------------------------------------------------------------- 1 | import { css, FlattenSimpleInterpolation } from 'styled-components' 2 | 3 | const DESKTOP_WIDE = 1920 4 | const DESKTOP = 1440 5 | const TABLET = 1024 6 | const MOBILE = 480 7 | 8 | const media = { 9 | overWideDesktop: `(min-width: ${DESKTOP_WIDE + 1}px)`, 10 | overDesktop: `(min-width: ${DESKTOP + 1}px)`, 11 | mobile: `(max-width: ${MOBILE}px)`, 12 | tablet: `(max-width: ${TABLET}px)`, 13 | } 14 | 15 | const setMediaWidth = (type?: 'sm' | 'lg'): FlattenSimpleInterpolation => css` 16 | margin: 0 auto; 17 | width: ${type === 'lg' ? DESKTOP : type === 'sm' ? MOBILE : TABLET}px; 18 | max-width: 100%; 19 | @media ${media.tablet} { 20 | width: auto; 21 | margin: 0; 22 | } 23 | ` 24 | 25 | const clickable = css` 26 | cursor: pointer; 27 | :hover { 28 | opacity: 0.8; 29 | } 30 | ` 31 | 32 | const clickableShadow = css` 33 | cursor: pointer; 34 | :hover { 35 | box-shadow: 0px 0px 6px white; 36 | } 37 | ` 38 | 39 | const ellipsis = css` 40 | display: block; 41 | overflow-x: hidden; 42 | text-overflow: ellipsis; 43 | ` 44 | 45 | export default { 46 | TABLET, 47 | MOBILE, 48 | DESKTOP_WIDE, 49 | setMediaWidth, 50 | media, 51 | clickable, 52 | clickableShadow, 53 | ellipsis, 54 | } 55 | -------------------------------------------------------------------------------- /.github/workflows/prod-deploy.yml: -------------------------------------------------------------------------------- 1 | name: klaytn-online-toolkit prod deploy workflow 2 | on: 3 | push: 4 | tags: 5 | - v[0-9]+.[0-9]+.[0-9]+ 6 | 7 | jobs: 8 | deploy: 9 | permissions: 10 | id-token: write 11 | contents: read 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout source code 16 | uses: actions/checkout@v3 17 | 18 | - name: Get AWS credentials 19 | uses: aws-actions/configure-aws-credentials@v1 20 | with: 21 | role-to-assume: ${{ secrets.AWS_IAM_ROLE_ARN_PROD }} 22 | role-session-name: SessionForKlaytnActions 23 | aws-region: ${{ secrets.AWS_REGION_PROD }} 24 | 25 | - name: Installing Node.js 26 | uses: actions/setup-node@v3 27 | with: 28 | node-version: '16' 29 | 30 | - name: Installing dependencies 31 | run: npm install 32 | 33 | - name: Build static files 34 | run: npm run build 35 | 36 | - name: Sync to S3 bucket and validation cloudfront 37 | env: 38 | S3_BUCKET: ${{ secrets.S3_BUCKET_TOOLKIT_PROD }} 39 | CLOUDFRONT_ID: ${{ secrets.CLOUDFRONT_ID_TOOLKIT_PROD }} 40 | run: | 41 | aws s3 sync build s3://$S3_BUCKET --delete 42 | aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_ID --paths "/*" 43 | -------------------------------------------------------------------------------- /src/App/index.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | import _ from 'lodash' 3 | import { Routes, Route, BrowserRouter } from 'react-router-dom' 4 | import { ToastContainer, Slide } from 'react-toastify' 5 | import { QueryClient, QueryClientProvider } from 'react-query' 6 | import 'react-toastify/dist/ReactToastify.css' 7 | 8 | import './black-dashboard-react.css' 9 | import { RouteType } from 'types' 10 | import routes from '../routes' 11 | 12 | import MainPage from '../views/Home' 13 | 14 | import Navigator from './Navbar' 15 | 16 | const queryClient = new QueryClient() 17 | 18 | const getRoutes = (routes: RouteType[]): ReactElement[][] => 19 | _.map(routes, (prop) => 20 | _.map(prop.items, (item) => ( 21 | } 25 | /> 26 | )) 27 | ) 28 | 29 | function App(): ReactElement { 30 | return ( 31 | 32 | 33 |
34 | 35 | 36 | } /> 37 | {getRoutes(routes)} 38 | 39 |
40 | 47 |
48 |
49 | ) 50 | } 51 | 52 | export default App 53 | -------------------------------------------------------------------------------- /src/views/Web3modal/web3modalComponents/AssetRow.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | import Icon from './Icon' 3 | import ERC20Icon from './ERC20Icon' 4 | import KlaytnIcon from 'images/klaytn-logo.png' 5 | import eth from 'images/eth.svg' 6 | import { UTIL } from 'consts' 7 | import { ReactElement } from 'react' 8 | 9 | const SAssetRow = styled.div` 10 | width: 100%; 11 | padding: 20px; 12 | display: flex; 13 | justify-content: space-between; 14 | ` 15 | const SAssetRowLeft = styled.div` 16 | display: flex; 17 | ` 18 | const SAssetName = styled.div` 19 | display: flex; 20 | margin-left: 10px; 21 | ` 22 | const SAssetRowRight = styled.div` 23 | display: flex; 24 | ` 25 | const SAssetBalance = styled.div` 26 | display: flex; 27 | ` 28 | 29 | const AssetRow = (props: any): ReactElement => { 30 | const { asset } = props 31 | const nativeCurrencyIcon = 32 | asset.symbol && asset.symbol.toLowerCase() === 'eth' 33 | ? eth 34 | : asset.symbol.toLowerCase() === 'klay' 35 | ? KlaytnIcon 36 | : null 37 | return ( 38 | 39 | 40 | {nativeCurrencyIcon ? ( 41 | 42 | ) : ( 43 | 44 | )} 45 | {asset.name} 46 | 47 | 48 | 49 | {`${UTIL.toBn(asset.balance).dividedBy(Math.pow(10, 18))} ${ 50 | asset.symbol 51 | }`} 52 | 53 | 54 | 55 | ) 56 | } 57 | 58 | export default AssetRow 59 | -------------------------------------------------------------------------------- /src/components/FormRadio.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | import styled from 'styled-components' 3 | import _ from 'lodash' 4 | 5 | import { STYLE } from 'consts' 6 | 7 | import { Text, Row } from 'components' 8 | 9 | const StyledRadioMenu = styled(Row)` 10 | gap: 8px; 11 | overflow: hidden; 12 | padding-right: 20px; 13 | ` 14 | 15 | const StyledRadioItem = styled(Row)<{ selected: boolean }>` 16 | ${STYLE.clickable}; 17 | border-radius: 300px; 18 | border: 2px solid; 19 | border-color: ${({ selected }): string => (selected ? '#1d8cf8' : 'gray')}; 20 | padding: 4px 16px; 21 | white-space: nowrap; 22 | ` 23 | 24 | export type ItemListType = { 25 | title: string 26 | value: T 27 | }[] 28 | 29 | const FormRadio = ({ 30 | selectedValue, 31 | itemList, 32 | onClick, 33 | }: { 34 | selectedValue: T 35 | itemList: ItemListType 36 | onClick: (value: T) => void 37 | }): ReactElement => { 38 | const RadioItem = ({ 39 | value, 40 | title, 41 | }: { 42 | value: T 43 | title: string 44 | }): ReactElement => { 45 | const selected = value === selectedValue 46 | 47 | return ( 48 | { 51 | onClick(value) 52 | }} 53 | > 54 | {title} 55 | 56 | ) 57 | } 58 | 59 | return ( 60 | 61 | {_.map(itemList, (x) => ( 62 | 67 | ))} 68 | 69 | ) 70 | } 71 | 72 | export default FormRadio 73 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Klaytn Online Toolkit 6 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/views/Web3modal/web3modalComponents/AccountAssets.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | import styled from 'styled-components' 3 | 4 | import { View } from 'components' 5 | import { IAssetData } from 'types' 6 | import AssetRow from './AssetRow' 7 | 8 | const StyledLayout = styled(View)` 9 | width: 100%; 10 | justify-content: space-between; 11 | ` 12 | 13 | const AccountAssets = (props: any): ReactElement => { 14 | const { assets, chainId } = props 15 | const defaultNativeCurrency = 16 | chainId === 1001 || chainId === 8217 17 | ? { 18 | contractAddress: '', 19 | symbol: 'KLAY', 20 | name: 'Klaytn', 21 | decimals: '18', 22 | balance: '0', 23 | } 24 | : { 25 | contractAddress: '', 26 | name: 'Ethereum', 27 | symbol: 'ETH', 28 | decimals: '18', 29 | balance: '0', 30 | } 31 | 32 | let nativeCurrency: IAssetData = defaultNativeCurrency 33 | let tokens: IAssetData[] = [] 34 | if (assets && assets.length) { 35 | const filteredNativeCurrency = assets.filter((asset: IAssetData) => 36 | asset && asset.symbol 37 | ? asset.symbol.toLowerCase() === nativeCurrency.symbol.toLowerCase() 38 | : false 39 | ) 40 | nativeCurrency = 41 | filteredNativeCurrency && filteredNativeCurrency.length 42 | ? filteredNativeCurrency[0] 43 | : defaultNativeCurrency 44 | tokens = assets.filter((asset: IAssetData) => 45 | asset && asset.symbol 46 | ? asset.symbol.toLowerCase() !== nativeCurrency.symbol.toLowerCase() 47 | : false 48 | ) 49 | } 50 | return ( 51 | 52 | 53 | {tokens.map((token) => ( 54 | 55 | ))} 56 | 57 | ) 58 | } 59 | 60 | export default AccountAssets 61 | -------------------------------------------------------------------------------- /src/views/Web3modal/web3modalComponents/ModalResult.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | import styled from 'styled-components' 3 | import _ from 'lodash' 4 | 5 | const SContainer = styled.div` 6 | height: 100%; 7 | min-height: 200px; 8 | display: flex; 9 | flex-direction: column; 10 | justify-content: center; 11 | align-items: center; 12 | word-break: break-word; 13 | ` 14 | 15 | interface IResultTableStyleProps { 16 | nested?: boolean 17 | } 18 | 19 | const STable = styled(SContainer)` 20 | flex-direction: column; 21 | min-height: ${({ nested }): string => (nested ? 'auto' : '200px')}; 22 | text-align: left; 23 | ` 24 | 25 | const SRow = styled.div` 26 | width: 100%; 27 | display: ${({ nested }): string => (nested ? 'block' : 'flex')}; 28 | margin: 6px 0; 29 | ` 30 | 31 | const SKey = styled.div` 32 | width: ${({ nested }): string => (nested ? '100%' : '30%')}; 33 | font-weight: 700; 34 | ` 35 | 36 | const SValue = styled.div` 37 | width: ${({ nested }): string => (nested ? '100%' : '70%')}; 38 | font-family: monospace; 39 | ` 40 | 41 | function ModalResult(props: any): ReactElement { 42 | if (!props.children) { 43 | return <> 44 | } 45 | const result = props.children 46 | 47 | return ( 48 | 49 | {Object.keys(result).map((key) => { 50 | const nested = _.isObject(result[key]) 51 | return ( 52 | 53 | {key} 54 | 55 | {nested ? ( 56 | {result[key]} 57 | ) : ( 58 | _.toString(result[key]) 59 | )} 60 | 61 | 62 | ) 63 | })} 64 | 65 | ) 66 | } 67 | 68 | export default ModalResult 69 | -------------------------------------------------------------------------------- /src/views/Miscellaneous/KeccakFromString.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useEffect, useMemo, useState } from 'react' 2 | import Caver from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { URLMAP } from 'consts' 6 | import { 7 | Card, 8 | CardHeader, 9 | CardBody, 10 | Label, 11 | Container, 12 | Text, 13 | FormTextarea, 14 | ResultForm, 15 | CardSection, 16 | CodeBlock, 17 | CardExample, 18 | View, 19 | } from 'components' 20 | import { ResultFormType } from 'types' 21 | 22 | const KeccakFromString = (): ReactElement => { 23 | const [inputValue, setInputValue] = useState('') 24 | const [result, setResult] = useState() 25 | 26 | const caver = useMemo(() => new Caver(URLMAP.network['mainnet']['rpc']), []) 27 | 28 | const exValue = 'Hello klaytn' 29 | 30 | const encodeABI = async (): Promise => { 31 | setResult(undefined) 32 | try { 33 | const res = caver.utils.sha3(inputValue) || '' 34 | setResult({ 35 | success: true, 36 | value: res, 37 | }) 38 | } catch (err) { 39 | setResult({ 40 | success: false, 41 | message: _.toString(err), 42 | }) 43 | } 44 | } 45 | 46 | useEffect(() => { 47 | encodeABI() 48 | }, [inputValue]) 49 | 50 | return ( 51 | 52 | 53 | 54 |

Keccak256 from String

55 | Keccak-256 online hash function 56 |
57 | 58 | 59 | 60 | 61 | 62 | 68 | 69 | 73 | 74 | 75 | 76 |
77 |
78 | ) 79 | } 80 | 81 | export default KeccakFromString 82 | -------------------------------------------------------------------------------- /src/views/Miscellaneous/components/KeystoreResultForm.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useMemo } from 'react' 2 | import { Keystore } from 'caver-js' 3 | 4 | import { COLOR } from 'consts' 5 | import { 6 | View, 7 | Label, 8 | CodeBlock, 9 | Text, 10 | CardSection, 11 | FormDownload, 12 | LinkA, 13 | Row, 14 | Button, 15 | } from 'components' 16 | import { ResultFormType } from 'types' 17 | 18 | const KeystoreResultForm = ({ 19 | result, 20 | title, 21 | keystoreName, 22 | }: { 23 | result?: ResultFormType 24 | title?: string 25 | keystoreName?: string 26 | }): ReactElement => { 27 | const resultStr = useMemo(() => { 28 | if (result?.success) { 29 | return typeof result.value === 'string' 30 | ? result.value 31 | : JSON.stringify(result.value, null, 2) 32 | } 33 | return '' 34 | }, [result]) 35 | 36 | return ( 37 | <> 38 | {result && ( 39 | 40 | {result.success ? ( 41 | <> 42 | 43 | 44 | 45 | 49 | 50 | 51 | 52 | Get some testnet KLAY 53 | 54 | 55 | 56 | 57 | 58 | Address : 59 | 60 | 61 | 62 | 63 | 64 | 65 | ) : ( 66 | {result.message} 67 | )} 68 | 69 | )} 70 | 71 | ) 72 | } 73 | 74 | export default KeystoreResultForm 75 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "klaytn-online-toolkit", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@klaytn/kaikas-web3-provider": "^1.0.3", 7 | "@klaytn/klip-web3-provider": "^1.0.2", 8 | "@klaytn/web3modal": "^1.1.0", 9 | "@tabler/icons": "^1.107.0", 10 | "@walletconnect/utils": "^1.8.0", 11 | "axios": "^0.27.2", 12 | "bignumber.js": "^9.1.0", 13 | "caver-js": "^1.6.8", 14 | "ethereumjs-util": "^7.1.5", 15 | "lodash": "^4.17.21", 16 | "prism-react-renderer": "^1.3.5", 17 | "react": "^17.0.2", 18 | "react-copy-to-clipboard": "^5.1.0", 19 | "react-device-detect": "^2.2.2", 20 | "react-dom": "^17.0.2", 21 | "react-query": "^3.39.2", 22 | "react-router-dom": "^6.4.2", 23 | "react-scripts": "^4.0.3", 24 | "react-toastify": "^8.2.0", 25 | "reactstrap": "^9.1.4", 26 | "styled-components": "^5.3.5", 27 | "typescript": "^4.8.3", 28 | "web-vitals": "^2.1.4", 29 | "web3": "^1.8.0" 30 | }, 31 | "devDependencies": { 32 | "@types/ethereumjs-util": "^6.1.0", 33 | "@types/jest": "^27.5.2", 34 | "@types/lodash": "^4.14.185", 35 | "@types/node": "^16.11.59", 36 | "@types/react": "^18.0.20", 37 | "@types/react-copy-to-clipboard": "^5.0.4", 38 | "@types/react-dom": "^18.0.6", 39 | "@types/styled-components": "^5.1.26", 40 | "gh-pages": "^4.0.0" 41 | }, 42 | "scripts": { 43 | "start": "react-scripts start", 44 | "build": "react-scripts build", 45 | "predeploy": "npm run build", 46 | "deploy": "gh-pages -d build", 47 | "lint": "eslint --fix src/**/*.{jsx,ts,tsx}", 48 | "eject": "react-scripts eject" 49 | }, 50 | "eslintConfig": { 51 | "extends": [ 52 | "react-app" 53 | ], 54 | "rules": { 55 | "@typescript-eslint/explicit-function-return-type": "error", 56 | "@typescript-eslint/no-unused-vars": "error", 57 | "import/no-anonymous-default-export": "off", 58 | "react-hooks/exhaustive-deps": "off" 59 | } 60 | }, 61 | "prettier": { 62 | "singleQuote": true, 63 | "semi": false 64 | }, 65 | "browserslist": { 66 | "production": [ 67 | ">0.2%", 68 | "not dead", 69 | "not op_mini all" 70 | ], 71 | "development": [ 72 | "last 1 chrome version", 73 | "last 1 firefox version", 74 | "last 1 safari version" 75 | ] 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/views/Account/components/GetKeySection.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | import { Keyring } from 'caver-js' 3 | 4 | import { COLOR, UTIL } from 'consts' 5 | 6 | import { 7 | Text, 8 | Button, 9 | View, 10 | Label, 11 | CodeBlock, 12 | Row, 13 | LinkA, 14 | CardSection, 15 | FormGetKey, 16 | } from 'components' 17 | import { UseAccountsReturn } from 'hooks/account/useAccounts' 18 | 19 | const GetKeySection = ({ 20 | keyring, 21 | setKeyring, 22 | useAccountsReturn, 23 | }: { 24 | keyring?: Keyring 25 | setKeyring: (value?: Keyring) => void 26 | useAccountsReturn: UseAccountsReturn 27 | }): ReactElement => { 28 | const { accountInfo, refetchAccountInfo } = useAccountsReturn 29 | 30 | return ( 31 | <> 32 | 33 | 34 | 35 | 36 | {keyring && ( 37 | 38 | 39 | 40 | 41 | 42 | 43 | {UTIL.toBn(accountInfo?.klay_balance).isLessThan(1) && ( 44 | <> 45 | 46 | 47 | 48 | 1. Get some testnet KLAY 49 | 50 | 51 | 52 | 53 | 54 | Address : 55 | 56 | 57 | 58 | 59 | 60 | 61 | 2. After getting testnet KLAY, you can retrieve your account 62 | info from Baobab network. 63 | 64 | 67 | 68 | 69 | )} 70 | 71 | )} 72 | 73 | ) 74 | } 75 | 76 | export default GetKeySection 77 | -------------------------------------------------------------------------------- /src/views/Transaction/RLPDecoder.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useEffect, useMemo, useState } from 'react' 2 | import Caver, { Transaction } from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { URLMAP } from 'consts' 6 | import { 7 | Button, 8 | Card, 9 | CardHeader, 10 | CardBody, 11 | Label, 12 | Container, 13 | Text, 14 | FormTextarea, 15 | ResultForm, 16 | CardSection, 17 | CodeBlock, 18 | CardExample, 19 | View, 20 | } from 'components' 21 | import { ResultFormType } from 'types' 22 | 23 | const RLPDecoder = (): ReactElement => { 24 | const [rlpEncoded, setRlpEncoded] = useState('') 25 | const [result, setResult] = useState>() 26 | 27 | const caver = useMemo(() => new Caver(URLMAP.network['mainnet']['rpc']), []) 28 | 29 | const exValue = 30 | '0x38e40c850ba43b74008261a8947d0104ac150f749d36bb34999bcade9f2c0bd2e6c4c3018080' 31 | 32 | const rlpDecode = async (): Promise => { 33 | setResult(undefined) 34 | try { 35 | const res = caver.transaction.decode(rlpEncoded) 36 | setResult({ 37 | success: true, 38 | value: res, 39 | }) 40 | } catch (err) { 41 | setResult({ 42 | success: false, 43 | message: _.toString(err), 44 | }) 45 | } 46 | } 47 | 48 | useEffect(() => { 49 | setResult(undefined) 50 | }, [rlpEncoded]) 51 | 52 | return ( 53 | 54 | 55 | 56 |

RLP Decoder

57 | 58 | Will decode RLP-encoded transaction string, a raw transaction, and 59 | show a Transaction instance. 60 | 61 |
62 | 63 | 64 | 65 | 66 | 67 | 72 | 73 | 77 | 78 | 79 | 80 | 81 |
82 |
83 | ) 84 | } 85 | 86 | export default RLPDecoder 87 | -------------------------------------------------------------------------------- /src/views/Kaikas/TxTypes4/SignMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | import Caver from 'caver-js' 3 | 4 | import { ResultFormType } from 'types' 5 | import { 6 | Label, 7 | FormInput, 8 | Button, 9 | CardSection, 10 | ResultForm, 11 | CodeBlock, 12 | View, 13 | } from 'components' 14 | 15 | const caver = new Caver(window.klaytn) 16 | 17 | type WalletInfoType = { 18 | walletProps: { 19 | walletAddress: string 20 | } 21 | } 22 | 23 | const SignMessage = ({ walletProps }: WalletInfoType): ReactElement => { 24 | const { walletAddress } = walletProps 25 | 26 | const [message, setMessage] = useState('Hello, world!') 27 | const [result, setResult] = useState() 28 | 29 | const sign = async (): Promise => { 30 | try { 31 | const signedMessage = await caver.klay.sign(message, walletAddress) 32 | setResult({ success: true, value: signedMessage }) 33 | } catch (err) { 34 | setResult({ 35 | success: false, 36 | message: 'Please input the appropriate values and sign it.', 37 | }) 38 | } 39 | } 40 | 41 | return ( 42 | <> 43 | 44 |

Sign Message (Not Tx Type)

45 | 46 | 47 | 48 | {}} 52 | value={walletAddress} 53 | /> 54 | 55 | 61 | 62 | 63 | 77 | 78 |
79 | {result && ( 80 | 81 | {result.success === true && ( 82 | 83 | )} 84 | {result && result.success === false && ( 85 | 86 | )} 87 | 88 | )} 89 | 90 | ) 91 | } 92 | 93 | export default SignMessage 94 | -------------------------------------------------------------------------------- /src/components/CodeBlock.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | import styled from 'styled-components' 3 | import Highlight, { defaultProps } from 'prism-react-renderer' 4 | import theme from 'prism-react-renderer/themes/nightOwl' 5 | import { IconCopy } from '@tabler/icons' 6 | import { CopyToClipboard } from 'react-copy-to-clipboard' 7 | 8 | import useToast from 'hooks/useToast' 9 | import View from './View' 10 | import { STYLE } from 'consts' 11 | 12 | const StyledBlockContainer = styled(View)` 13 | position: relative; 14 | ` 15 | 16 | const StyledCopyIconBox = styled(View)` 17 | ${STYLE.clickable} 18 | position: absolute; 19 | top: 5px; 20 | right: 10px; 21 | ` 22 | 23 | const StyledPre = styled.pre` 24 | text-align: left; 25 | padding: 5px; 26 | overflow-x: scroll; 27 | margin: 0; 28 | 29 | & .token-line { 30 | line-height: 1.3em; 31 | height: 1.3em; 32 | } 33 | ` 34 | 35 | const StyledLine = styled.div` 36 | display: table-row; 37 | ` 38 | 39 | const StyledLineNo = styled.span` 40 | display: table-cell; 41 | text-align: right; 42 | padding-right: 1em; 43 | user-select: none; 44 | opacity: 0.5; 45 | ` 46 | 47 | const StyledLineContent = styled.span` 48 | display: table-cell; 49 | ` 50 | 51 | const CodeBlock = ({ 52 | toggle = true, 53 | text, 54 | title, 55 | }: { 56 | toggle?: boolean 57 | text: string 58 | title?: string 59 | }): ReactElement => { 60 | const { toast } = useToast() 61 | 62 | const Block = (): ReactElement => ( 63 | 64 | 65 | {({ 66 | className, 67 | style, 68 | tokens, 69 | getLineProps, 70 | getTokenProps, 71 | }): ReactElement => ( 72 | 73 | {tokens.map((line, i) => ( 74 | 75 | {i + 1} 76 | 77 | {line.map((token, key) => ( 78 | 79 | ))} 80 | 81 | 82 | ))} 83 | 84 | )} 85 | 86 | 87 | { 90 | toast('Copied') 91 | }} 92 | > 93 | 94 | 95 | 96 | 97 | ) 98 | 99 | return toggle ? ( 100 |
101 | {title || 'Code'} 102 | 103 |
104 | ) : ( 105 | 106 | ) 107 | } 108 | 109 | export default CodeBlock 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/) 2 | 3 | # NO LONGER MAINTAINED 4 | 5 | > [!IMPORTANT] 6 | > Since the launch of Kaia Blockchain, this repository has been parked in favour of the new open-source projects in [Kaia's Github](https://github.com/kaiachain). Contributors have now moved there continuing with massive open-source contributions to our blockchain ecosystem. A big thank you to everyone who has contributed to this repository. 7 | > 8 | > For future development and contributions, please refer to the new [kaia-online-toolkit repository](https://github.com/kaiachain/kaia-online-toolkit) 9 | > 10 | > More information about Klaytn's chain merge with Finschia blockchain, please refer to the launching of Kaia blockchain [kaia.io](https://kaia.io). 11 | 12 | --- 13 | 14 | # Klaytn Online Toolkit 15 | Klaytn online toolkit provides code examples and github page to help to utilize the Klaytn SDK(caver-js) easily. 16 | You can test library on: https://toolkit.klaytn.foundation/ 17 | 18 | ## Web3Modal Example 19 | On github page, you can see web3modal demo which is derived from [web3modal/example](https://github.com/WalletConnect/web3modal/tree/master/example) and modified to add Kaikas wallet and Klip wallet. You can add support for multiple providers including Kaikas provider and Klip wallet provider by using [@klaytn/web3modal](https://github.com/klaytn/klaytn-web3modal). We have created a PR in web3modal repo, which is still under review. So we temporarily provide @klaytn/web3modal package. 20 | 21 | ### Kaikas wallet 22 | Download [@klaytn/kaikas-web3-provider](https://github.com/klaytn/kaikas-web3-provider) package. The following code is how to configure their provider options: 23 | 24 | ```javascript 25 | import Web3 from "web3"; 26 | import Web3Modal from "web3modal"; 27 | import { KaikasWeb3Provider } from "@klaytn/kaikas-web3-provider" 28 | 29 | const providerOptions = { 30 | kaikas: { 31 | package: KaikasWeb3Provider // required 32 | } 33 | }; 34 | 35 | const web3Modal = new Web3Modal({ 36 | providerOptions: providerOptions //required 37 | }); 38 | 39 | const provider = await web3Modal.connect(); 40 | 41 | const web3 = new Web3(provider); 42 | ``` 43 | 44 | ### Klip wallet 45 | Download [@klaytn/klip-web3-provider](https://github.com/klaytn/klip-web3-provider) package first. Then you can easily integrate Klip wallet as below: 46 | ```javascript 47 | import Web3 from "web3"; 48 | import Web3Modal from "web3modal"; 49 | import { KlipWeb3Provider } from "@klaytn/klip-web3-provider" 50 | 51 | const providerOptions = { 52 | klip: { 53 | package: KlipWeb3Provider, //required 54 | options: { 55 | bappName: "web3Modal Example App", //required 56 | rpcUrl: "RPC URL" //required 57 | } 58 | } 59 | }; 60 | 61 | const web3Modal = new Web3Modal({ 62 | providerOptions: providerOptions //required 63 | }); 64 | 65 | const provider = await web3Modal.connect(); 66 | 67 | const web3 = new Web3(provider); 68 | ``` 69 | 70 | ## Instructions 71 | 72 | 1. Install dependencies 73 | 74 | ```bash 75 | $ npm install 76 | ``` 77 | 78 | 2. Run 79 | 80 | ```bash 81 | $ npm start 82 | ``` 83 | -------------------------------------------------------------------------------- /src/views/SmartContract/ABIDecoder.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useEffect, useMemo, useState } from 'react' 2 | import Caver, { Result } from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { URLMAP } from 'consts' 6 | import { 7 | Button, 8 | Card, 9 | CardHeader, 10 | CardBody, 11 | Label, 12 | Container, 13 | Text, 14 | FormTextarea, 15 | ResultForm, 16 | CardSection, 17 | CodeBlock, 18 | CardExample, 19 | View, 20 | } from 'components' 21 | import { ResultFormType } from 'types' 22 | 23 | const ABIEncoder = (): ReactElement => { 24 | const [argTypes, setArgTypes] = useState('') 25 | const [encodedData, setEncodedData] = useState('') 26 | const [result, setResult] = useState>() 27 | 28 | const caver = useMemo(() => new Caver(URLMAP.network['mainnet']['rpc']), []) 29 | 30 | const exType = '"uint256"' 31 | const exData = 32 | '0x000000000000000000000009b02b6aef2f4c6d5f1a5aae08bf77321e33e476e6' 33 | 34 | const encodeABI = async (): Promise => { 35 | setResult(undefined) 36 | try { 37 | const types = JSON.parse(`[${argTypes}]`) 38 | const res = caver.abi.decodeParameters(types, encodedData) 39 | setResult({ 40 | success: true, 41 | value: res, 42 | }) 43 | } catch (err) { 44 | setResult({ 45 | success: false, 46 | message: _.toString(err), 47 | }) 48 | } 49 | } 50 | 51 | useEffect(() => { 52 | setResult(undefined) 53 | }, [argTypes, encodedData]) 54 | 55 | return ( 56 | 57 | 58 | 59 |

ABI Decoder

60 | Will decode ABI encoded parameters. 61 |
62 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 91 | 92 | 93 | 94 | 95 |
96 |
97 | ) 98 | } 99 | 100 | export default ABIEncoder 101 | -------------------------------------------------------------------------------- /src/views/Converter/SimpleConverter.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | import styled from 'styled-components' 3 | import { ConverterProps } from '.' 4 | import { Unit } from 'caver-js' 5 | import useToast from 'hooks/useToast' 6 | import { FormInput, View } from 'components' 7 | import { STYLE } from 'consts' 8 | import CopyToClipboard from 'react-copy-to-clipboard' 9 | import { IconCopy } from '@tabler/icons' 10 | 11 | const Title = styled.p` 12 | font-size: 24px; 13 | font-weight: 700; 14 | padding: 15px 15px 0; 15 | border: 0; 16 | color: rgba(255, 255, 255, 0.8); 17 | ` 18 | const Description = styled.p` 19 | font-size: 14px; 20 | font-weight: 400; 21 | padding: 15px 15px 0; 22 | border: 0; 23 | color: rgba(255, 255, 255, 0.8); 24 | ` 25 | const NameConverter = styled.div` 26 | color: #ffffff; 27 | font-size: 16px; 28 | font-weight: 400; 29 | padding-bottom: 15px; 30 | min-width: 140px; 31 | ` 32 | 33 | const SFormInput = styled(FormInput)` 34 | margin-bottom: 10px; 35 | width: 100%; 36 | ` 37 | const CardBodyConverter = styled.div` 38 | padding: 20px 15px; 39 | ` 40 | const FormGroup = styled.div` 41 | display: flex; 42 | align-items: center; 43 | position: relative; 44 | ` 45 | 46 | const StyledCopyIconBox = styled(View)` 47 | ${STYLE.clickable} 48 | position: absolute; 49 | transform: translateY(-30%); 50 | right: 10px; 51 | ` 52 | 53 | const optionsSimple: { unit: Unit; label: string;}[] = [ 54 | { unit: 'peb', label: 'Peb'}, 55 | { unit: 'ston', label: 'ston'}, 56 | { unit: 'KLAY', label: 'KLAY'}, 57 | ] 58 | 59 | const SimpleUnitConverter = (props: ConverterProps): ReactElement => { 60 | const { toast } = useToast() 61 | 62 | const { handleChange, getValue } = props 63 | 64 | return ( 65 | <> 66 | Simple Unit Converter 67 | 68 | The Peb converter from Klaytn is a simple and easy-to-use tool for 69 | converting between {optionsSimple.map((item) => item.label).join(', ')}. 70 | The page housing this tool provides a comprehensive explanation of these 71 | different units and their relationship to the expenditure of Gas in the 72 | Klaytn ecosystem. 73 | 74 | 75 | {optionsSimple.map((item) => { 76 | return ( 77 | 78 | {item.label} 79 | handleChange(value, item.unit)} 82 | value={getValue(item.unit)} 83 | /> 84 | 85 | { 88 | toast('Copied') 89 | }} 90 | > 91 | 92 | 93 | 94 | 95 | ) 96 | })} 97 | 98 | 99 | ) 100 | } 101 | 102 | export default SimpleUnitConverter 103 | -------------------------------------------------------------------------------- /src/views/BlockInfo/BlockHashDecoder.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useEffect, useMemo, useState } from 'react' 2 | import Caver, { Block } from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { URLMAP } from 'consts' 6 | import { 7 | Button, 8 | Card, 9 | CardHeader, 10 | CardBody, 11 | Label, 12 | Container, 13 | Text, 14 | FormInput, 15 | FormSelect, 16 | ResultForm, 17 | CardSection, 18 | CodeBlock, 19 | CardExample, 20 | View, 21 | } from 'components' 22 | import { ResultFormType } from 'types' 23 | 24 | type NetworkType = 'mainnet' | 'testnet' 25 | 26 | const EX_VALUE = { 27 | mainnet: '0x608d45ed14572c854036492dc08c131885ba5de294dd57e6c9468e1116f49063', 28 | testnet: '0xbeb0c8cbe6a7433459bded0e0f5cf7e48dd9039d38f3a618a82dee63cf297e05', 29 | } 30 | 31 | const BlockHashDecoder = (): ReactElement => { 32 | const [network, setNetwork] = useState('mainnet') 33 | const [blockHash, setBlockHash] = useState('') 34 | const [result, setResult] = useState>() 35 | 36 | const caver = useMemo( 37 | () => new Caver(URLMAP.network[network]['rpc']), 38 | [network] 39 | ) 40 | 41 | const exValue = useMemo(() => EX_VALUE[network], [network]) 42 | 43 | const decodeBlockHash = async (): Promise => { 44 | setResult(undefined) 45 | try { 46 | const res = await caver.rpc.klay.getBlockByHash(blockHash) 47 | setResult({ 48 | success: true, 49 | value: res, 50 | }) 51 | } catch (err) { 52 | setResult({ 53 | success: false, 54 | message: _.toString(err), 55 | }) 56 | } 57 | } 58 | 59 | useEffect(() => { 60 | setResult(undefined) 61 | }, [blockHash]) 62 | 63 | return ( 64 | 65 | 66 | 67 |

Block Hash Decoder

68 | Will return block information by block hash. 69 |
70 | 71 | 72 | 73 | 82 | 83 | 84 | 85 | 86 | 87 | 92 | 93 | 97 | 98 | 99 | 100 | 101 |
102 |
103 | ) 104 | } 105 | 106 | export default BlockHashDecoder 107 | -------------------------------------------------------------------------------- /src/views/SmartContract/ABIEncoder.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useEffect, useMemo, useState } from 'react' 2 | import Caver from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { URLMAP } from 'consts' 6 | import { 7 | Button, 8 | Card, 9 | CardHeader, 10 | CardBody, 11 | Label, 12 | Container, 13 | Text, 14 | FormTextarea, 15 | ResultForm, 16 | CardSection, 17 | CodeBlock, 18 | CardExample, 19 | View, 20 | } from 'components' 21 | import { ResultFormType } from 'types' 22 | 23 | const ABIEncoder = (): ReactElement => { 24 | const [argTypes, setArgTypes] = useState('') 25 | const [argValues, setArgValues] = useState('') 26 | const [result, setResult] = useState() 27 | 28 | const caver = useMemo(() => new Caver(URLMAP.network['mainnet']['rpc']), []) 29 | 30 | const exType = '"bool", "address", "uint8[]", "string"' 31 | const exValue = 32 | 'true, "0x77656c636f6d6520746f20657468657265756d2e", [34, 255], "Hello!%"' 33 | 34 | const encodeABI = async (): Promise => { 35 | setResult(undefined) 36 | try { 37 | const types = JSON.parse(`[${argTypes}]`) 38 | const values = JSON.parse(`[${argValues}]`) 39 | const res = caver.abi.encodeParameters(types, values) 40 | setResult({ 41 | success: true, 42 | value: res, 43 | }) 44 | } catch (err) { 45 | setResult({ 46 | success: false, 47 | message: _.toString(err), 48 | }) 49 | } 50 | } 51 | 52 | useEffect(() => { 53 | setResult(undefined) 54 | }, [argTypes, argValues]) 55 | 56 | return ( 57 | 58 | 59 | 60 |

ABI Encoder

61 | Will encode Solidity ABI data. 62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 76 | 77 | 78 | 79 | 85 | 86 | 87 | 94 | 95 | 96 | 97 | 98 | 99 |
100 |
101 | ) 102 | } 103 | 104 | export default ABIEncoder 105 | -------------------------------------------------------------------------------- /src/views/Kaikas/Common/FeeDelegation.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | import Caver, { TransactionReceipt } from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { ResultFormType } from 'types' 6 | import { 7 | Label, 8 | FormInput, 9 | Button, 10 | CardSection, 11 | ResultForm, 12 | CodeBlock, 13 | View, 14 | } from 'components' 15 | 16 | const caver = new Caver(window.klaytn) 17 | 18 | type FeeDelegationType = { 19 | feeDelegationProps: { 20 | walletAddress: string 21 | signedTx: string 22 | } 23 | } 24 | 25 | const FeeDelegation = ({ 26 | feeDelegationProps, 27 | }: FeeDelegationType): ReactElement => { 28 | const { walletAddress, signedTx } = feeDelegationProps 29 | 30 | const [txHash, setTxHash] = useState('') 31 | const [receipt, setReceipt] = useState>() 32 | const [error, setError] = useState() 33 | 34 | const sendTransaction = (): void => { 35 | try { 36 | caver.klay 37 | .sendTransaction({ 38 | senderRawTransaction: signedTx, 39 | feePayer: walletAddress, 40 | }) 41 | .once('transactionHash', (transactionHash) => { 42 | setError(undefined) 43 | setReceipt(undefined) 44 | setTxHash(transactionHash) 45 | }) 46 | .once('receipt', (receipt) => { 47 | setReceipt({ success: true, value: receipt }) 48 | }) 49 | .once('error', (err) => { 50 | setError({ 51 | success: false, 52 | message: err.message, 53 | }) 54 | }) 55 | } catch (err) { 56 | setError({ 57 | success: false, 58 | message: _.toString(err), 59 | }) 60 | } 61 | } 62 | 63 | return ( 64 | <> 65 | 66 |

Fee Payer

67 | 68 | 69 | 70 | {}} 74 | value={walletAddress} 75 | /> 76 | 77 | 78 | { 88 | setError(undefined) 89 | setReceipt(undefined) 90 | setTxHash(transactionHash) 91 | }) 92 | .once('receipt', (receipt) => { 93 | setReceipt({ success: true, value: receipt }) 94 | }) 95 | .once('error', (err) => { 96 | setError({ success: false, message: err.message }) 97 | })`} 98 | /> 99 | 100 |
101 | {!error && txHash && ( 102 | 103 |

Transaction Result

104 | {txHash && ( 105 | 109 | )} 110 | {receipt && } 111 |
112 | )} 113 | {error && } 114 | 115 | ) 116 | } 117 | 118 | export default FeeDelegation 119 | -------------------------------------------------------------------------------- /src/views/Miscellaneous/LoadKeystore.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useMemo, useState } from 'react' 2 | import Caver, { Keystore } from 'caver-js' 3 | import _ from 'lodash' 4 | import { Card, CardHeader, CardBody } from 'reactstrap' 5 | 6 | import { URLMAP, UTIL } from 'consts' 7 | import { 8 | Button, 9 | Container, 10 | Label, 11 | Text, 12 | FormInput, 13 | ResultForm, 14 | CardSection, 15 | CodeBlock, 16 | FormFile, 17 | PrivateKeyWarning, 18 | View, 19 | } from 'components' 20 | import { ResultFormType } from 'types' 21 | 22 | const LoadKeystore = (): ReactElement => { 23 | const [keystoreJSON, setKeystoreJSON] = useState() 24 | const [password, setPassword] = useState('') 25 | const [result, setResult] = useState() 26 | 27 | const caver = useMemo(() => new Caver(URLMAP.network['mainnet']['rpc']), []) 28 | 29 | const handleKeystoreChange = (files?: FileList): void => { 30 | if (files && files.length > 0) { 31 | const fileReader = new FileReader() 32 | fileReader.readAsText(files[0], 'UTF-8') 33 | fileReader.onload = (event): void => { 34 | if (typeof event.target?.result === 'string') { 35 | const json = UTIL.jsonTryParse(event.target.result) 36 | setKeystoreJSON(json) 37 | } 38 | } 39 | } 40 | } 41 | 42 | const decrypt = (): void => { 43 | if (keystoreJSON) { 44 | //decrypt and add priv key to PrivKey list 45 | try { 46 | const keyring = caver.wallet.keyring.decrypt(keystoreJSON, password) 47 | const message = `${keyring.type} 48 | ${JSON.stringify(keyring, null, 2)}` 49 | 50 | setResult({ 51 | success: true, 52 | value: message, 53 | }) 54 | } catch (err) { 55 | setResult({ 56 | success: false, 57 | message: _.toString(err), 58 | }) 59 | } 60 | } else { 61 | } 62 | } 63 | 64 | return ( 65 | 66 | 67 | 68 |

Load Keystore File

69 | 70 | Decrypt a keystore v3 or v4 JSON and return the decrypted Keyring 71 | instance.{' '} 72 | 73 | 74 |
75 | 76 | 77 | 78 | 79 | 80 | 85 | 86 | 87 | 88 | 94 | 95 | 96 | 104 | 105 | 106 | 107 | 108 |
109 |
110 | ) 111 | } 112 | 113 | export default LoadKeystore 114 | -------------------------------------------------------------------------------- /src/views/Converter/ExtendedConverter.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | import styled from 'styled-components' 3 | import { CopyToClipboard } from 'react-copy-to-clipboard' 4 | import { IconCopy } from '@tabler/icons' 5 | 6 | import useToast from 'hooks/useToast' 7 | import { FormInput, View } from 'components' 8 | import { ConverterProps } from '.' 9 | import { Unit } from 'caver-js' 10 | import { STYLE } from 'consts' 11 | 12 | const Title = styled.p` 13 | font-size: 24px; 14 | font-weight: 700; 15 | padding: 15px 15px 0; 16 | border: 0; 17 | color: rgba(255, 255, 255, 0.8); 18 | ` 19 | const Description = styled.p` 20 | font-size: 14px; 21 | font-weight: 400; 22 | padding: 15px 15px 0; 23 | border: 0; 24 | color: rgba(255, 255, 255, 0.8); 25 | ` 26 | const NameConverter = styled.div` 27 | color: #ffffff; 28 | font-size: 16px; 29 | font-weight: 400; 30 | padding-bottom: 15px; 31 | min-width: 140px; 32 | ` 33 | 34 | const SFormInput = styled(FormInput)` 35 | margin-bottom: 10px; 36 | width: 100%; 37 | ` 38 | const CardBodyConverter = styled.div` 39 | padding: 20px 15px; 40 | ` 41 | const FormGroup = styled.div` 42 | display: flex; 43 | align-items: center; 44 | position: relative; 45 | ` 46 | 47 | const StyledCopyIconBox = styled(View)` 48 | ${STYLE.clickable} 49 | position: absolute; 50 | transform: translateY(-30%); 51 | right: 10px; 52 | ` 53 | 54 | const optionsExtended: { 55 | unit: Unit 56 | label: string 57 | }[] = [ 58 | { unit: 'peb', label: 'Peb' }, 59 | { unit: 'kpeb', label: 'KPeb' }, 60 | { unit: 'Mpeb', label: 'MPeb' }, 61 | { unit: 'Gpeb', label: 'GPeb' }, 62 | { unit: 'ston', label: 'ston' }, 63 | { unit: 'uKLAY', label: 'uKLAY' }, 64 | { unit: 'mKLAY', label: 'mKLAY' }, 65 | { unit: 'KLAY', label: 'KLAY' }, 66 | { unit: 'kKLAY', label: 'kKLAY' }, 67 | { unit: 'MKLAY', label: 'MKLAY' }, 68 | { unit: 'GKLAY', label: 'GKLAY' }, 69 | { unit: 'TKLAY', label: 'TKLAY' }, 70 | ] 71 | 72 | const ExtendedUnitConverter = (props: ConverterProps): ReactElement => { 73 | const { toast } = useToast() 74 | 75 | const { handleChange, getValue } = props 76 | 77 | return ( 78 | <> 79 | Extended Unit Converter 80 | 81 | The Peb converter from Klaytn is a simple and easy-to-use tool for 82 | converting between{' '} 83 | {optionsExtended.map((item) => item.label).join(', ')}. The page housing 84 | this tool provides a comprehensive explanation of these different units 85 | and their relationship to the expenditure of Gas in the Klaytn 86 | ecosystem. 87 | 88 | 89 | {optionsExtended.map((item) => { 90 | return ( 91 | 92 | {item.label} 93 | handleChange(value, item.unit)} 97 | value={getValue(item.unit)} 98 | /> 99 | 100 | { 103 | toast('Copied') 104 | }} 105 | > 106 | 107 | 108 | 109 | 110 | ) 111 | })} 112 | 113 | 114 | ) 115 | } 116 | 117 | export default ExtendedUnitConverter 118 | -------------------------------------------------------------------------------- /src/views/Web3modal/web3modalComponents/Header.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react' 2 | import styled from 'styled-components' 3 | import * as PropTypes from 'prop-types' 4 | import { getChainData } from '../helpers/utilities' 5 | import Banner from './Banner' 6 | import useLayout from 'hooks/useLayout' 7 | 8 | export const transitions = { 9 | short: 'all 0.1s ease-in-out', 10 | base: 'all 0.2s ease-in-out', 11 | long: 'all 0.3s ease-in-out', 12 | button: 'all 0.15s ease-in-out', 13 | } 14 | 15 | const SHeader = styled.div` 16 | margin-top: -1px; 17 | margin-bottom: 1px; 18 | width: 100%; 19 | display: flex; 20 | align-items: center; 21 | padding: 20px 16px; 22 | ` 23 | 24 | const SActiveAccount = styled.div` 25 | display: flex; 26 | align-items: flex-end; 27 | position: relative; 28 | font-weight: 500; 29 | flex-direction: column; 30 | text-align: right; 31 | ` 32 | 33 | const SActiveChain = styled(SActiveAccount)` 34 | flex-direction: column; 35 | text-align: left; 36 | align-items: flex-start; 37 | & p { 38 | font-size: 0.8em; 39 | margin: 0; 40 | padding: 0; 41 | } 42 | & p:nth-child(2) { 43 | font-weight: bold; 44 | } 45 | ` 46 | 47 | interface IHeaderStyle { 48 | connected: boolean 49 | } 50 | const SAddress = styled.p` 51 | transition: ${transitions.base}; 52 | font-weight: bold; 53 | margin: ${({ connected }): string => (connected ? '-2px auto 0.7em' : '0')}; 54 | padding-bottom: 5; 55 | ` 56 | 57 | const SDisconnect = styled.div` 58 | transition: ${transitions.button}; 59 | font-size: 12px; 60 | font-family: monospace; 61 | top: 20px; 62 | opacity: 0.7; 63 | cursor: pointer; 64 | text-align: right; 65 | 66 | opacity: ${({ connected }): number => (connected ? 1 : 0)}; 67 | visibility: ${({ connected }): string => (connected ? 'visible' : 'hidden')}; 68 | pointer-events: ${({ connected }): string => (connected ? 'auto' : 'none')}; 69 | 70 | &:hover { 71 | transform: translateY(-1px); 72 | opacity: 0.5; 73 | } 74 | ` 75 | 76 | interface IHeaderProps { 77 | killSession: () => void 78 | connected: boolean 79 | address: string 80 | chainId: number 81 | } 82 | 83 | const Header = (props: IHeaderProps): ReactElement => { 84 | const { isUnderMobileWidth } = useLayout() 85 | 86 | const { connected, address, chainId, killSession } = props 87 | const chainData = chainId ? getChainData(chainId) : null 88 | return ( 89 | 96 | {connected && chainData ? ( 97 | 98 |

{`Connected to`}

99 |

{chainData.name}

100 |
101 | ) : ( 102 | 103 | )} 104 | {address && ( 105 | 106 | 114 | {address} 115 | 116 | 117 | {'Disconnect'} 118 | 119 | 120 | )} 121 |
122 | ) 123 | } 124 | 125 | Header.propTypes = { 126 | killSession: PropTypes.func.isRequired, 127 | address: PropTypes.string, 128 | } 129 | 130 | export default Header 131 | -------------------------------------------------------------------------------- /src/views/SmartContract/FunctionSignature.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useEffect, useMemo, useState } from 'react' 2 | import Caver, { AbiItem } from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { URLMAP } from 'consts' 6 | import { 7 | Card, 8 | CardHeader, 9 | CardBody, 10 | Label, 11 | Container, 12 | Text, 13 | FormTextarea, 14 | FormRadio, 15 | ResultForm, 16 | CardSection, 17 | CodeBlock, 18 | CardExample, 19 | View, 20 | } from 'components' 21 | import { ResultFormType } from 'types' 22 | 23 | enum InputTypeEnum { 24 | STRING = 'STRING', 25 | ABI = 'ABI', 26 | } 27 | 28 | const EX_VALUE = { 29 | [InputTypeEnum.STRING]: 'myMethod(uint256,string)', 30 | [InputTypeEnum.ABI]: 31 | '{"name":"yourMethod","type":"function","inputs":[{"type":"uint256","name":"myNumber"},{"type":"string","name":"mystring"}]}', 32 | } 33 | 34 | const FunctionSignature = (): ReactElement => { 35 | const [inputValue, setInputValue] = useState('') 36 | const [result, setResult] = useState() 37 | const [inputType, setInputType] = useState( 38 | InputTypeEnum.STRING 39 | ) 40 | 41 | const caver = useMemo(() => new Caver(URLMAP.network['mainnet']['rpc']), []) 42 | const exValue = useMemo(() => EX_VALUE[inputType], [inputType]) 43 | 44 | const encode = async (): Promise => { 45 | setResult(undefined) 46 | try { 47 | const param = 48 | inputType === InputTypeEnum.STRING 49 | ? inputValue 50 | : (JSON.parse(inputValue) as AbiItem) 51 | const res = caver.abi.encodeFunctionSignature(param) 52 | setResult({ 53 | success: true, 54 | value: res, 55 | }) 56 | } catch (err) { 57 | setResult({ 58 | success: false, 59 | message: _.toString(err), 60 | }) 61 | } 62 | } 63 | 64 | useEffect(() => { 65 | inputValue && encode() 66 | }, [inputValue]) 67 | 68 | return ( 69 | 70 | 71 | 72 |

Encode Function Signature

73 | 74 | Encodes the function signature to its ABI signature, which are the 75 | first 4 bytes of the sha3 hash of the function name including 76 | parameter types. 77 | 78 |
79 | 80 | 81 | 82 | 83 | 91 | 92 | 93 | 94 | 100 | 101 | 108 | 109 | 110 | 111 | 112 |
113 |
114 | ) 115 | } 116 | 117 | export default FunctionSignature 118 | -------------------------------------------------------------------------------- /src/views/Transaction/TxHashDecoder.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useEffect, useMemo, useState } from 'react' 2 | import Caver, { AbstractTransaction } from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { URLMAP } from 'consts' 6 | import { 7 | Button, 8 | Card, 9 | CardHeader, 10 | CardBody, 11 | Label, 12 | Container, 13 | Text, 14 | FormTextarea, 15 | FormSelect, 16 | ResultForm, 17 | CardSection, 18 | CodeBlock, 19 | CardExample, 20 | View, 21 | } from 'components' 22 | import { ResultFormType } from 'types' 23 | 24 | type NetworkType = 'mainnet' | 'testnet' 25 | 26 | const EX_VALUE = { 27 | mainnet: '0x272272d25387cd8b0d3bf842d0d9fa2dee7c014ae66c3fd7a53865453d9bc7cc', 28 | testnet: '0xc31e393790e48dfcc8bea115ab45287175665f083313ba1012b6f0aca9e0c78e', 29 | } 30 | 31 | const TxHashDecoder = (): ReactElement => { 32 | const [network, setNetwork] = useState('mainnet') 33 | const [txHash, setTxHash] = useState('') 34 | const [result, setResult] = useState>() 35 | const [rawTx, setRawTx] = useState('') 36 | const caver = useMemo( 37 | () => new Caver(URLMAP.network[network]['rpc']), 38 | [network] 39 | ) 40 | 41 | const exValue = useMemo(() => EX_VALUE[network], [network]) 42 | 43 | const decodeTxHash = async (): Promise => { 44 | setResult(undefined) 45 | try { 46 | const res = await caver.transaction.getTransactionByHash(txHash) 47 | setRawTx(res.getRawTransaction()) 48 | setResult({ 49 | success: true, 50 | value: res, 51 | }) 52 | } catch (err) { 53 | setResult({ 54 | success: false, 55 | message: _.toString(err), 56 | }) 57 | } 58 | } 59 | 60 | useEffect(() => { 61 | setResult(undefined) 62 | }, [txHash]) 63 | 64 | return ( 65 | 66 | 67 | 68 |

TxHash Decoder

69 | 70 | Query a transaction by transaction hash from Klaytn to get a 71 | transaction instance. 72 | 73 |
74 | 75 | 76 | 77 | 86 | 87 | 88 | 89 | 90 | 91 | 96 | 97 | 102 | 103 | 104 | 105 | {result?.success && ( 106 | 107 | 108 | 109 | 110 | )} 111 | 112 |
113 |
114 | ) 115 | } 116 | 117 | export default TxHashDecoder 118 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 18 | 19 | 20 | 21 | 22 | 23 | 27 | 28 | 32 | 33 | 42 | 46 | Klaytn Online Toolkit 47 | 48 | 76 | 77 | 78 | 79 | 80 |
81 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /src/App/Navbar.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useEffect, useState } from 'react' 2 | import { useLocation, useNavigate } from 'react-router-dom' 3 | import { 4 | Collapse, 5 | Navbar, 6 | NavbarToggler, 7 | Nav, 8 | Dropdown, 9 | DropdownToggle, 10 | DropdownMenu, 11 | DropdownItem, 12 | NavLink, 13 | Form, 14 | } from 'reactstrap' 15 | 16 | import { COLOR } from 'consts' 17 | 18 | import githubIcon from 'images/GitHub-Mark-Light-64px.png' 19 | import routes from '../routes' 20 | import { RouteType } from 'types' 21 | 22 | const RouteItem = ({ route }: { route: RouteType }): ReactElement => { 23 | const [isOpen, setIsOpen] = useState(false) 24 | const { pathname } = useLocation() 25 | const [selectedIndex, setSelectedIndex] = useState(-1) 26 | const navigate = useNavigate() 27 | 28 | const SubItem = ({ 29 | name, 30 | href, 31 | index, 32 | }: { 33 | name: string 34 | href: string 35 | index: number 36 | }): ReactElement => { 37 | useEffect(() => { 38 | if (pathname.includes(href)) { 39 | setSelectedIndex(index) 40 | } 41 | }, []) 42 | 43 | return ( 44 | { 50 | navigate(href) 51 | }} 52 | > 53 | {name} 54 | 55 | ) 56 | } 57 | 58 | useEffect(() => { 59 | if (false === pathname.includes(route.path)) { 60 | setSelectedIndex(-1) 61 | } 62 | }, [pathname]) 63 | 64 | return ( 65 | setIsOpen(true)} 69 | onMouseLeave={(): void => setIsOpen(false)} 70 | toggle={(): void => setIsOpen(!isOpen)} 71 | > 72 | -1 ? COLOR.primary : 'white', 77 | }} 78 | onClick={(): void => { 79 | navigate(`${route.path}${route.items[0].path}`) 80 | }} 81 | > 82 | {route.name} 83 | 84 | 85 | {route.items.map((item, j) => { 86 | const path = `${route.path}${item.path}` 87 | 88 | return ( 89 | 95 | ) 96 | })} 97 | 98 | 99 | ) 100 | } 101 | 102 | const Navigator = (): ReactElement => { 103 | const [isOpen, setIsOpen] = useState(false) 104 | const toggle = (): void => { 105 | setIsOpen(!isOpen) 106 | } 107 | 108 | return ( 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 122 |
123 | 124 | 129 | 130 |
131 |
132 |
133 | ) 134 | } 135 | 136 | export default Navigator 137 | -------------------------------------------------------------------------------- /src/views/Miscellaneous/CheckAccountKey.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useEffect, useMemo, useState } from 'react' 2 | import Caver, { AccountKeyForRPC } from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { URLMAP } from 'consts' 6 | import { 7 | Button, 8 | Card, 9 | CardHeader, 10 | CardBody, 11 | Label, 12 | Container, 13 | Text, 14 | FormSelect, 15 | ResultForm, 16 | FormInput, 17 | CardSection, 18 | CodeBlock, 19 | View, 20 | } from 'components' 21 | import { ResultFormType } from 'types' 22 | 23 | const idToType: Record = { 24 | 1: 'AccountKeyLegacy', 25 | 2: 'AccountKeyPublic', 26 | 3: 'AccountKeyFail (used for Smart Contract Accounts)', 27 | 4: 'AccountKeyWeightedMultiSig', 28 | 5: 'AccountKeyRoleBased', 29 | } 30 | 31 | type NetworkType = 'mainnet' | 'testnet' 32 | 33 | const CheckAccountKey = (): ReactElement => { 34 | const [network, setNetwork] = useState('mainnet') 35 | const [inputAddress, setInputAddress] = useState('') 36 | const [result, setResult] = useState>() 37 | 38 | const caver = useMemo( 39 | () => new Caver(URLMAP.network[network]['rpc']), 40 | [network] 41 | ) 42 | 43 | const title = useMemo(() => { 44 | if (result?.success && result.value?.keyType) { 45 | const keyType = idToType[result.value.keyType] 46 | if (keyType) { 47 | return `The account key type of given address is ${keyType}.` 48 | } 49 | } 50 | }, [result]) 51 | 52 | const checkAddress = async (): Promise => { 53 | setResult(undefined) 54 | try { 55 | const res = await caver.rpc.klay.getAccountKey(inputAddress) 56 | if (!res) { 57 | throw Error( 58 | 'The address does not exist on the actual blockchain network.' 59 | ) 60 | } 61 | 62 | setResult({ 63 | success: true, 64 | value: res, 65 | }) 66 | } catch (err) { 67 | setResult({ 68 | success: false, 69 | message: _.toString(err), 70 | }) 71 | } 72 | } 73 | 74 | useEffect(() => { 75 | setResult(undefined) 76 | }, [inputAddress]) 77 | 78 | return ( 79 | 80 | 81 | 82 |

Check Account Key Type

83 | 84 | You can check the account key of the Externally Owned Account (EOA) 85 | of the given address. The account key consist of public key(s) and a 86 | key type. If the account has AccountKeyLegacy or the account of the 87 | given address is a Smart Contract Account, it will return an empty 88 | key value. 89 | 90 |
91 | 92 | 93 | 94 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 115 | 116 | 117 | 118 | 119 |
120 |
121 | ) 122 | } 123 | 124 | export default CheckAccountKey 125 | -------------------------------------------------------------------------------- /src/views/Converter/index.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | import styled from 'styled-components' 3 | import BigNumber from 'bignumber.js' 4 | 5 | import { Card, Container } from 'components' 6 | import SimpleUnitConverter from './SimpleConverter' 7 | import ExtendedUnitConverter from './ExtendedConverter' 8 | import Caver, { Unit } from 'caver-js' 9 | BigNumber.config({ EXPONENTIAL_AT: 1e9 }) 10 | const caver = new Caver(window.klaytn) 11 | 12 | export interface ConverterProps { 13 | handleChange: (value: string, unit: Unit) => void 14 | getValue: (unit: Unit) => string 15 | } 16 | 17 | const TabWraper = styled.div` 18 | margin-top: 1rem; 19 | display: flex; 20 | background: #27293d; 21 | width: 100%; 22 | gap: 15px; 23 | padding: 20px; 24 | color: #696969; 25 | cursor: pointer; 26 | ` 27 | const TabsBox = styled.div` 28 | font-size: 16px; 29 | font-weight: 700; 30 | padding: 0.5em 1em 0.5em 1em; 31 | &.active { 32 | background: #444; 33 | color: #fff; 34 | border-radius: 6px; 35 | } 36 | ` 37 | 38 | const TABSCONVERTER = { 39 | Simple: 'Simple', 40 | Extended: 'Extended', 41 | } 42 | 43 | const TABS = [ 44 | { 45 | key: TABSCONVERTER.Simple, 46 | label: 'Simple Converter', 47 | value: TABSCONVERTER.Simple, 48 | }, 49 | { 50 | key: TABSCONVERTER.Extended, 51 | label: 'Extended Converter', 52 | value: TABSCONVERTER.Simple, 53 | }, 54 | ] 55 | 56 | const KlaytnUnitConverter = (): ReactElement => { 57 | const [currentTab, setCurrentTab] = useState(TABSCONVERTER.Simple) 58 | const defaultTabs = currentTab === TABSCONVERTER.Simple 59 | 60 | const [value, setValue] = useState<{ unit: Unit; value: string }>({ 61 | unit: 'peb', 62 | value: '', 63 | }) 64 | 65 | const getValue = (unit: Unit): string => { 66 | const decimalUnit = caver.utils.unitMap[value.unit] 67 | 68 | if (!value.value) return '' 69 | if (/^[0-9]+([.][0-9]+)?$/.test(value.value)) { 70 | const isDecimalUnit = new BigNumber( 71 | new BigNumber(decimalUnit).multipliedBy(value.value).toFixed(0) 72 | ) 73 | .dividedBy(decimalUnit).toString(); 74 | return caver.utils 75 | .convertFromPeb(caver.utils.convertToPeb(isDecimalUnit, value.unit), unit).toString() 76 | } 77 | if (/^[0-9]+([.]+)?$/.test(value.value)) { 78 | if (unit === value.unit) return value.value 79 | const isDecimalUnit = new BigNumber( 80 | new BigNumber(decimalUnit) 81 | .multipliedBy(value.value.replace('.', '')) 82 | .toFixed(0) 83 | ) 84 | .dividedBy(decimalUnit) 85 | .toString() 86 | return caver.utils 87 | .convertFromPeb(caver.utils.convertToPeb(isDecimalUnit, value.unit), unit).toString() 88 | } 89 | return '' 90 | } 91 | 92 | const handleChange = (value: string, unit: Unit): void => { 93 | if (!value || value === '.') setValue({ unit, value }) 94 | if (/^\d*\.?\d*$/.test(value)) setValue({ unit, value }) 95 | } 96 | 97 | const handleChangeTabs = (value: string): void => { 98 | setCurrentTab(value) 99 | setValue({ unit: 'peb', value: '' }) 100 | } 101 | 102 | return ( 103 | 104 | 105 | 106 | {TABS.map((tab) => ( 107 | handleChangeTabs(tab.key)} 111 | > 112 | {tab.label} 113 | 114 | ))} 115 | 116 | {defaultTabs ? ( 117 | 121 | ) : ( 122 | 126 | )} 127 | 128 | 129 | ) 130 | } 131 | 132 | export default KlaytnUnitConverter 133 | -------------------------------------------------------------------------------- /src/views/Kaikas/TxTypes1/ValueTransferLegacy.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | import Caver, { TransactionReceipt } from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { ResultFormType } from 'types' 6 | import { 7 | Label, 8 | FormInput, 9 | Button, 10 | CardSection, 11 | ResultForm, 12 | CodeBlock, 13 | View, 14 | } from 'components' 15 | 16 | const caver = new Caver(window.klaytn) 17 | 18 | type WalletInfoType = { 19 | walletProps: { 20 | walletAddress: string 21 | } 22 | } 23 | 24 | const ValueTransferLegacy = ({ walletProps }: WalletInfoType): ReactElement => { 25 | const { walletAddress } = walletProps 26 | 27 | const [toAddress, setToAddress] = useState('') 28 | const [value, setValue] = useState('') 29 | const [gas, setGas] = useState('3000000') 30 | 31 | const [txHash, setTxHash] = useState('') 32 | const [receipt, setReceipt] = useState>() 33 | const [error, setError] = useState() 34 | 35 | const signAndSendTransaction = (): void => { 36 | try { 37 | caver.klay 38 | .sendTransaction({ 39 | from: walletAddress, 40 | to: toAddress, 41 | value: caver.utils.toPeb(value, 'KLAY'), 42 | gas: gas, 43 | }) 44 | .once('transactionHash', (transactionHash) => { 45 | setError(undefined) 46 | setReceipt(undefined) 47 | setTxHash(transactionHash) 48 | }) 49 | .once('receipt', (receipt) => { 50 | setReceipt({ success: true, value: receipt }) 51 | }) 52 | .once('error', (err) => { 53 | setError({ 54 | success: false, 55 | message: err.message, 56 | }) 57 | }) 58 | } catch (err) { 59 | setError({ 60 | success: false, 61 | message: _.toString(err), 62 | }) 63 | } 64 | } 65 | 66 | return ( 67 | <> 68 | 69 |

Value Transfer (Legacy)

70 | 71 | 72 | 73 | {}} 77 | value={walletAddress} 78 | /> 79 | 80 | 86 | 87 | 93 | 94 | 100 | 101 | 104 | { 115 | setTxHash(transactionHash) 116 | }) 117 | .once('receipt', (receipt) => { 118 | setReceipt({ success: true, value: receipt }) 119 | }) 120 | .once('error', (err) => { 121 | setError({ success: false, message: err.message }) 122 | }) 123 | `} 124 | /> 125 | 126 |
127 | {!error && txHash && ( 128 | 129 |

Transaction Result

130 | {txHash && ( 131 | 135 | )} 136 | {receipt && } 137 |
138 | )} 139 | {error && } 140 | 141 | ) 142 | } 143 | 144 | export default ValueTransferLegacy 145 | -------------------------------------------------------------------------------- /src/views/Kaikas/TxTypes1/SmartContractDeployLegacy.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | import Caver, { TransactionReceipt } from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { ResultFormType } from 'types' 6 | import { 7 | Label, 8 | FormInput, 9 | Button, 10 | CardSection, 11 | ResultForm, 12 | CodeBlock, 13 | View, 14 | CardExample, 15 | } from 'components' 16 | 17 | import exBytecode from '../Common/exBytecode' 18 | 19 | const caver = new Caver(window.klaytn) 20 | 21 | type WalletInfoType = { 22 | walletProps: { 23 | walletAddress: string 24 | } 25 | } 26 | 27 | const SmartContractDeployLegacy = ({ 28 | walletProps, 29 | }: WalletInfoType): ReactElement => { 30 | const { walletAddress } = walletProps 31 | 32 | const [bytecode, setBytecode] = useState('') 33 | const [gas, setGas] = useState('3000000') 34 | 35 | const [txHash, setTxHash] = useState('') 36 | const [receipt, setReceipt] = useState>() 37 | const [error, setError] = useState() 38 | 39 | const signAndSendTransaction = (): void => { 40 | try { 41 | caver.klay 42 | .sendTransaction({ 43 | from: walletAddress, 44 | data: bytecode, 45 | gas: gas, 46 | }) 47 | .once('transactionHash', (transactionHash) => { 48 | setError(undefined) 49 | setReceipt(undefined) 50 | setTxHash(transactionHash) 51 | }) 52 | .once('receipt', (receipt) => { 53 | setReceipt({ success: true, value: receipt }) 54 | }) 55 | .once('error', (err) => { 56 | setError({ 57 | success: false, 58 | message: err.message, 59 | }) 60 | }) 61 | } catch (err) { 62 | setError({ 63 | success: false, 64 | message: _.toString(err), 65 | }) 66 | } 67 | } 68 | 69 | return ( 70 | <> 71 | 72 |

Smart Contract Deploy (Legacy)

73 | 74 | 75 | 76 | {}} 80 | value={walletAddress} 81 | /> 82 | 86 | 87 | 88 | 94 | 95 | 101 | 102 | 105 | { 115 | setTxHash(transactionHash) 116 | }) 117 | .once('receipt', (receipt) => { 118 | setReceipt({ success: true, value: receipt }) 119 | }) 120 | .once('error', (err) => { 121 | setError({ success: false, message: err.message }) 122 | }) 123 | `} 124 | /> 125 | 126 |
127 | {!error && txHash && ( 128 | 129 |

Transaction Result

130 | {txHash && ( 131 | 135 | )} 136 | {receipt && } 137 |
138 | )} 139 | {error && } 140 | 141 | ) 142 | } 143 | 144 | export default SmartContractDeployLegacy 145 | -------------------------------------------------------------------------------- /src/views/Kaikas/TxTypes2/ValueTransfer.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | import Caver, { TransactionReceipt } from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { ResultFormType } from 'types' 6 | import { 7 | Label, 8 | FormInput, 9 | Button, 10 | CardSection, 11 | ResultForm, 12 | CodeBlock, 13 | View, 14 | } from 'components' 15 | 16 | const caver = new Caver(window.klaytn) 17 | 18 | type WalletInfoType = { 19 | walletProps: { 20 | walletAddress: string 21 | } 22 | } 23 | 24 | const ValueTransfer = ({ walletProps }: WalletInfoType): ReactElement => { 25 | const { walletAddress } = walletProps 26 | 27 | const [toAddress, setToAddress] = useState('') 28 | const [value, setValue] = useState('') 29 | const [gas, setGas] = useState('3000000') 30 | 31 | const [txHash, setTxHash] = useState('') 32 | const [receipt, setReceipt] = useState>() 33 | const [error, setError] = useState() 34 | 35 | const signAndSendTransaction = (): void => { 36 | try { 37 | caver.klay 38 | .sendTransaction({ 39 | type: 'VALUE_TRANSFER', 40 | from: walletAddress, 41 | to: toAddress, 42 | value: caver.utils.toPeb(value, 'KLAY'), 43 | gas: gas, 44 | }) 45 | .once('transactionHash', (transactionHash) => { 46 | setError(undefined) 47 | setReceipt(undefined) 48 | setTxHash(transactionHash) 49 | }) 50 | .once('receipt', (receipt) => { 51 | setReceipt({ success: true, value: receipt }) 52 | }) 53 | .once('error', (err) => { 54 | setError({ 55 | success: false, 56 | message: err.message, 57 | }) 58 | }) 59 | } catch (err) { 60 | setError({ 61 | success: false, 62 | message: _.toString(err), 63 | }) 64 | } 65 | } 66 | 67 | return ( 68 | <> 69 | 70 |

Value Transfer

71 | 72 | 73 | 74 | {}} 78 | value={walletAddress} 79 | /> 80 | 81 | 87 | 88 | 94 | 95 | 101 | 102 | 105 | { 117 | setTxHash(transactionHash) 118 | }) 119 | .once('receipt', (receipt) => { 120 | setReceipt({ success: true, value: receipt }) 121 | }) 122 | .once('error', (err) => { 123 | setError({ success: false, message: err.message }) 124 | })`} 125 | /> 126 | 127 |
128 | {!error && txHash && ( 129 | 130 |

Transaction Result

131 | {txHash && ( 132 | 136 | )} 137 | {receipt && } 138 |
139 | )} 140 | {error && } 141 | 142 | ) 143 | } 144 | 145 | export default ValueTransfer 146 | -------------------------------------------------------------------------------- /src/views/Kaikas/TxTypes2/ValueTransferFeeDelegation.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | import Caver from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { ResultFormType } from 'types' 6 | import { 7 | Label, 8 | FormInput, 9 | Button, 10 | CardSection, 11 | ResultForm, 12 | CodeBlock, 13 | View, 14 | } from 'components' 15 | 16 | import FeeDelegation from '../Common/FeeDelegation' 17 | 18 | const caver = new Caver(window.klaytn) 19 | 20 | type WalletInfoType = { 21 | walletProps: { 22 | walletAddress: string 23 | } 24 | } 25 | 26 | const ValueTransferFeeDelegation = ({ 27 | walletProps, 28 | }: WalletInfoType): ReactElement => { 29 | const { walletAddress } = walletProps 30 | const [senderAddress, setSenderAddress] = useState('') 31 | 32 | const [toAddress, setToAddress] = useState('') 33 | const [value, setValue] = useState('') 34 | const [gas, setGas] = useState('3000000') 35 | 36 | const [signError, setSignError] = useState() 37 | const [signedTx, setSignedTx] = useState('') 38 | 39 | const signTransaction = (): void => { 40 | try { 41 | const txData = { 42 | type: 'FEE_DELEGATED_VALUE_TRANSFER', 43 | from: walletAddress, 44 | to: toAddress, 45 | value: caver.utils.toPeb(value, 'KLAY'), 46 | gas: gas, 47 | } 48 | 49 | caver.klay.signTransaction(txData, (err, res) => { 50 | if (err) { 51 | setSignError({ 52 | success: false, 53 | message: _.toString(err.message), 54 | }) 55 | } else { 56 | const { rawTransaction: senderRawTransaction } = JSON.parse( 57 | JSON.stringify(res) 58 | ) 59 | setSignError(undefined) 60 | setSenderAddress(walletAddress) 61 | setSignedTx(senderRawTransaction) 62 | } 63 | }) 64 | } catch (err) { 65 | setSignError({ 66 | success: false, 67 | message: _.toString(err), 68 | }) 69 | } 70 | } 71 | 72 | return ( 73 | <> 74 | 75 |

Value Transfer (Fee Delegation)

76 | 77 | 78 | 79 | {}} 83 | value={senderAddress || walletAddress} 84 | /> 85 | 86 | 92 | 93 | 99 | 100 | 106 | 107 | 108 | { 121 | if (err) { 122 | setSignError( {success: false, message: _.toString(err.message) }) 123 | } else { 124 | const { rawTransaction: senderRawTransaction } = JSON.parse(JSON.stringify(res)) 125 | setSignError(undefined) 126 | setSenderAddress(walletAddress) 127 | setSignedTx(senderRawTransaction) 128 | } 129 | })`} 130 | /> 131 | 132 |
133 | {signError && } 134 | {signedTx && ( 135 | 136 | 140 | 141 | )} 142 | {signedTx && ( 143 | 144 | )} 145 | 146 | ) 147 | } 148 | 149 | export default ValueTransferFeeDelegation 150 | -------------------------------------------------------------------------------- /src/views/Kaikas/TxTypes2/ValueTransferWithMemo.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | import Caver, { TransactionReceipt } from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { ResultFormType } from 'types' 6 | import { 7 | Label, 8 | FormInput, 9 | Button, 10 | CardSection, 11 | ResultForm, 12 | CodeBlock, 13 | View, 14 | } from 'components' 15 | 16 | const caver = new Caver(window.klaytn) 17 | 18 | type WalletInfoType = { 19 | walletProps: { 20 | walletAddress: string 21 | } 22 | } 23 | 24 | const ValueTransferWithMemo = ({ 25 | walletProps, 26 | }: WalletInfoType): ReactElement => { 27 | const { walletAddress } = walletProps 28 | 29 | const [toAddress, setToAddress] = useState('') 30 | const [value, setValue] = useState('') 31 | const [gas, setGas] = useState('3000000') 32 | const [memo, setMemo] = useState('') 33 | 34 | const [txHash, setTxHash] = useState('') 35 | const [receipt, setReceipt] = useState>() 36 | const [error, setError] = useState() 37 | 38 | const signAndSendTransaction = (): void => { 39 | try { 40 | caver.klay 41 | .sendTransaction({ 42 | type: 'VALUE_TRANSFER_MEMO', 43 | from: walletAddress, 44 | to: toAddress, 45 | value: caver.utils.toPeb(value, 'KLAY'), 46 | gas: gas, 47 | data: memo, 48 | }) 49 | .once('transactionHash', (transactionHash) => { 50 | setError(undefined) 51 | setReceipt(undefined) 52 | setTxHash(transactionHash) 53 | }) 54 | .once('receipt', (receipt) => { 55 | setReceipt({ success: true, value: receipt }) 56 | }) 57 | .once('error', (err) => { 58 | setError({ 59 | success: false, 60 | message: err.message, 61 | }) 62 | }) 63 | } catch (err) { 64 | setError({ 65 | success: false, 66 | message: _.toString(err), 67 | }) 68 | } 69 | } 70 | 71 | return ( 72 | <> 73 | 74 |

Value Transfer with Memo

75 | 76 | 77 | 78 | {}} 82 | value={walletAddress} 83 | /> 84 | 85 | 91 | 92 | 98 | 99 | 105 | 106 | 112 | 113 | 116 | { 129 | setTxHash(transactionHash) 130 | }) 131 | .once('receipt', (receipt) => { 132 | setReceipt({ success: true, value: receipt }) 133 | }) 134 | .once('error', (err) => { 135 | setError({ success: false, message: err.message }) 136 | })`} 137 | /> 138 | 139 |
140 | {!error && txHash && ( 141 | 142 |

Transaction Result

143 | {txHash && ( 144 | 148 | )} 149 | {receipt && } 150 |
151 | )} 152 | {error && } 153 | 154 | ) 155 | } 156 | 157 | export default ValueTransferWithMemo 158 | -------------------------------------------------------------------------------- /src/views/Miscellaneous/constants/exWKLAYAbi.ts: -------------------------------------------------------------------------------- 1 | const exWKLAYAbi = [ 2 | { 3 | anonymous: false, 4 | inputs: [ 5 | { indexed: true, internalType: 'address', name: 'src', type: 'address' }, 6 | { indexed: true, internalType: 'address', name: 'guy', type: 'address' }, 7 | { indexed: false, internalType: 'uint256', name: 'wad', type: 'uint256' }, 8 | ], 9 | name: 'Approval', 10 | type: 'event', 11 | }, 12 | { 13 | anonymous: false, 14 | inputs: [ 15 | { indexed: true, internalType: 'address', name: 'dst', type: 'address' }, 16 | { indexed: false, internalType: 'uint256', name: 'wad', type: 'uint256' }, 17 | ], 18 | name: 'Deposit', 19 | type: 'event', 20 | }, 21 | { 22 | anonymous: false, 23 | inputs: [ 24 | { indexed: true, internalType: 'address', name: 'src', type: 'address' }, 25 | { indexed: true, internalType: 'address', name: 'dst', type: 'address' }, 26 | { indexed: false, internalType: 'uint256', name: 'wad', type: 'uint256' }, 27 | ], 28 | name: 'Transfer', 29 | type: 'event', 30 | }, 31 | { 32 | anonymous: false, 33 | inputs: [ 34 | { indexed: true, internalType: 'address', name: 'src', type: 'address' }, 35 | { indexed: false, internalType: 'uint256', name: 'wad', type: 'uint256' }, 36 | ], 37 | name: 'Withdrawal', 38 | type: 'event', 39 | }, 40 | { payable: true, stateMutability: 'payable', type: 'fallback' }, 41 | { 42 | constant: true, 43 | inputs: [ 44 | { internalType: 'address', name: '', type: 'address' }, 45 | { internalType: 'address', name: '', type: 'address' }, 46 | ], 47 | name: 'allowance', 48 | outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], 49 | payable: false, 50 | stateMutability: 'view', 51 | type: 'function', 52 | }, 53 | { 54 | constant: false, 55 | inputs: [ 56 | { internalType: 'address', name: 'guy', type: 'address' }, 57 | { internalType: 'uint256', name: 'wad', type: 'uint256' }, 58 | ], 59 | name: 'approve', 60 | outputs: [{ internalType: 'bool', name: '', type: 'bool' }], 61 | payable: false, 62 | stateMutability: 'nonpayable', 63 | type: 'function', 64 | }, 65 | { 66 | constant: true, 67 | inputs: [{ internalType: 'address', name: '', type: 'address' }], 68 | name: 'balanceOf', 69 | outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], 70 | payable: false, 71 | stateMutability: 'view', 72 | type: 'function', 73 | }, 74 | { 75 | constant: true, 76 | inputs: [], 77 | name: 'decimals', 78 | outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }], 79 | payable: false, 80 | stateMutability: 'view', 81 | type: 'function', 82 | }, 83 | { 84 | constant: false, 85 | inputs: [], 86 | name: 'deposit', 87 | outputs: [], 88 | payable: true, 89 | stateMutability: 'payable', 90 | type: 'function', 91 | }, 92 | { 93 | constant: true, 94 | inputs: [], 95 | name: 'name', 96 | outputs: [{ internalType: 'string', name: '', type: 'string' }], 97 | payable: false, 98 | stateMutability: 'view', 99 | type: 'function', 100 | }, 101 | { 102 | constant: true, 103 | inputs: [], 104 | name: 'symbol', 105 | outputs: [{ internalType: 'string', name: '', type: 'string' }], 106 | payable: false, 107 | stateMutability: 'view', 108 | type: 'function', 109 | }, 110 | { 111 | constant: true, 112 | inputs: [], 113 | name: 'totalSupply', 114 | outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], 115 | payable: false, 116 | stateMutability: 'view', 117 | type: 'function', 118 | }, 119 | { 120 | constant: false, 121 | inputs: [ 122 | { internalType: 'address', name: 'dst', type: 'address' }, 123 | { internalType: 'uint256', name: 'wad', type: 'uint256' }, 124 | ], 125 | name: 'transfer', 126 | outputs: [{ internalType: 'bool', name: '', type: 'bool' }], 127 | payable: false, 128 | stateMutability: 'nonpayable', 129 | type: 'function', 130 | }, 131 | { 132 | constant: false, 133 | inputs: [ 134 | { internalType: 'address', name: 'src', type: 'address' }, 135 | { internalType: 'address', name: 'dst', type: 'address' }, 136 | { internalType: 'uint256', name: 'wad', type: 'uint256' }, 137 | ], 138 | name: 'transferFrom', 139 | outputs: [{ internalType: 'bool', name: '', type: 'bool' }], 140 | payable: false, 141 | stateMutability: 'nonpayable', 142 | type: 'function', 143 | }, 144 | { 145 | constant: false, 146 | inputs: [{ internalType: 'uint256', name: 'wad', type: 'uint256' }], 147 | name: 'withdraw', 148 | outputs: [], 149 | payable: false, 150 | stateMutability: 'nonpayable', 151 | type: 'function', 152 | }, 153 | ] 154 | 155 | export { exWKLAYAbi } 156 | -------------------------------------------------------------------------------- /src/views/Kaikas/TxTypes2/ValueTransferWithMemoFeeDelegation.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | import Caver from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { ResultFormType } from 'types' 6 | import { 7 | Label, 8 | FormInput, 9 | Button, 10 | CardSection, 11 | ResultForm, 12 | CodeBlock, 13 | View, 14 | } from 'components' 15 | 16 | import FeeDelegation from '../Common/FeeDelegation' 17 | 18 | const caver = new Caver(window.klaytn) 19 | 20 | type WalletInfoType = { 21 | walletProps: { 22 | walletAddress: string 23 | } 24 | } 25 | 26 | const ValueTransferWithMemoFeeDelegation = ({ 27 | walletProps, 28 | }: WalletInfoType): ReactElement => { 29 | const { walletAddress } = walletProps 30 | const [senderAddress, setSenderAddress] = useState('') 31 | 32 | const [toAddress, setToAddress] = useState('') 33 | const [value, setValue] = useState('') 34 | const [gas, setGas] = useState('3000000') 35 | const [memo, setMemo] = useState('') 36 | 37 | const [signError, setSignError] = useState() 38 | const [signedTx, setSignedTx] = useState('') 39 | 40 | const signTransaction = (): void => { 41 | try { 42 | const txData = { 43 | type: 'FEE_DELEGATED_VALUE_TRANSFER_MEMO', 44 | from: walletAddress, 45 | to: toAddress, 46 | value: caver.utils.toPeb(value, 'KLAY'), 47 | gas: gas, 48 | data: memo, 49 | } 50 | 51 | caver.klay.signTransaction(txData, (err, res) => { 52 | if (err) { 53 | setSignError({ 54 | success: false, 55 | message: _.toString(err.message), 56 | }) 57 | } else { 58 | const { rawTransaction: senderRawTransaction } = JSON.parse( 59 | JSON.stringify(res) 60 | ) 61 | setSignError(undefined) 62 | setSenderAddress(walletAddress) 63 | setSignedTx(senderRawTransaction) 64 | } 65 | }) 66 | } catch (err) { 67 | setSignError({ 68 | success: false, 69 | message: _.toString(err), 70 | }) 71 | } 72 | } 73 | 74 | return ( 75 | <> 76 | 77 |

Value Transfer with Memo (Fee Delegation)

78 | 79 | 80 | 81 | {}} 85 | value={senderAddress || walletAddress} 86 | /> 87 | 88 | 94 | 95 | 101 | 102 | 108 | 109 | 115 | 116 | 117 | { 131 | if (err) { 132 | setSignError( {success: false, message: _.toString(err.message) }) 133 | } else { 134 | const { rawTransaction: senderRawTransaction } = JSON.parse(JSON.stringify(res)) 135 | setSignError(undefined) 136 | setSenderAddress(walletAddress) 137 | setSignedTx(senderRawTransaction) 138 | } 139 | })`} 140 | /> 141 | 142 |
143 | {signError && } 144 | {signedTx && ( 145 | 146 | 150 | 151 | )} 152 | {signedTx && ( 153 | 154 | )} 155 | 156 | ) 157 | } 158 | 159 | export default ValueTransferWithMemoFeeDelegation 160 | -------------------------------------------------------------------------------- /src/views/Kaikas/TxTypes4/AddToken.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | 3 | import { 4 | Label, 5 | Text, 6 | FormInput, 7 | Button, 8 | CardSection, 9 | CodeBlock, 10 | View, 11 | CardExample, 12 | } from 'components' 13 | 14 | const AddToken = (): ReactElement => { 15 | const [tokenAddress, setTokenAddress] = useState('') 16 | const [tokenSymbol, setTokenSymbol] = useState('') 17 | const [decimal, setDecimal] = useState('') 18 | const [url, setUrl] = useState('') 19 | 20 | const [success, setSuccess] = useState('') 21 | 22 | const exToken = { 23 | 'Token Contract Address': '0xEa51fb63dD8cfc8574BB158054D86CA786e00F87', 24 | 'Token Symbol': 'BONG', 25 | 'Token Decimals': '18', 26 | 'Token Image (URL)': 27 | 'https://avatars3.githubusercontent.com/u/32095134?s=460&v=4', 28 | } 29 | 30 | type ResultType = { 31 | id: string 32 | jsonrpc: string 33 | result: boolean 34 | } 35 | 36 | const add = (): void => { 37 | const { klaytn } = window 38 | 39 | try { 40 | klaytn.sendAsync( 41 | { 42 | method: 'wallet_watchAsset', 43 | params: { 44 | type: 'ERC20', 45 | options: { 46 | address: tokenAddress, 47 | symbol: tokenSymbol, 48 | decimals: decimal, 49 | image: url, 50 | }, 51 | }, 52 | id: Math.round(Math.random() * 100000), 53 | }, 54 | (err: Object, res: ResultType) => { 55 | if (err) { 56 | setSuccess('no') 57 | } else if (res.result === true) { 58 | setSuccess('yes') 59 | } else if (res.result === false) { 60 | setSuccess('cancel') 61 | } 62 | } 63 | ) 64 | } catch { 65 | setSuccess('no') 66 | } 67 | } 68 | 69 | return ( 70 | <> 71 | 72 |

Add Token (Not Tx Type)

73 | 74 | { 77 | setTokenAddress(exToken['Token Contract Address']) 78 | setTokenSymbol(exToken['Token Symbol']) 79 | setDecimal(exToken['Token Decimals']) 80 | setUrl(exToken['Token Image (URL)']) 81 | }} 82 | /> 83 | 84 | 85 | 91 | 92 | 98 | 99 | 105 | 106 | 112 | 113 | 114 | { 133 | if (err) { 134 | setSuccess('no') 135 | } else if (res.result === true) { 136 | setSuccess('yes') 137 | } else if (res.result === false) { 138 | setSuccess('cancel') 139 | } 140 | } 141 | )`} 142 | /> 143 | 144 |
145 | {success === 'no' && ( 146 | 147 | Please input the appropriate values. 148 | 149 | )} 150 | {success === 'yes' && ( 151 | 152 | 153 | The token was added successfully or the token has already been 154 | added. Check the token list in the Kaikas. 155 | 156 | 157 | )} 158 | {success === 'cancel' && ( 159 | 160 | Token addition canceled. 161 | 162 | )} 163 | 164 | ) 165 | } 166 | 167 | export default AddToken 168 | -------------------------------------------------------------------------------- /src/views/Kaikas/TxTypes2/ValueTransferFeeDelegationWithRatio.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | import Caver from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { ResultFormType } from 'types' 6 | import { 7 | Label, 8 | FormInput, 9 | Button, 10 | CardSection, 11 | ResultForm, 12 | CodeBlock, 13 | View, 14 | } from 'components' 15 | 16 | import FeeDelegation from '../Common/FeeDelegation' 17 | 18 | const caver = new Caver(window.klaytn) 19 | 20 | type WalletInfoType = { 21 | walletProps: { 22 | walletAddress: string 23 | } 24 | } 25 | 26 | const ValueTransferFeeDelegationWithRatio = ({ 27 | walletProps, 28 | }: WalletInfoType): ReactElement => { 29 | const { walletAddress } = walletProps 30 | const [senderAddress, setSenderAddress] = useState('') 31 | 32 | const [toAddress, setToAddress] = useState('') 33 | const [value, setValue] = useState('') 34 | const [gas, setGas] = useState('3000000') 35 | const [ratio, setRatio] = useState('') 36 | 37 | const [signError, setSignError] = useState() 38 | const [signedTx, setSignedTx] = useState('') 39 | 40 | const signTransaction = (): void => { 41 | try { 42 | const txData = { 43 | type: 'FEE_DELEGATED_VALUE_TRANSFER_WITH_RATIO', 44 | from: walletAddress, 45 | to: toAddress, 46 | value: caver.utils.toPeb(value, 'KLAY'), 47 | feeRatio: ratio, 48 | gas: gas, 49 | } 50 | 51 | caver.klay.signTransaction(txData, (err, res) => { 52 | if (err) { 53 | setSignError({ 54 | success: false, 55 | message: _.toString(err.message), 56 | }) 57 | } else { 58 | const { rawTransaction: senderRawTransaction } = JSON.parse( 59 | JSON.stringify(res) 60 | ) 61 | setSignError(undefined) 62 | setSenderAddress(walletAddress) 63 | setSignedTx(senderRawTransaction) 64 | } 65 | }) 66 | } catch (err) { 67 | setSignError({ 68 | success: false, 69 | message: _.toString(err), 70 | }) 71 | } 72 | } 73 | 74 | return ( 75 | <> 76 | 77 |

Value Transfer (Fee Delegation with Ratio)

78 | 79 | 80 | 81 | {}} 85 | value={senderAddress || walletAddress} 86 | /> 87 | 88 | 94 | 95 | 101 | 102 | 108 | 109 | 115 | 116 | 117 | { 131 | if (err) { 132 | setSignError({ success: false, message: _.toString(err.message) }) 133 | } else { 134 | const { rawTransaction: senderRawTransaction } = JSON.parse(JSON.stringify(res)) 135 | setSignError(undefined) 136 | setSenderAddress(walletAddress) 137 | setSignedTx(senderRawTransaction) 138 | } 139 | })`} 140 | /> 141 | 142 |
143 | {signError && } 144 | {signedTx && ( 145 | 146 | 150 | 151 | )} 152 | {signedTx && ( 153 | 154 | )} 155 | 156 | ) 157 | } 158 | 159 | export default ValueTransferFeeDelegationWithRatio 160 | -------------------------------------------------------------------------------- /src/views/Web3modal/web3modalComponents/Modal.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import styled from 'styled-components' 3 | import * as PropTypes from 'prop-types' 4 | 5 | export const transitions = { 6 | short: 'all 0.1s ease-in-out', 7 | base: 'all 0.2s ease-in-out', 8 | long: 'all 0.3s ease-in-out', 9 | button: 'all 0.15s ease-in-out', 10 | } 11 | interface ILightboxStyleProps { 12 | show: boolean 13 | offset: number 14 | opacity?: number 15 | } 16 | 17 | const SLightbox = styled.div` 18 | transition: opacity 0.1s ease-in-out; 19 | text-align: center; 20 | position: absolute; 21 | width: 100%; 22 | height: 100%; 23 | top: 0; 24 | left: 0; 25 | z-index: 2; 26 | will-change: opacity; 27 | background-color: ${({ opacity }): string => { 28 | let alpha = 0.4 29 | if (typeof opacity === 'number') { 30 | alpha = opacity 31 | } 32 | return `rgba(0, 0, 0, ${alpha})` 33 | }}; 34 | opacity: ${({ show }): number => (show ? 1 : 0)}; 35 | visibility: ${({ show }): string => (show ? 'visible' : 'hidden')}; 36 | pointer-events: ${({ show }): string => (show ? 'auto' : 'none')}; 37 | display: flex; 38 | justify-content: center; 39 | align-items: center; 40 | ` 41 | 42 | const SModalContainer = styled.div` 43 | padding: 10px; 44 | position: fixed; 45 | top: 50%; 46 | left: 50%; 47 | transform: translate(-50%, -50%); 48 | align-items: center; 49 | justify-content: center; 50 | width: min(500px, 100%); 51 | ` 52 | 53 | interface ICloseButtonStyleProps { 54 | size: number 55 | color: string 56 | onClick?: any 57 | } 58 | 59 | const SCloseButton = styled.div` 60 | transition: ${transitions.short}; 61 | position: absolute; 62 | width: ${({ size }): string => `${size}px`}; 63 | height: ${({ size }): string => `${size}px`}; 64 | right: ${({ size }): string => `${size / 1.6667}px`}; 65 | top: ${({ size }): string => `${size / 1.6667}px`}; 66 | opacity: 0.5; 67 | cursor: pointer; 68 | &:hover { 69 | opacity: 1; 70 | } 71 | &:before, 72 | &:after { 73 | position: absolute; 74 | content: ' '; 75 | height: ${({ size }): string => `${size}px`}; 76 | width: 2px; 77 | background: ${({ color }): string => `${color}`}; 78 | } 79 | &:before { 80 | transform: rotate(45deg); 81 | } 82 | &:after { 83 | transform: rotate(-45deg); 84 | } 85 | ` 86 | 87 | const SCard = styled.div` 88 | width: min(500px, 100%); 89 | padding: 25px; 90 | background-color: white; 91 | border-radius: 6px; 92 | display: flex; 93 | flex-direction: column; 94 | justify-content: center; 95 | align-items: center; 96 | ` 97 | 98 | const SModalContent = styled.div` 99 | position: relative; 100 | width: 100%; 101 | position: relative; 102 | word-wrap: break-word; 103 | ` 104 | 105 | interface IModalState { 106 | offset: number 107 | } 108 | 109 | interface IModalProps { 110 | children: React.ReactNode 111 | show: boolean 112 | toggleModal: any 113 | opacity?: number 114 | } 115 | const INITIAL_STATE = { 116 | offset: 0, 117 | } 118 | 119 | class Modal extends React.Component { 120 | public static propTypes = { 121 | children: PropTypes.node.isRequired, 122 | show: PropTypes.bool.isRequired, 123 | toggleModal: PropTypes.func.isRequired, 124 | opacity: PropTypes.number, 125 | } 126 | 127 | public lightbox?: HTMLDivElement | null 128 | 129 | public state: IModalState = { 130 | ...INITIAL_STATE, 131 | } 132 | 133 | public componentDidUpdate(): void { 134 | if (this.lightbox) { 135 | const lightboxRect = this.lightbox.getBoundingClientRect() 136 | const offset = lightboxRect.top > 0 ? lightboxRect.top : 0 137 | 138 | if (offset !== INITIAL_STATE.offset && offset !== this.state.offset) { 139 | this.setState({ offset }) 140 | } 141 | } 142 | } 143 | 144 | public toggleModal = async (): Promise => { 145 | const d = typeof window !== 'undefined' ? document : '' 146 | const body = d ? d.body || d.getElementsByTagName('body')[0] : '' 147 | if (body) { 148 | if (this.props.show) { 149 | body.style.position = '' 150 | } else { 151 | body.style.position = 'fixed' 152 | } 153 | } 154 | this.props.toggleModal() 155 | } 156 | 157 | render(): React.ReactElement { 158 | const { offset } = this.state 159 | const { children, show, opacity } = this.props 160 | return ( 161 | (this.lightbox = c)} 166 | > 167 | 168 | 169 | 170 | {children} 171 | 172 | 173 | 174 | ) 175 | } 176 | } 177 | 178 | export default Modal 179 | -------------------------------------------------------------------------------- /src/views/Kaikas/TxTypes3/SmartContractDeploy.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | import Caver, { TransactionReceipt } from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { ResultFormType } from 'types' 6 | import { 7 | Label, 8 | FormInput, 9 | Button, 10 | CardSection, 11 | ResultForm, 12 | CodeBlock, 13 | View, 14 | CardExample, 15 | } from 'components' 16 | 17 | import exBytecode from '../Common/exBytecode' 18 | 19 | const caver = new Caver(window.klaytn) 20 | 21 | type WalletInfoType = { 22 | walletProps: { 23 | walletAddress: string 24 | } 25 | } 26 | 27 | const SmartContractDeploy = ({ walletProps }: WalletInfoType): ReactElement => { 28 | const { walletAddress } = walletProps 29 | 30 | const [bytecode, setBytecode] = useState('') 31 | const [value, setValue] = useState('') 32 | const [gas, setGas] = useState('3000000') 33 | 34 | const [txHash, setTxHash] = useState('') 35 | const [receipt, setReceipt] = useState>() 36 | const [error, setError] = useState() 37 | 38 | const signAndSendTransaction = (): void => { 39 | try { 40 | caver.klay 41 | .sendTransaction({ 42 | type: 'SMART_CONTRACT_DEPLOY', 43 | from: walletAddress, 44 | data: bytecode, 45 | value: caver.utils.toPeb(value, 'KLAY'), 46 | gas: gas, 47 | }) 48 | .once('transactionHash', (transactionHash) => { 49 | setError(undefined) 50 | setReceipt(undefined) 51 | setTxHash(transactionHash) 52 | }) 53 | .once('receipt', (receipt) => { 54 | setReceipt({ success: true, value: receipt }) 55 | }) 56 | .once('error', (err) => { 57 | setError({ 58 | success: false, 59 | message: err.message, 60 | }) 61 | }) 62 | } catch (err) { 63 | setError({ 64 | success: false, 65 | message: _.toString(err), 66 | }) 67 | } 68 | } 69 | 70 | return ( 71 | <> 72 | 73 |

Smart Contract Deploy

74 | 75 | 76 | 77 | {}} 81 | value={walletAddress} 82 | /> 83 | 87 | { 90 | setBytecode(exBytecode) 91 | setValue('0') 92 | }} 93 | /> 94 | 95 | 101 | 102 | 108 | 109 | 115 | 116 | 119 | { 132 | setTxHash(transactionHash) 133 | }) 134 | .once('receipt', (receipt) => { 135 | setReceipt({ success: true, value: receipt }) 136 | }) 137 | .once('error', (err) => { 138 | setError({ success: false, message: err.message }) 139 | })`} 140 | /> 141 | 142 |
143 | {!error && txHash && ( 144 | 145 |

Transaction Result

146 | {txHash && ( 147 | 151 | )} 152 | {receipt && } 153 |
154 | )} 155 | {error && } 156 | 157 | ) 158 | } 159 | 160 | export default SmartContractDeploy 161 | -------------------------------------------------------------------------------- /src/views/Kaikas/TxTypes3/SmartContractDeployFeeDelegation.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | import Caver from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { ResultFormType } from 'types' 6 | import { 7 | Label, 8 | FormInput, 9 | Button, 10 | CardSection, 11 | ResultForm, 12 | CodeBlock, 13 | View, 14 | CardExample, 15 | } from 'components' 16 | 17 | import exBytecode from '../Common/exBytecode' 18 | import FeeDelegation from '../Common/FeeDelegation' 19 | 20 | const caver = new Caver(window.klaytn) 21 | 22 | type WalletInfoType = { 23 | walletProps: { 24 | walletAddress: string 25 | } 26 | } 27 | 28 | const SmartContractDeployFeeDelegation = ({ 29 | walletProps, 30 | }: WalletInfoType): ReactElement => { 31 | const { walletAddress } = walletProps 32 | const [senderAddress, setSenderAddress] = useState('') 33 | 34 | const [bytecode, setBytecode] = useState('') 35 | const [value, setValue] = useState('') 36 | const [gas, setGas] = useState('3000000') 37 | 38 | const [signError, setSignError] = useState() 39 | const [signedTx, setSignedTx] = useState('') 40 | 41 | const signTransaction = (): void => { 42 | try { 43 | const tx = { 44 | type: 'FEE_DELEGATED_SMART_CONTRACT_DEPLOY', 45 | from: walletAddress, 46 | data: bytecode, 47 | value: caver.utils.toPeb(value, 'KLAY'), 48 | gas: gas, 49 | } 50 | 51 | caver.klay.signTransaction(tx, (err, res) => { 52 | if (err) { 53 | setSignError({ 54 | success: false, 55 | message: _.toString(err.message), 56 | }) 57 | } else { 58 | const { rawTransaction: senderRawTransaction } = JSON.parse( 59 | JSON.stringify(res) 60 | ) 61 | setSignError(undefined) 62 | setSenderAddress(walletAddress) 63 | setSignedTx(senderRawTransaction) 64 | } 65 | }) 66 | } catch (err) { 67 | setSignError({ 68 | success: false, 69 | message: _.toString(err), 70 | }) 71 | } 72 | } 73 | 74 | return ( 75 | <> 76 | 77 |

Smart Contract Deploy (Fee Delegation)

78 | 79 | 80 | 81 | {}} 85 | value={senderAddress || walletAddress} 86 | /> 87 | 91 | { 94 | setBytecode(exBytecode) 95 | setValue('0') 96 | }} 97 | /> 98 | 99 | 105 | 106 | 112 | 113 | 119 | 120 | 121 | { 134 | if (err) { 135 | setSignError({ success: false, message: _.toString(err.message) }) 136 | } else { 137 | const { rawTransaction: senderRawTransaction } = JSON.parse(JSON.stringify(res)) 138 | setSignedTx(senderRawTransaction) 139 | } 140 | })`} 141 | /> 142 | 143 |
144 | {signError && } 145 | {signedTx && ( 146 | 147 | 151 | 152 | )} 153 | {signedTx && ( 154 | 155 | )} 156 | 157 | ) 158 | } 159 | 160 | export default SmartContractDeployFeeDelegation 161 | -------------------------------------------------------------------------------- /src/consts/util.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from 'bignumber.js' 2 | import _ from 'lodash' 3 | 4 | import { Token, pToken, uToken } from 'types' 5 | 6 | const P_DECIMAL = 1e18 7 | const U_DECIMAL = 1e6 8 | 9 | const truncate = (text: string, [h, t]: number[] = [10, 10]): string => { 10 | const head = text.slice(0, h) 11 | const tail = text.slice(-1 * t, text.length) 12 | return text.length > h + t ? [head, tail].join('...') : text 13 | } 14 | 15 | const jsonTryParse = (value: string): T | undefined => { 16 | try { 17 | return JSON.parse(value) as T 18 | } catch { 19 | return undefined 20 | } 21 | } 22 | 23 | const setComma = (str: string | number): string => { 24 | const parts = _.toString(str).split('.') 25 | parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',') 26 | return parts.join('.') 27 | } 28 | 29 | const delComma = (str: string): string => { 30 | return _.toString(str).replace(/,/g, '') 31 | } 32 | 33 | const extractNumber = (str: string): string => str.replace(/\D+/g, '') 34 | 35 | const isNumberString = (value: number | string): boolean => 36 | false === new BigNumber(value || '').isNaN() 37 | 38 | const toBn = (value?: number | string): BigNumber => new BigNumber(value || 0) 39 | 40 | const isEven = (value: number): boolean => value % 2 === 0 41 | 42 | const isOdd = (value: number): boolean => !isEven(value) 43 | 44 | const microfyU2P = (value: uToken): pToken => 45 | toBn(value).multipliedBy(P_DECIMAL).div(U_DECIMAL).toString(10) as pToken 46 | 47 | const microfyU = (value: Token): uToken => 48 | toBn(value).multipliedBy(U_DECIMAL).toString(10) as uToken 49 | 50 | const microfyP = (value: Token): pToken => 51 | toBn(value).multipliedBy(P_DECIMAL).toString(10) as pToken 52 | 53 | const demicrofyU = (value: uToken): Token => 54 | toBn(value).div(U_DECIMAL).toString(10) as Token 55 | 56 | const demicrofyP = (value: pToken): Token => 57 | toBn(value).div(P_DECIMAL).toString(10) as Token 58 | 59 | const formatAmountU = ( 60 | value: uToken, 61 | option?: { 62 | abbreviate?: boolean 63 | toFix?: number 64 | } 65 | ): string => formatAmountP(microfyP(demicrofyU(value)), option) 66 | 67 | const formatAmountP = ( 68 | value: pToken, 69 | option?: { 70 | abbreviate?: boolean 71 | toFix?: number 72 | } 73 | ): string => { 74 | const demicrofyValue = toBn(demicrofyP(value)) 75 | let strValue = '0' 76 | if (option?.toFix !== undefined) { 77 | strValue = demicrofyValue.toFixed(option?.toFix, BigNumber.ROUND_DOWN) 78 | } else { 79 | strValue = demicrofyValue.toString(10) 80 | } 81 | 82 | if (option?.abbreviate) { 83 | const abbreviated = abbreviateNumber(strValue, { toFix: option?.toFix }) 84 | return `${setComma(abbreviated.value)}${abbreviated.unit}` 85 | } 86 | return setComma(strValue) 87 | } 88 | 89 | const abbreviateNumber = ( 90 | value: string, 91 | option?: { 92 | toFix?: number 93 | } 94 | ): { value: string; unit: string } => { 95 | const toFix = option?.toFix || 2 96 | const bn = toBn(value) 97 | if (bn.gte(1e12)) { 98 | return { 99 | value: bn.div(1e12).toFixed(toFix, BigNumber.ROUND_DOWN), 100 | unit: 'T', 101 | } 102 | } else if (bn.gte(1e9)) { 103 | return { 104 | value: bn.div(1e9).toFixed(toFix, BigNumber.ROUND_DOWN), 105 | unit: 'B', 106 | } 107 | } else if (bn.gte(1e6)) { 108 | return { 109 | value: bn.div(1e6).toFixed(toFix, BigNumber.ROUND_DOWN), 110 | unit: 'M', 111 | } 112 | } 113 | return { value, unit: '' } 114 | } 115 | 116 | const getPriceChange = ({ 117 | from, 118 | to, 119 | }: { 120 | from: BigNumber 121 | to: BigNumber 122 | }): { 123 | isIncreased: boolean 124 | rate: BigNumber 125 | } => { 126 | const isIncreased = to.isGreaterThanOrEqualTo(from) 127 | const rate = isIncreased 128 | ? to.div(from).minus(1) 129 | : new BigNumber(1).minus(to.div(from)) 130 | return { 131 | isIncreased, 132 | rate, 133 | } 134 | } 135 | 136 | const toBase64 = (value: string): string => 137 | Buffer.from(value).toString('base64') 138 | 139 | const fromBase64 = (value: string): string => 140 | Buffer.from(value, 'base64').toString() 141 | 142 | const formatPercentage = (per: BigNumber): string => { 143 | return per.lt(0.01) 144 | ? '<0.01' 145 | : per.gt(99.9) 146 | ? '>99.9' 147 | : per.multipliedBy(100).toFixed(2) 148 | } 149 | 150 | const isURL = (value: string): boolean => { 151 | try { 152 | new URL(value) 153 | return true 154 | } catch { 155 | return false 156 | } 157 | } 158 | 159 | export default { 160 | truncate, 161 | jsonTryParse, 162 | setComma, 163 | delComma, 164 | extractNumber, 165 | isNumberString, 166 | toBn, 167 | isEven, 168 | isOdd, 169 | microfyU2P, 170 | microfyU, 171 | microfyP, 172 | demicrofyU, 173 | demicrofyP, 174 | formatAmountU, 175 | formatAmountP, 176 | abbreviateNumber, 177 | getPriceChange, 178 | toBase64, 179 | fromBase64, 180 | formatPercentage, 181 | isURL, 182 | } 183 | -------------------------------------------------------------------------------- /src/views/Kaikas/TxTypes2/ValueTransferWithMemoFeeDelegationWithRatio.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | import Caver from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { ResultFormType } from 'types' 6 | import { 7 | Label, 8 | FormInput, 9 | Button, 10 | CardSection, 11 | ResultForm, 12 | CodeBlock, 13 | View, 14 | } from 'components' 15 | 16 | import FeeDelegation from '../Common/FeeDelegation' 17 | 18 | const caver = new Caver(window.klaytn) 19 | 20 | type WalletInfoType = { 21 | walletProps: { 22 | walletAddress: string 23 | } 24 | } 25 | 26 | const ValueTransferWithMemoFeeDelegationWithRatio = ({ 27 | walletProps, 28 | }: WalletInfoType): ReactElement => { 29 | const { walletAddress } = walletProps 30 | const [senderAddress, setSenderAddress] = useState('') 31 | 32 | const [toAddress, setToAddress] = useState('') 33 | const [value, setValue] = useState('') 34 | const [gas, setGas] = useState('3000000') 35 | const [ratio, setRatio] = useState('') 36 | const [memo, setMemo] = useState('') 37 | 38 | const [signError, setSignError] = useState() 39 | const [signedTx, setSignedTx] = useState('') 40 | 41 | const signTransaction = (): void => { 42 | try { 43 | const txData = { 44 | type: 'FEE_DELEGATED_VALUE_TRANSFER_MEMO_WITH_RATIO', 45 | from: walletAddress, 46 | to: toAddress, 47 | value: caver.utils.toPeb(value, 'KLAY'), 48 | feeRatio: ratio, 49 | gas: gas, 50 | data: memo, 51 | } 52 | 53 | caver.klay.signTransaction(txData, (err, res) => { 54 | if (err) { 55 | setSignError({ 56 | success: false, 57 | message: _.toString(err.message), 58 | }) 59 | } else { 60 | const { rawTransaction: senderRawTransaction } = JSON.parse( 61 | JSON.stringify(res) 62 | ) 63 | setSignError(undefined) 64 | setSenderAddress(walletAddress) 65 | setSignedTx(senderRawTransaction) 66 | } 67 | }) 68 | } catch (err) { 69 | setSignError({ 70 | success: false, 71 | message: _.toString(err), 72 | }) 73 | } 74 | } 75 | 76 | return ( 77 | <> 78 | 79 |

Value Transfer with Memo (Fee Delegation with Ratio)

80 | 81 | 82 | 83 | {}} 87 | value={senderAddress || walletAddress} 88 | /> 89 | 90 | 96 | 97 | 103 | 104 | 110 | 111 | 117 | 118 | 124 | 125 | 126 | { 141 | if (err) { 142 | setSignError({ success: false, message: _.toString(err.message) }) 143 | } else { 144 | const { rawTransaction: senderRawTransaction } = JSON.parse(JSON.stringify(res)) 145 | setSignError(undefined) 146 | setSenderAddress(walletAddress) 147 | setSignedTx(senderRawTransaction) 148 | } 149 | })`} 150 | /> 151 | 152 |
153 | {signError && } 154 | {signedTx && ( 155 | 156 | 160 | 161 | )} 162 | {signedTx && ( 163 | 164 | )} 165 | 166 | ) 167 | } 168 | 169 | export default ValueTransferWithMemoFeeDelegationWithRatio 170 | -------------------------------------------------------------------------------- /src/views/Kaikas/TxTypes3/SmartContractDeployFeeDelegationWithRatio.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | import Caver from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { ResultFormType } from 'types' 6 | import { 7 | Label, 8 | FormInput, 9 | Button, 10 | CardSection, 11 | ResultForm, 12 | CodeBlock, 13 | View, 14 | CardExample, 15 | } from 'components' 16 | 17 | import exBytecode from '../Common/exBytecode' 18 | import FeeDelegation from '../Common/FeeDelegation' 19 | 20 | const caver = new Caver(window.klaytn) 21 | 22 | type WalletInfoType = { 23 | walletProps: { 24 | walletAddress: string 25 | } 26 | } 27 | 28 | const SmartContractDeployFeeDelegationWithRatio = ({ 29 | walletProps, 30 | }: WalletInfoType): ReactElement => { 31 | const { walletAddress } = walletProps 32 | const [senderAddress, setSenderAddress] = useState('') 33 | 34 | const [bytecode, setBytecode] = useState('') 35 | const [value, setValue] = useState('') 36 | const [ratio, setRatio] = useState('') 37 | const [gas, setGas] = useState('3000000') 38 | 39 | const [signError, setSignError] = useState() 40 | const [signedTx, setSignedTx] = useState('') 41 | 42 | const signTransaction = (): void => { 43 | try { 44 | const tx = { 45 | type: 'FEE_DELEGATED_SMART_CONTRACT_DEPLOY_WITH_RATIO', 46 | from: walletAddress, 47 | data: bytecode, 48 | value: caver.utils.toPeb(value, 'KLAY'), 49 | feeRatio: ratio, 50 | gas: gas, 51 | } 52 | 53 | caver.klay.signTransaction(tx, (err, res) => { 54 | if (err) { 55 | setSignError({ 56 | success: false, 57 | message: _.toString(err.message), 58 | }) 59 | } else { 60 | const { rawTransaction: senderRawTransaction } = JSON.parse( 61 | JSON.stringify(res) 62 | ) 63 | setSignError(undefined) 64 | setSenderAddress(walletAddress) 65 | setSignedTx(senderRawTransaction) 66 | } 67 | }) 68 | } catch (err) { 69 | setSignError({ 70 | success: false, 71 | message: _.toString(err), 72 | }) 73 | } 74 | } 75 | 76 | return ( 77 | <> 78 | 79 |

Smart Contract Deploy (Fee Delegation with Ratio)

80 | 81 | 82 | 83 | {}} 87 | value={senderAddress || walletAddress} 88 | /> 89 | 93 | { 96 | setBytecode(exBytecode) 97 | setValue('0') 98 | }} 99 | /> 100 | 101 | 107 | 108 | 114 | 115 | 121 | 122 | 128 | 129 | 130 | { 143 | if (err) { 144 | setSignError({ success: false, message: _.toString(err.message) }) 145 | } else { 146 | const { rawTransaction: senderRawTransaction } = JSON.parse(JSON.stringify(res)) 147 | setSignedTx(senderRawTransaction) 148 | } 149 | })`} 150 | /> 151 | 152 |
153 | {signError && } 154 | {signedTx && ( 155 | 156 | 160 | 161 | )} 162 | {signedTx && ( 163 | 164 | )} 165 | 166 | ) 167 | } 168 | 169 | export default SmartContractDeployFeeDelegationWithRatio 170 | -------------------------------------------------------------------------------- /src/views/Kaikas/TxTypes1/AccountUpdateFeeDelegation.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | import Caver from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { ResultFormType } from 'types' 6 | import { 7 | Label, 8 | FormInput, 9 | Button, 10 | CardSection, 11 | ResultForm, 12 | CodeBlock, 13 | View, 14 | } from 'components' 15 | 16 | import FeeDelegation from '../Common/FeeDelegation' 17 | 18 | const caver = new Caver(window.klaytn) 19 | 20 | type WalletInfoType = { 21 | walletProps: { 22 | walletAddress: string 23 | } 24 | } 25 | 26 | const AccountUpdateFeeDelegation = ({ 27 | walletProps, 28 | }: WalletInfoType): ReactElement => { 29 | const { walletAddress } = walletProps 30 | const [senderAddress, setSenderAddress] = useState('') 31 | 32 | const [gas, setGas] = useState('3000000') 33 | 34 | const [signError, setSignError] = useState() 35 | const [signedTx, setSignedTx] = useState('') 36 | 37 | const [walletKey, setWalletKey] = useState('') 38 | const [publicKey, setPublicKey] = useState('') 39 | 40 | const signTransaction = (): void => { 41 | try { 42 | const tx = { 43 | type: 'FEE_DELEGATED_ACCOUNT_UPDATE', 44 | from: walletAddress, 45 | gas: gas, 46 | publicKey: publicKey, 47 | } 48 | 49 | caver.klay.signTransaction(tx, (err, res) => { 50 | if (err) { 51 | setSignError({ 52 | success: false, 53 | message: _.toString(err.message), 54 | }) 55 | } else { 56 | const { rawTransaction: senderRawTransaction } = JSON.parse( 57 | JSON.stringify(res) 58 | ) 59 | setSignError(undefined) 60 | setSenderAddress(walletAddress) 61 | setSignedTx(senderRawTransaction) 62 | } 63 | }) 64 | } catch (err) { 65 | setSignError({ 66 | success: false, 67 | message: _.toString(err), 68 | }) 69 | } 70 | } 71 | 72 | const generateKeyPair = (): void => { 73 | const { privateKey } = caver.klay.accounts.create() 74 | const newPublicKey = caver.klay.accounts.privateKeyToPublicKey(privateKey) 75 | const newWalletKey = `${privateKey}0x00${walletAddress}` 76 | setPublicKey(newPublicKey) 77 | setWalletKey(newWalletKey) 78 | } 79 | 80 | return ( 81 | <> 82 | 83 |

Generate New Keypair

84 | 85 | 86 | 87 | 93 | 94 | 97 | 107 | 108 |
109 | 110 |

Account Update (Fee Delegation)

111 | 112 | 113 | 114 | {}} 118 | value={senderAddress || walletAddress} 119 | /> 120 | 121 | 127 | 128 | 134 | 135 | 136 | { 148 | if (err) { 149 | setSignError({ success: false, message: _.toString(err.message) }) 150 | } else { 151 | const { rawTransaction: senderRawTransaction } = JSON.parse(JSON.stringify(res)) 152 | setSignedTx(senderRawTransaction) 153 | } 154 | })`} 155 | /> 156 | 157 |
158 | {signError && } 159 | {signedTx && ( 160 | 161 | 165 | 166 | )} 167 | {signedTx && ( 168 | 169 | )} 170 | 171 | ) 172 | } 173 | 174 | export default AccountUpdateFeeDelegation 175 | -------------------------------------------------------------------------------- /src/components/FormGetKey.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useEffect, useState } from 'react' 2 | import _ from 'lodash' 3 | import styled from 'styled-components' 4 | import { Keystore, Keyring } from 'caver-js' 5 | 6 | import { COLOR, UTIL } from 'consts' 7 | 8 | import { 9 | Text, 10 | Button, 11 | View, 12 | FormInput, 13 | CodeBlock, 14 | Row, 15 | FormFile, 16 | FormSelect, 17 | } from 'components' 18 | 19 | import { 20 | createFromPrivateKey, 21 | generateSingleKey, 22 | keyringDecrypt, 23 | } from 'logics/caverFuncntions' 24 | 25 | export type GetKeyFrom = 'privateKey' | 'keystore' 26 | 27 | const StyledContainer = styled(View)` 28 | display: grid; 29 | grid-template-columns: 140px 1fr; 30 | gap: 8px; 31 | ` 32 | 33 | const FormGetKey = ({ 34 | keyring, 35 | setKeyring, 36 | }: { 37 | keyring?: Keyring 38 | setKeyring: (value?: Keyring) => void 39 | }): ReactElement => { 40 | const [getKeyFrom, setGetKeyFrom] = useState('privateKey') 41 | const [privateKey, setPrivateKey] = useState('') 42 | const [keyringErrMsg, setKeyringErrMsg] = useState('') 43 | const [keystoreJSON, setKeystoreJSON] = useState() 44 | const [keystorePassword, setKeystorePassword] = useState('') 45 | 46 | const handleKeystoreChange = (files?: FileList): void => { 47 | if (files && files.length > 0) { 48 | const fileReader = new FileReader() 49 | fileReader.readAsText(files[0], 'UTF-8') 50 | fileReader.onload = (event): void => { 51 | if (typeof event.target?.result === 'string') { 52 | const json = UTIL.jsonTryParse(event.target.result) 53 | setKeystoreJSON(json) 54 | } 55 | } 56 | } 57 | } 58 | 59 | const onClickDecryptKeystore = (): void => { 60 | try { 61 | keystoreJSON && setKeyring(keyringDecrypt(keystoreJSON, keystorePassword)) 62 | } catch (error) { 63 | setKeyringErrMsg(_.toString(error)) 64 | } 65 | } 66 | 67 | useEffect(() => { 68 | if (getKeyFrom === 'privateKey' && privateKey) { 69 | try { 70 | setKeyring(createFromPrivateKey(privateKey)) 71 | } catch (error) { 72 | setKeyringErrMsg(_.toString(error)) 73 | } 74 | } else if (getKeyFrom === 'keystore' && keystoreJSON) { 75 | try { 76 | setKeyring(keyringDecrypt(keystoreJSON, '')) 77 | } catch { 78 | // just try decrypt 79 | } 80 | } 81 | 82 | return (): void => { 83 | setKeyring(undefined) 84 | setKeyringErrMsg('') 85 | } 86 | }, [privateKey, keystoreJSON, getKeyFrom]) 87 | 88 | useEffect(() => { 89 | setKeystorePassword('') 90 | }, [keystoreJSON]) 91 | 92 | return ( 93 | 94 | 103 | {getKeyFrom === 'privateKey' ? ( 104 | 105 | 106 | 107 | 112 | {keyringErrMsg && ( 113 | {keyringErrMsg} 114 | )} 115 | 116 | 122 | 123 | 124 | 129 | 130 | ) : ( 131 | 132 | 133 | 138 | 139 | {keystoreJSON && !keyring && ( 140 | 141 | 142 | 143 | 149 | 150 | 156 | 157 | {keystorePassword && keyringErrMsg && ( 158 | {keyringErrMsg} 159 | )} 160 | 161 | )} 162 | 166 | 167 | )} 168 | 169 | ) 170 | } 171 | 172 | export default FormGetKey 173 | -------------------------------------------------------------------------------- /src/views/KCT/DetectKCT.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useMemo, useState } from 'react' 2 | import Caver from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { URLMAP } from 'consts' 6 | import { ResultFormType } from 'types' 7 | import { 8 | Card, 9 | CardHeader, 10 | CardBody, 11 | Label, 12 | Container, 13 | Text, 14 | FormSelect, 15 | FormInput, 16 | Button, 17 | CardSection, 18 | ResultForm, 19 | LinkA, 20 | CodeBlock, 21 | View, 22 | } from 'components' 23 | 24 | type NetworkType = 'mainnet' | 'testnet' 25 | 26 | const DetectKCT = (): ReactElement => { 27 | const [contractAddress, setContractAddress] = useState('') 28 | const [result, setResult] = useState() 29 | const [network, setNetwork] = useState('mainnet') 30 | const [description, setDescription] = useState() 31 | 32 | const caver = useMemo( 33 | () => new Caver(URLMAP.network[network]['rpc']), 34 | [network] 35 | ) 36 | 37 | enum KCTEnum { 38 | KIP7 = 'KIP7', 39 | KIP17 = 'KIP17', 40 | KIP37 = 'KIP37', 41 | } 42 | 43 | const detectKCT = async (): Promise => { 44 | try { 45 | const kip7 = await caver.kct.kip7.detectInterface(contractAddress) 46 | if (kip7.IKIP7) { 47 | setDescription(KCTEnum.KIP7) 48 | setResult({ 49 | success: true, 50 | value: 'KIP-7', 51 | }) 52 | return 53 | } 54 | 55 | const kip17 = await caver.kct.kip17.detectInterface(contractAddress) 56 | if (kip17.IKIP17) { 57 | setDescription(KCTEnum.KIP17) 58 | setResult({ 59 | success: true, 60 | value: 'KIP-17', 61 | }) 62 | return 63 | } 64 | 65 | const kip37 = await caver.kct.kip37.detectInterface(contractAddress) 66 | if (kip37.IKIP37) { 67 | setDescription(KCTEnum.KIP37) 68 | setResult({ 69 | success: true, 70 | value: 'KIP-37', 71 | }) 72 | return 73 | } 74 | } catch (err) { 75 | setResult({ 76 | success: false, 77 | message: _.toString(err), 78 | }) 79 | } 80 | } 81 | 82 | return ( 83 | 84 | 85 | 86 |

Detect Klaytn Compatible Token (KCT)

87 | 88 | 89 | Klaytn Compatible Token (KCT) 90 | {' '} 91 | is a special type of a smart contract that implements certain 92 | technical specifications. Everyone who wants to issue tokens on top 93 | of Klaytn must follow the specification. KCT currently consists of 94 | KIP-7, KIP-17, and KIP-37. You can check which KCT the smart 95 | contract implements using its address. 96 | 97 |
98 | 99 | 100 | 101 | 110 | 111 | 112 | 113 | 114 | 115 | 121 | 122 | 123 | 134 | 135 | 136 | 137 | {result?.success && description === KCTEnum.KIP7 && ( 138 | 139 | 140 | 141 | KIP 7: Fungible Token Standard 142 | 143 | 144 | 145 | )} 146 | {result?.success && description === KCTEnum.KIP17 && ( 147 | 148 | 149 | 150 | KIP 17: Non-fungible Token Standard 151 | 152 | 153 | 154 | )} 155 | {result?.success && description === KCTEnum.KIP37 && ( 156 | 157 | 158 | 159 | KIP 37: Token Standard 160 | 161 | 162 | 163 | )} 164 | 165 |
166 |
167 | ) 168 | } 169 | 170 | export default DetectKCT 171 | -------------------------------------------------------------------------------- /src/views/Kaikas/TxTypes1/AccountUpdate.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useState } from 'react' 2 | import Caver, { TransactionReceipt } from 'caver-js' 3 | import _ from 'lodash' 4 | 5 | import { ResultFormType } from 'types' 6 | import { 7 | Label, 8 | FormInput, 9 | Button, 10 | CardSection, 11 | ResultForm, 12 | CodeBlock, 13 | View, 14 | } from 'components' 15 | 16 | const caver = new Caver(window.klaytn) 17 | 18 | type WalletInfoType = { 19 | walletProps: { 20 | walletAddress: string 21 | } 22 | } 23 | 24 | const AccountUpdate = ({ walletProps }: WalletInfoType): ReactElement => { 25 | const { walletAddress } = walletProps 26 | 27 | const [gas, setGas] = useState('3000000') 28 | 29 | const [walletKey, setWalletKey] = useState('') 30 | const [publicKey, setPublicKey] = useState('') 31 | 32 | const [txHash, setTxHash] = useState('') 33 | const [receipt, setReceipt] = useState>() 34 | const [error, setError] = useState() 35 | 36 | const signAndSendTransaction = (): void => { 37 | try { 38 | const tx = { 39 | type: 'ACCOUNT_UPDATE', 40 | from: walletAddress, 41 | gas: gas, 42 | publicKey: publicKey, 43 | } 44 | 45 | caver.klay 46 | .sendTransaction(tx) 47 | .once('transactionHash', (transactionHash) => { 48 | setError(undefined) 49 | setReceipt(undefined) 50 | setTxHash(transactionHash) 51 | }) 52 | .once('receipt', (receipt) => { 53 | setReceipt({ success: true, value: receipt }) 54 | }) 55 | .once('error', (err) => { 56 | setError({ 57 | success: false, 58 | message: err.message, 59 | }) 60 | }) 61 | } catch (err) { 62 | setError({ 63 | success: false, 64 | message: _.toString(err), 65 | }) 66 | } 67 | } 68 | 69 | const generateKeyPair = (): void => { 70 | const { privateKey } = caver.klay.accounts.create() 71 | const newPublicKey = caver.klay.accounts.privateKeyToPublicKey(privateKey) 72 | const newWalletKey = `${privateKey}0x00${walletAddress}` 73 | setPublicKey(newPublicKey) 74 | setWalletKey(newWalletKey) 75 | } 76 | 77 | return ( 78 | <> 79 | 80 |

Generate New Keypair

81 | 82 | 83 | 84 | 90 | 91 | 94 | 104 | 105 |
106 | 107 |

Account Update

108 | 109 | 110 | 111 | {}} 115 | value={walletAddress} 116 | /> 117 | 118 | 124 | 125 | 131 | 132 | 135 | { 149 | setTxHash(transactionHash) 150 | }) 151 | .once('receipt', (receipt) => { 152 | setReceipt({ success: true, value: receipt }) 153 | }) 154 | .once('error', (err) => { 155 | setError({ success: false, message: err.message }) 156 | }) 157 | } catch (err) { 158 | setError({ success: false, message: _.toString(err) }) 159 | } 160 | `} 161 | /> 162 | 163 |
164 | {!error && txHash && ( 165 | 166 |

Transaction Result

167 | {txHash && ( 168 | 172 | )} 173 | {receipt && } 174 |
175 | )} 176 | {error && } 177 | 178 | ) 179 | } 180 | 181 | export default AccountUpdate 182 | -------------------------------------------------------------------------------- /src/views/Web3modal/helpers/web3.ts: -------------------------------------------------------------------------------- 1 | import { Contract } from 'web3-eth-contract' 2 | import { WEB3MODAL } from 'consts' 3 | import { apiGetGasPriceKlaytn, getChainData } from './utilities' 4 | 5 | export function getKIP7Contract(web3: any, contractAddress: string): Contract { 6 | const tokenContract = new web3.eth.Contract( 7 | WEB3MODAL.KIP7_CONTRACT.abi, 8 | contractAddress 9 | ) 10 | return tokenContract 11 | } 12 | 13 | export function getKIP17Contract(web3: any, contractAddress: string): Contract { 14 | const tokenContract = new web3.eth.Contract( 15 | WEB3MODAL.KIP17_CONTRACT.abi, 16 | contractAddress 17 | ) 18 | return tokenContract 19 | } 20 | 21 | export function callBalanceOf( 22 | address: string, 23 | chainId: number, 24 | contractAddress: string, 25 | web3: any 26 | ): Promise { 27 | return new Promise(async (resolve, reject) => { 28 | try { 29 | const contract = getKIP7Contract(web3, contractAddress) 30 | 31 | await contract.methods 32 | .balanceOf(address) 33 | .call( 34 | { from: '0x0000000000000000000000000000000000000000' }, 35 | (err: any, data: any) => { 36 | if (err) { 37 | reject(err) 38 | } 39 | resolve(data) 40 | } 41 | ) 42 | } catch (err) { 43 | reject(err) 44 | } 45 | }) 46 | } 47 | 48 | export function callTransfer( 49 | address: string, 50 | chainId: number, 51 | contractAddress: string, 52 | web3: any 53 | ): Promise { 54 | return new Promise(async (resolve, reject) => { 55 | try { 56 | const contract = getKIP7Contract(web3, contractAddress) 57 | const chain = getChainData(chainId).chain 58 | const gasPrice = 59 | chain === 'klaytn' ? await apiGetGasPriceKlaytn(chainId) : undefined 60 | const gas = 61 | chain === 'klaytn' 62 | ? await contract.methods 63 | .transfer(address, '1') 64 | .estimateGas({ from: address }) 65 | : undefined 66 | await contract.methods 67 | .transfer(address, '1') 68 | .send( 69 | { from: address, gas: gas, gasPrice: gasPrice }, 70 | (err: any, data: any) => { 71 | if (err) { 72 | reject(err) 73 | } 74 | resolve(data) 75 | } 76 | ) 77 | } catch (err) { 78 | reject(err) 79 | } 80 | }) 81 | } 82 | 83 | export function callTransferFrom( 84 | address: string, 85 | chainId: number, 86 | contractAddress: string, 87 | web3: any, 88 | toAddress: string, 89 | tokenId: number 90 | ): Promise { 91 | return new Promise(async (resolve, reject) => { 92 | try { 93 | const contract = getKIP17Contract(web3, contractAddress) 94 | const chain = getChainData(chainId).chain 95 | const gasPrice = 96 | chain === 'klaytn' ? await apiGetGasPriceKlaytn(chainId) : undefined 97 | const gas = 98 | chain === 'klaytn' 99 | ? await contract.methods 100 | .transferFrom(address, toAddress, tokenId) 101 | .estimateGas({ from: address }) 102 | : undefined 103 | await contract.methods 104 | .transferFrom(address, toAddress, tokenId) 105 | .send( 106 | { from: address, gas: gas, gasPrice: gasPrice }, 107 | (err: any, data: any) => { 108 | if (err) { 109 | reject(err) 110 | } 111 | resolve(data) 112 | } 113 | ) 114 | } catch (err) { 115 | reject(err) 116 | } 117 | }) 118 | } 119 | 120 | export function callDeployNFT( 121 | address: string, 122 | chainId: number, 123 | web3: any, 124 | name: string, 125 | symbol: string 126 | ): Promise { 127 | return new Promise(async (resolve, reject) => { 128 | try { 129 | const contract = new web3.eth.Contract(WEB3MODAL.KIP17_CONTRACT.abi) 130 | const chain = getChainData(chainId).chain 131 | const gasPrice = 132 | chain === 'klaytn' ? await apiGetGasPriceKlaytn(chainId) : undefined 133 | await contract 134 | .deploy({ 135 | data: WEB3MODAL.KIP17_CONTRACT.bytecode, 136 | arguments: [name, symbol], 137 | }) 138 | .send( 139 | { 140 | from: address, 141 | gas: 10000000, 142 | gasPrice: gasPrice, 143 | }, 144 | (err: any, data: any) => { 145 | if (err) { 146 | reject(err) 147 | } 148 | resolve(data) 149 | } 150 | ) 151 | } catch (err) { 152 | reject(err) 153 | } 154 | }) 155 | } 156 | 157 | export function callMintNFT( 158 | address: string, 159 | chainId: number, 160 | contractAddress: string, 161 | web3: any, 162 | toAddress: string, 163 | tokenId: number, 164 | tokenURI: string 165 | ): Promise { 166 | return new Promise(async (resolve, reject) => { 167 | try { 168 | const contract = getKIP17Contract(web3, contractAddress) 169 | const chain = getChainData(chainId).chain 170 | const gasPrice = 171 | chain === 'klaytn' ? await apiGetGasPriceKlaytn(chainId) : undefined 172 | await contract.methods 173 | .mintWithTokenURI(toAddress, tokenId, tokenURI) 174 | .send( 175 | { 176 | from: address, 177 | gas: 10000000, 178 | gasPrice: gasPrice, 179 | }, 180 | (err: any, data: any) => { 181 | if (err) { 182 | reject(err) 183 | } 184 | resolve(data) 185 | } 186 | ) 187 | } catch (err) { 188 | reject(err) 189 | } 190 | }) 191 | } 192 | --------------------------------------------------------------------------------