├── .nvmrc ├── src ├── libs │ ├── layout │ │ ├── utils.ts │ │ ├── types.ts │ │ ├── header │ │ │ ├── index.tsx │ │ │ ├── header-data-updater.tsx │ │ │ ├── header-submenu-bar-buy-cspr-link.tsx │ │ │ └── components │ │ │ │ └── header-background-container.tsx │ │ ├── error │ │ │ ├── types.ts │ │ │ └── index.ts │ │ ├── index.tsx │ │ └── locked-router │ │ │ └── index.tsx │ ├── i18n │ │ └── domain │ │ │ └── index.ts │ ├── services │ │ ├── cep18-service │ │ │ ├── index.ts │ │ │ ├── use-fetch-cep18-tokens.ts │ │ │ └── queries.ts │ │ ├── contract-package │ │ │ └── index.ts │ │ ├── validators-service │ │ │ └── index.ts │ │ ├── app-events │ │ │ ├── index.ts │ │ │ └── use-get-active-app-marketing-event.ts │ │ ├── signature-request-service │ │ │ └── index.ts │ │ ├── nft-service │ │ │ ├── index.ts │ │ │ └── use-fetch-derive-media-type.ts │ │ ├── account-info │ │ │ ├── index.ts │ │ │ └── use-fetch-account-from-cspr-name.ts │ │ ├── balance-service │ │ │ ├── index.ts │ │ │ ├── constants.ts │ │ │ └── use-fetch-accounts-balances.ts │ │ ├── buy-cspr-service │ │ │ ├── index.ts │ │ │ └── use-get-on-ramp-providers.ts │ │ ├── ledger │ │ │ └── index.ts │ │ ├── deploys │ │ │ └── index.ts │ │ ├── utils.ts │ │ ├── query-client.ts │ │ └── deployer-service │ │ │ └── types.ts │ ├── ui │ │ ├── index.ts │ │ ├── components │ │ │ ├── secret-phrase-words-view │ │ │ │ ├── types.ts │ │ │ │ └── index.ts │ │ │ ├── form │ │ │ │ └── form.tsx │ │ │ ├── recipient-tabs │ │ │ │ └── utils.ts │ │ │ ├── tile │ │ │ │ └── tile.tsx │ │ │ ├── flex-row │ │ │ │ └── flex-row.tsx │ │ │ ├── maybe-link │ │ │ │ └── maybe-link.tsx │ │ │ ├── skeleton │ │ │ │ └── skeleton.tsx │ │ │ ├── copy-to-clipboard │ │ │ │ └── copy-to-clipboard.tsx │ │ │ ├── password-visibility-icon │ │ │ │ └── password-visibility-icon.tsx │ │ │ ├── portal-tooltip │ │ │ │ └── portal-tooltip.tsx │ │ │ ├── text-list │ │ │ │ └── text-list.tsx │ │ │ └── account-list │ │ │ │ └── utils.ts │ │ ├── utils │ │ │ ├── index.ts │ │ │ ├── get-linear-gradient-color.ts │ │ │ └── hex-to-rgba.ts │ │ ├── types.ts │ │ ├── forms │ │ │ ├── get-submit-button-state-from-validation.ts │ │ │ ├── recover-from-secret-phrase.ts │ │ │ ├── unlock-wallet.ts │ │ │ ├── create-password.ts │ │ │ ├── buy-cspr.ts │ │ │ ├── import-account-from-torus.ts │ │ │ └── rename-account.ts │ │ └── global-style.ts │ ├── crypto │ │ ├── ed_secret_key.pem │ │ ├── index.ts │ │ ├── secp_secret_key.pem │ │ ├── bip44.test.ts │ │ ├── bip32.test.ts │ │ ├── bip44.ts │ │ ├── bip39.test.ts │ │ ├── aes.ts │ │ ├── sign-deploy.tsx │ │ ├── create-asymmetric-key.ts │ │ ├── parse-secret-key-string-secp.ts │ │ └── bip32.ts │ ├── types │ │ └── account.ts │ └── entities │ │ └── Account.ts ├── background │ ├── service-message.ts │ ├── redux │ │ ├── vault-cipher │ │ │ ├── types.ts │ │ │ ├── actions.ts │ │ │ ├── selectors.ts │ │ │ └── reducer.ts │ │ ├── active-origin │ │ │ ├── types.ts │ │ │ ├── selectors.ts │ │ │ ├── actions.ts │ │ │ └── reducer.ts │ │ ├── active-origin-favicon │ │ │ ├── types.ts │ │ │ ├── selectors.ts │ │ │ ├── actions.ts │ │ │ └── reducer.ts │ │ ├── login-retry-lockout-time │ │ │ ├── types.ts │ │ │ ├── actions.ts │ │ │ ├── selectors.ts │ │ │ └── reducer.ts │ │ ├── recent-recipient-public-keys │ │ │ ├── types.ts │ │ │ ├── selectors.ts │ │ │ ├── actions.ts │ │ │ └── reducer.ts │ │ ├── app-events │ │ │ ├── types.ts │ │ │ ├── selectors.ts │ │ │ ├── actions.ts │ │ │ └── reducer.ts │ │ ├── windowManagement │ │ │ ├── types.ts │ │ │ ├── selectors.ts │ │ │ ├── actions.ts │ │ │ └── reducer.ts │ │ ├── trusted-wasm │ │ │ ├── types.ts │ │ │ ├── actions.ts │ │ │ └── selectors.ts │ │ ├── session │ │ │ ├── types.ts │ │ │ ├── selectors.ts │ │ │ ├── actions.ts │ │ │ └── reducer.ts │ │ ├── account-info │ │ │ ├── types.ts │ │ │ ├── selectors.ts │ │ │ └── actions.ts │ │ ├── keys │ │ │ ├── types.ts │ │ │ ├── actions.ts │ │ │ ├── reducer.ts │ │ │ └── selectors.ts │ │ ├── login-retry-count │ │ │ ├── selectors.ts │ │ │ ├── actions.ts │ │ │ └── reducer.ts │ │ ├── ledger │ │ │ ├── types.ts │ │ │ ├── selectors.ts │ │ │ └── actions.ts │ │ ├── last-activity-time │ │ │ ├── selectors.ts │ │ │ ├── actions.ts │ │ │ └── reducer.ts │ │ ├── sagas │ │ │ └── types.ts │ │ ├── rate-app │ │ │ ├── selectors.ts │ │ │ ├── actions.ts │ │ │ └── types.ts │ │ ├── contacts │ │ │ ├── types.ts │ │ │ ├── actions.ts │ │ │ └── selectors.ts │ │ ├── settings │ │ │ ├── types.ts │ │ │ └── actions.ts │ │ ├── vault │ │ │ └── types.ts │ │ ├── root-saga.ts │ │ ├── root-selector.ts │ │ └── utils.ts │ ├── internal-errors.ts │ ├── open-window.ts │ ├── send-sdk-response-to-specific-tab.ts │ ├── bring-web3-events.ts │ ├── workers │ │ └── verify-password-worker.ts │ ├── wallet-repositories.ts │ ├── background-events.ts │ ├── close-current-window.ts │ └── close-window-by-reload-extension.ts ├── global.d.ts ├── fixtures │ └── index.ts ├── @types │ ├── react-identicons │ │ └── react-identicons.d.ts │ ├── lapo │ │ └── lapo.d.ts │ └── custom.d.ts ├── apps │ ├── connect-to-app │ │ ├── router │ │ │ ├── index.ts │ │ │ ├── paths.ts │ │ │ └── use-typed-navigate.ts │ │ ├── index.html │ │ └── pages │ │ │ └── connecting │ │ │ └── index.tsx │ ├── import-account-with-file │ │ ├── router │ │ │ ├── types.ts │ │ │ ├── index.ts │ │ │ ├── paths.ts │ │ │ ├── use-typed-location.ts │ │ │ └── use-typed-navigate.ts │ │ ├── pages │ │ │ ├── import-account-with-file-upload │ │ │ │ └── types.ts │ │ │ ├── import-account-with-file-success │ │ │ │ └── index.tsx │ │ │ └── import-account-with-file │ │ │ │ └── index.tsx │ │ └── index.html │ ├── signature-request │ │ ├── pages │ │ │ └── sign-transaction │ │ │ │ ├── types.ts │ │ │ │ └── utils.ts │ │ ├── router │ │ │ ├── paths.ts │ │ │ ├── types.ts │ │ │ ├── index.ts │ │ │ ├── use-typed-location.ts │ │ │ └── use-typed-navigate.ts │ │ └── index.html │ ├── onboarding │ │ ├── router │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ ├── use-typed-location.ts │ │ │ ├── use-typed-navigate.ts │ │ │ └── paths.ts │ │ ├── index.html │ │ ├── utils │ │ │ └── close-active-tab.ts │ │ ├── pages │ │ │ ├── select-accounts-to-recover │ │ │ │ └── utils.ts │ │ │ ├── onboarding-success │ │ │ │ └── index.tsx │ │ │ └── reset-wallet │ │ │ │ └── content.tsx │ │ └── hooks │ │ │ └── use-onboarding-form-state.ts │ └── popup │ │ ├── router │ │ ├── index.ts │ │ ├── use-typed-location.ts │ │ ├── use-typed-navigate.ts │ │ ├── types.ts │ │ └── use-navigation-menu.ts │ │ ├── pages │ │ ├── import-account-from-ledger │ │ │ └── types.ts │ │ ├── transfer │ │ │ └── utils.ts │ │ ├── bring-web3-unlock │ │ │ └── index.tsx │ │ ├── all-accounts │ │ │ └── index.tsx │ │ ├── stakes │ │ │ └── step.tsx │ │ ├── change-password │ │ │ └── content.tsx │ │ ├── token-details │ │ │ └── utils.ts │ │ ├── deploy-details │ │ │ └── utils.ts │ │ └── no-connected-account │ │ │ └── index.tsx │ │ ├── index.html │ │ ├── hooks │ │ └── use-force-update.tsx │ │ └── constants.ts ├── assets │ ├── img │ │ ├── logo16.png │ │ ├── logo64.png │ │ ├── logo128.png │ │ └── logo192.png │ ├── icons │ │ ├── coingecko.png │ │ ├── friendly-market.png │ │ ├── radio-button-off.svg │ │ ├── connected.svg │ │ ├── connected-big.svg │ │ ├── checkbox-square.svg │ │ ├── ic-arrow-with-tail.svg │ │ ├── theme.svg │ │ ├── file.svg │ │ ├── chat.svg │ │ ├── clock.svg │ │ ├── generic.svg │ │ ├── more.svg │ │ ├── tick.svg │ │ ├── moon.svg │ │ ├── generic-contract-icon.svg │ │ ├── chevron.svg │ │ ├── chevron-up.svg │ │ ├── cross-in-circle.svg │ │ ├── placeholder-image-gray.svg │ │ ├── ledger-white.svg │ │ ├── cross.svg │ │ ├── edit.svg │ │ ├── copy.svg │ │ ├── nft-contract-icon.svg │ │ ├── tick-in-circle.svg │ │ ├── cep-18-default.svg │ │ ├── cep18-contract-icon.svg │ │ ├── lock.svg │ │ ├── search.svg │ │ ├── radio-button-on.svg │ │ ├── books.svg │ │ ├── burger-menu.svg │ │ ├── checkbox-square-checked.svg │ │ ├── show.svg │ │ ├── burn.svg │ │ ├── torus.svg │ │ ├── contact.svg │ │ ├── close-filter.svg │ │ ├── plus.svg │ │ ├── close.svg │ │ ├── burger-close.svg │ │ ├── info.svg │ │ ├── secure.svg │ │ ├── connection.svg │ │ ├── card.svg │ │ ├── sign.svg │ │ ├── team.svg │ │ ├── transfer.svg │ │ ├── receive.svg │ │ ├── media-placeholder.svg │ │ ├── delegate.svg │ │ ├── auction.svg │ │ ├── reset.svg │ │ ├── bid-contract-icon.svg │ │ ├── unlock.svg │ │ ├── download.svg │ │ ├── delete.svg │ │ ├── casper-wallet-logo.svg │ │ ├── error.svg │ │ ├── undelegate.svg │ │ ├── casper.svg │ │ └── associated-keys.svg │ ├── fonts │ │ ├── Inter-VariableFont.woff │ │ ├── Inter-VariableFont.woff2 │ │ ├── JetBrainsMono-VariableFont.woff │ │ ├── JetBrainsMono-VariableFont.woff2 │ │ └── fonts.css │ ├── illustrations │ │ └── redux-devtools-guide │ │ │ └── redux-devtools-settings.png │ └── locales │ │ ├── vi │ │ └── translation.json │ │ ├── pl │ │ └── translation.json │ │ ├── tr │ │ └── translation.json │ │ ├── nl │ │ └── translation.json │ │ ├── az │ │ └── translation.json │ │ ├── es │ │ └── translation.json │ │ └── fr │ │ └── translation.json ├── errors.ts ├── content │ ├── sdk-errors.ts │ ├── sdk-types.ts │ ├── sdk-event.ts │ └── sdk-event-type.ts └── hooks │ ├── use-is-dark-mode.ts │ ├── use-async-effect.ts │ ├── use-system-theme-detector.ts │ ├── use-click-away.ts │ ├── use-window-manager.ts │ └── use-copy-to-clipboard.ts ├── .husky ├── pre-commit └── pre-push ├── .markdownlint.json ├── assests ├── README.MD └── casper-wallet-wc-logo.svg ├── assets ├── README.MD └── casper-wallet-wc-logo.svg ├── .babelrc ├── xcode-project └── Casper Wallet │ ├── Casper Wallet │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── mac-icon-16@1x.png │ │ │ ├── mac-icon-16@2x.png │ │ │ ├── mac-icon-32@1x.png │ │ │ ├── mac-icon-32@2x.png │ │ │ ├── mac-icon-128@1x.png │ │ │ ├── mac-icon-128@2x.png │ │ │ ├── mac-icon-256@1x.png │ │ │ ├── mac-icon-256@2x.png │ │ │ ├── mac-icon-512@1x.png │ │ │ └── mac-icon-512@2x.png │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── LargeIcon.imageset │ │ │ └── Contents.json │ ├── Resources │ │ ├── Icon.png │ │ └── Style.css │ ├── Info.plist │ ├── Casper_Wallet.entitlements │ ├── Casper Wallet.entitlements │ ├── AppDelegate.swift │ └── Base.lproj │ │ └── Main.html │ ├── Casper Wallet.xcodeproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist │ └── Casper Wallet Extension │ ├── Casper_Wallet_Extension.entitlements │ ├── Info.plist │ └── SafariWebExtensionHandler.swift ├── e2e-tests ├── account_secret_key.cer ├── account_secret_key.pem └── popup │ └── account │ └── switch-account.spec.ts ├── scripts ├── build_src.sh ├── build_all.sh ├── build-xcode-project.sh ├── build_safari.sh ├── create-xcode-project.sh ├── locale_extract_pot.js └── locale_update_translations.js ├── SECURITY.md ├── utils ├── env.js ├── build.js └── build-dir-utils.js ├── ci ├── push.sh └── docker │ ├── Dockerfile │ └── nginx │ └── nginx.conf ├── .prettierrc ├── .github ├── dependabot.yml ├── pull_request_template.md └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── jest.e2e.config.js ├── constants.js ├── jest.config.js ├── .eslintrc └── jest.tsconfig.json /.nvmrc: -------------------------------------------------------------------------------- 1 | v18.20.3 2 | -------------------------------------------------------------------------------- /src/libs/layout/utils.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/background/service-message.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx lint-staged 2 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | npx lint-staged 2 | npm run ci-check 3 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom'; 2 | -------------------------------------------------------------------------------- /src/libs/i18n/domain/index.ts: -------------------------------------------------------------------------------- 1 | export * from './language'; 2 | -------------------------------------------------------------------------------- /src/fixtures/index.ts: -------------------------------------------------------------------------------- 1 | export * from './initial-state-for-popup-tests'; 2 | -------------------------------------------------------------------------------- /src/@types/react-identicons/react-identicons.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'react-identicons'; 2 | -------------------------------------------------------------------------------- /src/libs/services/cep18-service/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-fetch-cep18-tokens'; 2 | -------------------------------------------------------------------------------- /src/background/redux/vault-cipher/types.ts: -------------------------------------------------------------------------------- 1 | export type VaultCipherState = null | string; 2 | -------------------------------------------------------------------------------- /src/libs/layout/types.ts: -------------------------------------------------------------------------------- 1 | export type SubmenuActionType = 'back' | 'cancel' | 'close'; 2 | -------------------------------------------------------------------------------- /src/libs/services/contract-package/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-fetch-contract-package'; 2 | -------------------------------------------------------------------------------- /src/libs/services/validators-service/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-fetch-validators'; 2 | -------------------------------------------------------------------------------- /src/libs/ui/index.ts: -------------------------------------------------------------------------------- 1 | export * from './theme-config'; 2 | export * from './global-style'; 3 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "MD013": false, 4 | "MD041": false 5 | } 6 | -------------------------------------------------------------------------------- /src/background/redux/active-origin/types.ts: -------------------------------------------------------------------------------- 1 | export type ActiveOriginState = string | null; 2 | -------------------------------------------------------------------------------- /src/libs/services/app-events/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-get-active-app-marketing-event'; 2 | -------------------------------------------------------------------------------- /src/apps/connect-to-app/router/index.ts: -------------------------------------------------------------------------------- 1 | export * from './paths'; 2 | export * from './use-typed-navigate'; 3 | -------------------------------------------------------------------------------- /src/assets/img/logo16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/src/assets/img/logo16.png -------------------------------------------------------------------------------- /src/assets/img/logo64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/src/assets/img/logo64.png -------------------------------------------------------------------------------- /src/background/redux/active-origin-favicon/types.ts: -------------------------------------------------------------------------------- 1 | export type ActiveOriginFaviconState = string | null; 2 | -------------------------------------------------------------------------------- /src/libs/services/signature-request-service/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-fetch-data-for-signature-request'; 2 | -------------------------------------------------------------------------------- /src/libs/ui/components/secret-phrase-words-view/types.ts: -------------------------------------------------------------------------------- 1 | export type PartialPhraseArray = (string | null)[]; 2 | -------------------------------------------------------------------------------- /src/assets/img/logo128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/src/assets/img/logo128.png -------------------------------------------------------------------------------- /src/assets/img/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/src/assets/img/logo192.png -------------------------------------------------------------------------------- /src/background/redux/login-retry-lockout-time/types.ts: -------------------------------------------------------------------------------- 1 | export type LoginRetryLockoutTimeState = number | null; 2 | -------------------------------------------------------------------------------- /src/background/redux/recent-recipient-public-keys/types.ts: -------------------------------------------------------------------------------- 1 | export type RecentRecipientPublicKeysState = string[]; 2 | -------------------------------------------------------------------------------- /assests/README.MD: -------------------------------------------------------------------------------- 1 | `casper-wallet-wc-logo.svg` - used as public asset for WalletConnect integration. Do not remove it. 2 | -------------------------------------------------------------------------------- /assets/README.MD: -------------------------------------------------------------------------------- 1 | `casper-wallet-wc-logo.svg` - used as public asset for WalletConnect integration. Do not remove it. 2 | -------------------------------------------------------------------------------- /src/assets/icons/coingecko.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/src/assets/icons/coingecko.png -------------------------------------------------------------------------------- /src/background/redux/app-events/types.ts: -------------------------------------------------------------------------------- 1 | export interface AppEventsState { 2 | dismissedEventIds: number[]; 3 | } 4 | -------------------------------------------------------------------------------- /src/background/redux/windowManagement/types.ts: -------------------------------------------------------------------------------- 1 | export interface WindowManagementState { 2 | windowId: number | null; 3 | } 4 | -------------------------------------------------------------------------------- /src/libs/ui/components/form/form.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Form = styled.form``; 4 | -------------------------------------------------------------------------------- /src/apps/import-account-with-file/router/types.ts: -------------------------------------------------------------------------------- 1 | export type LocationState = { 2 | importAccountStatusMessage?: string; 3 | }; 4 | -------------------------------------------------------------------------------- /src/libs/services/nft-service/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-fetch-nft-tokens'; 2 | export * from './use-fetch-derive-media-type'; 3 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react", 5 | "@babel/preset-typescript" 6 | ] 7 | } -------------------------------------------------------------------------------- /src/assets/icons/friendly-market.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/src/assets/icons/friendly-market.png -------------------------------------------------------------------------------- /src/@types/lapo/lapo.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@lapo/asn1js'; 2 | declare module '@lapo/asn1js/hex'; 3 | declare module '@lapo/asn1js/base64'; 4 | -------------------------------------------------------------------------------- /src/assets/fonts/Inter-VariableFont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/src/assets/fonts/Inter-VariableFont.woff -------------------------------------------------------------------------------- /src/libs/services/account-info/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-fetch-accounts-info'; 2 | export * from './use-fetch-account-from-cspr-name'; 3 | -------------------------------------------------------------------------------- /src/libs/services/balance-service/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-fetch-accounts-balances'; 2 | export * from './use-fetch-wallet-balance'; 3 | -------------------------------------------------------------------------------- /src/assets/fonts/Inter-VariableFont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/src/assets/fonts/Inter-VariableFont.woff2 -------------------------------------------------------------------------------- /src/errors.ts: -------------------------------------------------------------------------------- 1 | export class PasswordDoesNotExistError extends Error { 2 | constructor() { 3 | super("Password doesn't exist"); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/libs/services/buy-cspr-service/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-get-on-ramp-providers'; 2 | export * from './use-fetch-on-ramp-countries-and-currencies'; 3 | -------------------------------------------------------------------------------- /src/libs/services/ledger/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ledger'; 2 | export * from './transport'; 3 | export * from './types'; 4 | export * from './errors'; 5 | -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /e2e-tests/account_secret_key.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MC4CAQAwBQYDK2VwBCIEIMLtdAiYwOcQ6J8UW1mgDe6VeQrsker8nKp6MK61kC35 3 | -----END PRIVATE KEY----- 4 | -------------------------------------------------------------------------------- /e2e-tests/account_secret_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MC4CAQAwBQYDK2VwBCIEIHRZr1HEgKVbgchuatwA7dCWDWB7QZe+bpDb5dguIyLE 3 | -----END PRIVATE KEY----- 4 | -------------------------------------------------------------------------------- /src/assets/fonts/JetBrainsMono-VariableFont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/src/assets/fonts/JetBrainsMono-VariableFont.woff -------------------------------------------------------------------------------- /src/assets/fonts/JetBrainsMono-VariableFont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/src/assets/fonts/JetBrainsMono-VariableFont.woff2 -------------------------------------------------------------------------------- /src/apps/signature-request/pages/sign-transaction/types.ts: -------------------------------------------------------------------------------- 1 | export enum SigningPageState { 2 | MainContent, 3 | LedgerConfirmation, 4 | RowDataContent 5 | } 6 | -------------------------------------------------------------------------------- /src/apps/signature-request/router/paths.ts: -------------------------------------------------------------------------------- 1 | export enum RouterPath { 2 | Any = '*', 3 | SignDeploy = '/sign-deploy', 4 | SignMessage = '/sign-message' 5 | } 6 | -------------------------------------------------------------------------------- /src/apps/signature-request/router/types.ts: -------------------------------------------------------------------------------- 1 | import { ErrorLocationState } from '@libs/layout'; 2 | 3 | export interface LocationState extends ErrorLocationState {} 4 | -------------------------------------------------------------------------------- /src/libs/crypto/ed_secret_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MC4CAQAwBQYDK2VwBCIEIKu6biwimq52O4qzdyAp78RrIblNs6GXZdkcqr0+iLLj 3 | -----END PRIVATE KEY----- 4 | -------------------------------------------------------------------------------- /scripts/build_src.sh: -------------------------------------------------------------------------------- 1 | HASH=$(git rev-parse --short HEAD) 2 | 3 | zip -r casper-wallet-src#$HASH.zip src scripts utils *.* .env && mv casper-wallet-src#$HASH.zip build/ 4 | -------------------------------------------------------------------------------- /src/apps/onboarding/router/index.ts: -------------------------------------------------------------------------------- 1 | export * from './types'; 2 | export * from './paths'; 3 | export * from './use-typed-location'; 4 | export * from './use-typed-navigate'; 5 | -------------------------------------------------------------------------------- /src/background/redux/trusted-wasm/types.ts: -------------------------------------------------------------------------------- 1 | export type TrustedWasmState = { 2 | /** {origin: wasmHashes[]} */ 3 | hashesByOriginDict: Record; 4 | }; 5 | -------------------------------------------------------------------------------- /src/libs/ui/components/recipient-tabs/utils.ts: -------------------------------------------------------------------------------- 1 | export enum RecipientTabName { 2 | Recent = 'Recent', 3 | MyAccounts = 'My accounts', 4 | Contacts = 'Contacts' 5 | } 6 | -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Resources/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/xcode-project/Casper Wallet/Casper Wallet/Resources/Icon.png -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If you find a security vulnerability in Casper Wallet, please send an email to *security@make.software*. 6 | -------------------------------------------------------------------------------- /src/apps/signature-request/router/index.ts: -------------------------------------------------------------------------------- 1 | export * from './paths'; 2 | export * from './types'; 3 | export * from './use-typed-navigate'; 4 | export * from './use-typed-location'; 5 | -------------------------------------------------------------------------------- /src/background/redux/session/types.ts: -------------------------------------------------------------------------------- 1 | export interface SessionState { 2 | encryptionKeyHash: string | null; 3 | isLocked: boolean; 4 | isContactEditingAllowed: boolean; 5 | } 6 | -------------------------------------------------------------------------------- /src/libs/services/deploys/index.ts: -------------------------------------------------------------------------------- 1 | export * from './use-fetch-cspr-transfer-deploys'; 2 | export * from './use-fetch-cep18-transfer-deploys'; 3 | export * from './use-fetch-deploys'; 4 | -------------------------------------------------------------------------------- /src/libs/services/utils.ts: -------------------------------------------------------------------------------- 1 | export const toJson = (res: Response): Promise => res.json(); 2 | 3 | export const handleError = (err: Error) => { 4 | console.error(err); 5 | }; 6 | -------------------------------------------------------------------------------- /src/libs/ui/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './get-color-from-theme'; 2 | export * from './formatters'; 3 | export * from './get-linear-gradient-color'; 4 | export * from './hex-to-rgba'; 5 | -------------------------------------------------------------------------------- /src/apps/import-account-with-file/pages/import-account-with-file-upload/types.ts: -------------------------------------------------------------------------------- 1 | export type ImportAccountFormValues = { 2 | secretKeyFile: string | undefined; 3 | name: string; 4 | }; 5 | -------------------------------------------------------------------------------- /src/apps/import-account-with-file/router/index.ts: -------------------------------------------------------------------------------- 1 | export * from './paths'; 2 | export * from './types'; 3 | export * from './use-typed-location'; 4 | export * from './use-typed-navigate'; 5 | -------------------------------------------------------------------------------- /src/background/redux/account-info/types.ts: -------------------------------------------------------------------------------- 1 | export interface AccountInfoState { 2 | pendingDeployHashes: string[]; 3 | accountTrackingIdOfSentNftTokens: Record; 4 | } 5 | -------------------------------------------------------------------------------- /src/background/redux/keys/types.ts: -------------------------------------------------------------------------------- 1 | export type KeysState = { 2 | passwordHash: string | null; 3 | passwordSaltHash: string | null; 4 | keyDerivationSaltHash: string | null; 5 | }; 6 | -------------------------------------------------------------------------------- /scripts/build_all.sh: -------------------------------------------------------------------------------- 1 | HASH=$(git rev-parse --short HEAD) 2 | 3 | npm run build:chrome && npm run build:firefox && cd ./build && zip -r casper-wallet-2.2.1rc1#$HASH.zip ./* && npm run build:src 4 | -------------------------------------------------------------------------------- /src/apps/onboarding/router/types.ts: -------------------------------------------------------------------------------- 1 | import { ErrorLocationState } from '@libs/layout'; 2 | 3 | export interface LocationState extends ErrorLocationState { 4 | secretPhrase?: string[]; 5 | } 6 | -------------------------------------------------------------------------------- /src/background/redux/active-origin/selectors.ts: -------------------------------------------------------------------------------- 1 | import { RootState } from 'typesafe-actions'; 2 | 3 | export const selectActiveOrigin = (state: RootState): string | null => 4 | state.activeOrigin; 5 | -------------------------------------------------------------------------------- /src/background/redux/app-events/selectors.ts: -------------------------------------------------------------------------------- 1 | import { RootState } from 'typesafe-actions'; 2 | 3 | export const selectDismissedAppEvents = (state: RootState) => 4 | state.appEvents.dismissedEventIds; 5 | -------------------------------------------------------------------------------- /src/background/redux/login-retry-count/selectors.ts: -------------------------------------------------------------------------------- 1 | import { RootState } from 'typesafe-actions'; 2 | 3 | export const selectLoginRetryCount = (state: RootState): number => 4 | state.loginRetryCount; 5 | -------------------------------------------------------------------------------- /src/apps/connect-to-app/router/paths.ts: -------------------------------------------------------------------------------- 1 | export enum RouterPath { 2 | SwitchAccount = '/', 3 | SelectAccount = '/', 4 | ApproveConnection = '/approve-connection', 5 | Connecting = '/connecting' 6 | } 7 | -------------------------------------------------------------------------------- /src/assets/illustrations/redux-devtools-guide/redux-devtools-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/src/assets/illustrations/redux-devtools-guide/redux-devtools-settings.png -------------------------------------------------------------------------------- /src/background/redux/active-origin/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | export const activeOriginChanged = createAction('ACTIVE_ORIGIN_CHANGED')< 4 | string | null 5 | >(); 6 | -------------------------------------------------------------------------------- /src/libs/layout/header/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './header-submenu-bar-nav-link'; 2 | export * from './header-view-in-explorer'; 3 | export * from './header-network-switcher'; 4 | export * from './header-popup'; 5 | -------------------------------------------------------------------------------- /src/@types/custom.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' { 2 | const content: any; 3 | export default content; 4 | } 5 | 6 | declare module '*.png' { 7 | const value: string; 8 | export default value; 9 | } 10 | -------------------------------------------------------------------------------- /src/apps/popup/router/index.ts: -------------------------------------------------------------------------------- 1 | export * from './paths'; 2 | export * from './types'; 3 | export * from './use-navigation-menu'; 4 | export * from './use-typed-location'; 5 | export * from './use-typed-navigate'; 6 | -------------------------------------------------------------------------------- /src/assets/icons/radio-button-off.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/background/redux/ledger/types.ts: -------------------------------------------------------------------------------- 1 | export interface LedgerState { 2 | windowId: number | null; 3 | deploy: string | null; 4 | transaction: string | null; 5 | recipientToSaveOnSuccess: string | null; 6 | } 7 | -------------------------------------------------------------------------------- /src/background/redux/windowManagement/selectors.ts: -------------------------------------------------------------------------------- 1 | import { RootState } from 'typesafe-actions'; 2 | 3 | export const selectWindowId = (state: RootState): number | null => 4 | state.windowManagement.windowId; 5 | -------------------------------------------------------------------------------- /src/libs/ui/components/secret-phrase-words-view/index.ts: -------------------------------------------------------------------------------- 1 | export * from './secret-phrase-words-view'; 2 | export * from './copy-secret-phrase-bar'; 3 | export * from './word-picker'; 4 | export * from './word-tag'; 5 | -------------------------------------------------------------------------------- /src/assets/icons/connected.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/background/redux/last-activity-time/selectors.ts: -------------------------------------------------------------------------------- 1 | import { RootState } from 'typesafe-actions'; 2 | 3 | export const selectVaultLastActivityTime = (state: RootState): number | null => 4 | state.lastActivityTime; 5 | -------------------------------------------------------------------------------- /src/assets/icons/connected-big.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/background/internal-errors.ts: -------------------------------------------------------------------------------- 1 | export const CannotGetSenderOriginError = () => 2 | Error('Cannot get sender origin.'); 3 | 4 | export const CannotGetActiveAccountError = () => 5 | Error('Cannot get active account.'); 6 | -------------------------------------------------------------------------------- /src/background/redux/active-origin-favicon/selectors.ts: -------------------------------------------------------------------------------- 1 | import { RootState } from 'typesafe-actions'; 2 | 3 | export const selectActiveOriginFavicon = (state: RootState): string | null => 4 | state.activeOriginFavicon; 5 | -------------------------------------------------------------------------------- /utils/env.js: -------------------------------------------------------------------------------- 1 | // tiny wrapper with default env vars 2 | module.exports = { 3 | NODE_ENV: process.env.NODE_ENV || 'development', 4 | PORT: process.env.PORT || 3000, 5 | BROWSER: process.env.BROWSER || 'chrome' 6 | }; 7 | -------------------------------------------------------------------------------- /scripts/build-xcode-project.sh: -------------------------------------------------------------------------------- 1 | # Building safari extension binary using XCode Project 2 | cd ./xcode-project/Casper\ Wallet 3 | xcodebuild -quiet -project Casper\ Wallet.xcodeproj -alltargets -configuration Release 4 | cd ../.. 5 | -------------------------------------------------------------------------------- /src/background/redux/active-origin-favicon/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | export const activeOriginFaviconChanged = createAction( 4 | 'ACTIVE_ORIGIN_FAVICON_CHANGED' 5 | )(); 6 | -------------------------------------------------------------------------------- /src/assets/icons/checkbox-square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /scripts/build_safari.sh: -------------------------------------------------------------------------------- 1 | rm -rf build/safari 2 | 3 | ./scripts/build-xcode-project.sh 4 | 5 | # Copy binary 6 | mkdir ./build/safari 7 | cp -R xcode-project/Casper\ Wallet/build/Release/Casper\ Wallet.app ./build/safari/Casper\ Wallet.app 8 | -------------------------------------------------------------------------------- /src/apps/popup/pages/import-account-from-ledger/types.ts: -------------------------------------------------------------------------------- 1 | import { Account } from '@libs/types/account'; 2 | 3 | export type ILedgerAccountListItem = Omit< 4 | Account, 5 | 'hidden' | 'secretKey' | 'imported' | 'hardware' 6 | > & { id: string }; 7 | -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-16@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-16@1x.png -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-16@2x.png -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-32@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-32@1x.png -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-32@2x.png -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-128@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-128@1x.png -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-128@2x.png -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-256@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-256@1x.png -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-256@2x.png -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-512@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-512@1x.png -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/make-software/casper-wallet/HEAD/xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AppIcon.appiconset/mac-icon-512@2x.png -------------------------------------------------------------------------------- /src/apps/popup/pages/transfer/utils.ts: -------------------------------------------------------------------------------- 1 | export enum TransactionSteps { 2 | Token = 'token', 3 | Recipient = 'recipient', 4 | Amount = 'amount', 5 | Confirm = 'confirm', 6 | ConfirmWithLedger = 'confirm with ledger', 7 | Success = 'success' 8 | } 9 | -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/libs/crypto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './aes'; 2 | export * from './bip32'; 3 | export * from './bip39'; 4 | export * from './bip44'; 5 | export * from './parse-secret-key-string'; 6 | export * from './parse-secret-key-string-secp'; 7 | export * from './sign-deploy'; 8 | -------------------------------------------------------------------------------- /src/libs/crypto/secp_secret_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHQCAQEEIF9N7aGLID1DPkpayubhr4t0YuRbX+ppcwSXIIIKP33joAcGBSuBBAAK 3 | oUQDQgAEljx37Q3PHMCYzpNVqnHCt07clmRajhziiCmA6Ygcc+W+rKMDv4vXDJ21 4 | u7aZawhRbZl0Ilnrz9MVHbhiSeva0g== 5 | -----END EC PRIVATE KEY----- -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/background/redux/sagas/types.ts: -------------------------------------------------------------------------------- 1 | import { VaultState } from '@background/redux/vault/types'; 2 | 3 | export type UnlockVault = { 4 | vault: VaultState; 5 | newKeyDerivationSaltHash: string; 6 | newVaultCipher: string; 7 | newEncryptionKeyHash: string; 8 | }; 9 | -------------------------------------------------------------------------------- /ci/push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | max_retry=5 4 | counter=0 5 | until git push origin master 6 | do 7 | sleep 3 8 | [[ counter -eq $max_retry ]] && echo "Failed job!" && exit 1 9 | echo "Trying again. Try #$counter" && git pull origin master 10 | ((counter++)) 11 | done -------------------------------------------------------------------------------- /src/background/redux/app-events/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | export const dismissAppEvent = createAction('DISMISS_APP_EVENT')(); 4 | 5 | export const resetAppEventsDismission = createAction( 6 | 'RESET_APP_EVENTS_DISMISSION' 7 | )(); 8 | -------------------------------------------------------------------------------- /src/background/redux/keys/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | import { KeysState } from './types'; 4 | 5 | export const keysUpdated = createAction('KEYS_UPDATED')>(); 6 | 7 | export const keysReseted = createAction('KEYS_RESETED')(); 8 | -------------------------------------------------------------------------------- /src/background/redux/rate-app/selectors.ts: -------------------------------------------------------------------------------- 1 | import { RootState } from 'typesafe-actions'; 2 | 3 | export const selectRatedInStore = (state: RootState) => 4 | state.rateApp.ratedInStore; 5 | 6 | export const selectAskForReviewAfter = (state: RootState) => 7 | state.rateApp.askForReviewAfter; 8 | -------------------------------------------------------------------------------- /src/libs/layout/error/types.ts: -------------------------------------------------------------------------------- 1 | export interface ErrorContent { 2 | errorHeaderText?: string; 3 | errorContentText?: string; 4 | } 5 | 6 | export interface ErrorLocationState extends ErrorContent { 7 | errorPrimaryButtonLabel?: string; 8 | errorRedirectPath?: string | null; 9 | } 10 | -------------------------------------------------------------------------------- /src/background/redux/vault-cipher/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | export const vaultCipherReseted = createAction('VAULT_CIPHER_RESETED')(); 4 | 5 | export const vaultCipherCreated = createAction('VAULT_CIPHER_CREATED')<{ 6 | vaultCipher: string; 7 | }>(); 8 | -------------------------------------------------------------------------------- /src/assets/icons/ic-arrow-with-tail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/background/redux/vault-cipher/selectors.ts: -------------------------------------------------------------------------------- 1 | import { RootState } from 'typesafe-actions'; 2 | 3 | export const selectVaultCipherDoesExist = (state: RootState): boolean => 4 | state.vaultCipher != null; 5 | 6 | export const selectVaultCipher = (state: RootState): string | null => 7 | state.vaultCipher; 8 | -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/background/redux/last-activity-time/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | export const lastActivityTimeRefreshed = createAction( 4 | 'LAST_ACTIVITY_TIME_REFRESHED', 5 | () => ({ 6 | lastActivityTime: Date.now() 7 | }) 8 | )<{ 9 | lastActivityTime: number; 10 | }>(); 11 | -------------------------------------------------------------------------------- /src/apps/popup/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Casper Wallet 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /src/libs/services/balance-service/constants.ts: -------------------------------------------------------------------------------- 1 | import { ICsprBalance } from 'casper-wallet-core/src/domain/tokens'; 2 | 3 | type AccountsBalances = Record | undefined; 4 | 5 | export interface UseFetchAccountsBalances { 6 | accountsBalances: AccountsBalances; 7 | isLoadingBalances: boolean; 8 | } 9 | -------------------------------------------------------------------------------- /src/apps/import-account-with-file/router/paths.ts: -------------------------------------------------------------------------------- 1 | export enum RouterPath { 2 | ImportAccountWithFile = '/', 3 | ImportAccountWithFileUpload = '/import-account-with-file-upload', 4 | ImportAccountWithFileSuccess = '/import-account-with-file-success', 5 | ImportAccountWithFileFailure = '/import-account-with-file-failure' 6 | } 7 | -------------------------------------------------------------------------------- /src/background/redux/login-retry-count/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | export const loginRetryCountReseted = createAction( 4 | 'LOGIN_RETRY_COUNT_RESETED' 5 | )(); 6 | 7 | export const loginRetryCountIncremented = createAction( 8 | 'LOGIN_RETRY_COUNT_INCREMENTED' 9 | )(); 10 | -------------------------------------------------------------------------------- /scripts/create-xcode-project.sh: -------------------------------------------------------------------------------- 1 | # Converting Firefox build folder into a Xcode Project 2 | # Firefox extension is built on manifest V2, the same version Safari required 3 | xcrun safari-web-extension-converter ./build/firefox --project-location xcode-project --bundle-identifier software.make.Casper-Wallet --macos-only --no-open --no-prompt 4 | -------------------------------------------------------------------------------- /src/apps/onboarding/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Casper Wallet: Onboarding 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /src/assets/icons/theme.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/apps/signature-request/router/use-typed-location.ts: -------------------------------------------------------------------------------- 1 | import { useLocation } from 'react-router-dom'; 2 | 3 | import { LocationState } from './types'; 4 | 5 | export function useTypedLocation() { 6 | const location = useLocation(); 7 | 8 | return location as typeof location & { 9 | state?: LocationState; 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /src/apps/connect-to-app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Casper Wallet: Connect to Site 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /src/background/redux/account-info/selectors.ts: -------------------------------------------------------------------------------- 1 | import { RootState } from 'typesafe-actions'; 2 | 3 | export const selectPendingDeployHashes = (state: RootState) => 4 | state.accountInfo.pendingDeployHashes; 5 | 6 | export const selectAccountTrackingIdOfSentNftTokens = (state: RootState) => 7 | state.accountInfo.accountTrackingIdOfSentNftTokens; 8 | -------------------------------------------------------------------------------- /src/apps/onboarding/router/use-typed-location.ts: -------------------------------------------------------------------------------- 1 | import { useLocation } from 'react-router-dom'; 2 | 3 | import { LocationState } from '@onboarding/router/types'; 4 | 5 | export function useTypedLocation() { 6 | const location = useLocation(); 7 | 8 | return location as typeof location & { 9 | state?: LocationState; 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /src/apps/signature-request/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Casper Wallet: Signature Request 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /src/apps/import-account-with-file/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Casper Wallet: Import account 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /src/background/redux/contacts/types.ts: -------------------------------------------------------------------------------- 1 | export interface Contact { 2 | name: string; 3 | publicKey: string; 4 | lastModified: string; 5 | } 6 | 7 | export type ContactsState = { 8 | contacts: Contact[]; 9 | lastModified: string | null; 10 | }; 11 | 12 | export interface EditContactActionType extends Contact { 13 | oldName: string; 14 | } 15 | -------------------------------------------------------------------------------- /src/libs/ui/types.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export type BaseProps = { 4 | id?: string; 5 | dataTestId?: string; 6 | className?: string; 7 | children?: any; 8 | style?: React.CSSProperties; 9 | onClick?: (ev: any) => void; 10 | }; 11 | 12 | export interface KeyValueItem { 13 | key: number | string; 14 | value: string; 15 | } 16 | -------------------------------------------------------------------------------- /src/libs/crypto/bip44.test.ts: -------------------------------------------------------------------------------- 1 | import { Bip44Path } from './__fixtures'; 2 | import { getBip44Path } from './bip44'; 3 | 4 | describe('bip44', () => { 5 | it('should generate valid bip44 derivation path for casper coin', () => { 6 | expect(getBip44Path(0)).toBe(Bip44Path.Address0); 7 | expect(getBip44Path(1)).toBe(Bip44Path.Address1); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /src/background/redux/recent-recipient-public-keys/selectors.ts: -------------------------------------------------------------------------------- 1 | import { RootState } from 'typesafe-actions'; 2 | 3 | import { RecentRecipientPublicKeysState } from '@background/redux/recent-recipient-public-keys/types'; 4 | 5 | export const selectRecentRecipientPublicKeys = ( 6 | state: RootState 7 | ): RecentRecipientPublicKeysState => state.recentRecipientPublicKeys; 8 | -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/apps/import-account-with-file/router/use-typed-location.ts: -------------------------------------------------------------------------------- 1 | import { useLocation } from 'react-router-dom'; 2 | 3 | import { LocationState } from '@import-account-with-file/router/types'; 4 | 5 | export function useTypedLocation() { 6 | const location = useLocation(); 7 | 8 | return location as typeof location & { 9 | state?: LocationState; 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /src/apps/popup/pages/bring-web3-unlock/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { closeCurrentWindow } from '@background/close-current-window'; 4 | 5 | // we use this for closing the window after the user unlocks the wallet for bringweb3 cashback 6 | export const BringWeb3Unlock: React.FC = () => { 7 | closeCurrentWindow(); 8 | 9 | return null; 10 | }; 11 | -------------------------------------------------------------------------------- /src/background/redux/recent-recipient-public-keys/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | export const recipientPublicKeyAdded = createAction( 4 | 'RECIPIENT_PUBLIC_KEY_ADDED', 5 | (payload: string) => payload 6 | )(); 7 | 8 | export const recipientPublicKeyReseted = createAction( 9 | 'RECIPIENT_PUBLIC_KEY_RESETED' 10 | )(); 11 | -------------------------------------------------------------------------------- /src/apps/popup/router/use-typed-location.ts: -------------------------------------------------------------------------------- 1 | import { Location } from '@remix-run/router'; 2 | import { useLocation } from 'react-router-dom'; 3 | 4 | import { LocationState } from './types'; 5 | 6 | interface UseTypedLocation extends Location { 7 | state: LocationState; 8 | } 9 | export function useTypedLocation(): UseTypedLocation { 10 | return useLocation(); 11 | } 12 | -------------------------------------------------------------------------------- /src/assets/icons/file.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/libs/layout/header/header-data-updater.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { useFetchWalletBalance } from '@libs/services/balance-service'; 4 | import { useFetchCep18Tokens } from '@libs/services/cep18-service'; 5 | 6 | export const HeaderDataUpdater: React.FC = () => { 7 | useFetchWalletBalance(); 8 | useFetchCep18Tokens(); 9 | 10 | return null; 11 | }; 12 | -------------------------------------------------------------------------------- /src/libs/ui/components/tile/tile.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Tile = styled.div<{ borderRadius?: 'base' }>` 4 | width: 100%; 5 | background-color: ${({ theme }) => theme.color.backgroundPrimary}; 6 | 7 | border-radius: ${({ theme, borderRadius }) => 8 | borderRadius ? theme.borderRadius.base : theme.borderRadius.twelve}px; 9 | `; 10 | -------------------------------------------------------------------------------- /src/apps/connect-to-app/router/use-typed-navigate.ts: -------------------------------------------------------------------------------- 1 | import { To, useNavigate } from 'react-router-dom'; 2 | 3 | export function useTypedNavigate() { 4 | const navigate = useNavigate(); 5 | 6 | return navigate as { 7 | ( 8 | to: To, 9 | options?: { 10 | replace?: boolean; 11 | } 12 | ): void; 13 | (delta: number): void; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /src/background/redux/login-retry-lockout-time/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | export const loginRetryLockoutTimeReseted = createAction( 4 | 'LOGIN_RETRY_LOCKOUT_TIME_RESETED' 5 | )(); 6 | 7 | export const loginRetryLockoutTimeSet = createAction( 8 | 'LOGIN_RETRY_LOCKOUT_TIME_SET', 9 | (payload: number) => payload 10 | )(); 11 | -------------------------------------------------------------------------------- /src/libs/layout/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './containers'; 2 | export * from './header'; 3 | export * from './popup-layout'; 4 | export * from './layout-window'; 5 | export * from './layout-tab'; 6 | export * from './error'; 7 | export * from './locked-router'; 8 | export * from './reset-vault'; 9 | export * from './unlock-protected-page-content'; 10 | export * from './unlock-vault'; 11 | -------------------------------------------------------------------------------- /src/apps/signature-request/pages/sign-transaction/utils.ts: -------------------------------------------------------------------------------- 1 | import { TxCommonDetailsKeys } from './signature-request-content'; 2 | 3 | export const mapTxKeyToLabel: Record = { 4 | expires: 'Expires', 5 | fee: 'Transaction payment', 6 | memo: 'Transaction ID', 7 | network: 'Network', 8 | sender: 'Sender', 9 | txHash: 'Transaction hash' 10 | }; 11 | -------------------------------------------------------------------------------- /src/background/redux/rate-app/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | export const ratedInStoreChanged = createAction( 4 | 'RATED_IN_STORE_CHANGED' 5 | )(); 6 | 7 | export const askForReviewAfterChanged = createAction( 8 | 'ASK_FOR_REVIEW_AFTER_CHANGED' 9 | )(); 10 | 11 | export const resetRateApp = createAction('RESET_RATE_APP')(); 12 | -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ITSAppUsesNonExemptEncryption 6 | 7 | SFSafariWebExtensionConverterVersion 8 | 14.3 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/background/redux/active-origin/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer } from 'typesafe-actions'; 2 | 3 | import { activeOriginChanged } from './actions'; 4 | import { ActiveOriginState } from './types'; 5 | 6 | const initialState = null as ActiveOriginState; 7 | 8 | export const reducer = createReducer(initialState).handleAction( 9 | activeOriginChanged, 10 | (state, { payload }) => payload 11 | ); 12 | -------------------------------------------------------------------------------- /src/libs/crypto/bip32.test.ts: -------------------------------------------------------------------------------- 1 | import { FIXED_PUBLIC_KEY_0, FIXED_SECRET_PHRASE } from './__fixtures'; 2 | import { deriveKeyPair } from './bip32'; 3 | 4 | describe('bip32', () => { 5 | it('should derive the same key as ledger from the same mnomonic phrase', () => { 6 | const keyPair = deriveKeyPair(FIXED_SECRET_PHRASE, 0); 7 | expect(keyPair.publicKey).toBe(FIXED_PUBLIC_KEY_0); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /src/libs/ui/components/flex-row/flex-row.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react'; 2 | 3 | import FlexBox, { FlexBoxProps } from '../flex-box/flex-box'; 4 | 5 | export interface FlexRowProps extends FlexBoxProps {} 6 | 7 | export const FlexRow = forwardRef( 8 | (props, ref) => 9 | ); 10 | 11 | export default FlexRow; 12 | -------------------------------------------------------------------------------- /ci/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16.15.1-alpine3.15 as builder 2 | 3 | WORKDIR /app/builder 4 | 5 | COPY . . 6 | 7 | RUN cd playground && npm install 8 | RUN cd playground && npm run build 9 | 10 | FROM nginx:1.22.0 11 | 12 | WORKDIR /usr/share/nginx/html 13 | 14 | COPY --from=builder /app/builder/playground/build ./ 15 | COPY --from=builder /app/builder/ci/docker/nginx/nginx.conf /etc/nginx/nginx.conf 16 | 17 | -------------------------------------------------------------------------------- /src/assets/icons/chat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/libs/crypto/bip44.ts: -------------------------------------------------------------------------------- 1 | // Registered at https://github.com/satoshilabs/slips/blob/master/slip-0044.md 2 | const CSPR_COIN_INDEX = 506; 3 | 4 | export function getBip44Path(index: number): string { 5 | return [ 6 | 'm', 7 | `44'`, // bip 44 8 | `${CSPR_COIN_INDEX}'`, // coin index 9 | `0'`, // wallet 10 | `0`, // external 11 | `${index}` // child account index 12 | ].join('/'); 13 | } 14 | -------------------------------------------------------------------------------- /src/libs/ui/utils/get-linear-gradient-color.ts: -------------------------------------------------------------------------------- 1 | export const getLinearGradientColor = ( 2 | gradientColor: 3 | | { 4 | deg: string; 5 | from: string; 6 | to: string; 7 | } 8 | | string 9 | ) => { 10 | if (typeof gradientColor === 'string') return gradientColor; 11 | 12 | return `linear-gradient(${gradientColor.deg}, ${gradientColor.from}, ${gradientColor.to})`; 13 | }; 14 | -------------------------------------------------------------------------------- /src/libs/services/query-client.ts: -------------------------------------------------------------------------------- 1 | import { QueryClient as NewQueryClient } from '@tanstack/react-query'; 2 | 3 | export const newQueryClient = new NewQueryClient({ 4 | defaultOptions: { 5 | queries: { 6 | staleTime: 3 * 60 * 1000, 7 | refetchInterval: 3 * 60 * 1000, 8 | gcTime: 3 * 60 * 1000, 9 | retry: false 10 | }, 11 | mutations: { 12 | retry: false 13 | } 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Casper_Wallet.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/background/redux/session/selectors.ts: -------------------------------------------------------------------------------- 1 | import { RootState } from 'typesafe-actions'; 2 | 3 | export const selectEncryptionKeyHash = (state: RootState): string | null => 4 | state.session.encryptionKeyHash; 5 | 6 | export const selectVaultIsLocked = (state: RootState): boolean => 7 | state.session.isLocked; 8 | 9 | export const selectIsContactEditingAllowed = (state: RootState) => 10 | state.session.isContactEditingAllowed; 11 | -------------------------------------------------------------------------------- /src/apps/onboarding/utils/close-active-tab.ts: -------------------------------------------------------------------------------- 1 | import { tabs } from 'webextension-polyfill'; 2 | 3 | export async function closeActiveTab() { 4 | try { 5 | const tabsList = await tabs.query({ 6 | active: true, 7 | currentWindow: true 8 | }); 9 | if (tabsList.length > 0 && tabsList[0].id != null) { 10 | await tabs.remove(tabsList[0].id); 11 | } 12 | } catch (e) { 13 | console.error(e); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet Extension/Casper_Wallet_Extension.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Assets.xcassets/LargeIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "scale" : "3x" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/assets/icons/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/apps/popup/router/use-typed-navigate.ts: -------------------------------------------------------------------------------- 1 | import { To, useNavigate } from 'react-router-dom'; 2 | 3 | import { LocationState } from './types'; 4 | 5 | export function useTypedNavigate() { 6 | const navigate = useNavigate(); 7 | 8 | return navigate as { 9 | ( 10 | to: To, 11 | options?: { 12 | replace?: boolean; 13 | state?: LocationState; 14 | } 15 | ): void; 16 | (delta: number): void; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/assets/icons/generic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/background/redux/active-origin-favicon/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer } from 'typesafe-actions'; 2 | 3 | import { activeOriginFaviconChanged } from './actions'; 4 | import { ActiveOriginFaviconState } from './types'; 5 | 6 | const initialState: ActiveOriginFaviconState = null; 7 | 8 | export const reducer = createReducer( 9 | initialState 10 | ).handleAction(activeOriginFaviconChanged, (state, { payload }) => payload); 11 | -------------------------------------------------------------------------------- /src/apps/onboarding/router/use-typed-navigate.ts: -------------------------------------------------------------------------------- 1 | import { To, useNavigate } from 'react-router-dom'; 2 | 3 | import { LocationState } from './types'; 4 | 5 | export function useTypedNavigate() { 6 | const navigate = useNavigate(); 7 | 8 | return navigate as { 9 | ( 10 | to: To, 11 | options?: { 12 | replace?: boolean; 13 | state?: LocationState; 14 | } 15 | ): void; 16 | (delta: number): void; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/content/sdk-errors.ts: -------------------------------------------------------------------------------- 1 | export type SdkError = { 2 | message: string; 3 | code: number; 4 | }; 5 | 6 | const createAppError = (message: string, code: number): SdkError => { 7 | return { 8 | message, 9 | code 10 | }; 11 | }; 12 | 13 | export const WalletLockedError = () => createAppError('Wallet is locked.', 1); 14 | 15 | export const SiteNotConnectedError = () => 16 | createAppError('Active account not approved to connect with the site.', 2); 17 | -------------------------------------------------------------------------------- /src/apps/signature-request/router/use-typed-navigate.ts: -------------------------------------------------------------------------------- 1 | import { To, useNavigate } from 'react-router-dom'; 2 | 3 | import { LocationState } from './types'; 4 | 5 | export function useTypedNavigate() { 6 | const navigate = useNavigate(); 7 | 8 | return navigate as { 9 | ( 10 | to: To, 11 | options?: { 12 | replace?: boolean; 13 | state?: LocationState; 14 | } 15 | ): void; 16 | (delta: number): void; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/assets/icons/more.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/tick.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Casper Wallet.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | com.apple.security.network.client 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/background/open-window.ts: -------------------------------------------------------------------------------- 1 | // TODO: Remake it after background store will be complete 2 | import { 3 | OpenWindowProps, 4 | createOpenWindow 5 | } from '@background/create-open-window'; 6 | 7 | let windowId: number | null = null; 8 | 9 | export function openWindow(openWindowProps: OpenWindowProps) { 10 | createOpenWindow({ 11 | windowId, 12 | setWindowId: (id: number) => (windowId = id), 13 | clearWindowId: () => (windowId = null) 14 | })(openWindowProps); 15 | } 16 | -------------------------------------------------------------------------------- /src/apps/popup/hooks/use-force-update.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useState } from 'react'; 2 | 3 | export function useForceUpdate() { 4 | const [value, setValue] = useState(0); // integer state 5 | const forceUpdate = useCallback(() => { 6 | setValue(value => value + 1); // update state to force rerender 7 | // will reload forceUpdate so can be used as dependency 8 | // eslint-disable-next-line react-hooks/exhaustive-deps 9 | }, [value]); 10 | 11 | return forceUpdate; 12 | } 13 | -------------------------------------------------------------------------------- /src/assets/icons/moon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/background/redux/settings/types.ts: -------------------------------------------------------------------------------- 1 | import { NetworkSetting } from '@src/constants'; 2 | 3 | import { TimeoutDurationSetting } from '@popup/constants'; 4 | 5 | export enum ThemeMode { 6 | SYSTEM = 'System', 7 | DARK = 'Dark', 8 | LIGHT = 'Light' 9 | } 10 | 11 | export interface SettingsState { 12 | activeTimeoutDuration: TimeoutDurationSetting; 13 | activeNetwork: NetworkSetting; 14 | casperNetworkApiVersion: string; 15 | isDarkMode: boolean; 16 | themeMode: ThemeMode; 17 | } 18 | -------------------------------------------------------------------------------- /src/assets/icons/generic-contract-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/apps/import-account-with-file/router/use-typed-navigate.ts: -------------------------------------------------------------------------------- 1 | import { To, useNavigate } from 'react-router-dom'; 2 | 3 | import { LocationState } from '@import-account-with-file/router/types'; 4 | 5 | export function useTypedNavigate() { 6 | const navigate = useNavigate(); 7 | 8 | return navigate as { 9 | ( 10 | to: To, 11 | options?: { 12 | replace?: boolean; 13 | state?: LocationState; 14 | } 15 | ): void; 16 | (delta: number): void; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/assets/icons/chevron.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/libs/ui/components/maybe-link/maybe-link.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react'; 2 | 3 | import { Link } from '@libs/ui/components'; 4 | 5 | interface IMaybeLinkProps extends PropsWithChildren { 6 | link?: string | null; 7 | } 8 | 9 | export const MaybeLink: React.FC = ({ link, children }) => { 10 | return link ? ( 11 | 12 | {children} 13 | 14 | ) : ( 15 | children 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "arrowParens": "avoid", 4 | "trailingComma": "none", 5 | "importOrder": ["", "@src/*", "@popup/*", "@import-account-with-file/*", "@connect-to-app/*", "@signature-request/*", "@onboarding/*", "@background/*", "@hooks/*", "@content/*", "@libs/*", "^[./]"], 6 | "importOrderSeparation": true, 7 | "importOrderSortSpecifiers": true, 8 | "importOrderGroupNamespaceSpecifiers": true, 9 | "plugins": ["@trivago/prettier-plugin-sort-imports"] 10 | } -------------------------------------------------------------------------------- /src/assets/icons/chevron-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Please see the documentation for all configuration options: 2 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: "npm" 7 | directory: "/" 8 | schedule: 9 | interval: "daily" 10 | target-branch: "develop" 11 | # Specify labels for npm pull requests 12 | labels: 13 | - "dependencies" 14 | # Add reviewers 15 | reviewers: 16 | - "Comp0te" 17 | -------------------------------------------------------------------------------- /src/assets/icons/cross-in-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/placeholder-image-gray.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/background/redux/vault/types.ts: -------------------------------------------------------------------------------- 1 | import { Account } from '@libs/types/account'; 2 | 3 | type AccountNamesByOriginDict = Record; 4 | type SiteNameByOriginDict = Record; 5 | 6 | export type VaultState = { 7 | secretPhrase: null | string[]; 8 | accounts: Account[]; 9 | accountNamesByOriginDict: AccountNamesByOriginDict; 10 | siteNameByOriginDict: SiteNameByOriginDict; 11 | activeAccountName: string | null; 12 | jsonById: Record; 13 | }; 14 | -------------------------------------------------------------------------------- /src/assets/icons/ledger-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/background/redux/login-retry-lockout-time/selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSelector } from 'reselect'; 2 | import { RootState } from 'typesafe-actions'; 3 | 4 | import { LoginRetryLockoutTimeState } from './types'; 5 | 6 | export const selectLoginRetryLockoutTime = ( 7 | state: RootState 8 | ): LoginRetryLockoutTimeState => state.loginRetryLockoutTime; 9 | 10 | export const selectHasLoginRetryLockoutTime = createSelector( 11 | selectLoginRetryLockoutTime, 12 | loginRetryLockoutTime => loginRetryLockoutTime != null 13 | ); 14 | -------------------------------------------------------------------------------- /src/apps/connect-to-app/pages/connecting/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { HeaderPopup, LayoutWindow } from '@libs/layout'; 4 | 5 | import { ConnectingContent } from './content'; 6 | 7 | export interface ConnectingPageProps { 8 | origin: string; 9 | } 10 | 11 | export function ConnectingPage({ origin }: ConnectingPageProps) { 12 | return ( 13 | } 15 | renderContent={() => } 16 | /> 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/background/redux/root-saga.ts: -------------------------------------------------------------------------------- 1 | import { all } from 'redux-saga/effects'; 2 | 3 | import { watchCasper2NetworkSaga } from './sagas/check-casper2-network-saga'; 4 | import { onboardingSagas } from './sagas/onboarding-sagas'; 5 | import { trustedWasmSaga } from './sagas/trusted-wasm-saga'; 6 | import { vaultSagas } from './sagas/vault-sagas'; 7 | 8 | export default function* rootSaga() { 9 | yield all([ 10 | vaultSagas(), 11 | onboardingSagas(), 12 | watchCasper2NetworkSaga(), 13 | trustedWasmSaga() 14 | ]); 15 | } 16 | -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet Extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSExtension 6 | 7 | NSExtensionPointIdentifier 8 | com.apple.Safari.web-extension 9 | NSExtensionPrincipalClass 10 | $(PRODUCT_MODULE_NAME).SafariWebExtensionHandler 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/background/redux/last-activity-time/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer } from 'typesafe-actions'; 2 | 3 | import { vaultUnlocked } from '../session/actions'; 4 | import { lastActivityTimeRefreshed } from './actions'; 5 | 6 | export type LastActivityTimeState = number | null; 7 | 8 | const initialState = null as LastActivityTimeState; 9 | 10 | export const reducer = createReducer(initialState).handleAction( 11 | [lastActivityTimeRefreshed, vaultUnlocked], 12 | (state, { payload: { lastActivityTime } }) => lastActivityTime 13 | ); 14 | -------------------------------------------------------------------------------- /src/background/redux/vault-cipher/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer } from 'typesafe-actions'; 2 | 3 | import { vaultCipherCreated, vaultCipherReseted } from './actions'; 4 | import { VaultCipherState } from './types'; 5 | 6 | const initialState = null as VaultCipherState; 7 | 8 | export const reducer = createReducer(initialState) 9 | .handleAction(vaultCipherReseted, (): VaultCipherState => initialState) 10 | .handleAction( 11 | vaultCipherCreated, 12 | (state, action): VaultCipherState => action.payload.vaultCipher 13 | ); 14 | -------------------------------------------------------------------------------- /utils/build.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | 3 | const { NODE_ENV } = require('./env'); 4 | const config = require('../webpack.config'); 5 | 6 | const { cleanUpBuildDir } = require('./build-dir-utils'); 7 | 8 | // Do this as the first thing so that any code reading it knows the right env. 9 | process.env.ASSET_PATH = '/'; 10 | 11 | delete config.chromeExtensionBoilerplate; 12 | 13 | config.mode = NODE_ENV || 'development'; 14 | 15 | cleanUpBuildDir(); 16 | webpack(config, function (err) { 17 | if (err) throw err; 18 | }); 19 | -------------------------------------------------------------------------------- /src/assets/icons/cross.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/background/redux/contacts/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | import { 4 | Contact, 5 | EditContactActionType 6 | } from '@background/redux/contacts/types'; 7 | 8 | export const newContactAdded = createAction('NEW_CONTACT_ADDED')(); 9 | 10 | export const contactRemoved = createAction('CONTACT_REMOVED')(); 11 | 12 | export const contactUpdated = 13 | createAction('CONTACT_UPDATED')(); 14 | 15 | export const contactsReseted = createAction('CONTACTS_RESETED')(); 16 | -------------------------------------------------------------------------------- /src/background/redux/login-retry-lockout-time/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer } from 'typesafe-actions'; 2 | 3 | import { 4 | loginRetryLockoutTimeReseted, 5 | loginRetryLockoutTimeSet 6 | } from './actions'; 7 | import { LoginRetryLockoutTimeState } from './types'; 8 | 9 | const initialState = null as LoginRetryLockoutTimeState; 10 | 11 | export const reducer = createReducer(initialState) 12 | .handleAction(loginRetryLockoutTimeReseted, () => initialState) 13 | .handleAction(loginRetryLockoutTimeSet, (state, action) => action.payload); 14 | -------------------------------------------------------------------------------- /src/assets/icons/edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/background/redux/ledger/selectors.ts: -------------------------------------------------------------------------------- 1 | import { RootState } from 'typesafe-actions'; 2 | 3 | export const selectLedgerNewWindowId = (state: RootState): number | null => 4 | state.ledger.windowId; 5 | 6 | export const selectLedgerDeploy = (state: RootState): string | null => 7 | state.ledger.deploy; 8 | 9 | export const selectLedgerTransaction = (state: RootState): string | null => 10 | state.ledger.transaction; 11 | 12 | export const selectLedgerRecipientToSaveOnSuccess = ( 13 | state: RootState 14 | ): string | null => state.ledger.recipientToSaveOnSuccess; 15 | -------------------------------------------------------------------------------- /src/background/send-sdk-response-to-specific-tab.ts: -------------------------------------------------------------------------------- 1 | import { tabs } from 'webextension-polyfill'; 2 | 3 | import { SdkMethod } from '@content/sdk-method'; 4 | 5 | // TODO: should use tab id to send back to specific tab 6 | export async function sendSdkResponseToSpecificTab(action: SdkMethod) { 7 | const tabsList = await tabs.query({ 8 | active: true 9 | }); 10 | 11 | tabsList.forEach(async tab => { 12 | if (tab.id) { 13 | tabs.sendMessage(tab.id, action); 14 | } else { 15 | throw Error('Tab without id: ' + tab); 16 | } 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /src/assets/icons/nft-contract-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/icons/tick-in-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Casper Wallet 4 | // 5 | // Created by Piotrek Witek on 19/04/2023. 6 | // 7 | 8 | import Cocoa 9 | 10 | @main 11 | class AppDelegate: NSObject, NSApplicationDelegate { 12 | 13 | func applicationDidFinishLaunching(_ notification: Notification) { 14 | // Override point for customization after application launch. 15 | } 16 | 17 | func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 18 | return true 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/assets/icons/cep-18-default.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/cep18-contract-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/background/redux/login-retry-count/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer } from 'typesafe-actions'; 2 | 3 | import { loginRetryCountIncremented, loginRetryCountReseted } from './actions'; 4 | 5 | export type LoginRetryCountState = number; 6 | 7 | const initialState = 0 as LoginRetryCountState; 8 | 9 | export const reducer = createReducer(initialState) 10 | .handleAction( 11 | loginRetryCountReseted, 12 | (): LoginRetryCountState => initialState 13 | ) 14 | .handleAction( 15 | loginRetryCountIncremented, 16 | (state): LoginRetryCountState => state + 1 17 | ); 18 | -------------------------------------------------------------------------------- /src/libs/ui/components/skeleton/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SkeletonLib, { SkeletonProps } from 'react-loading-skeleton'; 3 | import { useTheme } from 'styled-components'; 4 | 5 | export const Skeleton: React.FC = ({ 6 | baseColor, 7 | highlightColor, 8 | ...props 9 | }) => { 10 | const theme = useTheme(); 11 | 12 | return ( 13 | 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /src/assets/icons/radio-button-on.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/libs/ui/forms/get-submit-button-state-from-validation.ts: -------------------------------------------------------------------------------- 1 | interface Props { 2 | isValid?: boolean; 3 | isDirty?: boolean; 4 | isSubmitting?: boolean; 5 | } 6 | 7 | export function calculateSubmitButtonDisabled({ 8 | isValid, 9 | isDirty, 10 | isSubmitting 11 | }: Props) { 12 | if (isDirty != null && isValid != null) { 13 | return !isValid || !isDirty || isSubmitting; 14 | } 15 | 16 | if (isDirty != null) { 17 | return !isDirty || isSubmitting; 18 | } 19 | 20 | if (isValid != null) { 21 | return !isValid || isSubmitting; 22 | } 23 | 24 | return false; 25 | } 26 | -------------------------------------------------------------------------------- /src/hooks/use-is-dark-mode.ts: -------------------------------------------------------------------------------- 1 | import { useSelector } from 'react-redux'; 2 | 3 | import { selectThemeModeSetting } from '@background/redux/settings/selectors'; 4 | import { ThemeMode } from '@background/redux/settings/types'; 5 | 6 | import { useSystemThemeDetector } from '@hooks/use-system-theme-detector'; 7 | 8 | export const useIsDarkMode = () => { 9 | const themeMode = useSelector(selectThemeModeSetting); 10 | 11 | const isSystemDarkTheme = useSystemThemeDetector(); 12 | 13 | return themeMode === ThemeMode.SYSTEM 14 | ? isSystemDarkTheme 15 | : themeMode === ThemeMode.DARK; 16 | }; 17 | -------------------------------------------------------------------------------- /src/background/redux/session/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | export const sessionReseted = createAction('SESSION_RESETED')(); 4 | 5 | export const encryptionKeyHashCreated = createAction( 6 | 'ENCRYPTION_KEY_HASH_CREATED' 7 | )<{ 8 | encryptionKeyHash: string; 9 | }>(); 10 | 11 | export const vaultUnlocked = createAction('VAULT_UNLOCKED', () => ({ 12 | lastActivityTime: Date.now() 13 | }))<{ 14 | lastActivityTime: number; 15 | }>(); 16 | 17 | export const contactEditingPermissionChanged = createAction( 18 | 'CONTACT_EDITING_PERMISSION_CHANGED' 19 | )(); 20 | -------------------------------------------------------------------------------- /src/hooks/use-async-effect.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | 3 | /** onResult called only if mounted */ 4 | export const useAsyncEffect = ( 5 | func: () => Promise, 6 | onResult: (p: T) => void, 7 | deps: any[] 8 | ) => { 9 | useEffect(() => { 10 | let mounted = true; 11 | 12 | (async () => { 13 | const resp = await func(); 14 | 15 | if (mounted) { 16 | onResult(resp); 17 | } 18 | })(); 19 | 20 | return () => { 21 | mounted = false; 22 | }; 23 | // eslint-disable-next-line react-hooks/exhaustive-deps 24 | }, deps); 25 | }; 26 | -------------------------------------------------------------------------------- /src/background/redux/trusted-wasm/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | export const addWasmToTrusted = createAction('ADD_WASM_TO_TRUSTED')<{ 4 | origin: string; 5 | wasmHash: string; 6 | }>(); 7 | 8 | export const removeWasmFromTrusted = createAction('REMOVE_WASM_FROM_TRUSTED')<{ 9 | origin: string; 10 | wasmHash: string; 11 | }>(); 12 | 13 | export const removeAllWasmFromTrustedOrigin = createAction( 14 | 'REMOVE_ALL_WASM_FROM_TRUSTED_ORIGIN' 15 | )<{ 16 | origin: string; 17 | }>(); 18 | 19 | export const resetTrustedWasmState = createAction('RESET_TRUSTED_WASM_STATE')(); 20 | -------------------------------------------------------------------------------- /src/apps/popup/router/types.ts: -------------------------------------------------------------------------------- 1 | import { IAppMarketingEvent, IDeploy } from 'casper-wallet-core'; 2 | 3 | import { NFTData } from '@popup/pages/transfer-nft/utils'; 4 | 5 | import { TokenType } from '@hooks/use-casper-token'; 6 | 7 | import { ErrorLocationState } from '@libs/layout/error/types'; 8 | 9 | export interface LocationState extends ErrorLocationState { 10 | showNavigationMenu?: boolean; 11 | activeTabId?: number; 12 | tokenData?: TokenType | null; 13 | nftData?: NFTData; 14 | recipientPublicKey?: string; 15 | deploy?: IDeploy; 16 | navigateHome?: boolean; 17 | appEvent?: IAppMarketingEvent; 18 | } 19 | -------------------------------------------------------------------------------- /src/background/bring-web3-events.ts: -------------------------------------------------------------------------------- 1 | import { ActionType, createAction } from 'typesafe-actions'; 2 | 3 | export const bringWeb3Events = { 4 | getActivePublicKey: createAction('GET_ACTIVE_PUBLIC_KEY')(), 5 | getActivePublicKeyResponse: createAction('GET_ACTIVE_PUBLIC_KEY_RESPONSE')<{ 6 | publicKey: string; 7 | }>(), 8 | promptLoginRequest: createAction('PROMPT_LOGIN_REQUEST')(), 9 | getTheme: createAction('GET_THEME')(), 10 | getThemeResponse: createAction('GET_THEME_RESPONSE')<{ 11 | theme: 'light' | 'dark'; 12 | }>() 13 | }; 14 | 15 | export type BringWeb3Events = ActionType; 16 | -------------------------------------------------------------------------------- /src/background/redux/keys/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer } from 'typesafe-actions'; 2 | 3 | import { keysReseted, keysUpdated } from './actions'; 4 | import { KeysState } from './types'; 5 | 6 | type State = KeysState; 7 | 8 | const initialState: State = { 9 | passwordHash: null, 10 | passwordSaltHash: null, 11 | keyDerivationSaltHash: null 12 | }; 13 | 14 | export const reducer = createReducer(initialState) 15 | .handleAction( 16 | keysUpdated, 17 | (state, action): State => ({ 18 | ...state, 19 | ...action.payload 20 | }) 21 | ) 22 | .handleAction(keysReseted, () => initialState); 23 | -------------------------------------------------------------------------------- /src/apps/onboarding/pages/select-accounts-to-recover/utils.ts: -------------------------------------------------------------------------------- 1 | import { deriveKeyPair } from '@libs/crypto'; 2 | import { KeyPair } from '@libs/types/account'; 3 | 4 | export interface KeyPairOptions { 5 | size: number; 6 | offset: number; 7 | secretPhrase: string[]; 8 | } 9 | 10 | export const getKeyPairList = ({ 11 | size, 12 | offset, 13 | secretPhrase 14 | }: KeyPairOptions) => { 15 | const keyPairs: KeyPair[] = []; 16 | 17 | for (let i = 0; i < size; i++) { 18 | const keyPair = deriveKeyPair(secretPhrase, offset + i); 19 | 20 | keyPairs.push(keyPair); 21 | } 22 | 23 | return keyPairs; 24 | }; 25 | -------------------------------------------------------------------------------- /src/background/redux/ledger/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | export const ledgerNewWindowIdChanged = createAction( 4 | 'LEDGER_NEW_WINDOW_ID_CHANGED' 5 | )(); 6 | export const ledgerDeployChanged = createAction( 7 | 'LEDGER_DEPLOY_CHANGED' 8 | )(); 9 | export const ledgerTransactionChanged = createAction( 10 | 'LEDGER_TRANSACTION_CHANGED' 11 | )(); 12 | export const ledgerRecipientToSaveOnSuccessChanged = createAction( 13 | 'LEDGER_RECIPIENT_TO_SAVE_ON_SUCCESS_CHANGED' 14 | )(); 15 | export const ledgerStateCleared = createAction('LEDGER_STATE_CLEARED')(); 16 | -------------------------------------------------------------------------------- /src/libs/layout/error/index.ts: -------------------------------------------------------------------------------- 1 | import { ErrorLocationState } from './types'; 2 | 3 | export const ErrorPath = '/error'; 4 | 5 | export function createErrorLocationState({ 6 | errorHeaderText, 7 | errorContentText, 8 | errorPrimaryButtonLabel, 9 | errorRedirectPath 10 | }: Required) { 11 | return { 12 | state: { 13 | errorHeaderText, 14 | errorContentText, 15 | errorPrimaryButtonLabel, 16 | errorRedirectPath 17 | } 18 | }; 19 | } 20 | 21 | export * from './error-boundary'; 22 | export * from './tab-page'; 23 | export * from './types'; 24 | export * from './window-page'; 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | # See https://help.github.com/ignore-files/ for more about ignoring files. 4 | 5 | # dependencies 6 | node_modules 7 | 8 | # testing 9 | coverage 10 | test-artifacts 11 | 12 | # development 13 | output 14 | 15 | # production 16 | build 17 | web-ext-artifacts 18 | 19 | # misc 20 | .DS_Store 21 | .env.local 22 | .env.development.local 23 | .env.test.local 24 | .env.production.local 25 | .history 26 | xcuserdata 27 | 28 | # secrets 29 | secrets.*.js 30 | 31 | # packed extension 32 | chrome.crx 33 | /test-results/ 34 | /playwright-report/ 35 | /blob-report/ 36 | /playwright/.cache/ 37 | 38 | # env 39 | .env -------------------------------------------------------------------------------- /src/assets/icons/books.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/background/redux/trusted-wasm/selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSelector } from 'reselect'; 2 | import { RootState } from 'typesafe-actions'; 3 | 4 | import { selectActiveOrigin } from '../active-origin/selectors'; 5 | 6 | export const selectTrustedWasmByOriginDict = (state: RootState) => 7 | state.trustedWasm.hashesByOriginDict; 8 | 9 | export const selectTrustedWasmForActiveOrigin = createSelector( 10 | selectActiveOrigin, 11 | selectTrustedWasmByOriginDict, 12 | (origin, hashesByOriginDict) => 13 | origin != null && (hashesByOriginDict?.[origin] || []).length > 0 14 | ? hashesByOriginDict[origin] 15 | : [] 16 | ); 17 | -------------------------------------------------------------------------------- /src/background/workers/verify-password-worker.ts: -------------------------------------------------------------------------------- 1 | import { verifyPasswordAgainstHash } from '@libs/crypto/hashing'; 2 | 3 | interface VerifyPasswordEvent extends MessageEvent { 4 | data: { 5 | passwordHash: string; 6 | passwordSaltHash: string; 7 | password: string; 8 | }; 9 | } 10 | 11 | onmessage = async (event: VerifyPasswordEvent) => { 12 | const { passwordHash, passwordSaltHash, password } = event.data; 13 | const isPasswordCorrect = await verifyPasswordAgainstHash( 14 | passwordHash, 15 | passwordSaltHash, 16 | password 17 | ); 18 | 19 | postMessage({ 20 | isPasswordCorrect 21 | }); 22 | }; 23 | -------------------------------------------------------------------------------- /src/assets/icons/burger-menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/checkbox-square-checked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/background/redux/contacts/selectors.ts: -------------------------------------------------------------------------------- 1 | import { RootState } from 'typesafe-actions'; 2 | 3 | export const selectAllContacts = (state: RootState) => state.contacts.contacts; 4 | 5 | export const selectCountOfContacts = (state: RootState) => 6 | state.contacts.contacts.length; 7 | 8 | export const selectAllContactsNames = (state: RootState) => 9 | state.contacts.contacts.map(contact => contact.name); 10 | 11 | export const selectAllContactsPublicKeys = (state: RootState) => 12 | state.contacts.contacts.map(contact => contact.publicKey); 13 | 14 | export const selectLastModified = (state: RootState) => 15 | state.contacts.lastModified; 16 | -------------------------------------------------------------------------------- /src/background/redux/keys/selectors.ts: -------------------------------------------------------------------------------- 1 | import { RootState } from 'typesafe-actions'; 2 | 3 | export const selectKeysDoesExist = (state: RootState): boolean => 4 | state.keys.passwordHash != null && 5 | state.keys.passwordSaltHash != null && 6 | state.keys.keyDerivationSaltHash != null; 7 | 8 | export const selectPasswordHash = (state: RootState): string | null => 9 | state.keys.passwordHash; 10 | 11 | export const selectPasswordSaltHash = (state: RootState): string | null => 12 | state.keys.passwordSaltHash; 13 | 14 | export const selectKeyDerivationSaltHash = (state: RootState): string | null => 15 | state.keys.keyDerivationSaltHash; 16 | -------------------------------------------------------------------------------- /src/background/redux/windowManagement/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | export const windowIdChanged = createAction('WINDOW_ID_CHANGED')(); 4 | export const windowIdCleared = createAction('WINDOW_ID_CLEARED')(); 5 | export const onboardingAppInit = createAction('ONBOARDING_APP_INIT')(); 6 | export const popupWindowInit = createAction('POPUP_WINDOW_INIT')(); 7 | export const connectWindowInit = createAction('CONNECT_WINDOW_INIT')(); 8 | export const importWindowInit = createAction('IMPORT_WINDOW_INIT')(); 9 | export const signWindowInit = createAction('SIGN_WINDOW_INIT')(); 10 | -------------------------------------------------------------------------------- /src/assets/icons/show.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /scripts/locale_extract_pot.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { readFileSync, writeFileSync } = require('fs'); 3 | const { i18nextToPot } = require('i18next-conv'); 4 | 5 | const options = { 6 | project: 'casper-signer-v2', 7 | ctxSeparator: false, 8 | keyseparator: false, 9 | foldLength: 90 10 | }; 11 | 12 | function save(target) { 13 | return result => { 14 | writeFileSync(target, result); 15 | }; 16 | } 17 | 18 | const source = path.join(__dirname, '../lang/casper-signer-v2.json'); 19 | const target = path.join(__dirname, '../lang/casper-signer-v2.pot'); 20 | 21 | i18nextToPot('en', readFileSync(source), options).then(save(target)); 22 | -------------------------------------------------------------------------------- /src/apps/popup/pages/all-accounts/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { 4 | HeaderPopup, 5 | HeaderSubmenuBarNavLink, 6 | PopupLayout 7 | } from '@libs/layout'; 8 | 9 | import { AllAccountsContent } from './content'; 10 | 11 | export const AllAccountsPage = () => ( 12 | ( 14 | ( 19 | 20 | )} 21 | /> 22 | )} 23 | renderContent={() => } 24 | /> 25 | ); 26 | -------------------------------------------------------------------------------- /src/background/wallet-repositories.ts: -------------------------------------------------------------------------------- 1 | import { setupRepositories } from 'casper-wallet-core'; 2 | 3 | const { 4 | deploysRepository, 5 | accountInfoRepository, 6 | tokensRepository, 7 | nftsRepository, 8 | validatorsRepository, 9 | onRampRepository, 10 | appEventsRepository, 11 | txSignatureRequestRepository, 12 | contractPackageRepository 13 | } = setupRepositories(); 14 | 15 | export { 16 | deploysRepository, 17 | accountInfoRepository, 18 | tokensRepository, 19 | nftsRepository, 20 | validatorsRepository, 21 | onRampRepository, 22 | appEventsRepository, 23 | txSignatureRequestRepository, 24 | contractPackageRepository 25 | }; 26 | -------------------------------------------------------------------------------- /src/libs/layout/header/header-submenu-bar-buy-cspr-link.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Trans, useTranslation } from 'react-i18next'; 3 | 4 | import { RouterPath, useTypedNavigate } from '@popup/router'; 5 | 6 | import { Link, Typography } from '@libs/ui/components'; 7 | 8 | export const HeaderSubmenuBarBuyCSPRLink = () => { 9 | const { t } = useTranslation(); 10 | const navigate = useTypedNavigate(); 11 | 12 | return ( 13 | navigate(RouterPath.BuyCSPR)}> 14 | 15 | Buy CSPR 16 | 17 | 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /src/libs/crypto/bip39.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | decodeSeed, 3 | encodeSeed, 4 | generateSecretPhrase, 5 | validateSecretPhrase 6 | } from './bip39'; 7 | 8 | describe('bip39', () => { 9 | const originalMnemonic = generateSecretPhrase(); 10 | 11 | it('should generate valid mnemonic', () => { 12 | expect(validateSecretPhrase(originalMnemonic)).toBeTruthy(); 13 | expect(originalMnemonic).toHaveLength(24); 14 | }); 15 | 16 | it('should encode and decode entropy correctly', () => { 17 | const entropy = encodeSeed(originalMnemonic); 18 | const mnemonic = decodeSeed(entropy); 19 | expect(mnemonic).toStrictEqual(originalMnemonic); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /jest.e2e.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: [''], 3 | preset: 'ts-jest/presets/js-with-ts', 4 | transform: { 5 | '^.+\\.ts$': ['ts-jest', { tsconfig: '/jest.tsconfig.json' }] 6 | }, 7 | clearMocks: true, 8 | moduleDirectories: ['node_modules'], 9 | coveragePathIgnorePatterns: ['/node_modules/'], 10 | testRegex: '(/tests?/.*(test|spec))\\.tsx?$', 11 | moduleFileExtensions: ['js', 'ts'], 12 | modulePathIgnorePatterns: ['/.*/__mocks__', '/src'], 13 | testEnvironment: 'jsdom', 14 | setupFilesAfterEnv: ['@testing-library/jest-dom'], 15 | testTimeout: 1000 * 60, 16 | testPathIgnorePatterns: ['e2e-tests/'] 17 | }; 18 | -------------------------------------------------------------------------------- /src/apps/onboarding/router/paths.ts: -------------------------------------------------------------------------------- 1 | export enum RouterPath { 2 | Any = '*', 3 | Welcome = '/', 4 | CreateVaultPassword = '/create-vault-password', 5 | CreateSecretPhrase = '/create-secret-phrase', 6 | RecoverFromSecretPhrase = '/recover-from-secret-phrase', 7 | CreateSecretPhraseConfirmation = '/create-secret-phrase-confirmation', 8 | WriteDownSecretPhrase = '/write-down-secret-phrase', 9 | ConfirmSecretPhrase = '/confirm-secret-phrase', 10 | ConfirmSecretPhraseSuccess = '/confirm-secret-phrase-success', 11 | OnboardingSuccess = '/onboarding-success', 12 | ResetWallet = '/reset-wallet', 13 | SelectAccountsToRecover = '/select-accounts-to-recover' 14 | } 15 | -------------------------------------------------------------------------------- /src/assets/icons/burn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/background/redux/recent-recipient-public-keys/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer } from 'typesafe-actions'; 2 | 3 | import { recipientPublicKeyAdded, recipientPublicKeyReseted } from './actions'; 4 | import { RecentRecipientPublicKeysState } from './types'; 5 | 6 | const initialState = [] as RecentRecipientPublicKeysState; 7 | 8 | export const reducer = createReducer(initialState) 9 | .handleAction(recipientPublicKeyReseted, () => initialState) 10 | .handleAction( 11 | recipientPublicKeyAdded, 12 | // This is a hack to make sure the most recent recipient is always at the top of the list. 13 | (state, action) => [...new Set([action.payload, ...state])] 14 | ); 15 | -------------------------------------------------------------------------------- /src/libs/services/nft-service/use-fetch-derive-media-type.ts: -------------------------------------------------------------------------------- 1 | import { useQuery } from '@tanstack/react-query'; 2 | import { Maybe } from 'casper-wallet-core/src/typings/common'; 3 | 4 | import { nftsRepository } from '@background/wallet-repositories'; 5 | 6 | export const useFetchDeriveMediaType = (url?: Maybe) => { 7 | const { data: contentType, isFetching: isLoadingMediaType } = useQuery({ 8 | queryKey: ['deriveNftMediaType', url], 9 | queryFn: () => nftsRepository.deriveNftMediaType(url || '', false), 10 | enabled: Boolean(url), 11 | refetchInterval: false 12 | }); 13 | 14 | return { 15 | contentType, 16 | isLoadingMediaType 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /src/assets/icons/torus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/libs/ui/utils/hex-to-rgba.ts: -------------------------------------------------------------------------------- 1 | // https://css-tricks.com/converting-color-spaces-in-javascript/#aa-hex-to-rgb 2 | export function hexToRGBA(hex: string, alpha: string) { 3 | let red = null; 4 | let green = null; 5 | let blue = null; 6 | 7 | // 3 digits 8 | if (hex.length === 4) { 9 | red = '0x' + hex[1] + hex[1]; 10 | green = '0x' + hex[2] + hex[2]; 11 | blue = '0x' + hex[3] + hex[3]; 12 | 13 | // 6 digits 14 | } else if (hex.length === 7) { 15 | red = '0x' + hex[1] + hex[2]; 16 | green = '0x' + hex[3] + hex[4]; 17 | blue = '0x' + hex[5] + hex[6]; 18 | } 19 | 20 | return `rgba(${Number(red)}, ${Number(green)}, ${Number(blue)}, ${alpha})`; 21 | } 22 | -------------------------------------------------------------------------------- /src/background/redux/windowManagement/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer } from 'typesafe-actions'; 2 | 3 | import { windowIdChanged, windowIdCleared } from './actions'; 4 | import { WindowManagementState } from './types'; 5 | 6 | type State = WindowManagementState; 7 | 8 | const initialState: State = { 9 | windowId: null 10 | }; 11 | 12 | export const reducer = createReducer(initialState) 13 | .handleAction( 14 | windowIdChanged, 15 | (state, { payload }): State => ({ 16 | ...state, 17 | windowId: payload 18 | }) 19 | ) 20 | .handleAction( 21 | windowIdCleared, 22 | (state): State => ({ 23 | ...state, 24 | windowId: null 25 | }) 26 | ); 27 | -------------------------------------------------------------------------------- /src/apps/popup/constants.ts: -------------------------------------------------------------------------------- 1 | export enum TimeoutDurationSetting { 2 | '1 min' = '1 min', 3 | '5 min' = '5 min', 4 | '15 min' = '15 min', 5 | '30 min' = '30 min', 6 | '1 hour' = '1 hour', 7 | '24 hours' = '24 hours' 8 | } 9 | 10 | export const MapTimeoutDurationSettingToValue = { 11 | [TimeoutDurationSetting['1 min']]: 1000 * 60 * 1, 12 | [TimeoutDurationSetting['5 min']]: 1000 * 60 * 5, 13 | [TimeoutDurationSetting['15 min']]: 1000 * 60 * 15, 14 | [TimeoutDurationSetting['30 min']]: 1000 * 60 * 30, 15 | [TimeoutDurationSetting['1 hour']]: 1000 * 60 * 60, 16 | [TimeoutDurationSetting['24 hours']]: 1000 * 60 * 60 * 24 17 | }; 18 | 19 | export const LOCK_VAULT_TIMEOUT = 1000 * 60 * 5; 20 | -------------------------------------------------------------------------------- /src/assets/icons/contact.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/background/redux/rate-app/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Interface representing the structure of the RateAppState. 3 | * 4 | * @interface 5 | * @exports 6 | * @name RateAppState 7 | */ 8 | export interface RateAppState { 9 | /** 10 | * Indicates whether the application was rated in the store. 11 | * 12 | * @type {boolean} 13 | * @name ratedInStore 14 | */ 15 | ratedInStore: boolean; 16 | 17 | /** 18 | * Stores the timestamp after which the user 19 | * will be asked for a rate app. 20 | * 21 | * Is null if the user has never been asked for a rate app. 22 | * 23 | * @type {number | null} 24 | * @name askForReviewAfter 25 | */ 26 | askForReviewAfter: number | null; 27 | } 28 | -------------------------------------------------------------------------------- /src/libs/types/account.ts: -------------------------------------------------------------------------------- 1 | import { CasperWalletSupports } from '@content/sdk-types'; 2 | 3 | export interface KeyPair { 4 | secretKey: string; // can be empty string 5 | publicKey: string; 6 | } 7 | export interface Account extends KeyPair { 8 | name: string; 9 | imported?: boolean; 10 | hardware?: HardwareWalletType; 11 | hidden: boolean; 12 | derivationIndex?: number; 13 | supports?: CasperWalletSupports[]; 14 | } 15 | 16 | export enum HardwareWalletType { 17 | Ledger = 'Ledger' 18 | } 19 | 20 | export interface AccountListRows extends Account { 21 | id: string; 22 | } 23 | 24 | export type AccountListRowWithAccountHash = T & { 25 | accountHash: string; 26 | }; 27 | -------------------------------------------------------------------------------- /src/assets/icons/close-filter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/hooks/use-system-theme-detector.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | export const useSystemThemeDetector = () => { 4 | const getCurrentTheme = () => 5 | window.matchMedia('(prefers-color-scheme: dark)').matches; 6 | const [isDarkTheme, setIsDarkTheme] = useState(getCurrentTheme()); 7 | 8 | const mqListener = (e: MediaQueryListEvent) => { 9 | setIsDarkTheme(e.matches); 10 | }; 11 | 12 | useEffect(() => { 13 | const darkThemeMq = window.matchMedia('(prefers-color-scheme: dark)'); 14 | 15 | darkThemeMq.addEventListener('change', mqListener); 16 | 17 | return () => darkThemeMq.removeEventListener('change', mqListener); 18 | }, []); 19 | 20 | return isDarkTheme; 21 | }; 22 | -------------------------------------------------------------------------------- /src/assets/icons/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/burger-close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/background/background-events.ts: -------------------------------------------------------------------------------- 1 | import { ActionType, createAction } from 'typesafe-actions'; 2 | 3 | import { PopupState } from './redux/types'; 4 | 5 | // General purpose events emitted by background to all extension windows 6 | 7 | export const backgroundEvent = { 8 | popupStateUpdated: createAction('popupStateUpdated')() 9 | }; 10 | 11 | export type BackgroundEvent = ActionType; 12 | export type popupStateUpdated = ActionType< 13 | typeof backgroundEvent.popupStateUpdated 14 | >; 15 | 16 | export function isBackgroundEvent(action?: { 17 | type?: unknown; 18 | meta?: unknown; 19 | }): action is BackgroundEvent { 20 | return typeof action?.type === 'string' && action.meta === undefined; 21 | } 22 | -------------------------------------------------------------------------------- /src/libs/services/deployer-service/types.ts: -------------------------------------------------------------------------------- 1 | export interface RPCResponse { 2 | api_version: string; 3 | deploy_hash: string; 4 | } 5 | 6 | export interface RPCErrorResponse { 7 | code: number; 8 | message: string; 9 | data: string | any; 10 | } 11 | 12 | export interface ICasperNodeStatusResponse { 13 | last_progress: string; 14 | } 15 | 16 | export interface ICasperNetworkSendDeployResponse { 17 | jsonrpc: '2.0'; 18 | id: number; 19 | result: { 20 | api_version: string; 21 | deploy_hash: string; 22 | }; 23 | } 24 | 25 | export interface ICasperNetworkSendDeployErrorResponse { 26 | error: { 27 | code: number; 28 | data: string; 29 | message: string; 30 | }; 31 | id: number; 32 | jsonrpc: string; 33 | } 34 | -------------------------------------------------------------------------------- /src/assets/icons/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/background/close-current-window.ts: -------------------------------------------------------------------------------- 1 | import { windows } from 'webextension-polyfill'; 2 | 3 | // import { getWindowId } from '@background/redux/import-account-actions-should-be-removed'; 4 | 5 | export async function closeCurrentWindow() { 6 | try { 7 | // const windowId = await getWindowId(); 8 | // if (windowId) { 9 | // await browser.windows.remove(windowId); 10 | // } else { 11 | // If there is no windowId in the state it'll fallback to use currentWindow id to close 12 | const currentWindow = await windows.getCurrent(); 13 | if (currentWindow.type === 'popup' && currentWindow.id) { 14 | await windows.remove(currentWindow.id); 15 | } 16 | // } 17 | } catch (error) { 18 | throw error; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/libs/crypto/aes.ts: -------------------------------------------------------------------------------- 1 | import * as aes from 'micro-aes-gcm/index'; 2 | 3 | import { 4 | convertBase64ToBytes, 5 | convertBytesToBase64, 6 | convertHexToBytes 7 | } from './utils'; 8 | 9 | export async function aesEncryptString( 10 | keyHash: string, 11 | str: string 12 | ): Promise { 13 | const key = convertHexToBytes(keyHash); 14 | const bytes = await aes.encrypt(key, str); 15 | return convertBytesToBase64(bytes); 16 | } 17 | 18 | export async function aesDecryptString( 19 | keyHash: string, 20 | cipherBase64: string 21 | ): Promise { 22 | const key = convertHexToBytes(keyHash); 23 | const jsonBytes = await aes.decrypt(key, convertBase64ToBytes(cipherBase64)); 24 | return aes.utils.bytesToUtf8(jsonBytes); 25 | } 26 | -------------------------------------------------------------------------------- /src/background/redux/app-events/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer } from 'typesafe-actions'; 2 | 3 | import { 4 | dismissAppEvent, 5 | resetAppEventsDismission 6 | } from '@background/redux/app-events/actions'; 7 | 8 | import { AppEventsState } from './types'; 9 | 10 | const initialState: AppEventsState = { 11 | dismissedEventIds: [] 12 | }; 13 | 14 | export const reducer = createReducer(initialState) 15 | .handleAction( 16 | dismissAppEvent, 17 | (state: AppEventsState, action: ReturnType) => ({ 18 | ...state, 19 | dismissedEventIds: [ 20 | ...new Set([...state.dismissedEventIds, action.payload]) 21 | ] 22 | }) 23 | ) 24 | .handleAction(resetAppEventsDismission, () => initialState); 25 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | _**Make sure to fill in all the below sections.**_ 2 | 3 | ## Description 4 | 5 | // Provide a useful description of this PR, aimed at helping reviewers and contributors 6 | 7 | ## Linked tickets 8 | 9 | [WALLET-XXX](https://make-software.atlassian.net/browse/WALLET-XXX) 10 | 11 | ## Checklist 12 | 13 | - [ ] Make sure this PR title follows semantic release conventions: 14 | 15 | - [ ] If the PR adds any new text to the UI, make sure they are localized 16 | 17 | - [ ] Include a screenshot or recording if implementing significant UI or user flow change 18 | 19 | - [ ] When this PR affects architecture changes wait for review from Dmytro before merging 20 | -------------------------------------------------------------------------------- /src/apps/onboarding/hooks/use-onboarding-form-state.ts: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | import { SecretPhrase } from '@libs/crypto'; 4 | 5 | export interface FormState { 6 | secretPhrase: SecretPhrase | null; 7 | } 8 | 9 | export type SetFormState = (name: keyof FormState, value: any) => void; 10 | export const useOnboardingFormState = () => { 11 | const [onboardingFormState, setOnboardingFormState] = useState({ 12 | secretPhrase: null 13 | }); 14 | 15 | const setFormState: SetFormState = (name, value) => 16 | setOnboardingFormState(prevOnboardingFormState => ({ 17 | ...prevOnboardingFormState, 18 | [name]: value 19 | })); 20 | 21 | return { 22 | onboardingFormState, 23 | setFormState 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /src/assets/icons/secure.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/libs/services/buy-cspr-service/use-get-on-ramp-providers.ts: -------------------------------------------------------------------------------- 1 | import { useMutation } from '@tanstack/react-query'; 2 | 3 | import { onRampRepository } from '@background/wallet-repositories'; 4 | 5 | export const useGetOnRampProviders = () => { 6 | const { mutate: getOnRampProviders, isPending: isProvidersLoading } = 7 | useMutation({ 8 | mutationFn: onRampRepository.getOnRampProviders 9 | }); 10 | 11 | const { 12 | mutate: getOnRampProviderLocation, 13 | isPending: isProviderLocationLoading 14 | } = useMutation({ 15 | mutationFn: onRampRepository.getProviderLocation 16 | }); 17 | 18 | return { 19 | getOnRampProviders, 20 | isProvidersLoading, 21 | getOnRampProviderLocation, 22 | isProviderLocationLoading 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /src/background/redux/root-selector.ts: -------------------------------------------------------------------------------- 1 | export * from './active-origin/selectors'; 2 | export * from './active-origin-favicon/selectors'; 3 | export * from './keys/selectors'; 4 | export * from './last-activity-time/selectors'; 5 | export * from './login-retry-count/selectors'; 6 | export * from './login-retry-lockout-time/selectors'; 7 | export * from './session/selectors'; 8 | export * from './vault/selectors'; 9 | export * from './vault-cipher/selectors'; 10 | export * from './windowManagement/selectors'; 11 | export * from './settings/selectors'; 12 | export * from './recent-recipient-public-keys/selectors'; 13 | export * from './rate-app/selectors'; 14 | export * from './ledger/selectors'; 15 | export * from './app-events/selectors'; 16 | export * from './trusted-wasm/selectors'; 17 | -------------------------------------------------------------------------------- /src/libs/ui/forms/recover-from-secret-phrase.ts: -------------------------------------------------------------------------------- 1 | import * as Yup from 'yup'; 2 | import { yupResolver } from '@hookform/resolvers/yup/dist/yup'; 3 | import { UseFormProps, useForm } from 'react-hook-form'; 4 | 5 | import { useValidSecretPhraseRule } from '@libs/ui/forms/form-validation-rules'; 6 | 7 | export type RecoverSecretPhraseFormValues = { 8 | phrase: string; 9 | }; 10 | 11 | export function useRecoverFromSecretPhraseForm() { 12 | const formSchema = Yup.object().shape({ 13 | phrase: useValidSecretPhraseRule() 14 | }); 15 | 16 | const formOptions: UseFormProps = { 17 | reValidateMode: 'onChange', 18 | resolver: yupResolver(formSchema) 19 | }; 20 | 21 | return useForm(formOptions); 22 | } 23 | -------------------------------------------------------------------------------- /src/assets/icons/connection.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/background/redux/account-info/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | export const accountInfoReset = createAction('ACCOUNT_INFO_RESET')(); 4 | 5 | export const accountPendingDeployHashesChanged = createAction( 6 | 'ACCOUNT_PENDING_DEPLOY_HASHES_CHANGED' 7 | )(); 8 | 9 | export const accountPendingDeployHashesRemove = createAction( 10 | 'ACCOUNT_PENDING_TRANSACTIONS_REMOVE' 11 | )(); 12 | 13 | export const accountTrackingIdOfSentNftTokensChanged = createAction( 14 | 'ACCOUNT_TRACKING_ID_OF_SENT_NFT_TOKENS_CHANGED' 15 | )<{ 16 | trackingId: string; 17 | deployHash: string; 18 | }>(); 19 | 20 | export const accountTrackingIdOfSentNftTokensRemoved = createAction( 21 | 'ACCOUNT_TRACKING_ID_OF_SENT_NFT_TOKENS_REMOVED' 22 | )(); 23 | -------------------------------------------------------------------------------- /src/libs/crypto/sign-deploy.tsx: -------------------------------------------------------------------------------- 1 | import { Conversions, PrivateKey, PublicKey } from 'casper-js-sdk'; 2 | 3 | import { getPrivateKeyHexFromSecretKey } from '@src/utils'; 4 | 5 | export const signDeployForProviderResponse = ( 6 | deployHash: Uint8Array, 7 | publicKeyHex: string, 8 | privateKeyBase64: string 9 | ) => { 10 | const publicKey = PublicKey.fromHex(publicKeyHex); 11 | const privateKey = PrivateKey.fromHex( 12 | getPrivateKeyHexFromSecretKey(Conversions.base64to16(privateKeyBase64)), 13 | publicKey.cryptoAlg 14 | ); 15 | 16 | // signature is a signed deploy hash 17 | const signature = privateKey.sign(deployHash); 18 | 19 | // ERROR HANDLING 20 | if (signature == null) { 21 | throw Error('Signature is empty.'); 22 | } 23 | 24 | return signature; 25 | }; 26 | -------------------------------------------------------------------------------- /src/assets/fonts/fonts.css: -------------------------------------------------------------------------------- 1 | /* license is located relative to this file at './Inter-OFL.txt' */ 2 | @font-face { 3 | font-family: 'Inter'; 4 | src: 5 | local('Inter Regular'), 6 | local('Inter-Regular'), 7 | url('./Inter-VariableFont.woff2') format('woff2'), 8 | url('./Inter-VariableFont.woff') format('woff'); 9 | font-weight: 100 900; 10 | font-display: swap; 11 | } 12 | 13 | /* license is located relative to this file at './JetBrainsMono-OFL.txt' */ 14 | @font-face { 15 | font-family: 'JetBrains Mono'; 16 | src: 17 | local('JetBrains Mono'), 18 | local('JetBrains-Mono'), 19 | url('./JetBrainsMono-VariableFont.woff2') format('woff2'), 20 | url('./JetBrainsMono-VariableFont.woff') format('woff'); 21 | font-weight: 100 900; 22 | font-display: swap; 23 | } 24 | -------------------------------------------------------------------------------- /src/assets/icons/card.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/libs/crypto/create-asymmetric-key.ts: -------------------------------------------------------------------------------- 1 | import { Conversions, PrivateKey, PublicKey } from 'casper-js-sdk'; 2 | 3 | import { getPrivateKeyHexFromSecretKey } from '@src/utils'; 4 | 5 | export interface AsymmetricKeys { 6 | publicKey: PublicKey; 7 | secretKey: PrivateKey | null; 8 | } 9 | 10 | export function createAsymmetricKeys( 11 | publicKeyHex: string, 12 | secretKeyBase64: string 13 | ): AsymmetricKeys { 14 | const publicKey = PublicKey.fromHex(publicKeyHex); 15 | const privateKey = secretKeyBase64.length 16 | ? PrivateKey.fromHex( 17 | getPrivateKeyHexFromSecretKey(Conversions.base64to16(secretKeyBase64)), 18 | publicKey.cryptoAlg 19 | ) 20 | : null; 21 | 22 | return { 23 | publicKey: publicKey, 24 | secretKey: privateKey 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /src/libs/services/cep18-service/use-fetch-cep18-tokens.ts: -------------------------------------------------------------------------------- 1 | import { useQuery } from '@tanstack/react-query'; 2 | import { useSelector } from 'react-redux'; 3 | 4 | import { selectActiveNetworkSetting } from '@background/redux/settings/selectors'; 5 | import { selectVaultActiveAccount } from '@background/redux/vault/selectors'; 6 | 7 | import { fetchCep18TokensQuery } from '@libs/services/cep18-service/queries'; 8 | 9 | export const useFetchCep18Tokens = () => { 10 | const activeAccount = useSelector(selectVaultActiveAccount); 11 | const network = useSelector(selectActiveNetworkSetting); 12 | 13 | const { data: cep18Tokens = [], isFetching: isLoadingTokens } = useQuery( 14 | fetchCep18TokensQuery({ network, activeAccount }) 15 | ); 16 | 17 | return { cep18Tokens, isLoadingTokens }; 18 | }; 19 | -------------------------------------------------------------------------------- /src/assets/icons/sign.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/team.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/transfer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/apps/popup/pages/stakes/step.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Trans, useTranslation } from 'react-i18next'; 3 | 4 | import { 5 | ContentContainer, 6 | ParagraphContainer, 7 | SpacingSize 8 | } from '@libs/layout'; 9 | import { Typography } from '@libs/ui/components'; 10 | 11 | interface ValidatorStepProps { 12 | children?: React.ReactNode; 13 | headerText: string; 14 | } 15 | export const Step = ({ headerText, children }: ValidatorStepProps) => { 16 | const { t } = useTranslation(); 17 | 18 | return ( 19 | 20 | 21 | 22 | {headerText} 23 | 24 | 25 | 26 | {children} 27 | 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /src/assets/icons/receive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/libs/ui/forms/unlock-wallet.ts: -------------------------------------------------------------------------------- 1 | import * as Yup from 'yup'; 2 | import { yupResolver } from '@hookform/resolvers/yup/dist/yup'; 3 | import { UseFormProps, useForm } from 'react-hook-form'; 4 | 5 | import { useVerifyPasswordAgainstHashRule } from './form-validation-rules'; 6 | 7 | export type UnlockWalletFormValues = { 8 | password: string; 9 | }; 10 | 11 | export function useUnlockWalletForm( 12 | passwordHash: string, 13 | passwordSaltHash: string 14 | ) { 15 | const formSchema = Yup.object().shape({ 16 | password: useVerifyPasswordAgainstHashRule(passwordHash, passwordSaltHash) 17 | }); 18 | 19 | const formOptions: UseFormProps = { 20 | reValidateMode: 'onSubmit', 21 | resolver: yupResolver(formSchema) 22 | }; 23 | 24 | return useForm(formOptions); 25 | } 26 | -------------------------------------------------------------------------------- /scripts/locale_update_translations.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { readFileSync, writeFileSync } = require('fs'); 3 | const { gettextToI18next } = require('i18next-conv'); 4 | 5 | const options = { 6 | project: 'casper-signer-v2', 7 | ctxSeparator: false, 8 | keyseparator: false, 9 | foldLength: 90 10 | }; 11 | 12 | function save(target) { 13 | return result => { 14 | writeFileSync(target, result); 15 | }; 16 | } 17 | 18 | const languages = ['en', 'tr', 'ua', 'fr', 'nl', 'ru', 'pl', 'vi', 'az', 'es']; 19 | 20 | languages.forEach(lang => { 21 | const source = path.join(__dirname, `../lang/${lang}.po`); 22 | const target = path.join( 23 | __dirname, 24 | `../src/assets/locales/${lang}/translation.json` 25 | ); 26 | 27 | gettextToI18next(lang, readFileSync(source), options).then(save(target)); 28 | }); 29 | -------------------------------------------------------------------------------- /src/assets/locales/vi/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "Your vault is ready, but no accounts yet": "Vault của bạn đã sẵn sàng, nhưng chưa có tài khoản nào", 3 | "Please, create an account or import an account you already have.": "Vui lòng tạo tài khoản hoặc nhập tài khoản bạn đã có.", 4 | "Create Account": "Tạo tài khoản", 5 | "Import Account": "Nhập tài khoản", 6 | "Should be at least": "Phải có ít nhất", 7 | "characters": "ký tự", 8 | "Passwords don't match": "Mật khẩu không khớp", 9 | "Create new vault": "Tạo vault mới", 10 | "Please set a password for your vault. Try to use at least": "Vui lòng đặt mật khẩu cho kho tiền của bạn. Cố gắng sử dụng ít nhất", 11 | "to ensure a strong passphrase.": " để đảm bảo một cụm mật khẩu mạnh.", 12 | "Password": "Mật khẩu", 13 | "Confirm password": "Xác nhận mật khẩu", 14 | "Create Vault": "Tạo Vault" 15 | } 16 | -------------------------------------------------------------------------------- /src/assets/locales/pl/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "Your vault is ready, but no accounts yet": "Twój skarbiec jest gotowy, ale nie masz jeszcze kont", 3 | "Please, create an account or import an account you already have.": "Utwórz konto lub zaimportuj konto, które już posiadasz.", 4 | "Create Account": "Utwórz Konto", 5 | "Import Account": "Importuj Konto", 6 | "Should be at least": "Powinno mieć co najmniej", 7 | "characters": "znaków", 8 | "Passwords don't match": "Hasła nie pasują", 9 | "Create new vault": "Utwórz nowy skarbiec", 10 | "Please set a password for your vault. Try to use at least": "Ustaw hasło do swojego skarbca. Postaraj się użyć co najmniej", 11 | "to ensure a strong passphrase.": ", aby zapewnić silne hasło.", 12 | "Password": "Hasło", 13 | "Confirm password": "Potwierdź hasło", 14 | "Create Vault": "Utwórz skarbiec" 15 | } 16 | -------------------------------------------------------------------------------- /src/apps/popup/pages/change-password/content.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Trans, useTranslation } from 'react-i18next'; 3 | 4 | import { 5 | ContentContainer, 6 | ParagraphContainer, 7 | SpacingSize 8 | } from '@libs/layout'; 9 | import { Typography } from '@libs/ui/components'; 10 | 11 | interface ChangePasswordPageContentProps { 12 | children: React.ReactNode; 13 | } 14 | 15 | export const ChangePasswordPageContent = ({ 16 | children 17 | }: ChangePasswordPageContentProps) => { 18 | const { t } = useTranslation(); 19 | 20 | return ( 21 | 22 | 23 | 24 | Change Password 25 | 26 | 27 | 28 | {children} 29 | 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /src/hooks/use-click-away.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useRef } from 'react'; 2 | 3 | export type Props = { 4 | eventType?: 'click'; 5 | callback: (event: any) => void; 6 | }; 7 | 8 | export function useClickAway({ eventType = 'click', callback }: Props) { 9 | const ref = useRef(null); 10 | 11 | const handleClickOutside = useCallback( 12 | (event: any) => { 13 | if (ref && ref.current) { 14 | if (!ref.current.contains(event.target)) { 15 | callback(event); 16 | } 17 | } 18 | }, 19 | [callback, ref] 20 | ); 21 | 22 | useEffect(() => { 23 | document.addEventListener(eventType, handleClickOutside, true); 24 | return () => { 25 | document.removeEventListener(eventType, handleClickOutside, true); 26 | }; 27 | }, [eventType, handleClickOutside]); 28 | 29 | return { ref }; 30 | } 31 | -------------------------------------------------------------------------------- /src/libs/ui/forms/create-password.ts: -------------------------------------------------------------------------------- 1 | import * as Yup from 'yup'; 2 | import { yupResolver } from '@hookform/resolvers/yup/dist/yup'; 3 | import { UseFormProps, useForm } from 'react-hook-form'; 4 | 5 | import { 6 | useCreatePasswordRule, 7 | useRepeatPasswordRule 8 | } from '@libs/ui/forms/form-validation-rules'; 9 | 10 | export type CreatePasswordFormValues = { 11 | password: string; 12 | confirmPassword: string; 13 | }; 14 | 15 | export function useCreatePasswordForm() { 16 | const formSchema = Yup.object().shape({ 17 | password: useCreatePasswordRule(), 18 | confirmPassword: useRepeatPasswordRule('password') 19 | }); 20 | 21 | const formOptions: UseFormProps = { 22 | reValidateMode: 'onChange', 23 | resolver: yupResolver(formSchema) 24 | }; 25 | 26 | return useForm(formOptions); 27 | } 28 | -------------------------------------------------------------------------------- /src/assets/locales/tr/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "Your vault is ready, but no accounts yet": "Kasanız hazır, ancak henüz hiç hesap yok", 3 | "Please, create an account or import an account you already have.": "Lütfen bir hesap oluşturun ya da sahip olduğunuz bir hesabı içe aktarın.", 4 | "Create Account": "Hesap Oluştur", 5 | "Import Account": "Hesabı İçe Aktar", 6 | "Should be at least": "En az şu kadar olmalıdır:", 7 | "characters": "karakter", 8 | "Passwords don't match": "Parolalar eşleşmiyor", 9 | "Create new vault": "Yeni kasa oluştur", 10 | "Please set a password for your vault. Try to use at least": "Lütfen kasanız için bir parola belirleyin. Güçlü bir parola sağlamak için en az", 11 | "to ensure a strong passphrase.": " kullanmaya çalışın.", 12 | "Password": "Parola", 13 | "Confirm password": "Parolayı Onayla", 14 | "Create Vault": "Kasa Oluştur" 15 | } 16 | -------------------------------------------------------------------------------- /src/libs/layout/locked-router/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { HashRouter, Route, Routes } from 'react-router-dom'; 3 | 4 | import { ResetVaultPage } from '@libs/layout/reset-vault'; 5 | import { UnlockVaultPage } from '@libs/layout/unlock-vault'; 6 | 7 | export const LockedRouterPath = { 8 | Any: '*', 9 | ResetVault: '/reset-vault' 10 | }; 11 | 12 | interface LockedRouterProps { 13 | popupLayout?: boolean; 14 | } 15 | 16 | export const LockedRouter = ({ popupLayout }: LockedRouterProps) => ( 17 | 18 | 19 | } 22 | /> 23 | } 26 | /> 27 | 28 | 29 | ); 30 | -------------------------------------------------------------------------------- /assests/casper-wallet-wc-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/casper-wallet-wc-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/media-placeholder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/background/redux/settings/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from 'typesafe-actions'; 2 | 3 | import { NetworkSetting } from '@src/constants'; 4 | 5 | import { TimeoutDurationSetting } from '@popup/constants'; 6 | 7 | import { ThemeMode } from '@background/redux/settings/types'; 8 | 9 | export const activeTimeoutDurationSettingChanged = createAction( 10 | 'ACTIVE_TIMEOUT_DURATION_SETTING_CHANGED' 11 | )(); 12 | 13 | export const activeNetworkSettingChanged = createAction( 14 | 'ACTIVE_NETWORK_SETTING_CHANGED' 15 | )(); 16 | 17 | export const themeModeSettingChanged = createAction( 18 | 'THEME_MODE_SETTING_CHANGED' 19 | )(); 20 | 21 | export const vaultSettingsReseted = createAction('VAULT_SETTINGS_RESETED')(); 22 | 23 | export const casperNetworkApiVersionChanged = createAction( 24 | 'CASPER2_NETWORK_CHANGED' 25 | )(); 26 | -------------------------------------------------------------------------------- /src/hooks/use-window-manager.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import { useSelector } from 'react-redux'; 3 | 4 | import { createOpenWindow } from '@background/create-open-window'; 5 | import { dispatchToMainStore } from '@background/redux/utils'; 6 | import { 7 | windowIdChanged, 8 | windowIdCleared 9 | } from '@background/redux/windowManagement/actions'; 10 | import { selectWindowId } from '@background/redux/windowManagement/selectors'; 11 | 12 | export function useWindowManager() { 13 | const windowId = useSelector(selectWindowId); 14 | 15 | const openWindow = useMemo( 16 | () => 17 | createOpenWindow({ 18 | windowId, 19 | setWindowId: (id: number) => dispatchToMainStore(windowIdChanged(id)), 20 | clearWindowId: () => dispatchToMainStore(windowIdCleared()) 21 | }), 22 | [windowId] 23 | ); 24 | 25 | return { openWindow }; 26 | } 27 | -------------------------------------------------------------------------------- /src/apps/popup/pages/token-details/utils.ts: -------------------------------------------------------------------------------- 1 | import { ITokenWithFiatBalance } from 'casper-wallet-core'; 2 | 3 | import { formatCep18Tokens } from '@popup/pages/home/components/tokens-list/utils'; 4 | 5 | import { TokenType } from '@hooks/use-casper-token'; 6 | 7 | export const getTokenData = ( 8 | initialTokenData: TokenType | null, 9 | cep18Tokens: ITokenWithFiatBalance[], 10 | casperToken: TokenType | null, 11 | tokenName?: string 12 | ) => { 13 | if (tokenName === 'Casper') { 14 | return casperToken; 15 | } else { 16 | // CEP-18 token case 17 | const formatedCep18Tokens = formatCep18Tokens(cep18Tokens); 18 | 19 | const token = formatedCep18Tokens?.find(token => token.id === tokenName); 20 | 21 | if (token) { 22 | return token; 23 | } else { 24 | return initialTokenData ? { ...initialTokenData, amount: '0' } : null; 25 | } 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /src/assets/icons/delegate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/locales/nl/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "Your vault is ready, but no accounts yet": "Je kluis is klaar, maar nog geen accounts", 3 | "Please, create an account or import an account you already have.": "Maak een account aan of importeer account dat u al heeft.", 4 | "Create Account": "Account Aanmaken", 5 | "Import Account": "Account Importeren", 6 | "Should be at least": "Moet minimaal bevatten", 7 | "characters": "tekens", 8 | "Passwords don't match": "Wachtwoorden komen niet overeen", 9 | "Create new vault": "Nieuwe kluis maken", 10 | "Please set a password for your vault. Try to use at least": "Stel een wachtwoord in voor uw kluis. Probeer ten minste", 11 | "to ensure a strong passphrase.": " te gebruiken om een sterke wachtwoordzin te garanderen.", 12 | "Password": "Wachtwoord", 13 | "Confirm password": "Bevestig wachtwoord", 14 | "Create Vault": "Kluis maken" 15 | } 16 | -------------------------------------------------------------------------------- /src/apps/onboarding/pages/onboarding-success/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Trans, useTranslation } from 'react-i18next'; 3 | 4 | import { closeActiveTab } from '@onboarding/utils/close-active-tab'; 5 | 6 | import { LayoutTab, TabFooterContainer } from '@libs/layout'; 7 | import { Button } from '@libs/ui/components'; 8 | 9 | import { OnboardingSuccessPageContent } from './content'; 10 | 11 | export function OnboardingSuccessPage() { 12 | const { t } = useTranslation(); 13 | return ( 14 | } 17 | renderFooter={() => ( 18 | 19 | 22 | 23 | )} 24 | /> 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/assets/icons/auction.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/locales/az/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "Your vault is ready, but no accounts yet": "Kassanız hazırdır, lakin hələ heç bir hesab yoxdur", 3 | "Please, create an account or import an account you already have.": "Zəhmət olmasa, hesab yaradın və ya artıq mövcud olan hesabı idxal edin.", 4 | "Create Account": "Hesab Yarat", 5 | "Import Account": "Hesabı idxal edin", 6 | "Should be at least": "Ən azı olmalıdır", 7 | "characters": "simvol", 8 | "Passwords don't match": "Parollar uyğun gəlmir", 9 | "Create new vault": "Yeni kassa yaradın", 10 | "Please set a password for your vault. Try to use at least": "Zəhmət olmasa anbarınız üçün parol təyin edin. Güclü parol ifadəsini təmin etmək üçün ən azı", 11 | "to ensure a strong passphrase.": " istifadə etməyə çalışın.", 12 | "Password": "Parol", 13 | "Confirm password": "Parolu təsdiqləyin", 14 | "Create Vault": "Vault yaradın" 15 | } 16 | -------------------------------------------------------------------------------- /src/libs/ui/forms/buy-cspr.ts: -------------------------------------------------------------------------------- 1 | import * as Yup from 'yup'; 2 | import { yupResolver } from '@hookform/resolvers/yup/dist/yup'; 3 | import { UseFormProps, useForm } from 'react-hook-form'; 4 | 5 | import { useBuyCSPRKeyRule } from '@libs/ui/forms/form-validation-rules'; 6 | 7 | export type BuyCSPRFormValues = { 8 | fiatAmount: string; 9 | casperAmount: string; 10 | }; 11 | 12 | export const useBuyCSPR = (defaultAmount: string) => { 13 | const buyCSPRSchema = Yup.object().shape({ 14 | fiatAmount: useBuyCSPRKeyRule(), 15 | casperAmount: Yup.string() 16 | }); 17 | 18 | const buyFromOptions: UseFormProps = { 19 | reValidateMode: 'onChange', 20 | mode: 'onChange', 21 | resolver: yupResolver(buyCSPRSchema), 22 | defaultValues: { 23 | fiatAmount: defaultAmount 24 | } 25 | }; 26 | 27 | return useForm(buyFromOptions); 28 | }; 29 | -------------------------------------------------------------------------------- /src/assets/locales/es/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "Your vault is ready, but no accounts yet": "Su bóveda está lista, pero aún no hay cuentas", 3 | "Please, create an account or import an account you already have.": "Por favor, crea una cuenta o importa una cuenta que ya tengas.", 4 | "Create Account": "Crear una Cuenta", 5 | "Import Account": "Cuenta de Importación", 6 | "Should be at least": "Debe tener al menos", 7 | "characters": "caracteres", 8 | "Passwords don't match": "Las contraseñas no coinciden", 9 | "Create new vault": "Crear nueva bóveda", 10 | "Please set a password for your vault. Try to use at least": "Establezca una contraseña para su bóveda. Trate de usar al menos", 11 | "to ensure a strong passphrase.": " para garantizar una frase de contraseña segura.", 12 | "Password": "Clave", 13 | "Confirm password": "Confirmar Contraseña", 14 | "Create Vault": "Crear bóveda" 15 | } 16 | -------------------------------------------------------------------------------- /src/assets/icons/reset.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/libs/layout/header/components/header-background-container.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import { useIsDarkMode } from '@hooks/use-is-dark-mode'; 5 | 6 | interface HeaderBackgroundContainerTypes { 7 | children: React.ReactNode; 8 | isOpen: boolean; 9 | } 10 | 11 | const Container = styled.div<{ isOpen: boolean; isDarkMode: boolean }>` 12 | padding: 8px; 13 | border-radius: ${({ theme }) => theme.borderRadius.twelve}px; 14 | background: ${({ isOpen, isDarkMode }) => 15 | isOpen ? (isDarkMode ? '#930C16' : '#BA0F1C') : 'inherit'}; 16 | `; 17 | 18 | export const HeaderBackgroundContainer = ({ 19 | children, 20 | isOpen 21 | }: HeaderBackgroundContainerTypes) => { 22 | const isDarkMode = useIsDarkMode(); 23 | 24 | return ( 25 | 26 | {children} 27 | 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /utils/build-dir-utils.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const { 5 | ExtensionBuildPath, 6 | isChrome, 7 | isSafari, 8 | isFirefox 9 | } = require('../constants'); 10 | 11 | function getExtensionBuildAbsolutePath() { 12 | if (isChrome) { 13 | return path.join(__dirname, '../', ExtensionBuildPath.Chrome); 14 | } else if (isFirefox) { 15 | return path.join(__dirname, '../', ExtensionBuildPath.Firefox); 16 | } else if (isSafari) { 17 | return path.join(__dirname, '../', ExtensionBuildPath.Safari); 18 | } 19 | throw new Error('Unknown browser passed'); 20 | } 21 | 22 | function cleanUpBuildDir() { 23 | const extensionBuildAbsolutePath = getExtensionBuildAbsolutePath(); 24 | fs.rmSync(extensionBuildAbsolutePath, { recursive: true, force: true }); 25 | } 26 | 27 | module.exports = { 28 | getExtensionBuildAbsolutePath, 29 | cleanUpBuildDir 30 | }; 31 | -------------------------------------------------------------------------------- /src/libs/ui/components/copy-to-clipboard/copy-to-clipboard.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import { useCopyToClipboard } from '@hooks/use-copy-to-clipboard'; 5 | 6 | const Container = styled.div` 7 | cursor: ${({ onClick }) => (onClick != null ? 'pointer' : 'auto')}; 8 | `; 9 | 10 | interface RenderContentProps { 11 | isClicked: boolean; 12 | } 13 | 14 | interface CopyToClipboardProps { 15 | renderContent: (renderContentProps: RenderContentProps) => JSX.Element; 16 | valueToCopy: string; 17 | } 18 | 19 | export function CopyToClipboard({ 20 | renderContent, 21 | valueToCopy 22 | }: CopyToClipboardProps) { 23 | const { isClicked, handleCopyOnClick } = useCopyToClipboard(valueToCopy); 24 | 25 | return ( 26 | 27 | {renderContent({ isClicked })} 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/content/sdk-types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Each event will contain a `json string` payload in the `event.detail` property. This payload contains the Casper Wallet internal state so you can keep you application UI in sync. 3 | */ 4 | export type CasperWalletState = { 5 | /** contain wallet is locked flag */ 6 | isLocked: boolean; 7 | /** if unlocked contain connected status flag of active key otherwise undefined */ 8 | isConnected: boolean | undefined; 9 | /** if unlocked and connected contain active key otherwise undefined */ 10 | activeKey: string | undefined; 11 | /** if unlocked and connected, contain a list of supported features for the current active key otherwise `undefined` */ 12 | activeKeySupports: CasperWalletSupports[] | undefined; 13 | }; 14 | 15 | export enum CasperWalletSupports { 16 | signDeploy = 'sign-deploy', 17 | signTransactionV1 = 'sign-transactionv1', 18 | signMessage = 'sign-message' 19 | } 20 | -------------------------------------------------------------------------------- /src/libs/entities/Account.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from 'casper-js-sdk'; 2 | 3 | // PublicKey casper nomenclature: 4 | // - public key = base16 hex => algo prefix + public key hex 5 | // - account hash - internal representation of a public key with a fixed length 6 | 7 | // base16 hex: '01deba7173738a7f55de3ad9dc27e081df41ff285f25887ec424a8a65b43d0cf77' 8 | // base64 "2qdt4zi/b/uagi2L9Y+db0I0Jt62CTpR/Td9HhiRAu0=" 9 | 10 | // ED = 01 public keys should be 66 chars long (with the prefix) 11 | // SEC = 02 public keys should be 68 chars long (with the prefix) 12 | 13 | export const getRawPublicKey = (publicKeyHex: string): PublicKey => 14 | PublicKey.fromHex(publicKeyHex); 15 | 16 | export const getAccountHashFromPublicKey = ( 17 | publicKey: string | undefined 18 | ): string => { 19 | if (!publicKey) { 20 | throw Error('Missing public key'); 21 | } 22 | 23 | return getRawPublicKey(publicKey).accountHash().toHex(); 24 | }; 25 | -------------------------------------------------------------------------------- /src/libs/services/account-info/use-fetch-account-from-cspr-name.ts: -------------------------------------------------------------------------------- 1 | import { useQuery } from '@tanstack/react-query'; 2 | import { CasperNetwork } from 'casper-wallet-core/src/domain/common/common'; 3 | import { useSelector } from 'react-redux'; 4 | 5 | import { selectActiveNetworkSetting } from '@background/redux/settings/selectors'; 6 | import { accountInfoRepository } from '@background/wallet-repositories'; 7 | 8 | export const useFetchAccountFromCsprName = (csprName: string) => { 9 | const network = useSelector(selectActiveNetworkSetting); 10 | 11 | const { data: accountInfo, isFetching: isLoading } = useQuery({ 12 | queryKey: ['ACCOUNT_INFO_FROM_CSPR_NAME', csprName], 13 | queryFn: () => 14 | accountInfoRepository.resolveAccountFromCsprName( 15 | csprName, 16 | network.toLowerCase() as CasperNetwork, 17 | false 18 | ) 19 | }); 20 | 21 | return { accountInfo, isLoading }; 22 | }; 23 | -------------------------------------------------------------------------------- /ci/docker/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | user root; 2 | worker_processes auto; 3 | error_log /var/log/nginx/error.log; 4 | pid /run/nginx.pid; 5 | 6 | include /usr/share/nginx/modules/*.conf; 7 | 8 | events { 9 | worker_connections 1024; 10 | } 11 | 12 | http { 13 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 14 | '$status $body_bytes_sent "$http_referer" ' 15 | '"$http_user_agent" "$http_x_forwarded_for"'; 16 | 17 | access_log /var/log/nginx/access.log main; 18 | sendfile on; 19 | tcp_nopush on; 20 | tcp_nodelay on; 21 | keepalive_timeout 65; 22 | types_hash_max_size 2048; 23 | include /etc/nginx/mime.types; 24 | default_type application/octet-stream; 25 | 26 | server { 27 | listen 80 default_server; 28 | root /usr/share/nginx/html; 29 | 30 | location / { 31 | try_files $uri /index.html; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/background/close-window-by-reload-extension.ts: -------------------------------------------------------------------------------- 1 | import { runtime, tabs } from 'webextension-polyfill'; 2 | 3 | import { isFirefoxBuild, isSafariBuild } from '@src/utils'; 4 | 5 | // It's hacky for Safari browser => browser.runtime.reload(); 6 | // window.close() method can only be called on windows that were opened by a script using the Window.open() method. 7 | // If the window was > not opened by a script, an error similar to this one appears in the console: 8 | // Scripts may not close windows that were not opened by script 9 | // WARNING: IT WILL RELOAD ENTIRE EXTENSION 10 | export function closeWindowByReloadExtension() { 11 | if (isSafariBuild) { 12 | tabs.create({ url: 'onboarding.html', active: true }); 13 | runtime.reload(); 14 | return; 15 | } 16 | if (isFirefoxBuild) { 17 | runtime.reload(); 18 | return; 19 | } 20 | window.close(); 21 | tabs.create({ url: 'onboarding.html', active: true }); 22 | } 23 | -------------------------------------------------------------------------------- /src/assets/icons/bid-contract-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/background/redux/utils.ts: -------------------------------------------------------------------------------- 1 | import { call, select } from 'redux-saga/effects'; 2 | import { RootState } from 'typesafe-actions'; 3 | import { runtime } from 'webextension-polyfill'; 4 | 5 | import { ReduxAction } from './redux-action'; 6 | 7 | declare global { 8 | interface Window { 9 | __REDUX_DEVTOOLS_EXTENSION_COMPOSE__: any; 10 | } 11 | } 12 | 13 | export function dispatchToMainStore(action: ReduxAction) { 14 | return runtime.sendMessage(action).catch(() => { 15 | console.error('Dispatch to Main Store: ' + action.type); 16 | }); 17 | } 18 | 19 | export function* sagaSelect(selector: (state: RootState) => Result) { 20 | const res: Result = yield select(selector); 21 | return res; 22 | } 23 | 24 | export function* sagaCall( 25 | fn: (...args: Args) => Promise, 26 | ...args: Args 27 | ) { 28 | const res: Result = yield call(fn, ...args) as Result; 29 | return res; 30 | } 31 | -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Resources/Style.css: -------------------------------------------------------------------------------- 1 | * { 2 | -webkit-user-select: none; 3 | -webkit-user-drag: none; 4 | cursor: default; 5 | } 6 | 7 | :root { 8 | color-scheme: light dark; 9 | 10 | --spacing: 20px; 11 | } 12 | 13 | html { 14 | height: 100%; 15 | } 16 | 17 | body { 18 | display: flex; 19 | align-items: center; 20 | justify-content: center; 21 | flex-direction: column; 22 | 23 | gap: var(--spacing); 24 | margin: 0 calc(var(--spacing) * 2); 25 | height: 100%; 26 | 27 | font: -apple-system-short-body; 28 | text-align: center; 29 | } 30 | 31 | body:not(.state-on, .state-off) :is(.state-on, .state-off) { 32 | display: none; 33 | } 34 | 35 | body.state-on :is(.state-off, .state-unknown) { 36 | display: none; 37 | } 38 | 39 | body.state-off :is(.state-on, .state-unknown) { 40 | display: none; 41 | } 42 | 43 | button { 44 | font-size: 1em; 45 | } 46 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: piotrwitek 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots & Videos** 24 | If applicable, add screenshots and links to video recordings to help explain your problem. 25 | 26 | **Required information:** 27 | - Operating System: [e.g. Windows 10, macOS 11.6] 28 | - Browser Name and Version [e.g. Firefox 107.0, Google Chrome 112.0.5615.49] 29 | - Casper Wallet Version [e.g. 1.2.6.2] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /constants.js: -------------------------------------------------------------------------------- 1 | const { NODE_ENV, BROWSER: browserEnvVar } = require('./utils/env'); 2 | 3 | const extensionName = 'Casper Wallet'; 4 | const buildRootDir = ['test', 'production'].includes(NODE_ENV) 5 | ? 'build' 6 | : 'output'; 7 | 8 | const ExtensionBuildPath = { 9 | Chrome: `${buildRootDir}/chrome`, 10 | Firefox: `${buildRootDir}/firefox`, 11 | Safari: `${buildRootDir}/safari/${extensionName}` 12 | }; 13 | 14 | const ManifestPath = { 15 | v3: 'src/manifest.v3.json', 16 | v2: 'src/manifest.v2.json', 17 | v2_Safari: 'src/manifest.v2.safari.json' 18 | }; 19 | 20 | const isSafari = browserEnvVar && browserEnvVar === 'safari'; 21 | const isChrome = browserEnvVar && browserEnvVar === 'chrome'; 22 | const isFirefox = browserEnvVar && browserEnvVar === 'firefox'; 23 | 24 | module.exports = { 25 | ExtensionBuildPath, 26 | extensionName, 27 | browserEnvVar, 28 | ManifestPath, 29 | isFirefox, 30 | isSafari, 31 | isChrome 32 | }; 33 | -------------------------------------------------------------------------------- /src/libs/ui/components/password-visibility-icon/password-visibility-icon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { SvgIcon } from '@libs/ui/components'; 4 | 5 | export type PasswordInputType = 'password' | 'text'; 6 | 7 | interface PasswordVisibilityIconProps { 8 | passwordInputType: PasswordInputType; 9 | setPasswordInputType: (type: PasswordInputType) => void; 10 | } 11 | 12 | export function PasswordVisibilityIcon({ 13 | passwordInputType, 14 | setPasswordInputType 15 | }: PasswordVisibilityIconProps) { 16 | return ( 17 | 19 | setPasswordInputType( 20 | passwordInputType === 'password' ? 'text' : 'password' 21 | ) 22 | } 23 | src={ 24 | passwordInputType === 'password' 25 | ? 'assets/icons/hide.svg' 26 | : 'assets/icons/show.svg' 27 | } 28 | color="contentDisabled" 29 | size={20} 30 | /> 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/assets/icons/unlock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/libs/services/cep18-service/queries.ts: -------------------------------------------------------------------------------- 1 | import { CasperNetwork } from 'casper-wallet-core/src/domain/common/common'; 2 | 3 | import { NetworkSetting, TOKENS_REFRESH_RATE } from '@src/constants'; 4 | 5 | import { tokensRepository } from '@background/wallet-repositories'; 6 | 7 | import { Account } from '@libs/types/account'; 8 | 9 | interface FetchCep18TokensQueryProps { 10 | network: NetworkSetting; 11 | activeAccount: Account | undefined; 12 | } 13 | 14 | export const fetchCep18TokensQuery = ({ 15 | network, 16 | activeAccount 17 | }: FetchCep18TokensQueryProps) => ({ 18 | queryKey: ['CEP18_TOKENS', network, activeAccount?.publicKey], 19 | queryFn: () => 20 | tokensRepository.getTokens({ 21 | network: network.toLowerCase() as CasperNetwork, 22 | publicKey: activeAccount?.publicKey || '', 23 | withProxyHeader: false 24 | }), 25 | refetchInterval: TOKENS_REFRESH_RATE, 26 | staleTime: TOKENS_REFRESH_RATE 27 | }); 28 | -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet Extension/SafariWebExtensionHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SafariWebExtensionHandler.swift 3 | // Casper Wallet Extension 4 | // 5 | // Created by Piotrek Witek on 19/04/2023. 6 | // 7 | 8 | import SafariServices 9 | import os.log 10 | 11 | let SFExtensionMessageKey = "message" 12 | 13 | class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling { 14 | 15 | func beginRequest(with context: NSExtensionContext) { 16 | let item = context.inputItems[0] as! NSExtensionItem 17 | let message = item.userInfo?[SFExtensionMessageKey] 18 | os_log(.default, "Received message from browser.runtime.sendNativeMessage: %@", message as! CVarArg) 19 | 20 | let response = NSExtensionItem() 21 | response.userInfo = [ SFExtensionMessageKey: [ "Response to": message ] ] 22 | 23 | context.completeRequest(returningItems: [response], completionHandler: nil) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /e2e-tests/popup/account/switch-account.spec.ts: -------------------------------------------------------------------------------- 1 | import { popup, popupExpect } from '../../fixtures'; 2 | 3 | popup.describe('Popup UI: switch account', () => { 4 | popup( 5 | 'should switch to another account', 6 | async ({ popupPage, unlockVault }) => { 7 | await unlockVault(); 8 | 9 | await popupPage.getByTestId('connection-status-modal').click(); 10 | await popupPage.waitForLoadState('networkidle'); 11 | await popupPage.getByText('Account 1').nth(1).click(); 12 | 13 | await popupExpect(popupPage.getByText('Account 1')).toBeVisible(); 14 | 15 | await popupPage.getByTestId('connection-status-modal').click(); 16 | await popupPage.waitForLoadState('networkidle'); 17 | await popupPage.getByText('Account 2').click(); 18 | 19 | await popupExpect(popupPage.getByText('Account 1')).toBeHidden(); 20 | 21 | await popupExpect(popupPage.getByText('Account 2')).toBeVisible(); 22 | } 23 | ); 24 | }); 25 | -------------------------------------------------------------------------------- /src/assets/locales/fr/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "Your vault is ready, but no accounts yet": "Votre coffre-fort est prêt, mais pas encore de comptes", 3 | "Please, create an account or import an account you already have.": "Veuillez créer un compte ou importer un compte que vous avez déjà.", 4 | "Create Account": "Créer un Compte", 5 | "Import Account": "Compte d'Importation", 6 | "Should be at least": "Doit contenir au moins", 7 | "characters": "caractères", 8 | "Passwords don't match": "Les mots de passe ne correspondent pas", 9 | "Create new vault": "Créer un nouveau coffre", 10 | "Please set a password for your vault. Try to use at least": "Veuillez définir un mot de passe pour votre coffre-fort. Essayez d'utiliser au moins", 11 | "to ensure a strong passphrase.": " pour garantir une phrase secrète solide.", 12 | "Password": "Mot de passe", 13 | "Confirm password": "Confirmez le mot de passe", 14 | "Create Vault": "Créer un coffre-fort" 15 | } 16 | -------------------------------------------------------------------------------- /src/content/sdk-event.ts: -------------------------------------------------------------------------------- 1 | import { ActionType, createAction } from 'typesafe-actions'; 2 | 3 | import { CasperWalletState } from './sdk-types'; 4 | 5 | // Event emitted to connected sites 6 | 7 | export const sdkEvent = { 8 | connectedAccountEvent: createAction( 9 | 'connectedAccountEvent' 10 | )(), 11 | disconnectedAccountEvent: createAction( 12 | 'disconnectedAccountEvent' 13 | )(), 14 | changedTab: createAction('changedTabEvent')(), 15 | changedConnectedAccountEvent: createAction( 16 | 'changedConnectedAccountEvent' 17 | )(), 18 | lockedEvent: createAction('lockedEvent')(), 19 | unlockedEvent: createAction('unlockedEvent')(), 20 | changedActiveAccountSupportsEvent: createAction( 21 | 'changedActiveAccountSupportsEvent' 22 | )() 23 | }; 24 | 25 | export type SdkEvent = ActionType; 26 | -------------------------------------------------------------------------------- /src/content/sdk-event-type.ts: -------------------------------------------------------------------------------- 1 | const EVENT_TYPE_PREFIX = 'casper-wallet'; 2 | 3 | /** 4 | * Event types emitted by the Casper Wallet extension. 5 | */ 6 | export const CasperWalletEventType = { 7 | /** Account was connected using the wallet: */ 8 | Connected: `${EVENT_TYPE_PREFIX}:connected`, 9 | /** Active key was changed using the Wallet interface: */ 10 | ActiveKeyChanged: `${EVENT_TYPE_PREFIX}:activeKeyChanged`, 11 | /** Account was disconnected using the wallet: */ 12 | Disconnected: `${EVENT_TYPE_PREFIX}:disconnected`, 13 | /** Browser tab was changed to some connected site: */ 14 | TabChanged: `${EVENT_TYPE_PREFIX}:tabChanged`, 15 | /** Wallet was locked: */ 16 | Locked: `${EVENT_TYPE_PREFIX}:locked`, 17 | /** Wallet was unlocked: */ 18 | Unlocked: `${EVENT_TYPE_PREFIX}:unlocked`, 19 | /** Active key was changed using the Wallet interface: */ 20 | ActiveKeySupportsChanged: `${EVENT_TYPE_PREFIX}:activeKeySupportsChanged` 21 | } as const; 22 | -------------------------------------------------------------------------------- /src/apps/popup/pages/deploy-details/utils.ts: -------------------------------------------------------------------------------- 1 | import { IDeploy } from 'casper-wallet-core'; 2 | import { isWasmDeployExecutionType } from 'casper-wallet-core/src/utils/deploy'; 3 | 4 | import { 5 | DeployActionEntryPointNameMap, 6 | DeployPlateEntryPointNameMap, 7 | ExecutionTypesMap 8 | } from '@src/constants'; 9 | 10 | export const getEntryPointName = (deploy: IDeploy, isAction?: boolean) => { 11 | if (deploy?.type === 'CSPR_NATIVE') { 12 | return 'Transfer'; 13 | } else if (deploy?.type === 'ASSOCIATED_KEYS') { 14 | return 'Update account'; 15 | } else if (isWasmDeployExecutionType(deploy)) { 16 | return 'WASM transaction'; 17 | } 18 | 19 | const entryPointName = deploy?.entryPoint 20 | ? isAction 21 | ? DeployActionEntryPointNameMap[deploy.entryPoint] 22 | : DeployPlateEntryPointNameMap[deploy.entryPoint] 23 | : ExecutionTypesMap[deploy.executionTypeId ?? 2]; 24 | 25 | return entryPointName ?? deploy?.entryPoint; 26 | }; 27 | -------------------------------------------------------------------------------- /src/assets/icons/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/libs/services/balance-service/use-fetch-accounts-balances.ts: -------------------------------------------------------------------------------- 1 | import { useQuery } from '@tanstack/react-query'; 2 | import { useSelector } from 'react-redux'; 3 | 4 | import { selectActiveNetworkSetting } from '@background/redux/settings/selectors'; 5 | 6 | import { UseFetchAccountsBalances } from '@libs/services/balance-service/constants'; 7 | import { accountsBalancesQuery } from '@libs/services/balance-service/queries'; 8 | 9 | export const useFetchAccountsBalances = ( 10 | accountHashes: string[] 11 | ): UseFetchAccountsBalances => { 12 | const network = useSelector(selectActiveNetworkSetting); 13 | 14 | const accountHashesString = accountHashes.toString(); 15 | 16 | const { data: accountsBalances, isFetching: isLoadingBalances } = useQuery( 17 | accountsBalancesQuery({ 18 | network, 19 | accountHashes, 20 | accountHashesString 21 | }) 22 | ); 23 | 24 | return { 25 | accountsBalances, 26 | isLoadingBalances 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /src/libs/ui/forms/import-account-from-torus.ts: -------------------------------------------------------------------------------- 1 | import * as Yup from 'yup'; 2 | import { yupResolver } from '@hookform/resolvers/yup/dist/yup'; 3 | import { UseFormProps, useForm } from 'react-hook-form'; 4 | 5 | import { 6 | useAccountNameRule, 7 | useTorusSecretKeyRule 8 | } from './form-validation-rules'; 9 | 10 | export type ImportAccountFromTorusFromValues = { 11 | name: string; 12 | secretKey: string; 13 | }; 14 | 15 | export const useImportAccountFromTorus = (existingAccountNames: string[]) => { 16 | const formSchema = Yup.object().shape({ 17 | name: useAccountNameRule(value => { 18 | return value != null && !existingAccountNames.includes(value); 19 | }), 20 | secretKey: useTorusSecretKeyRule() 21 | }); 22 | 23 | const formOptions: UseFormProps = { 24 | mode: 'onChange', 25 | resolver: yupResolver(formSchema) 26 | }; 27 | 28 | return useForm(formOptions); 29 | }; 30 | -------------------------------------------------------------------------------- /src/apps/onboarding/pages/reset-wallet/content.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Trans, useTranslation } from 'react-i18next'; 3 | 4 | import { TabPageContainer, TabTextContainer } from '@libs/layout'; 5 | import { Typography } from '@libs/ui/components'; 6 | 7 | // Design of this page is temporary. Should be changed after it will be done in Figma 8 | 9 | export function ResetWalletPageContent() { 10 | const { t } = useTranslation(); 11 | 12 | return ( 13 | 14 | 15 | Are you sure you want to start again? 16 | 17 | 18 | 19 | 20 | 21 | You'll start the onboarding from the beggining and be able to set 22 | your wallet password again. 23 | 24 | 25 | 26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /src/apps/popup/router/use-navigation-menu.ts: -------------------------------------------------------------------------------- 1 | import { useTypedLocation } from './use-typed-location'; 2 | import { useTypedNavigate } from './use-typed-navigate'; 3 | 4 | export function useNavigationMenu() { 5 | const navigate = useTypedNavigate(); 6 | const location = useTypedLocation(); 7 | 8 | const toggleNavigationMenu = () => { 9 | navigate(location.pathname, { 10 | replace: true, 11 | state: { 12 | showNavigationMenu: !location.state?.showNavigationMenu 13 | } 14 | }); 15 | }; 16 | 17 | const openNavigationMenu = () => { 18 | navigate(location.pathname, { 19 | replace: true, 20 | state: { showNavigationMenu: true } 21 | }); 22 | }; 23 | const closeNavigationMenu = () => { 24 | navigate(location.pathname, { 25 | replace: true, 26 | state: { showNavigationMenu: false } 27 | }); 28 | }; 29 | 30 | return { 31 | toggleNavigationMenu, 32 | openNavigationMenu, 33 | closeNavigationMenu 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /src/hooks/use-copy-to-clipboard.ts: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useEffect, useState } from 'react'; 2 | 3 | export const useCopyToClipboard = (valueToCopy: string) => { 4 | const overlayTimeout = 2000; 5 | const [isClicked, setIsClicked] = useState(false); 6 | 7 | const handleCopyOnClick = useCallback( 8 | async (event: React.MouseEvent) => { 9 | event.stopPropagation(); 10 | 11 | if (isClicked) { 12 | return; 13 | } 14 | 15 | setIsClicked(true); 16 | await navigator.clipboard.writeText(valueToCopy); 17 | }, 18 | [isClicked, valueToCopy] 19 | ); 20 | 21 | useEffect(() => { 22 | let timeout: NodeJS.Timeout; 23 | 24 | if (isClicked) { 25 | timeout = setTimeout(() => { 26 | setIsClicked(false); 27 | }, overlayTimeout); 28 | } 29 | 30 | return () => timeout && clearTimeout(timeout); 31 | }, [isClicked, setIsClicked]); 32 | 33 | return { 34 | isClicked, 35 | handleCopyOnClick 36 | }; 37 | }; 38 | -------------------------------------------------------------------------------- /src/libs/crypto/parse-secret-key-string-secp.ts: -------------------------------------------------------------------------------- 1 | import { Conversions, KeyAlgorithm, PrivateKey } from 'casper-js-sdk'; 2 | 3 | import { getPrivateKeyHexFromSecretKey } from '@src/utils'; 4 | 5 | import { AsymmetricKeys } from '@libs/crypto/create-asymmetric-key'; 6 | 7 | export const parseSecretKeyStringSecp = (secretKeyHex: string) => { 8 | let keyPair: AsymmetricKeys; 9 | 10 | try { 11 | const privateKey = PrivateKey.fromHex( 12 | getPrivateKeyHexFromSecretKey(secretKeyHex), 13 | KeyAlgorithm.SECP256K1 14 | ); 15 | const publicKey = privateKey.publicKey; 16 | 17 | keyPair = { 18 | publicKey, 19 | secretKey: privateKey 20 | }; 21 | } catch (error) { 22 | throw Error('Invalid secret key'); 23 | } 24 | 25 | if (!keyPair.secretKey) { 26 | throw Error('Invalid secret key'); 27 | } 28 | 29 | return { 30 | secretKeyBase64: Conversions.encodeBase64(keyPair.secretKey.toBytes()), 31 | publicKeyHex: keyPair.publicKey.toHex(false) 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /src/libs/services/app-events/use-get-active-app-marketing-event.ts: -------------------------------------------------------------------------------- 1 | import { useQuery } from '@tanstack/react-query'; 2 | 3 | import { APP_MARKETING_EVENTS_REFRESH_RATE } from '@src/constants'; 4 | 5 | import { appEventsRepository } from '@background/wallet-repositories'; 6 | 7 | export const useGetActiveAppMarketingEvent = (ignoreEventIds: number[]) => { 8 | const idsKey = JSON.stringify(ignoreEventIds); 9 | 10 | const { 11 | data: activeMarketingEvent = null, 12 | isFetching: isActiveMarketingEventLoading 13 | } = useQuery({ 14 | queryKey: ['APP_MARKETING_EVENTS', idsKey], 15 | queryFn: () => 16 | appEventsRepository.getActiveMarketingEvent({ 17 | withProxyHeader: false, 18 | env: 'PRODUCTION', 19 | ignoreEventIds 20 | }), 21 | refetchInterval: APP_MARKETING_EVENTS_REFRESH_RATE, 22 | staleTime: APP_MARKETING_EVENTS_REFRESH_RATE 23 | }); 24 | 25 | return { 26 | activeMarketingEvent, 27 | isActiveMarketingEventLoading 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /src/assets/icons/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/apps/import-account-with-file/pages/import-account-with-file-success/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Trans, useTranslation } from 'react-i18next'; 3 | 4 | import { closeCurrentWindow } from '@background/close-current-window'; 5 | 6 | import { 7 | FooterButtonsContainer, 8 | HeaderPopup, 9 | LayoutWindow 10 | } from '@libs/layout'; 11 | import { Button } from '@libs/ui/components'; 12 | 13 | import { ImportAccountWithFileSuccessContentPage } from './content'; 14 | 15 | export const ImportAccountWithFileSuccessPage = () => { 16 | const { t } = useTranslation(); 17 | 18 | return ( 19 | } 21 | renderContent={() => } 22 | renderFooter={() => ( 23 | 24 | 27 | 28 | )} 29 | /> 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /src/assets/icons/casper-wallet-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/error.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/undelegate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /xcode-project/Casper Wallet/Casper Wallet/Base.lproj/Main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Casper Wallet Icon 14 |

You can turn on Casper Wallet’s extension in Safari Extensions preferences.

15 |

Casper Wallet’s extension is currently on. You can turn it off in Safari Extensions preferences.

16 |

Casper Wallet’s extension is currently off. You can turn it on in Safari Extensions preferences.

17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest/presets/js-with-ts', 3 | testEnvironment: 'node', 4 | transform: { 5 | '^.+\\.(j|t)sx?$': ['ts-jest', { tsconfig: '/tsconfig.json' }] 6 | }, 7 | transformIgnorePatterns: ['/node_modules/(?!micro-aes-gcm/.*)'], 8 | coveragePathIgnorePatterns: ['/node_modules/'], 9 | testRegex: '(/tests?/.*|(\\.|/)(test|spec))\\.tsx?$', 10 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 11 | modulePathIgnorePatterns: ['/e2e-tests'], 12 | moduleNameMapper: { 13 | '^@src/(.*)$': '/src/$1', 14 | '^@content/sdk-types$': '/src/content/sdk-types', 15 | '^@libs/types/(.*)$': '/src/libs/types/$1' 16 | }, 17 | setupFilesAfterEnv: ['@testing-library/jest-dom'], 18 | coverageReporters: ['json', 'text'], 19 | coverageThreshold: { 20 | global: { 21 | branches: 100, 22 | functions: 100, 23 | lines: 100, 24 | statements: 100 25 | } 26 | }, 27 | testPathIgnorePatterns: ['e2e-tests/'] 28 | }; 29 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "react-app", 4 | "plugin:jsx-a11y/recommended", 5 | "plugin:react-hooks/recommended", 6 | "plugin:prettier/recommended", 7 | "plugin:import/recommended", 8 | "plugin:import/typescript" 9 | ], 10 | "plugins": [ 11 | "jsx-a11y", 12 | "react-hooks", 13 | "import" 14 | ], 15 | "parser": "@typescript-eslint/parser", 16 | "settings": { 17 | "import/resolver": { 18 | "typescript": { 19 | "alwaysTryTypes": true 20 | } 21 | } 22 | }, 23 | "rules": { 24 | "semi": 1, 25 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx", ".ts", ".tsx"] }], 26 | "no-unused-vars": "off", 27 | "@typescript-eslint/no-unused-vars": ["error", { 28 | "args": "after-used" 29 | }], 30 | "jsx-a11y/no-autofocus": "off", 31 | "import/no-cycle": [ 32 | 1, 33 | { 34 | "maxDepth": 10, 35 | "ignoreExternal": true 36 | } 37 | ], 38 | "import/no-named-as-default": 0, 39 | "import/export": 1 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/apps/popup/pages/no-connected-account/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Trans, useTranslation } from 'react-i18next'; 3 | 4 | import { RouterPath, useTypedNavigate } from '@popup/router'; 5 | 6 | import { FooterButtonsContainer, HeaderPopup, PopupLayout } from '@libs/layout'; 7 | import { Button } from '@libs/ui/components'; 8 | 9 | import { NoConnectedAccountPageContent } from './content'; 10 | 11 | export const NoConnectedAccountPage = () => { 12 | const navigate = useTypedNavigate(); 13 | const { t } = useTranslation(); 14 | 15 | return ( 16 | ( 18 | 19 | )} 20 | renderContent={() => } 21 | renderFooter={() => ( 22 | 23 | 26 | 27 | )} 28 | /> 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /jest.tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "esnext", 5 | "jsx": "react", 6 | "allowJs": true, 7 | "sourceMap": false, 8 | "experimentalDecorators": true, 9 | "noImplicitUseStrict": true, 10 | "removeComments": true, 11 | "moduleResolution": "node", 12 | "lib": ["es2017", "dom"], 13 | "typeRoots": ["node_modules/@types"], 14 | "esModuleInterop": true, 15 | "baseUrl": "./", 16 | "paths": { 17 | "@src/*": ["src/*"], 18 | "@popup/*": ["src/apps/popup/*"], 19 | "@import-account-with-file/*": ["src/apps/import-account-with-file/*"], 20 | "@connect-to-app/*": ["src/apps/connect-to-app/*"], 21 | "@signature-request/*": ["src/apps/signature-request/*"], 22 | "@onboarding/*": ["src/apps/onboarding/*"], 23 | "@background/*": ["src/background/*"], 24 | "@content/*": ["src/content/*"], 25 | "@libs/*": ["src/libs/*"], 26 | "@hooks/*": ["src/hooks/*"] 27 | } 28 | }, 29 | "exclude": ["node_modules", "build"] 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/libs/crypto/bip32.ts: -------------------------------------------------------------------------------- 1 | import { HDKey } from '@scure/bip32'; 2 | 3 | import { KeyPair } from '@libs/types/account'; 4 | 5 | import { secretPhraseToSeed, validateSecretPhrase } from './bip39'; 6 | import { getBip44Path } from './bip44'; 7 | import { privateKeyBytesToBase64, publicKeyBytesToHex } from './utils'; 8 | 9 | export function deriveKeyPair( 10 | secretPhrase: null | string[], 11 | index: number 12 | ): KeyPair { 13 | if (!validateSecretPhrase(secretPhrase)) { 14 | throw Error('secret phrase is invalid'); 15 | } 16 | const seed = secretPhraseToSeed(secretPhrase); 17 | const hdKey = HDKey.fromMasterSeed(seed); 18 | 19 | const path = getBip44Path(index); 20 | const secpKey = hdKey.derive(path); 21 | 22 | if (secpKey.publicKey == null || secpKey.privateKey == null) { 23 | throw Error('hdKey derivation failed'); 24 | } 25 | 26 | const publicKey = publicKeyBytesToHex(secpKey.publicKey); 27 | const secretKey = privateKeyBytesToBase64(secpKey.privateKey); 28 | 29 | return { 30 | publicKey, 31 | secretKey 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /src/libs/ui/global-style.ts: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from 'styled-components'; 2 | 3 | export const GlobalStyle = createGlobalStyle<{ theme: any }>` 4 | html { 5 | height: 100%; 6 | 7 | font-size: 10px; 8 | background: ${props => props.theme.color.backgroundSecondary}; 9 | } 10 | 11 | body { 12 | height: 100%; 13 | min-height: 600px; 14 | min-width: 360px; 15 | padding: 0; 16 | 17 | background: none; 18 | -webkit-font-smoothing: antialiased; 19 | -moz-osx-font-smoothing: grayscale; 20 | } 21 | 22 | .ms-container { 23 | height: 100%; 24 | } 25 | 26 | #app-container { 27 | height: 100%; 28 | min-height: 600px; 29 | background: ${props => props.theme.color.backgroundSecondary}; 30 | border-radius: 16px; 31 | 32 | color: ${props => props.theme.color.contentPrimary}; 33 | 34 | display: flex; 35 | justify-content: center; 36 | } 37 | 38 | * { 39 | box-sizing: border-box; 40 | margin: 0; 41 | 42 | font-family: 'Inter', sans-serif; 43 | } 44 | `; 45 | -------------------------------------------------------------------------------- /src/libs/ui/forms/rename-account.ts: -------------------------------------------------------------------------------- 1 | import * as Yup from 'yup'; 2 | import { yupResolver } from '@hookform/resolvers/yup/dist/yup'; 3 | import { UseFormProps, useForm } from 'react-hook-form'; 4 | 5 | import { useAccountNameRule } from './form-validation-rules'; 6 | 7 | export type RenameAccountFormValues = { 8 | name: string; 9 | }; 10 | 11 | export function useRenameAccount( 12 | accountName: string | undefined, 13 | existingAccountNames: string[] 14 | ) { 15 | const formSchema = Yup.object().shape({ 16 | name: useAccountNameRule(value => { 17 | return ( 18 | value != null && 19 | !existingAccountNames 20 | .filter(existingAccountName => existingAccountName !== accountName) 21 | .includes(value) 22 | ); 23 | }) 24 | }); 25 | 26 | const formOptions: UseFormProps = { 27 | mode: 'onChange', 28 | resolver: yupResolver(formSchema), 29 | defaultValues: { 30 | name: accountName 31 | } 32 | }; 33 | 34 | return useForm(formOptions); 35 | } 36 | -------------------------------------------------------------------------------- /src/libs/ui/components/portal-tooltip/portal-tooltip.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren, useCallback, useState } from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import { FullWidthPortalTooltip } from './fullwidth-portal-tooltip'; 5 | 6 | interface TooltipProps extends PropsWithChildren { 7 | title: JSX.Element | null; 8 | } 9 | 10 | const ChildrenContainer = styled.div` 11 | cursor: pointer; 12 | `; 13 | 14 | export const PortalTooltip: React.FC = ({ title, children }) => { 15 | const [open, setOpen] = useState(false); 16 | 17 | const onMouseEnter = useCallback(() => { 18 | setOpen(true); 19 | }, []); 20 | 21 | const onMouseLeave = useCallback(() => { 22 | setOpen(false); 23 | }, []); 24 | 25 | if (!title) { 26 | return <>{children}; 27 | } 28 | 29 | return ( 30 |
31 | 32 | {children} 33 | 34 |
35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /src/background/redux/session/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createReducer } from 'typesafe-actions'; 2 | 3 | import { 4 | contactEditingPermissionChanged, 5 | encryptionKeyHashCreated, 6 | sessionReseted, 7 | vaultUnlocked 8 | } from './actions'; 9 | import { SessionState } from './types'; 10 | 11 | type State = SessionState; 12 | 13 | const initialState: State = { 14 | encryptionKeyHash: null, 15 | isLocked: true, 16 | isContactEditingAllowed: false 17 | }; 18 | 19 | export const reducer = createReducer(initialState) 20 | .handleAction(sessionReseted, (): State => initialState) 21 | .handleAction( 22 | vaultUnlocked, 23 | (state): State => ({ 24 | ...state, 25 | isLocked: false 26 | }) 27 | ) 28 | .handleAction( 29 | encryptionKeyHashCreated, 30 | (state, action): State => ({ 31 | ...state, 32 | encryptionKeyHash: action.payload.encryptionKeyHash 33 | }) 34 | ) 35 | .handleAction( 36 | contactEditingPermissionChanged, 37 | (state): State => ({ 38 | ...state, 39 | isContactEditingAllowed: true 40 | }) 41 | ); 42 | -------------------------------------------------------------------------------- /src/libs/ui/components/text-list/text-list.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import { Typography } from '@libs/ui/components'; 5 | import { KeyValueItem } from '@libs/ui/types'; 6 | 7 | const UnorderedList = styled.ul` 8 | padding-inline-start: unset; 9 | list-style-type: none; 10 | 11 | line-height: 3rem; 12 | 13 | padding-left: 1.9rem; 14 | text-indent: -1.9rem; 15 | `; 16 | 17 | const ListItem = styled.li` 18 | &::before { 19 | content: '•'; 20 | 21 | font-size: 15px; 22 | color: ${({ theme }) => theme.color.contentSecondary}; 23 | margin-right: 10px; 24 | } 25 | `; 26 | 27 | interface TextListProps { 28 | items: KeyValueItem[]; 29 | } 30 | 31 | export function TextList({ items }: TextListProps) { 32 | return ( 33 | 34 | {items.map(({ key, value }) => ( 35 | 36 | 37 | {value} 38 | 39 | 40 | ))} 41 | 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /src/assets/icons/casper.svg: -------------------------------------------------------------------------------- 1 | 4 | 5 | Created by potrace 1.16, written by Peter Selinger 2001-2019 6 | 7 | 9 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/assets/icons/associated-keys.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/apps/import-account-with-file/pages/import-account-with-file/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Trans, useTranslation } from 'react-i18next'; 3 | import { useNavigate } from 'react-router-dom'; 4 | 5 | import { 6 | FooterButtonsContainer, 7 | HeaderPopup, 8 | LayoutWindow 9 | } from '@libs/layout'; 10 | import { Button } from '@libs/ui/components'; 11 | 12 | import { RouterPath } from '../../router'; 13 | import { ImportAccountWithFileContentPage } from './content'; 14 | 15 | export function ImportAccountWithFilePage() { 16 | const navigate = useNavigate(); 17 | const { t } = useTranslation(); 18 | 19 | return ( 20 | } 22 | renderContent={() => } 23 | renderFooter={() => ( 24 | 25 | 30 | 31 | )} 32 | /> 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/libs/ui/components/account-list/utils.ts: -------------------------------------------------------------------------------- 1 | import { Account } from '@libs/types/account'; 2 | 3 | export function sortAccounts( 4 | accounts: Account[], 5 | activeAccountName: string | null, 6 | connectedAccountNames: string[] 7 | ) { 8 | const accountsWithoutActiveAccount = ( 9 | activeAccountName != null 10 | ? accounts.filter(account => account.name !== activeAccountName) 11 | : accounts 12 | ).sort((a: Account, b: Account) => { 13 | const aIsConnectedAccount = connectedAccountNames.includes(a.name); 14 | const bIsConnectedAccount = connectedAccountNames.includes(b.name); 15 | 16 | if ( 17 | (aIsConnectedAccount && bIsConnectedAccount) || 18 | (!aIsConnectedAccount && !bIsConnectedAccount) 19 | ) { 20 | return 0; 21 | } 22 | 23 | return aIsConnectedAccount && !bIsConnectedAccount ? -1 : 1; 24 | }); 25 | 26 | const activeAccount = accounts.find( 27 | account => account.name === activeAccountName 28 | ); 29 | 30 | return activeAccount != null 31 | ? [activeAccount, ...accountsWithoutActiveAccount] 32 | : accountsWithoutActiveAccount; 33 | } 34 | --------------------------------------------------------------------------------