├── public ├── CNAME ├── favicon.ico ├── header.png ├── home_bg.png ├── stacks_icon.png ├── icons │ ├── icon-128x128.png │ ├── icon-192x192.png │ └── icon-512x512.png └── pro_icon.svg ├── src ├── assets │ ├── logo.png │ └── stacks_icon.png ├── locales │ ├── zh-CN │ │ ├── component.ts │ │ ├── pwa.ts │ │ ├── globalHeader.ts │ │ ├── settingDrawer.ts │ │ ├── menu.ts │ │ └── settings.ts │ ├── en-US │ │ ├── component.ts │ │ ├── pwa.ts │ │ ├── globalHeader.ts │ │ ├── settingDrawer.ts │ │ ├── menu.ts │ │ └── settings.ts │ ├── zh-CN.ts │ └── en-US.ts ├── components │ ├── PageLoading │ │ └── index.tsx │ ├── HeaderDropdown │ │ ├── index.less │ │ └── index.tsx │ ├── HeaderSearch │ │ ├── index.less │ │ └── index.tsx │ ├── Footer │ │ └── index.tsx │ ├── NoticeIcon │ │ ├── index.less │ │ ├── NoticeList.less │ │ ├── NoticeList.tsx │ │ ├── NoticeIcon.tsx │ │ └── index.tsx │ └── RightContent │ │ ├── index.tsx │ │ ├── index.less │ │ ├── AvatarDropdown.tsx │ │ └── SwitchNetwork.tsx ├── access.ts ├── utils │ ├── utils.less │ ├── utils.test.ts │ └── utils.ts ├── services │ ├── sysConf │ │ ├── data.d.ts │ │ └── conf.ts │ ├── locale.ts │ ├── wallet │ │ ├── data.d.ts │ │ ├── faucet.ts │ │ └── key.ts │ ├── publicdata │ │ ├── exportUtils.ts │ │ ├── tokenInfo.ts │ │ ├── data.d.ts │ │ └── chainInfo.ts │ ├── user.ts │ ├── API.d.ts │ ├── client │ │ ├── socket.ts │ │ ├── miningInfo.ts │ │ ├── data.d.ts │ │ └── Client.ts │ ├── constants.ts │ └── login.ts ├── pages │ ├── user │ │ └── login │ │ │ ├── locales │ │ │ ├── zh-CN.ts │ │ │ └── en-US.ts │ │ │ ├── components │ │ │ └── Login │ │ │ │ ├── LoginContext.tsx │ │ │ │ ├── LoginSubmit.tsx │ │ │ │ ├── index.less │ │ │ │ ├── LoginTab.tsx │ │ │ │ ├── map.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── LoginItem.tsx │ │ │ └── style.less │ ├── 404.tsx │ ├── wallet │ │ ├── locales │ │ │ ├── zh-CN.ts │ │ │ └── en-US.ts │ │ ├── index.tsx │ │ ├── models │ │ │ ├── addAccount.ts │ │ │ └── wallet.ts │ │ └── components │ │ │ ├── AddAccountForm.tsx │ │ │ └── walletTable.tsx │ ├── client │ │ ├── models │ │ │ ├── btcPriceInfo.ts │ │ │ ├── nodeList.ts │ │ │ ├── blockInfo.ts │ │ │ ├── miningInfo.ts │ │ │ ├── minerInfo.ts │ │ │ └── chainSyncInfo.ts │ │ ├── index.tsx │ │ ├── component │ │ │ ├── Step1AccountContent.tsx │ │ │ ├── BlockInfoTable.tsx │ │ │ ├── ChainSyncInfoTable.tsx │ │ │ ├── MiningInfoTable.tsx │ │ │ ├── MinerInfoTable.tsx │ │ │ ├── AuthCode.tsx │ │ │ ├── Step3NodeInfoContent.tsx │ │ │ └── Step2BurnFeeContent.tsx │ │ └── locales │ │ │ ├── zh-CN.ts │ │ │ └── en-US.ts │ └── publicData │ │ ├── locales │ │ ├── zh-CN.ts │ │ └── en-US.ts │ │ ├── index.tsx │ │ └── component │ │ ├── TokenPriceInfoTable.tsx │ │ ├── ChainInfoTable.tsx │ │ └── BlockInfoTable.tsx ├── manifest.json ├── models │ └── networkModel.ts ├── global.less ├── typings.d.ts ├── service-worker.js ├── global.tsx └── app.tsx ├── mock ├── route.ts ├── notices.ts └── user.ts ├── jsconfig.json ├── README.md ├── jest.config.js ├── config ├── defaultSettings.ts ├── proxy.ts └── config.ts ├── .gitignore ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ ├── bug_report.md │ └── testnet-bug.md ├── tsconfig.json ├── tests ├── PuppeteerEnvironment.js ├── getBrowser.js ├── beforeTest.js └── run-tests.js └── package.json /public/CNAME: -------------------------------------------------------------------------------- 1 | preview.pro.ant.design -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daemon-Technologies/Mining-Bot/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daemon-Technologies/Mining-Bot/HEAD/public/header.png -------------------------------------------------------------------------------- /public/home_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daemon-Technologies/Mining-Bot/HEAD/public/home_bg.png -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daemon-Technologies/Mining-Bot/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /public/stacks_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daemon-Technologies/Mining-Bot/HEAD/public/stacks_icon.png -------------------------------------------------------------------------------- /src/assets/stacks_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daemon-Technologies/Mining-Bot/HEAD/src/assets/stacks_icon.png -------------------------------------------------------------------------------- /public/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daemon-Technologies/Mining-Bot/HEAD/public/icons/icon-128x128.png -------------------------------------------------------------------------------- /public/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daemon-Technologies/Mining-Bot/HEAD/public/icons/icon-192x192.png -------------------------------------------------------------------------------- /public/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daemon-Technologies/Mining-Bot/HEAD/public/icons/icon-512x512.png -------------------------------------------------------------------------------- /mock/route.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | '/api/auth_routes': { 3 | '/form/advanced-form': { authority: ['admin', 'user'] }, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /src/locales/zh-CN/component.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 'component.tagSelect.expand': '展开', 3 | 'component.tagSelect.collapse': '收起', 4 | 'component.tagSelect.all': '全部', 5 | }; 6 | -------------------------------------------------------------------------------- /src/locales/en-US/component.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 'component.tagSelect.expand': 'Expand', 3 | 'component.tagSelect.collapse': 'Collapse', 4 | 'component.tagSelect.all': 'All', 5 | }; 6 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "emitDecoratorMetadata": true, 4 | "experimentalDecorators": true, 5 | "baseUrl": ".", 6 | "paths": { 7 | "@/*": ["./src/*"] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/components/PageLoading/index.tsx: -------------------------------------------------------------------------------- 1 | import { PageLoading } from '@ant-design/pro-layout'; 2 | 3 | // loading components from code split 4 | // https://umijs.org/plugin/umi-plugin-react.html#dynamicimport 5 | export default PageLoading; 6 | -------------------------------------------------------------------------------- /src/locales/zh-CN/pwa.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.pwa.offline': '当前处于离线状态', 3 | 'app.pwa.serviceworker.updated': '有新内容', 4 | 'app.pwa.serviceworker.updated.hint': '请点击“刷新”按钮或者手动刷新页面', 5 | 'app.pwa.serviceworker.updated.ok': '刷新', 6 | }; 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mining-Bot 2 | 3 | 4 | Please Visit [Mining-Bot Documentation](https://daemon-technologies.github.io/docs/) 5 | 6 | - [WSL Tutorial Video](https://www.youtube.com/watch?v=FXifFx0Akzc) 7 | - [MacOS](https://www.youtube.com/watch?v=TCtCTttsSeI) 8 | -------------------------------------------------------------------------------- /src/access.ts: -------------------------------------------------------------------------------- 1 | // src/access.ts 2 | import { getNetworkFromStorage } from '@/utils/utils' 3 | 4 | export default function access(initialState?: { currentUser?: API.UserInfo | undefined }) { 5 | let networkType = getNetworkFromStorage() 6 | return { 7 | 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testURL: 'http://localhost:8000', 3 | testEnvironment: './tests/PuppeteerEnvironment', 4 | verbose: false, 5 | globals: { 6 | ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false, 7 | localStorage: null, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /src/locales/en-US/pwa.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 'app.pwa.offline': 'You are offline now', 3 | 'app.pwa.serviceworker.updated': 'New content is available', 4 | 'app.pwa.serviceworker.updated.hint': 'Please press the "Refresh" button to reload current page', 5 | 'app.pwa.serviceworker.updated.ok': 'Refresh', 6 | }; 7 | -------------------------------------------------------------------------------- /src/utils/utils.less: -------------------------------------------------------------------------------- 1 | // mixins for clearfix 2 | // ------------------------ 3 | .clearfix() { 4 | zoom: 1; 5 | &::before, 6 | &::after {ZF 7 | display: table; 8 | content: ' '; 9 | } 10 | &::after { 11 | clear: both; 12 | height: 0; 13 | font-size: 0; 14 | visibility: hidden; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/components/HeaderDropdown/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/es/style/themes/default.less'; 2 | 3 | .container > * { 4 | background-color: @popover-bg; 5 | border-radius: 4px; 6 | box-shadow: @shadow-1-down; 7 | } 8 | 9 | @media screen and (max-width: @screen-xs) { 10 | .container { 11 | width: 100% !important; 12 | } 13 | .container > * { 14 | border-radius: 0 !important; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/services/sysConf/data.d.ts: -------------------------------------------------------------------------------- 1 | export interface SysConf { 2 | miningLocalServerUrl: string; 3 | miningLocalChainUrl: string; 4 | miningMonitorUrl: string; 5 | sidecarUrl: string; 6 | btcNodeInfo?: NodeInfo; 7 | } 8 | 9 | export interface NodeInfo { 10 | peerHost: string; 11 | username: string; 12 | password: string; 13 | rpcPort: number; 14 | peerPort: number; 15 | } -------------------------------------------------------------------------------- /src/pages/user/login/locales/zh-CN.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 'login.unlock': '账户解锁', 3 | 'login.setLockPwd': '设置锁定密码', 4 | 'button.unlock': '解锁', 5 | 'button.login': '登录', 6 | 7 | 'login.title': 'Stacks挖矿机器人', 8 | 'login.subTitle': 'Stacks挖矿机器人是一款非常有趣的工具!', 9 | 10 | 'message.unlock.leastLength': '密码长度至少为8位!', 11 | 'message.unlock.pwd': '请输入密码!', 12 | 'message.unlock.pwdAgain': '请再次输入密码!', 13 | }; -------------------------------------------------------------------------------- /src/pages/user/login/components/Login/LoginContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | export interface LoginContextProps { 4 | tabUtil?: { 5 | addTab: (id: string) => void; 6 | removeTab: (id: string) => void; 7 | }; 8 | updateActive?: (activeItem: { [key: string]: string } | string) => void; 9 | } 10 | 11 | const LoginContext: React.Context = createContext({}); 12 | 13 | export default LoginContext; 14 | -------------------------------------------------------------------------------- /src/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Result } from 'antd'; 2 | import React from 'react'; 3 | import { history } from 'umi'; 4 | 5 | const NoFoundPage: React.FC<{}> = () => ( 6 | history.push('/')}> 12 | Back Home 13 | 14 | } 15 | /> 16 | ); 17 | 18 | export default NoFoundPage; 19 | -------------------------------------------------------------------------------- /config/defaultSettings.ts: -------------------------------------------------------------------------------- 1 | import { Settings as LayoutSettings } from '@ant-design/pro-layout'; 2 | 3 | export default { 4 | navTheme: 'dark', 5 | // 拂晓蓝 6 | primaryColor: '#1890ff', 7 | layout: 'mix', 8 | contentWidth: 'Fluid', 9 | fixedHeader: false, 10 | fixSiderbar: true, 11 | colorWeak: false, 12 | menu: { 13 | locale: true, 14 | }, 15 | title: 'Mining Bot', 16 | pwa: false, 17 | iconfontUrl: '', 18 | } as LayoutSettings & { 19 | pwa: boolean; 20 | }; 21 | -------------------------------------------------------------------------------- /src/pages/wallet/locales/zh-CN.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 'account.title': '账户信息', 3 | 'account.address': '地址', 4 | 'account.type': '地址类型', 5 | 'account.balance': '余额', 6 | 'account.add': '添加账户', 7 | 'account.choose': '已选择', 8 | 9 | 'faucet.get' : '获取测试币', 10 | 'faucet.add' : '获取', 11 | 'faucet.notification': '充值提示', 12 | 'faucet.notification.content': '您是否要领取测试币到地址 : ', 13 | 14 | 'choose.num': '项', 15 | 16 | 'button.delete' : '批量删除' 17 | }; -------------------------------------------------------------------------------- /src/pages/client/models/btcPriceInfo.ts: -------------------------------------------------------------------------------- 1 | import { getBtcPrice } from "@/services/publicdata/tokenInfo"; 2 | import { useState } from "react"; 3 | 4 | export default () => { 5 | const [btcPrice, setBtcPrice] = useState(0); 6 | 7 | const getBtcUsdtPrice = async () => { 8 | const price = await getBtcPrice(); 9 | if (price) { 10 | setBtcPrice(price); 11 | } 12 | } 13 | 14 | return { 15 | btcPrice, 16 | getBtcUsdtPrice, 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Mining Bot", 3 | "short_name": "Mining Bot", 4 | "display": "standalone", 5 | "start_url": "./?utm_source=homescreen", 6 | "theme_color": "#002140", 7 | "background_color": "#001529", 8 | "icons": [ 9 | { 10 | "src": "icons/icon-192x192.png", 11 | "sizes": "192x192" 12 | }, 13 | { 14 | "src": "icons/icon-128x128.png", 15 | "sizes": "128x128" 16 | }, 17 | { 18 | "src": "icons/icon-512x512.png", 19 | "sizes": "512x512" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /src/components/HeaderSearch/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/es/style/themes/default.less'; 2 | 3 | .headerSearch { 4 | .input { 5 | width: 0; 6 | min-width: 0; 7 | overflow: hidden; 8 | background: transparent; 9 | border-radius: 0; 10 | transition: width 0.3s, margin-left 0.3s; 11 | :global(.ant-select-selection) { 12 | background: transparent; 13 | } 14 | input { 15 | box-shadow: none !important; 16 | } 17 | 18 | &.show { 19 | width: 210px; 20 | margin-left: 8px; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/pages/user/login/locales/en-US.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 'login.unlock': 'Unlock Your Account', 3 | 'login.setLockPwd': 'Set Your Lock Password', 4 | 'button.unlock': 'Unlock', 5 | 'button.login': 'Login', 6 | 7 | 'login.title': 'Stacks Mining Bot', 8 | 'login.subTitle': 'Stacks Mining Bot is an interesting tool!', 9 | 10 | 'message.unlock.leastLength': 'password should be at least 8 characters!', 11 | 'message.unlock.pwd': 'please input your password!', 12 | 'message.unlock.pwdAgain': 'please input your password again!', 13 | }; -------------------------------------------------------------------------------- /src/pages/wallet/locales/en-US.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 'account.title': 'Account Info', 3 | 'account.address': 'Address', 4 | 'account.type': 'Type', 5 | 'account.balance': 'Balance', 6 | 'account.add': 'Add', 7 | 'account.choose': 'Already choose', 8 | 9 | 'faucet.get' : 'Get Faucet', 10 | 'faucet.add' : 'Get Faucet', 11 | 'faucet.notification': 'Faucet Confirm', 12 | 'faucet.notification.content': 'If you want to get Faucet for address : ', 13 | 14 | 'choose.num': 'items', 15 | 16 | 'button.delete' : 'Delete' 17 | }; -------------------------------------------------------------------------------- /src/components/Footer/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { DefaultFooter } from '@ant-design/pro-layout'; 3 | import { getLanguage } from '@ant-design/pro-layout/lib/locales'; 4 | 5 | const { CN } = require('@/services/constants'); 6 | 7 | export default () => ( 8 | 19 | ); 20 | -------------------------------------------------------------------------------- /src/services/locale.ts: -------------------------------------------------------------------------------- 1 | import { getLocale } from "umi"; 2 | import enUS from 'antd/lib/locale/en_US'; 3 | import zhCN from 'antd/lib/locale/zh_CN'; 4 | import { Locale } from "antd/lib/locale-provider"; 5 | 6 | const { CN } = require('@/services/constants'); 7 | 8 | export function showMessage(cnMes: string, enMes: string): string { 9 | if (getLocale() === CN) { 10 | return cnMes; 11 | } 12 | return enMes; 13 | } 14 | 15 | export function switchConfigProviderLocale(): Locale { 16 | if (getLocale() === CN) { 17 | return zhCN; 18 | } 19 | return enUS; 20 | } -------------------------------------------------------------------------------- /src/services/wallet/data.d.ts: -------------------------------------------------------------------------------- 1 | // wrapped token price entity 2 | export interface Account { 3 | address: string; // such as ST1ZFAP71CCAHCM54VSRN8AWTZ29M8R6WYE4YA3WW 4 | type: string; // such as STX/BTC 5 | balance: string; // such as 0.23493812 6 | skEnc: string; // encrypted private key 7 | iv: string; 8 | authTag: string; 9 | } 10 | 11 | export interface AccountPk { 12 | pk: string; 13 | type: string; 14 | skEnc: string; 15 | iv: string; 16 | authTag: string; 17 | } 18 | 19 | export interface NewAccount { 20 | mnemonic?: string; 21 | type?: 1 | 2; 22 | } 23 | -------------------------------------------------------------------------------- /src/pages/user/login/components/Login/LoginSubmit.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from 'antd'; 2 | 3 | import { ButtonProps } from 'antd/es/button'; 4 | import React from 'react'; 5 | import classNames from 'classnames'; 6 | import styles from './index.less'; 7 | 8 | interface LoginSubmitProps extends ButtonProps { 9 | className?: string; 10 | } 11 | 12 | const LoginSubmit: React.FC = ({ className, ...rest }) => { 13 | const clsString = classNames(styles.submit, className); 14 | return 53 | ); 54 | notification.open({ 55 | message: useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated' }), 56 | description: useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated.hint' }), 57 | btn, 58 | key, 59 | onClose: async () => {}, 60 | }); 61 | }); 62 | } else if ('serviceWorker' in navigator) { 63 | // unregister service worker 64 | const { serviceWorker } = navigator; 65 | if (serviceWorker.getRegistrations) { 66 | serviceWorker.getRegistrations().then((sws) => { 67 | sws.forEach((sw) => { 68 | sw.unregister(); 69 | }); 70 | }); 71 | } 72 | serviceWorker.getRegistration().then((sw) => { 73 | if (sw) sw.unregister(); 74 | }); 75 | 76 | // remove all caches 77 | if (window.caches && window.caches.keys) { 78 | caches.keys().then((keys) => { 79 | keys.forEach((key) => { 80 | caches.delete(key); 81 | }); 82 | }); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/components/RightContent/AvatarDropdown.tsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from 'react'; 2 | import { LogoutOutlined, UserOutlined } from '@ant-design/icons'; 3 | import { Avatar, Menu, Spin } from 'antd'; 4 | import { FormattedMessage, history, useModel } from 'umi'; 5 | import { getPageQuery } from '@/utils/utils'; 6 | import { outLogin } from '@/services/login'; 7 | import { stringify } from 'querystring'; 8 | import HeaderDropdown from '../HeaderDropdown'; 9 | import styles from './index.less'; 10 | 11 | export interface GlobalHeaderRightProps { 12 | menu?: boolean; 13 | } 14 | 15 | /** 16 | * 退出登录,并且将当前的 url 保存 17 | */ 18 | const loginOut = async () => { 19 | await outLogin(); 20 | const { redirect } = getPageQuery(); 21 | // Note: There may be security issues, please note 22 | if (window.location.pathname !== '/user/login' && !redirect) { 23 | history.replace({ 24 | pathname: '/user/login', 25 | search: stringify({ 26 | redirect: window.location.href, 27 | }), 28 | }); 29 | } 30 | }; 31 | 32 | const AvatarDropdown: React.FC = ({ menu }) => { 33 | const { initialState, setInitialState } = useModel('@@initialState'); 34 | 35 | const onMenuClick = useCallback( 36 | (event: { 37 | key: React.Key; 38 | keyPath: React.Key[]; 39 | item: React.ReactInstance; 40 | domEvent: React.MouseEvent; 41 | }) => { 42 | const { key } = event; 43 | if (key === 'logout') { 44 | setInitialState({ ...initialState, currentUser: undefined }); 45 | loginOut(); 46 | return; 47 | } 48 | history.push(`/account/${key}`); 49 | }, 50 | [], 51 | ); 52 | 53 | const loading = ( 54 | 55 | 62 | 63 | ); 64 | 65 | if (!initialState) { 66 | return loading; 67 | } 68 | 69 | const { currentUser } = initialState; 70 | 71 | if (!currentUser) { 72 | return loading; 73 | } 74 | 75 | const menuHeaderDropdown = ( 76 | 77 | {menu && } 78 | 79 | 80 | 81 | 82 | 83 | ); 84 | return ( 85 | 86 | 87 | } /> 88 | {/* {currentUser.name} */} 89 | 90 | 91 | ); 92 | }; 93 | 94 | export default AvatarDropdown; 95 | -------------------------------------------------------------------------------- /src/components/RightContent/SwitchNetwork.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | import { Menu, Dropdown, Button, message, Tag } from 'antd'; 4 | import { networkState, ConnectProps, connect } from 'umi'; 5 | import logo from '@/assets/stacks_icon.png'; 6 | import { getNetworkFromStorage } from '@/utils/utils' 7 | 8 | interface PageProps extends ConnectProps { 9 | index: networkState; 10 | } 11 | 12 | const SwitchNetwork: React.FC = () => { 13 | const [networkName, setNetworkName] = useState("Xenon"); 14 | //console.log(network, dispatch) 15 | /* 16 | const changeNetwork = (newNetwork:any) => { 17 | dispatch({ 18 | type: 'network/save', 19 | payload: { 20 | network: newNetwork 21 | } 22 | }) 23 | } 24 | */ 25 | const changeNetworkDAO = (newNetwork: any) => { 26 | localStorage.setItem("network", newNetwork) 27 | } 28 | 29 | 30 | useEffect(() => { 31 | switch (getNetworkFromStorage()) { 32 | case "Xenon": setNetworkName("Xenon"); break; 33 | case "Mainnet": setNetworkName("Mainnet"); break; 34 | } 35 | }, []) 36 | 37 | 38 | const onClick = ({ key }) => { 39 | switch (key) { 40 | case "Xenon": { 41 | setNetworkName("Xenon"); 42 | message.info(`Switch to Xenon network`); 43 | changeNetworkDAO("Xenon"); 44 | window.location.reload(); 45 | // switchPage('Xenon'); 46 | break; 47 | } 48 | case "Mainnet": { 49 | setNetworkName("Mainnet"); 50 | message.info(`Switch to Mainnet network`); 51 | changeNetworkDAO("Mainnet"); 52 | window.location.reload(); 53 | // switchPage('Xenon'); 54 | break; 55 | } 56 | } 57 | 58 | }; 59 | 60 | const menu = ( 61 | 62 | 63 | Xenon 64 | 65 | 66 | Mainnet 67 | 68 | 69 | ); 70 | 71 | 72 | 73 | return (
74 | 75 | 78 | 79 | { 80 | networkName === 'Xenon' ? 81 | 82 | {networkName} 83 | 84 | : 85 | 86 | {networkName} 87 | } 88 | 89 |
) 90 | } 91 | 92 | export default connect(({ network }: { network: networkState; }) => ({ 93 | network 94 | }))(SwitchNetwork); -------------------------------------------------------------------------------- /src/components/HeaderSearch/index.tsx: -------------------------------------------------------------------------------- 1 | import { SearchOutlined } from '@ant-design/icons'; 2 | import { AutoComplete, Input } from 'antd'; 3 | import useMergeValue from 'use-merge-value'; 4 | import { AutoCompleteProps } from 'antd/es/auto-complete'; 5 | import React, { useRef } from 'react'; 6 | 7 | import classNames from 'classnames'; 8 | import styles from './index.less'; 9 | 10 | export interface HeaderSearchProps { 11 | onSearch?: (value?: string) => void; 12 | onChange?: (value?: string) => void; 13 | onVisibleChange?: (b: boolean) => void; 14 | className?: string; 15 | placeholder?: string; 16 | options: AutoCompleteProps['options']; 17 | defaultOpen?: boolean; 18 | open?: boolean; 19 | defaultValue?: string; 20 | value?: string; 21 | } 22 | 23 | const HeaderSearch: React.FC = (props) => { 24 | const { 25 | className, 26 | defaultValue, 27 | onVisibleChange, 28 | placeholder, 29 | open, 30 | defaultOpen, 31 | ...restProps 32 | } = props; 33 | 34 | const inputRef = useRef(null); 35 | 36 | const [value, setValue] = useMergeValue(defaultValue, { 37 | value: props.value, 38 | onChange: props.onChange, 39 | }); 40 | 41 | const [searchMode, setSearchMode] = useMergeValue(defaultOpen || false, { 42 | value: props.open, 43 | onChange: onVisibleChange, 44 | }); 45 | 46 | const inputClass = classNames(styles.input, { 47 | [styles.show]: searchMode, 48 | }); 49 | 50 | return ( 51 |
{ 54 | setSearchMode(true); 55 | if (searchMode && inputRef.current) { 56 | inputRef.current.focus(); 57 | } 58 | }} 59 | onTransitionEnd={({ propertyName }) => { 60 | if (propertyName === 'width' && !searchMode) { 61 | if (onVisibleChange) { 62 | onVisibleChange(searchMode); 63 | } 64 | } 65 | }} 66 | > 67 | 73 | 80 | { 87 | if (e.key === 'Enter') { 88 | if (restProps.onSearch) { 89 | restProps.onSearch(value); 90 | } 91 | } 92 | }} 93 | onBlur={() => { 94 | setSearchMode(false); 95 | }} 96 | /> 97 | 98 |
99 | ); 100 | }; 101 | 102 | export default HeaderSearch; 103 | -------------------------------------------------------------------------------- /src/pages/client/component/MiningInfoTable.tsx: -------------------------------------------------------------------------------- 1 | import { MiningInfo, MiningInfoQueryParams } from '@/services/client/data'; 2 | import { exportInfo } from '@/services/publicdata/exportUtils'; 3 | import { ExportOutlined } from '@ant-design/icons'; 4 | import ProTable, { ProColumns } from '@ant-design/pro-table'; 5 | import { Button, Tag } from 'antd'; 6 | import React from 'react'; 7 | import { FormattedMessage, useModel } from 'umi'; 8 | 9 | const MiningInfoTable: React.FC<{}> = () => { 10 | 11 | const { operationBoardState } = useModel('client.operationBoard'); 12 | const { miningInfoState, queryMiningInfo } = useModel('client.miningInfo'); 13 | const { minerAddress } = operationBoardState; 14 | const { miningInfoList } = miningInfoState; 15 | const miningInfoColumns: ProColumns[] = [ 16 | { 17 | title: , 18 | dataIndex: 'stacks_block_height', 19 | width: 35, 20 | render: (_) => {_}, 21 | search: false, 22 | }, 23 | { 24 | title: , 25 | dataIndex: 'stx_address', 26 | copyable: true, 27 | ellipsis: true, 28 | width: 150, 29 | search: false, 30 | }, 31 | { 32 | title: , 33 | dataIndex: 'btc_address', 34 | render: (text, record, index, action) => 35 | [ {record.btc_address} ], 36 | copyable: true, 37 | ellipsis: true, 38 | width: 150, 39 | }, 40 | { 41 | title: , 42 | dataIndex: 'burn_fee', 43 | width: 50, 44 | search: false, 45 | }, 46 | ] 47 | 48 | return ( 49 | 50 | size="small" 51 | headerTitle={} 52 | rowKey="stacks_block_height" 53 | pagination={{ 54 | pageSize: 10, 55 | }} 56 | request={async (params: MiningInfoQueryParams) => queryMiningInfo(params)} 57 | columns={miningInfoColumns} 58 | toolBarRender={() => [ 59 | , 62 | ]} 63 | search={{ labelWidth: 'auto' }} 64 | /> 65 | ) 66 | } 67 | 68 | export default MiningInfoTable; -------------------------------------------------------------------------------- /src/pages/client/component/MinerInfoTable.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { MinerInfo, MinerInfoQueryParams } from "@/services/client/data"; 3 | import ProTable, { ProColumns } from "@ant-design/pro-table"; 4 | import { FormattedMessage, useModel } from "umi"; 5 | import { Button } from 'antd'; 6 | import { ExportOutlined } from '@ant-design/icons'; 7 | import { exportInfo } from '@/services/publicdata/exportUtils'; 8 | 9 | const MinerInfoTable: React.FC<{}> = () => { 10 | const { operationBoardState } = useModel('client.operationBoard'); 11 | let { minerAddress } = operationBoardState; 12 | const { minerInfoState, queryMinerInfo } = useModel('client.minerInfo'); 13 | let { minerInfoList } = minerInfoState; 14 | const minerInfoColumns: ProColumns[] = [ 15 | { 16 | title: , 17 | dataIndex: 'stx_address', 18 | width: 150, 19 | copyable: true, 20 | ellipsis: true, 21 | search: false 22 | }, 23 | { 24 | title: , 25 | dataIndex: 'btc_address', 26 | width: 120, 27 | render: (text, record, index, action) => 28 | [ {record.btc_address} ], 29 | copyable: true, 30 | ellipsis: true, 31 | }, 32 | { 33 | title: , 34 | dataIndex: 'actual_win', 35 | width: 50, 36 | search: false, 37 | }, 38 | { 39 | title: , 40 | dataIndex: 'total_mined', 41 | width: 50, 42 | search: false, 43 | }, 44 | { 45 | title: , 46 | dataIndex: 'miner_burned', 47 | width: 70, 48 | search: false, 49 | }, 50 | ] 51 | 52 | return ( 53 | <> 54 | 55 | headerTitle={} 56 | rowKey="stx_address" 57 | pagination={{ 58 | pageSize: 10, 59 | }} 60 | request={(params: MinerInfoQueryParams) => queryMinerInfo(params)} 61 | columns={minerInfoColumns} 62 | size="small" 63 | toolBarRender={() => [ 64 | , 67 | ]} 68 | search={{ labelWidth: 'auto' }} 69 | /> 70 | 71 | ) 72 | } 73 | 74 | export default MinerInfoTable; -------------------------------------------------------------------------------- /src/services/publicdata/chainInfo.ts: -------------------------------------------------------------------------------- 1 | import { request } from 'umi'; 2 | import { ChainInfo, BlockInfo, TxInfo } from './data'; 3 | import { getNetworkFromStorage } from '@/utils/utils' 4 | import { getSysConf } from '../sysConf/conf'; 5 | 6 | const { 7 | nodeXenonURL, 8 | nodeMainnetURL, 9 | sidecarURLMainnet 10 | } = require('@/services/constants') 11 | 12 | 13 | 14 | export async function getChainInfo() { 15 | let baseURL = nodeXenonURL; 16 | switch (getNetworkFromStorage()) { 17 | case "Xenon": baseURL = nodeXenonURL; 18 | break; 19 | case "Mainnet": baseURL = nodeMainnetURL; 20 | break; //TODO 21 | default: break; 22 | } 23 | let result; 24 | try { 25 | result = await request(`${baseURL}/v2/info`, { 26 | method: 'GET', 27 | timeout: 8000, 28 | }) 29 | } 30 | catch (error) { 31 | result = undefined 32 | } 33 | const chainInfoList: ChainInfo[] = []; 34 | chainInfoList.push({ 35 | stacksChainHeight: (result == undefined ? "NaN" : result.stacks_tip_height), 36 | burnChainHeight: (result == undefined ? "NaN" : result.stable_burn_block_height), 37 | }) 38 | 39 | return { 'data': chainInfoList } //new Promise((resolve)=>{resolve(chainInfoList)}) 40 | } 41 | 42 | export async function getBlockInfo() { 43 | let baseURL = sidecarURLMainnet; 44 | const confInfo = getSysConf(); 45 | switch (getNetworkFromStorage()) { 46 | case "Xenon": baseURL = confInfo.sidecarUrl; break; 47 | case "Mainnet": baseURL = confInfo.sidecarUrl; break; 48 | default: break; 49 | } 50 | return request(`${baseURL}/v1/block?limit=5`, { 51 | method: "GET" 52 | }).then(async (resp) => { 53 | const results: BlockInfo = await Promise.all( 54 | resp.results.map(async (item: BlockInfo) => { 55 | const { txs } = item 56 | let totalFee = 0; 57 | await Promise.all(txs.map(async (itemTxInfo: any) => { 58 | const respTxInfo = await getTxInfo(itemTxInfo) 59 | totalFee += parseInt(respTxInfo.data.fee_rate as string, 10); 60 | return itemTxInfo 61 | })) 62 | return { ...item, total_fee: totalFee, canonical: item.canonical ? "success" : "pending" } as BlockInfo 63 | }) 64 | ) 65 | return { 'data': results } 66 | }) 67 | } 68 | 69 | export async function getTxInfo(tx_id: any) { 70 | let baseURL = sidecarURLMainnet; 71 | const confInfo = getSysConf(); 72 | console.log('confINfo:', confInfo.sidecarUrl) 73 | switch (getNetworkFromStorage()) { 74 | case "Xenon": baseURL = confInfo.sidecarUrl; break; 75 | case "Mainnet": baseURL = confInfo.sidecarUrl; break; 76 | default: break; 77 | } 78 | return request(`${baseURL}/v1/tx/${tx_id}`, { 79 | method: "GET" 80 | }).then((resp: TxInfo) => { 81 | return { 'data': resp } 82 | }) 83 | } 84 | 85 | export async function getTxsInfo(txs: string[]) { 86 | 87 | const data: TxInfo[] = []; 88 | await Promise.all(txs.map(async (item: any) => { 89 | const resp = await getTxInfo(item) 90 | data.push(resp.data as TxInfo) 91 | })) 92 | return { data: data } 93 | } 94 | -------------------------------------------------------------------------------- /src/pages/wallet/components/AddAccountForm.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Modal, Button, Select } from 'antd'; 3 | import Form from 'antd/es/form'; 4 | import TextArea from 'antd/lib/input/TextArea'; 5 | import { NewAccount } from '@/services/wallet/data'; 6 | import { useModel } from 'umi'; 7 | import { showMessage } from "@/services/locale"; 8 | const { btcType, stxType } = require('@/services/constants'); 9 | 10 | export interface FormValueType extends Partial { 11 | mnemonic?: string; 12 | type?: 1 | 2; 13 | } 14 | 15 | const FormItem = Form.Item; 16 | 17 | const formLayout = { 18 | labelCol: { span: 7 }, 19 | wrapperCol: { span: 13 }, 20 | }; 21 | 22 | const AddAccountForm: React.FC<{}> = () => { 23 | const [formVals, setFormVals] = useState({}); 24 | const { handleAddNewAccount, getVisible, handleModalVisible } = useModel('wallet.addAccount'); 25 | const { actionRef } = useModel('wallet.wallet'); 26 | const [form] = Form.useForm(); 27 | 28 | const handleCancel = async () => { 29 | form.resetFields(); 30 | handleModalVisible(false) 31 | } 32 | 33 | const handleFormData = async () => { 34 | const fieldsValue = await form.validateFields(); 35 | setFormVals({ ...formVals, ...fieldsValue }); 36 | let success = await handleAddNewAccount({ ...formVals, ...fieldsValue }); 37 | form.resetFields(); 38 | 39 | if (success) { 40 | handleModalVisible(false); 41 | if (actionRef.current) { 42 | actionRef.current.reload(); 43 | } 44 | } 45 | 46 | } 47 | 48 | const renderContent = () => { 49 | return ( 50 | <> 51 | 56 |