├── .changeset
├── README.md
└── config.json
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ └── Bug_report.md
└── workflows
│ ├── cdn-deploy.yaml
│ ├── ci.yaml
│ └── release.yaml
├── .gitignore
├── .husky
├── .gitignore
├── commit-msg
└── pre-commit
├── .npmignore
├── .prettierignore
├── .prettierrc
├── CONTRIBUTING.md
├── LICENSE
├── MIGRATING.md
├── README.md
├── docs
└── images
│ ├── dev-portal-client-details.png
│ ├── dev-portal-new-app.png
│ └── dev-portal-redirect-urls.png
├── examples
├── ethers
│ ├── .gitignore
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ ├── postcss.config.js
│ ├── public
│ │ └── callback.html
│ ├── src
│ │ ├── App.jsx
│ │ ├── index.css
│ │ └── main.jsx
│ ├── tailwind.config.js
│ └── vite.config.js
├── standalone
│ ├── .gitignore
│ ├── README.md
│ ├── callback
│ │ └── callback.html
│ ├── common.js
│ ├── connect-button
│ │ ├── index.html
│ │ └── index.js
│ ├── index.html
│ ├── package.json
│ ├── sign-typed-data
│ │ ├── index.html
│ │ └── index.js
│ └── webpack.config.js
├── viem
│ ├── .gitignore
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ ├── postcss.config.js
│ ├── public
│ │ └── callback.html
│ ├── src
│ │ ├── App.jsx
│ │ ├── index.css
│ │ └── main.jsx
│ ├── tailwind.config.js
│ └── vite.config.js
└── web3js
│ ├── .eslintrc.cjs
│ ├── .gitignore
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ ├── postcss.config.js
│ ├── public
│ └── callback.html
│ ├── src
│ ├── App.jsx
│ ├── index.css
│ └── main.jsx
│ ├── tailwind.config.js
│ └── vite.config.js
├── jsconfig.json
├── package-lock.json
├── package.json
├── packages
├── bitski-provider
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── jest.config.js
│ ├── package.json
│ ├── scripts
│ │ └── insert-package-version.mjs
│ ├── src
│ │ ├── bitski-provider.ts
│ │ ├── constants.ts
│ │ ├── dialogs
│ │ │ ├── iframe.ts
│ │ │ ├── index.ts
│ │ │ ├── popup.ts
│ │ │ └── shared.ts
│ │ ├── index.ts
│ │ ├── middleware
│ │ │ ├── block-cache.ts
│ │ │ ├── chain-management.ts
│ │ │ ├── eth-accounts.ts
│ │ │ ├── fetch-rest.ts
│ │ │ ├── fetch-rpc.ts
│ │ │ ├── filter.ts
│ │ │ ├── fixture.ts
│ │ │ ├── signature.ts
│ │ │ ├── subscription.ts
│ │ │ ├── transaction-validator.ts
│ │ │ └── typed-data-sanitizer.ts
│ │ ├── signers
│ │ │ ├── browser.ts
│ │ │ ├── dialog.ts
│ │ │ ├── popup.ts
│ │ │ ├── rpc.ts
│ │ │ └── shared.ts
│ │ ├── store.ts
│ │ ├── styles
│ │ │ └── dialog.ts
│ │ ├── types.ts
│ │ └── utils
│ │ │ ├── async.ts
│ │ │ ├── fetch.ts
│ │ │ ├── legacy-middleware.ts
│ │ │ ├── parse-utils.ts
│ │ │ ├── promise-queue.ts
│ │ │ ├── request-context.ts
│ │ │ ├── transaction.ts
│ │ │ └── type-utils.ts
│ ├── tests
│ │ ├── connect.test.ts
│ │ ├── middlewares
│ │ │ ├── block-cache.test.ts
│ │ │ ├── chain-management.test.ts
│ │ │ ├── eth-accounts.test.ts
│ │ │ ├── fetch-rest.test.ts
│ │ │ ├── fetch-rpc.test.ts
│ │ │ ├── filter.test.ts
│ │ │ ├── fixture.test.ts
│ │ │ ├── signature.test.ts
│ │ │ ├── subscription.test.ts
│ │ │ ├── transaction-validator.test.ts
│ │ │ └── typed-data-sanitizer.test.ts
│ │ ├── store.test.ts
│ │ └── util
│ │ │ ├── async.ts
│ │ │ ├── create-provider.ts
│ │ │ ├── mem-store.ts
│ │ │ ├── mock-engine.ts
│ │ │ └── setup-jest.ts
│ ├── tsconfig.json
│ ├── tsconfig.main.json
│ └── tsconfig.module.json
├── bitski
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── callback.html
│ ├── jest.config.js
│ ├── package.json
│ ├── rollup.config.mjs
│ ├── scripts
│ │ ├── copy-readme.mjs
│ │ ├── insert-package-version.mjs
│ │ └── minify.mjs
│ ├── src
│ │ ├── -private
│ │ │ ├── auth
│ │ │ │ ├── access-token.ts
│ │ │ │ ├── auth-provider.ts
│ │ │ │ ├── oauth-manager.ts
│ │ │ │ ├── openid-auth-provider.ts
│ │ │ │ ├── popup-handler.ts
│ │ │ │ ├── token-store.ts
│ │ │ │ └── user.ts
│ │ │ ├── callback.ts
│ │ │ ├── components
│ │ │ │ └── connect-button.ts
│ │ │ ├── constants.ts
│ │ │ ├── network.ts
│ │ │ ├── sdk.ts
│ │ │ ├── styles
│ │ │ │ └── connect-button.ts
│ │ │ └── utils
│ │ │ │ ├── callback.ts
│ │ │ │ ├── no-hash-query-string-utils.ts
│ │ │ │ ├── numbers.ts
│ │ │ │ ├── popup-validator.ts
│ │ │ │ └── request-utils.ts
│ │ ├── index.ts
│ │ ├── load.ts
│ │ └── provider-shim.ts
│ ├── tests
│ │ ├── auth-provider.test.ts
│ │ ├── bitski.test.ts
│ │ ├── connect-button.test.ts
│ │ ├── oauth-manager.test.ts
│ │ ├── shim.test.ts
│ │ └── util
│ │ │ ├── mem-store.ts
│ │ │ ├── mock-oauth-manager.ts
│ │ │ └── setup-jest.ts
│ ├── tsconfig.json
│ ├── tsconfig.main.json
│ └── tsconfig.module.json
├── eth-provider-types
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── index.ts
│ ├── package.json
│ └── tsconfig.json
├── waas-react-sdk
│ ├── .gitignore
│ ├── .prettierignore
│ ├── .prettierrc
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ ├── postcss.config.cjs
│ ├── public
│ │ └── vite.svg
│ ├── schema.graphql
│ ├── src
│ │ ├── App.tsx
│ │ ├── lib
│ │ │ ├── BitskiContext.tsx
│ │ │ ├── api.ts
│ │ │ ├── assets
│ │ │ │ ├── apple.svg
│ │ │ │ ├── arrow-rotate-right-left.svg
│ │ │ │ ├── chains
│ │ │ │ │ ├── icon-base.svg
│ │ │ │ │ ├── icon-ethereum.svg
│ │ │ │ │ ├── icon-optimism.svg
│ │ │ │ │ └── icon-polygon.svg
│ │ │ │ ├── check-checked.svg
│ │ │ │ ├── check-disabled.svg
│ │ │ │ ├── chevron-left-small.svg
│ │ │ │ ├── chevron-right-small.svg
│ │ │ │ ├── coinbase-wallet.svg
│ │ │ │ ├── connector-icon-passkey.svg
│ │ │ │ ├── connector-state-connected.svg
│ │ │ │ ├── connector-state-error.svg
│ │ │ │ ├── cross-small.svg
│ │ │ │ ├── dapp-icon.svg
│ │ │ │ ├── email.svg
│ │ │ │ ├── empty-activities.svg
│ │ │ │ ├── empty-tokens.svg
│ │ │ │ ├── external-wallets.svg
│ │ │ │ ├── google.svg
│ │ │ │ ├── icon-activity-selected.svg
│ │ │ │ ├── icon-activity.svg
│ │ │ │ ├── icon-coinbase.svg
│ │ │ │ ├── icon-disconnect.svg
│ │ │ │ ├── icon-eth.svg
│ │ │ │ ├── icon-matic.svg
│ │ │ │ ├── icon-optimism.svg
│ │ │ │ ├── icon-swaps-selected.svg
│ │ │ │ ├── icon-swaps.svg
│ │ │ │ ├── icon-tokens-selected.svg
│ │ │ │ ├── icon-tokens.svg
│ │ │ │ ├── icon-walletconnect.svg
│ │ │ │ ├── injected-wallet.svg
│ │ │ │ ├── loading.png
│ │ │ │ ├── metamask.svg
│ │ │ │ ├── other-wallets.svg
│ │ │ │ ├── pending.png
│ │ │ │ ├── phantom.svg
│ │ │ │ ├── phone.svg
│ │ │ │ ├── settings.svg
│ │ │ │ ├── waas-logo.png
│ │ │ │ └── x.svg
│ │ │ ├── components
│ │ │ │ ├── BitskiWalletProvider.tsx
│ │ │ │ ├── BitskiWalletViewer.tsx
│ │ │ │ ├── BitskiWidget
│ │ │ │ │ ├── BitskiAuth.styles.css
│ │ │ │ │ ├── BitskiAuth.tsx
│ │ │ │ │ ├── BitskiConnect.tsx
│ │ │ │ │ ├── BitskiProvider.tsx
│ │ │ │ │ ├── BitskiWidget.tsx
│ │ │ │ │ ├── EmailInput.tsx
│ │ │ │ │ ├── SmsInput.tsx
│ │ │ │ │ ├── Socials.tsx
│ │ │ │ │ ├── TOS.tsx
│ │ │ │ │ ├── Wallets.tsx
│ │ │ │ │ ├── constants.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── states
│ │ │ │ │ │ ├── ConnectionSessionCard.tsx
│ │ │ │ │ │ └── Idle.tsx
│ │ │ │ │ └── types
│ │ │ │ │ │ ├── config.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── provider.ts
│ │ │ │ │ │ └── states.ts
│ │ │ │ ├── ChainIcon.tsx
│ │ │ │ ├── ChainSwitcher.tsx
│ │ │ │ ├── CopyAddress.tsx
│ │ │ │ ├── Dialog
│ │ │ │ │ ├── Dialog.styles.css
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── EmptyActivities.tsx
│ │ │ │ ├── EmptySwaps.tsx
│ │ │ │ ├── EmptyTokens.tsx
│ │ │ │ ├── LoadingSpinner.tsx
│ │ │ │ ├── SettingsMenu.tsx
│ │ │ │ ├── Skeleton.tsx
│ │ │ │ └── hooks
│ │ │ │ │ ├── TotalBalanceUSD.graphql
│ │ │ │ │ ├── useActivity.ts
│ │ │ │ │ └── useTokens.ts
│ │ │ ├── connectors
│ │ │ │ ├── bitski.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── localStoragePopup.ts
│ │ │ │ ├── loginMethodConnectorConfigurator.ts
│ │ │ │ └── phantom.ts
│ │ │ ├── constants.ts
│ │ │ ├── generated
│ │ │ │ ├── gql
│ │ │ │ │ ├── fragment-masking.ts
│ │ │ │ │ ├── gql.ts
│ │ │ │ │ ├── graphql.ts
│ │ │ │ │ └── index.ts
│ │ │ │ └── graphql.schema.json
│ │ │ ├── index.css
│ │ │ ├── index.ts
│ │ │ ├── useBitski.tsx
│ │ │ └── utils
│ │ │ │ ├── createBitskiConfig.ts
│ │ │ │ ├── getBlockchainAccounts.ts
│ │ │ │ ├── hasWindowProvider.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── isMobile.ts
│ │ │ │ ├── toFormattedValue.ts
│ │ │ │ ├── toRawValue.ts
│ │ │ │ ├── truncateAddress.ts
│ │ │ │ ├── truncateTitle.ts
│ │ │ │ ├── validateChains.ts
│ │ │ │ └── validateConnectors.ts
│ │ ├── main.tsx
│ │ └── vite-env.d.ts
│ ├── tailwind.config.cjs
│ ├── tsconfig.json
│ └── vite.config.ts
└── wagmi-connector
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── package.json
│ ├── src
│ └── index.ts
│ └── tsconfig.json
└── turbo.json
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4 | with multi-package repos, or single-package repos to help you version and publish your code. You can
5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6 |
7 | We have a quick list of common questions to get you started engaging with this project in
8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
9 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json",
3 | "changelog": [
4 | "@changesets/changelog-github",
5 | {
6 | "repo": "BitskiCo/bitski-js"
7 | }
8 | ],
9 | "commit": false,
10 | "fixed": [],
11 | "linked": [],
12 | "access": "public",
13 | "baseBranch": "main",
14 | "updateInternalDependencies": "patch",
15 | "ignore": []
16 | }
17 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 | end_of_line = lf
9 | charset = utf-8
10 | trim_trailing_whitespace = true
11 | insert_final_newline = true
12 | indent_style = space
13 | indent_size = 2
14 |
15 | [*.{diff,md}]
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | examples
2 | docs
3 |
4 | # compiled output
5 | packages/**/dist/
6 | packages/**/lib/
7 |
8 | # misc
9 | packages/**/coverage/
10 |
11 | # Jest
12 | jest.config.base.js
13 | **/jest.config.js
14 | packages/bitski/scripts/version.js
15 |
16 | typedoc.js
17 |
18 | packages/bitski/tests/bitski.test.test
19 | packages/bitski/src/index.ts
20 | packages/bitski/src/provider-shim.ts
21 |
22 | packages/eth-provider-types/index.d.ts
23 | packages/eth-provider-types/index.js
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": { "browser": true, "es2020": true },
3 | "extends": [
4 | "eslint:recommended",
5 | "plugin:@typescript-eslint/recommended",
6 | "plugin:react-hooks/recommended",
7 | "plugin:react/recommended",
8 | "plugin:prettier/recommended"
9 | ],
10 | "ignorePatterns": ["dist"],
11 | "parser": "@typescript-eslint/parser",
12 | "parserOptions": {
13 | "ecmaVersion": 12,
14 | "sourceType": "module"
15 | },
16 | "plugins": ["@typescript-eslint", "react-refresh"],
17 | "rules": {
18 | "@typescript-eslint/no-var-requires": "off",
19 | "@typescript-eslint/no-explicit-any": "off",
20 | "@typescript-eslint/no-empty-function": "warn",
21 | "react/react-in-jsx-scope": "off",
22 | "react-refresh/only-export-components": ["warn", { "allowConstantExport": true }],
23 | "no-console": "error"
24 | },
25 | "settings": {
26 | "react": {
27 | "version": "detect"
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | docs/* linguist-documentation
2 | docs/api/* linguist-generated
3 | packages/*/lib/* linguist-generated
4 | packages/*/dist/* linguist-generated
5 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | custom_fields: []
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior:
12 |
13 | 1. Go to '...'
14 | 2. Click on '....'
15 | 3. Scroll down to '....'
16 | 4. See error
17 |
18 | **Expected behavior**
19 | A clear and concise description of what you expected to happen.
20 |
21 | **Screenshots**
22 | If applicable, add screenshots to help explain your problem.
23 |
24 | **Desktop (please complete the following information):**
25 |
26 | - OS: [e.g. iOS]
27 | - Browser [e.g. chrome, safari]
28 | - Version [e.g. 22]
29 |
30 | **Smartphone (please complete the following information):**
31 |
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **SDK:**
38 |
39 | - Version [e.g. 0.1.0]
40 |
41 | **Additional context**
42 | Add any other context about the problem here.
43 |
--------------------------------------------------------------------------------
/.github/workflows/cdn-deploy.yaml:
--------------------------------------------------------------------------------
1 | name: Upload CDN
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | deploy:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/setup-node@v2
13 | with:
14 | node-version: '18'
15 | registry-url: https://npm.pkg.github.com
16 |
17 | - uses: actions/checkout@master
18 |
19 | - name: Install Dependencies
20 | run: npm ci
21 | env:
22 | NODE_AUTH_TOKEN: ${{ secrets.GH_PACKAGE_READ_PAT }}
23 |
24 | - name: Build projects
25 | run: npm run build --workspaces || echo "No workspaces found, skipping build";
26 |
27 | - name: Build dist
28 | run: npm run build
29 |
30 | - name: Configure AWS Credentials
31 | uses: aws-actions/configure-aws-credentials@v1
32 | with:
33 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
34 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
35 | aws-region: us-west-2
36 |
37 | - name: Deploy static site to S3 bucket
38 | run: aws s3 sync --acl public-read --delete --cache-control max-age=60 ./packages/bitski/dist/bundled/ s3://cdn.bitskistatic.com/js/sdk/v3.3
39 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: ['main']
6 | pull_request:
7 | types: [opened, synchronize]
8 |
9 | jobs:
10 | build:
11 | name: Build and Test
12 | timeout-minutes: 15
13 | runs-on: ubuntu-latest
14 | strategy:
15 | matrix:
16 | node-version: ['20']
17 |
18 | steps:
19 | - name: Check out code
20 | uses: actions/checkout@v4
21 |
22 | - name: Use Node.js ${{ matrix.node-version }}
23 | uses: actions/setup-node@v4
24 | with:
25 | node-version: ${{ matrix.node-version }}
26 | cache: 'npm'
27 |
28 | - name: Install dependencies
29 | run: npm install
30 |
31 | - name: Format code
32 | run: npm run format
33 |
34 | - name: Lint code
35 | run: npm run lint
36 |
37 | - name: Build
38 | run: npm run build
39 |
40 | - name: Test
41 | run: npm run test
42 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: Release
2 | on:
3 | push:
4 | branches:
5 | - main
6 | jobs:
7 | release:
8 | name: Release
9 | strategy:
10 | matrix:
11 | node-version: ['18']
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout Repo
15 | uses: actions/checkout@v3
16 |
17 | - name: Setup Cache
18 | uses: actions/cache@v3
19 | with:
20 | path: |
21 | node_modules/.cache/turbo
22 | key: ${{ runner.os }}-${{ matrix.os }}-${{ hashFiles('**/package-lock.json') }}
23 | restore-keys: |
24 | ${{ runner.os }}-${{ matrix.os }}-
25 | ${{ runner.os }}-
26 |
27 | - name: Use Node.js ${{ matrix.node-version }}
28 | uses: actions/setup-node@v2
29 | with:
30 | node-version: ${{ matrix.node-version }}
31 |
32 | - name: Install dependencies
33 | run: npm install
34 |
35 | - name: Import GPG key
36 | uses: crazy-max/ghaction-import-gpg@v4
37 | with:
38 | gpg_private_key: ${{ secrets.RELEASE_BOT_GPG_KEY }}
39 | git_user_signingkey: true
40 | git_commit_gpgsign: true
41 |
42 | - name: Create Release Pull Request or Publish to npm
43 | id: changesets
44 | uses: changesets/action@v1
45 | with:
46 | publish: npm run release
47 | setupGitUser: false
48 | env:
49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
50 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | junit.xml
8 |
9 | # Runtime data
10 | pids
11 | *.pid
12 | *.seed
13 | *.pid.lock
14 |
15 | # Directory for instrumented libs generated by jscoverage/JSCover
16 | lib-cov
17 |
18 | # Coverage directory used by tools like istanbul
19 | coverage
20 |
21 | # nyc test coverage
22 | .nyc_output
23 |
24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
25 | .grunt
26 |
27 | # Bower dependency directory (https://bower.io/)
28 | bower_components
29 |
30 | # node-waf configuration
31 | .lock-wscript
32 |
33 | # Compiled binary addons (http://nodejs.org/api/addons.html)
34 | build/Release
35 |
36 | # Dependency directories
37 | node_modules/
38 | jspm_packages/
39 |
40 | # Typescript v1 declaration files
41 | typings/
42 |
43 | # Optional npm cache directory
44 | .npm
45 |
46 | # Optional eslint cache
47 | .eslintcache
48 |
49 | # Optional REPL history
50 | .node_repl_history
51 |
52 | # Output of 'npm pack'
53 | *.tgz
54 |
55 | # Yarn Integrity file
56 | .yarn-integrity
57 |
58 | # dotenv environment variables file
59 | .env
60 |
61 | # Jekyll
62 | _site/*
63 |
64 | # Compiled library
65 | dist
66 | etc
67 |
68 | # Rollup
69 | .rpt2_cache/
70 |
71 | # package locks
72 | examples/package-lock.json
73 | packages/bitski/package-lock.json
74 | packages/bitski-provider/package-lock.json
75 |
76 | .turbo
77 | build/**
78 | dist/**
79 |
80 | # compiled output
81 | packages/**/dist/
82 |
83 | # dependencies
84 | packages/**/node_modules/
85 |
86 | # misc
87 | packages/**/coverage/
88 | .eslintcache
89 |
90 | # Docs, this is copied on build from the root README.md
91 | packages/bitski/README.md
92 |
--------------------------------------------------------------------------------
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
2 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "\$(dirname "\$0")/_/husky.sh"
3 |
4 | npx --no -- commitlint --edit "\${1}"
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | examples
2 | docs
3 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | build
4 | package
5 | .env
6 | .env.*
7 | !.env.example
8 | packages/**/package-lock.json
9 | docs/**
10 | examples/**
11 | typedoc.js
12 |
13 | **/CHANGELOG.md
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "singleQuote": true,
4 | "printWidth": 100
5 | }
6 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Bitski
2 |
3 | We welcome bug reports, feedback, and pull requests to our SDK. Here are a few notes to help those interested in contributing.
4 |
5 | ## Reporting Issues
6 |
7 | Please use our issue template to give us the details we'll need to debug. You can find the template under .github/ISSUE_TEMPLATE/Bug_report.md.
8 |
9 | ## Development
10 |
11 | This project uses lerna to manage multiple NPM packages in a single repo. In order to get started developing, clone this repo, then run `npm install` to install lerna and various dev dependencies.
12 |
13 | When you run `npm install`, we have configured the project to also run `lerna bootstrap` which installs the dependencies of each package and creates links for shared dependencies.
14 |
15 | ## Building
16 |
17 | To do a build, from the root project run `npm run build`. This will run a build on each package using lerna.
18 |
19 | ## Testing
20 |
21 | To run the tests, run `npm test` from the root project. This will also generate a coverage report under the coverage folder.
22 |
23 | ## Pull Requests
24 |
25 | Please base your changes off and submit PRs against the develop branch. Develop is our pre-release branch, while master is our released branch. Our CI will automatically run tests for each package.
26 |
27 | ## Releases
28 |
29 | In order to keep things in sync we use lerna to release new versions to NPM. Here is the process we use:
30 |
31 | 1. Checkout develop branch
32 | 2. Pull from develop
33 | 3. `lerna publish` (select version) bumps version in packages, tags, tarballs, publishes, pushes tags and commit develop
34 | 4. Checkout new branch (release/version-number)
35 | 5. Checkout master
36 | 6. `git merge --no-ff release/version-number`
37 | 7. `git push`
38 | 8. Add release under Releases with new tag
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018 Out There Labs, Inc
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/MIGRATING.md:
--------------------------------------------------------------------------------
1 | # Migrating from 2.x to 3.x
2 |
3 | - Add `https://cdn.bitskistatic.com` to your CSP to allow the full SDK to load:
4 |
5 | - `connect-src`: `https://api.bitski.com`
6 | - `script-src`: `https://cdn.bitskistatic.com`
7 |
8 | - The provider now implements the standard `request()` method. Users should
9 | update to this method and move away from `send` and `sendAsync`, but these
10 | legacy methods are still available for the time being.
11 |
12 | - The signature and behavior of `send` and `sendAsync` has been updated to match
13 | the standard behavior of other providers (see Web3 or Ethers docs for details).
14 |
15 | - The provider now supports the `wallet_switchEthereumChain` and
16 | `wallet_addEthereumChain` RPC methods.
17 |
18 | - `Bitski.addProvider` no longer returns different instances of the provider for
19 | different networks. It instead returns a single provider instance, and if a
20 | network is specified, it calls `wallet_switchEthereumChain` to change the active
21 | chain of the provider.
22 |
23 | # Migrating from 1.x to 2.x
24 |
25 | - The `authStatus` property has been changed to the `getAuthStatus()`, which now
26 | returns a promise instead of the auth status directly.
27 |
28 | ```js
29 | // before
30 | const status = bitski.authStatus;
31 |
32 | // after
33 | const status = await bitski.getAuthStatus();
34 | ```
35 |
36 | - The `Store` interface must now return promises for all of its functions. The
37 | default behavior still uses local storage.
38 |
--------------------------------------------------------------------------------
/docs/images/dev-portal-client-details.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BitskiCo/bitski-js/82a2a6faac4bfead544ca6d26769b843ed8f6ecc/docs/images/dev-portal-client-details.png
--------------------------------------------------------------------------------
/docs/images/dev-portal-new-app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BitskiCo/bitski-js/82a2a6faac4bfead544ca6d26769b843ed8f6ecc/docs/images/dev-portal-new-app.png
--------------------------------------------------------------------------------
/docs/images/dev-portal-redirect-urls.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BitskiCo/bitski-js/82a2a6faac4bfead544ca6d26769b843ed8f6ecc/docs/images/dev-portal-redirect-urls.png
--------------------------------------------------------------------------------
/examples/ethers/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/examples/ethers/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/ethers/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bitski-web3js-example",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.0.28",
18 | "@types/react-dom": "^18.0.11",
19 | "@vitejs/plugin-react": "^4.0.0",
20 | "autoprefixer": "^10.4.14",
21 | "bitski": "^3.3.0",
22 | "daisyui": "^2.51.6",
23 | "eslint": "^8.38.0",
24 | "eslint-plugin-react": "^7.32.2",
25 | "eslint-plugin-react-hooks": "^4.6.0",
26 | "eslint-plugin-react-refresh": "^0.3.4",
27 | "ethers": "^6.3.0",
28 | "postcss": "^8.4.31",
29 | "react-daisyui": "^3.1.2",
30 | "tailwindcss": "^3.3.2",
31 | "vite": "^4.5.2"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/examples/ethers/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/examples/ethers/public/callback.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Logging in...
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/examples/ethers/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/examples/ethers/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.jsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/examples/ethers/tailwind.config.js:
--------------------------------------------------------------------------------
1 | import daisyui from 'daisyui';
2 |
3 | /** @type {import('tailwindcss').Config} */
4 | export default {
5 | content: [
6 | 'node_modules/daisyui/dist/**/*.js',
7 | 'node_modules/react-daisyui/dist/**/*.js',
8 | "./index.html",
9 | "./src/**/*.{js,ts,jsx,tsx}",
10 | ],
11 | theme: {
12 | extend: {},
13 | },
14 | plugins: [daisyui],
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/examples/ethers/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/examples/standalone/.gitignore:
--------------------------------------------------------------------------------
1 | dist/
2 |
--------------------------------------------------------------------------------
/examples/standalone/README.md:
--------------------------------------------------------------------------------
1 | # Examples
2 | To run these, run ```npm install; npm run start:dev``` and then open http://localhost:8080/
--------------------------------------------------------------------------------
/examples/standalone/callback/callback.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Logging in...
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/examples/standalone/common.js:
--------------------------------------------------------------------------------
1 | import { Bitski } from 'bitski';
2 | import Web3 from 'web3';
3 |
4 | console.log("Setting up bitski...");
5 |
6 | window.bitski = new Bitski('3b6d0360-071c-4210-8862-176164d6ec76', 'http://localhost:8080/callback/callback.html');
7 | window.provider = window.bitski.getProvider();
8 | window.web3 = new Web3(window.provider);
9 |
10 | window.web3.eth.getBlockNumber().then(number => console.log(number));
11 |
--------------------------------------------------------------------------------
/examples/standalone/connect-button/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Connect Button Examples
10 |
11 | Small Button
12 |
13 |
14 | Medium Button (Default)
15 |
16 |
17 | Large Button
18 |
19 |
20 | Default Invocation
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/examples/standalone/connect-button/index.js:
--------------------------------------------------------------------------------
1 | window.addEventListener("load", function(){
2 | // Create small button
3 | window.bitski.getConnectButton({ container: document.querySelector('#small-button-container'), size: 'SMALL' }, callback);
4 |
5 | // Create medium button
6 | window.bitski.getConnectButton({ container: document.querySelector('#medium-button-container'), size: 'MEDIUM' }, callback);
7 |
8 | // Create large button
9 | window.bitski.getConnectButton({ container: document.querySelector('#large-button-container'), size: 'LARGE' }, callback);
10 |
11 | var defaultButton = window.bitski.getConnectButton();
12 | defaultButton.callback = callback;
13 |
14 | document.querySelector('#default-button').appendChild(defaultButton.element);
15 | });
16 |
17 | function callback(error, user) {
18 | if(user) {
19 | window.web3.eth.getAccounts().then((accounts) => {
20 | var account = accounts[0];
21 | console.log("Getting balance for " + account);
22 | return window.web3.eth.getBalance(account);
23 | }).then((balance) => {
24 | alert('Signed in! Current balance: ' + balance);
25 | return window.bitski.signOut();
26 | }).catch((error) => {
27 | console.error("Error: " + error);
28 | });
29 | }
30 |
31 | if (error) {
32 | console.error("Error signing in: " + error);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/examples/standalone/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Connect Button Example
7 | Sign Typed Data
8 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/standalone/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "start:dev": "webpack-dev-server --mode development"
4 | },
5 | "dependencies": {
6 | "bitski": "^3.3.0",
7 | "web3": "1.9.0"
8 | },
9 | "devDependencies": {
10 | "webpack": "^4.17.1",
11 | "webpack-cli": "^3.1.0",
12 | "webpack-dev-server": "^3.1.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/examples/standalone/sign-typed-data/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Sign Typed Data
10 |
11 |
14 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/standalone/sign-typed-data/index.js:
--------------------------------------------------------------------------------
1 | window.addEventListener("load", function() {
2 | const bitski = window.bitski;
3 |
4 | // Create connect button
5 | const connectButton = bitski.getConnectButton({ container: document.querySelector('#connect-button') });
6 | connectButton.callback = function(error, user){
7 | if (user) {
8 | updateLoggedInState();
9 | }
10 |
11 | if (error) {
12 | console.error("Error signing in: " + error);
13 | }
14 | };
15 |
16 | const signButton = document.querySelector("#sign-button");
17 | signButton.onclick = () => {
18 | signTypedData();
19 | return false;
20 | };
21 |
22 | updateLoggedInState();
23 | });
24 |
25 | function updateLoggedInState() {
26 | const loggedOutContainer = document.querySelector('#logged-out');
27 | const loggedInContainer = document.querySelector('#logged-in');
28 |
29 | if (bitski.authStatus === 'NOT_CONNECTED') {
30 | loggedOutContainer.style = 'display: block;';
31 | loggedInContainer.style = 'display: none;';
32 | } else {
33 | loggedOutContainer.style = 'display: none;';
34 | loggedInContainer.style = 'display: block;';
35 | }
36 | }
37 |
38 | function signTypedData() {
39 | const web3 = window.web3;
40 | const provider = window.provider;
41 | const msgParams = {
42 | types: {
43 | EIP712Domain: [
44 | { name: 'name', type: 'string' },
45 | { name: 'version', type: 'string' },
46 | { name: 'chainId', type: 'uint256' },
47 | { name: 'verifyingContract', type: 'address' },
48 | ],
49 | Person: [
50 | { name: 'name', type: 'string' },
51 | { name: 'wallet', type: 'address' }
52 | ],
53 | Mail: [
54 | { name: 'from', type: 'Person' },
55 | { name: 'to', type: 'Person' },
56 | { name: 'contents', type: 'string' }
57 | ],
58 | },
59 | primaryType: 'Mail',
60 | domain: {
61 | name: 'Ether Mail',
62 | version: '0x1',
63 | chainId: '0x1',
64 | verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
65 | },
66 | message: {
67 | from: {
68 | name: 'Cow',
69 | wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
70 | },
71 | to: {
72 | name: 'Bob',
73 | wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
74 | },
75 | contents: 'Hello, Bob!',
76 | },
77 | };
78 | console.log("Sending: ", JSON.stringify(msgParams));
79 | web3.eth.getAccounts().then(accounts => {
80 | const from = accounts[0];
81 | provider.send('eth_signTypedData', [from, msgParams]).then(response => {
82 | console.log("Success! Response: " + response);
83 | alert(`Signed data: ${response}`);
84 | }).catch(err => {
85 | console.error('Error signing: ' + err);
86 | });
87 | }).catch(err => {
88 | console.error('Error getting accounts: '+ err);
89 | });
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/examples/standalone/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | entry: './common.js',
5 | devtool: 'inline-source-map',
6 | node: {
7 | fs: 'empty'
8 | },
9 | output: {
10 | path: path.resolve(__dirname, 'dist'),
11 | filename: 'common.js'
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/examples/viem/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/examples/viem/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/viem/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bitski-web3js-example",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.0.28",
18 | "@types/react-dom": "^18.0.11",
19 | "@vitejs/plugin-react": "^4.0.0",
20 | "autoprefixer": "^10.4.14",
21 | "bitski": "^3.3.0",
22 | "daisyui": "^2.51.6",
23 | "eslint": "^8.38.0",
24 | "eslint-plugin-react": "^7.32.2",
25 | "eslint-plugin-react-hooks": "^4.6.0",
26 | "eslint-plugin-react-refresh": "^0.3.4",
27 | "postcss": "^8.4.31",
28 | "react-daisyui": "^3.1.2",
29 | "tailwindcss": "^3.3.2",
30 | "viem": "^0.3.16",
31 | "vite": "^4.5.2"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/examples/viem/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/examples/viem/public/callback.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Logging in...
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/examples/viem/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/examples/viem/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.jsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/examples/viem/tailwind.config.js:
--------------------------------------------------------------------------------
1 | import daisyui from 'daisyui';
2 |
3 | /** @type {import('tailwindcss').Config} */
4 | export default {
5 | content: [
6 | 'node_modules/daisyui/dist/**/*.js',
7 | 'node_modules/react-daisyui/dist/**/*.js',
8 | "./index.html",
9 | "./src/**/*.{js,ts,jsx,tsx}",
10 | ],
11 | theme: {
12 | extend: {},
13 | },
14 | plugins: [daisyui],
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/examples/viem/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/examples/web3js/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: { browser: true, es2020: true },
3 | extends: [
4 | 'eslint:recommended',
5 | 'plugin:react/recommended',
6 | 'plugin:react/jsx-runtime',
7 | 'plugin:react-hooks/recommended',
8 | ],
9 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
10 | settings: { react: { version: '18.2' } },
11 | plugins: ['react-refresh'],
12 | rules: {
13 | 'react-refresh/only-export-components': 'warn',
14 | },
15 | }
16 |
--------------------------------------------------------------------------------
/examples/web3js/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/examples/web3js/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/web3js/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bitski-web3js-example",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.0.28",
18 | "@types/react-dom": "^18.0.11",
19 | "@vitejs/plugin-react": "^4.0.0",
20 | "autoprefixer": "^10.4.14",
21 | "bitski": "^3.3.0",
22 | "daisyui": "^2.51.6",
23 | "eslint": "^8.38.0",
24 | "eslint-plugin-react": "^7.32.2",
25 | "eslint-plugin-react-hooks": "^4.6.0",
26 | "eslint-plugin-react-refresh": "^0.3.4",
27 | "postcss": "^8.4.31",
28 | "react-daisyui": "^3.1.2",
29 | "tailwindcss": "^3.3.2",
30 | "vite": "^4.5.2",
31 | "web3": "^4.0.3"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/examples/web3js/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/examples/web3js/public/callback.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Logging in...
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/examples/web3js/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/examples/web3js/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.jsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root')).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/examples/web3js/tailwind.config.js:
--------------------------------------------------------------------------------
1 | import daisyui from 'daisyui';
2 |
3 | /** @type {import('tailwindcss').Config} */
4 | export default {
5 | content: [
6 | 'node_modules/daisyui/dist/**/*.js',
7 | 'node_modules/react-daisyui/dist/**/*.js',
8 | "./index.html",
9 | "./src/**/*.{js,ts,jsx,tsx}",
10 | ],
11 | theme: {
12 | extend: {},
13 | },
14 | plugins: [daisyui],
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/examples/web3js/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": { "target": "es6", "experimentalDecorators": true },
3 | "exclude": ["node_modules", "bower_components", "tmp", "vendor", ".git", "dist"]
4 | }
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bitski-js",
3 | "private": true,
4 | "workspaces": [
5 | "packages/*"
6 | ],
7 | "scripts": {
8 | "build": "turbo run build",
9 | "test": "turbo run test",
10 | "check": "turbo run check",
11 | "package": "turbo run package",
12 | "lint": "eslint .",
13 | "format": "prettier --write --plugin-search-dir=. .",
14 | "release": "turbo run build && changeset publish"
15 | },
16 | "volta": {
17 | "node": "18.17.0"
18 | },
19 | "version": "0.0.0",
20 | "devDependencies": {
21 | "@changesets/changelog-github": "^0.4.4",
22 | "@changesets/cli": "^2.22.0",
23 | "@commitlint/cli": "^17.6.1",
24 | "@commitlint/config-conventional": "^16.2.4",
25 | "eslint-plugin-react": "^7.33.2",
26 | "eslint-plugin-react-hooks": "^4.6.0",
27 | "eslint-plugin-react-refresh": "^0.4.5",
28 | "@typescript-eslint/eslint-plugin": "^6.14.0",
29 | "@typescript-eslint/parser": "^6.14.0",
30 | "eslint": "^8.55.0",
31 | "eslint-config-prettier": "^8.3.0",
32 | "eslint-plugin-prettier": "^4.0.0",
33 | "husky": "^8.0.1",
34 | "jest-fetch-mock": "^3.0.3",
35 | "prettier": "^2.4.1",
36 | "replace-in-files": "^3.0.0",
37 | "turbo": "^1.2.4",
38 | "typescript": "^5.2.2"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/packages/bitski-provider/.gitignore:
--------------------------------------------------------------------------------
1 | lib
--------------------------------------------------------------------------------
/packages/bitski-provider/jest.config.js:
--------------------------------------------------------------------------------
1 | // jshint esversion: 9
2 | module.exports = {
3 | roots: ['/src', '/tests'],
4 | transform: {
5 | '^.+\\.ts$': [
6 | 'ts-jest',
7 | {
8 | diagnostics: false,
9 | },
10 | ],
11 | },
12 | testRegex: '(/tests/.*.(test|spec)).(jsx?|tsx?)$',
13 | automock: false,
14 | collectCoverage: true,
15 | collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**'],
16 | coveragePathIgnorePatterns: [],
17 | moduleFileExtensions: ['js', 'ts'],
18 | coverageReporters: ['json', 'text', 'html', 'cobertura'],
19 | verbose: true,
20 | globals: {
21 | 'ts-jest': {
22 | diagnostics: false,
23 | },
24 | },
25 | displayName: 'Bitski Provider',
26 | testEnvironment: 'jsdom',
27 | setupFiles: ['/tests/util/setup-jest.ts'],
28 | };
29 |
--------------------------------------------------------------------------------
/packages/bitski-provider/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bitski-provider",
3 | "description": "Core Bitski provider",
4 | "license": "MIT",
5 | "main": "lib/index.js",
6 | "module": "dist/index.js",
7 | "types": "lib/index.d.ts",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/BitskiCo/bitski-js"
11 | },
12 | "version": "3.5.2",
13 | "scripts": {
14 | "test": "jest",
15 | "lint": "eslint . --cache",
16 | "build": "tsc -p tsconfig.main.json && tsc -p tsconfig.module.json && node ./scripts/insert-package-version.mjs",
17 | "prettier": "prettier --config ../../.prettierrc '{src,tests}/**/*.ts' --write"
18 | },
19 | "dependencies": {
20 | "@metamask/safe-event-emitter": "2.0.0",
21 | "bn.js": "^4.11.8",
22 | "decoders": "^2.0.1",
23 | "eth-block-tracker": "^5.0.0",
24 | "eth-json-rpc-filters": "^5.0.0",
25 | "eth-json-rpc-middleware": "^9.0.1",
26 | "eth-provider-types": "^0.2.0",
27 | "eth-rpc-errors": "^4.0.3",
28 | "json-rpc-engine": "^6.1.0",
29 | "json-rpc-error": "^2.0.0",
30 | "uuid": "9.0.0"
31 | },
32 | "devDependencies": {
33 | "@babel/core": "^7.6.4",
34 | "@babel/plugin-transform-runtime": "^7.6.2",
35 | "@babel/preset-env": "^7.6.3",
36 | "@testing-library/dom": "^8.19.0",
37 | "@types/jest": "^29.5.11",
38 | "@types/node": "^20.11.5",
39 | "babelify": "^10.0.0",
40 | "jest": "29.7.0",
41 | "jest-fetch-mock": "^3.0.3",
42 | "jest-environment-jsdom": "^29.0.0",
43 | "ts-jest": "^29.1.1"
44 | },
45 | "browserify": {
46 | "transform": [
47 | [
48 | "babelify",
49 | {
50 | "presets": [
51 | "@babel/preset-env"
52 | ],
53 | "plugins": [
54 | "@babel/plugin-transform-runtime"
55 | ]
56 | }
57 | ]
58 | ]
59 | },
60 | "gitHead": "7b9f0b01dd8a36a4294f27740ce264ecd95af35c"
61 | }
62 |
--------------------------------------------------------------------------------
/packages/bitski-provider/scripts/insert-package-version.mjs:
--------------------------------------------------------------------------------
1 | import replaceInFiles from 'replace-in-files';
2 | import packageJson from '../package.json' assert { type: 'json' };
3 |
4 | await replaceInFiles({
5 | files: ['./lib/**/*', './dist/**/*'],
6 | from: 'BITSKI_PROVIDER_VERSION',
7 | to: `"bitski-provider-v${packageJson.version}"`,
8 | });
9 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/dialogs/index.ts:
--------------------------------------------------------------------------------
1 | export interface DialogOpts {
2 | url: string;
3 | handleMessage: (message: Message) => Result;
4 | handleClose: () => Result;
5 | }
6 |
7 | export type OpenDialog = (
8 | opts: DialogOpts,
9 | ) => {
10 | result: Promise;
11 | cancel: () => void;
12 | };
13 |
14 | export { openIframeDialog } from './iframe';
15 | export { openPopupDialog } from './popup';
16 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/dialogs/popup.ts:
--------------------------------------------------------------------------------
1 | import { OpenDialog } from '.';
2 | import { addPostMessageHandler } from './shared';
3 |
4 | const POPUP_HEIGHT = 620;
5 | const POPUP_WIDTH = 390;
6 |
7 | export const openPopupDialog: OpenDialog = (opts) => {
8 | // Fixes dual-screen position Most browsers Firefox
9 | const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : window.screenX;
10 | const dualScreenTop = window.screenTop !== undefined ? window.screenTop : window.screenY;
11 |
12 | const width = window.innerWidth
13 | ? window.innerWidth
14 | : document.documentElement.clientWidth
15 | ? document.documentElement.clientWidth
16 | : screen.width;
17 | const height = window.innerHeight
18 | ? window.innerHeight
19 | : document.documentElement.clientHeight
20 | ? document.documentElement.clientHeight
21 | : screen.height;
22 |
23 | const systemZoom = width / window.screen.availWidth;
24 | const left = (width - POPUP_WIDTH) / 2 / systemZoom + dualScreenLeft;
25 | const top = (height - POPUP_HEIGHT) / 2 / systemZoom + dualScreenTop;
26 | const newWindow = window.open(
27 | opts.url,
28 | '_blank',
29 | `
30 | width=${POPUP_WIDTH / systemZoom},
31 | height=${POPUP_HEIGHT / systemZoom},
32 | top=${top},
33 | left=${left}
34 | `,
35 | );
36 |
37 | if (!newWindow) {
38 | throw new Error('Could not open the signer window, please enable popups.');
39 | }
40 |
41 | return {
42 | result: new Promise((resolve, reject) => {
43 | let finished = false;
44 |
45 | addPostMessageHandler(
46 | opts.handleMessage,
47 | () => {
48 | finished = true;
49 | newWindow.close();
50 | },
51 | resolve,
52 | reject,
53 | );
54 |
55 | const pollTimer = window.setInterval(() => {
56 | if (newWindow.closed !== false) {
57 | window.clearInterval(pollTimer);
58 |
59 | if (finished) return;
60 |
61 | try {
62 | resolve(opts.handleClose());
63 | } catch (e) {
64 | reject(e);
65 | }
66 | }
67 | }, 200);
68 |
69 | newWindow.focus();
70 | }),
71 | cancel: () => newWindow.close(),
72 | };
73 | };
74 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/dialogs/shared.ts:
--------------------------------------------------------------------------------
1 | import { IFRAME_MESSAGE_ORIGIN_ENDS_WITH } from '../constants';
2 |
3 | export const addPostMessageHandler = (
4 | handleMessage: (message: Message) => Result,
5 | close: () => void,
6 | resolve: (result: Result) => void,
7 | reject: (e: unknown) => void,
8 | ) => {
9 | if (typeof window === 'undefined') return;
10 |
11 | window.addEventListener('message', (event: MessageEvent) => {
12 | // Ignore messages from the current window, and from frames that aren't on Bitski.com
13 | if (event.source === window || !event.origin.endsWith(IFRAME_MESSAGE_ORIGIN_ENDS_WITH)) {
14 | return;
15 | }
16 |
17 | const data = event.data;
18 |
19 | // Ignore message events that don't actually have data
20 | if (data === undefined || data === null) {
21 | return;
22 | }
23 |
24 | try {
25 | resolve(handleMessage(data));
26 | } catch (e) {
27 | reject(e);
28 | } finally {
29 | close();
30 | }
31 | });
32 | };
33 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './types';
2 | export * from './constants';
3 | export { LocalStorageStore } from './store';
4 | export { createBitskiProvider, BitskiProvider } from './bitski-provider';
5 | export { default as createBrowserSigner } from './signers/browser';
6 | export { default as createRpcSigner } from './signers/rpc';
7 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/middleware/block-cache.ts:
--------------------------------------------------------------------------------
1 | import { createBlockCacheMiddleware as legacyCreateBlockCacheMiddleware } from 'eth-json-rpc-middleware/dist/block-cache';
2 | import { JsonRpcMiddleware } from 'json-rpc-engine';
3 | import { createLegacyMiddleware } from '../utils/legacy-middleware';
4 |
5 | export const createBlockCacheMiddleware = (): JsonRpcMiddleware =>
6 | createLegacyMiddleware(({ blockTracker }) => {
7 | // TODO type mismatch but they should be compatible
8 | return legacyCreateBlockCacheMiddleware({ blockTracker: blockTracker as any });
9 | });
10 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/middleware/chain-management.ts:
--------------------------------------------------------------------------------
1 | import {
2 | EthChainDefinition,
3 | EthEvent,
4 | EthMethod,
5 | SwitchEthereumChainParameter,
6 | } from 'eth-provider-types';
7 | import { ethErrors } from 'eth-rpc-errors';
8 | import { createAsyncMiddleware, JsonRpcMiddleware } from 'json-rpc-engine';
9 | import { getRequestContext } from '../utils/request-context';
10 | import { expect } from '../utils/type-utils';
11 |
12 | export const createChainManagementMiddleware = (): JsonRpcMiddleware => {
13 | return createAsyncMiddleware(async (req, res, next) => {
14 | const { method } = req;
15 | const context = getRequestContext(req);
16 |
17 | if (method === EthMethod.eth_chainId) {
18 | res.result = context.chain.chainId;
19 |
20 | return;
21 | }
22 |
23 | if (method === EthMethod.wallet_addEthereumChain) {
24 | const definition = expect(
25 | req.params?.[0],
26 | 'addEthereumChain requires a chain definition parameter',
27 | ) as EthChainDefinition;
28 |
29 | await context.store.addChain(definition);
30 |
31 | res.result = null;
32 |
33 | return;
34 | }
35 |
36 | if (method === EthMethod.wallet_switchEthereumChain) {
37 | const chainDetails = expect(
38 | req.params?.[0],
39 | 'switchEthereumChain requires a chainId',
40 | ) as SwitchEthereumChainParameter;
41 |
42 | const chain = await context.store.findChain(chainDetails.chainId);
43 |
44 | if (!chain) {
45 | throw ethErrors.provider.userRejectedRequest({ message: 'Chain does not exist' });
46 | }
47 |
48 | await context.store.setCurrentChainId(chainDetails.chainId);
49 | context.emit(EthEvent.chainChanged, chainDetails.chainId);
50 |
51 | res.result = null;
52 |
53 | return;
54 | }
55 |
56 | return next();
57 | });
58 | };
59 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/middleware/eth-accounts.ts:
--------------------------------------------------------------------------------
1 | import { EthMethod } from 'eth-provider-types';
2 | import { createAsyncMiddleware, JsonRpcMiddleware } from 'json-rpc-engine';
3 | import { getRequestContext } from '../utils/request-context';
4 | import { InternalBitskiProviderConfig } from '../types';
5 | import { fetchJsonWithRetry } from '../utils/fetch';
6 | import { ethErrors } from 'eth-rpc-errors';
7 |
8 | const REFRESH = Symbol();
9 | const ANON_USER = 'anonymous';
10 |
11 | interface BlockchainAccount {
12 | id: string;
13 | profileId: string;
14 | displayName: string;
15 | kind: string;
16 | coinType: number;
17 | address: string;
18 | createdAt: string;
19 | updatedAt: string;
20 | }
21 |
22 | // We fetch accounts directly in this middleware so that we ensure we're always
23 | // hitting Bitski direcly and not another RPC url (e.g. for custom RPCs/chains).
24 | const fetchAccounts = async (config: InternalBitskiProviderConfig): Promise => {
25 | const headers = { ...config.additionalHeaders };
26 |
27 | if (config.getAccessToken) {
28 | headers['Authorization'] = `Bearer ${await config.getAccessToken()}`;
29 | }
30 |
31 | const { accounts } = (await fetchJsonWithRetry(
32 | config.fetch,
33 | 5,
34 | `${config.apiBaseUrl}/v2/blockchain/accounts`,
35 | {
36 | method: 'GET',
37 | headers,
38 | },
39 | )) as { accounts: BlockchainAccount[] };
40 |
41 | if (!accounts) {
42 | throw ethErrors.rpc.internal('Could not find blockchain accounts');
43 | }
44 |
45 | const moreThanOneAccount = accounts.length > 1;
46 | const mainAccount = accounts.find((a) => a.kind === 'bitski');
47 |
48 | if (moreThanOneAccount && !mainAccount) {
49 | throw ethErrors.rpc.internal('Could not find blockchain accounts');
50 | }
51 |
52 | if (moreThanOneAccount && mainAccount) {
53 | return [mainAccount.address];
54 | }
55 |
56 | const accountAddresses = accounts.map((a) => a.address);
57 |
58 | return accountAddresses;
59 | };
60 |
61 | export const createEthAccountsMiddleware = (): JsonRpcMiddleware => {
62 | const cache = new Map();
63 |
64 | return createAsyncMiddleware(async (req, res, next) => {
65 | if (req.method !== EthMethod.eth_accounts && req.method !== EthMethod.eth_requestAccounts) {
66 | return next();
67 | }
68 |
69 | const { config } = getRequestContext(req);
70 | const user = await config.getUser?.();
71 |
72 | const userId = user?.id ?? ANON_USER;
73 |
74 | let accounts = cache.get(userId);
75 |
76 | if (!accounts || accounts === REFRESH) {
77 | accounts = await fetchAccounts(config);
78 |
79 | cache.set(userId, accounts);
80 | setTimeout(() => cache.set(userId, REFRESH), 5 * 60 * 1000);
81 | }
82 |
83 | res.result = accounts;
84 | });
85 | };
86 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/middleware/fetch-rest.ts:
--------------------------------------------------------------------------------
1 | import { EthMethod } from 'eth-provider-types';
2 | import { createAsyncMiddleware, JsonRpcMiddleware } from 'json-rpc-engine';
3 | import { getRequestContext } from '../utils/request-context';
4 | import { fetchJsonWithRetry } from '../utils/fetch';
5 |
6 | const MATCHING_METHODS: string[] = [
7 | EthMethod.eth_getBlockByNumber,
8 | EthMethod.eth_blockNumber,
9 | EthMethod.net_version,
10 | EthMethod.eth_getLogs,
11 | ];
12 |
13 | export const createFetchRestMiddleware = (): JsonRpcMiddleware => {
14 | return createAsyncMiddleware(async (req, res, next) => {
15 | if (!MATCHING_METHODS.includes(req.method)) {
16 | return next();
17 | }
18 |
19 | const { config, chain } = getRequestContext(req);
20 |
21 | const rpcUrl = new URL(chain.rpcUrls[0]);
22 | if (rpcUrl.hostname !== 'api.bitski.com') {
23 | // Custom RPC url
24 | return next();
25 | }
26 |
27 | const query =
28 | (req.params?.length ?? 0) > 0
29 | ? `?params=${encodeURIComponent(JSON.stringify(req.params))}`
30 | : '';
31 |
32 | const url = `${chain.rpcUrls[0]}/${req.method}${query}`;
33 |
34 | const headers = { ...config.additionalHeaders };
35 |
36 | res.result = fetchJsonWithRetry(config.fetch, 5, url, {
37 | method: 'GET',
38 | headers,
39 | credentials: 'omit',
40 | });
41 | });
42 | };
43 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/middleware/fetch-rpc.ts:
--------------------------------------------------------------------------------
1 | import { createAsyncMiddleware, JsonRpcMiddleware } from 'json-rpc-engine';
2 | import { getRequestContext } from '../utils/request-context';
3 | import { fetchJsonRpcWithRetry } from '../utils/fetch';
4 |
5 | export const createFetchRpcMiddleware = (): JsonRpcMiddleware => {
6 | return createAsyncMiddleware(async (req, res) => {
7 | const { config, chain } = getRequestContext(req);
8 |
9 | const headers = { ...config.additionalHeaders };
10 |
11 | res.result = await fetchJsonRpcWithRetry(config.fetch, 5, chain.rpcUrls[0], {
12 | method: 'POST',
13 | headers,
14 | body: {
15 | id: req.id,
16 | jsonrpc: req.jsonrpc,
17 | method: req.method,
18 | params: req.params,
19 | },
20 | });
21 | });
22 | };
23 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/middleware/filter.ts:
--------------------------------------------------------------------------------
1 | import { JsonRpcMiddleware } from 'json-rpc-engine';
2 | import legacyCreateFilterMiddleware from 'eth-json-rpc-filters';
3 | import { createLegacyMiddleware } from '../utils/legacy-middleware';
4 |
5 | export const createFilterMiddleware = (): JsonRpcMiddleware =>
6 | createLegacyMiddleware(({ blockTracker, provider, context }) => {
7 | const middleware = legacyCreateFilterMiddleware({
8 | blockTracker,
9 | provider,
10 | }) as JsonRpcMiddleware & { destroy(): void };
11 |
12 | context.addDestructor(middleware.destroy);
13 |
14 | return middleware;
15 | });
16 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/middleware/fixture.ts:
--------------------------------------------------------------------------------
1 | import { EthMethod, EthMethodResults } from 'eth-provider-types';
2 | import { JsonRpcMiddleware } from 'json-rpc-engine';
3 |
4 | export type Fixtures = Partial<{
5 | [key in EthMethod]: EthMethodResults[key];
6 | }>;
7 |
8 | export const DEFAULT_FIXTURES: Fixtures = {
9 | [EthMethod.web3_clientVersion]: 'Bitski/latest',
10 | [EthMethod.net_listening]: true,
11 | [EthMethod.eth_hashrate]: '0x00',
12 | [EthMethod.eth_mining]: false,
13 | };
14 |
15 | export const createFixtureMiddleware = (
16 | fixtures: Fixtures = DEFAULT_FIXTURES,
17 | ): JsonRpcMiddleware => {
18 | return (req, res, next, end) => {
19 | const fixture = fixtures[req.method];
20 |
21 | if (fixture !== undefined) {
22 | res.result = fixture;
23 | return end();
24 | } else {
25 | next();
26 | }
27 | };
28 | };
29 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/middleware/signature.ts:
--------------------------------------------------------------------------------
1 | import { EthMethod } from 'eth-provider-types';
2 | import { createAsyncMiddleware, JsonRpcMiddleware } from 'json-rpc-engine';
3 | import { SIGN_METHODS, SUPPORTED_CHAIN_IDS } from '../constants';
4 | import { getRequestContext } from '../utils/request-context';
5 | import { EthSignMethod, EthSignMethodParams } from '../types';
6 |
7 | export const createSignatureMiddleware = (): JsonRpcMiddleware => {
8 | return createAsyncMiddleware(async (req, res, next) => {
9 | if (!SIGN_METHODS.includes(req.method)) {
10 | return next();
11 | }
12 |
13 | const context = getRequestContext(req);
14 |
15 | const requiresLocalSend =
16 | !SUPPORTED_CHAIN_IDS.includes(context.chain.chainId) &&
17 | req.method === EthMethod.eth_sendTransaction;
18 |
19 | // When we don't support a chain on the server (e.g. custom RPC url), we need
20 | // to sign the transaction via our flow, then send the signed payload locally
21 | const method = requiresLocalSend
22 | ? EthMethod.eth_signTransaction
23 | : (req.method as EthSignMethod);
24 |
25 | const signedResponse = await context.config.sign(
26 | method,
27 | req.params as EthSignMethodParams[EthSignMethod],
28 | context,
29 | );
30 |
31 | res.result = requiresLocalSend
32 | ? context.request({ method: EthMethod.eth_sendRawTransaction, params: [signedResponse] })
33 | : signedResponse;
34 | });
35 | };
36 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/middleware/subscription.ts:
--------------------------------------------------------------------------------
1 | import { JsonRpcMiddleware } from 'json-rpc-engine';
2 | import createSubscriptionManager from 'eth-json-rpc-filters/subscriptionManager';
3 | import { createLegacyMiddleware } from '../utils/legacy-middleware';
4 | import SafeEventEmitter from '@metamask/safe-event-emitter';
5 | import { EthEvent, EthProviderMessageType } from 'eth-provider-types';
6 |
7 | interface SubNotification {
8 | params: {
9 | subscription: string;
10 | result: unknown;
11 | };
12 | }
13 |
14 | export const createSubscriptionMiddleware = (): JsonRpcMiddleware =>
15 | createLegacyMiddleware(({ blockTracker, provider, context }) => {
16 | const manager = createSubscriptionManager({ blockTracker, provider }) as {
17 | middleware: JsonRpcMiddleware & { destroy(): void };
18 | events: SafeEventEmitter;
19 | };
20 |
21 | manager.events.on('notification', (notification: SubNotification) => {
22 | const data = {
23 | ...notification.params,
24 | subscription: `${context.chain.chainId}:${notification.params.subscription}`,
25 | };
26 |
27 | context.emit(EthEvent.message, {
28 | type: EthProviderMessageType.eth_subscription,
29 | data,
30 | });
31 |
32 | context.emit(EthEvent.data, null, { params: data });
33 | });
34 |
35 | context.addDestructor(manager.middleware.destroy);
36 |
37 | return manager.middleware;
38 | });
39 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/middleware/transaction-validator.ts:
--------------------------------------------------------------------------------
1 | import { EthMethod, EthTransaction } from 'eth-provider-types';
2 | import { createAsyncMiddleware, JsonRpcMiddleware } from 'json-rpc-engine';
3 | import { getRequestContext } from '../utils/request-context';
4 | import { expect } from '../utils/type-utils';
5 |
6 | export const createTransactionValidatorMiddleware = (): JsonRpcMiddleware<
7 | [transaction: Partial],
8 | unknown
9 | > => {
10 | return createAsyncMiddleware(async (req, _res, next) => {
11 | if (
12 | req.method === EthMethod.eth_sendTransaction ||
13 | req.method === EthMethod.eth_signTransaction
14 | ) {
15 | const [transaction] = expect(req.params, `${req.method} request missing required parameters`);
16 | const context = getRequestContext(req);
17 |
18 | if (transaction.from === undefined) {
19 | const accounts = await context.request({ method: EthMethod.eth_accounts });
20 | transaction.from = accounts[0];
21 | }
22 | }
23 |
24 | next();
25 | });
26 | };
27 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/signers/browser.ts:
--------------------------------------------------------------------------------
1 | import { InternalBitskiProviderConfig, RequestContext, SignFn } from '../types';
2 | import { fetchJsonWithRetry } from '../utils/fetch';
3 | import { createBitskiTransaction, Transaction } from '../utils/transaction';
4 | import { getSignerUrl } from './shared';
5 |
6 | /**
7 | * Responsible for submitting the Transaction object to the API
8 | * @param transaction The Transaction object to submit
9 | * @param accessToken The current user's access token
10 | */
11 | const submitTransaction = async (
12 | transaction: Transaction,
13 | config: InternalBitskiProviderConfig,
14 | ): Promise => {
15 | const headers = { ...config.additionalHeaders };
16 |
17 | if (config.getAccessToken) {
18 | headers['Authorization'] = `Bearer ${await config.getAccessToken()}`;
19 | }
20 |
21 | const transactionApiUrl =
22 | config.waas?.transactionProxyUrl ?? `${config.apiBaseUrl}/v1/transactions`;
23 |
24 | const response = (await fetchJsonWithRetry(config.fetch, 5, transactionApiUrl, {
25 | method: 'POST',
26 | body: { transaction },
27 | headers,
28 | })) as { transaction: Transaction };
29 |
30 | return response.transaction;
31 | };
32 |
33 | const redirectToCallbackURL = (
34 | transaction: Transaction,
35 | config: InternalBitskiProviderConfig,
36 | ): Promise => {
37 | window.location.href = getSignerUrl(transaction.id, config);
38 |
39 | // return a non-resolving promise so we block until redirect
40 | // eslint-disable-next-line @typescript-eslint/no-empty-function
41 | return new Promise(() => {});
42 | };
43 |
44 | export type ShowSignerPopupFn = (
45 | transaction: Transaction,
46 | context: RequestContext,
47 | submitTransaction: () => Promise,
48 | ) => Promise;
49 | export interface BrowserSignerConfig {
50 | showPopup?: ShowSignerPopupFn;
51 | }
52 |
53 | export default function createBrowserSigner({ showPopup }: BrowserSignerConfig = {}): SignFn {
54 | return async (method, params, requestContext): Promise => {
55 | const { config } = requestContext;
56 | const transaction = await createBitskiTransaction(
57 | method,
58 | params,
59 | requestContext.chain,
60 | requestContext.paymaster,
61 | requestContext.config.additionalSigningContext,
62 | );
63 |
64 | // If we have a callback URL, use the redirect flow
65 | if (config.transactionCallbackUrl) {
66 | const persisted = await submitTransaction(transaction, config);
67 | return redirectToCallbackURL(persisted, config);
68 | } else {
69 | if (!showPopup) {
70 | throw new Error('You must provide a showPopup function when using the popup sign method');
71 | }
72 |
73 | // Show the modal (await response)
74 | return showPopup(transaction, requestContext, () => submitTransaction(transaction, config));
75 | }
76 | };
77 | }
78 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/signers/dialog.ts:
--------------------------------------------------------------------------------
1 | import { ethErrors } from 'eth-rpc-errors';
2 | import { getSignerUrl } from './shared';
3 | import { ShowSignerPopupFn } from './browser';
4 | import { PromiseQueue } from '../utils/promise-queue';
5 | import { OpenDialog } from '../dialogs';
6 |
7 | // Global state, this manages the currently open signer popup.
8 | interface SignRequestItem {
9 | url: string;
10 | openDialog: OpenDialog;
11 | }
12 |
13 | const SIGN_REQUEST_QUEUE = new PromiseQueue(({ url, openDialog }: SignRequestItem) => {
14 | return openDialog({
15 | url,
16 | handleMessage(message: { result?: string; error?: string }) {
17 | if (message.error) {
18 | throw new Error(message.error);
19 | } else {
20 | return message.result!;
21 | }
22 | },
23 | handleClose() {
24 | throw ethErrors.provider.userRejectedRequest();
25 | },
26 | });
27 | });
28 |
29 | export const createDialogSigner =
30 | (openDialog: OpenDialog, isIframe: boolean): ShowSignerPopupFn =>
31 | (transaction, context, submitTransaction): Promise => {
32 | const url = getSignerUrl(transaction.id, context.config, isIframe);
33 |
34 | const signRequest = {
35 | url,
36 | openDialog,
37 | };
38 |
39 | // We can submit the transaction and show our authorization modal at the
40 | // same time, so they load in parallel
41 | submitTransaction().catch((error) => SIGN_REQUEST_QUEUE.cancel(signRequest, error));
42 |
43 | return SIGN_REQUEST_QUEUE.push(signRequest);
44 | };
45 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/signers/rpc.ts:
--------------------------------------------------------------------------------
1 | import { SignFn } from '../types';
2 | import { fetchJsonRpcWithRetry } from '../utils/fetch';
3 |
4 | const signFn: SignFn = async (method, params, { config, chain }): Promise => {
5 | const headers = { ...config.additionalHeaders };
6 |
7 | if (config.getAccessToken) {
8 | headers.Authorization = `Bearer ${await config.getAccessToken()}`;
9 | }
10 |
11 | return (await fetchJsonRpcWithRetry(config.fetch, 5, chain.rpcUrls[0], {
12 | method: 'POST',
13 | headers,
14 | body: {
15 | id: 1,
16 | jsonrpc: '2.0',
17 | method,
18 | params,
19 | },
20 | })) as string;
21 | };
22 |
23 | /**
24 | * This sign function signs by directly calling the method at the RPC endpoint,
25 | * without any user interaction. It requires the user to provide an access token
26 | * with `sign` scope, e.g. a client secret issued from the Bitski developer
27 | * portal.
28 | */
29 | export default function createRpcSigner(): SignFn {
30 | return signFn;
31 | }
32 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/signers/shared.ts:
--------------------------------------------------------------------------------
1 | import { InternalBitskiProviderConfig } from '../types';
2 |
3 | export const getSignerUrl = (
4 | transactionId: string,
5 | config: InternalBitskiProviderConfig,
6 | isIframe = false,
7 | ): string => {
8 | const searchParams = config.signerQueryParams ?? new URLSearchParams();
9 |
10 | if (config.transactionCallbackUrl) {
11 | searchParams.set('redirectURI', config.transactionCallbackUrl);
12 | }
13 |
14 | if (config.waas?.enabled || config.waas?.userId) {
15 | const appId = config.appId ?? config.clientId;
16 | let federatedId = btoa(`${appId}`);
17 |
18 | if (config.waas?.userId) {
19 | federatedId = btoa(`${appId}:${config.waas?.userId}`);
20 | }
21 |
22 | searchParams.set('login_hint', `fa_${federatedId}`);
23 | }
24 |
25 | if (isIframe) {
26 | searchParams.set('isIframe', 'true');
27 | }
28 |
29 | const searchParamsSerialized = searchParams.toString();
30 | const searchParamsString = searchParamsSerialized !== '' ? `?${searchParamsSerialized}` : '';
31 |
32 | return `${config.signerBaseUrl}/transactions/${transactionId}${searchParamsString}`;
33 | };
34 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/styles/dialog.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable */
2 |
3 | const css = `
4 | #bitski-dialog-container {
5 | position: fixed;
6 | left: 0;
7 | right: 0;
8 | top: 0;
9 | bottom: 0;
10 | background: rgba(0, 0, 0, 0);
11 | z-index: 1000;
12 | transition: background linear 0.2s;
13 | pointer-events: none;
14 | }
15 | #bitski-dialog-container.bitski-visible {
16 | background: rgba(0, 0, 0, 0.5);
17 | pointer-events: auto;
18 | }
19 | .bitski-dialog {
20 | opacity: 0;
21 | transform: translateY(100vh);
22 | transition: opacity 300ms linear, transform 400ms cubic-bezier(0.19, 1, 0.22, 1);
23 | pointer-events: none;
24 | position: absolute;
25 | top: 0;
26 | left: 0;
27 | width: 100%;
28 | height: 100%;
29 | }
30 | #bitski-dialog-container.bitski-visible .bitski-dialog {
31 | opacity: 1;
32 | transform: none;
33 | transition: opacity 300ms linear, transform 600ms cubic-bezier(0.19, 1, 0.22, 1);
34 | pointer-events: auto;
35 | }
36 | .bitski-dialog .bitski-close-button {
37 | background: transparent url('https://cdn.bitskistatic.com/sdk/close.svg') no-repeat 50% 50%;
38 | position: absolute;
39 | right: 12px;
40 | top: 12px;
41 | border: none;
42 | outline: none;
43 | margin: 0;
44 | cursor: pointer;
45 | padding: 0;
46 | width: 28px;
47 | height: 28px;
48 | z-index: 100;
49 | overflow: hidden;
50 | text-indent: -1000px;
51 | }
52 | .bitski-dialog-body {
53 | background: #fff;
54 | position: absolute;
55 | top: 0;
56 | bottom: 0;
57 | left: 0;
58 | right: 0;
59 | z-index: 5;
60 | max-width: 100%;
61 | }
62 | .bitski-dialog-body.bitski-loading::after {
63 | content: "";
64 | background: url('https://cdn.bitskistatic.com/sdk/loading.svg') no-repeat 50% 50%;
65 | animation: rotate 600ms linear infinite;
66 | position: absolute;
67 | top: 50%;
68 | left: 50%;
69 | opacity: 0.3;
70 | width: 38px;
71 | height: 38px;
72 | margin-left: -19px;
73 | margin-top: -19px;
74 | z-index: -1;
75 | }
76 | @media (min-width: 600px) {
77 | #bitski-dialog-container {
78 | display: flex;
79 | align-items: center;
80 | justify-content: center;
81 | }
82 | .bitski-dialog {
83 | position: relative;
84 | width: 400px;
85 | height: 420px;
86 | }
87 | .bitski-dialog-body {
88 | border-radius: 16px;
89 | overflow: hidden;
90 | box-shadow: 0px 0px 0px 1px rgba(0,0,0,0.1), 0px 10px 50px rgba(0,0,0,0.4);
91 | }
92 | }
93 |
94 | @keyframes rotate {
95 | 0% {
96 | transform: rotate(0deg);
97 | }
98 | 50% {
99 | transform: rotate(180deg);
100 | }
101 | 100% {
102 | transform: rotate(360deg);
103 | }
104 | }
105 | `;
106 | export default css;
107 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/utils/async.ts:
--------------------------------------------------------------------------------
1 | export const sleep = (ms: number): Promise =>
2 | new Promise((resolve) => setTimeout(resolve, ms));
3 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/utils/promise-queue.ts:
--------------------------------------------------------------------------------
1 | interface PromiseQueueItem {
2 | item: T;
3 | cancel?: () => void;
4 | finished: boolean;
5 | resolve: (result: Result) => void;
6 | reject: (e: unknown) => void;
7 | }
8 |
9 | export class PromiseQueue {
10 | private queue: PromiseQueueItem[] = [];
11 |
12 | private currentItem?: PromiseQueueItem;
13 |
14 | constructor(private runItem: (details: T) => { result: Promise; cancel?: () => void }) {}
15 |
16 | push(item: T): Promise {
17 | let resolve, reject;
18 | const promise = new Promise((res, rej) => {
19 | resolve = res;
20 | reject = rej;
21 | });
22 |
23 | this.queue.push({
24 | item,
25 | resolve,
26 | reject,
27 | cancel: undefined,
28 | finished: false,
29 | });
30 |
31 | if (!this.currentItem) {
32 | this.runNextItem();
33 | }
34 |
35 | return promise;
36 | }
37 |
38 | cancel(item: T, reason: unknown) {
39 | const { currentItem } = this;
40 |
41 | if (currentItem?.item === item) {
42 | currentItem.cancel?.();
43 | currentItem.reject(reason);
44 | currentItem.finished = true;
45 | this.runNextItem();
46 | } else {
47 | this.queue = this.queue.filter(({ item: queueItem }) => queueItem !== item);
48 | }
49 | }
50 |
51 | async runNextItem() {
52 | const currentItem = (this.currentItem = this.queue.shift());
53 |
54 | if (!currentItem) {
55 | return;
56 | }
57 |
58 | const { item, resolve, reject } = currentItem;
59 |
60 | try {
61 | const { result, cancel } = this.runItem(item);
62 | currentItem.cancel = cancel;
63 | const res = await result;
64 |
65 | if (currentItem.finished) return;
66 |
67 | resolve(res);
68 | } catch (e) {
69 | if (currentItem.finished) return;
70 |
71 | reject(e);
72 | } finally {
73 | if (!currentItem.finished) {
74 | currentItem.finished = true;
75 |
76 | this.runNextItem();
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/utils/request-context.ts:
--------------------------------------------------------------------------------
1 | import { JsonRpcRequest } from 'json-rpc-engine';
2 | import { RequestContext } from '../types';
3 | import { expect } from './type-utils';
4 |
5 | export const getRequestContext = (req: JsonRpcRequest): RequestContext => {
6 | return expect(
7 | (req as unknown as { context: RequestContext }).context,
8 | 'no context found on this request',
9 | );
10 | };
11 |
12 | export const setRequestContext = (
13 | req: JsonRpcRequest,
14 | context: RequestContext,
15 | ): void => {
16 | (req as unknown as { context: RequestContext }).context = context;
17 | };
18 |
--------------------------------------------------------------------------------
/packages/bitski-provider/src/utils/type-utils.ts:
--------------------------------------------------------------------------------
1 | import { ethErrors } from 'eth-rpc-errors';
2 |
3 | export function expect(value: T | undefined | null, message: string): T {
4 | if (value === undefined || value === null) {
5 | throw ethErrors.rpc.invalidInput(message);
6 | }
7 |
8 | return value;
9 | }
10 |
11 | export function assert(predicate: boolean, message: string): asserts predicate {
12 | if (!predicate) {
13 | throw ethErrors.rpc.invalidInput(message);
14 | }
15 | }
16 |
17 | export const isError = (value: unknown): value is Error => value instanceof Error;
18 |
--------------------------------------------------------------------------------
/packages/bitski-provider/tests/connect.test.ts:
--------------------------------------------------------------------------------
1 | import { EthEvent, EthMethod } from 'eth-provider-types';
2 | import { Goerli, Mainnet } from '../src/constants';
3 | import { sleep } from './util/async';
4 | import { createTestProvider } from './util/create-provider';
5 |
6 | describe('connect event', () => {
7 | test('emits connect event when initialized', async () => {
8 | expect.assertions(1);
9 | const provider = createTestProvider();
10 |
11 | provider.on(EthEvent.connect, ({ chainId }) => {
12 | expect(chainId).toBe(Mainnet.chainId);
13 | });
14 |
15 | // wait for listener to be called
16 | await sleep(10);
17 | });
18 |
19 | test('passes the correct chain id on connect event', async () => {
20 | expect.assertions(1);
21 | const provider = createTestProvider();
22 |
23 | await provider.request({
24 | method: EthMethod.wallet_switchEthereumChain,
25 | params: [{ chainId: Goerli.chainId }],
26 | });
27 |
28 | provider.on(EthEvent.connect, ({ chainId }) => {
29 | expect(chainId).toBe(Goerli.chainId);
30 | });
31 |
32 | // wait for listener to be called
33 | await sleep(10);
34 | });
35 |
36 | test('emits connect event only after first listener for connect is added', async () => {
37 | expect.assertions(1);
38 | const provider = createTestProvider();
39 |
40 | // sleep a random amount of time before adding the listener
41 | await sleep(123);
42 |
43 | provider.on(EthEvent.connect, ({ chainId }) => {
44 | expect(chainId).toBe(Mainnet.chainId);
45 | });
46 |
47 | // wait for listener to be called
48 | await sleep(10);
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/packages/bitski-provider/tests/middlewares/fetch-rest.test.ts:
--------------------------------------------------------------------------------
1 | import { EthMethod } from 'eth-provider-types';
2 | import { createTestProvider } from '../util/create-provider';
3 |
4 | describe('fetch-rest middleware', () => {
5 | test('sends GET requests for specific methods', async () => {
6 | expect.assertions(11);
7 | const provider = createTestProvider();
8 |
9 | fetchMock.mockResponse(async (req) => {
10 | expect(req.url).toBe('https://api.bitski.com/v1/web3/chains/1/eth_blockNumber');
11 | expect(req.method).toBe('GET');
12 |
13 | expect(req.headers.get('X-API-KEY')).toBe('test-client-id');
14 | expect(req.headers.get('X-CLIENT-ID')).toBe('test-client-id');
15 | expect(req.headers.get('X-CLIENT-VERSION')).toBe('test-version');
16 |
17 | return JSON.stringify('0x123');
18 | });
19 |
20 | const result = await provider.request({
21 | method: EthMethod.eth_blockNumber,
22 | });
23 |
24 | expect(result).toEqual('0x123');
25 | });
26 |
27 | test('encodes parameters as query params', async () => {
28 | expect.assertions(3);
29 | const provider = createTestProvider();
30 |
31 | fetchMock.mockResponse(async (req) => {
32 | if (req.url.includes('eth_blockNumber')) {
33 | return JSON.stringify({
34 | id: 0,
35 | jsonrpc: '2.0',
36 | result: '0x123',
37 | });
38 | }
39 |
40 | expect(req.url).toBe(
41 | 'https://api.bitski.com/v1/web3/chains/1/eth_getBlockByNumber?params=%5B%220x123%22%2Cfalse%5D',
42 | );
43 | expect(req.method).toBe('GET');
44 |
45 | return JSON.stringify({ number: '0x123' });
46 | });
47 |
48 | const result = await provider.request({
49 | method: EthMethod.eth_getBlockByNumber,
50 | params: ['0x123', false],
51 | });
52 |
53 | expect(result).toEqual({ number: '0x123' });
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/packages/bitski-provider/tests/middlewares/fixture.test.ts:
--------------------------------------------------------------------------------
1 | import { EthMethod } from 'eth-provider-types';
2 | import { createTestProvider } from '../util/create-provider';
3 |
4 | describe('fixture middleware', () => {
5 | test('responds with fixtures for certain methods', async () => {
6 | const provider = createTestProvider();
7 |
8 | const result = await provider.request({ method: EthMethod.web3_clientVersion });
9 |
10 | expect(result).toEqual('Bitski/latest');
11 | expect(fetchMock.mock.calls.length).toBe(0);
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/packages/bitski-provider/tests/middlewares/transaction-validator.test.ts:
--------------------------------------------------------------------------------
1 | import { EthMethod } from 'eth-provider-types';
2 | import { createTestProvider } from '../util/create-provider';
3 |
4 | describe('transaction-validator middleware', () => {
5 | test('adds `from` if it was not present', async () => {
6 | expect.assertions(3);
7 | const provider = createTestProvider({
8 | getUser: async () => ({
9 | id: 'test-id',
10 | }),
11 | });
12 |
13 | const txn = {
14 | to: '0x',
15 | value: '0x',
16 | gas: '0x',
17 | gasPrice: '0x',
18 | };
19 |
20 | fetchMock.once(async () => {
21 | return JSON.stringify({
22 | accounts: [
23 | {
24 | kind: 'bitski',
25 | address: '0x123',
26 | },
27 | ],
28 | });
29 | });
30 |
31 | fetchMock.once(async (req) => {
32 | const { method, params } = await req.json();
33 |
34 | expect(method).toEqual(EthMethod.eth_sendTransaction);
35 | expect(params[0]).toEqual({
36 | from: '0x123',
37 | ...txn,
38 | });
39 |
40 | return JSON.stringify({ result: '0x123' });
41 | });
42 |
43 | const result = await provider.request({
44 | method: EthMethod.eth_sendTransaction,
45 | params: [{ ...txn }],
46 | });
47 |
48 | expect(result).toBe('0x123');
49 | });
50 |
51 | test('it only updates values that are missing', async () => {
52 | expect.assertions(3);
53 | const provider = createTestProvider({
54 | getUser: async () => ({
55 | id: 'test-id',
56 | accounts: ['0x123'],
57 | }),
58 | });
59 |
60 | const txn = {
61 | from: '0x456',
62 | to: '0x',
63 | value: '0x',
64 | gas: '0x',
65 | gasPrice: '0x',
66 | };
67 |
68 | fetchMock.mockResponse(async (req) => {
69 | const { method, params } = await req.json();
70 |
71 | expect(method).toEqual(EthMethod.eth_sendTransaction);
72 | expect(params[0]).toEqual({
73 | ...txn,
74 | from: '0x456',
75 | });
76 |
77 | return JSON.stringify({ result: '0x123' });
78 | });
79 |
80 | const result = await provider.request({
81 | method: EthMethod.eth_sendTransaction,
82 | params: [{ ...txn }],
83 | });
84 |
85 | expect(result).toBe('0x123');
86 | });
87 | });
88 |
--------------------------------------------------------------------------------
/packages/bitski-provider/tests/store.test.ts:
--------------------------------------------------------------------------------
1 | import { EthMethod } from 'eth-provider-types';
2 | import { CHAINS_STORAGE_KEY, CURRENT_CHAIN_STORAGE_KEY } from '../src/store';
3 | import { DEFAULT_CHAINS, Goerli } from '../src/constants';
4 | import { toHex } from '../src/utils/parse-utils';
5 | import { createTestProvider } from './util/create-provider';
6 | import MemStore from './util/mem-store';
7 |
8 | describe('store', () => {
9 | test('stores chains and current chain id', async () => {
10 | const store = new MemStore();
11 | const provider = createTestProvider({ store });
12 |
13 | const customChain = { chainId: toHex(77), rpcUrls: ['http://localhost:3000'] };
14 |
15 | await provider.request({
16 | method: EthMethod.wallet_addEthereumChain,
17 | params: [customChain],
18 | });
19 |
20 | await provider.request({
21 | method: EthMethod.wallet_switchEthereumChain,
22 | params: [{ chainId: toHex(77) }],
23 | });
24 |
25 | const chains = await store.getItem(CHAINS_STORAGE_KEY);
26 | const currentChainId = await store.getItem(CURRENT_CHAIN_STORAGE_KEY);
27 |
28 | expect(chains).toEqual([...DEFAULT_CHAINS, customChain]);
29 | expect(currentChainId).toBe(toHex(77));
30 | });
31 |
32 | test('restores state from store', async () => {
33 | const store = new MemStore();
34 |
35 | const customChain = { chainId: toHex(77), rpcUrls: ['http://localhost:3000'] };
36 |
37 | store.setItem(CHAINS_STORAGE_KEY, [...DEFAULT_CHAINS, customChain]);
38 | store.setItem(CURRENT_CHAIN_STORAGE_KEY, Goerli.chainId);
39 |
40 | const provider = createTestProvider({ store });
41 |
42 | // defaults to persisted chain id
43 | const result1 = await provider.request({ method: EthMethod.eth_chainId });
44 | expect(result1).toBe(Goerli.chainId);
45 |
46 | // switch to persisted custom chain
47 | const result2 = await provider.request({
48 | method: EthMethod.wallet_switchEthereumChain,
49 | params: [{ chainId: toHex(77) }],
50 | });
51 | expect(result2).toBe(null);
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/packages/bitski-provider/tests/util/async.ts:
--------------------------------------------------------------------------------
1 | // Make sure we can always sleep even if we're using fake timers elsewhere
2 | const originalSetTimetout = globalThis.setTimeout;
3 |
4 | export const sleep = (ms: number): Promise =>
5 | new Promise((resolve) => originalSetTimetout(resolve, ms));
6 |
--------------------------------------------------------------------------------
/packages/bitski-provider/tests/util/create-provider.ts:
--------------------------------------------------------------------------------
1 | import { BitskiProvider, BitskiProviderConfig, createBitskiProvider } from '../../src/index';
2 | import createRpcSigner from '../../src/signers/rpc';
3 | import MemStore from './mem-store';
4 |
5 | export const TEST_CLIENT_ID = 'test-client-id';
6 |
7 | export const createTestProvider = (opts?: Partial): BitskiProvider => {
8 | return createBitskiProvider({
9 | clientId: TEST_CLIENT_ID,
10 | store: new MemStore(),
11 | sign: createRpcSigner(),
12 | ...opts,
13 | });
14 | };
15 |
--------------------------------------------------------------------------------
/packages/bitski-provider/tests/util/mem-store.ts:
--------------------------------------------------------------------------------
1 | import { BitskiProviderStore } from '../../src/index';
2 |
3 | export default class MemStore implements BitskiProviderStore {
4 | private store: { [key: string]: unknown } = {};
5 |
6 | keys(): string[] {
7 | return Object.keys(this.store);
8 | }
9 |
10 | async getItem(key: string): Promise {
11 | return this.store[key];
12 | }
13 |
14 | async setItem(key: string, value: unknown): Promise {
15 | this.store[key] = value;
16 | }
17 |
18 | async clearItem(key: string): Promise {
19 | delete this.store[key];
20 | }
21 |
22 | async clear(): Promise {
23 | this.store = {};
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/bitski-provider/tests/util/mock-engine.ts:
--------------------------------------------------------------------------------
1 | import Web3ProviderEngine from '@bitski/provider-engine';
2 | import { FixtureSubprovider } from '@bitski/provider-engine';
3 |
4 | export class MockEngine extends Web3ProviderEngine {
5 | constructor(opts) {
6 | super(opts);
7 | this.addProvider(
8 | new FixtureSubprovider({
9 | eth_blockNumber: '0x0',
10 | eth_getBlockByNumber: false,
11 | eth_hashrate: '0x00',
12 | eth_mining: false,
13 | eth_syncing: true,
14 | net_listening: true,
15 | web3_clientVersion: 'ProviderEngine/v0.0.0/javascript',
16 | }),
17 | );
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/bitski-provider/tests/util/setup-jest.ts:
--------------------------------------------------------------------------------
1 | import { enableFetchMocks } from 'jest-fetch-mock';
2 |
3 | enableFetchMocks();
4 | globalThis.BITSKI_PROVIDER_VERSION = 'test-version';
5 |
--------------------------------------------------------------------------------
/packages/bitski-provider/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "dist",
4 | "noImplicitAny": false,
5 | "strict": true,
6 | "moduleResolution": "node",
7 | "lib": ["es2017", "dom"],
8 | "target": "ES2017",
9 | "esModuleInterop": true
10 | },
11 | "include": ["src", "**/*.test.ts"]
12 | }
13 |
--------------------------------------------------------------------------------
/packages/bitski-provider/tsconfig.main.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "lib",
5 | "module": "commonjs",
6 | "declaration": true,
7 | "sourceMap": true
8 | },
9 | "include": ["src"]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/bitski-provider/tsconfig.module.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "dist/",
5 | "module": "es6"
6 | },
7 | "include": ["src"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/bitski/.gitignore:
--------------------------------------------------------------------------------
1 | lib
--------------------------------------------------------------------------------
/packages/bitski/callback.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Logging in...
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/bitski/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | roots: ['/src', '/tests'],
3 | transform: {
4 | '^.+\\.ts$': [
5 | 'ts-jest',
6 | {
7 | diagnostics: false,
8 | },
9 | ],
10 | },
11 | testRegex: '(/tests/.*.(test|spec)).(jsx?|tsx?)$',
12 | automock: false,
13 | collectCoverage: true,
14 | collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**'],
15 | coveragePathIgnorePatterns: [],
16 | moduleFileExtensions: ['js', 'ts'],
17 | coverageReporters: ['json', 'text', 'html', 'cobertura'],
18 | verbose: true,
19 | globals: {
20 | 'ts-jest': {
21 | diagnostics: false,
22 | },
23 | },
24 | displayName: 'Bitski SDK',
25 | testEnvironment: 'jsdom',
26 | automock: false,
27 | resetMocks: false,
28 | setupFiles: ['/tests/util/setup-jest.ts'],
29 | };
30 |
--------------------------------------------------------------------------------
/packages/bitski/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bitski",
3 | "description": "Bitski Javascript SDK",
4 | "license": "MIT",
5 | "main": "lib/index.js",
6 | "module": "dist/esm/index.js",
7 | "types": "lib/index.d.ts",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/BitskiCo/bitski-js"
11 | },
12 | "version": "4.2.1",
13 | "scripts": {
14 | "lint": "eslint . --cache",
15 | "test": "jest",
16 | "build": "tsc -p tsconfig.main.json && tsc -p tsconfig.module.json && node ./scripts/insert-package-version.mjs && npm run bundle && node ./scripts/copy-readme.mjs",
17 | "bundle": "mkdir -p dist/bundled && npm run bundle:main && npm run bundle:callback && npm run minify",
18 | "bundle:main": "rollup --config rollup.config.mjs",
19 | "bundle:callback": "browserify dist/esm/-private/callback.js -d -t [ babelify --presets [ @babel/preset-env ] --plugins [ @babel/plugin-transform-runtime ] ] > dist/bundled/callback.js",
20 | "minify": "node ./scripts/minify.mjs",
21 | "prettier": "prettier --config .prettierrc '{src,tests}/**/*.ts' --write"
22 | },
23 | "dependencies": {
24 | "@openid/appauth": "^1.2.6",
25 | "bitski-provider": "^3.5.1",
26 | "decoders": "^2.0.1",
27 | "eth-provider-types": "^0.2.0",
28 | "hash-it": "^6.0.0"
29 | },
30 | "devDependencies": {
31 | "@babel/core": "^7.8.7",
32 | "@babel/plugin-transform-runtime": "^7.6.2",
33 | "@babel/preset-env": "^7.6.3",
34 | "@rollup/plugin-commonjs": "^23.0.4",
35 | "@rollup/plugin-node-resolve": "^15.0.1",
36 | "@types/jest": "^29.5.11",
37 | "@types/node": "^20.11.5",
38 | "babelify": "^10.0.0",
39 | "browserify": "^16.5.0",
40 | "jest": "^29.7.0",
41 | "jest-fetch-mock": "^3.0.3",
42 | "jest-environment-jsdom": "^29.0.0",
43 | "rollup": "^3.7.1",
44 | "rollup-plugin-node-globals": "^1.4.0",
45 | "rollup-plugin-node-polyfills": "^0.2.1",
46 | "terser": "^5.16.1",
47 | "ts-jest": "^29.1.1"
48 | },
49 | "browserslist": [
50 | "last 3 chrome versions",
51 | "last 3 firefox versions",
52 | "last 3 safari versions",
53 | "last 3 ios versions",
54 | "last 3 chromeandroid versions",
55 | "last 3 edge versions"
56 | ],
57 | "gitHead": "7b9f0b01dd8a36a4294f27740ce264ecd95af35c"
58 | }
59 |
--------------------------------------------------------------------------------
/packages/bitski/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | import { nodeResolve } from '@rollup/plugin-node-resolve';
2 | import nodePolyfills from 'rollup-plugin-node-polyfills';
3 | import nodeGlobals from 'rollup-plugin-node-globals';
4 | import commonjs from '@rollup/plugin-commonjs';
5 |
6 | export default {
7 | input: 'dist/esm/-private/sdk.js',
8 | output: {
9 | file: 'dist/bundled/bitski.bundle.js',
10 | format: 'umd',
11 | name: 'Bitski',
12 | },
13 | plugins: [
14 | commonjs(),
15 | nodeResolve(),
16 | nodePolyfills(),
17 | nodeGlobals({
18 | process: false,
19 | buffer: false,
20 | dirname: false,
21 | filename: false,
22 | baseDir: false,
23 | }),
24 | ],
25 | };
26 |
--------------------------------------------------------------------------------
/packages/bitski/scripts/copy-readme.mjs:
--------------------------------------------------------------------------------
1 | import { promises as fs } from 'fs';
2 | import replaceInFiles from 'replace-in-files';
3 |
4 | await fs.copyFile('../../README.md', './README.md');
5 |
6 | await replaceInFiles({
7 | files: ['./README.md'],
8 | from: /\(\/docs\/images/g,
9 | to: `(https://raw.githubusercontent.com/BitskiCo/bitski-js/main/docs/images`,
10 | });
11 |
--------------------------------------------------------------------------------
/packages/bitski/scripts/insert-package-version.mjs:
--------------------------------------------------------------------------------
1 | import replaceInFiles from 'replace-in-files';
2 | import packageJson from '../package.json' assert { type: 'json' };
3 |
4 | await replaceInFiles({
5 | files: ['./lib/**/*', './dist/**/*'],
6 | from: 'BITSKI_SDK_VERSION',
7 | to: `"bitski-sdk-v${packageJson.version}"`,
8 | });
9 |
--------------------------------------------------------------------------------
/packages/bitski/scripts/minify.mjs:
--------------------------------------------------------------------------------
1 | import { minify } from 'terser';
2 | import { promises as fs } from 'fs';
3 |
4 | const src = await fs.readFile('./dist/bundled/bitski.bundle.js', 'utf8');
5 |
6 | const min = await minify(src);
7 |
8 | await fs.writeFile('./dist/bundled/bitski.min.js', min.code, 'utf8');
9 |
--------------------------------------------------------------------------------
/packages/bitski/src/-private/auth/access-token.ts:
--------------------------------------------------------------------------------
1 | import { TokenResponse } from '@openid/appauth';
2 | /**
3 | * Represents a Bitski access token
4 | */
5 | export class AccessToken {
6 | /**
7 | * Creates a token from a TokenResponse object
8 | * @param tokenResponse The token response object to build a token from
9 | */
10 | public static fromTokenResponse(tokenResponse: TokenResponse) {
11 | let expiresAt: number | undefined;
12 | if (tokenResponse.expiresIn) {
13 | expiresAt = Math.floor(Date.now() / 1000) + tokenResponse.expiresIn;
14 | }
15 | return new AccessToken(tokenResponse.accessToken, expiresAt, tokenResponse.scope);
16 | }
17 |
18 | /**
19 | * Creates a token from a storage string
20 | * @param s JSON string representing the token
21 | */
22 | public static fromString(s: string): AccessToken | undefined {
23 | let parsed: any | undefined;
24 | try {
25 | parsed = JSON.parse(s);
26 | } catch (error) {
27 | return;
28 | }
29 | if (!parsed.token) {
30 | return;
31 | }
32 | return new AccessToken(parsed.token, parsed.expiresAt, parsed.scope);
33 | }
34 |
35 | /**
36 | * The actual access token
37 | */
38 | public token: string;
39 |
40 | /**
41 | * When the token expires (in seconds)
42 | */
43 | public expiresAt?: number;
44 |
45 | /**
46 | * Scopes this token has access to
47 | */
48 | public scope?: string;
49 |
50 | /**
51 | * Calculates if the token is still active
52 | */
53 | public get expired() {
54 | if (this.expiresAt) {
55 | const now = Math.floor(Date.now() / 1000);
56 | const expiresIn = this.expiresAt - now;
57 | return expiresIn <= 0;
58 | }
59 | return false;
60 | }
61 |
62 | /**
63 | *
64 | * @param token the access token
65 | * @param expiresAt the token expiration date (in seconds) (optional)
66 | * @param scope the scopes this token represents (optional)
67 | */
68 | constructor(token: string, expiresAt?: number, scope?: string) {
69 | this.token = token;
70 | this.scope = scope;
71 | this.expiresAt = expiresAt;
72 | }
73 |
74 | /**
75 | * Returns a JSON string suitable for writing in local storage
76 | */
77 | public toStorageString(): string {
78 | return JSON.stringify({
79 | expiresAt: this.expiresAt,
80 | scope: this.scope,
81 | token: this.token,
82 | });
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/packages/bitski/src/-private/auth/auth-provider.ts:
--------------------------------------------------------------------------------
1 | import type { AuthenticationStatus, OAuthSignInMethod } from '../constants';
2 | import type { SignInOptions } from './oauth-manager';
3 | import type { User } from './user';
4 |
5 | export interface AuthProvider {
6 | getAuthStatus(): Promise;
7 | signIn(method: OAuthSignInMethod, opts?: SignInOptions): Promise;
8 | connect(): Promise;
9 | signInOrConnect(signInMethod?: OAuthSignInMethod, opts?: SignInOptions): Promise;
10 | getUser(): Promise;
11 | redirectCallback(): Promise;
12 | signOut(): Promise;
13 | }
14 |
--------------------------------------------------------------------------------
/packages/bitski/src/-private/auth/user.ts:
--------------------------------------------------------------------------------
1 | export interface UserInfoResponse {
2 | sub: string;
3 | accounts?: string[];
4 | email?: string;
5 | phone_number?: string;
6 | phone_number_verified?: boolean;
7 | email_verified?: boolean;
8 | preferred_username?: string;
9 | }
10 |
11 | export class User {
12 | public static fromJson(json: UserInfoResponse): User {
13 | return new User(
14 | json.sub,
15 | json.accounts,
16 | json.email,
17 | json.email_verified,
18 | json.phone_number,
19 | json.phone_number_verified,
20 | json.preferred_username,
21 | );
22 | }
23 |
24 | public static fromString(s: string): User | undefined {
25 | let parsed;
26 | try {
27 | parsed = JSON.parse(s);
28 | } catch (e) {
29 | return;
30 | }
31 | if (parsed.id) {
32 | return new User(
33 | parsed.id,
34 | parsed.accounts,
35 | parsed.email,
36 | parsed.emailVerified,
37 | parsed.phoneNumber,
38 | parsed.phoneNumberVerified,
39 | parsed.preferredUsername,
40 | );
41 | }
42 | return;
43 | }
44 |
45 | public id: string;
46 | public accounts?: string[];
47 | public email?: string;
48 | public emailVerified?: boolean;
49 | public phoneNumber?: string;
50 | public phoneNumberVerified?: boolean;
51 | public preferredUsername?: string;
52 |
53 | constructor(
54 | id: string,
55 | accounts?: string[],
56 | email?: string,
57 | emailVerified?: boolean,
58 | phone?: string,
59 | phoneNumberVerified?: boolean,
60 | preferredUsername?: string,
61 | ) {
62 | this.id = id;
63 | this.accounts = accounts;
64 | this.email = email;
65 | this.emailVerified = emailVerified;
66 | this.phoneNumber = phone;
67 | this.phoneNumberVerified = phoneNumberVerified;
68 | this.preferredUsername = preferredUsername;
69 | }
70 |
71 | public toStorageString() {
72 | return JSON.stringify({
73 | accounts: this.accounts,
74 | email: this.email,
75 | emailVerified: this.emailVerified,
76 | id: this.id,
77 | phoneNumber: this.phoneNumber,
78 | phoneNumberVerified: this.phoneNumberVerified,
79 | preferredUsername: this.preferredUsername,
80 | });
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/packages/bitski/src/-private/callback.ts:
--------------------------------------------------------------------------------
1 | import { processCallback } from './utils/callback';
2 |
3 | // Call the callback immediately
4 | try {
5 | processCallback();
6 | } catch (error) {
7 | // eslint-disable-next-line no-console
8 | console.error('Error logging in: ' + error); // tslint:disable-line
9 | }
10 |
--------------------------------------------------------------------------------
/packages/bitski/src/-private/constants.ts:
--------------------------------------------------------------------------------
1 | // SDK
2 | export const SDK_VERSION = '0.14.1';
3 |
4 | // URLs
5 | export const BITSKI_USER_API_HOST = 'https://www.bitski.com/v1';
6 | export const BITSKI_TRANSACTION_API_BASE_URL = 'https://api.bitski.com/v1';
7 | export const BITSKI_WEB_BASE_URL = 'https://sign.bitski.com';
8 | export const IFRAME_MESSAGE_ORIGIN_INCLUDES = '.bitski.com';
9 |
10 | // OAuth
11 | export const DEFAULT_OAUTH_CONFIGURATION = {
12 | authorization_endpoint: 'https://account.bitski.com/oauth2/auth',
13 | revocation_endpoint: '',
14 | token_endpoint: 'https://account.bitski.com/oauth2/token',
15 | userinfo_endpoint: 'https://account.bitski.com/userinfo',
16 | };
17 | export const DEFAULT_SCOPES = ['openid']; // scopes that are always included
18 | export const DEFAULT_OPTIONAL_SCOPES = ['offline']; // scopes that are included by default, but can be overridden
19 |
20 | // Popup Window
21 | export const CHECK_FOR_POPUP_CLOSE_INTERVAL = 500;
22 | export const DEFAULT_POPUP_FEATURES = {
23 | location: 'no',
24 | toolbar: 'no',
25 | width: 500,
26 | height: 500,
27 | left: 100,
28 | top: 100,
29 | };
30 |
31 | // Storage
32 | export const REFRESH_TOKEN_KEY = 'bitski.refresh_token';
33 | export const ACCESS_TOKEN_KEY = 'bitski.access_token';
34 | export const ID_TOKEN_KEY = 'bitski.id_token';
35 | export const USER_KEY = 'bitski.user';
36 |
37 | // Methods
38 | export const CACHED_METHODS = ['eth_accounts'];
39 | export const DEFAULT_AUTHORIZED_METHODS = [
40 | 'eth_sendTransaction',
41 | 'eth_signTransaction',
42 | 'eth_sign',
43 | 'personal_sign',
44 | 'eth_signTypedData',
45 | 'eth_signTypedData_v3', // For metamask compatibility
46 | 'eth_signTypedData_v4',
47 | ];
48 |
49 | export enum OAuthSignInMethod {
50 | Redirect = 'REDIRECT',
51 | Popup = 'POPUP',
52 | Silent = 'SILENT', // Deprecated
53 | }
54 |
55 | export enum AuthenticationStatus {
56 | Connected = 'CONNECTED',
57 | Expired = 'EXPIRED',
58 | NotConnected = 'NOT_CONNECTED',
59 | }
60 |
--------------------------------------------------------------------------------
/packages/bitski/src/-private/network.ts:
--------------------------------------------------------------------------------
1 | export interface Network {
2 | rpcUrl: string;
3 | chainId: number;
4 | }
5 |
6 | export const Mainnet: Network = {
7 | chainId: 1,
8 | rpcUrl: 'https://api.bitski.com/v1/web3/mainnet',
9 | };
10 |
11 | export const Goerli: Network = {
12 | chainId: 5,
13 | rpcUrl: 'https://api.bitski.com/v1/web3/goerli',
14 | };
15 |
16 | export const Sepolia: Network = {
17 | chainId: 5,
18 | rpcUrl: 'https://api.bitski.com/v1/web3/sepolia',
19 | };
20 |
21 | export const Base: Network = {
22 | chainId: 8453,
23 | rpcUrl: 'https://api.bitski.com/v1/web3/base',
24 | };
25 |
26 | export const BaseGoerli: Network = {
27 | chainId: 84531,
28 | rpcUrl: 'https://api.bitski.com/v1/web3/basegor',
29 | };
30 |
31 | export const Polygon: Network = {
32 | chainId: 137,
33 | rpcUrl: 'https://api.bitski.com/v1/web3/polygon',
34 | };
35 |
36 | export const Mumbai: Network = {
37 | chainId: 80001,
38 | rpcUrl: 'https://api.bitski.com/v1/web3/mumbai',
39 | };
40 |
41 | export const BinanceSmartChain: Network = {
42 | chainId: 56,
43 | rpcUrl: 'https://api.bitski.com/v1/web3/bsc',
44 | };
45 |
46 | export const BinanceSmartChainTestnet: Network = {
47 | chainId: 97,
48 | rpcUrl: 'https://api.bitski.com/v1/web3/bnbt',
49 | };
50 |
--------------------------------------------------------------------------------
/packages/bitski/src/-private/styles/connect-button.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable */
2 |
3 | const css = `
4 | .bitski-connect-button {
5 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, 'Helvetica Neue', sans-serif;
6 | font-weight: 500;
7 | background-color: #1C11D9;
8 | background-repeat: no-repeat;
9 | background-position: 0px 0px;
10 | border: none;
11 | color: #fff;
12 | margin: 0;
13 | padding: 0;
14 | cursor: pointer;
15 | text-shadow: 1px 0 1px rgba(0, 0, 0, 0.03);
16 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.14);
17 | transition: background 200ms linear, transform 200ms ease-out;
18 | -webkit-user-select: none;
19 | -moz-user-select: none;
20 | -ms-user-select: none;
21 | user-select: none;
22 | }
23 | .bitski-connect-button:focus,
24 | .bitski-connect-button:active {
25 | background-color: #2117C7;
26 | transform: scale(0.99, 0.99);
27 | color: rgba(255, 255, 255, 0.8);
28 | }
29 | .bitski-connect-button.size-small {
30 | background-image: url('https://cdn.bitskistatic.com/sdk/btn-v2-bg-sm.svg');
31 | border-radius: 3px;
32 | font-size: 10px;
33 | height: 22px;
34 | line-height: 19px;
35 | padding-left: 30px;
36 | padding-right: 8px;
37 | }
38 | .bitski-connect-button.size-medium {
39 | background-image: url('https://cdn.bitskistatic.com/sdk/btn-v2-bg-md.svg');
40 | border-radius: 4px;
41 | font-size: 11px;
42 | height: 30px;
43 | line-height: 29px;
44 | padding-left: 40px;
45 | padding-right: 12px;
46 | }
47 | .bitski-connect-button.size-large {
48 | background-image: url('https://cdn.bitskistatic.com/sdk/btn-v2-bg-lg.svg');
49 | border-radius: 5px;
50 | font-size: 14px;
51 | height: 44px;
52 | line-height: 44px;
53 | padding-left: 57px;
54 | padding-right: 15px;
55 | }
56 | `;
57 |
58 | export default css;
59 |
--------------------------------------------------------------------------------
/packages/bitski/src/-private/utils/callback.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Automatically handles finalizing the oauth sign in process with the Bitski SDK
3 | */
4 | export function processCallback() {
5 | if (window.opener) {
6 | notifyOpener(window.location);
7 | } else {
8 | throw new Error('Parent window could not be found');
9 | }
10 | }
11 |
12 | /**
13 | * Notifies the opener when in a popup
14 | * @param url the url that contains the query params
15 | */
16 | function notifyOpener(url: Location): void {
17 | if (window.opener) {
18 | if (url) {
19 | // parse url to get state
20 | const data = parseUrlParams(url);
21 | if (data.state) {
22 | const name = `popupCallback_${data.state}`;
23 | const callback = window.opener[name];
24 | if (callback) {
25 | callback(url);
26 | } else {
27 | throw new Error('No callback found on opener');
28 | }
29 | } else {
30 | throw new Error('No state found in response');
31 | }
32 | }
33 | } else {
34 | throw new Error('No window.opener');
35 | }
36 | }
37 |
38 | /**
39 | * Extracts query params from the hash of the url
40 | * @param url the url to parse
41 | */
42 | export function parseUrlParams(url: Location): any {
43 | let params: string | undefined;
44 |
45 | if (url.href.includes('#') && !url.href.endsWith('#')) {
46 | params = extractQuery(url.hash);
47 | } else if (url.href.includes('?')) {
48 | params = url.search.split('?').pop();
49 | }
50 |
51 | if (!params) {
52 | throw new Error('No params found in result');
53 | }
54 |
55 | return params.split('&').reduce((prev, item) => {
56 | const [key, value] = item.split('=');
57 | if (key && value) {
58 | prev[decodeURIComponent(key)] = decodeURIComponent(value);
59 | }
60 | return prev;
61 | }, {});
62 | }
63 |
64 | function extractQuery(url): string {
65 | if (!url.includes('#')) {
66 | throw new Error('No params found in result');
67 | }
68 | return url.split('#').pop();
69 | }
70 |
--------------------------------------------------------------------------------
/packages/bitski/src/-private/utils/no-hash-query-string-utils.ts:
--------------------------------------------------------------------------------
1 | import { BasicQueryStringUtils, LocationLike, StringMap } from '@openid/appauth';
2 |
3 | export class NoHashQueryStringUtils extends BasicQueryStringUtils {
4 | public parse(input: LocationLike): StringMap {
5 | return super.parse(input, false);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/bitski/src/-private/utils/numbers.ts:
--------------------------------------------------------------------------------
1 | export function toHex(number: number): string {
2 | return `0x${number.toString(16)}`;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/bitski/src/-private/utils/popup-validator.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A simple utility class that will check to see if a popup is blocked.
3 | * Derived from info and examples on this page:
4 | * https://stackoverflow.com/questions/2914/how-can-i-detect-if-a-browser-is-blocking-a-popup
5 | */
6 | export class PopupValidator {
7 | // Will be called if the popup is blocked
8 | protected errorHandler: () => void;
9 |
10 | constructor(errorHandler: () => void) {
11 | this.errorHandler = errorHandler;
12 | }
13 |
14 | // Check a popup window to see if it has been blocked.
15 | // The error handler will be called asynchronously if
16 | // the window has been detected to have been blocked.
17 | public check(popup: Window | null) {
18 | if (popup) {
19 | if (/chrome/.test(navigator.userAgent.toLowerCase())) {
20 | setTimeout(() => {
21 | this.isPopupBlocked(popup);
22 | }, 2000);
23 | } else {
24 | popup.onload = () => {
25 | this.isPopupBlocked(popup);
26 | };
27 | }
28 | } else {
29 | this.handleBlocked();
30 | }
31 | }
32 |
33 | protected isPopupBlocked(popup: Window) {
34 | if (popup.innerHeight > 0 === false) {
35 | this.handleBlocked();
36 | }
37 | }
38 |
39 | protected handleBlocked() {
40 | this.errorHandler();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/packages/bitski/src/-private/utils/request-utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Parses a Fetch Response to extract either the result or the error
3 | * @param response the fetch response to parse
4 | */
5 | export function parseResponse(response: Response): Promise {
6 | return response
7 | .json()
8 | .catch(() => {
9 | throw new Error('Invalid JSON response');
10 | })
11 | .then((json) => {
12 | if (response.status >= 200 && response.status < 300) {
13 | return json as T;
14 | } else {
15 | if (json && json.error && json.error.message) {
16 | throw new Error(json.error.message);
17 | } else if (json && json.error) {
18 | throw new Error(json.error);
19 | } else {
20 | throw new Error('Unknown error');
21 | }
22 | }
23 | });
24 | }
25 |
--------------------------------------------------------------------------------
/packages/bitski/tests/shim.test.ts:
--------------------------------------------------------------------------------
1 | // This test requires the production build to have been run, it ensures that the
2 | // shim is working correctly and a basic request can be made.
3 |
4 | // import the bitski min, which will set the global Bitski object
5 | import { EthMethod } from 'eth-provider-types';
6 | import Bundle from '../dist/bundled/bitski.min.js';
7 |
8 | globalThis.Bitski = Bundle;
9 |
10 | // import the shim
11 | import { Bitski } from '../src/index';
12 |
13 | describe('built shim', () => {
14 | test('should be able to make a request', async () => {
15 | const bitski = new Bitski('test-client-id', 'http://localhost:3000');
16 | const provider = bitski.getProvider();
17 |
18 | fetchMock.mockResponse(async (req) => {
19 | if (req.method === 'GET') {
20 | // loading block number
21 | return JSON.stringify({ result: '0x123' });
22 | }
23 |
24 | expect(req.url).toBe('https://api.bitski.com/v1/web3/chains/1');
25 | expect(req.method).toBe('POST');
26 |
27 | expect(req.headers.get('X-API-KEY')).toBe('test-client-id');
28 | expect(req.headers.get('X-CLIENT-ID')).toBe('test-client-id');
29 | expect(req.headers.get('X-CLIENT-VERSION')).toMatch(/bitski-sdk-v\d+\.\d+\.\d+/);
30 |
31 | const body = await req.json();
32 |
33 | expect(body).toMatchObject({
34 | method: 'eth_getBlockByHash',
35 | params: ['0x123', false],
36 | });
37 |
38 | // Make sure we aren't including any extra parameters
39 | expect(Object.keys(body)).toEqual(['id', 'jsonrpc', 'method', 'params']);
40 |
41 | return JSON.stringify({
42 | id: 0,
43 | jsonrpc: '2.0',
44 | result: {
45 | number: '0x123',
46 | },
47 | });
48 | });
49 |
50 | const result = await provider.request({
51 | method: EthMethod.eth_getBlockByHash,
52 | params: ['0x123', false],
53 | });
54 |
55 | expect(result).toEqual({ number: '0x123' });
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/packages/bitski/tests/util/mem-store.ts:
--------------------------------------------------------------------------------
1 | import { BitskiProviderStore } from 'bitski-provider';
2 |
3 | export default class MemStore implements BitskiProviderStore {
4 | private store: { [key: string]: unknown } = {};
5 |
6 | keys(): string[] {
7 | return Object.keys(this.store);
8 | }
9 |
10 | async getItem(key: string): Promise {
11 | return this.store[key];
12 | }
13 |
14 | async setItem(key: string, value: unknown): Promise {
15 | this.store[key] = value;
16 | }
17 |
18 | async clearItem(key: string): Promise {
19 | delete this.store[key];
20 | }
21 |
22 | async clear(): Promise {
23 | this.store = {};
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/bitski/tests/util/mock-oauth-manager.ts:
--------------------------------------------------------------------------------
1 | import { AuthorizationRequest, TokenRequest } from '@openid/appauth';
2 | import { OAuthManager, SignInOptions } from '../../src/-private/auth/oauth-manager';
3 |
4 | export class MockOAuthManager extends OAuthManager {
5 | public currentAuthRequest?: AuthorizationRequest;
6 | public currentTokenRequest?: TokenRequest;
7 |
8 | protected createAuthRequest(opts: SignInOptions): AuthorizationRequest {
9 | const request = super.createAuthRequest(opts);
10 | this.currentAuthRequest = request;
11 | return request;
12 | }
13 |
14 | protected createTokenRequest(code: string): TokenRequest {
15 | const request = super.createTokenRequest(code);
16 | this.currentTokenRequest = request;
17 | return request;
18 | }
19 |
20 | protected createRefreshTokenRequest(refreshToken: string): TokenRequest {
21 | const request = super.createRefreshTokenRequest(refreshToken);
22 | this.currentTokenRequest = request;
23 | return request;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/bitski/tests/util/setup-jest.ts:
--------------------------------------------------------------------------------
1 | import { enableFetchMocks } from 'jest-fetch-mock';
2 |
3 | enableFetchMocks();
4 | globalThis.BITSKI_SDK_VERSION = 'test-version';
5 |
--------------------------------------------------------------------------------
/packages/bitski/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "noImplicitAny": false,
4 | "strict": true,
5 | "target": "ES2017",
6 | "moduleResolution": "node",
7 | "lib": ["es2017", "dom"],
8 | "esModuleInterop": true,
9 | "allowSyntheticDefaultImports": true
10 | },
11 | "include": ["src", "**/*.test.ts"]
12 | }
13 |
--------------------------------------------------------------------------------
/packages/bitski/tsconfig.main.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "lib",
5 | "module": "commonjs",
6 | "sourceMap": true,
7 | "declaration": true
8 | },
9 | "include": ["src"]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/bitski/tsconfig.module.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "dist/esm/",
5 | "module": "es6"
6 | },
7 | "include": ["src"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/eth-provider-types/.gitignore:
--------------------------------------------------------------------------------
1 | index.js
2 | index.d.ts
3 | index.js.map
4 |
--------------------------------------------------------------------------------
/packages/eth-provider-types/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eth-provider-types",
3 | "version": "0.2.1",
4 | "description": "TypeScript types for Ethereum providers, with types for every RPC method",
5 | "main": "./index.js",
6 | "types": "index.d.ts",
7 | "files": [
8 | "index.js",
9 | "index.js.map",
10 | "index.d.ts",
11 | "*.md"
12 | ],
13 | "author": "Bitski",
14 | "license": "ISC",
15 | "scripts": {
16 | "build": "tsc"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/eth-provider-types/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "noImplicitAny": true,
4 | "strict": true,
5 | "moduleResolution": "node",
6 | "lib": ["es2017", "dom"],
7 | "module": "commonjs",
8 | "target": "ES2017",
9 | "declaration": true,
10 | "sourceMap": true
11 | },
12 | "include": ["index.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | /build
3 | /package
4 | .env
5 | .env.*
6 | !.env.example
7 | /static
8 |
9 | # Ignore files for PNPM, NPM and YARN
10 | pnpm-lock.yaml
11 | package-lock.json
12 | yarn.lock
13 | .cloudflare
14 | /.next
15 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": false,
3 | "singleQuote": true,
4 | "trailingComma": "all",
5 | "printWidth": 100,
6 | "htmlWhitespaceSensitivity": "ignore"
7 | }
8 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/README.md:
--------------------------------------------------------------------------------
1 | # @bitski/waas-react-sdk
2 |
3 | > DISCLAIMER: While we are under v1.0.0 - please expect breaking changes. All changes will be communicated via the CHANGELOG.md
4 |
5 | Solve all your dApp’s authentication challenges with a simple React Widget.
6 |
7 | We aim to use tools you are already familiar with, mainly Wagmi and Viem so you can focus on building your application.
8 |
9 | To get started, create an account in the Bitski Developer portal. Follow the Web3 onboarding flow to get your Bitski `appId`` and reference the React snippet below to get going immediately.
10 |
11 | ## Quickstart
12 |
13 | Install via `npm i @bitski/waas-react-sdk`
14 |
15 | Bitski’s WaaS React SDK uses Wagmi and Viem so you don’t have to learn new tools. We support many chains—just use `viem/chains` and `LoginMethod` from the SDK.
16 |
17 | ```react
18 | import { BitskiProvider, BitskiWidget, LoginMethod } from "@bitski/waas-react-sdk";
19 | import {base, mainnet, polygon} from "viem/chains";
20 |
21 | export const Dapp = () => {
22 |
28 |
29 | // ... The rest of your app's code
30 |
31 | }
32 | ```
33 |
34 | ### Callback URL
35 |
36 | In order for the Bitski Waas React SDK to work, you must provide one public callback URL with the Bitski JS SDK imported.
37 |
38 | ```
39 |
40 |
41 |
42 | Logging in...
43 |
44 |
45 |
46 | ```
47 |
48 | Say goodbye to the awkward authentication dance and get back to doing what you do best—building your dApp! If you’re running into issues on how to use your Bitski Wallet, free feel to file an issue.
49 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Bitski Widget
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@bitski/waas-react-sdk",
3 | "version": "1.0.4",
4 | "type": "module",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "types": "dist/index.d.ts",
8 | "exports": {
9 | ".": {
10 | "import": "./dist/index.es.js",
11 | "require": "./dist/index.cjs.js",
12 | "types": "./dist/index.d.ts"
13 | },
14 | "./dist/style.css": "./dist/style.css"
15 | },
16 | "files": [
17 | "/dist"
18 | ],
19 | "sideEffects": true,
20 | "scripts": {
21 | "dev": "vite",
22 | "build": "tsc && vite build",
23 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
24 | "format": "prettier --config ../../.prettierrc '{src,tests}/**/*.ts' --write",
25 | "preview": "vite preview"
26 | },
27 | "peerDependencies": {
28 | "@tanstack/react-query": ">=5.0.0",
29 | "@wagmi/connectors": ">=2.0.0",
30 | "@wagmi/core": ">=2.0.0",
31 | "react": "^18",
32 | "react-dom": "^18",
33 | "viem": "2.x",
34 | "wagmi": "2.x"
35 | },
36 | "dependencies": {
37 | "@apollo/client": "^3.8.10",
38 | "@floating-ui/react": "^0.26.8",
39 | "@xstate/react": "^4.0.3",
40 | "bitski": "^4.2.1",
41 | "graphql-request": "^6.1.0",
42 | "lodash.debounce": "^4.0.8",
43 | "xstate": "^5.6.0"
44 | },
45 | "devDependencies": {
46 | "@apollo/rover": "^0.22.0",
47 | "@graphql-codegen/cli": "^5.0.0",
48 | "@graphql-codegen/client-preset": "^4.1.0",
49 | "@graphql-codegen/introspection": "^4.0.0",
50 | "@graphql-codegen/typescript": "^4.0.1",
51 | "@graphql-codegen/typescript-operations": "^4.0.1",
52 | "@statelyai/inspect": "^0.2.3",
53 | "@tanstack/eslint-plugin-query": "^5.18.1",
54 | "@tanstack/react-query": "^5.0.0",
55 | "@types/lodash.debounce": "^4.0.9",
56 | "@types/node": "^20.11.5",
57 | "@types/react": "^18.2.43",
58 | "@types/react-dom": "^18.2.17",
59 | "@vitejs/plugin-react": "^4.2.1",
60 | "autoprefixer": "^10.0.1",
61 | "postcss": "^8",
62 | "react": "^18.2.0",
63 | "react-dom": "^18.2.0",
64 | "tailwindcss": "^3.3.0",
65 | "viem": "^2.0.0",
66 | "vite": "^5.0.12",
67 | "vite-plugin-dts": "^3.7.1",
68 | "vite-plugin-lib-inject-css": "^1.3.0",
69 | "wagmi": "^2.0.0"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/App.tsx:
--------------------------------------------------------------------------------
1 | import './lib/index.css';
2 | import { BitskiProvider, BitskiWidget, Tab } from './lib';
3 | import { LoginMethod } from './lib';
4 | import { base, mainnet, optimism, polygon } from 'viem/chains';
5 |
6 | function App() {
7 | const providerConfig = {
8 | loginMethods: [
9 | LoginMethod.Email,
10 | LoginMethod.Wallet,
11 | LoginMethod.Apple,
12 | LoginMethod.Google,
13 | LoginMethod.X,
14 | ],
15 | chains: [mainnet, base, polygon, optimism],
16 | };
17 |
18 | return (
19 |
20 |
34 |
35 |
36 |
37 | );
38 | }
39 |
40 | export default App;
41 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/apple.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/arrow-rotate-right-left.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/chains/icon-base.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/chains/icon-ethereum.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/chains/icon-optimism.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/check-checked.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/check-disabled.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/chevron-left-small.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/chevron-right-small.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/coinbase-wallet.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/connector-icon-passkey.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/connector-state-connected.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/connector-state-error.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/cross-small.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/external-wallets.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/google.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/icon-activity-selected.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/icon-activity.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/icon-coinbase.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/icon-disconnect.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/icon-eth.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/icon-matic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/icon-optimism.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/icon-swaps-selected.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/icon-swaps.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/icon-tokens-selected.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/icon-tokens.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/icon-walletconnect.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/injected-wallet.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BitskiCo/bitski-js/82a2a6faac4bfead544ca6d26769b843ed8f6ecc/packages/waas-react-sdk/src/lib/assets/loading.png
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/pending.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BitskiCo/bitski-js/82a2a6faac4bfead544ca6d26769b843ed8f6ecc/packages/waas-react-sdk/src/lib/assets/pending.png
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/phantom.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/phone.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/settings.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/waas-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BitskiCo/bitski-js/82a2a6faac4bfead544ca6d26769b843ed8f6ecc/packages/waas-react-sdk/src/lib/assets/waas-logo.png
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/assets/x.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/BitskiWidget/BitskiAuth.styles.css:
--------------------------------------------------------------------------------
1 | /* Content animations */
2 | @keyframes scaleIn {
3 | from {
4 | transform: scale(0.85);
5 | }
6 | to {
7 | transform: scale(1);
8 | }
9 | }
10 |
11 | @keyframes scaleOut {
12 | from {
13 | transform: scale(1);
14 | }
15 | to {
16 | transform: scale(0.85);
17 | }
18 | }
19 |
20 | .Dialog-content-entering {
21 | animation: scaleIn 0.3s ease-out;
22 | }
23 |
24 | .Dialog-content-exiting {
25 | animation: scaleOut 0.3s ease-in;
26 | }
27 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/BitskiWidget/BitskiAuth.tsx:
--------------------------------------------------------------------------------
1 | import IdleConnection from './states/Idle';
2 | import { useDialogContext } from '../Dialog';
3 | import { useEffect, useState } from 'react';
4 | import './BitskiAuth.styles.css';
5 | import { ConnectionState, ConnectionStateKind } from '../../BitskiContext';
6 | import {useBitski} from "../../useBitski";
7 | import {ConnectionSessionCard} from "./states/ConnectionSessionCard";
8 |
9 | interface BitskiAuthProps {
10 | children?: React.ReactNode;
11 | logoUrl?: string;
12 | collapsed?: boolean;
13 | }
14 |
15 | const AuthWrapper = ({ children }: { children: React.ReactNode }) => {
16 | const { context: floatingContext } = useDialogContext();
17 | const [contentAnimationState, setContentAnimationState] = useState('exited');
18 | useEffect(() => {
19 | if (floatingContext.open) {
20 | setContentAnimationState('entering');
21 | setTimeout(() => setContentAnimationState('entered'), 300);
22 | } else {
23 | setContentAnimationState('exiting');
24 | setTimeout(() => setContentAnimationState('exited'), 300);
25 | }
26 | }, [floatingContext.open]);
27 |
28 | return {children}
;
29 | };
30 |
31 | function componentForConnectionState(connectionState: ConnectionState, reset: () => void) {
32 | switch (connectionState.kind) {
33 | case ConnectionStateKind.Discovering:
34 | return ;
35 | case ConnectionStateKind.NotConnected:
36 | return ;
37 | case ConnectionStateKind.Pending:
38 | case ConnectionStateKind.Connected:
39 | case ConnectionStateKind.Error:
40 | return
41 | }
42 | }
43 |
44 | export default function BitskiAuth({ collapsed }: BitskiAuthProps) {
45 | const { connectionState, reset } = useBitski();
46 | let Component = componentForConnectionState(connectionState, reset);
47 | return collapsed ? {Component} : Component;
48 | }
49 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/BitskiWidget/BitskiConnect.tsx:
--------------------------------------------------------------------------------
1 | import { truncateAddress } from '../../utils';
2 | import { ConnectionStateKind } from '../../BitskiContext';
3 | import {useBitski} from "../../useBitski";
4 |
5 | interface BitskiConnectProps {
6 | children?: React.ReactNode;
7 | displayText?: string;
8 | onClick?: () => void;
9 | }
10 |
11 | const DefaultConnect = ({ displayText }: { displayText: string }) => {
12 | const connectionState = useBitski().connectionState;
13 | let text: string;
14 | switch (connectionState.kind) {
15 | case ConnectionStateKind.Connected:
16 | text = truncateAddress(connectionState.address);
17 | break;
18 | default:
19 | text = displayText;
20 | }
21 |
22 | return (
23 |
24 | {text}
25 |
26 | );
27 | };
28 |
29 | export default function BitskiConnect({
30 | children,
31 | displayText = 'Login',
32 | onClick,
33 | }: BitskiConnectProps) {
34 | return (
35 |
39 | {children ? children : }
40 |
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/BitskiWidget/BitskiProvider.tsx:
--------------------------------------------------------------------------------
1 | import {QueryClient, QueryClientProvider} from '@tanstack/react-query';
2 | import {ReactNode, useReducer, useState} from 'react';
3 | import {WagmiProvider} from 'wagmi';
4 |
5 | import {createBitskiConfig, validateChains, validateConnectors} from '../../utils';
6 |
7 | import {LoginMethod} from './constants';
8 | import {Chain} from 'viem/chains';
9 | import {ConnectorConfig} from './types';
10 | import {BitskiContext, ConnectionStateKind, connectionStateReducer} from '../../BitskiContext';
11 | import {Tab} from '../BitskiWalletViewer';
12 | import {BitskiWalletProvider} from '../BitskiWalletProvider';
13 |
14 | interface BitskiProviderProps {
15 | children: ReactNode;
16 | appId: string;
17 | callbackURL?: string;
18 | chains: readonly [Chain, ...Chain[]];
19 | loginMethods: LoginMethod[];
20 | tabs?: Tab[]
21 | config?: ConnectorConfig | ConnectorConfig[];
22 | logoUrl?: string;
23 | signMessageOnConnect?: boolean;
24 | }
25 |
26 | function BitskiProvider({
27 | children,
28 | chains,
29 | appId,
30 | callbackURL,
31 | loginMethods,
32 | config,
33 | logoUrl,
34 | signMessageOnConnect,
35 | tabs
36 | }: BitskiProviderProps) {
37 | const [queryClient] = useState(() => new QueryClient());
38 | const [connectionState, dispatchConnectionAction] = useReducer(connectionStateReducer, {
39 | kind: ConnectionStateKind.Discovering,
40 | });
41 |
42 | const wagmiConfig = createBitskiConfig({
43 | chains: validateChains(chains),
44 | connectors: validateConnectors({ appId, callbackURL, loginMethods, config }),
45 | });
46 |
47 | const resolvedTabs = tabs ? tabs : [Tab.Tokens]
48 |
49 | return (
50 |
61 |
62 |
63 | {children}
64 |
65 |
66 |
67 | );
68 | }
69 |
70 | export default BitskiProvider;
71 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/BitskiWidget/BitskiWidget.tsx:
--------------------------------------------------------------------------------
1 | import BitskiConnect from './BitskiConnect';
2 | import { Dialog, DialogContent, DialogTrigger } from '../Dialog';
3 | import { BitskiWalletViewer, Tab } from '../BitskiWalletViewer';
4 | import { useContext } from 'react';
5 | import { BitskiContext, ConnectionStateKind } from '../../BitskiContext';
6 | import { BitskiAuth } from './index';
7 |
8 | export interface BitskiWidgetProps {
9 | connect?: React.ReactNode;
10 | logoUrl?: string;
11 | loginText?: string;
12 | }
13 |
14 | function BitskiWidget({ connect, logoUrl, loginText }: BitskiWidgetProps) {
15 | const { connectionState } = useContext(BitskiContext);
16 | let dialogContent;
17 | switch (connectionState.kind) {
18 | case ConnectionStateKind.Connected:
19 | dialogContent = ;
20 | break;
21 | default:
22 | dialogContent = ;
23 | }
24 |
25 | return (
26 |
27 |
28 | {connect ? connect : }
29 |
30 | {dialogContent}
31 |
32 | );
33 | }
34 |
35 | export default BitskiWidget;
36 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/BitskiWidget/EmailInput.tsx:
--------------------------------------------------------------------------------
1 | import { Connector } from 'wagmi';
2 | import { useState } from 'react';
3 | import { BitskiConnector } from '../../connectors';
4 | import React from 'react';
5 |
6 | interface EmailInputProps {
7 | connector: Connector | BitskiConnector;
8 | connect: ({
9 | connector,
10 | parameters,
11 | }: {
12 | connector: Connector;
13 | parameters?: Record;
14 | }) => void;
15 | }
16 |
17 | export const EmailInput = ({ connector, connect }: EmailInputProps) => {
18 | const [email, setEmail] = useState('');
19 |
20 | const buttonEnabled = email.length > 0;
21 | const buttonBgColor = buttonEnabled ? 'bg-black' : 'bg-[color:var(--aux-light-grey)]';
22 | const buttonTextColor = buttonEnabled ? 'text-white' : 'text-[color:var(--aux-grey)]';
23 | const inputTextColor = email.length > 0 ? 'text-black' : 'text-[color:var(--main-grey)]';
24 |
25 | const handleSubmit = (event: React.FormEvent) => {
26 | event.preventDefault();
27 |
28 | if ('setEmail' in connector) {
29 | connector.setEmail(email);
30 | }
31 |
32 | connect({ connector });
33 | };
34 |
35 | return (
36 |
57 | );
58 | };
59 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/BitskiWidget/Socials.tsx:
--------------------------------------------------------------------------------
1 | import { Connector, useConnect } from 'wagmi';
2 | import { Social } from './constants';
3 | import appleIcon from '../../assets/apple.svg';
4 | import googleIcon from '../../assets/google.svg';
5 | import xIcon from '../../assets/x.svg';
6 |
7 | interface SocialProps {
8 | name: string;
9 | icon: string;
10 | onSocialClick: () => void;
11 | }
12 |
13 | const SocialBtn = ({ name, icon, onSocialClick }: SocialProps) => {
14 | return (
15 |
19 |
20 |
21 | );
22 | };
23 |
24 | function useSocials(): Social[] {
25 | return [Social.Apple, Social.Google, Social.X];
26 | }
27 |
28 | function socialRow(
29 | social: Social,
30 | connector: Connector,
31 | ): { name: string; icon: string; social: Social; connector: Connector } {
32 | switch (social) {
33 | case Social.Apple:
34 | return {
35 | name: 'Apple',
36 | icon: appleIcon,
37 | social,
38 | connector,
39 | };
40 | case Social.Google:
41 | return {
42 | name: 'Google',
43 | icon: googleIcon,
44 | social,
45 | connector,
46 | };
47 | case Social.X:
48 | return {
49 | name: 'X',
50 | icon: xIcon,
51 | social,
52 | connector,
53 | };
54 | default:
55 | throw new Error('Invalid social');
56 | }
57 | }
58 |
59 | interface ConnectableSocial {
60 | social: Social;
61 | connector: Connector;
62 | }
63 |
64 | export default function Socials(props: {
65 | onSocialClick: (connectableSocial: ConnectableSocial) => void;
66 | }) {
67 | const socials = useSocials();
68 | const { connectors } = useConnect();
69 |
70 | const connectableSocials = socials
71 | .map((social) => {
72 | const connector = connectors.filter((connector) => {
73 | return connector.id === social;
74 | })[0];
75 |
76 | return { social, connector };
77 | })
78 | .filter(({ connector }) => connector);
79 |
80 | const rows = connectableSocials.map(({ social, connector }) => socialRow(social, connector));
81 |
82 | return (
83 |
84 | {rows.map(({ name, icon, social, connector }) => (
85 | props.onSocialClick({ social, connector })}
90 | />
91 | ))}
92 |
93 | );
94 | }
95 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/BitskiWidget/TOS.tsx:
--------------------------------------------------------------------------------
1 | export default function TOS() {
2 | return (
3 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/BitskiWidget/constants.ts:
--------------------------------------------------------------------------------
1 | export enum LoginMethod {
2 | Wallet = 'wallet',
3 | Email = 'email',
4 | Google = 'google',
5 | Apple = 'apple',
6 | X = 'x',
7 | Sms = 'sms',
8 | }
9 |
10 | export type LoginMethods = `${LoginMethod}`;
11 |
12 | export enum ExternalWallet {
13 | Phantom = 'phantom',
14 | MetaMask = 'metaMaskSDK',
15 | Injected = 'injected',
16 | CoinbaseWallet = 'coinbaseWalletSDK',
17 | WalletConnect = 'walletConnect',
18 | }
19 |
20 | export enum Social {
21 | Apple = 'apple',
22 | Google = 'google',
23 | X = 'x',
24 | }
25 |
26 | export enum ConnectionState {
27 | Idle = 'idle',
28 | Pending = 'pending',
29 | Connected = 'connected',
30 | Error = 'error',
31 | }
32 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/BitskiWidget/index.ts:
--------------------------------------------------------------------------------
1 | export { default as BitskiWidget } from './BitskiWidget';
2 | export { default as BitskiProvider } from './BitskiProvider';
3 | export { default as BitskiConnect } from './BitskiConnect';
4 | export { default as BitskiAuth } from './BitskiAuth';
5 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/BitskiWidget/states/Idle.tsx:
--------------------------------------------------------------------------------
1 | import { Connector, useConnectors } from 'wagmi';
2 | import { EmailInput } from '../EmailInput';
3 | import { LoginMethod, Social } from '../constants';
4 | import Wallets from '../Wallets';
5 | import Socials from '../Socials';
6 | import { SmsInput } from '../SmsInput';
7 | import { useContext } from 'react';
8 | import { BitskiContext } from '../../../BitskiContext';
9 | import TOS from '../TOS';
10 | import { useBitski } from '../../../useBitski';
11 |
12 | export default function IdleConnection() {
13 | const { loginMethods, logoUrl } = useContext(BitskiContext);
14 | const connectors = useConnectors();
15 | const { connect } = useBitski();
16 |
17 | const emailConnector = connectors.find((connector) => connector.id === LoginMethod.Email);
18 | const smsConnector: Connector | undefined = connectors.find(
19 | (connector) => connector.id === LoginMethod.Sms,
20 | );
21 | const socialConnectors = connectors.filter((connector) => connector.name in Social);
22 |
23 | return (
24 |
25 |
26 | Login or Sign Up
27 |
28 | {logoUrl ?
: null}
29 |
30 |
31 | {emailConnector ? (
32 | connect(emailConnector)} />
33 | ) : null}
34 |
35 | {smsConnector ? (
36 | connect(smsConnector)} />
37 | ) : null}
38 |
39 | {socialConnectors.length ? (
40 | await connect(social.connector)} />
41 | ) : null}
42 |
43 |
44 |
45 | {loginMethods.includes(LoginMethod.Wallet) ? (
46 |
await connect(wallet.connector)} />
47 | ) : null}
48 |
49 |
50 |
51 | );
52 | }
53 |
54 | function Or({ loginMethods }: { loginMethods: LoginMethod[] }) {
55 | return loginMethods.includes(LoginMethod.Wallet) && loginMethods.length > 1 ? (
56 |
57 |
58 |
59 | OR
60 |
61 |
62 |
63 | ) : null;
64 | }
65 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/BitskiWidget/types/config.ts:
--------------------------------------------------------------------------------
1 | import {
2 | CoinbaseWalletParameters,
3 | MetaMaskParameters,
4 | WalletConnectParameters,
5 | InjectedParameters,
6 | } from 'wagmi/connectors';
7 | import { BitskiParameters } from '../../../connectors/bitski';
8 |
9 | export interface ConnectorConfig {
10 | wallet: 'injected' | 'phantom' | 'coinbaseWallet' | 'metaMask' | 'walletConnect' | 'bitski';
11 | options?:
12 | | CoinbaseWalletParameters
13 | | MetaMaskParameters
14 | | WalletConnectParameters
15 | | InjectedParameters
16 | | BitskiParameters;
17 | }
18 |
19 | export type ConfigTypeMap = {
20 | injected: InjectedParameters;
21 | phantom: InjectedParameters;
22 | walletConnect: WalletConnectParameters;
23 | metaMask: MetaMaskParameters;
24 | coinbaseWallet: CoinbaseWalletParameters;
25 | bitski: BitskiParameters;
26 | };
27 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/BitskiWidget/types/index.ts:
--------------------------------------------------------------------------------
1 | export type { ConnectorConfig, ConfigTypeMap } from './config';
2 | export type { LoginMethods } from './provider';
3 | export type {
4 | IdleState,
5 | PendingState,
6 | ConnectedState,
7 | ErrorState,
8 | ConnectionStateType,
9 | } from './states';
10 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/BitskiWidget/types/provider.ts:
--------------------------------------------------------------------------------
1 | import { LoginMethod } from '../constants';
2 |
3 | export type LoginMethods = `${LoginMethod}`;
4 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/BitskiWidget/types/states.ts:
--------------------------------------------------------------------------------
1 | import { Connector } from 'wagmi';
2 | import { ConnectionState } from '../constants';
3 |
4 | export type IdleState = { type: ConnectionState.Idle };
5 | export type PendingState = { type: ConnectionState.Pending; pendingConnector: Connector };
6 | export type ConnectedState = {
7 | type: ConnectionState.Connected;
8 | connector: Connector;
9 | address: string;
10 | chain: string;
11 | };
12 | export type ErrorState = { type: ConnectionState.Error; connector: Connector | undefined };
13 |
14 | export type ConnectionStateType = IdleState | PendingState | ConnectedState | ErrorState;
15 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/ChainIcon.tsx:
--------------------------------------------------------------------------------
1 | import { base, mainnet, optimism, polygon } from 'viem/chains';
2 | import iconEthereum from '../assets/chains/icon-ethereum.svg';
3 | import iconPolygon from '../assets/chains/icon-polygon.svg';
4 | import iconBase from '../assets/chains/icon-base.svg';
5 | import iconOptimism from '../assets/chains/icon-optimism.svg';
6 |
7 | const iconMap: Record = {
8 | [mainnet.id]: { src: iconEthereum },
9 | [polygon.id]: { src: iconPolygon },
10 | [base.id]: { src: iconBase },
11 | [optimism.id]: { src: iconOptimism },
12 | };
13 |
14 | export function ChainIcon({ chainId, size }: { chainId: number; size: number }) {
15 | const iconEntry = iconMap[chainId];
16 | if (!iconEntry) {
17 | return null
18 | }
19 | return (
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/ChainSwitcher.tsx:
--------------------------------------------------------------------------------
1 | import { useDismiss, useFloating, useInteractions } from '@floating-ui/react';
2 | import { ChainIcon } from './ChainIcon';
3 | import { useState } from 'react';
4 | import { useAccount, useSwitchChain } from 'wagmi';
5 | import checkChecked from '../assets/check-checked.svg';
6 | import checkDisabled from '../assets/check-disabled.svg';
7 | import { LoadingSpinner } from './LoadingSpinner';
8 |
9 | interface ChainSwitcherProps {}
10 |
11 | export const ChainSwitcher = ({}: ChainSwitcherProps) => {
12 | const { chain: selectedChain } = useAccount();
13 | const { chains, switchChain } = useSwitchChain();
14 |
15 | const [isOpen, setIsOpen] = useState(false);
16 |
17 | const { refs, floatingStyles, context } = useFloating({
18 | placement: 'bottom-start',
19 | open: isOpen,
20 | onOpenChange: setIsOpen,
21 | });
22 |
23 | const dismiss = useDismiss(context);
24 |
25 | const { getReferenceProps, getFloatingProps } = useInteractions([dismiss]);
26 |
27 | const onChainClick = (chainId: number) => {
28 | switchChain({ chainId });
29 | setIsOpen(false);
30 | };
31 |
32 | return (
33 | <>
34 | setIsOpen(!isOpen)}
39 | >
40 | {selectedChain ? : }
41 |
42 | {isOpen && (
43 |
49 | {chains.slice(1).map((chain) => {
50 | const checkSrc = chain.id === selectedChain?.id ? checkChecked : checkDisabled;
51 | return (
52 |
onChainClick(chain.id)}
56 | >
57 |
58 |
59 | {chain.name}
60 |
61 |
62 |
63 |
64 |
65 | );
66 | })}
67 |
68 | )}
69 | >
70 | );
71 | };
72 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/CopyAddress.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import { truncateAddress } from '../utils';
3 |
4 | export const CopyAddress = ({ address }: { address: string }) => {
5 | const [copied, setCopied] = useState(false);
6 |
7 | const copy = async () => {
8 | await window.navigator.clipboard.writeText(address);
9 | setCopied(true);
10 | setTimeout(() => setCopied(false), 300);
11 | };
12 |
13 | return (
14 |
18 | {copied ? 'Copied!' : truncateAddress(address)}
19 |
20 | );
21 | };
22 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/Dialog/Dialog.styles.css:
--------------------------------------------------------------------------------
1 | /* Overlay animations */
2 | @keyframes fadeIn {
3 | from {
4 | opacity: 0;
5 | }
6 | to {
7 | opacity: 1;
8 | }
9 | }
10 |
11 | @keyframes fadeOut {
12 | from {
13 | opacity: 1;
14 | }
15 | to {
16 | opacity: 0;
17 | }
18 | }
19 |
20 | /* Overlay Styles */
21 | .Dialog-overlay {
22 | background: rgba(0, 0, 0, 0.4);
23 | display: grid;
24 | place-items: center;
25 | }
26 |
27 | .Dialog-overlay-entering,
28 | .Dialog-overlay-entered {
29 | animation: fadeIn 0.3s ease-out;
30 | }
31 |
32 | .Dialog-overlay-exiting {
33 | animation: fadeOut 0.3s ease-in;
34 | }
35 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/EmptyActivities.tsx:
--------------------------------------------------------------------------------
1 | import emptyActivitiesImage from '../assets/empty-activities.svg';
2 |
3 | export const EmptyActivities = () => {
4 | return (
5 |
6 |
7 |
No past wallet activity
8 |
9 | This is your long-term memory on the blockchain. You'll be able to view your future
10 | transaction history here.
11 |
12 |
13 | );
14 | };
15 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/EmptySwaps.tsx:
--------------------------------------------------------------------------------
1 | import emptyTokensImage from '../assets/empty-tokens.svg';
2 |
3 | export const EmptySwaps = () => {
4 | return (
5 |
6 |
7 |
You don't have any tokens to swap
8 |
9 | Deposit tokens into your wallet to begin swapping. You can copy your address to transfer
10 | tokens into the wallet by clicking on it above.
11 |
12 |
13 | );
14 | };
15 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/EmptyTokens.tsx:
--------------------------------------------------------------------------------
1 | import emptyTokensImage from '../assets/empty-tokens.svg';
2 |
3 | export const EmptyTokens = () => {
4 | return (
5 |
6 |
7 |
You don't own any tokens yet
8 |
9 | Deposit tokens into your wallet to see your balance. You can copy your address to transfer
10 | tokens into the wallet by clicking on it above.
11 |
12 |
13 | );
14 | };
15 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/LoadingSpinner.tsx:
--------------------------------------------------------------------------------
1 | export const LoadingSpinner = () => {
2 | return (
3 |
9 |
17 |
22 |
23 | );
24 | };
25 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/SettingsMenu.tsx:
--------------------------------------------------------------------------------
1 | import { useDismiss, useFloating, useInteractions } from '@floating-ui/react';
2 | import iconDisconnect from '../assets/icon-disconnect.svg';
3 | import { Connector } from 'wagmi';
4 | import { useBitski } from '..';
5 | import { useState } from 'react';
6 | import iconSettings from '../assets/settings.svg';
7 |
8 | export const SettingsMenu = ({ connector }: { connector: Connector }) => {
9 | const { disconnect } = useBitski();
10 | const [isOpen, setIsOpen] = useState(false);
11 |
12 | const { refs, floatingStyles, context } = useFloating({
13 | placement: 'bottom-end',
14 | open: isOpen,
15 | onOpenChange: setIsOpen,
16 | });
17 |
18 | const dismiss = useDismiss(context);
19 |
20 | const { getReferenceProps, getFloatingProps } = useInteractions([dismiss]);
21 |
22 | return (
23 | <>
24 | setIsOpen(!isOpen)}>
25 |
26 |
27 | {isOpen && (
28 |
34 |
{
36 | await disconnect(connector);
37 | }}
38 | className="flex items-center gap-2 w-full hover:bg-[color:var(--aux-grey)] hover:cursor-pointer p-2"
39 | >
40 |
41 |
42 | Disconnect
43 |
44 |
45 |
46 | )}
47 | >
48 | );
49 | };
50 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/Skeleton.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const Skeleton = () => {
4 | return (
5 |
12 | );
13 | };
14 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/hooks/TotalBalanceUSD.graphql:
--------------------------------------------------------------------------------
1 | query TotalBalanceUSD($input: GetCurrencyBalancesV2Input!) {
2 | currencyBalances(input: $input) {
3 | totalBalanceUSD {
4 | formatted
5 | }
6 | connections {
7 | nodes {
8 | address {
9 | raw
10 | truncated
11 | }
12 | amountV2 {
13 | amount {
14 | decimals
15 | formatted
16 | value
17 | }
18 | formatted
19 | }
20 | value {
21 | decimals
22 | formatted
23 | value
24 | }
25 | currency {
26 | displayName
27 | image {
28 | url
29 | }
30 | symbol
31 | decimals
32 | }
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/components/hooks/useTokens.ts:
--------------------------------------------------------------------------------
1 | export interface TokenBalance {
2 | amount: string;
3 | amountUSD: string;
4 | name: string;
5 | image?: string;
6 | }
7 |
8 | interface Tokens {
9 | totalBalanceUsd: string;
10 | balances: TokenBalance[];
11 | }
12 |
13 | export enum TokenStateKind {
14 | NoAddress = 'noAddress',
15 | Loading = 'loading',
16 | Tokens = 'tokens',
17 | Error = 'error',
18 | }
19 |
20 | export type TokensState =
21 | | { kind: TokenStateKind.NoAddress }
22 | | { kind: TokenStateKind.Loading; address: string; chainId: number }
23 | | { kind: TokenStateKind.Tokens; address: string; chainId: number; tokens: Tokens }
24 | | { kind: TokenStateKind.Error; address: string; chainId: number };
25 |
26 | export enum TokenActionKind {
27 | FetchStart = 'Fetch Start',
28 | FetchSuccess = 'Fetch Success',
29 | FetchError = 'Fetch Error',
30 | }
31 |
32 | export type TokenAction =
33 | | {
34 | kind: TokenActionKind.FetchSuccess;
35 | address: string;
36 | chainId: number;
37 | tokens: Tokens;
38 | }
39 | | {
40 | kind: TokenActionKind.FetchStart;
41 | address: string;
42 | chainId: number;
43 | }
44 | | {
45 | kind: TokenActionKind.FetchError;
46 | address: string;
47 | chainId: number;
48 | };
49 |
50 | export function tokensReducer(tokensState: TokensState, action: TokenAction): TokensState {
51 | switch (action.kind) {
52 | case TokenActionKind.FetchStart:
53 | return {
54 | kind: TokenStateKind.Loading,
55 | address: action.address,
56 | chainId: action.chainId,
57 | };
58 | case TokenActionKind.FetchSuccess:
59 | switch (tokensState.kind) {
60 | case TokenStateKind.NoAddress:
61 | break;
62 | default:
63 | if (tokensState.address != action.address) {
64 | // Fetch does not match current address
65 | return tokensState;
66 | }
67 | }
68 | return {
69 | kind: TokenStateKind.Tokens,
70 | address: action.address,
71 | chainId: action.chainId,
72 | tokens: action.tokens,
73 | };
74 | case TokenActionKind.FetchError:
75 | return {
76 | kind: TokenStateKind.Error,
77 | address: action.address,
78 | chainId: action.chainId,
79 | };
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/connectors/index.ts:
--------------------------------------------------------------------------------
1 | export { bitski } from './bitski';
2 | export type { BitskiConnector, BitskiParameters } from './bitski';
3 | export { phantom } from './phantom';
4 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/connectors/localStoragePopup.ts:
--------------------------------------------------------------------------------
1 | const POPUP_HEIGHT = 500;
2 | const POPUP_WIDTH = 500;
3 |
4 | export const openLocalStoragePopup = (): Promise =>
5 | new Promise((resolve, reject) => {
6 | const width = window.screen.width;
7 | const height = window.screen.height;
8 |
9 | const leftOffset = (width - POPUP_WIDTH) / 2;
10 | const topOffset = (height - POPUP_HEIGHT) / 2;
11 |
12 | const closeId = crypto.randomUUID();
13 |
14 | window.addEventListener(
15 | 'message',
16 | (event) => {
17 | if (event.origin !== window.location.origin) return;
18 |
19 | if (event.data?.closeId === closeId || event.data?.closeId === null) {
20 | resolve();
21 | }
22 | },
23 | false,
24 | );
25 |
26 | const popup = window.open(
27 | `https://sign.bitski.com/import-local?closeId=${closeId}`,
28 | '_blank',
29 | `popup=yes,
30 | width=${POPUP_WIDTH},
31 | height=${POPUP_HEIGHT},
32 | top=${topOffset},
33 | left=${leftOffset}
34 | `,
35 | );
36 | });
37 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/connectors/phantom.ts:
--------------------------------------------------------------------------------
1 | import { injected, InjectedParameters } from 'wagmi/connectors';
2 |
3 | export function phantom(parameters: InjectedParameters) {
4 | return injected({
5 | ...parameters,
6 | target() {
7 | return {
8 | id: 'phantom',
9 | name: 'Phantom',
10 | provider(window) {
11 | if (window?.phantom?.ethereum) return window.phantom?.ethereum;
12 | if (window?.ethereum?.providers)
13 | return window?.ethereum.providers.find((provider) => provider.isPhantom);
14 | if (window?.ethereum && window?.ethereum.isPhantom) return window?.ethereum;
15 | return undefined;
16 | },
17 | };
18 | },
19 | });
20 | }
21 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/constants.ts:
--------------------------------------------------------------------------------
1 | export const BITSKI_GRAPHQL_ENDPOINT = 'https://api.bitski.com/graphql';
2 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/generated/gql/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./fragment-masking";
2 | export * from "./gql";
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/index.ts:
--------------------------------------------------------------------------------
1 | import './index.css';
2 | export { BitskiProvider, BitskiWidget, BitskiAuth } from './components/BitskiWidget';
3 | export { BitskiWalletViewer, Tab } from './components/BitskiWalletViewer';
4 | export { BitskiWalletProvider } from './components/BitskiWalletProvider';
5 | export type { LoginMethods } from './components/BitskiWidget/types';
6 | export { LoginMethod } from './components/BitskiWidget/constants';
7 | export { ConnectionStateKind, type ConnectionState } from './BitskiContext';
8 | export {useBitski} from "./useBitski";
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/utils/createBitskiConfig.ts:
--------------------------------------------------------------------------------
1 | import type { Transport } from 'viem';
2 | import { http, createConfig, CreateConnectorFn } from 'wagmi';
3 | import { type Chain } from 'wagmi/chains';
4 |
5 | interface BitskiWagmiConfigOptions {
6 | chains: readonly [Chain, ...Chain[]];
7 | connectors: CreateConnectorFn[];
8 | }
9 |
10 | export const createBitskiConfig = ({ chains, connectors }: BitskiWagmiConfigOptions) => {
11 | const transports: Record = {};
12 |
13 | for (const chain of chains) {
14 | transports[chain.id] = http();
15 | }
16 |
17 | return createConfig({
18 | chains,
19 | connectors,
20 | ssr: false,
21 | transports,
22 | multiInjectedProviderDiscovery: true,
23 | });
24 | };
25 |
26 | declare module 'wagmi' {
27 | interface Register {
28 | config: typeof createBitskiConfig;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/utils/getBlockchainAccounts.ts:
--------------------------------------------------------------------------------
1 | export async function getBlockchainAccounts(
2 | fetch: typeof window.fetch,
3 | token: string,
4 | ): Promise {
5 | const response = await fetch('https://api.bitski.com/v2/blockchain/accounts', {
6 | method: 'GET',
7 | headers: {
8 | Authorization: `Bearer ${token}`,
9 | },
10 | });
11 | const { accounts } = (await response.json()) as AccountsResponse;
12 | return accounts;
13 | }
14 |
15 | export const LOCAL_STORAGE_LABEL = 'wallet.bitski.com/local-storage';
16 |
17 | export interface BlockchainAccount {
18 | id: string;
19 | address: string;
20 | kind: string;
21 | labels: Record;
22 | annotations: Record;
23 | }
24 |
25 | export interface AccountsResponse {
26 | accounts: BlockchainAccount[];
27 | }
28 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/utils/hasWindowProvider.ts:
--------------------------------------------------------------------------------
1 | export const hasWindowProvider = () => {
2 | if (typeof window !== 'undefined' && window.ethereum) {
3 | return true;
4 | }
5 |
6 | return false;
7 | };
8 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/utils/index.ts:
--------------------------------------------------------------------------------
1 | export { createBitskiConfig } from './createBitskiConfig';
2 | export { validateChains } from './validateChains';
3 | export { validateConnectors } from './validateConnectors';
4 | export { hasWindowProvider } from './hasWindowProvider';
5 | export { isMobile } from './isMobile';
6 | export { truncateAddress } from './truncateAddress';
7 | export { truncateTitle } from './truncateTitle';
8 | export { toFormattedValue } from './toFormattedValue';
9 | export { toRawValue } from './toRawValue';
10 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/utils/isMobile.ts:
--------------------------------------------------------------------------------
1 | export const isMobile = (): boolean => {
2 | if (typeof window !== 'undefined' && typeof navigator !== 'undefined') {
3 | const isMobileUserAgent = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
4 | const maxWidth = 768;
5 | const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
6 | return isMobileUserAgent && window.innerWidth <= maxWidth && hasTouch;
7 | }
8 |
9 | return false;
10 | };
11 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/utils/toFormattedValue.ts:
--------------------------------------------------------------------------------
1 | import { formatUnits } from 'viem';
2 |
3 | export const toFormattedValue = (amount: string, decimals?: number | null) => {
4 | if (!decimals) {
5 | throw new Error('Missing decimals for setting token value.');
6 | }
7 |
8 | return formatUnits(BigInt(amount), decimals);
9 | };
10 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/utils/toRawValue.ts:
--------------------------------------------------------------------------------
1 | import { parseUnits } from 'viem';
2 |
3 | export const toRawValue = (amount: string, decimals?: number | null) => {
4 | if (!decimals) {
5 | throw new Error('Missing decimals for setting token value.');
6 | }
7 |
8 | return parseUnits(amount, decimals);
9 | };
10 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/utils/truncateAddress.ts:
--------------------------------------------------------------------------------
1 | // Captures 0x + 4 characters, then the last 4 characters.
2 | const truncateRegex = /^(0x[a-zA-Z0-9]{4})[a-zA-Z0-9]+([a-zA-Z0-9]{4})$/;
3 |
4 | /**
5 | * Truncates an ethereum address to the format 0x0000…0000
6 | * @param address Full address to truncate
7 | * @returns Truncated address
8 | */
9 | export const truncateAddress = (address: string) => {
10 | const match = address.match(truncateRegex);
11 | if (!match) return address;
12 | return `${match[1]}…${match[2]}`;
13 | };
14 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/utils/truncateTitle.ts:
--------------------------------------------------------------------------------
1 | const MAX_TITLE_LENGTH = 22;
2 |
3 | export function truncateTitle(input: string): string {
4 | if (input.length <= MAX_TITLE_LENGTH) {
5 | return input;
6 | } else {
7 | return input.slice(0, MAX_TITLE_LENGTH) + '...';
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/lib/utils/validateChains.ts:
--------------------------------------------------------------------------------
1 | import { Chain } from 'viem/chains';
2 |
3 | export const validateChains = (chains: readonly [Chain, ...Chain[]]) => {
4 | // Allow all Chains, will error downstream if we don't support it
5 | return chains;
6 | };
7 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import App from './App';
4 |
5 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
6 |
7 |
8 | ,
9 | );
10 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/tailwind.config.cjs:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
4 | theme: {},
5 | plugins: [],
6 | };
7 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Bundler",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/waas-react-sdk/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 | import path from 'node:path';
4 | import dts from 'vite-plugin-dts';
5 | import tailwindcss from 'tailwindcss';
6 | import { libInjectCss } from 'vite-plugin-lib-inject-css';
7 |
8 | export default defineConfig({
9 | css: {
10 | postcss: {
11 | plugins: [tailwindcss],
12 | },
13 | },
14 | build: {
15 | copyPublicDir: false,
16 | commonjsOptions: {
17 | include: [/eth-provider-types/, /node_modules/],
18 | },
19 | emptyOutDir: true,
20 | lib: {
21 | entry: path.resolve(__dirname, 'src/lib/index.ts'),
22 | name: 'BitskiWaasReactSDK',
23 | formats: ['es', 'cjs'],
24 | fileName: (format) => `index.${format}.js`,
25 | },
26 | rollupOptions: {
27 | external: [
28 | 'react',
29 | 'react/jsx-runtime',
30 | 'react-dom',
31 | 'tailwindcss',
32 | 'wagmi',
33 | 'viem',
34 | '@tanstack/react-query',
35 | '@wagmi/core',
36 | '@wagmi/connectors',
37 | ],
38 | output: {
39 | globals: {
40 | react: 'React',
41 | 'react/jsx-runtime': 'react/jsx-runtime',
42 | 'react-dom': 'ReactDOM',
43 | tailwindcss: 'tailwindcss',
44 | wagmi: 'wagmi',
45 | viem: 'viem',
46 | '@tanstack/react-query': '@tanstack/react-query',
47 | '@wagmi/core': '@wagmi/core',
48 | '@wagmi/connectors': '@wagmi/connectors',
49 | },
50 | },
51 | },
52 | sourcemap: true,
53 | },
54 | optimizeDeps: {
55 | include: ['eth-provider-types'],
56 | },
57 | plugins: [libInjectCss(), react(), dts({ include: 'src/lib', rollupTypes: true })],
58 | });
59 |
--------------------------------------------------------------------------------
/packages/wagmi-connector/.gitignore:
--------------------------------------------------------------------------------
1 | lib
--------------------------------------------------------------------------------
/packages/wagmi-connector/README.md:
--------------------------------------------------------------------------------
1 | # Bitski Wagmi Connector
2 |
3 | [](https://www.npmjs.com/package/@bitski/wagmi-connector)
4 |
5 | ## Installation
6 |
7 | ```
8 | npm install @bitski/wagmi-connector
9 | ```
10 |
11 | ## Usage
12 |
13 | Below are common examples. For more details on all configurable options, please also see the wagmi and RainbowKit docs.
14 |
15 | ### Wagmi Only
16 |
17 | ```javascript
18 | import { BitskiConnector } from '@bitski/wagmi-connector';
19 | import { createConfig } from '@wagmi/core';
20 |
21 | wagmiConfig = createConfig({
22 | connectors: [
23 | new BitskiConnector({
24 | chains,
25 | options: {
26 | id: 'my-connector',
27 | name: 'My App Wallet',
28 | appId: 'my-bitski-app-id',
29 | bitskiOptions: {
30 | waas: { enabled: false },
31 | callbackURL: 'https://callback.url:3000',
32 | // For more options, see the list of ProviderOptions under the bitski package
33 | },
34 | },
35 | }),
36 | new WalletConnectConnector({
37 | chains,
38 | options: { projectId: walletConnectProjectId, showQrModal: false, metadata },
39 | }),
40 | ],
41 | ...defaultConfig,
42 | });
43 | ```
44 |
45 | ### RainbowKit + Wagmi
46 |
47 | ```javascript
48 | import { bitskiWallet } from '@bitski/wagmi-connector';
49 | import {
50 | connectorsForWallets,
51 | RainbowKitProvider,
52 | Locale,
53 | } from "@rainbow-me/rainbowkit";
54 | import {
55 | injectedWallet,
56 | metaMaskWallet,
57 | coinbaseWallet,
58 | } from "@rainbow-me/rainbowkit/wallets";
59 | import { configureChains, createConfig, WagmiConfig } from "wagmi";
60 | import { mainnet, polygon, optimism, arbitrum, base, zora } from "viem/chains";
61 | import { publicProvider } from "wagmi/providers/public";
62 |
63 | const connectors = connectorsForWallets([
64 | {
65 | groupName: "Recommended",
66 | wallets: [
67 | bitskiWallet({
68 | options: { appId: 'my-bitski-app-id', bitskiOptions: { network } },
69 | chains,
70 | }),
71 | ],
72 | },
73 | {
74 | groupName: "Other Wallets",
75 | wallets: [
76 | injectedWallet({ chains }),
77 | metaMaskWallet({ chains, projectId: "YOUR_PROJECT_ID" }),
78 | coinbaseWallet({ appName: "YOUR_APP_NAME", chains }),
79 | ],
80 | },
81 | ]);
82 |
83 | const wagmiConfig = createConfig({
84 | autoConnect: true,
85 | connectors,
86 | publicClient,
87 | webSocketPublicClient,
88 | });
89 |
90 | function MyApp({ Component, pageProps }: AppProps) {
91 | const { locale } = useRouter() as { locale: Locale };
92 | return (
93 |
94 |
95 |
96 |
97 |
98 | );
99 | }
100 | ```
101 |
--------------------------------------------------------------------------------
/packages/wagmi-connector/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@bitski/wagmi-connector",
3 | "version": "1.0.3",
4 | "description": "Wagmi adapter for Bitski",
5 | "main": "lib/index.js",
6 | "module": "dist/index.js",
7 | "types": "lib/index.d.ts",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/BitskiCo/bitski-js"
11 | },
12 | "scripts": {
13 | "lint": "eslint . --cache",
14 | "build": "tsc -p tsconfig.json",
15 | "prettier": "prettier --config ../../.prettierrc '{src,tests}/**/*.ts' --write"
16 | },
17 | "dependencies": {
18 | "@wagmi/core": "^1.4.13",
19 | "bitski": "^4.0.0",
20 | "viem": "^1.19.13",
21 | "@rainbow-me/rainbowkit": "^1.3.3"
22 | },
23 | "devDependencies": {
24 | "@babel/core": "^7.6.4",
25 | "@babel/plugin-transform-runtime": "^7.6.2",
26 | "@babel/preset-env": "^7.6.3",
27 | "babelify": "^10.0.0"
28 | },
29 | "browserify": {
30 | "transform": [
31 | [
32 | "babelify",
33 | {
34 | "presets": [
35 | "@babel/preset-env"
36 | ],
37 | "plugins": [
38 | "@babel/plugin-transform-runtime"
39 | ]
40 | }
41 | ]
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/packages/wagmi-connector/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "dist",
4 | "rootDir": "src",
5 | "noImplicitAny": true,
6 | "moduleResolution": "node",
7 | "lib": ["ESNext"],
8 | "target": "ES2020",
9 | "esModuleInterop": true,
10 | "isolatedModules": true,
11 | "preserveSymlinks": true,
12 | "declaration": true,
13 | "declarationDir": "lib",
14 | "jsx": "preserve",
15 | "skipLibCheck": true,
16 | "strict": true,
17 | "resolveJsonModule": true
18 | },
19 | "include": ["src/**/*.ts"]
20 | }
21 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turborepo.org/schema.json",
3 | "baseBranch": "origin/main",
4 | "pipeline": {
5 | "build": {
6 | "dependsOn": ["^build"]
7 | },
8 | "package": {
9 | "dependsOn": ["^build"],
10 | "outputs": ["package/**"]
11 | },
12 | "test": {
13 | "dependsOn": ["^build"],
14 | "outputs": []
15 | },
16 | "check": {
17 | "outputs": []
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------